Skip to content

Add Support for Configuration of Dart JS Interop Gen #386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions web_generator/lib/src/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import 'package:dart_style/dart_style.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';

abstract interface class Config {
/// The name for the configuration
String? get name;

/// The description for the configuration
String? get description;

/// Preamble to add at the top of generated code
String? get preamble;

/// The input files
List<String> get input;

/// The output file or directory to write bindings to
String get output;

/// The configuration file
Uri? get filename;

/// The Dart Language Version to use
Version get languageVersion;

bool get singleFileOutput => input.length == 1;

factory Config(
{required List<String> input,
required String output,
required Version languageVersion,
}) = ConfigImpl._;
}

class ConfigImpl implements Config {
@override
String? description;

@override
Uri? filename;

@override
List<String> input;

@override
String? name;

@override
String output;

@override
Version languageVersion;

@override
String? preamble;

ConfigImpl._(
{required this.input,
required this.output,
required this.languageVersion,
});

@override
// TODO: implement singleFileOutput
bool get singleFileOutput => input.length == 1;
}

class YamlConfig implements Config {
@override
Uri filename;

@override
List<String> input;

@override
String? description;

@override
String? name;

@override
String output;

@override
bool get singleFileOutput => input.length == 1;

@override
Version languageVersion;

@override
String? preamble;

YamlConfig._(
{required this.filename,
required this.input,
required this.output,
this.description,
this.name,
this.preamble,
String? languageVersion})
: languageVersion = languageVersion == null
? DartFormatter.latestLanguageVersion
: Version.parse(languageVersion);

factory YamlConfig.fromYaml(YamlMap yaml, {required String filename}) {
List<String> input;
final yamlInput = yaml['input'];
if (yamlInput is YamlList) {
input = yamlInput.map((y) => y is String ? y : y.toString()).toList();
} else if (yamlInput is String) {
input = [yamlInput];
} else {
throw TypeError();
}

return YamlConfig._(
filename: Uri.file(filename),
input: input,
output: yaml['output'] as String,
name: yaml['name'] as String?,
description: yaml['description'] as String?,
languageVersion: yaml['language_version'] as String?,
preamble: yaml['preamble'] as String?
);
}
}
37 changes: 25 additions & 12 deletions web_generator/lib/src/dart_main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import 'package:args/args.dart';
import 'package:code_builder/code_builder.dart' as code;
import 'package:dart_style/dart_style.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';

import 'config.dart';
import 'dts/parser.dart';
import 'dts/transform.dart';
import 'generate_bindings.dart';
Expand Down Expand Up @@ -36,22 +38,33 @@ void main(List<String> args) async {
languageVersion: Version.parse(languageVersionString),
);
} else if (argResult.wasParsed('declaration')) {
await generateJSInteropBindings(
inputs: argResult['input'] as Iterable<String>,
output: argResult['output'] as String,
languageVersion: Version.parse(languageVersionString),
);
final Config config;

if (argResult.wasParsed('config')) {
final filename = argResult['config'] as String;
final configContent = fs.readFileSync(
filename.toJS, JSReadFileOptions(encoding: 'utf8'.toJS)) as JSString;
final yaml = loadYamlDocument(configContent.toDart);
config =
YamlConfig.fromYaml(yaml.contents as YamlMap, filename: filename);

} else {
config = Config(
input: argResult['input'] as List<String>,
output: argResult['output'] as String,
languageVersion: Version.parse(languageVersionString),
);

}

await generateJSInteropBindings(config);
}
}

// TODO(nikeokoronkwo): Add support for configuration
Future<void> generateJSInteropBindings({
required Iterable<String> inputs,
required String output,
required Version languageVersion,
}) async {
Future<void> generateJSInteropBindings(Config config) async {
// generate
final jsDeclarations = parseDeclarationFiles(inputs);
final jsDeclarations = parseDeclarationFiles(config.input);

// transform declarations
final dartDeclarations = transformDeclarations(jsDeclarations);
Expand All @@ -60,7 +73,7 @@ Future<void> generateJSInteropBindings({
final generatedCode = dartDeclarations.generate();

// write code to file
fs.writeFileSync(output.toJS, generatedCode.toJS);
fs.writeFileSync(config.output.toJS, generatedCode.toJS);
}

Future<void> generateIDLBindings({
Expand Down
5 changes: 0 additions & 5 deletions web_generator/lib/src/dts/config.dart

This file was deleted.

1 change: 1 addition & 0 deletions web_generator/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ dependencies:
path: ^1.8.3
pub_semver: ^2.1.5
test: ^1.24.4
yaml: ^3.1.3
5 changes: 5 additions & 0 deletions web_generator/test/assets/basic_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: IDL Demo Gen
preamble: |
// GENERATED FILE: DO NOT EDIT
input: intro.d.ts
output: output.dart
6 changes: 6 additions & 0 deletions web_generator/test/assets/invalid_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: Nanoid and Lodash
preamble: |
// GENERATED FILE: DO NOT EDIT
input:
nanoid: 'nanoid.d.ts'
output: output
9 changes: 9 additions & 0 deletions web_generator/test/assets/invalid_output_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: Nanoid and Lodash
preamble: |
// GENERATED FILE: DO NOT EDIT
input:
- 'nanoid.d.ts'
- 'lodash.d.ts'
output:
- nanoid.dart
- lodash.dart
7 changes: 7 additions & 0 deletions web_generator/test/assets/multi_file_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: Nanoid and Lodash
preamble: |
// GENERATED FILE: DO NOT EDIT
input:
- 'nanoid.d.ts'
- 'lodash.d.ts'
output: output
66 changes: 66 additions & 0 deletions web_generator/test/config_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@TestOn('vm')
library;

import 'dart:io';

import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:web_generator/src/config.dart';
import 'package:yaml/yaml.dart';

final configurationTests = {
'basic': {
'name': 'IDL Demo Gen',
'single': true,
'input': ['intro.d.ts'],
'valid': true
},
'multi_file': {
'name': 'Nanoid and Lodash',
'single': false,
'input': ['nanoid.d.ts', 'lodash.d.ts'],
'valid': true
},
'invalid': {'valid': false},
'invalid_output': {'valid': false}
};

void main() {
group('Configuration Test', () {
group('YAML Config Test', () {
final assetsDir = p.join('test', 'assets');

final assets = Directory(assetsDir);

for (final file in assets.listSync().whereType<File>().where((f) =>
p.basenameWithoutExtension(f.path).endsWith('_config') &&
p.extension(f.path) == '.yaml')) {
final fileName =
p.basenameWithoutExtension(file.path).replaceFirst('_config', '');
final fileContents = file.readAsStringSync();
final configTest = configurationTests[fileName];

if (configTest == null) continue;

test(fileName, () {
final yaml = loadYamlDocument(fileContents);

if (configTest.containsKey('valid') && configTest['valid'] == false) {
expect(
() => YamlConfig.fromYaml(yaml.contents as YamlMap,
filename: p.basename(file.path)),
throwsA(isA<TypeError>()));
} else {
// validate
final config = YamlConfig.fromYaml(yaml.contents as YamlMap,
filename: p.basename(file.path));

expect(config.name, equals(configTest['name']));
expect(config.input, equals(configTest['input']));
expect(config.singleFileOutput, equals(configTest['single']));
}
});
}
});
});
}
Loading