From 38c2f0ae438c4878b070981e87df29150db00f5d Mon Sep 17 00:00:00 2001 From: Diego Esmerio Date: Sun, 15 May 2022 01:51:05 -0300 Subject: [PATCH] Proof of concept files --- .gitignore | 8 + CHANGELOG.md | 3 + analysis_options.yaml | 30 ++++ bin/fsgen.dart | 16 ++ lib/fsgen.dart | 129 +++++++++++++++ pubspec.lock | 369 ++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 17 ++ test/fsgen_test.dart | 6 + 8 files changed, 578 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 analysis_options.yaml create mode 100644 bin/fsgen.dart create mode 100644 lib/fsgen.dart create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/fsgen_test.dart diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10f66fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Files and directories created by pub. +.dart_tool/ +.packages +# Conventional directory for build output. +build/ +# Extra files +.idea +doc \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b78d64c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +- Initial version. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/bin/fsgen.dart b/bin/fsgen.dart new file mode 100644 index 0000000..d0dc95f --- /dev/null +++ b/bin/fsgen.dart @@ -0,0 +1,16 @@ +import 'package:dartdoc/options.dart'; +import 'package:fsgen/fsgen.dart' as fsgen; +import 'package:dartdoc/dartdoc.dart'; + +Future main(List arguments) async { + var config = parseOptions(pubPackageMetaProvider, arguments); + if (config == null) { + return; + } + final packageConfigProvider = PhysicalPackageConfigProvider(); + final packageBuilder = + PubPackageBuilder(config, pubPackageMetaProvider, packageConfigProvider); + final dartdoc = Dartdoc.withEmptyGenerator(config, packageBuilder); + dartdoc.generator = fsgen.FsGenerator(); + dartdoc.executeGuarded(); +} \ No newline at end of file diff --git a/lib/fsgen.dart b/lib/fsgen.dart new file mode 100644 index 0000000..6e0fcb5 --- /dev/null +++ b/lib/fsgen.dart @@ -0,0 +1,129 @@ +import 'package:dartdoc/dartdoc.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/element.dart'; + +class Dependency { + final Uri libraryUri; + final String libraryName; + final String libraryVersion; + + Dependency(this.libraryUri, this.libraryName, this.libraryVersion); + + @override + bool operator ==(Object other) => + other is Dependency && + (libraryUri == other.libraryUri && + libraryName == other.libraryName && + libraryVersion == other.libraryVersion); + + @override + int get hashCode => Object.hash(libraryUri, libraryName, libraryVersion); + + @override + String toString() { + return 'Dependency{libraryUri: $libraryUri, libraryName: $libraryName, libraryVersion: $libraryVersion}'; + } +} + +class FsGenerator implements Generator { + Dependency? dependency( + LibraryElement? elementType, PackageGraph packageGraph) { + if (elementType == null) return null; + final metadata = packageGraph.packageMetaProvider + .fromElement(elementType, packageGraph.config.sdkDir); + return Dependency(elementType.source.uri, metadata!.name, metadata.version); + } + + String extractNamedParams(Iterable parameters) { + var positionalUntil = -1; + for (final p in parameters) { + positionalUntil += 1; + if (p.isNamed) { + break; + } + } + return positionalUntil != -1 && positionalUntil != parameters.length - 1? '[] ' : ''; + } + String renderParams(Iterable parameters) { + return '(${parameters.isEmpty ? '' : parameters.map((e) => '${e.isRequiredNamed || e.isRequiredPositional ? '' : '?'}${e.name} : ${e is TypeParameterElementType ? "'":''}${e.type.element?.name ?? e.type.alias?.element.name ?? e.type.getDisplayString(withNullability: true)}').reduce((value, element) => '$value, $element')})'; + } + + String renderGenericArgs(Iterable genericParams) => genericParams + .isEmpty + ? '' + : '<${genericParams.map((e) => "${e.kind == ElementKind.TYPE_PARAMETER ? "'":''}${e.name}").reduce((value, element) => '$value, $element')}>'; + + @override + Future generate(PackageGraph packageGraph, FileWriter writer) async { + for (final package in packageGraph.packages) { + var buffer = StringBuffer(); + + for (final lib in package.libraries) { + if (!lib.isPublic) continue; + buffer.writeln('module rec ``${lib.name}``='); + final currentLibUri = lib.element.source.uri; + final dependencies = Set.identity(); + for (final libEl in lib.element.importedLibraries) { + if (libEl.isPrivate) continue; + final dep = dependency(libEl, packageGraph); + if (dep != null && dep.libraryUri != currentLibUri) { + dependencies.add(dep); + } + } + for (final clazz in lib.classes) { + if (!clazz.isCanonical) continue; + final genericParams = clazz.typeParameters; + final renderedClassGenerics = + renderGenericArgs(genericParams.map((e) => e.element!)); + var headerPrinted = false; + var moreThanHeader = false; + var defaultConstructor = clazz.unnamedConstructor; + final superClazz = (clazz.supertype?.isPublic ?? false) ? clazz.supertype : null; + final superGenerics = renderGenericArgs( + superClazz?.typeArguments.where((e) => e.type.element != null) + .map((e) => e.type.element!) ?? []); + buffer.writeln(' ['); + if (defaultConstructor != null) { + headerPrinted = true; + final renderedParams = renderParams(defaultConstructor.parameters.map((e) => e.element!)); + buffer.writeln( + ' type ${clazz.name}$renderedClassGenerics ${extractNamedParams(defaultConstructor.parameters.map((e) => e.element!))} $renderedParams ='); + } + if (superClazz != null) { + moreThanHeader = true; + final args = ((defaultConstructor?.element as ConstructorElementImpl?)?.superConstructor?.parameters.length ?? 0); + buffer.writeln(' inherit ${superClazz.name}$superGenerics(${args == 0 ? '' : List.generate(args, (_) => 'jsNative') + .reduce((value, element) => '$value, $element')})'); + } + + if (!headerPrinted) { + headerPrinted = true; + buffer.writeln(' type ${clazz.name}$renderedClassGenerics ='); + } + for (final constructor in clazz.constructors + .where((element) => !element.isUnnamedConstructor)) { + if (!constructor.isCanonical) continue; + final renderedParams = renderParams(constructor.parameters.map((e) => e.element!)); + moreThanHeader = true; + final treated = constructor.name.substring(clazz.name.length + 1); + buffer.writeln( + ' ${extractNamedParams(constructor.parameters.map((e) => e.element!))}${treated != 'new' ? 'static member' : ''} $treated$renderedParams : ${clazz.name}$renderedClassGenerics = jsNative'); + //print('SDK? ${lib.isInSdk} - $package - ${package.packageMeta.version} - ${lib.element.source.uri} - $lib - $clazz - ${clazz.supertype ?? 'No super'} - $constructor'); + } + + if (!moreThanHeader) { + buffer.writeln(' class end'); + } + buffer.writeln(); + } + + writer.write('${lib.hashCode}.fs', buffer.toString()); + + print('$lib dependencies:'); + for (final d in dependencies) { + print(d); + } + } + } + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..d9809ee --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,369 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "38.0.0" + analyzer: + dependency: "direct main" + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "3.4.1" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.1" + dartdoc: + dependency: "direct main" + description: + name: dartdoc + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + lints: + dependency: "direct dev" + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + markdown: + dependency: transitive + description: + name: markdown + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.21.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.9" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.13" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "8.3.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.17.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..3943a6e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,17 @@ +name: fsgen +description: Generate F# bindings for Fable . +version: 0.0.1 +# homepage: https://www.example.com + +environment: + sdk: '>=2.17.0 <3.0.0' + +# dependencies: +# path: ^1.8.0 + +dev_dependencies: + lints: ^2.0.0 + test: ^1.16.0 +dependencies: + dartdoc: ^5.0.1 + analyzer: ^3.4.1 diff --git a/test/fsgen_test.dart b/test/fsgen_test.dart new file mode 100644 index 0000000..1fbe889 --- /dev/null +++ b/test/fsgen_test.dart @@ -0,0 +1,6 @@ +import 'package:fsgen/fsgen.dart'; +import 'package:test/test.dart'; + +void main() { + +}