diff --git a/.github/workflows/js_interop.yaml b/.github/workflows/js_interop.yaml index 1e8e5adf..b925d6c9 100644 --- a/.github/workflows/js_interop.yaml +++ b/.github/workflows/js_interop.yaml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - sdk: [3.9, beta, dev] + sdk: ['3.10', beta, dev] test_config: ['-p chrome', '-p chrome -c dart2wasm', '-p node'] steps: @@ -38,4 +38,7 @@ jobs: - run: dart format --output=none --set-exit-if-changed . if: ${{ matrix.sdk == 'dev' }} - run: dart analyze --fatal-infos + if: ${{ matrix.sdk == 'dev' }} + - run: dart analyze + if: ${{ matrix.sdk != 'dev' }} - run: dart test ${{ matrix.test_config }} diff --git a/.github/workflows/js_interop_gen.yaml b/.github/workflows/js_interop_gen.yaml new file mode 100644 index 00000000..1d486215 --- /dev/null +++ b/.github/workflows/js_interop_gen.yaml @@ -0,0 +1,53 @@ +name: package:js_interop_gen +permissions: read-all + +on: + # Run CI on pushes to the main branch and on PRs. + push: + branches: [ main ] + paths: + - '.github/workflows/js_interop_gen.yaml' + - 'js_interop_gen/**' + pull_request: + paths: + - '.github/workflows/js_interop_gen.yaml' + - 'js_interop_gen/**' + schedule: + - cron: "0 0 * * 0" + +defaults: + run: + working-directory: js_interop_gen/ + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: ['3.10', dev] + test_config: ['', '-p chrome', '-p chrome -c dart2wasm'] + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - uses: actions/setup-node@v6 + with: + node-version: 22 + - uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 + with: + sdk: ${{ matrix.sdk }} + + - run: dart pub get + - name: Install npm dependencies in js_interop_gen + run: npm install + working-directory: js_interop_gen/lib/src + - name: Mark npm install as done + run: touch .last_npm_install + working-directory: js_interop_gen/lib/src + - run: dart format --output=none --set-exit-if-changed . + if: matrix.sdk == 'dev' && matrix.test_config == '' + - run: dart analyze --fatal-infos + if: matrix.sdk == 'dev' && matrix.test_config == '' + - run: dart analyze + if: matrix.sdk != 'dev' && matrix.test_config == '' + - run: dart test ${{ matrix.test_config }} diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index af236163..129ffcad 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - sdk: [3.4, beta, dev] + sdk: ['3.10', beta, dev] test_config: ['-p chrome', '-p chrome -c dart2wasm'] steps: @@ -38,6 +38,9 @@ jobs: - run: dart format --output=none --set-exit-if-changed . if: ${{ matrix.sdk == 'dev' }} - run: dart analyze --fatal-infos + if: ${{ matrix.sdk == 'dev' }} + - run: dart analyze + if: ${{ matrix.sdk != 'dev' }} - run: dart test ${{ matrix.test_config }} # Validate the 'dart fix' metadata. diff --git a/.github/workflows/web_generator.yaml b/.github/workflows/web_generator.yaml index b96ebfd1..3d211525 100644 --- a/.github/workflows/web_generator.yaml +++ b/.github/workflows/web_generator.yaml @@ -8,10 +8,12 @@ on: paths: - '.github/workflows/web_generator.yaml' - 'web_generator/**' + - 'js_interop_gen/**' pull_request: paths: - '.github/workflows/web_generator.yaml' - 'web_generator/**' + - 'js_interop_gen/**' schedule: - cron: "0 0 * * 0" @@ -26,7 +28,6 @@ jobs: fail-fast: false matrix: sdk: ['3.10', dev] - test_config: ['', '-p chrome', '-p chrome -c dart2wasm'] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd @@ -39,10 +40,12 @@ jobs: - run: dart pub get - run: dart format --output=none --set-exit-if-changed . - if: matrix.test_config == '' + if: matrix.sdk == 'dev' - run: dart analyze --fatal-infos - if: matrix.sdk == 'dev' && matrix.test_config == '' - - run: dart test ${{ matrix.test_config }} + if: matrix.sdk == 'dev' + - run: dart analyze + if: matrix.sdk != 'dev' + - run: dart test # test bin/update_idl_bindings.dart test_generate: @@ -52,8 +55,10 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + - run: npm install - run: dart pub get - run: dart pub -C ../web get + - run: dart pub -C ../js_interop_gen get - run: dart bin/update_idl_bindings.dart - run: dart analyze --fatal-infos ../web @@ -65,7 +70,9 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + - run: npm install - run: dart pub get - run: dart pub -C ../web get + - run: dart pub -C ../js_interop_gen get - run: dart bin/update_idl_bindings.dart --generate-all - run: dart analyze --fatal-infos ../web diff --git a/.gitignore b/.gitignore index e4377b96..c49b2d48 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ doc/api/ pubspec.lock -web_generator/lib/src/*.js -web_generator/lib/src/*.js.* -web_generator/lib/src/node_modules/ +js_interop_gen/lib/src/*.js +js_interop_gen/lib/src/*.js.* +js_interop_gen/lib/src/node_modules/ +web_generator/node_modules +.compile.lock +.last_npm_install diff --git a/README.md b/README.md index 3f73e21c..62312967 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ - - -## Packages - -| Package | Description | Version | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -| [web](web/) | Lightweight browser API bindings built around [JS interop](https://dart.dev/interop/js-interop). | [![pub package](https://img.shields.io/pub/v/web.svg)](https://pub.dev/packages/web) | -| [js_interop](js_interop/) | Utility functions and types for [`dart:js_interop`](https://api.dart.dev/dart-js_interop/) and [`dart:js_interop_unsafe`](https://api.dart.dev/dart-js_interop_unsafe/). | [![pub package](https://img.shields.io/pub/v/js_interop.svg)](https://pub.dev/packages/js_interop) | -| [web_generator](web_generator/) | Tools to generate Dart interfaces from TypeScript declaration files and Web IDL definitions. | N/A | +| Package | Description | Version | CI | +| --- | --- | --- | --- | +| [web](web/) | Lightweight browser API bindings built around [JS interop](https://dart.dev/interop/js-interop). | [![pub package](https://img.shields.io/pub/v/web.svg)](https://pub.dev/packages/web) | [![package:web](https://github.com/dart-lang/web/actions/workflows/web.yaml/badge.svg)](https://github.com/dart-lang/web/actions/workflows/web.yaml) | +| [js_interop](js_interop/) | Utility functions and types for [`dart:js_interop`](https://api.dart.dev/dart-js_interop/) and [`dart:js_interop_unsafe`](https://api.dart.dev/dart-js_interop_unsafe/). | [![pub package](https://img.shields.io/pub/v/js_interop.svg)](https://pub.dev/packages/js_interop) | [![package:js_interop](https://github.com/dart-lang/web/actions/workflows/js_interop.yaml/badge.svg)](https://github.com/dart-lang/web/actions/workflows/js_interop.yaml) | +| [js_interop_gen](js_interop_gen/) | Tools to generate Dart interfaces from TypeScript declaration files and Web IDL definitions. | N/A | [![package:js_interop_gen](https://github.com/dart-lang/web/actions/workflows/js_interop_gen.yaml/badge.svg)](https://github.com/dart-lang/web/actions/workflows/js_interop_gen.yaml) | +| [web_generator](web_generator/) | Internal tooling to generate the `web` package bindings. | N/A | [![package:web_generator](https://github.com/dart-lang/web/actions/workflows/web_generator.yaml/badge.svg)](https://github.com/dart-lang/web/actions/workflows/web_generator.yaml) | diff --git a/js_interop/pubspec.yaml b/js_interop/pubspec.yaml index 3cc5e9a0..c6f23c8f 100644 --- a/js_interop/pubspec.yaml +++ b/js_interop/pubspec.yaml @@ -4,7 +4,7 @@ description: Utility APIs for dart:js_interop and dart:js_interop_unsafe. repository: https://github.com/dart-lang/web environment: - sdk: ^3.9.0 + sdk: ^3.10.0 dev_dependencies: test: ^1.26.0 diff --git a/js_interop/test/date_test.dart b/js_interop/test/date_test.dart index 76b334be..11f8f371 100644 --- a/js_interop/test/date_test.dart +++ b/js_interop/test/date_test.dart @@ -106,7 +106,10 @@ void main() { group('instance', () { late JSDate date; - setUp(() => date = JSDate.nowAsDate()); + setUp(() { + date = JSDate.nowAsDate(); + date.date = 1; + }); test('date', () { date.date = 12; @@ -196,6 +199,7 @@ void main() { test('toUtcString:', () => expect(date.toUtcString(), isA())); test('toDart', () { + var date = JSDate.nowAsDate(); var dartDate = date.toDart; expect(dartDate, isA()); expect(dartDate.timeZoneName, equals(DateTime.now().timeZoneName)); diff --git a/web_generator/CHANGELOG.md b/js_interop_gen/CHANGELOG.md similarity index 82% rename from web_generator/CHANGELOG.md rename to js_interop_gen/CHANGELOG.md index a278ca4d..550171ec 100644 --- a/web_generator/CHANGELOG.md +++ b/js_interop_gen/CHANGELOG.md @@ -1,6 +1,5 @@ ## 1.0.0-wip -- Initial separation of `web_generator` from `web`. - New IDL interface `RHL` added. `ExtendedAttribute` idl interface updated to expose its `rhs` property and `Interfacelike` idl interface updated to expose `extAttrs` property. The generator now adds a @@ -9,4 +8,4 @@ description. - Added `--generate-all` option to generate all bindings, including experimental and non-standard APIs. -- Adapt paths for different development environments. \ No newline at end of file +- Adapt paths for different development environments. diff --git a/web_generator/LICENSE b/js_interop_gen/LICENSE similarity index 100% rename from web_generator/LICENSE rename to js_interop_gen/LICENSE diff --git a/js_interop_gen/README.md b/js_interop_gen/README.md new file mode 100644 index 00000000..9414eb1d --- /dev/null +++ b/js_interop_gen/README.md @@ -0,0 +1,112 @@ +Tools to generate Dart interfaces from TypeScript declaration files and Web IDL definitions. + +## Using this +The tools to generate bindings are written in Dart, compiled to JavaScript, and +run on Node. + +There is one entrypoint present in this package: +- `js_interop_gen.dart`: This entrypoint is for generating Dart interfaces from TS Declaration code, given the path to a `.d.ts` file + +### Requirements +#### Node.js +The js_interop_gen requires **Node.js v22 or newer**. +Older Node versions are not supported and may result in runtime failures. + +## TS Declarations +To generate Dart interfaces for a given `.d.ts` file, run the following at the root of this package: +```shell +dart run js_interop_gen +dart run js_interop_gen -o +``` + +If multiple files are passed, the output option is regarded as an output directory instead. + +For more information on the command-line options you can pass alongside, you can check the help information +```shell +dart run js_interop_gen --help +``` + +### Configuration +The generator also has support for configuring the output of the generator, allowing support for configuring features like: variadic argument count, preamble (if any), TS Compiler Options, etc. + +These configuration options can either be passed from the command line, or via a YAML configuration file. To pass a configuration file, pass the `--config` option. + +Given a sample configuration file: +```yaml +input: a.d.ts +output: b.d.ts +ts-config-file: tsconfig.json +``` + +The following are equivalent +```shell +dart run js_interop_gen -o b.d.ts --ts-config tsconfig.json a.d.ts +dart run js_interop_gen --config config.yaml +``` + +Note that not all configuration options are direct mappings between the CLI and the configuration file. + +### Configuration File Reference + +| Option | Description | Example | +|--------|-------------|---------| +| `name` | The name of the bindings |
`name: MyBindings`
| +| `description` | A description of the bindings (optional) |
`description: My awesome bindings`
| +| `preamble` | Preamble text to insert before the bindings (optional) |
`preamble: \|`
` // Generated. Do not edit.`
| +| `input` | A file (single string) or set of files (array of strings) passed into the generator |
`input: bindings.d.ts`

or
`input: `
` - bindings.d.ts`
| +| `output` | The output file or directory to write the bindings to |
`output: lib/src/js`
| +| `include` | Declarations to include in the generated output (as a list). Can either be passed as a raw string to match the full name, or as a regular expression. By default, the generator outputs **all exported declarations** |
`include: `
` - myNumber`
| +| `language_version` | The Dart Language Version to use, usually for formatting (optional) |
`language_version: 3.6.0`
| +| `ts_config` | An object consisting of TS Configurations following the [tsconfig.json file schema](https://www.typescriptlang.org/tsconfig/) used for configuring the TypeScript Program/Compiler (optional) |
`ts_config: `
` compilerOptions: `
` target: es2020`
| +| `ts_config_file` | The TS Configuration file (tsconfig.json) if any (optional) |
`ts_config_file: tsconfig.json`
| +| `generate_all` | Include generating declarations for code that isn't exported. Defaults to false |
`generate_all: true`
| +| `ignore_errors` | Ignore source code warnings and errors (they will still be printed). Defaults to false |
`ignore_errors: true`
| +| `functions.varargs` | The number of arguments that variable-argument functions should take. Defaults to 4 |
`functions: `
` varargs: 6`
| + +### Conventions + +The generator scripts use a number of conventions to consistently handle TS +definitions: + +#### Top Level Declarations +- Top level declarations are handled as top level dart `external` declarations annotated with `@JS`. + +#### Enums +- Enums are represented as extension types with static members. +- In most cases, values are known beforehand, so the representation types of the resulting extension types are Dart primitive types with non-external static members. +- If the value for an enum is not given, then the representation type is an interop type, and any members without a value are marked as `external` + +#### Interfaces and Classes +- Interfaces and Classes are emitted as extension types that wrap and implement `JSObject` +- Interface and Class inheritance is maintained using `implements` between extension types +- Classes have default constructors if none are provided +- Interface and Classes support members such as: properties, functions, operators, `call` declarations, construct signatures, getters and setters. +- Readonly properties are represented as getters. +- Overriding signatures are annotated with [`@redeclare`](https://pub.dev/documentation/meta/latest/meta/redeclare-constant.html). +- Multiple instances of interfaces in a given scope are merged together into a single interface. Also supports merging with `var` declarations. + +#### Namespaces +- Namespaces in TS are converted to extension types on `JSObject` with static members. +- Typed declarations (such as enums, classes, interfaces and namespaces) are prefixed and generated in the general module space. +- Constructor calls for classes are generated for every class in a namespace as a static redirect method. +- Namespaces nested inside the namespace are as well generated as static getters on the namespace, which are references to the namespaces themselves. +- Supports overloading and declaration merging with classes, interfaces, enums, `var` declarations, and functions. + +#### Types +- Supports mapping basic JS types to Dart `js_interop` types. +- `never` type returned from a const declaration, function, readonly property or method has the declaration annotated with [`@doNotStore`](https://pub.dev/documentation/meta/latest/meta/doNotStore-constant.html). +- Supports automatically mapping web types to `package:web` types +- Anonymous Unions/Intersections: Anonymous unions and intersections are represented as extension types with cast members to cast the value as a type in the union/intersection. If the union is homogenous and contains strings/numbers, then it is generated more like an enum, with specific static values. An intersection with `null` or `undefined` equals `never` +- Anonymous Objects: Anonymous objects are represented similar to interface declarations, with an object literal constructor. +- Anonymous Closures and Construct Signatures: Anonymous closures and construct signatures are represented as extension types on `JSFunction` overriding the `call` method. This allows for a stronger type signature than just `JSFunction`. While closures leave their calls as `external`, construct signatures just redirect to the class constructor. +- Generic types are represented as normal Dart generics, and they are constrained by `JSAny` by default. +- `typeof` declarations are references to the type of the declaration, which are: + - Variable: The variable's type + - Enum: An object representation of the enum (in order to support `keyof` correctly). + - Function: `JSFunction` + - Otherwise, the extension type representation of the type that it refers to is used. +- `keyof` declarations are represented as unions of the keys for the operand type. + +#### Other +- Supports importing/exporting declarations. If the files that are being imported are included in input, then such references are treated as imports in Dart, else the declarations are generated in-place. +- Supports documentation (JSDoc), and maps some annotations to Dart annotations such as `@deprecated` and `@experimental` diff --git a/web_generator/analysis_options.yaml b/js_interop_gen/analysis_options.yaml similarity index 100% rename from web_generator/analysis_options.yaml rename to js_interop_gen/analysis_options.yaml diff --git a/js_interop_gen/bin/js_interop_gen.dart b/js_interop_gen/bin/js_interop_gen.dart new file mode 100644 index 00000000..e952de3c --- /dev/null +++ b/js_interop_gen/bin/js_interop_gen.dart @@ -0,0 +1,261 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:io/ansi.dart' as ansi; +import 'package:io/io.dart'; +import 'package:js_interop_gen/src/cli.dart'; +import 'package:js_interop_gen/src/sdk_version.dart'; +import 'package:path/path.dart' as p; +import 'package:source_map_stack_trace/source_map_stack_trace.dart'; +import 'package:source_maps/parser.dart' as sm; +import 'package:stack_trace/stack_trace.dart'; + +void main(List arguments) async { + final ArgResults argResult; + + try { + argResult = _parser.parse(arguments); + } on FormatException catch (e) { + print(''' +${ansi.lightRed.wrap(e.message)} + +$_usage'''); + exitCode = ExitCode.usage.code; + return; + } + + if (argResult.flag('help')) { + print(_usage); + return; + } + + if (argResult.rest.isEmpty && !argResult.wasParsed('config')) { + print(''' +${ansi.lightRed.wrap('At least one argument is needed')} + +$_usage'''); + exitCode = ExitCode.usage.code; + return; + } + + try { + checkSdkVersion(p.current); + } on SdkVersionException catch (e) { + print(ansi.lightRed.wrap(e.message)); + exitCode = ExitCode.config.code; + return; + } + + assert(p.fromUri(Platform.script).endsWith(_thisScript.toFilePath())); + + // Run `npm install` or `npm update` as needed. + final update = argResult.flag('update'); + await runProc('npm', [ + update ? 'update' : 'install', + ], workingDirectory: bindingsGeneratorPath); + + final contextFile = await createJsTypeSupertypeContext(); + + try { + // Compute JS type supertypes for union calculation in translator. + await computeJsTypeSupertypes(); + + if (argResult.flag('compile')) { + // Compile Dart to Javascript. + await compileDartMain(); + } + + final inputFiles = argResult.rest; + if (inputFiles.isEmpty) { + print('Pass an input file to get started'); + print(_usage); + exit(1); + } + final specifiedOutput = argResult.option('output'); + final outputFile = + specifiedOutput ?? + (inputFiles.length > 1 + ? p.join(p.current, inputFiles.first.replaceAll('.d.ts', '.dart')) + : p.join( + p.current, + inputFiles.single.replaceAll('.d.ts', '.dart'), + )); + final defaultWebGenConfigPath = p.join(p.current, 'webgen.yaml'); + final configFile = + argResult.option('config') ?? + (File(defaultWebGenConfigPath).existsSync() + ? defaultWebGenConfigPath + : null); + final relativeConfigFile = configFile != null + ? p.relative(configFile, from: bindingsGeneratorPath) + : null; + final relativeOutputPath = p.relative( + outputFile, + from: bindingsGeneratorPath, + ); + final tsConfigPath = argResult.option('ts-config'); + final tsConfigRelativePath = tsConfigPath != null + ? p.relative(tsConfigPath, from: bindingsGeneratorPath) + : null; + // Run app with `node`. + final process = await runProcWithResult('node', [ + 'main.mjs', + '--declaration', + if (argResult.rest.isNotEmpty) ...[ + ...inputFiles.map( + (i) => '--input=${p.relative(i, from: bindingsGeneratorPath)}', + ), + '--output=$relativeOutputPath', + ], + if (tsConfigRelativePath case final tsConfig?) '--ts-config=$tsConfig', + if (relativeConfigFile case final config?) '--config=$config', + if (argResult.wasParsed('ignore-errors')) '--ignore-errors', + if (argResult.wasParsed('generate-all')) '--generate-all', + if (argResult.wasParsed('strict-unsupported')) '--strict-unsupported', + ], workingDirectory: bindingsGeneratorPath); + + final stderrFuture = process.stderr.transform(utf8.decoder).join('\n'); + process.stdout.transform(utf8.decoder).listen(stdout.write); + + final processExitCode = await process.exitCode; + final processStderr = await stderrFuture; + + if (processExitCode != 0) { + if (argResult['raw-stack-traces'] as bool || + processStderr.contains('ParseError')) { + // only use raw stack traces + stderr.write(processStderr); + exit(processExitCode); + } + + // split stderr along '=' line + final parts = processStderr.split('=' * 50); + if (parts.length != 2) { + stderr.write('Unexpected error format from JS process. Expected separator not found or found multiple times.\n'); + stderr.write(processStderr); + exit(processExitCode); + } + final [parseStderr, transformStderr] = parts; + + // read map file + final jsMapFile = p.join(bindingsGeneratorPath, 'dart_main.js.map'); + final jsMap = await File(jsMapFile).readAsString(); + + // produce stack traces + final jsMapping = sm.parse(jsMap); + final jsTrace = Trace.parse(transformStderr); + + // TODO: Is there a good way of mapping frames from node_modules or + // npm packages? + final trace = mapStackTrace(jsMapping, jsTrace); + Chain.capture(() => throw Exception(trace)); + } + } finally { + await contextFile.delete(); + } + + final inputFiles = argResult.rest; + if (inputFiles.isEmpty) { + print('Pass an input file to get started'); + print(_usage); + exitCode = ExitCode.usage.code; + return; + } + final specifiedOutput = argResult.option('output'); + final outputFile = + specifiedOutput ?? + (inputFiles.length > 1 + ? p.join(p.current, inputFiles.first.replaceAll('.d.ts', '.dart')) + : p.join(p.current, inputFiles.single.replaceAll('.d.ts', '.dart'))); + final defaultWebGenConfigPath = p.join(p.current, 'webgen.yaml'); + final configFile = + argResult.option('config') ?? + (File(defaultWebGenConfigPath).existsSync() + ? defaultWebGenConfigPath + : null); + final relativeConfigFile = configFile != null + ? p.relative(configFile, from: bindingsGeneratorPath) + : null; + final relativeOutputPath = p.relative( + outputFile, + from: bindingsGeneratorPath, + ); + final tsConfigPath = argResult.option('ts-config'); + final tsConfigRelativePath = tsConfigPath != null + ? p.relative(tsConfigPath, from: bindingsGeneratorPath) + : null; + // Run app with `node`. + await runNode([ + 'main.mjs', + '--declaration', + if (argResult.rest.isNotEmpty) ...[ + ...inputFiles.map( + (i) => '--input=${p.relative(i, from: bindingsGeneratorPath)}', + ), + '--output=$relativeOutputPath', + ], + if (tsConfigRelativePath case final tsConfig?) '--ts-config=$tsConfig', + if (relativeConfigFile case final config?) '--config=$config', + if (argResult.wasParsed('ignore-errors')) '--ignore-errors', + if (argResult.wasParsed('generate-all')) '--generate-all', + if (argResult.wasParsed('strict-unsupported')) '--strict-unsupported', + ], workingDirectory: bindingsGeneratorPath); + + return; +} + +final _thisScript = Uri.parse('bin/js_interop_gen.dart'); + +final _usage = + ''' +${ansi.styleBold.wrap('Dart Interop Gen')}: +$_thisScript dts <.d.ts file> [options] + +Usage: +${_parser.usage}'''; + +final _parser = ArgParser() + ..addFlag('help', negatable: false, help: 'Show help information') + ..addFlag('update', abbr: 'u', help: 'Update npm dependencies') + ..addFlag('compile', defaultsTo: true) + ..addOption( + 'output', + abbr: 'o', + help: 'The output path to generate the Dart interface code', + ) + ..addOption( + 'ts-config', + help: + 'Path to TS Configuration Options File (tsconfig.json) to pass' + ' to the parser/transformer', + ) + ..addFlag('ignore-errors', help: 'Ignore Generator Errors', negatable: false) + ..addFlag( + 'generate-all', + help: + 'Generate all declarations ' + '(including private declarations)', + negatable: false, + ) + ..addFlag( + 'strict-unsupported', + help: + 'Treat unsupported declarations/types as errors. Only used for development of the generator', + negatable: false, + hide: true, + ) + ..addOption( + 'config', + hide: true, + abbr: 'c', + help: 'The configuration file to use for this tool', + ) + ..addFlag( + 'raw-stack-traces', + help: 'Provide raw stack traces from the JS runtime', + ); diff --git a/web_generator/lib/src/ast/base.dart b/js_interop_gen/lib/src/ast/base.dart similarity index 100% rename from web_generator/lib/src/ast/base.dart rename to js_interop_gen/lib/src/ast/base.dart diff --git a/web_generator/lib/src/ast/builtin.dart b/js_interop_gen/lib/src/ast/builtin.dart similarity index 99% rename from web_generator/lib/src/ast/builtin.dart rename to js_interop_gen/lib/src/ast/builtin.dart index babe03c7..2a374d10 100644 --- a/web_generator/lib/src/ast/builtin.dart +++ b/js_interop_gen/lib/src/ast/builtin.dart @@ -7,7 +7,6 @@ import 'package:code_builder/code_builder.dart'; import '../interop_gen/namer.dart'; -import '../web_rename_map.dart'; import 'base.dart'; /// A built in type supported by `dart:js_interop` or by this library @@ -223,6 +222,7 @@ class PackageWebType extends NamedType { String name, { bool? isNullable, List typeParams = const [], + Map renameMap = const {}, }) { return PackageWebType._( name: renameMap.containsKey(name) ? renameMap[name]! : name, diff --git a/web_generator/lib/src/ast/declarations.dart b/js_interop_gen/lib/src/ast/declarations.dart similarity index 100% rename from web_generator/lib/src/ast/declarations.dart rename to js_interop_gen/lib/src/ast/declarations.dart diff --git a/web_generator/lib/src/ast/documentation.dart b/js_interop_gen/lib/src/ast/documentation.dart similarity index 100% rename from web_generator/lib/src/ast/documentation.dart rename to js_interop_gen/lib/src/ast/documentation.dart diff --git a/web_generator/lib/src/ast/helpers.dart b/js_interop_gen/lib/src/ast/helpers.dart similarity index 100% rename from web_generator/lib/src/ast/helpers.dart rename to js_interop_gen/lib/src/ast/helpers.dart diff --git a/web_generator/lib/src/ast/merger.dart b/js_interop_gen/lib/src/ast/merger.dart similarity index 100% rename from web_generator/lib/src/ast/merger.dart rename to js_interop_gen/lib/src/ast/merger.dart diff --git a/web_generator/lib/src/ast/types.dart b/js_interop_gen/lib/src/ast/types.dart similarity index 100% rename from web_generator/lib/src/ast/types.dart rename to js_interop_gen/lib/src/ast/types.dart diff --git a/web_generator/lib/src/banned_names.dart b/js_interop_gen/lib/src/banned_names.dart similarity index 100% rename from web_generator/lib/src/banned_names.dart rename to js_interop_gen/lib/src/banned_names.dart diff --git a/web_generator/lib/src/bcd.dart b/js_interop_gen/lib/src/bcd.dart similarity index 90% rename from web_generator/lib/src/bcd.dart rename to js_interop_gen/lib/src/bcd.dart index 9e5c8eb9..bca37405 100644 --- a/web_generator/lib/src/bcd.dart +++ b/js_interop_gen/lib/src/bcd.dart @@ -5,8 +5,6 @@ import 'dart:convert' hide json; import 'dart:js_interop'; -import 'package:path/path.dart' as p; - import 'js/filesystem_api.dart'; /// A class to read from the browser-compat-data files and parse interface and @@ -20,25 +18,21 @@ class BrowserCompatData { static bool isEventHandlerSupported(String name) => _eventHandlers[name]?.any((bcd) => bcd.shouldGenerate) == true; - static BrowserCompatData read({required bool generateAll}) { - final path = p.join( - 'node_modules', - '@mdn', - 'browser-compat-data', - 'data.json', - ); + static BrowserCompatData read({ + required bool generateAll, + required String path, + }) { final content = (fs.readFileSync(path.toJS, JSReadFileOptions(encoding: 'utf8'.toJS)) as JSString) .toDart; final contentMap = jsonDecode(content) as Map; - final api = contentMap['api'] as Map; + final api = (contentMap['api'] as Map).cast(); // MDN files WebAssembly compat data in a separate folder, so we need to // unify. - final webassembly = - (contentMap['webassembly'] as Map)['api'] - as Map; + final webassembly = ((contentMap['webassembly'] as Map)['api'] as Map) + .cast(); api.addAll(webassembly); // Add info for the namespace as well. api['WebAssembly'] = webassembly; @@ -155,11 +149,13 @@ abstract class BCDItem { BCDItem(this.name, this.json); - Map get _compat => json['__compat'] as Map; - String get _sourceFile => _compat['source_file'] as String; - Map get _status => _compat['status'] as Map; + Map get _compat => + (json['__compat'] as Map? ?? {}).cast(); + String get _sourceFile => _compat['source_file'] as String? ?? ''; + Map get _status => + (_compat['status'] as Map? ?? {}).cast(); Map get _support => - _compat['support'] as Map; + (_compat['support'] as Map? ?? {}).cast(); bool get deprecated => _status['deprecated'] as bool? ?? false; bool get experimental => _status['experimental'] as bool? ?? false; diff --git a/js_interop_gen/lib/src/cli.dart b/js_interop_gen/lib/src/cli.dart new file mode 100644 index 00000000..76f47e10 --- /dev/null +++ b/js_interop_gen/lib/src/cli.dart @@ -0,0 +1,250 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; +import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:io/ansi.dart' as ansi; +import 'package:package_config/package_config.dart'; + +import 'package:path/path.dart' as p; + +final bindingsGeneratorPath = p.fromUri( + Isolate.resolvePackageUriSync(Uri.parse('package:js_interop_gen/src')), +); + +final _webGeneratorRoot = p.dirname(p.dirname(bindingsGeneratorPath)); + +Future getPackageLanguageVersion(String pkgPath) async { + final packageConfig = await findPackageConfig(Directory(pkgPath)); + if (packageConfig == null) { + throw StateError('No package config for "$pkgPath"'); + } + final package = packageConfig.packageOf( + Uri.file(p.join(pkgPath, 'pubspec.yaml')), + ); + if (package == null) { + throw StateError('No package at "$pkgPath"'); + } + final languageVersion = package.languageVersion; + if (languageVersion == null) { + throw StateError('No language version "$pkgPath"'); + } + // Force a minimum of 3.10 for stable formatting of extension types. + final major = languageVersion.major; + final minor = languageVersion.minor; + if (major < 3 || (major == 3 && minor < 10)) { + return '3.10.0'; + } + return '$languageVersion.0'; +} + +Future compileDartMain({String? langVersion, String? dir}) async { + langVersion ??= await getPackageLanguageVersion(_webGeneratorRoot); + await runProc(Platform.executable, [ + 'compile', + 'js', + '--enable-asserts', + '--server-mode', + '-DlanguageVersion=$langVersion', + 'dart_main.dart', + '-o', + 'dart_main.js', + ], workingDirectory: dir ?? bindingsGeneratorPath); +} + +Future runNode( + List arguments, { + required String workingDirectory, + bool detached = false, +}) async { + await runProc( + 'node', + ['--enable-source-maps', ...arguments], + workingDirectory: workingDirectory, + detached: detached, + ); +} + +Future runNodeWithResult( + List arguments, { + required String workingDirectory, +}) async { + return runProcWithResult('node', [ + '--enable-source-maps', + ...arguments, + ], workingDirectory: workingDirectory); +} + +Future runProcWithResult( + String executable, + List arguments, { + required String workingDirectory, +}) async { + print(ansi.styleBold.wrap(['*', executable, ...arguments].join(' '))); + return Process.start( + executable, + arguments, + runInShell: Platform.isWindows, + workingDirectory: workingDirectory, + ); +} + +Future runProc( + String executable, + List arguments, { + required String workingDirectory, + bool detached = false, +}) async { + print(ansi.styleBold.wrap(['*', executable, ...arguments].join(' '))); + final proc = await Process.start( + executable, + arguments, + mode: detached ? ProcessStartMode.detached : ProcessStartMode.normal, + runInShell: Platform.isWindows, + workingDirectory: workingDirectory, + ); + if (!detached) { + proc.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(print); + proc.stderr + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((line) => print(ansi.red.wrap(line) ?? line)); + } + final procExit = await proc.exitCode; + if (procExit != 0) { + throw ProcessException(executable, arguments, 'Process failed', procExit); + } +} + +Future createJsTypeSupertypeContext() async { + final contextFile = await File( + p.join(bindingsGeneratorPath, '_js_supertypes_src.dart'), + ).create(); + await contextFile.writeAsString(''' +import 'dart:js_interop'; + +@JS() +external JSPromise get promise; +'''); + return contextFile; +} + +/// Generates a map of the JS type hierarchy defined in `dart:js_interop` that's +/// used by both translators. +/// Computes the JS type hierarchy defined in `dart:js_interop` as a Dart +/// script. +Future computeJsTypeSupertypes() async { + final contextFile = await createJsTypeSupertypeContext(); + try { + // Use a file that uses `dart:js_interop` for analysis. + final contextCollection = AnalysisContextCollection( + includedPaths: [contextFile.path], + ); + final session = contextCollection.contexts.single.currentSession; + final result = await session.getLibraryByUri('dart:js_interop'); + final dartJsInterop = (result as LibraryElementResult).element; + final definedNames = dartJsInterop.exportNamespace.definedNames2; + // `SplayTreeMap` to avoid moving types around in `dart:js_interop` + // affecting the code generation. + final jsTypeSupertypes = SplayTreeMap(); + for (final name in definedNames.keys) { + final element = definedNames[name]; + if (element is ExtensionTypeElement) { + // JS types are any extension type that starts with 'JS' in + // `dart:js_interop`. + bool isJSType(InterfaceElement element) => + element is ExtensionTypeElement && + element.library == dartJsInterop && + element.name!.startsWith('JS'); + if (!isJSType(element)) continue; + + String? parentJsType; + final supertype = element.supertype; + final immediateSupertypes = [ + ?supertype, + ...element.interfaces, + ]..removeWhere((supertype) => supertype.isDartCoreObject); + // We should have at most one non-trivial supertype. + assert(immediateSupertypes.length <= 1); + for (final supertype in immediateSupertypes) { + if (isJSType(supertype.element)) { + parentJsType = "'${supertype.element.name!}'"; + } + } + // Ensure that the hierarchy forms a tree. + assert((parentJsType == null) == (name == 'JSAny')); + jsTypeSupertypes["'$name'"] = parentJsType; + } + } + + return ''' +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Generated code. Do not modify by hand. +// Generated from Dart SDK ${Platform.version.split(' ').first} +// To update run: dart run js_interop_gen/tool/update_supertypes.dart + +const Map jsTypeSupertypes = { +${jsTypeSupertypes.entries.map((e) => " ${e.key}: ${e.value},").join('\n')} +}; +'''; + } finally { + await contextFile.delete(); + } +} + +/// Checks if `js_type_supertypes.dart` needs to be updated and warns if so. +Future checkJsTypeSupertypes() async { + final jsTypeSupertypesScript = await computeJsTypeSupertypes(); + final jsTypeSupertypesPath = p.join( + bindingsGeneratorPath, + 'js_type_supertypes.dart', + ); + + final file = File(jsTypeSupertypesPath); + if (file.existsSync()) { + final currentContent = file.readAsStringSync(); + final sdkLineRegex = RegExp( + r'^// Generated from Dart SDK.*$', + multiLine: true, + ); + if (currentContent + .replaceAll('\r\n', '\n') + .replaceAll(sdkLineRegex, '') + .trim() != + jsTypeSupertypesScript + .replaceAll('\r\n', '\n') + .replaceAll(sdkLineRegex, '') + .trim()) { + print( + ansi.yellow.wrap( + 'WARNING: js_type_supertypes.dart needs to be updated!', + ), + ); + print( + ansi.yellow.wrap( + 'Run: dart run js_interop_gen/tool/update_supertypes.dart', + ), + ); + } + } else { + print(ansi.yellow.wrap('WARNING: js_type_supertypes.dart does not exist!')); + print( + ansi.yellow.wrap( + 'Run: dart run js_interop_gen/tool/update_supertypes.dart', + ), + ); + } +} diff --git a/web_generator/lib/src/config.dart b/js_interop_gen/lib/src/config.dart similarity index 97% rename from web_generator/lib/src/config.dart rename to js_interop_gen/lib/src/config.dart index 3ff47c77..2430a9fe 100644 --- a/web_generator/lib/src/config.dart +++ b/js_interop_gen/lib/src/config.dart @@ -4,11 +4,11 @@ import 'dart:convert'; -import 'package:dart_style/dart_style.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart'; +import 'sdk_version.dart'; import 'util.dart'; class FunctionConfig { @@ -207,7 +207,7 @@ class YamlConfig implements Config { this.ignoreErrors = false, this.generateAll = false, }) : languageVersion = languageVersion == null - ? DartFormatter.latestLanguageVersion + ? dartLanguageVersion : Version.parse(languageVersion); factory YamlConfig.fromYaml( @@ -215,6 +215,7 @@ class YamlConfig implements Config { required String filename, List? input, String? output, + String? languageVersion, }) { List inputFiles; final yamlInput = yaml['input']; @@ -244,7 +245,7 @@ class YamlConfig implements Config { output: p.join(p.dirname(filename), (yaml['output'] ?? output) as String), name: yaml['name'] as String?, description: yaml['description'] as String?, - languageVersion: yaml['language_version'] as String?, + languageVersion: languageVersion ?? yaml['language_version'] as String?, preamble: yaml['preamble'] as String?, tsConfig: tsConfig != null ? jsonDecode(jsonEncode(tsConfig)) as Map diff --git a/web_generator/lib/src/dart_main.dart b/js_interop_gen/lib/src/dart_main.dart similarity index 83% rename from web_generator/lib/src/dart_main.dart rename to js_interop_gen/lib/src/dart_main.dart index 26f0c3f8..3693632f 100644 --- a/web_generator/lib/src/dart_main.dart +++ b/js_interop_gen/lib/src/dart_main.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; import 'dart:js_interop'; import 'package:args/args.dart'; @@ -32,7 +33,13 @@ void main(List args) async { final argResult = _parser.parse(args); + // TODO: Work with Dart stack traces printed in JS if (argResult.wasParsed('idl')) { + final bcdJsonPath = argResult['bcd-json'] as String?; + if (bcdJsonPath == null) { + print('Error: --bcd-json is required for IDL bindings.'); + return; + } await generateIDLBindings( input: (argResult['input'] as List).isEmpty ? null @@ -40,6 +47,8 @@ void main(List args) async { output: argResult['output'] as String, generateAll: argResult['generate-all'] as bool, languageVersion: Version.parse(languageVersionString), + idlJsonPath: argResult['idl-json'] as String?, + bcdJsonPath: bcdJsonPath, ); } else if (argResult.wasParsed('declaration')) { final Config config; @@ -56,6 +65,7 @@ void main(List args) async { config = YamlConfig.fromYaml( yaml.contents as YamlMap, filename: filename, + languageVersion: languageVersionString, ); } else { final tsConfigFile = argResult['ts-config'] as String?; @@ -121,6 +131,8 @@ Future generateIDLBindings({ required String output, required bool generateAll, required Version languageVersion, + String? idlJsonPath, + required String bcdJsonPath, }) async { if (input == null) { // parse dom library as normal @@ -128,37 +140,20 @@ Future generateIDLBindings({ ensureDirectoryExists('$output/$librarySubDir'); + final dir = p.dirname(p.fromUri(url)); + final renameMapPath = p.join(dir, 'web_rename_map.json'); final (bindings, renameMap) = await generateBindings( packageRoot, librarySubDir, generateAll: generateAll, + renameMapPath: renameMapPath, + idlJsonPath: idlJsonPath, + bcdJsonPath: bcdJsonPath, ); if (renameMap.isNotEmpty) { - final lib = code.Library( - (l) => l - ..comments.add(''' -// Copyright (c) ${DateTime.now().year}, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// AUTO GENERATED: DO NOT EDIT -''') - ..body.add( - code.Field( - (f) => f - ..name = 'renameMap' - ..type = code.refer('Map') - ..modifier = code.FieldModifier.constant - ..assignment = code.literalConstMap(renameMap).code, - ), - ), - ); - final libCode = _emitLibrary(lib, languageVersion); - fs.writeFileSync( - p.join(p.dirname(p.fromUri(url)), 'web_rename_map.dart').toJS, - libCode.toJS, - ); + final jsonStr = jsonEncode(renameMap); + fs.writeFileSync(renameMapPath.toJS, jsonStr.toJS); } for (var entry in bindings.entries) { @@ -173,16 +168,20 @@ Future generateIDLBindings({ // parse individual files ensureDirectoryExists(output); - final bindings = await generateBindingsForFiles({ - for (final file in allInputFiles) - file: - (fs.readFileSync( - file.toJS, - JSReadFileOptions(encoding: 'utf-8'.toJS), - ) - as JSString) - .toDart, - }, output); + final bindings = await generateBindingsForFiles( + { + for (final file in allInputFiles) + file: + (fs.readFileSync( + file.toJS, + JSReadFileOptions(encoding: 'utf-8'.toJS), + ) + as JSString) + .toDart, + }, + output, + bcdJsonPath: bcdJsonPath, + ); for (var entry in bindings.entries) { final libraryPath = entry.key; @@ -254,4 +253,9 @@ final _parser = ArgParser() hide: true, valueHelp: '[file].yaml', help: 'Configuration', + ) + ..addOption('idl-json', help: 'Path to the pre-parsed IDL JSON file') + ..addOption( + 'bcd-json', + help: 'Path to the browser-compat-data data.json file', ); diff --git a/web_generator/lib/src/doc_provider.dart b/js_interop_gen/lib/src/doc_provider.dart similarity index 100% rename from web_generator/lib/src/doc_provider.dart rename to js_interop_gen/lib/src/doc_provider.dart diff --git a/js_interop_gen/lib/src/elements.dart b/js_interop_gen/lib/src/elements.dart new file mode 100644 index 00000000..d30d88b7 --- /dev/null +++ b/js_interop_gen/lib/src/elements.dart @@ -0,0 +1,569 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:js_interop'; + +import 'banned_names.dart'; +import 'bcd.dart'; +import 'doc_provider.dart'; +import 'js/webidl_api.dart' as idl; +import 'translator.dart'; +import 'type_aliases.dart'; +import 'type_union.dart'; +import 'util.dart'; + +/// If [rawType] corresponds to an IDL type that we declare as a typedef, +/// desugars the typedef, accounting for nullability along the way. +/// +/// Otherwise, returns null. +RawType? desugarTypedef(RawType rawType) { + final decl = Translator.instance!.typeToDeclaration[rawType.type]; + return switch (decl?.type) { + 'typedef' => _getRawType( + (decl as idl.Typedef).idlType, + )..nullable |= rawType.nullable, + 'callback' || + 'callback interface' => RawType('JSFunction', rawType.nullable), + 'enum' => RawType('JSString', rawType.nullable), + _ => null, + }; +} + +RawType _computeRawTypeUnion(RawType rawType1, RawType rawType2) { + final type1 = rawType1.type; + final type2 = rawType2.type; + final nullable1 = rawType1.nullable; + final nullable2 = rawType2.nullable; + final typeParam1 = rawType1.typeParameter; + final typeParam2 = rawType2.typeParameter; + + RawType? computeTypeParamUnion(RawType? typeParam1, RawType? typeParam2) => + typeParam1 != null && typeParam2 != null + ? _computeRawTypeUnion(typeParam1, typeParam2) + : null; + + if (type1 == type2) { + return RawType( + type1, + nullable1 || nullable2, + computeTypeParamUnion(typeParam1, typeParam2), + ); + } + if (type1 == 'JSUndefined') return RawType(type2, true, typeParam2); + if (type2 == 'JSUndefined') return RawType(type1, true, typeParam1); + if (type1 == 'JSInteger' || type1 == 'JSDouble') rawType1.type = 'JSNumber'; + if (type2 == 'JSInteger' || type2 == 'JSDouble') rawType2.type = 'JSNumber'; + + final unionableType1 = _getJSTypeEquivalent(rawType1) ?? rawType1; + final unionableType2 = _getJSTypeEquivalent(rawType2) ?? rawType2; + + return RawType( + computeJsTypeUnion(unionableType1.type, unionableType2.type) ?? 'JSAny', + unionableType1.nullable || unionableType2.nullable, + computeTypeParamUnion( + unionableType1.typeParameter, + unionableType2.typeParameter, + ), + ); +} + +/// Given a [rawType], return its JS type-equivalent type if it's a type that is +/// declared in the IDL. +/// +/// Otherwise, return null. +RawType? _getJSTypeEquivalent(RawType rawType) { + final type = rawType.type; + final nullable = rawType.nullable; + final decl = Translator.instance!.typeToDeclaration[type]; + if (decl != null) { + final nodeType = decl.type; + switch (nodeType) { + case 'interface': + case 'dictionary': + return RawType('JSObject', nullable); + default: + final desugaredType = desugarTypedef(rawType); + if (desugaredType != null) { + return _getJSTypeEquivalent(desugaredType) ?? desugaredType; + } + throw Exception('Unhandled type $type with node type: $nodeType'); + } + } + return null; +} + +/// Returns a [RawType] for the given [idl.IDLType]. +RawType _getRawType(idl.IDLType idlType) { + if (idlType.union) { + final types = (idlType.idlType as JSArray).toDart; + final unionType = _getRawType(types[0]); + for (var i = 1; i < types.length; i++) { + unionType.update(types[i]); + } + return unionType..nullable |= idlType.nullable; + } + String type; + var nullable = idlType.nullable; + RawType? typeParameter; + if (idlType.generic.isNotEmpty) { + final types = (idlType.idlType as JSArray).toDart; + if (types.length == 1) { + typeParameter = _getRawType(types[0]); + } else if (types.length > 1) { + assert(types.length == 2); + assert(idlType.generic == 'record'); + } + type = idlType.generic; + } else { + type = (idlType.idlType as JSString).toDart; + } + + if (type == 'WindowProxy') type = 'Window'; + if (type == 'any') nullable = true; + final translator = Translator.instance!; + final decl = translator.typeToDeclaration[type]; + final alias = idlOrBuiltinToJsTypeAliases[type]; + assert(decl != null || alias != null); + if (alias == null && !translator.markTypeAsUsed(type)) { + type = _getJSTypeEquivalent(RawType(type, false))!.type; + } + return RawType(alias ?? type, nullable, typeParameter); +} + +class Attribute extends Property { + final bool isStatic; + final bool isReadOnly; + + Attribute( + super.name, + super.idlType, + super.mdnProperty, { + required this.isStatic, + required this.isReadOnly, + }); +} + +class Constant extends Property { + final String valueType; + final JSAny value; + Constant(super.name, super.idlType, this.valueType, this.value); +} + +class Field extends Property { + final bool isRequired; + + Field( + super.name, + super.idlType, + super.mdnProperty, { + required this.isRequired, + }); +} + +class MemberName { + final String name; + final String jsOverride; + + factory MemberName(String name, [String jsOverride = '']) { + final rename = dartRename(name); + if (rename != name && jsOverride.isEmpty) jsOverride = name; + return MemberName._(rename, jsOverride); + } + + MemberName._(this.name, this.jsOverride); +} + +class OverridableConstructor extends OverridableMember { + OverridableConstructor(idl.Constructor constructor) + : super(constructor.arguments); + + void update(idl.Constructor that) => _processParameters(that.arguments); +} + +abstract class OverridableMember { + final List parameters = []; + + OverridableMember(JSArray rawParameters) { + for (var i = 0; i < rawParameters.length; i++) { + parameters.add(Parameter(rawParameters[i])); + } + } + + void _processParameters(JSArray thoseParameters) { + final thatLength = thoseParameters.length; + for (var i = thatLength; i < parameters.length; i++) { + parameters[i].isOptional = true; + } + for (var i = 0; i < thatLength; i++) { + final argument = thoseParameters[i]; + if (i >= parameters.length) { + parameters.add(Parameter(argument)..isOptional = true); + } else { + parameters[i].update(argument); + } + } + } +} + +class OverridableOperation extends OverridableMember { + bool _finalized = false; + MemberName _name; + + final String special; + final RawType returnType; + final MdnProperty? mdnProperty; + late final MemberName name = _generateName(); + + factory OverridableOperation( + idl.Operation operation, + MemberName memberName, + MdnProperty? mdnProperty, + ) => OverridableOperation._( + memberName, + operation.special, + _getRawType(operation.idlType), + mdnProperty, + operation.arguments, + ); + + OverridableOperation._( + this._name, + this.special, + this.returnType, + this.mdnProperty, + super.parameters, + ); + + bool get isStatic => special == 'static'; + + void underscoreName() { + final jsName = _name.jsOverride.isEmpty ? _name.name : _name.jsOverride; + _name = MemberName('${_name.name}_', jsName); + } + + void update(idl.Operation that) { + assert( + !_finalized, + 'Call to OverridableOperation.update was made after the operation was ' + 'finalized.', + ); + final jsOverride = _name.jsOverride; + final thisName = jsOverride.isNotEmpty ? jsOverride : _name.name; + assert( + (that.name.isEmpty || thisName == that.name) && special == that.special, + ); + returnType.update(that.idlType); + _processParameters(that.arguments); + } + + MemberName _generateName() { + _finalized = true; + final dartName = _name.name; + if (dartName == returnType.type || + parameters.any((parameter) => dartName == parameter.type.type)) { + underscoreName(); + } + return _name; + } +} + +class Parameter { + final Set _names; + final RawType type; + bool isOptional; + bool isVariadic; + late final String name = _generateName(); + + factory Parameter(idl.Argument argument) => Parameter._( + {argument.name}, + _getRawType(argument.idlType), + argument.optional, + argument.variadic, + ); + + Parameter._(this._names, this.type, this.isOptional, this.isVariadic); + + void update(idl.Argument argument) { + final thatName = argument.name; + _names.add(thatName); + type.update(argument.idlType); + if (argument.optional) { + isOptional = true; + } + if (argument.variadic) { + isVariadic = true; + } + } + + String _generateName() { + final namesList = _names.toList(); + namesList.sort(); + return namesList + .sublist(0, 1) + .followedBy(namesList.sublist(1).map(capitalize)) + .join('Or'); + } +} + +class PartialInterfacelike { + final String name; + final String type; + String? inheritance; + final Map operations = {}; + final Map staticOperations = {}; + final List properties = []; + final List extensionProperties = []; + final MdnInterface? mdnInterface; + OverridableConstructor? constructor; + + factory PartialInterfacelike( + idl.Interfacelike interfacelike, + MdnInterface? mdnInterface, + ) { + final partialInterfacelike = PartialInterfacelike._( + interfacelike.name, + interfacelike.type, + interfacelike.inheritance, + mdnInterface, + ); + partialInterfacelike._processMembers(interfacelike.members); + return partialInterfacelike; + } + + PartialInterfacelike._( + this.name, + this.type, + String? inheritance, + this.mdnInterface, + ) { + _setInheritance(inheritance); + } + + void update(idl.Interfacelike interfacelike) { + assert( + (name == interfacelike.name && type == interfacelike.type) || + interfacelike.type == 'interface mixin', + ); + assert( + interfacelike.inheritance == null || inheritance == null, + 'An interface should only be defined once.', + ); + _setInheritance(interfacelike.inheritance); + _processMembers(interfacelike.members); + } + + bool _hasHTMLConstructorAttribute(idl.Constructor constructor) => constructor + .extAttrs + .toDart + .any((extAttr) => extAttr.name == 'HTMLConstructor'); + + void _processMembers(JSArray nodeMembers) { + for (var i = 0; i < nodeMembers.length; i++) { + final member = nodeMembers[i]; + final type = member.type; + switch (type) { + case 'constructor': + if (!_shouldGenerateMember(name)) break; + final idlConstructor = member as idl.Constructor; + if (_hasHTMLConstructorAttribute(idlConstructor)) break; + if (constructor == null) { + constructor = OverridableConstructor(idlConstructor); + } else { + constructor!.update(idlConstructor); + } + break; + case 'const': + final constant = member as idl.Constant; + properties.add( + Constant( + MemberName(constant.name), + constant.idlType, + constant.value.type, + constant.value.value, + ), + ); + break; + case 'attribute': + final attribute = member as idl.Attribute; + final isStatic = attribute.special == 'static'; + final attributeName = attribute.name; + if (!_shouldGenerateMember(attributeName, isStatic: isStatic)) break; + final isExtensionMember = + name == 'SVGElement' && attributeName == 'className'; + final memberList = isExtensionMember + ? extensionProperties + : properties; + memberList.add( + Attribute( + MemberName(attributeName), + attribute.idlType, + mdnInterface?.propertyFor(attributeName, isStatic: isStatic), + isStatic: isStatic, + isReadOnly: attribute.readonly, + ), + ); + break; + case 'operation': + final operation = member as idl.Operation; + final special = operation.special; + var operationName = operation.name; + var shouldQueryMDN = true; + switch (special) { + case 'getter': + if (operationName.isEmpty) { + operationName = 'operator []'; + shouldQueryMDN = false; + } + break; + case 'setter': + if (operationName.isEmpty) { + operationName = 'operator []='; + shouldQueryMDN = false; + } + break; + case 'static': + break; + default: + if (operationName.isEmpty) continue; + } + final isStatic = operation.special == 'static'; + if (shouldQueryMDN && + !_shouldGenerateMember(operationName, isStatic: isStatic)) { + break; + } + final docs = shouldQueryMDN + ? mdnInterface?.propertyFor(operationName, isStatic: isStatic) + : null; + if (isStatic) { + if (staticOperations.containsKey(operationName)) { + staticOperations[operationName]!.update(operation); + } else { + staticOperations[operationName] = OverridableOperation( + operation, + MemberName(operationName), + docs, + ); + if (operations.containsKey(operationName)) { + staticOperations[operationName]!.underscoreName(); + } + } + } else { + if (operations.containsKey(operationName)) { + operations[operationName]!.update(operation); + } else { + staticOperations[operationName]?.underscoreName(); + operations[operationName] = OverridableOperation( + operation, + MemberName(operationName), + docs, + ); + } + } + break; + case 'field': + final field = member as idl.Field; + final fieldName = field.name; + if (!_shouldGenerateMember(fieldName)) break; + properties.add( + Field( + MemberName(fieldName), + field.idlType, + mdnInterface?.propertyFor(fieldName, isStatic: false), + isRequired: field.required, + ), + ); + break; + case 'maplike': + case 'setlike': + case 'iterable': + case 'async_iterable': + break; + default: + throw Exception('Unrecognized member type $type'); + } + } + } + + /// Given the [declaredInheritance] by the IDL, find the closest supertype + /// that is actually generated, and set the inheritance equal to that type. + void _setInheritance(String? declaredInheritance) { + if (declaredInheritance == null) return; + final translator = Translator.instance!; + while (declaredInheritance != null) { + if (translator.markTypeAsUsed(declaredInheritance)) { + inheritance = declaredInheritance; + break; + } else { + declaredInheritance = + (translator.typeToDeclaration[declaredInheritance] + as idl.Interfacelike) + .inheritance; + } + } + } + + /// Given a [memberName] and whether it [isStatic], return whether it is a + /// member that should be emitted according to the compat data. + bool _shouldGenerateMember(String memberName, {bool isStatic = false}) { + if (Translator.instance!.browserCompatData.generateAll) return true; + if (type != 'interface' && type != 'namespace') return true; + final interfaceBcd = Translator.instance!.browserCompatData + .retrieveInterfaceFor(name)!; + final bcd = interfaceBcd.retrievePropertyFor( + memberName, + isStatic: isStatic || type == 'namespace', + ); + final shouldGenerate = bcd?.shouldGenerate; + if (shouldGenerate != null) return shouldGenerate; + if (!isStatic && BrowserCompatData.isEventHandlerSupported(memberName)) { + return true; + } + return false; + } +} + +sealed class Property { + late final MemberName name; + final RawType type; + final MdnProperty? mdnProperty; + + // TODO(srujzs): Remove ignore after + // https://github.com/dart-lang/sdk/issues/55720 is resolved. + // ignore: unused_element_parameter + Property(MemberName name, idl.IDLType idlType, [this.mdnProperty]) + : type = _getRawType(idlType) { + final dartName = name.name; + final jsName = name.jsOverride.isEmpty ? dartName : name.jsOverride; + this.name = dartName == type.type + ? MemberName('${dartName}_', jsName) + : name; + } +} + +/// A class representing either a type that corresponds to an IDL declaration or +/// a `dart:js_interop` JS types (including sentinels). +/// +/// This should not include IDL types for which there isn't a declaration e.g. +/// `any` or a JS built-in type e.g. `ArrayBuffer`. +class RawType { + String type; + bool nullable; + RawType? typeParameter; + + RawType(this.type, this.nullable, [this.typeParameter]) { + if (type == 'JSUndefined') nullable = true; + } + + @override + String toString() => + 'RawType(type: $type, nullable: $nullable, ' + 'typeParameter: $typeParameter)'; + + void update(idl.IDLType idlType) { + final union = _computeRawTypeUnion(this, _getRawType(idlType)); + type = union.type; + nullable = union.nullable; + typeParameter = union.typeParameter; + } +} diff --git a/web_generator/lib/src/formatting.dart b/js_interop_gen/lib/src/formatting.dart similarity index 100% rename from web_generator/lib/src/formatting.dart rename to js_interop_gen/lib/src/formatting.dart diff --git a/web_generator/lib/src/generate_bindings.dart b/js_interop_gen/lib/src/generate_bindings.dart similarity index 59% rename from web_generator/lib/src/generate_bindings.dart rename to js_interop_gen/lib/src/generate_bindings.dart index a82b599b..e658608b 100644 --- a/web_generator/lib/src/generate_bindings.dart +++ b/js_interop_gen/lib/src/generate_bindings.dart @@ -2,10 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; import 'dart:js_interop'; import 'package:path/path.dart' as p; - +import 'js/filesystem_api.dart'; import 'js/webidl2.dart' as webidl2; import 'js/webidl_api.dart' as webidl; import 'js/webref_css_api.dart'; @@ -15,9 +16,9 @@ import 'translator.dart'; import 'util.dart'; /// Generate CSS property names for setting / getting CSS properties in JS. -Future> _generateCSSStyleDeclarations() async { +Future> _generateCSSStyleDeclarations([JSObject? data]) async { final cssStyleDeclarations = {}; - final array = objectEntries(await css.listAll().toDart); + final array = objectEntries(data ?? await css.listAll().toDart); for (var i = 0; i < array.length; i++) { final entry = array[i] as JSArray; final data = entry[1]; @@ -51,9 +52,11 @@ Future> _generateCSSStyleDeclarations() async { /// Parse the elements spec and construct a map of element interfaces to the /// tag names that correspond to the interface. -Future>> _generateElementTagMap() async { +Future>> _generateElementTagMap([ + JSObject? data, +]) async { final elementMap = >{}; - final array = objectEntries(await elements.listAll().toDart); + final array = objectEntries(data ?? await elements.listAll().toDart); for (var i = 0; i < array.length; i++) { final entry = array[i] as JSArray; final data = entry[1] as ElementsEntries; @@ -75,23 +78,73 @@ Future<(TranslationResult, Map)> generateBindings( String packageRoot, String librarySubDir, { required bool generateAll, + String? renameMapPath, + String? idlJsonPath, + required String bcdJsonPath, }) async { - final cssStyleDeclarations = await _generateCSSStyleDeclarations(); - final elementHTMLMap = await _generateElementTagMap(); + var renameMap = {}; + if (renameMapPath != null) { + if (fs.existsSync(renameMapPath.toJS).toDart) { + final jsonStr = + (fs.readFileSync( + renameMapPath.toJS, + JSReadFileOptions(encoding: 'utf8'.toJS), + ) + as JSString) + .toDart; + final json = jsonDecode(jsonStr) as Map; + renameMap = json.map((k, v) => MapEntry(k, v as String)); + } + } + JSObject? idlData; + JSObject? cssData; + JSObject? elementsData; + + if (idlJsonPath != null) { + final jsonStr = + (fs.readFileSync( + idlJsonPath.toJS, + JSReadFileOptions(encoding: 'utf8'.toJS), + ) + as JSString) + .toDart; + final json = jsonDecode(jsonStr) as Map; + + idlData = (json['idl'] as Map?)?.jsify() as JSObject?; + cssData = (json['css'] as Map?)?.jsify() as JSObject?; + elementsData = (json['elements'] as Map?)?.jsify() as JSObject?; + } + + final cssStyleDeclarations = await _generateCSSStyleDeclarations(cssData); + final elementHTMLMap = await _generateElementTagMap(elementsData); final translator = Translator( librarySubDir, cssStyleDeclarations, elementHTMLMap, generateAll: generateAll, packageRoot: packageRoot, + loadedRenameMap: renameMap, + bcdJsonPath: bcdJsonPath, ); - final array = objectEntries(await idl.parseAll().toDart); - for (var i = 0; i < array.length; i++) { - final entry = array[i] as JSArray; - final shortname = (entry[0] as JSString).toDart; - final ast = entry[1] as JSArray; - translator.collect(shortname, ast); + + if (idlData != null) { + final array = objectEntries(idlData); + for (var i = 0; i < array.length; i++) { + final entry = array[i] as JSArray; + final shortname = (entry[0] as JSString).toDart; + final ast = entry[1] as JSArray; + translator.collect(shortname, ast); + } + } else { + final array = objectEntries(await idl.parseAll().toDart); + for (var i = 0; i < array.length; i++) { + final entry = array[i] as JSArray; + final shortname = (entry[0] as JSString).toDart; + final ast = entry[1] as JSArray; + translator.collect(shortname, ast); + } } + translator.addInterfacesAndNamespaces(); final result = translator.translate(); final renamedTypes = translator.renamedClasses; @@ -101,18 +154,23 @@ Future<(TranslationResult, Map)> generateBindings( Future generateBindingsForFiles( Map fileContents, - String output, -) async { + String output, { + required String bcdJsonPath, +}) async { // generate CSS style declarations and element tag map incase they are // needed for the input files. - final cssStyleDeclarations = await _generateCSSStyleDeclarations(); - final elementHTMLMap = await _generateElementTagMap(); + final emptyJsObject = {}.jsify() as JSObject; + final cssStyleDeclarations = await _generateCSSStyleDeclarations( + emptyJsObject, + ); + final elementHTMLMap = await _generateElementTagMap(emptyJsObject); final translator = Translator( output, cssStyleDeclarations, elementHTMLMap, generateAll: true, generateForWeb: false, + bcdJsonPath: bcdJsonPath, ); for (final file in fileContents.entries) { diff --git a/web_generator/lib/src/interop_gen/hasher.dart b/js_interop_gen/lib/src/interop_gen/hasher.dart similarity index 100% rename from web_generator/lib/src/interop_gen/hasher.dart rename to js_interop_gen/lib/src/interop_gen/hasher.dart diff --git a/web_generator/lib/src/interop_gen/namer.dart b/js_interop_gen/lib/src/interop_gen/namer.dart similarity index 100% rename from web_generator/lib/src/interop_gen/namer.dart rename to js_interop_gen/lib/src/interop_gen/namer.dart diff --git a/web_generator/lib/src/interop_gen/parser.dart b/js_interop_gen/lib/src/interop_gen/parser.dart similarity index 88% rename from web_generator/lib/src/interop_gen/parser.dart rename to js_interop_gen/lib/src/interop_gen/parser.dart index 837e4d9c..8d9d414f 100644 --- a/web_generator/lib/src/interop_gen/parser.dart +++ b/js_interop_gen/lib/src/interop_gen/parser.dart @@ -65,9 +65,15 @@ ParserResult parseDeclarationFiles(Config config) { final diagnostics = parsedCommandLine.errors.toDart; // handle any diagnostics - handleDiagnostics(diagnostics); if (!ignoreErrors && diagnostics.isNotEmpty) { + printErr( + 'ParseError: There were some errors when parsing the given ' + 'configuration file', + ); + handleDiagnostics(diagnostics); exit(1); + } else { + handleDiagnostics(diagnostics); } } @@ -84,11 +90,16 @@ ParserResult parseDeclarationFiles(Config config) { ]; // handle diagnostics - handleDiagnostics(diagnostics); - - if (diagnostics.isNotEmpty && !ignoreErrors) { - // exit + if (!ignoreErrors && diagnostics.isNotEmpty) { + printErr( + 'ParseError: There were some errors when parsing the given ' + 'declaration file', + ); + handleDiagnostics(diagnostics); exit(1); + } else { + handleDiagnostics(diagnostics); + printErr('=' * 50); } return ParserResult(program: program, files: files); diff --git a/web_generator/lib/src/interop_gen/qualified_name.dart b/js_interop_gen/lib/src/interop_gen/qualified_name.dart similarity index 100% rename from web_generator/lib/src/interop_gen/qualified_name.dart rename to js_interop_gen/lib/src/interop_gen/qualified_name.dart diff --git a/web_generator/lib/src/interop_gen/sub_type.dart b/js_interop_gen/lib/src/interop_gen/sub_type.dart similarity index 100% rename from web_generator/lib/src/interop_gen/sub_type.dart rename to js_interop_gen/lib/src/interop_gen/sub_type.dart diff --git a/web_generator/lib/src/interop_gen/transform.dart b/js_interop_gen/lib/src/interop_gen/transform.dart similarity index 100% rename from web_generator/lib/src/interop_gen/transform.dart rename to js_interop_gen/lib/src/interop_gen/transform.dart index 72d78efa..01a095e9 100644 --- a/web_generator/lib/src/interop_gen/transform.dart +++ b/js_interop_gen/lib/src/interop_gen/transform.dart @@ -39,10 +39,10 @@ class TransformResult { // TODO(https://github.com/dart-lang/web/issues/388): Handle union of overloads // (namespaces + functions, multiple interfaces, etc) Map generate(Config config) { - final formatter = DartFormatter(languageVersion: config.languageVersion); - _setGlobalOptions(config); + final formatter = DartFormatter(languageVersion: config.languageVersion); + return {...programDeclarationMap, ...commonTypes}.map((file, declMap) { final emitter = DartEmitter.scoped( useNullSafetySyntax: true, diff --git a/js_interop_gen/lib/src/interop_gen/transform/export_reference.dart b/js_interop_gen/lib/src/interop_gen/transform/export_reference.dart new file mode 100644 index 00000000..97d739ab --- /dev/null +++ b/js_interop_gen/lib/src/interop_gen/transform/export_reference.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class ExportReference { + final String name; + final String as; + final bool defaultExport; + + const ExportReference( + this.name, { + required this.as, + this.defaultExport = false, + }); + + @override + bool operator ==(Object other) => + other is ExportReference && + name == other.name && + as == other.as && + defaultExport == other.defaultExport; + + @override + int get hashCode => Object.hash(name, as, defaultExport); +} diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/js_interop_gen/lib/src/interop_gen/transform/transformer.dart similarity index 96% rename from web_generator/lib/src/interop_gen/transform/transformer.dart rename to js_interop_gen/lib/src/interop_gen/transform/transformer.dart index 15c53319..7fa8552c 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/js_interop_gen/lib/src/interop_gen/transform/transformer.dart @@ -2,10 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:collection'; import 'dart:js_interop'; + import 'package:collection/collection.dart'; import 'package:path/path.dart' as p; + import '../../ast/base.dart'; import '../../ast/builtin.dart'; import '../../ast/declarations.dart'; @@ -19,32 +20,13 @@ import '../../js/filesystem_api.dart'; import '../../js/helpers.dart'; import '../../js/typescript.dart' as ts; import '../../js/typescript.types.dart'; +import '../../translator.dart'; import '../hasher.dart'; import '../namer.dart'; import '../qualified_name.dart'; import '../transform.dart'; - -class ExportReference { - final String name; - final String as; - final bool defaultExport; - - const ExportReference( - this.name, { - required this.as, - this.defaultExport = false, - }); - - @override - bool operator ==(Object other) => - other is ExportReference && - name == other.name && - as == other.as && - defaultExport == other.defaultExport; - - @override - int get hashCode => Object.hash(name, as, defaultExport); -} +import 'export_reference.dart'; +import 'utils.dart'; /// A class for transforming nodes in a given [file] /// @@ -600,14 +582,12 @@ class Transformer { ? (nameNode as TSIdentifier).text : (nameNode as TSLiteralExpression).text; final nameForDart = nameNode.kind == TSSyntaxKind.StringLiteral - ? dartRename(_toCamelCase(name)) + ? dartRename(toCamelCase(name)) : name; final (:id, name: dartName) = parentNamer.makeUnique(nameForDart, 'var'); - final (:isStatic, :isReadonly, :scope) = _parseModifiers( - property.modifiers, - ); + final (:isStatic, :isReadonly, :scope) = parseModifiers(property.modifiers); ReferredType? propType; if (property.type case final type? when ts.isTypeReferenceNode(type)) { @@ -661,9 +641,7 @@ class Transformer { final typeParams = method.typeParameters?.toDart; - final (:isStatic, isReadonly: _, :scope) = _parseModifiers( - method.modifiers, - ); + final (:isStatic, isReadonly: _, :scope) = parseModifiers(method.modifiers); ReferredType? methodType; if (method.type case final type? when ts.isTypeReferenceNode(type)) { @@ -746,7 +724,7 @@ class Transformer { final (isStatic: _, isReadonly: _, :scope) = (constructor.isA() || constructor.kind == TSSyntaxKind.Constructor) - ? _parseModifiers((constructor as TSConstructorDeclaration).modifiers) + ? parseModifiers((constructor as TSConstructorDeclaration).modifiers) : (isStatic: false, isReadonly: false, scope: DeclScope.public); return ConstructorDeclaration( @@ -821,7 +799,7 @@ class Transformer { final typeParams = indexSignature.typeParameters?.toDart; - final (:isStatic, :isReadonly, :scope) = _parseModifiers( + final (:isStatic, :isReadonly, :scope) = parseModifiers( indexSignature.modifiers, ); @@ -895,7 +873,7 @@ class Transformer { final typeParams = getter.typeParameters?.toDart; - final (isStatic: _, isReadonly: _, :scope) = _parseModifiers( + final (isStatic: _, isReadonly: _, :scope) = parseModifiers( getter.modifiers, ); @@ -952,7 +930,7 @@ class Transformer { final typeParams = setter.typeParameters?.toDart; - final (isStatic: _, isReadonly: _, :scope) = _parseModifiers( + final (isStatic: _, isReadonly: _, :scope) = parseModifiers( setter.modifiers, ); @@ -1116,9 +1094,7 @@ class Transformer { switch (memInitializer.kind) { case TSSyntaxKind.NumericLiteral: // parse numeric literal - final value = _parseNumericLiteral( - memInitializer as TSNumericLiteral, - ); + final value = num.parse((memInitializer as TSNumericLiteral).text); final primitiveType = value is int ? PrimitiveType.int : PrimitiveType.double; @@ -1142,9 +1118,7 @@ class Transformer { break; case TSSyntaxKind.StringLiteral: // parse string literal - final value = _parseStringLiteral( - memInitializer as TSStringLiteral, - ); + final value = (memInitializer as TSStringLiteral).text; const primitiveType = PrimitiveType.string; members.add( EnumMember( @@ -1191,14 +1165,6 @@ class Transformer { ); } - num _parseNumericLiteral(TSNumericLiteral numericLiteral) { - return num.parse(numericLiteral.text); - } - - String _parseStringLiteral(TSStringLiteral stringLiteral) { - return stringLiteral.text; - } - TypeAliasDeclaration _transformTypeAlias( TSTypeAliasDeclaration typealias, { UniqueNamer? namer, @@ -2514,6 +2480,7 @@ class Transformer { .map(getJSTypeAlternative) .toList(), isNullable: isNullable, + renameMap: Translator.instance?.loadedRenameMap ?? const {}, ); } @@ -2647,12 +2614,7 @@ class Transformer { fullyQualifiedName.asName, ); }) - .reduce( - (prev, next) => [ - if (prev != null) ...prev, - if (next != null) ...next, - ], - ); + .reduce((prev, next) => [...?prev, ...?next]); final nodes = referencedDeclarations?.whereType().toList() ?? @@ -3110,79 +3072,3 @@ class Transformer { return filteredDeclarations; } } - -({bool isReadonly, bool isStatic, DeclScope scope}) _parseModifiers([ - TSNodeArray? modifiers, -]) { - var isReadonly = false; - var isStatic = false; - var scope = DeclScope.public; - - for (final modifier in modifiers?.toDart ?? []) { - switch (modifier.kind) { - case TSSyntaxKind.StaticKeyword: - isStatic = true; - break; - case TSSyntaxKind.ReadonlyKeyword: - isReadonly = true; - break; - case TSSyntaxKind.PrivateKeyword: - scope = DeclScope.private; - break; - case TSSyntaxKind.ProtectedKeyword: - scope = DeclScope.protected; - break; - case TSSyntaxKind.PublicKeyword: - scope = DeclScope.public; - break; - default: - break; - } - } - - return (isStatic: isStatic, isReadonly: isReadonly, scope: scope); -} - -Iterable _parseQualifiedName(TSQualifiedName name) { - final list = []; - if (name.left.kind == TSSyntaxKind.Identifier) { - list.add(QualifiedNamePart((name.left as TSIdentifier).text)); - } else { - list.addAll(_parseQualifiedName(name.left as TSQualifiedName)); - } - - list.add(QualifiedNamePart(name.right.text)); - - return list; -} - -QualifiedName parseQualifiedNameFromTSQualifiedName(TSQualifiedName name) { - final list = LinkedList(); - list.addAll(_parseQualifiedName(name)); - return QualifiedName(list); -} - -QualifiedName parseQualifiedName( - @UnionOf([TSQualifiedName, TSIdentifier]) TSNode name, -) { - if (name.kind == TSSyntaxKind.Identifier) { - return QualifiedName.raw((name as TSIdentifier).text); - } else { - return parseQualifiedNameFromTSQualifiedName(name as TSQualifiedName); - } -} - -String _toCamelCase(String text) { - final parts = text.split(RegExp(r'[-=]')); - final sb = StringBuffer(); - for (var i = 0; i < parts.length; i++) { - final part = parts[i]; - if (part.isEmpty) continue; - if (i == 0) { - sb.write(part.substring(0, 1).toLowerCase() + part.substring(1)); - } else { - sb.write(part.substring(0, 1).toUpperCase() + part.substring(1)); - } - } - return sb.toString(); -} diff --git a/js_interop_gen/lib/src/interop_gen/transform/utils.dart b/js_interop_gen/lib/src/interop_gen/transform/utils.dart new file mode 100644 index 00000000..5ea454bc --- /dev/null +++ b/js_interop_gen/lib/src/interop_gen/transform/utils.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; +import 'dart:js_interop'; + +import '../../ast/base.dart'; +import '../../js/annotations.dart'; +import '../../js/typescript.types.dart'; +import '../qualified_name.dart'; + +({bool isReadonly, bool isStatic, DeclScope scope}) parseModifiers([ + TSNodeArray? modifiers, +]) { + var isReadonly = false; + var isStatic = false; + var scope = DeclScope.public; + + for (final modifier in modifiers?.toDart ?? []) { + switch (modifier.kind) { + case TSSyntaxKind.StaticKeyword: + isStatic = true; + break; + case TSSyntaxKind.ReadonlyKeyword: + isReadonly = true; + break; + case TSSyntaxKind.PrivateKeyword: + scope = DeclScope.private; + break; + case TSSyntaxKind.ProtectedKeyword: + scope = DeclScope.protected; + break; + case TSSyntaxKind.PublicKeyword: + scope = DeclScope.public; + break; + default: + break; + } + } + + return (isStatic: isStatic, isReadonly: isReadonly, scope: scope); +} + +Iterable parseQualifiedNameParts(TSQualifiedName name) { + final list = []; + if (name.left.kind == TSSyntaxKind.Identifier) { + list.add(QualifiedNamePart((name.left as TSIdentifier).text)); + } else { + list.addAll(parseQualifiedNameParts(name.left as TSQualifiedName)); + } + + list.add(QualifiedNamePart(name.right.text)); + + return list; +} + +QualifiedName parseQualifiedNameFromTSQualifiedName(TSQualifiedName name) { + final list = LinkedList() + ..addAll(parseQualifiedNameParts(name)); + return QualifiedName(list); +} + +QualifiedName parseQualifiedName( + @UnionOf([TSQualifiedName, TSIdentifier]) TSNode name, +) { + if (name.kind == TSSyntaxKind.Identifier) { + return QualifiedName.raw((name as TSIdentifier).text); + } else { + return parseQualifiedNameFromTSQualifiedName(name as TSQualifiedName); + } +} + +String toCamelCase(String text) { + final parts = text.split(RegExp(r'[-=]')); + final sb = StringBuffer(); + var first = true; + for (final part in parts) { + if (part.isEmpty) continue; + if (first) { + sb.write(part[0].toLowerCase()); + sb.write(part.substring(1)); + first = false; + } else { + sb.write(part[0].toUpperCase()); + sb.write(part.substring(1)); + } + } + return sb.toString(); +} diff --git a/web_generator/lib/src/js/annotations.dart b/js_interop_gen/lib/src/js/annotations.dart similarity index 100% rename from web_generator/lib/src/js/annotations.dart rename to js_interop_gen/lib/src/js/annotations.dart diff --git a/web_generator/lib/src/js/filesystem_api.dart b/js_interop_gen/lib/src/js/filesystem_api.dart similarity index 100% rename from web_generator/lib/src/js/filesystem_api.dart rename to js_interop_gen/lib/src/js/filesystem_api.dart diff --git a/web_generator/lib/src/js/helpers.dart b/js_interop_gen/lib/src/js/helpers.dart similarity index 100% rename from web_generator/lib/src/js/helpers.dart rename to js_interop_gen/lib/src/js/helpers.dart diff --git a/web_generator/lib/src/js/node.dart b/js_interop_gen/lib/src/js/node.dart similarity index 100% rename from web_generator/lib/src/js/node.dart rename to js_interop_gen/lib/src/js/node.dart diff --git a/web_generator/lib/src/js/typescript.dart b/js_interop_gen/lib/src/js/typescript.dart similarity index 100% rename from web_generator/lib/src/js/typescript.dart rename to js_interop_gen/lib/src/js/typescript.dart diff --git a/web_generator/lib/src/js/typescript.types.dart b/js_interop_gen/lib/src/js/typescript.types.dart similarity index 100% rename from web_generator/lib/src/js/typescript.types.dart rename to js_interop_gen/lib/src/js/typescript.types.dart diff --git a/web_generator/lib/src/js/webidl2.dart b/js_interop_gen/lib/src/js/webidl2.dart similarity index 100% rename from web_generator/lib/src/js/webidl2.dart rename to js_interop_gen/lib/src/js/webidl2.dart diff --git a/web_generator/lib/src/js/webidl_api.dart b/js_interop_gen/lib/src/js/webidl_api.dart similarity index 100% rename from web_generator/lib/src/js/webidl_api.dart rename to js_interop_gen/lib/src/js/webidl_api.dart diff --git a/web_generator/lib/src/js/webref_css_api.dart b/js_interop_gen/lib/src/js/webref_css_api.dart similarity index 100% rename from web_generator/lib/src/js/webref_css_api.dart rename to js_interop_gen/lib/src/js/webref_css_api.dart diff --git a/web_generator/lib/src/js/webref_elements_api.dart b/js_interop_gen/lib/src/js/webref_elements_api.dart similarity index 100% rename from web_generator/lib/src/js/webref_elements_api.dart rename to js_interop_gen/lib/src/js/webref_elements_api.dart diff --git a/web_generator/lib/src/js/webref_idl_api.dart b/js_interop_gen/lib/src/js/webref_idl_api.dart similarity index 100% rename from web_generator/lib/src/js/webref_idl_api.dart rename to js_interop_gen/lib/src/js/webref_idl_api.dart diff --git a/web_generator/lib/src/js_type_supertypes.dart b/js_interop_gen/lib/src/js_type_supertypes.dart similarity index 92% rename from web_generator/lib/src/js_type_supertypes.dart rename to js_interop_gen/lib/src/js_type_supertypes.dart index 22e56a42..43250215 100644 --- a/web_generator/lib/src/js_type_supertypes.dart +++ b/js_interop_gen/lib/src/js_type_supertypes.dart @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. // Generated code. Do not modify by hand. +// Generated from Dart SDK 3.10.0 +// To update run: dart run tool/update_supertypes.dart const Map jsTypeSupertypes = { 'JSAny': null, diff --git a/web_generator/lib/src/main.mjs b/js_interop_gen/lib/src/main.mjs similarity index 85% rename from web_generator/lib/src/main.mjs rename to js_interop_gen/lib/src/main.mjs index 13abb5fa..a3b8e624 100644 --- a/web_generator/lib/src/main.mjs +++ b/js_interop_gen/lib/src/main.mjs @@ -5,9 +5,6 @@ import * as childProcess from 'child_process'; import * as fs from 'fs'; import { createRequire } from 'module'; -import * as css from '@webref/css'; -import * as elements from '@webref/elements'; -import * as idl from '@webref/idl'; import * as webidl2 from "webidl2"; import * as ts from 'typescript'; @@ -16,10 +13,7 @@ const require = createRequire(import.meta.url); // Setup properties for JS interop in Dart. globalThis.self = globalThis; globalThis.childProcess = childProcess; -globalThis.css = css; -globalThis.elements = elements; globalThis.fs = fs; -globalThis.idl = idl; globalThis.webidl2 = webidl2; globalThis.ts = ts; globalThis.location = { href: `file://${process.cwd()}/` } diff --git a/js_interop_gen/lib/src/package-lock.json b/js_interop_gen/lib/src/package-lock.json new file mode 100644 index 00000000..9285dd6f --- /dev/null +++ b/js_interop_gen/lib/src/package-lock.json @@ -0,0 +1,39 @@ +{ + "name": "dart_web_bindings_generator", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dart_web_bindings_generator", + "version": "0.0.1", + "license": "BSD 3", + "dependencies": { + "typescript": "^5.9.3", + "webidl2": "^24.4.1" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/webidl2": { + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/webidl2/-/webidl2-24.5.0.tgz", + "integrity": "sha512-fxOigKkIem1iAgQ9t4cFOP+kWEA8y6Be/uh50FpJh0FijoeeT/VMrOyJzNLUgjy0rGMEcHeReKDCqj0g9dIe9A==", + "license": "W3C", + "engines": { + "node": ">= 18" + } + } + } +} diff --git a/web_generator/lib/src/package.json b/js_interop_gen/lib/src/package.json similarity index 70% rename from web_generator/lib/src/package.json rename to js_interop_gen/lib/src/package.json index b03c07f5..4df2e583 100644 --- a/web_generator/lib/src/package.json +++ b/js_interop_gen/lib/src/package.json @@ -9,10 +9,6 @@ "author": "Dart project authors", "license": "BSD 3", "dependencies": { - "@mdn/browser-compat-data": "^5.5.2", - "@webref/css": "^6.11.0", - "@webref/elements": "^2.2.2", - "@webref/idl": "^3.43.1", "typescript": "^5.9.3", "webidl2": "^24.4.1" } diff --git a/js_interop_gen/lib/src/sdk_version.dart b/js_interop_gen/lib/src/sdk_version.dart new file mode 100644 index 00000000..8e86616c --- /dev/null +++ b/js_interop_gen/lib/src/sdk_version.dart @@ -0,0 +1,75 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml/yaml.dart'; + +/// The language version this package supports. +/// +/// For the purposes of code generation and tooling, we treat this as the +/// language version of the SDK. +final dartLanguageVersion = Version(3, 10, 0); + +/// Derives the language version from an SDK constraint. +Version deriveLanguageVersion(VersionConstraint constraint) { + final minVersion = switch (constraint) { + Version() => constraint, + VersionRange(min: final min) => min ?? Version(0, 0, 0), + _ => throw ArgumentError( + 'Unsupported constraint type: ${constraint.runtimeType}', + ), + }; + return Version(minVersion.major, minVersion.minor, 0); +} + +/// Checks that the target package at [targetPackagePath] has a valid SDK +/// constraint that aligns with [dartLanguageVersion]. +void checkSdkVersion(String targetPackagePath) { + final pubspecFile = File(p.join(targetPackagePath, 'pubspec.yaml')); + if (!pubspecFile.existsSync()) { + throw SdkVersionException( + 'Could not find pubspec.yaml in "$targetPackagePath".', + ); + } + + try { + final pubspecContent = pubspecFile.readAsStringSync(); + final sdkConstraintStr = switch (loadYaml(pubspecContent)) { + {'environment': {'sdk': final String s}} => s, + _ => null, + }; + + if (sdkConstraintStr == null) { + throw SdkVersionException( + 'The target package must have an SDK constraint in pubspec.yaml. ' + 'Expected at least ^$dartLanguageVersion.', + ); + } + + final sdkConstraint = VersionConstraint.parse(sdkConstraintStr); + final targetLanguageVersion = deriveLanguageVersion(sdkConstraint); + if (targetLanguageVersion < dartLanguageVersion) { + throw SdkVersionException( + 'The target package requires a language version of ' + '$targetLanguageVersion derived from SDK constraint ' + '"$sdkConstraintStr", but the generator requires at least ' + '$dartLanguageVersion.', + ); + } + } on SdkVersionException { + rethrow; + } catch (e) { + throw SdkVersionException('Failed to read or parse pubspec.yaml: $e'); + } +} + +/// Exception thrown when the target package's SDK version is incompatible. +class SdkVersionException implements Exception { + final String message; + SdkVersionException(this.message); + @override + String toString() => message; +} diff --git a/web_generator/lib/src/singletons.dart b/js_interop_gen/lib/src/singletons.dart similarity index 100% rename from web_generator/lib/src/singletons.dart rename to js_interop_gen/lib/src/singletons.dart diff --git a/web_generator/lib/src/translator.dart b/js_interop_gen/lib/src/translator.dart similarity index 57% rename from web_generator/lib/src/translator.dart rename to js_interop_gen/lib/src/translator.dart index 712571b0..710ac348 100644 --- a/web_generator/lib/src/translator.dart +++ b/js_interop_gen/lib/src/translator.dart @@ -10,12 +10,13 @@ import 'package:path/path.dart' as p; import 'banned_names.dart'; import 'bcd.dart'; import 'doc_provider.dart'; +import 'elements.dart'; import 'formatting.dart'; import 'js/webidl_api.dart' as idl; import 'js/webref_elements_api.dart'; +import 'js_type_supertypes.dart'; import 'singletons.dart'; import 'type_aliases.dart'; -import 'type_union.dart'; import 'util.dart'; typedef TranslationResult = Map; @@ -101,626 +102,6 @@ class _Library { } } -/// If [rawType] corresponds to an IDL type that we declare as a typedef, -/// desugars the typedef, accounting for nullability along the way. -/// -/// Otherwise, returns null. -_RawType? _desugarTypedef(_RawType rawType) { - final decl = Translator.instance!._typeToDeclaration[rawType.type]; - return switch (decl?.type) { - 'typedef' => _getRawType( - (decl as idl.Typedef).idlType, - )..nullable |= rawType.nullable, - // TODO(srujzs): If we ever add a generic JS function type, we should - // maybe leverage that here so we have stronger type-checking of - // callbacks. - 'callback' || - 'callback interface' => _RawType('JSFunction', rawType.nullable), - // TODO(srujzs): Enums in the WebIDL are just strings, but we could make - // them easier to work with on the Dart side. - 'enum' => _RawType('JSString', rawType.nullable), - _ => null, - }; -} - -/// Given a [rawType], return its JS type-equivalent type if it's a type that is -/// declared in the IDL. -/// -/// Otherwise, return null. -_RawType? _getJSTypeEquivalent(_RawType rawType) { - final type = rawType.type; - final nullable = rawType.nullable; - final decl = Translator.instance!._typeToDeclaration[type]; - if (decl != null) { - final nodeType = decl.type; - switch (nodeType) { - case 'interface': - case 'dictionary': - return _RawType('JSObject', nullable); - default: - final desugaredType = _desugarTypedef(rawType); - if (desugaredType != null) { - // The output of `_desugarTypedef` is always an IDL decl type or a JS - // type, so either get the equivalent in the former case or return the - // JS type directly. - return _getJSTypeEquivalent(desugaredType) ?? desugaredType; - } - throw Exception('Unhandled type $type with node type: $nodeType'); - } - } - return null; -} - -_RawType _computeRawTypeUnion(_RawType rawType1, _RawType rawType2) { - final type1 = rawType1.type; - final type2 = rawType2.type; - final nullable1 = rawType1.nullable; - final nullable2 = rawType2.nullable; - final typeParam1 = rawType1.typeParameter; - final typeParam2 = rawType2.typeParameter; - - // If either type parameter is null, then the resulting union can never be a - // generic type, so return null. - _RawType? computeTypeParamUnion(_RawType? typeParam1, _RawType? typeParam2) => - typeParam1 != null && typeParam2 != null - ? _computeRawTypeUnion(typeParam1, typeParam2) - : null; - - // Equality. - if (type1 == type2) { - return _RawType( - type1, - nullable1 || nullable2, - computeTypeParamUnion(typeParam1, typeParam2), - ); - } - // This sentinel is only for nullability. - if (type1 == 'JSUndefined') return _RawType(type2, true, typeParam2); - if (type2 == 'JSUndefined') return _RawType(type1, true, typeParam1); - // If the two types are not equal, we can just use `JSNumber` as the union can - // never be `JSInteger` or `JSDouble` anyways. - if (type1 == 'JSInteger' || type1 == 'JSDouble') rawType1.type = 'JSNumber'; - if (type2 == 'JSInteger' || type2 == 'JSDouble') rawType2.type = 'JSNumber'; - - // In the case of unions, we should try and get a JS type-able type to get a - // better LUB. - final unionableType1 = _getJSTypeEquivalent(rawType1) ?? rawType1; - final unionableType2 = _getJSTypeEquivalent(rawType2) ?? rawType2; - - // We choose `JSAny` if they're not both JS types. - return _RawType( - computeJsTypeUnion(unionableType1.type, unionableType2.type) ?? 'JSAny', - unionableType1.nullable || unionableType2.nullable, - computeTypeParamUnion( - unionableType1.typeParameter, - unionableType2.typeParameter, - ), - ); -} - -/// Returns a [_RawType] for the given [idl.IDLType]. -_RawType _getRawType(idl.IDLType idlType) { - // For union types, we take the possible union of all the types using a LUB. - if (idlType.union) { - final types = (idlType.idlType as JSArray).toDart; - final unionType = _getRawType(types[0]); - for (var i = 1; i < types.length; i++) { - unionType.update(types[i]); - } - return unionType..nullable |= idlType.nullable; - } - String type; - var nullable = idlType.nullable; - _RawType? typeParameter; - if (idlType.generic.isNotEmpty) { - final types = (idlType.idlType as JSArray).toDart; - if (types.length == 1) { - typeParameter = _getRawType(types[0]); - } else if (types.length > 1) { - assert(types.length == 2); - assert(idlType.generic == 'record'); - } - type = idlType.generic; - } else { - type = (idlType.idlType as JSString).toDart; - } - - // Handles types that don't exist in the set of IDL type declarations. They - // are either some special values or JS builtin types. - - // `WindowProxy` doesn't exist as an interface in the IDL. For our purposes, - // `Window` is the appropriate interface. - if (type == 'WindowProxy') type = 'Window'; - // `any` is marked non-nullable in the IDL, but since it is a union of - // `undefined`, it can be nullable for our purposes. - if (type == 'any') nullable = true; - final translator = Translator.instance!; - final decl = translator._typeToDeclaration[type]; - final alias = idlOrBuiltinToJsTypeAliases[type]; - assert(decl != null || alias != null); - if (alias == null && !translator.markTypeAsUsed(type)) { - // If the type is an IDL type that is never generated, use its JS type - // equivalent. - type = _getJSTypeEquivalent(_RawType(type, false))!.type; - } - return _RawType(alias ?? type, nullable, typeParameter); -} - -/// A class representing either a type that corresponds to an IDL declaration or -/// a `dart:js_interop` JS types (including sentinels). -/// -/// This should not include IDL types for which there isn't a declaration e.g. -/// `any` or a JS built-in type e.g. `ArrayBuffer`. -class _RawType { - String type; - bool nullable; - _RawType? typeParameter; - - _RawType(this.type, this.nullable, [this.typeParameter]) { - // While the IDL does not define `undefined` as nullable, it is treated as - // null in interop. - if (type == 'JSUndefined') nullable = true; - } - - void update(idl.IDLType idlType) { - final union = _computeRawTypeUnion(this, _getRawType(idlType)); - type = union.type; - nullable = union.nullable; - typeParameter = union.typeParameter; - } - - @override - String toString() => - '_RawType(type: $type, nullable: $nullable, ' - 'typeParameter: $typeParameter)'; -} - -class _Parameter { - final Set _names; - final _RawType type; - bool isOptional; - bool isVariadic; - late final String name = _generateName(); - - _Parameter._(this._names, this.type, this.isOptional, this.isVariadic); - - factory _Parameter(idl.Argument argument) => _Parameter._( - {argument.name}, - _getRawType(argument.idlType), - argument.optional, - argument.variadic, - ); - - String _generateName() { - final namesList = _names.toList(); - namesList.sort(); - return namesList - .sublist(0, 1) - .followedBy(namesList.sublist(1).map(capitalize)) - .join('Or'); - } - - void update(idl.Argument argument) { - final thatName = argument.name; - _names.add(thatName); - type.update(argument.idlType); - if (argument.optional) { - isOptional = true; - } - if (argument.variadic) { - isVariadic = true; - } - } -} - -sealed class _Property { - late final _MemberName name; - final _RawType type; - final MdnProperty? mdnProperty; - - // TODO(srujzs): Remove ignore after - // https://github.com/dart-lang/sdk/issues/55720 is resolved. - // ignore: unused_element_parameter - _Property(_MemberName name, idl.IDLType idlType, [this.mdnProperty]) - : type = _getRawType(idlType) { - // Rename the property if there's a collision with the type name. - final dartName = name.name; - final jsName = name.jsOverride.isEmpty ? dartName : name.jsOverride; - this.name = dartName == type.type - ? _MemberName('${dartName}_', jsName) - : name; - } -} - -class _Attribute extends _Property { - final bool isStatic; - final bool isReadOnly; - - _Attribute( - super.name, - super.idlType, - super.mdnProperty, { - required this.isStatic, - required this.isReadOnly, - }); -} - -class _Field extends _Property { - final bool isRequired; - - _Field( - super.name, - super.idlType, - super.mdnProperty, { - required this.isRequired, - }); -} - -class _Constant extends _Property { - final String valueType; - final JSAny value; - _Constant(super.name, super.idlType, this.valueType, this.value); -} - -abstract class _OverridableMember { - final List<_Parameter> parameters = []; - - _OverridableMember(JSArray rawParameters) { - for (var i = 0; i < rawParameters.length; i++) { - parameters.add(_Parameter(rawParameters[i])); - } - } - - void _processParameters(JSArray thoseParameters) { - // Assume if we have extra arguments beyond what was provided in some other - // method, that these are all optional. - final thatLength = thoseParameters.length; - for (var i = thatLength; i < parameters.length; i++) { - parameters[i].isOptional = true; - } - for (var i = 0; i < thatLength; i++) { - final argument = thoseParameters[i]; - if (i >= parameters.length) { - // We assume these parameters must be optional, regardless of what the - // IDL says. - parameters.add(_Parameter(argument)..isOptional = true); - } else { - parameters[i].update(argument); - } - } - } -} - -class _OverridableOperation extends _OverridableMember { - bool _finalized = false; - _MemberName _name; - - final String special; - final _RawType returnType; - final MdnProperty? mdnProperty; - late final _MemberName name = _generateName(); - - _OverridableOperation._( - this._name, - this.special, - this.returnType, - this.mdnProperty, - super.parameters, - ); - - factory _OverridableOperation( - idl.Operation operation, - _MemberName memberName, - MdnProperty? mdnProperty, - ) => _OverridableOperation._( - memberName, - operation.special, - _getRawType(operation.idlType), - mdnProperty, - operation.arguments, - ); - - bool get isStatic => special == 'static'; - - _MemberName _generateName() { - // The name is determined after all updates are done, so finalize the - // operation. - _finalized = true; - // Rename the member if the name collides with a return or parameter type. - final dartName = _name.name; - if (dartName == returnType.type || - parameters.any((parameter) => dartName == parameter.type.type)) { - underscoreName(); - } - return _name; - } - - void underscoreName() { - final jsName = _name.jsOverride.isEmpty ? _name.name : _name.jsOverride; - _name = _MemberName('${_name.name}_', jsName); - } - - void update(idl.Operation that) { - assert( - !_finalized, - 'Call to _OverridableOperation.update was made after the operation was ' - 'finalized.', - ); - final jsOverride = _name.jsOverride; - final thisName = jsOverride.isNotEmpty ? jsOverride : _name.name; - assert( - (that.name.isEmpty || thisName == that.name) && special == that.special, - ); - returnType.update(that.idlType); - _processParameters(that.arguments); - } -} - -class _OverridableConstructor extends _OverridableMember { - _OverridableConstructor(idl.Constructor constructor) - : super(constructor.arguments); - - void update(idl.Constructor that) => _processParameters(that.arguments); -} - -class _PartialInterfacelike { - final String name; - final String type; - String? inheritance; - final Map operations = {}; - final Map staticOperations = {}; - final List<_Property> properties = []; - final List<_Property> extensionProperties = []; - final MdnInterface? mdnInterface; - _OverridableConstructor? constructor; - - _PartialInterfacelike._( - this.name, - this.type, - String? inheritance, - this.mdnInterface, - ) { - _setInheritance(inheritance); - } - - factory _PartialInterfacelike( - idl.Interfacelike interfacelike, - MdnInterface? mdnInterface, - ) { - final partialInterfacelike = _PartialInterfacelike._( - interfacelike.name, - interfacelike.type, - interfacelike.inheritance, - mdnInterface, - ); - partialInterfacelike._processMembers(interfacelike.members); - return partialInterfacelike; - } - - void _processMembers(JSArray nodeMembers) { - for (var i = 0; i < nodeMembers.length; i++) { - final member = nodeMembers[i]; - final type = member.type; - switch (type) { - case 'constructor': - if (!_shouldGenerateMember(name)) break; - final idlConstructor = member as idl.Constructor; - if (_hasHTMLConstructorAttribute(idlConstructor)) break; - if (constructor == null) { - constructor = _OverridableConstructor(idlConstructor); - } else { - constructor!.update(idlConstructor); - } - break; - case 'const': - final constant = member as idl.Constant; - // Note that constants do not have browser compatibility data, so we - // always emit. - properties.add( - _Constant( - _MemberName(constant.name), - constant.idlType, - constant.value.type, - constant.value.value, - ), - ); - break; - case 'attribute': - final attribute = member as idl.Attribute; - final isStatic = attribute.special == 'static'; - final attributeName = attribute.name; - if (!_shouldGenerateMember(attributeName, isStatic: isStatic)) break; - // `SVGElement.className` returns an `SVGAnimatedString`, but its - // corresponding setter `Element.className` takes a `String`. As these - // two types are incompatible, we need to move this member to an - // extension instead. As it shares the same name as the getter - // `Element.className`, users will need to apply the extension - // explicitly. - final isExtensionMember = - name == 'SVGElement' && attributeName == 'className'; - final memberList = isExtensionMember - ? extensionProperties - : properties; - memberList.add( - _Attribute( - _MemberName(attributeName), - attribute.idlType, - mdnInterface?.propertyFor(attributeName, isStatic: isStatic), - isStatic: isStatic, - isReadOnly: attribute.readonly, - ), - ); - break; - case 'operation': - final operation = member as idl.Operation; - final special = operation.special; - var operationName = operation.name; - // Some special operations may not have any MDN data and may be given - // a name that is irrelevant to the IDL, so avoid querying in that - // case and always emit. - var shouldQueryMDN = true; - switch (special) { - case 'getter': - if (operationName.isEmpty) { - operationName = 'operator []'; - shouldQueryMDN = false; - } - break; - case 'setter': - if (operationName.isEmpty) { - operationName = 'operator []='; - shouldQueryMDN = false; - } - break; - case 'static': - break; - default: - // TODO(srujzs): Should we handle other special operations, - // unnamed or otherwise? For now, don't emit the unnamed ones and - // do nothing special for the named ones. - if (operationName.isEmpty) continue; - } - final isStatic = operation.special == 'static'; - if (shouldQueryMDN && - !_shouldGenerateMember(operationName, isStatic: isStatic)) { - break; - } - final docs = shouldQueryMDN - ? mdnInterface?.propertyFor(operationName, isStatic: isStatic) - : null; - // Static member may have the same name as instance members in the - // IDL, but not in Dart. Rename the static member if so. - if (isStatic) { - if (staticOperations.containsKey(operationName)) { - staticOperations[operationName]!.update(operation); - } else { - staticOperations[operationName] = _OverridableOperation( - operation, - _MemberName(operationName), - docs, - ); - if (operations.containsKey(operationName)) { - staticOperations[operationName]!.underscoreName(); - } - } - } else { - if (operations.containsKey(operationName)) { - operations[operationName]!.update(operation); - } else { - staticOperations[operationName]?.underscoreName(); - operations[operationName] = _OverridableOperation( - operation, - _MemberName(operationName), - docs, - ); - } - } - break; - case 'field': - final field = member as idl.Field; - final fieldName = field.name; - if (!_shouldGenerateMember(fieldName)) break; - properties.add( - _Field( - _MemberName(fieldName), - field.idlType, - mdnInterface?.propertyFor(fieldName, isStatic: false), - isRequired: field.required, - ), - ); - break; - case 'maplike': - case 'setlike': - case 'iterable': - // TODO(srujzs): Generate members for these types. - break; - default: - throw Exception('Unrecognized member type $type'); - } - } - } - - /// Given the [declaredInheritance] by the IDL, find the closest supertype - /// that is actually generated, and set the inheritance equal to that type. - void _setInheritance(String? declaredInheritance) { - if (declaredInheritance == null) return; - final translator = Translator.instance!; - while (declaredInheritance != null) { - if (translator.markTypeAsUsed(declaredInheritance)) { - inheritance = declaredInheritance; - break; - } else { - declaredInheritance = - (translator._typeToDeclaration[declaredInheritance] - as idl.Interfacelike) - .inheritance; - } - } - } - - /// Given a [memberName] and whether it [isStatic], return whether it is a - /// member that should be emitted according to the compat data. - bool _shouldGenerateMember(String memberName, {bool isStatic = false}) { - if (Translator.instance!.browserCompatData.generateAll) return true; - // Compat data only exists for interfaces and namespaces. Mixins and - // dictionaries should always generate their members. - if (type != 'interface' && type != 'namespace') return true; - final interfaceBcd = Translator.instance!.browserCompatData - .retrieveInterfaceFor(name)!; - final bcd = interfaceBcd.retrievePropertyFor( - memberName, - // Compat data treats namespace members as static, but the IDL does not. - isStatic: isStatic || type == 'namespace', - ); - final shouldGenerate = bcd?.shouldGenerate; - if (shouldGenerate != null) return shouldGenerate; - // Events can bubble up to the window, document, or other elements. In the - // case where we have no compatibility data, we assume that an event can - // bubble up to this interface and support the event handler. - if (!isStatic && BrowserCompatData.isEventHandlerSupported(memberName)) { - return true; - } - // TODO(srujzs): Sometimes compatibility data can be up or down the type - // hierarchy, so it may be worth checking supertypes and subtypes. In - // practice, it doesn't seem to make a difference in the output. - return false; - } - - void update(idl.Interfacelike interfacelike) { - assert( - (name == interfacelike.name && type == interfacelike.type) || - interfacelike.type == 'interface mixin', - ); - assert( - interfacelike.inheritance == null || inheritance == null, - 'An interface should only be defined once.', - ); - _setInheritance(interfacelike.inheritance); - _processMembers(interfacelike.members); - } - - // Constructors with the attribute `HTMLConstructor` are intended for custom - // element behavior, and are not useful otherwise, so avoid emitting them. - // https://html.spec.whatwg.org/#html-element-constructors - bool _hasHTMLConstructorAttribute(idl.Constructor constructor) => constructor - .extAttrs - .toDart - .any((extAttr) => extAttr.name == 'HTMLConstructor'); -} - -class _MemberName { - final String name; - final String jsOverride; - - _MemberName._(this.name, this.jsOverride); - - factory _MemberName(String name, [String jsOverride = '']) { - final rename = dartRename(name); - if (rename != name && jsOverride.isEmpty) jsOverride = name; - return _MemberName._(rename, jsOverride); - } -} - class Translator { final String? packageRoot; final String _librarySubDir; @@ -732,13 +113,18 @@ class Translator { final _typeToDeclaration = {}; final _typeToPartials = >{}; final _typeToLibrary = {}; - final _interfacelikes = {}; + final _interfacelikes = {}; final _includes = >{}; final _usedTypes = {}; + Map get typeToDeclaration => _typeToDeclaration; final _renamedClasses = {}; + final _currentDocImports = {}; + final _currentLibraryImports = {}; Map get renamedClasses => _renamedClasses; + final Map loadedRenameMap; + late String _currentlyTranslatingUrl; late DocProvider docProvider; late BrowserCompatData browserCompatData; @@ -753,10 +139,15 @@ class Translator { this.packageRoot, required bool generateAll, bool generateForWeb = true, + this.loadedRenameMap = const {}, + required String bcdJsonPath, }) : _generateForWeb = generateForWeb { instance = this; docProvider = DocProvider.create(); - browserCompatData = BrowserCompatData.read(generateAll: generateAll); + browserCompatData = BrowserCompatData.read( + generateAll: generateAll, + path: bcdJsonPath, + ); } void _addOrUpdateInterfaceLike(idl.Interfacelike interfacelike) { @@ -764,7 +155,7 @@ class Translator { if (_interfacelikes.containsKey(name)) { _interfacelikes[name]!.update(interfacelike); } else { - _interfacelikes[name] = _PartialInterfacelike( + _interfacelikes[name] = PartialInterfacelike( interfacelike, docProvider.interfaceFor(name), ); @@ -855,7 +246,7 @@ class Translator { return true; case 'typedef': _usedTypes.add(decl); - final desugaredType = _desugarTypedef(_RawType(type, false))!.type; + final desugaredType = desugarTypedef(RawType(type, false))!.type; markTypeAsUsed(desugaredType); return true; case 'enum': @@ -910,15 +301,127 @@ class Translator { _libraries[libraryPath] = library; } - code.TypeDef _typedef(String name, _RawType rawType) => code.TypeDef( + List _generateUnionDocs(idl.IDLType idlType) { + if (!idlType.union) return []; + final types = (idlType.idlType as JSArray).toDart; + + for (final t in types) { + _collectDocImports(t); + } + + final typeNames = types.map(_getTypeNameRaw).toList(); + final uniqueNames = typeNames.toSet().toList(); + + if (uniqueNames.length <= 1) return []; + + final formattedNames = uniqueNames.map((name) { + final decl = _typeToDeclaration[name]; + if (decl != null && _usedTypes.contains(decl)) { + return '[$name]'; + } + // If it's a generic type (contains <), use fancy formatting. + if (name.contains('<')) { + final parts = name.split('<'); + final base = parts[0]; + final generic = parts[1].replaceAll('>', ''); + final genericParts = generic.split(',').map((s) => s.trim()); + final linkedGenericParts = genericParts + .map((part) => '[$part]') + .join(', '); + return '[$base]\\<$linkedGenericParts\\>'; + } + // Link if it's a mapped primitive or a valid JS interop type from + // supertypes map. + if (_mapIdlPrimitiveToDart(name) != null || + jsTypeSupertypes.containsKey(name)) { + return '[$name]'; + } + return '`$name`'; + }).toList(); + + formattedNames.sort(); + + final singleLine = '/// Union of: ${formattedNames.join(', ')}'; + if (singleLine.length > 80) { + return [ + '/// Union of ${formattedNames.length} types', + '///', + for (final name in formattedNames) '/// - $name', + ]; + } + + return [singleLine]; + } + + void _collectDocImports(idl.IDLType idlType) { + if (idlType.union || idlType.generic.isNotEmpty) { + final types = (idlType.idlType as JSArray).toDart; + for (final t in types) { + _collectDocImports(t); + } + return; + } + final name = (idlType.idlType as JSString).toDart; + final library = _typeToLibrary[name]; + if (library != null && library.url != _currentlyTranslatingUrl) { + _currentDocImports.add(library.url); + } + } + + String? _mapIdlPrimitiveToDart(String idlType) { + if (idlType == 'WindowProxy') return 'Window'; + final alias = idlOrBuiltinToJsTypeAliases[idlType]; + if (alias == 'JSObject' && idlType != 'object') return null; + if (alias == 'JSInteger' || alias == 'JSDouble') return 'JSNumber'; + return alias; + } + + String _getTypeNameRaw(idl.IDLType idlType) { + if (idlType.union) { + final types = (idlType.idlType as JSArray).toDart; + return types.map(_getTypeNameRaw).join(' | '); + } + if (idlType.generic.isNotEmpty) { + final types = (idlType.idlType as JSArray).toDart; + final genericName = + idlOrBuiltinToJsTypeAliases[idlType.generic] ?? idlType.generic; + if (types.length == 1) { + return '$genericName<${_getTypeNameRaw(types[0])}>'; + } + if (types.length > 1) { + return '$genericName<${types.map(_getTypeNameRaw).join(', ')}>'; + } + return genericName; + } + final name = (idlType.idlType as JSString).toDart; + + final mapped = _mapIdlPrimitiveToDart(name); + if (mapped != null) { + return mapped; + } + + final alias = idlOrBuiltinToJsTypeAliases[name]; + if (alias == 'JSObject' && name != 'object') { + return name; + } + + return alias ?? name; + } + + code.TypeDef _typedef( + String name, + RawType rawType, [ + idl.Typedef? idlTypedef, + ]) => code.TypeDef( (b) => b ..name = name - // Any typedefs that need to be handled differently when used in a return - // type context will be handled in `_typeReference` separately. - ..definition = _typeReference(rawType), + ..definition = _typeReference(rawType) + ..docs.addAll([ + if (idlTypedef != null) ..._generateUnionDocs(idlTypedef.idlType), + ]), ); - code.Method _topLevelGetter(_RawType type, String getterName) => code.Method( + code.Method _topLevelGetter(RawType type, String getterName) => code.Method( (b) => b ..annotations.addAll(_jsOverride('', alwaysEmit: true)) ..external = true @@ -937,7 +440,7 @@ class Translator { /// rather only emit a valid interop type. This is used for type arguments as /// they are bound to `JSAny?`. code.TypeReference _typeReference( - _RawType type, { + RawType type, { bool returnType = false, bool onlyEmitInteropTypes = false, }) { @@ -956,7 +459,7 @@ class Translator { // unused as they were ever only used in a generic. Should we delete them // or do they provide value to users? If we do delete them, a good way of // detecting if they're unused is making `_usedTypes` a ref counter. - final rawType = _desugarTypedef(type); + final rawType = desugarTypedef(type); if (rawType != null && jsTypeToDartPrimitiveAliases.containsKey(rawType.type)) { dartType = rawType.type; @@ -967,7 +470,7 @@ class Translator { 'JSInteger' => 'JSNumber', 'JSDouble' => 'JSNumber', // When the result is `undefined`, we use `JSAny?`. We explicitly - // declare `JSUndefined` `_RawType`s to be nullable, so no need to set + // declare `JSUndefined` `RawType`s to be nullable, so no need to set // nullable. 'JSUndefined' => 'JSAny', _ => dartType, @@ -981,7 +484,7 @@ class Translator { // them or do they provide value to users? If we do delete them, a good // way of detecting if they're unused is making `_usedTypes` a ref // counter. - final rawType = _desugarTypedef(type); + final rawType = desugarTypedef(type); final underlyingType = rawType?.type ?? dartType; if (underlyingType == 'JSDouble') dartType = 'double'; } @@ -997,6 +500,7 @@ class Translator { ); } final url = _urlForType(dartType); + return code.TypeReference( (b) => b ..symbol = dartType @@ -1021,14 +525,15 @@ class Translator { // Else is a core type, so no import required. } else if (url == _currentlyTranslatingUrl) { url = null; - } else if (p.dirname(url) == p.dirname(_currentlyTranslatingUrl)) { - url = p.basename(url); + } else { + _currentLibraryImports.add(url); + url = p.url.relative(url, from: p.url.dirname(_currentlyTranslatingUrl)); } return url; } T _overridableMember( - _OverridableMember member, + OverridableMember member, T Function( List requiredParameters, List optionalParameters, @@ -1065,7 +570,7 @@ class Translator { return generator(requiredParameters, optionalParameters); } - code.Constructor _constructor(_OverridableConstructor constructor) => + code.Constructor _constructor(OverridableConstructor constructor) => _overridableMember( constructor, (requiredParameters, optionalParameters) => code.Constructor( @@ -1100,7 +605,7 @@ class Translator { for (final property in interfacelike.properties) { // We currently only lower dictionaries to object literals, and // dictionaries can only have 'field' members. - final field = property as _Field; + final field = property as Field; final isRequired = field.isRequired; final parameter = code.Parameter( (b) => b @@ -1155,7 +660,7 @@ class Translator { ]), ]; - code.Method _operation(_OverridableOperation operation) { + code.Method _operation(OverridableOperation operation) { final memberName = operation.name; // The IDL may return the value that is set. Dart doesn't let us use any // type besides `void` for `[]=`, so we ignore the return value. @@ -1179,7 +684,7 @@ class Translator { } List _getterSetter({ - required _MemberName memberName, + required MemberName memberName, required code.Reference Function() getGetterType, required code.Reference Function() getSetterType, required bool isStatic, @@ -1222,7 +727,7 @@ class Translator { } List _attribute( - _Attribute attribute, + Attribute attribute, MdnInterface? mdnInterface, ) { return _getterSetter( @@ -1235,7 +740,7 @@ class Translator { ); } - (List, List) _constant(_Constant constant) { + (List, List) _constant(Constant constant) { // If it's a value type that we can emit directly in Dart as a constant, // emit this as a field so users can `switch` over it. Value types taken // from: https://github.com/w3c/webidl2.js/blob/main/README.md#default-and-const-values @@ -1280,7 +785,7 @@ class Translator { ); } - List _field(_Field field, MdnInterface? mdnInterface) { + List _field(Field field, MdnInterface? mdnInterface) { return _getterSetter( memberName: field.name, getGetterType: () => _typeReference(field.type, returnType: true), @@ -1292,23 +797,23 @@ class Translator { } (List, List) _property( - _Property member, + Property member, MdnInterface? mdnInterface, ) => switch (member) { - _Attribute() => ([], _attribute(member, mdnInterface)), - _Field() => ([], _field(member, mdnInterface)), - _Constant() => _constant(member), + Attribute() => ([], _attribute(member, mdnInterface)), + Field() => ([], _field(member, mdnInterface)), + Constant() => _constant(member), }; (List, List) _properties( - List<_Property> properties, + List properties, MdnInterface? mdnInterface, ) => properties.fold(([], []), (specs, property) { final (fields, methods) = _property(property, mdnInterface); return (specs.$1..addAll(fields), specs.$2..addAll(methods)); }); - List _operations(List<_OverridableOperation> operations) => [ + List _operations(List operations) => [ for (final operation in operations) _operation(operation), ]; @@ -1316,10 +821,10 @@ class Translator { return [ for (final style in _cssStyleDeclarations) ..._getterSetter( - memberName: _MemberName(style), + memberName: MemberName(style), getGetterType: () => - _typeReference(_RawType('JSString', false), returnType: true), - getSetterType: () => _typeReference(_RawType('JSString', false)), + _typeReference(RawType('JSString', false), returnType: true), + getSetterType: () => _typeReference(RawType('JSString', false)), isStatic: false, readOnly: false, mdnInterface: null, @@ -1385,8 +890,8 @@ class Translator { } code.Extension _extension({ - required _RawType type, - required List<_Property> extensionProperties, + required RawType type, + required List extensionProperties, }) { final properties = _properties(extensionProperties, null); return code.Extension( @@ -1405,15 +910,15 @@ class Translator { required MdnInterface? mdnInterface, required BCDInterfaceStatus? interfaceStatus, required List implements, - required _OverridableConstructor? constructor, - required List<_OverridableOperation> operations, - required List<_OverridableOperation> staticOperations, - required List<_Property> properties, + required OverridableConstructor? constructor, + required List operations, + required List staticOperations, + required List properties, required bool isObjectLiteral, }) { final docs = mdnInterface == null ? [] : mdnInterface.formattedDocs; - final jsObject = _typeReference(_RawType('JSObject', false)); + final jsObject = _typeReference(RawType('JSObject', false)); const representationFieldName = '_'; final legacyNameSpace = extendedAttributes .where( @@ -1449,7 +954,7 @@ class Translator { ) ..implements.addAll( implements - .map((interface) => _typeReference(_RawType(interface, false))) + .map((interface) => _typeReference(RawType(interface, false))) .followedBy([jsObject]), ) ..constructors.addAll( @@ -1509,7 +1014,7 @@ class Translator { if (interfacelike.inheritance != null) interfacelike.inheritance!, ]; - final rawType = _RawType(dartClassName, false); + final rawType = RawType(dartClassName, false); if (!isNamespace && jsName != dartClassName) { _renamedClasses[jsName] = dartClassName; @@ -1536,9 +1041,51 @@ class Translator { } code.Library _library(_Library library) => code.Library((b) { + _currentDocImports.clear(); + _currentLibraryImports.clear(); + + final body = [ + for (final typedef in library.typedefs.where(_usedTypes.contains)) + _typedef( + typedef.name, + desugarTypedef(RawType(typedef.name, false))!, + typedef, + ), + for (final callback in library.callbacks.where(_usedTypes.contains)) + _typedef(callback.name, desugarTypedef(RawType(callback.name, false))!), + for (final callbackInterface in library.callbackInterfaces.where( + _usedTypes.contains, + )) + _typedef( + callbackInterface.name, + desugarTypedef(RawType(callbackInterface.name, false))!, + ), + for (final enum_ in library.enums.where(_usedTypes.contains)) + _typedef(enum_.name, desugarTypedef(RawType(enum_.name, false))!), + for (final interfacelike in library.interfacelikes.where( + _usedTypes.contains, + )) + ..._interfacelike(interfacelike), + ]; + + final docImports = + _currentDocImports + .where((url) => !_currentLibraryImports.contains(url)) + .toList() + ..sort(); + if (_generateForWeb) { b.comments.addAll([...licenseHeader, '', ...mozLicenseHeader]); } + + if (docImports.isNotEmpty) { + final currentDir = p.url.dirname(_currentlyTranslatingUrl); + b.docs.addAll([ + for (final url in docImports) + '/// @docImport \'${p.url.relative(url, from: currentDir)}\';', + ]); + } + b ..ignoreForFile.addAll([ // JS constants are allowed to be all uppercased. @@ -1553,31 +1100,7 @@ class Translator { // Once this package moves to an SDK version that contains a fix // for that, this can be removed. ..annotations.addAll(_jsOverride('', alwaysEmit: true)) - ..body.addAll([ - for (final typedef in library.typedefs.where(_usedTypes.contains)) - _typedef( - typedef.name, - _desugarTypedef(_RawType(typedef.name, false))!, - ), - for (final callback in library.callbacks.where(_usedTypes.contains)) - _typedef( - callback.name, - _desugarTypedef(_RawType(callback.name, false))!, - ), - for (final callbackInterface in library.callbackInterfaces.where( - _usedTypes.contains, - )) - _typedef( - callbackInterface.name, - _desugarTypedef(_RawType(callbackInterface.name, false))!, - ), - for (final enum_ in library.enums.where(_usedTypes.contains)) - _typedef(enum_.name, _desugarTypedef(_RawType(enum_.name, false))!), - for (final interfacelike in library.interfacelikes.where( - _usedTypes.contains, - )) - ..._interfacelike(interfacelike), - ]); + ..body.addAll(body); }); code.Library generateRootImport(Iterable files) => code.Library( diff --git a/web_generator/lib/src/type_aliases.dart b/js_interop_gen/lib/src/type_aliases.dart similarity index 100% rename from web_generator/lib/src/type_aliases.dart rename to js_interop_gen/lib/src/type_aliases.dart diff --git a/web_generator/lib/src/type_union.dart b/js_interop_gen/lib/src/type_union.dart similarity index 100% rename from web_generator/lib/src/type_union.dart rename to js_interop_gen/lib/src/type_union.dart diff --git a/web_generator/lib/src/util.dart b/js_interop_gen/lib/src/util.dart similarity index 100% rename from web_generator/lib/src/util.dart rename to js_interop_gen/lib/src/util.dart diff --git a/web_generator/lib/src/utils/case.dart b/js_interop_gen/lib/src/utils/case.dart similarity index 100% rename from web_generator/lib/src/utils/case.dart rename to js_interop_gen/lib/src/utils/case.dart diff --git a/js_interop_gen/pubspec.yaml b/js_interop_gen/pubspec.yaml new file mode 100644 index 00000000..3863bbc8 --- /dev/null +++ b/js_interop_gen/pubspec.yaml @@ -0,0 +1,30 @@ +name: js_interop_gen +version: 1.0.0-wip +description: >- + A tool for generating Dart JS interop bindings from Web IDL and TypeScript + definitions. +repository: https://github.com/dart-lang/web + +environment: + sdk: ^3.10.0 + +dependencies: + analyzer: ^12.0.0 + args: ^2.5.0 + code_builder: ^4.10.0 + collection: ^1.19.1 + dart_style: ^3.1.8 + io: ^1.0.4 + meta: ^1.18.0 + package_config: ^2.1.1 + path: ^1.9.0 + pub_semver: ^2.1.5 + yaml: ^3.1.3 + +dev_dependencies: + dart_flutter_team_lints: ^3.0.0 + test: ^1.31.0 + web: ^1.1.1 + +executables: + js_interop_gen: diff --git a/web_generator/test/assets/basic_config.yaml b/js_interop_gen/test/assets/basic_config.yaml similarity index 100% rename from web_generator/test/assets/basic_config.yaml rename to js_interop_gen/test/assets/basic_config.yaml diff --git a/web_generator/test/assets/config.yaml b/js_interop_gen/test/assets/config.yaml similarity index 91% rename from web_generator/test/assets/config.yaml rename to js_interop_gen/test/assets/config.yaml index 6ceb7dec..6c50be50 100644 --- a/web_generator/test/assets/config.yaml +++ b/js_interop_gen/test/assets/config.yaml @@ -3,7 +3,7 @@ preamble: | // This preamble is for testing only. // GENERATED FILE: DO NOT EDIT // - // Created by `web_generator` + // Created by `js_interop_gen` input: 'test.d.ts' output: '../../.dart_tool/test_config.dart' include: diff --git a/web_generator/test/assets/invalid.d.ts b/js_interop_gen/test/assets/invalid.d.ts similarity index 100% rename from web_generator/test/assets/invalid.d.ts rename to js_interop_gen/test/assets/invalid.d.ts diff --git a/web_generator/test/assets/invalid_config.yaml b/js_interop_gen/test/assets/invalid_config.yaml similarity index 100% rename from web_generator/test/assets/invalid_config.yaml rename to js_interop_gen/test/assets/invalid_config.yaml diff --git a/web_generator/test/assets/invalid_output_config.yaml b/js_interop_gen/test/assets/invalid_output_config.yaml similarity index 100% rename from web_generator/test/assets/invalid_output_config.yaml rename to js_interop_gen/test/assets/invalid_output_config.yaml diff --git a/web_generator/test/assets/multi_file_config.yaml b/js_interop_gen/test/assets/multi_file_config.yaml similarity index 100% rename from web_generator/test/assets/multi_file_config.yaml rename to js_interop_gen/test/assets/multi_file_config.yaml diff --git a/web_generator/test/assets/test.d.ts b/js_interop_gen/test/assets/test.d.ts similarity index 100% rename from web_generator/test/assets/test.d.ts rename to js_interop_gen/test/assets/test.d.ts diff --git a/web_generator/test/assets/test_config.dart b/js_interop_gen/test/assets/test_config.dart similarity index 98% rename from web_generator/test/assets/test_config.dart rename to js_interop_gen/test/assets/test_config.dart index 13a0225c..d85d6cbb 100644 --- a/web_generator/test/assets/test_config.dart +++ b/js_interop_gen/test/assets/test_config.dart @@ -1,7 +1,7 @@ // This preamble is for testing only. // GENERATED FILE: DO NOT EDIT // -// Created by `web_generator` +// Created by `js_interop_gen` // ignore_for_file: constant_identifier_names, non_constant_identifier_names diff --git a/web_generator/test/assets/test_no_config.dart b/js_interop_gen/test/assets/test_no_config.dart similarity index 100% rename from web_generator/test/assets/test_no_config.dart rename to js_interop_gen/test/assets/test_no_config.dart diff --git a/web_generator/test/assets/unsupported_test.d.ts b/js_interop_gen/test/assets/unsupported_test.d.ts similarity index 100% rename from web_generator/test/assets/unsupported_test.d.ts rename to js_interop_gen/test/assets/unsupported_test.d.ts diff --git a/web_generator/test/assets/unsupported_test_expected.dart b/js_interop_gen/test/assets/unsupported_test_expected.dart similarity index 100% rename from web_generator/test/assets/unsupported_test_expected.dart rename to js_interop_gen/test/assets/unsupported_test_expected.dart diff --git a/web_generator/test/config_gen_test.dart b/js_interop_gen/test/config_gen_test.dart similarity index 85% rename from web_generator/test/config_gen_test.dart rename to js_interop_gen/test/config_gen_test.dart index 5a1ff64e..fc8d03f7 100644 --- a/web_generator/test/config_gen_test.dart +++ b/js_interop_gen/test/config_gen_test.dart @@ -5,25 +5,19 @@ @TestOn('vm') library; +import 'package:js_interop_gen/src/cli.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'package:web_generator/src/cli.dart'; import 'test_shared.dart'; void main() { - final bindingsGenPath = p.join('lib', 'src'); - group('Config Gen Test', () { final assetsPath = p.join('test', 'assets'); final outputPath = p.join('.dart_tool'); setUpAll(() async { - // set up npm - await runProc('npm', ['install'], workingDirectory: bindingsGenPath); - - // compile file - await compileDartMain(dir: bindingsGenPath); + await compileBindingsGen(); }); final inputFile = p.join(assetsPath, 'test.d.ts'); @@ -35,7 +29,7 @@ void main() { final inputFilePath = p.relative(inputFile, from: bindingsGenPath); final outFilePath = p.relative(outputFile, from: bindingsGenPath); - await runProc('node', [ + await runNode([ 'main.mjs', '--input=$inputFilePath', '--output=$outFilePath', @@ -54,7 +48,7 @@ void main() { final outFilePath = p.relative(outputFile, from: bindingsGenPath); final configFilePath = p.relative(configFile, from: bindingsGenPath); - await runProc('node', [ + await runNode([ 'main.mjs', '--input=$inputFilePath', '--output=$outFilePath', diff --git a/web_generator/test/generate_docs_test.dart b/js_interop_gen/test/generate_docs_test.dart similarity index 98% rename from web_generator/test/generate_docs_test.dart rename to js_interop_gen/test/generate_docs_test.dart index 6cad99e2..8e6cb4b8 100644 --- a/web_generator/test/generate_docs_test.dart +++ b/js_interop_gen/test/generate_docs_test.dart @@ -2,10 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:js_interop_gen/src/formatting.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/formatting.dart'; - void main() { group('formatDocs', () { test('simple', () { diff --git a/web_generator/test/hasher_test.dart b/js_interop_gen/test/hasher_test.dart similarity index 94% rename from web_generator/test/hasher_test.dart rename to js_interop_gen/test/hasher_test.dart index 5c619bd8..d3714b53 100644 --- a/web_generator/test/hasher_test.dart +++ b/js_interop_gen/test/hasher_test.dart @@ -1,7 +1,7 @@ import 'dart:math'; +import 'package:js_interop_gen/src/interop_gen/hasher.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/interop_gen/hasher.dart'; void main() { test('mul32 fuzz', () { diff --git a/web_generator/test/integration/idl/callbacks_expected.dart b/js_interop_gen/test/integration/idl/callbacks_expected.dart similarity index 100% rename from web_generator/test/integration/idl/callbacks_expected.dart rename to js_interop_gen/test/integration/idl/callbacks_expected.dart diff --git a/web_generator/test/integration/idl/callbacks_input.idl b/js_interop_gen/test/integration/idl/callbacks_input.idl similarity index 100% rename from web_generator/test/integration/idl/callbacks_input.idl rename to js_interop_gen/test/integration/idl/callbacks_input.idl diff --git a/web_generator/test/integration/idl/constructors_expected.dart b/js_interop_gen/test/integration/idl/constructors_expected.dart similarity index 100% rename from web_generator/test/integration/idl/constructors_expected.dart rename to js_interop_gen/test/integration/idl/constructors_expected.dart diff --git a/web_generator/test/integration/idl/constructors_input.idl b/js_interop_gen/test/integration/idl/constructors_input.idl similarity index 100% rename from web_generator/test/integration/idl/constructors_input.idl rename to js_interop_gen/test/integration/idl/constructors_input.idl diff --git a/web_generator/test/integration/idl/dictionaries_expected.dart b/js_interop_gen/test/integration/idl/dictionaries_expected.dart similarity index 100% rename from web_generator/test/integration/idl/dictionaries_expected.dart rename to js_interop_gen/test/integration/idl/dictionaries_expected.dart diff --git a/web_generator/test/integration/idl/dictionaries_input.idl b/js_interop_gen/test/integration/idl/dictionaries_input.idl similarity index 100% rename from web_generator/test/integration/idl/dictionaries_input.idl rename to js_interop_gen/test/integration/idl/dictionaries_input.idl diff --git a/web_generator/test/integration/idl/enum_expected.dart b/js_interop_gen/test/integration/idl/enum_expected.dart similarity index 100% rename from web_generator/test/integration/idl/enum_expected.dart rename to js_interop_gen/test/integration/idl/enum_expected.dart diff --git a/web_generator/test/integration/idl/enum_input.idl b/js_interop_gen/test/integration/idl/enum_input.idl similarity index 100% rename from web_generator/test/integration/idl/enum_input.idl rename to js_interop_gen/test/integration/idl/enum_input.idl diff --git a/web_generator/test/integration/idl/indexers_expected.dart b/js_interop_gen/test/integration/idl/indexers_expected.dart similarity index 100% rename from web_generator/test/integration/idl/indexers_expected.dart rename to js_interop_gen/test/integration/idl/indexers_expected.dart diff --git a/web_generator/test/integration/idl/indexers_input.idl b/js_interop_gen/test/integration/idl/indexers_input.idl similarity index 100% rename from web_generator/test/integration/idl/indexers_input.idl rename to js_interop_gen/test/integration/idl/indexers_input.idl diff --git a/js_interop_gen/test/integration/idl/inheritance_fallback_expected.dart b/js_interop_gen/test/integration/idl/inheritance_fallback_expected.dart new file mode 100644 index 00000000..c84549f1 --- /dev/null +++ b/js_interop_gen/test/integration/idl/inheritance_fallback_expected.dart @@ -0,0 +1,13 @@ +// Generated from Web IDL definitions. + +// ignore_for_file: constant_identifier_names, non_constant_identifier_names + +@JS() +library; + +import 'dart:js_interop'; + +extension type SubInterface._(JSObject _) implements JSObject { + external double get value; + external set value(num value); +} diff --git a/js_interop_gen/test/integration/idl/inheritance_fallback_input.idl b/js_interop_gen/test/integration/idl/inheritance_fallback_input.idl new file mode 100644 index 00000000..255e6c3f --- /dev/null +++ b/js_interop_gen/test/integration/idl/inheritance_fallback_input.idl @@ -0,0 +1,3 @@ +interface SubInterface : NonExistentSuper { + attribute double value; +}; diff --git a/web_generator/test/integration/idl/interfaces_expected.dart b/js_interop_gen/test/integration/idl/interfaces_expected.dart similarity index 100% rename from web_generator/test/integration/idl/interfaces_expected.dart rename to js_interop_gen/test/integration/idl/interfaces_expected.dart diff --git a/web_generator/test/integration/idl/interfaces_input.idl b/js_interop_gen/test/integration/idl/interfaces_input.idl similarity index 100% rename from web_generator/test/integration/idl/interfaces_input.idl rename to js_interop_gen/test/integration/idl/interfaces_input.idl diff --git a/web_generator/test/integration/idl/methods_expected.dart b/js_interop_gen/test/integration/idl/methods_expected.dart similarity index 100% rename from web_generator/test/integration/idl/methods_expected.dart rename to js_interop_gen/test/integration/idl/methods_expected.dart diff --git a/web_generator/test/integration/idl/methods_input.idl b/js_interop_gen/test/integration/idl/methods_input.idl similarity index 100% rename from web_generator/test/integration/idl/methods_input.idl rename to js_interop_gen/test/integration/idl/methods_input.idl diff --git a/web_generator/test/integration/idl/mixin_interfaces_expected.dart b/js_interop_gen/test/integration/idl/mixin_interfaces_expected.dart similarity index 100% rename from web_generator/test/integration/idl/mixin_interfaces_expected.dart rename to js_interop_gen/test/integration/idl/mixin_interfaces_expected.dart diff --git a/web_generator/test/integration/idl/mixin_interfaces_input.idl b/js_interop_gen/test/integration/idl/mixin_interfaces_input.idl similarity index 100% rename from web_generator/test/integration/idl/mixin_interfaces_input.idl rename to js_interop_gen/test/integration/idl/mixin_interfaces_input.idl diff --git a/js_interop_gen/test/integration/idl/name_collision_expected.dart b/js_interop_gen/test/integration/idl/name_collision_expected.dart new file mode 100644 index 00000000..b2257d18 --- /dev/null +++ b/js_interop_gen/test/integration/idl/name_collision_expected.dart @@ -0,0 +1,14 @@ +// Generated from Web IDL definitions. + +// ignore_for_file: constant_identifier_names, non_constant_identifier_names + +@JS() +library; + +import 'dart:js_interop'; + +extension type CollisionType._(JSObject _) implements JSObject {} +extension type TestCollision._(JSObject _) implements JSObject { + @JS('CollisionType') + external CollisionType CollisionType_(); +} diff --git a/js_interop_gen/test/integration/idl/name_collision_input.idl b/js_interop_gen/test/integration/idl/name_collision_input.idl new file mode 100644 index 00000000..3ab4f4c1 --- /dev/null +++ b/js_interop_gen/test/integration/idl/name_collision_input.idl @@ -0,0 +1,6 @@ +interface CollisionType { +}; + +interface TestCollision { + CollisionType CollisionType(); +}; diff --git a/web_generator/test/integration/idl/namespaces_expected.dart b/js_interop_gen/test/integration/idl/namespaces_expected.dart similarity index 100% rename from web_generator/test/integration/idl/namespaces_expected.dart rename to js_interop_gen/test/integration/idl/namespaces_expected.dart diff --git a/web_generator/test/integration/idl/namespaces_input.idl b/js_interop_gen/test/integration/idl/namespaces_input.idl similarity index 100% rename from web_generator/test/integration/idl/namespaces_input.idl rename to js_interop_gen/test/integration/idl/namespaces_input.idl diff --git a/web_generator/test/integration/idl/properties_expected.dart b/js_interop_gen/test/integration/idl/properties_expected.dart similarity index 100% rename from web_generator/test/integration/idl/properties_expected.dart rename to js_interop_gen/test/integration/idl/properties_expected.dart diff --git a/web_generator/test/integration/idl/properties_input.idl b/js_interop_gen/test/integration/idl/properties_input.idl similarity index 100% rename from web_generator/test/integration/idl/properties_input.idl rename to js_interop_gen/test/integration/idl/properties_input.idl diff --git a/web_generator/test/integration/idl/typedefs_expected.dart b/js_interop_gen/test/integration/idl/typedefs_expected.dart similarity index 56% rename from web_generator/test/integration/idl/typedefs_expected.dart rename to js_interop_gen/test/integration/idl/typedefs_expected.dart index 75109d31..649f3ccb 100644 --- a/web_generator/test/integration/idl/typedefs_expected.dart +++ b/js_interop_gen/test/integration/idl/typedefs_expected.dart @@ -7,7 +7,22 @@ library; import 'dart:js_interop'; +/// Union of 11 types +/// +/// - [JSDataView] +/// - [JSFloat32Array] +/// - [JSFloat64Array] +/// - [JSInt16Array] +/// - [JSInt32Array] +/// - [JSInt8Array] +/// - [JSTypedArray] +/// - [JSUint16Array] +/// - [JSUint32Array] +/// - [JSUint8Array] +/// - [JSUint8ClampedArray] typedef ArrayBufferView = JSObject; + +/// Union of: [ArrayBufferView], [JSArrayBuffer] typedef BufferSource = JSObject; typedef Timestamp = int; extension type DataHandler._(JSObject _) implements JSObject { diff --git a/web_generator/test/integration/idl/typedefs_input.idl b/js_interop_gen/test/integration/idl/typedefs_input.idl similarity index 100% rename from web_generator/test/integration/idl/typedefs_input.idl rename to js_interop_gen/test/integration/idl/typedefs_input.idl diff --git a/web_generator/test/integration/idl/types_expected.dart b/js_interop_gen/test/integration/idl/types_expected.dart similarity index 100% rename from web_generator/test/integration/idl/types_expected.dart rename to js_interop_gen/test/integration/idl/types_expected.dart diff --git a/web_generator/test/integration/idl/types_input.idl b/js_interop_gen/test/integration/idl/types_input.idl similarity index 100% rename from web_generator/test/integration/idl/types_input.idl rename to js_interop_gen/test/integration/idl/types_input.idl diff --git a/js_interop_gen/test/integration/idl/union_docs_expected.dart b/js_interop_gen/test/integration/idl/union_docs_expected.dart new file mode 100644 index 00000000..bce4dfcd --- /dev/null +++ b/js_interop_gen/test/integration/idl/union_docs_expected.dart @@ -0,0 +1,43 @@ +// Generated from Web IDL definitions. + +// ignore_for_file: constant_identifier_names, non_constant_identifier_names + +@JS() +library; + +import 'dart:js_interop'; + +/// Union of 9 types +/// +/// - [JSFloat32Array] +/// - [JSFloat64Array] +/// - [JSInt16Array] +/// - [JSInt32Array] +/// - [JSInt8Array] +/// - [JSUint16Array] +/// - [JSUint32Array] +/// - [JSUint8Array] +/// - [JSUint8ClampedArray] +typedef AllTypedArrays = JSTypedArray; + +/// Union of: [JSArrayBuffer], `SharedArrayBuffer` +typedef BufferUnion = JSObject; + +/// Union of: [JSBoolean], [JSString] +typedef PrimitiveUnion = JSAny; + +/// Union of 2 types +/// +/// - [JSArray]\<[JSString]\> +/// - [JSObject]\<[JSString], [JSString]\> +typedef GenericUnion = JSObject; + +/// Union of: [JSInt16Array], [JSInt32Array], [JSInt8Array], [JSString] +typedef NonCollapsingLongUnion = JSAny; +extension type DummyInterface._(JSObject _) implements JSObject { + external void testAllTypedArrays(AllTypedArrays a); + external void testBufferUnion(BufferUnion b); + external void testPrimitiveUnion(PrimitiveUnion p); + external void testGenericUnion(GenericUnion g); + external void testNonCollapsingLongUnion(NonCollapsingLongUnion n); +} diff --git a/js_interop_gen/test/integration/idl/union_docs_input.idl b/js_interop_gen/test/integration/idl/union_docs_input.idl new file mode 100644 index 00000000..b02ef77b --- /dev/null +++ b/js_interop_gen/test/integration/idl/union_docs_input.idl @@ -0,0 +1,17 @@ +typedef (Int8Array or Int16Array or Int32Array or Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or Float32Array or Float64Array) AllTypedArrays; + +typedef (SharedArrayBuffer or ArrayBuffer) BufferUnion; + +typedef (DOMString or boolean) PrimitiveUnion; + +typedef (sequence or record) GenericUnion; + +typedef (Int8Array or Int16Array or Int32Array or DOMString) NonCollapsingLongUnion; + +interface DummyInterface { + void testAllTypedArrays(AllTypedArrays a); + void testBufferUnion(BufferUnion b); + void testPrimitiveUnion(PrimitiveUnion p); + void testGenericUnion(GenericUnion g); + void testNonCollapsingLongUnion(NonCollapsingLongUnion n); +}; diff --git a/js_interop_gen/test/integration/idl_test.dart b/js_interop_gen/test/integration/idl_test.dart new file mode 100644 index 00000000..8dc738b1 --- /dev/null +++ b/js_interop_gen/test/integration/idl_test.dart @@ -0,0 +1,100 @@ +@TestOn('vm') +library; + +import 'dart:io'; + +import 'package:js_interop_gen/src/cli.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +import '../test_shared.dart'; + +/// Actual test output can be found in `.dart_tool/idl` +void main() { + group('IDL Integration Test', () { + final testGenFolder = p.join('test', 'integration', 'idl'); + final inputDir = Directory(testGenFolder); + final outputDir = p.join('.dart_tool', 'js_interop_gen'); + + setUpAll(() async { + await compileBindingsGen(); + + if (!(await Directory(outputDir).exists())) { + await Directory(outputDir).create(recursive: true); + } + + final dummyBcd = File(p.join(outputDir, 'dummy_bcd.json')); + await dummyBcd.writeAsString('{"api": {}, "webassembly": {"api": {}}}'); + }); + + final inputFiles = + inputDir + .listSync() + .whereType() + .where((f) => p.basenameWithoutExtension(f.path).endsWith('_input')) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + for (final inputFile in inputFiles) { + final inputFileName = p.basenameWithoutExtension(inputFile.path); + final inputName = inputFileName.replaceFirst('_input', ''); + + final outputActualPath = p.join(outputDir, '${inputName}_actual.dart'); + final outputExpectedPath = p.join( + testGenFolder, + '${inputName}_expected.dart', + ); + + test( + inputName, + skip: inputName == 'inheritance_fallback' + ? 'Skipping because fixing the generator crash for missing types ' + 'causes a diff in generated code (dropping Sensor) due to ' + 'compat data interactions. We will fix this in a later ' + 'refactor step.' + : null, + () async { + final inputFilePath = p.relative( + inputFile.path, + from: bindingsGenPath, + ); + final outFilePath = p.relative( + outputActualPath, + from: bindingsGenPath, + ); + + // TODO(kevmoo): Do a more complete cleanup to remove dependency on + // BCD and webref bits entirely from js_interop_gen tests. + final dummyBcdPath = p.relative( + p.join(outputDir, 'dummy_bcd.json'), + from: bindingsGenPath, + ); + + await runNode([ + 'main.mjs', + '--input=$inputFilePath', + '--output=${p.dirname(outFilePath)}', + '--idl', + '--bcd-json=$dummyBcdPath', + ], workingDirectory: bindingsGenPath); + + await File( + p.join(p.dirname(outputActualPath), '${inputName}_input.dart'), + ).rename(outputActualPath); + + expectFilesEqual(outputExpectedPath, outputActualPath); + }, + ); + + tearDownAll(() { + inputDir + .listSync() + .whereType() + .where( + (f) => p.basenameWithoutExtension(f.path).endsWith('_actual'), + ) + .forEach((f) => f.deleteSync()); + }); + } + }); +} diff --git a/web_generator/test/integration/interop_gen/_tuples.dart b/js_interop_gen/test/integration/interop_gen/_tuples.dart similarity index 83% rename from web_generator/test/integration/interop_gen/_tuples.dart rename to js_interop_gen/test/integration/interop_gen/_tuples.dart index 062140d6..1c09e7e6 100644 --- a/web_generator/test/integration/interop_gen/_tuples.dart +++ b/js_interop_gen/test/integration/interop_gen/_tuples.dart @@ -6,8 +6,7 @@ import 'dart:js_interop' as _i1; extension type JSTuple2._( _i1.JSArray<_i1.JSAny?> _ -) - implements _i1.JSArray<_i1.JSAny?> { +) implements _i1.JSArray<_i1.JSAny?> { A get $1 => (_[0] as A); B get $2 => (_[1] as B); @@ -20,8 +19,7 @@ extension type JSTuple3< A extends _i1.JSAny?, B extends _i1.JSAny?, C extends _i1.JSAny? ->._(_i1.JSArray<_i1.JSAny?> _) - implements _i1.JSArray<_i1.JSAny?> { +>._(_i1.JSArray<_i1.JSAny?> _) implements _i1.JSArray<_i1.JSAny?> { A get $1 => (_[0] as A); B get $2 => (_[1] as B); @@ -38,8 +36,7 @@ extension type JSReadonlyTuple3< A extends _i1.JSAny?, B extends _i1.JSAny?, C extends _i1.JSAny? ->._(_i1.JSArray<_i1.JSAny?> _) - implements _i1.JSArray<_i1.JSAny?> { +>._(_i1.JSArray<_i1.JSAny?> _) implements _i1.JSArray<_i1.JSAny?> { A get $1 => (_[0] as A); B get $2 => (_[1] as B); @@ -51,8 +48,7 @@ extension type JSTuple4< B extends _i1.JSAny?, C extends _i1.JSAny?, D extends _i1.JSAny? ->._(_i1.JSArray<_i1.JSAny?> _) - implements _i1.JSArray<_i1.JSAny?> { +>._(_i1.JSArray<_i1.JSAny?> _) implements _i1.JSArray<_i1.JSAny?> { A get $1 => (_[0] as A); B get $2 => (_[1] as B); diff --git a/web_generator/test/integration/interop_gen/classes_expected.dart b/js_interop_gen/test/integration/interop_gen/classes_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/classes_expected.dart rename to js_interop_gen/test/integration/interop_gen/classes_expected.dart diff --git a/web_generator/test/integration/interop_gen/classes_input.d.ts b/js_interop_gen/test/integration/interop_gen/classes_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/classes_input.d.ts rename to js_interop_gen/test/integration/interop_gen/classes_input.d.ts diff --git a/web_generator/test/integration/interop_gen/enum_expected.dart b/js_interop_gen/test/integration/interop_gen/enum_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/enum_expected.dart rename to js_interop_gen/test/integration/interop_gen/enum_expected.dart diff --git a/web_generator/test/integration/interop_gen/enum_input.d.ts b/js_interop_gen/test/integration/interop_gen/enum_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/enum_input.d.ts rename to js_interop_gen/test/integration/interop_gen/enum_input.d.ts diff --git a/web_generator/test/integration/interop_gen/functions_expected.dart b/js_interop_gen/test/integration/interop_gen/functions_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/functions_expected.dart rename to js_interop_gen/test/integration/interop_gen/functions_expected.dart diff --git a/web_generator/test/integration/interop_gen/functions_input.d.ts b/js_interop_gen/test/integration/interop_gen/functions_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/functions_input.d.ts rename to js_interop_gen/test/integration/interop_gen/functions_input.d.ts diff --git a/web_generator/test/integration/interop_gen/import_export_expected.dart b/js_interop_gen/test/integration/interop_gen/import_export_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/import_export_expected.dart rename to js_interop_gen/test/integration/interop_gen/import_export_expected.dart diff --git a/web_generator/test/integration/interop_gen/import_export_input.d.ts b/js_interop_gen/test/integration/interop_gen/import_export_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/import_export_input.d.ts rename to js_interop_gen/test/integration/interop_gen/import_export_input.d.ts diff --git a/web_generator/test/integration/interop_gen/indexed_access_test_expected.dart b/js_interop_gen/test/integration/interop_gen/indexed_access_test_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/indexed_access_test_expected.dart rename to js_interop_gen/test/integration/interop_gen/indexed_access_test_expected.dart diff --git a/web_generator/test/integration/interop_gen/indexed_access_test_input.d.ts b/js_interop_gen/test/integration/interop_gen/indexed_access_test_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/indexed_access_test_input.d.ts rename to js_interop_gen/test/integration/interop_gen/indexed_access_test_input.d.ts diff --git a/web_generator/test/integration/interop_gen/interfaces_expected.dart b/js_interop_gen/test/integration/interop_gen/interfaces_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/interfaces_expected.dart rename to js_interop_gen/test/integration/interop_gen/interfaces_expected.dart diff --git a/web_generator/test/integration/interop_gen/interfaces_input.d.ts b/js_interop_gen/test/integration/interop_gen/interfaces_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/interfaces_input.d.ts rename to js_interop_gen/test/integration/interop_gen/interfaces_input.d.ts diff --git a/web_generator/test/integration/interop_gen/jsdoc_expected.dart b/js_interop_gen/test/integration/interop_gen/jsdoc_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/jsdoc_expected.dart rename to js_interop_gen/test/integration/interop_gen/jsdoc_expected.dart diff --git a/web_generator/test/integration/interop_gen/jsdoc_input.d.ts b/js_interop_gen/test/integration/interop_gen/jsdoc_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/jsdoc_input.d.ts rename to js_interop_gen/test/integration/interop_gen/jsdoc_input.d.ts diff --git a/web_generator/test/integration/interop_gen/literal_test_expected.dart b/js_interop_gen/test/integration/interop_gen/literal_test_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/literal_test_expected.dart rename to js_interop_gen/test/integration/interop_gen/literal_test_expected.dart diff --git a/web_generator/test/integration/interop_gen/literal_test_input.d.ts b/js_interop_gen/test/integration/interop_gen/literal_test_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/literal_test_input.d.ts rename to js_interop_gen/test/integration/interop_gen/literal_test_input.d.ts diff --git a/web_generator/test/integration/interop_gen/namespaces_expected.dart b/js_interop_gen/test/integration/interop_gen/namespaces_expected.dart similarity index 99% rename from web_generator/test/integration/interop_gen/namespaces_expected.dart rename to js_interop_gen/test/integration/interop_gen/namespaces_expected.dart index 50b04bab..dafc9a5f 100644 --- a/web_generator/test/integration/interop_gen/namespaces_expected.dart +++ b/js_interop_gen/test/integration/interop_gen/namespaces_expected.dart @@ -247,8 +247,7 @@ extension type EnterpriseApp_DataServices._(_i1.JSObject _) @_i1.JS('EnterpriseApp.DataServices.IDataService') extension type EnterpriseApp_DataServices_IDataService._( _i1.JSObject _ -) - implements _i1.JSObject { +) implements _i1.JSObject { external _i1.JSArray getAll(); external T getById(String id); external void save(T item); diff --git a/web_generator/test/integration/interop_gen/namespaces_input.d.ts b/js_interop_gen/test/integration/interop_gen/namespaces_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/namespaces_input.d.ts rename to js_interop_gen/test/integration/interop_gen/namespaces_input.d.ts diff --git a/web_generator/test/integration/interop_gen/overload_expected.dart b/js_interop_gen/test/integration/interop_gen/overload_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/overload_expected.dart rename to js_interop_gen/test/integration/interop_gen/overload_expected.dart diff --git a/web_generator/test/integration/interop_gen/overload_input.d.ts b/js_interop_gen/test/integration/interop_gen/overload_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/overload_input.d.ts rename to js_interop_gen/test/integration/interop_gen/overload_input.d.ts diff --git a/web_generator/test/integration/interop_gen/project/config.yaml b/js_interop_gen/test/integration/interop_gen/project/config.yaml similarity index 100% rename from web_generator/test/integration/interop_gen/project/config.yaml rename to js_interop_gen/test/integration/interop_gen/project/config.yaml diff --git a/web_generator/test/integration/interop_gen/project/input/a.d.ts b/js_interop_gen/test/integration/interop_gen/project/input/a.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/project/input/a.d.ts rename to js_interop_gen/test/integration/interop_gen/project/input/a.d.ts diff --git a/web_generator/test/integration/interop_gen/project/input/b.d.ts b/js_interop_gen/test/integration/interop_gen/project/input/b.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/project/input/b.d.ts rename to js_interop_gen/test/integration/interop_gen/project/input/b.d.ts diff --git a/web_generator/test/integration/interop_gen/project/input/c.d.ts b/js_interop_gen/test/integration/interop_gen/project/input/c.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/project/input/c.d.ts rename to js_interop_gen/test/integration/interop_gen/project/input/c.d.ts diff --git a/web_generator/test/integration/interop_gen/project/input/e.d.ts b/js_interop_gen/test/integration/interop_gen/project/input/e.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/project/input/e.d.ts rename to js_interop_gen/test/integration/interop_gen/project/input/e.d.ts diff --git a/web_generator/test/integration/interop_gen/project/input/f.d.ts b/js_interop_gen/test/integration/interop_gen/project/input/f.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/project/input/f.d.ts rename to js_interop_gen/test/integration/interop_gen/project/input/f.d.ts diff --git a/web_generator/test/integration/interop_gen/project/output/a.dart b/js_interop_gen/test/integration/interop_gen/project/output/a.dart similarity index 100% rename from web_generator/test/integration/interop_gen/project/output/a.dart rename to js_interop_gen/test/integration/interop_gen/project/output/a.dart diff --git a/web_generator/test/integration/interop_gen/project/output/b.dart b/js_interop_gen/test/integration/interop_gen/project/output/b.dart similarity index 100% rename from web_generator/test/integration/interop_gen/project/output/b.dart rename to js_interop_gen/test/integration/interop_gen/project/output/b.dart diff --git a/web_generator/test/integration/interop_gen/project/output/c.dart b/js_interop_gen/test/integration/interop_gen/project/output/c.dart similarity index 100% rename from web_generator/test/integration/interop_gen/project/output/c.dart rename to js_interop_gen/test/integration/interop_gen/project/output/c.dart diff --git a/web_generator/test/integration/interop_gen/project/output/f.dart b/js_interop_gen/test/integration/interop_gen/project/output/f.dart similarity index 100% rename from web_generator/test/integration/interop_gen/project/output/f.dart rename to js_interop_gen/test/integration/interop_gen/project/output/f.dart diff --git a/web_generator/test/integration/interop_gen/string_literals_expected.dart b/js_interop_gen/test/integration/interop_gen/string_literals_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/string_literals_expected.dart rename to js_interop_gen/test/integration/interop_gen/string_literals_expected.dart diff --git a/web_generator/test/integration/interop_gen/string_literals_input.d.ts b/js_interop_gen/test/integration/interop_gen/string_literals_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/string_literals_input.d.ts rename to js_interop_gen/test/integration/interop_gen/string_literals_input.d.ts diff --git a/web_generator/test/integration/interop_gen/ts_typing_expected.dart b/js_interop_gen/test/integration/interop_gen/ts_typing_expected.dart similarity index 99% rename from web_generator/test/integration/interop_gen/ts_typing_expected.dart rename to js_interop_gen/test/integration/interop_gen/ts_typing_expected.dart index 36cb5000..d152ba58 100644 --- a/web_generator/test/integration/interop_gen/ts_typing_expected.dart +++ b/js_interop_gen/test/integration/interop_gen/ts_typing_expected.dart @@ -230,8 +230,7 @@ extension type const KeyOf_TypeOf_MyEnum._(String _) { } extension type _AnonymousFunction_3217419._( _i1.JSFunction _ -) - implements _i1.JSFunction { +) implements _i1.JSFunction { external ComposedType call(T object); } extension type ComposedType._(_i1.JSObject _) diff --git a/web_generator/test/integration/interop_gen/ts_typing_input.d.ts b/js_interop_gen/test/integration/interop_gen/ts_typing_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/ts_typing_input.d.ts rename to js_interop_gen/test/integration/interop_gen/ts_typing_input.d.ts diff --git a/web_generator/test/integration/interop_gen/typealias_expected.dart b/js_interop_gen/test/integration/interop_gen/typealias_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/typealias_expected.dart rename to js_interop_gen/test/integration/interop_gen/typealias_expected.dart diff --git a/web_generator/test/integration/interop_gen/typealias_input.d.ts b/js_interop_gen/test/integration/interop_gen/typealias_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/typealias_input.d.ts rename to js_interop_gen/test/integration/interop_gen/typealias_input.d.ts diff --git a/web_generator/test/integration/interop_gen/types_expected.dart b/js_interop_gen/test/integration/interop_gen/types_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/types_expected.dart rename to js_interop_gen/test/integration/interop_gen/types_expected.dart diff --git a/web_generator/test/integration/interop_gen/types_input.d.ts b/js_interop_gen/test/integration/interop_gen/types_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/types_input.d.ts rename to js_interop_gen/test/integration/interop_gen/types_input.d.ts diff --git a/web_generator/test/integration/interop_gen/unsupported_properties_expected.dart b/js_interop_gen/test/integration/interop_gen/unsupported_properties_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/unsupported_properties_expected.dart rename to js_interop_gen/test/integration/interop_gen/unsupported_properties_expected.dart diff --git a/web_generator/test/integration/interop_gen/unsupported_properties_input.d.ts b/js_interop_gen/test/integration/interop_gen/unsupported_properties_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/unsupported_properties_input.d.ts rename to js_interop_gen/test/integration/interop_gen/unsupported_properties_input.d.ts diff --git a/web_generator/test/integration/interop_gen/variables_expected.dart b/js_interop_gen/test/integration/interop_gen/variables_expected.dart similarity index 100% rename from web_generator/test/integration/interop_gen/variables_expected.dart rename to js_interop_gen/test/integration/interop_gen/variables_expected.dart diff --git a/web_generator/test/integration/interop_gen/variables_input.d.ts b/js_interop_gen/test/integration/interop_gen/variables_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/variables_input.d.ts rename to js_interop_gen/test/integration/interop_gen/variables_input.d.ts diff --git a/web_generator/test/integration/interop_gen/web_types_expected.dart b/js_interop_gen/test/integration/interop_gen/web_types_expected.dart similarity index 99% rename from web_generator/test/integration/interop_gen/web_types_expected.dart rename to js_interop_gen/test/integration/interop_gen/web_types_expected.dart index 2ae2b55c..7865225f 100644 --- a/web_generator/test/integration/interop_gen/web_types_expected.dart +++ b/js_interop_gen/test/integration/interop_gen/web_types_expected.dart @@ -69,8 +69,7 @@ extension type AnonymousUnion_3403652._(_i1.JSAny _) extension type HTMLTransformFunc< T extends _i2.HTMLElement, R extends _i2.HTMLElement ->._(_i1.JSObject _) - implements _i1.JSObject { +>._(_i1.JSObject _) implements _i1.JSObject { external R call(T element); } extension type EventManipulationFunc._(_i1.JSObject _) implements _i1.JSObject { diff --git a/web_generator/test/integration/interop_gen/web_types_input.d.ts b/js_interop_gen/test/integration/interop_gen/web_types_input.d.ts similarity index 100% rename from web_generator/test/integration/interop_gen/web_types_input.d.ts rename to js_interop_gen/test/integration/interop_gen/web_types_input.d.ts diff --git a/web_generator/test/integration/interop_gen_test.dart b/js_interop_gen/test/integration/interop_gen_test.dart similarity index 80% rename from web_generator/test/integration/interop_gen_test.dart rename to js_interop_gen/test/integration/interop_gen_test.dart index 34a99d71..a7131d6f 100644 --- a/web_generator/test/integration/interop_gen_test.dart +++ b/js_interop_gen/test/integration/interop_gen_test.dart @@ -7,33 +7,34 @@ library; import 'dart:io'; +import 'package:js_interop_gen/src/cli.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'package:web_generator/src/cli.dart'; import '../test_shared.dart'; /// Actual test output can be found in `.dart_tool/idl` void main() { - final bindingsGenPath = p.join('lib', 'src'); group('Interop Gen Integration Test', () { final testGenFolder = p.join('test', 'integration', 'interop_gen'); final inputDir = Directory(testGenFolder); final outputDir = Directory(p.join('.dart_tool', 'interop_gen')); setUpAll(() async { - // set up npm - await runProc('npm', ['install'], workingDirectory: bindingsGenPath); - - // compile file - await compileDartMain(dir: bindingsGenPath); + await compileBindingsGen(); await outputDir.create(recursive: true); }); - for (final inputFile in inputDir.listSync().whereType().where( - (f) => p.basenameWithoutExtension(f.path).contains('_input'), - )) { + final inputFiles = + inputDir + .listSync() + .whereType() + .where((f) => p.basenameWithoutExtension(f.path).contains('_input')) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + for (final inputFile in inputFiles) { final inputFileName = p.basenameWithoutExtension(inputFile.path); final inputName = inputFileName.replaceFirst('_input.d', ''); @@ -51,7 +52,7 @@ void main() { final inputFilePath = p.relative(inputFile.path, from: bindingsGenPath); final outFilePath = p.relative(outputActualPath, from: bindingsGenPath); // run the entrypoint - await runProc('node', [ + await runNode([ 'main.mjs', '--input=$inputFilePath', '--output=$outFilePath', @@ -75,11 +76,7 @@ void main() { final outputExpectedPath = p.join(testGenFolder, 'output'); setUpAll(() async { - // set up npm - await runProc('npm', ['install'], workingDirectory: bindingsGenPath); - - // compile file - await compileDartMain(dir: bindingsGenPath); + await compileBindingsGen(); await outputDir.create(recursive: true); }); @@ -90,7 +87,7 @@ void main() { from: bindingsGenPath, ); // run the entrypoint - await runProc('node', [ + await runNode([ 'main.mjs', '--config=$inputConfigPath', '--declaration', diff --git a/web_generator/test/invalid_input_test.dart b/js_interop_gen/test/invalid_input_test.dart similarity index 76% rename from web_generator/test/invalid_input_test.dart rename to js_interop_gen/test/invalid_input_test.dart index 752e27f7..10bc8014 100644 --- a/web_generator/test/invalid_input_test.dart +++ b/js_interop_gen/test/invalid_input_test.dart @@ -7,30 +7,27 @@ library; import 'dart:convert'; +import 'package:js_interop_gen/src/cli.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'package:web_generator/src/cli.dart'; + +import 'test_shared.dart'; /// Actual test output can be found in `.dart_tool/idl` void main() { - final bindingsGenPath = p.join('lib', 'src'); group('Interop Gen Integration Test', () { final testFile = p.join('test', 'assets', 'invalid.d.ts'); final outputFile = p.join('.dart_tool', 'interop_gen', 'invalid.dart'); - setUp(() async { - // set up npm - await runProc('npm', ['install'], workingDirectory: bindingsGenPath); - - // compile file - await compileDartMain(dir: bindingsGenPath); + setUpAll(() async { + await compileBindingsGen(); }); test('Expect Parsing to Fail', () async { final inputFilePath = p.relative(testFile, from: bindingsGenPath); final outputFilePath = p.relative(outputFile, from: bindingsGenPath); - final process = await runProcWithResult('node', [ + final process = await runNodeWithResult([ 'main.mjs', '--input=$inputFilePath', '--output=$outputFilePath', diff --git a/web_generator/test/namer_test.dart b/js_interop_gen/test/namer_test.dart similarity index 96% rename from web_generator/test/namer_test.dart rename to js_interop_gen/test/namer_test.dart index 259b540c..1565336d 100644 --- a/web_generator/test/namer_test.dart +++ b/js_interop_gen/test/namer_test.dart @@ -1,5 +1,5 @@ +import 'package:js_interop_gen/src/interop_gen/namer.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/interop_gen/namer.dart'; void main() { group('Namer Test', () { diff --git a/web_generator/test/qualified_name_test.dart b/js_interop_gen/test/qualified_name_test.dart similarity index 97% rename from web_generator/test/qualified_name_test.dart rename to js_interop_gen/test/qualified_name_test.dart index ed19e7fa..24ca1f68 100644 --- a/web_generator/test/qualified_name_test.dart +++ b/js_interop_gen/test/qualified_name_test.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:js_interop_gen/src/interop_gen/qualified_name.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/interop_gen/qualified_name.dart'; final normalStringExamples = { 'A': ['A'], diff --git a/js_interop_gen/test/sdk_version_test.dart b/js_interop_gen/test/sdk_version_test.dart new file mode 100644 index 00000000..77bb0f55 --- /dev/null +++ b/js_interop_gen/test/sdk_version_test.dart @@ -0,0 +1,65 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') +library; + +import 'dart:io'; +import 'dart:isolate'; + +import 'package:js_interop_gen/src/sdk_version.dart'; +import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart'; + +void main() { + test('sdkVersion aligns with pubspec.yaml', () async { + final uri = await Isolate.resolvePackageUri( + Uri.parse('package:js_interop_gen/src/sdk_version.dart'), + ); + expect(uri, isNotNull); + final packageRoot = p.dirname(p.dirname(p.dirname(p.fromUri(uri!)))); + final pubspecFile = File(p.join(packageRoot, 'pubspec.yaml')); + + expect( + pubspecFile.existsSync(), + isTrue, + reason: 'pubspec.yaml not found at ${pubspecFile.path}', + ); + + final pubspecContent = pubspecFile.readAsStringSync(); + final sdkConstraintStr = switch (loadYaml(pubspecContent)) { + {'environment': {'sdk': final String s}} => s, + _ => throw const FormatException( + 'No sdk constraint found in pubspec.yaml', + ), + }; + + final sdkConstraint = VersionConstraint.parse(sdkConstraintStr); + final expectedVersion = deriveLanguageVersion(sdkConstraint); + + expect( + dartLanguageVersion, + equals(expectedVersion), + reason: + 'dartLanguageVersion $dartLanguageVersion does not match expected ' + 'version $expectedVersion derived from minimum of constraint ' + '$sdkConstraint', + ); + }); + + test('language version derived from SDK constraint', () { + void check(String constraintStr, Version expected) { + final constraint = VersionConstraint.parse(constraintStr); + final languageVersion = deriveLanguageVersion(constraint); + expect(languageVersion, equals(expected)); + } + + check('^3.12.2-0', Version(3, 12, 0)); + check('^3.10.0', Version(3, 10, 0)); + check('3.12.0', Version(3, 12, 0)); + check('<4.0.0', Version(0, 0, 0)); + }); +} diff --git a/js_interop_gen/test/test_shared.dart b/js_interop_gen/test/test_shared.dart new file mode 100644 index 00000000..4d29013d --- /dev/null +++ b/js_interop_gen/test/test_shared.dart @@ -0,0 +1,114 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:js_interop_gen/src/cli.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +const _rewriteFiles = false; + +/// The path to the bindings generator source code. +final bindingsGenPath = p.join('lib', 'src'); + +/// If the target JS file already exists and is newer than all the input Dart +/// files, we skip the compile. +Future compileBindingsGen() async { + final lockFile = File(p.join(bindingsGenPath, '.compile.lock')); + final raf = await lockFile.open(mode: FileMode.append); + await raf.lock(FileLock.blockingExclusive); + try { + if (_needsCompile()) { + // set up npm + final packageJson = File(p.join(bindingsGenPath, 'package.json')); + final npmMarker = File(p.join(bindingsGenPath, '.last_npm_install')); + + if (!Directory(p.join(bindingsGenPath, 'node_modules')).existsSync() || + (packageJson.existsSync() && + (!npmMarker.existsSync() || + packageJson.statSync().modified.isAfter( + npmMarker.statSync().modified, + )))) { + final nodeModules = Directory(p.join(bindingsGenPath, 'node_modules')); + try { + if (await nodeModules.exists()) { + await nodeModules.delete(recursive: true); + } + } on FileSystemException catch (e) { + // Ignore if already deleted or if we hit file locks. + if (await nodeModules.exists()) { + print('Warning: Failed to delete node_modules: $e'); + } + } + await runProc('npm', ['install'], workingDirectory: bindingsGenPath); + npmMarker.writeAsStringSync(DateTime.now().toIso8601String()); + } + + // compile file + await compileDartMain(dir: bindingsGenPath); + } + } finally { + await raf.unlock(); + await raf.close(); + } +} + +/// Returns `true` if the JS file needs to be recompiled. +/// +/// Based on the file change times of the Dart files in `bindingsGenPath`. +bool _needsCompile() { + final jsFile = File(p.join(bindingsGenPath, 'dart_main.js')); + if (!jsFile.existsSync()) return true; + + final jsStat = jsFile.statSync(); + + // Check package.json + final packageJson = File(p.join(bindingsGenPath, 'package.json')); + if (packageJson.existsSync() && + packageJson.statSync().modified.isAfter(jsStat.modified)) { + return true; + } + + // Check tsconfig.json + final tsConfig = File(p.join(bindingsGenPath, 'tsconfig.json')); + if (tsConfig.existsSync() && + tsConfig.statSync().modified.isAfter(jsStat.modified)) { + return true; + } + + final srcDir = Directory(bindingsGenPath); + for (final file in srcDir.listSync(recursive: true)) { + if (file is File && file.path.endsWith('.dart')) { + if (file.statSync().modified.isAfter(jsStat.modified)) { + return true; + } + } + } + return false; +} + +void expectFilesEqual(String expectedPath, String actualPath) { + final expectedFile = File(expectedPath); + final actualFile = File(actualPath); + + if (!actualFile.existsSync()) { + fail('Generated file not found: $actualPath'); + } + + final actual = actualFile.readAsStringSync(); + + if (_rewriteFiles) { + expectedFile.writeAsStringSync(actual); + addTearDown(() { + fail('Rewrote $expectedPath'); + }); + } else { + expect( + actual, + expectedFile.readAsStringSync(), + reason: 'Output did not match expected file in $expectedPath', + ); + } +} diff --git a/web_generator/test/type_map_test.dart b/js_interop_gen/test/type_map_test.dart similarity index 97% rename from web_generator/test/type_map_test.dart rename to js_interop_gen/test/type_map_test.dart index 6e09bcca..9dda5689 100644 --- a/web_generator/test/type_map_test.dart +++ b/js_interop_gen/test/type_map_test.dart @@ -5,13 +5,13 @@ @TestOn('node') library; +import 'package:js_interop_gen/src/ast/base.dart'; +import 'package:js_interop_gen/src/ast/builtin.dart'; +import 'package:js_interop_gen/src/ast/declarations.dart'; +import 'package:js_interop_gen/src/ast/types.dart'; +import 'package:js_interop_gen/src/interop_gen/namer.dart'; +import 'package:js_interop_gen/src/interop_gen/sub_type.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/ast/base.dart'; -import 'package:web_generator/src/ast/builtin.dart'; -import 'package:web_generator/src/ast/declarations.dart'; -import 'package:web_generator/src/ast/types.dart'; -import 'package:web_generator/src/interop_gen/namer.dart'; -import 'package:web_generator/src/interop_gen/sub_type.dart'; void main() { group('Type Map Test', () { diff --git a/web_generator/test/type_union_test.dart b/js_interop_gen/test/type_union_test.dart similarity index 96% rename from web_generator/test/type_union_test.dart rename to js_interop_gen/test/type_union_test.dart index a15fe529..a1a768ac 100644 --- a/web_generator/test/type_union_test.dart +++ b/js_interop_gen/test/type_union_test.dart @@ -2,10 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:js_interop_gen/src/type_union.dart'; import 'package:test/test.dart'; -import 'package:web_generator/src/type_union.dart'; - void main() { test('Non-JS types', () { expect(computeJsTypeUnion('Window', 'Document'), null); diff --git a/web_generator/test/unsupported_test.dart b/js_interop_gen/test/unsupported_test.dart similarity index 83% rename from web_generator/test/unsupported_test.dart rename to js_interop_gen/test/unsupported_test.dart index 85385d48..5393edcf 100644 --- a/web_generator/test/unsupported_test.dart +++ b/js_interop_gen/test/unsupported_test.dart @@ -7,15 +7,14 @@ library; import 'dart:convert'; +import 'package:js_interop_gen/src/cli.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'package:web_generator/src/cli.dart'; import 'test_shared.dart'; /// Actual test output can be found in `.dart_tool/idl` void main() { - final bindingsGenPath = p.join('lib', 'src'); group('Interop Gen Unsupported Test', () { final testFile = p.join('test', 'assets', 'unsupported_test.d.ts'); final outputFile = p.join('.dart_tool', 'unsupported_test.dart'); @@ -26,18 +25,14 @@ void main() { ); setUpAll(() async { - // set up npm - await runProc('npm', ['install'], workingDirectory: bindingsGenPath); - - // compile file - await compileDartMain(dir: bindingsGenPath); + await compileBindingsGen(); }); test('Strict Unsupported', () async { final inputFilePath = p.relative(testFile, from: bindingsGenPath); final outputFilePath = p.relative(outputFile, from: bindingsGenPath); - final process = await runProcWithResult('node', [ + final process = await runNodeWithResult([ 'main.mjs', '--input=$inputFilePath', '--output=$outputFilePath', @@ -55,7 +50,7 @@ void main() { final inputFilePath = p.relative(testFile, from: bindingsGenPath); final outputFilePath = p.relative(outputFile, from: bindingsGenPath); - await runProc('node', [ + await runNode([ 'main.mjs', '--input=$inputFilePath', '--output=$outputFilePath', diff --git a/js_interop_gen/test/utils_test.dart b/js_interop_gen/test/utils_test.dart new file mode 100644 index 00000000..51e40ec0 --- /dev/null +++ b/js_interop_gen/test/utils_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('node') +library; + +import 'package:js_interop_gen/src/interop_gen/transform/utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('toCamelCase tests', () { + final testCases = { + 'webkit-appearance': 'webkitAppearance', + '-webkit-appearance': 'webkitAppearance', + 'accept-charset': 'acceptCharset', + 'accept-=charset': 'acceptCharset', + 'accept': 'accept', + '-accept': 'accept', + '': '', + '-': '', + '--': '', + 'a-b': 'aB', + '-a-b': 'aB', + 'Webkit-Appearance': 'webkitAppearance', + 'accept-charset-': 'acceptCharset', + }; + + for (final MapEntry(key: input, value: expected) in testCases.entries) { + test('"$input" -> "$expected"', () { + expect(toCamelCase(input), equals(expected)); + }); + } + }); +} diff --git a/js_interop_gen/tool/update_supertypes.dart b/js_interop_gen/tool/update_supertypes.dart new file mode 100644 index 00000000..e8926085 --- /dev/null +++ b/js_interop_gen/tool/update_supertypes.dart @@ -0,0 +1,18 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'package:js_interop_gen/src/cli.dart'; +import 'package:path/path.dart' as p; + +Future main() async { + print('Updating js_type_supertypes.dart...'); + final content = await computeJsTypeSupertypes(); + final jsTypeSupertypesPath = p.join( + bindingsGeneratorPath, + 'js_type_supertypes.dart', + ); + await File(jsTypeSupertypesPath).writeAsString(content); + print('Done!'); +} diff --git a/web/README.md b/web/README.md index 325c5f58..8c12a0e6 100644 --- a/web/README.md +++ b/web/README.md @@ -1,4 +1,3 @@ -[![package:web](https://github.com/dart-lang/web/actions/workflows/web.yaml/badge.svg)](https://github.com/dart-lang/web/actions/workflows/web.yaml) [![pub package](https://img.shields.io/pub/v/web.svg)](https://pub.dev/packages/web) [![package publisher](https://img.shields.io/pub/publisher/web.svg)](https://pub.dev/packages/web/publisher) diff --git a/web/analysis_options.yaml b/web/analysis_options.yaml index f9cf0aab..28376889 100644 --- a/web/analysis_options.yaml +++ b/web/analysis_options.yaml @@ -12,7 +12,7 @@ analyzer: errors: # 43 instances in generated code. camel_case_types: ignore - # 420 instances in the MDN docs. + # 811 instances in the MDN docs. comment_references: ignore # 14 instances in the MDN docs. lines_longer_than_80_chars: ignore diff --git a/web/lib/src/dom/accelerometer.dart b/web/lib/src/dom/accelerometer.dart index df31ab01..76f9f361 100644 --- a/web/lib/src/dom/accelerometer.dart +++ b/web/lib/src/dom/accelerometer.dart @@ -47,8 +47,9 @@ extension type AccelerometerSensorOptions._(JSObject _) /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/LinearAccelerationSensor). extension type LinearAccelerationSensor._(JSObject _) implements Sensor, JSObject { - external factory LinearAccelerationSensor( - [AccelerometerSensorOptions options]); + external factory LinearAccelerationSensor([ + AccelerometerSensorOptions options, + ]); } /// The **`GravitySensor`** interface of the diff --git a/web/lib/src/dom/angle_instanced_arrays.dart b/web/lib/src/dom/angle_instanced_arrays.dart index 08be0458..00f70d3e 100644 --- a/web/lib/src/dom/angle_instanced_arrays.dart +++ b/web/lib/src/dom/angle_instanced_arrays.dart @@ -90,8 +90,5 @@ extension type ANGLE_instanced_arrays._(JSObject _) implements JSObject { /// > [!NOTE] /// > When using [WebGL2RenderingContext], this method is available as /// > [WebGL2RenderingContext.vertexAttribDivisor] by default. - external void vertexAttribDivisorANGLE( - GLuint index, - GLuint divisor, - ); + external void vertexAttribDivisorANGLE(GLuint index, GLuint divisor); } diff --git a/web/lib/src/dom/background_sync.dart b/web/lib/src/dom/background_sync.dart index c2dd6e22..bc852fd0 100644 --- a/web/lib/src/dom/background_sync.dart +++ b/web/lib/src/dom/background_sync.dart @@ -51,10 +51,7 @@ extension type SyncManager._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/SyncEvent). extension type SyncEvent._(JSObject _) implements ExtendableEvent, JSObject { - external factory SyncEvent( - String type, - SyncEventInit init, - ); + external factory SyncEvent(String type, SyncEventInit init); /// @AvailableInWorkers("service") /// diff --git a/web/lib/src/dom/clipboard_apis.dart b/web/lib/src/dom/clipboard_apis.dart index ced1d956..5d0e8e89 100644 --- a/web/lib/src/dom/clipboard_apis.dart +++ b/web/lib/src/dom/clipboard_apis.dart @@ -150,8 +150,9 @@ extension type Clipboard._(JSObject _) implements EventTarget, JSObject { /// The method can in theory return arbitrary data (unlike /// [Clipboard.readText], which can only return text). /// Browsers commonly support reading text, HTML, and PNG image data. - external JSPromise read( - [ClipboardUnsanitizedFormats formats]); + external JSPromise read([ + ClipboardUnsanitizedFormats formats, + ]); /// The **`readText()`** method of the [Clipboard] interface returns a /// `Promise` which fulfills with a copy of the textual contents of the system diff --git a/web/lib/src/dom/console.dart b/web/lib/src/dom/console.dart index 9c759f9e..00e0bcd5 100644 --- a/web/lib/src/dom/console.dart +++ b/web/lib/src/dom/console.dart @@ -49,50 +49,14 @@ extension type $Console._(JSObject _) implements JSObject { JSAny? data4, ]); external void clear(); - external void debug([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void error([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void info([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void log([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void table([ - JSAny? tabularData, - JSArray properties, - ]); - external void trace([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void warn([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); - external void dir([ - JSAny? item, - JSObject? options, - ]); + external void debug([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void error([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void info([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void log([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void table([JSAny? tabularData, JSArray properties]); + external void trace([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void warn([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); + external void dir([JSAny? item, JSObject? options]); external void dirxml([ JSAny? data1, JSAny? data2, @@ -101,12 +65,7 @@ extension type $Console._(JSObject _) implements JSObject { ]); external void count([String label]); external void countReset([String label]); - external void group([ - JSAny? data1, - JSAny? data2, - JSAny? data3, - JSAny? data4, - ]); + external void group([JSAny? data1, JSAny? data2, JSAny? data3, JSAny? data4]); external void groupCollapsed([ JSAny? data1, JSAny? data2, diff --git a/web/lib/src/dom/cookie_store.dart b/web/lib/src/dom/cookie_store.dart index dc1cd1ad..ba4f52e4 100644 --- a/web/lib/src/dom/cookie_store.dart +++ b/web/lib/src/dom/cookie_store.dart @@ -56,10 +56,7 @@ extension type CookieStore._(JSObject _) implements EventTarget, JSObject { /// /// The **`set()`** method of the [CookieStore] interface sets a cookie with /// the given `name` and `value` or `options` object. - external JSPromise set( - JSAny nameOrOptions, [ - String value, - ]); + external JSPromise set(JSAny nameOrOptions, [String value]); /// @AvailableInWorkers("window_and_service") /// @@ -71,10 +68,7 @@ extension type CookieStore._(JSObject _) implements EventTarget, JSObject { external set onchange(EventHandler value); } extension type CookieStoreGetOptions._(JSObject _) implements JSObject { - external factory CookieStoreGetOptions({ - String name, - String url, - }); + external factory CookieStoreGetOptions({String name, String url}); external String get name; external set name(String value); @@ -179,7 +173,8 @@ extension type CookieStoreManager._(JSObject _) implements JSObject { /// The **`subscribe()`** method of the [CookieStoreManager] interface /// subscribes a [ServiceWorkerRegistration] to cookie change events. external JSPromise subscribe( - JSArray subscriptions); + JSArray subscriptions, + ); /// @AvailableInWorkers("window_and_service") /// @@ -194,7 +189,8 @@ extension type CookieStoreManager._(JSObject _) implements JSObject { /// the [ServiceWorkerRegistration] from receiving previously subscribed /// events. external JSPromise unsubscribe( - JSArray subscriptions); + JSArray subscriptions, + ); } /// The **`CookieChangeEvent`** interface of the [Cookie Store API] is the event diff --git a/web/lib/src/dom/credential_management.dart b/web/lib/src/dom/credential_management.dart index 36514db5..0dc714a1 100644 --- a/web/lib/src/dom/credential_management.dart +++ b/web/lib/src/dom/credential_management.dart @@ -10,6 +10,7 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +/// @docImport 'html.dart'; @JS() library; @@ -21,6 +22,7 @@ import 'fedcm.dart'; import 'web_otp.dart'; import 'webauthn.dart'; +/// Union of: [HTMLFormElement], [PasswordCredentialData] typedef PasswordCredentialInit = JSObject; typedef CredentialMediationRequirement = String; diff --git a/web/lib/src/dom/css_font_loading.dart b/web/lib/src/dom/css_font_loading.dart index 54563dcb..63ca1d24 100644 --- a/web/lib/src/dom/css_font_loading.dart +++ b/web/lib/src/dom/css_font_loading.dart @@ -265,10 +265,7 @@ extension type FontFaceSet._(JSObject _) implements EventTarget, JSObject { /// The `load()` method of the [FontFaceSet] forces all the fonts given in /// parameters to be loaded. - external JSPromise> load( - String font, [ - String text, - ]); + external JSPromise> load(String font, [String text]); /// The `check()` method of the [FontFaceSet] returns `true` if you can render /// some text using the given font specification without attempting to use any @@ -285,10 +282,7 @@ extension type FontFaceSet._(JSObject _) implements EventTarget, JSObject { /// > return `true`. This behavior helps avoid the visual issues associated /// > with font swapping but may be counterintuitive if you're trying to /// > confirm the availability of a specific font. - external bool check( - String font, [ - String text, - ]); + external bool check(String font, [String text]); external EventHandler get onloading; external set onloading(EventHandler value); external EventHandler get onloadingdone; diff --git a/web/lib/src/dom/css_masking.dart b/web/lib/src/dom/css_masking.dart index 3eafa0c1..0d8b73d6 100644 --- a/web/lib/src/dom/css_masking.dart +++ b/web/lib/src/dom/css_masking.dart @@ -29,10 +29,7 @@ extension type SVGClipPathElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGClipPathElement] using the tag 'clipPath'. SVGClipPathElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'clipPath', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath'); /// The read-only **`clipPathUnits`** property of the [SVGClipPathElement] /// interface reflects the `clipPathUnits` attribute of a element which @@ -60,10 +57,7 @@ extension type SVGClipPathElement._(JSObject _) extension type SVGMaskElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGMaskElement] using the tag 'mask'. SVGMaskElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'mask', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'mask'); /// The read-only **`maskUnits`** property of the [SVGMaskElement] interface /// reflects the `maskUnits` attribute of a element which defines the diff --git a/web/lib/src/dom/css_paint_api.dart b/web/lib/src/dom/css_paint_api.dart index d3cff754..5f9ffcef 100644 --- a/web/lib/src/dom/css_paint_api.dart +++ b/web/lib/src/dom/css_paint_api.dart @@ -43,23 +43,10 @@ import 'html.dart'; extension type PaintRenderingContext2D._(JSObject _) implements JSObject { external void save(); external void restore(); - external void scale( - num x, - num y, - ); + external void scale(num x, num y); external void rotate(num angle); - external void translate( - num x, - num y, - ); - external void transform( - num a, - num b, - num c, - num d, - num e, - num f, - ); + external void translate(num x, num y); + external void transform(num a, num b, num c, num d, num e, num f); external DOMMatrix getTransform(); external void setTransform([ JSAny aOrTransform, @@ -70,12 +57,7 @@ extension type PaintRenderingContext2D._(JSObject _) implements JSObject { num f, ]); external void resetTransform(); - external CanvasGradient createLinearGradient( - num x0, - num y0, - num x1, - num y1, - ); + external CanvasGradient createLinearGradient(num x0, num y0, num x1, num y1); external CanvasGradient createRadialGradient( num x0, num y0, @@ -88,45 +70,20 @@ extension type PaintRenderingContext2D._(JSObject _) implements JSObject { CanvasImageSource image, String repetition, ); - external void clearRect( - num x, - num y, - num w, - num h, - ); - external void fillRect( - num x, - num y, - num w, - num h, - ); - external void strokeRect( - num x, - num y, - num w, - num h, - ); + external void clearRect(num x, num y, num w, num h); + external void fillRect(num x, num y, num w, num h); + external void strokeRect(num x, num y, num w, num h); external void beginPath(); - external void fill([ - JSAny fillRuleOrPath, - CanvasFillRule fillRule, - ]); + external void fill([JSAny fillRuleOrPath, CanvasFillRule fillRule]); external void stroke([Path2D path]); - external void clip([ - JSAny fillRuleOrPath, - CanvasFillRule fillRule, - ]); + external void clip([JSAny fillRuleOrPath, CanvasFillRule fillRule]); external bool isPointInPath( JSAny pathOrX, num xOrY, [ JSAny fillRuleOrY, CanvasFillRule fillRule, ]); - external bool isPointInStroke( - JSAny pathOrX, - num xOrY, [ - num y, - ]); + external bool isPointInStroke(JSAny pathOrX, num xOrY, [num y]); external void drawImage( CanvasImageSource image, num dxOrSx, diff --git a/web/lib/src/dom/css_typed_om.dart b/web/lib/src/dom/css_typed_om.dart index 1e35056c..8cbbeba5 100644 --- a/web/lib/src/dom/css_typed_om.dart +++ b/web/lib/src/dom/css_typed_om.dart @@ -17,9 +17,16 @@ import 'dart:js_interop'; import 'geometry.dart'; +/// Union of: [CSSVariableReferenceValue], [JSString] typedef CSSUnparsedSegment = JSAny; + +/// Union of: [CSSKeywordValue], [JSString] typedef CSSKeywordish = JSAny; + +/// Union of: [CSSNumericValue], [JSNumber] typedef CSSNumberish = JSAny; + +/// Union of: [CSSKeywordish], [CSSNumericValue] typedef CSSPerspectiveValue = JSAny; typedef CSSNumericBaseType = String; typedef CSSMathOperator = String; @@ -38,10 +45,7 @@ extension type CSSStyleValue._(JSObject _) implements JSObject { /// interface sets a specific CSS property to the specified values and returns /// the first /// value as a [CSSStyleValue] object. - external static CSSStyleValue parse( - String property, - String cssText, - ); + external static CSSStyleValue parse(String property, String cssText); /// The **`parseAll()`** static method of the [CSSStyleValue] /// interface sets all occurrences of a specific CSS property to the specified @@ -146,10 +150,7 @@ extension type CSSUnparsedValue._(JSObject _) external factory CSSUnparsedValue(JSArray members); external CSSUnparsedSegment operator [](int index); - external void operator []=( - int index, - CSSUnparsedSegment val, - ); + external void operator []=(int index, CSSUnparsedSegment val); /// The **`length`** read-only property of the /// [CSSUnparsedValue] interface returns the number of items in the object. @@ -362,10 +363,7 @@ extension type CSSNumericValue._(JSObject _) /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue). extension type CSSUnitValue._(JSObject _) implements CSSNumericValue, JSObject { - external factory CSSUnitValue( - num value, - String unit, - ); + external factory CSSUnitValue(num value, String unit); /// The **`CSSUnitValue.value`** property of the /// [CSSUnitValue] interface returns a double indicating the number of units. @@ -541,10 +539,7 @@ extension type CSSTransformValue._(JSObject _) external factory CSSTransformValue(JSArray transforms); external CSSTransformComponent operator [](int index); - external void operator []=( - int index, - CSSTransformComponent val, - ); + external void operator []=(int index, CSSTransformComponent val); /// The **`toMatrix()`** method of the /// [CSSTransformValue] interface returns a [DOMMatrix] object. @@ -700,11 +695,7 @@ extension type CSSRotate._(JSObject _) /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CSSScale). extension type CSSScale._(JSObject _) implements CSSTransformComponent, JSObject { - external factory CSSScale( - CSSNumberish x, - CSSNumberish y, [ - CSSNumberish z, - ]); + external factory CSSScale(CSSNumberish x, CSSNumberish y, [CSSNumberish z]); /// The **`x`** property of the /// [CSSScale] interface gets and sets the abscissa or x-axis of the @@ -743,10 +734,7 @@ extension type CSSScale._(JSObject _) /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CSSSkew). extension type CSSSkew._(JSObject _) implements CSSTransformComponent, JSObject { - external factory CSSSkew( - CSSNumericValue ax, - CSSNumericValue ay, - ); + external factory CSSSkew(CSSNumericValue ax, CSSNumericValue ay); /// The **`ax`** property of the /// [CSSSkew] interface gets and sets the angle used to distort the element diff --git a/web/lib/src/dom/cssom.dart b/web/lib/src/dom/cssom.dart index 170d6b08..b71b7930 100644 --- a/web/lib/src/dom/cssom.dart +++ b/web/lib/src/dom/cssom.dart @@ -167,10 +167,7 @@ extension type CSSStyleSheet._(JSObject _) implements StyleSheet, JSObject { /// > [CSSStyleSheet], it actually inserts the rule into /// > `[CSSStyleSheet].cssRules` — its internal /// > [CSSRuleList]. - external int insertRule( - String rule, [ - int index, - ]); + external int insertRule(String rule, [int index]); /// The [CSSStyleSheet] method /// **`deleteRule()`** removes a rule from the stylesheet @@ -201,11 +198,7 @@ extension type CSSStyleSheet._(JSObject _) implements StyleSheet, JSObject { /// stylesheet. You should avoid using this method, and should instead use the /// more standard /// [CSSStyleSheet.insertRule] method. - external int addRule([ - String selector, - String style, - int index, - ]); + external int addRule([String selector, String style, int index]); /// The obsolete [CSSStyleSheet] method /// **`removeRule()`** removes a rule from the stylesheet @@ -522,10 +515,7 @@ extension type CSSImportRule._(JSObject _) implements CSSRule, JSObject { extension type CSSGroupingRule._(JSObject _) implements CSSRule, JSObject { /// The **`insertRule()`** method of the /// [CSSGroupingRule] interface adds a new CSS rule to a list of CSS rules. - external int insertRule( - String rule, [ - int index, - ]); + external int insertRule(String rule, [int index]); /// The **`deleteRule()`** method of the /// [CSSGroupingRule] interface removes a CSS rule from a list of child CSS @@ -621,11 +611,7 @@ extension type CSSStyleDeclaration._(JSObject _) implements JSObject { /// The /// **`CSSStyleDeclaration.setProperty()`** method interface sets /// a new value for a property on a CSS style declaration object. - external void setProperty( - String property, - String value, [ - String priority, - ]); + external void setProperty(String property, String value, [String priority]); /// The **`CSSStyleDeclaration.removeProperty()`** method interface /// removes a property from a CSS style declaration object. @@ -2037,10 +2023,7 @@ external $CSS get CSS; @JS('CSS') extension type $CSS._(JSObject _) implements JSObject { external String escape(String ident); - external bool supports( - String conditionTextOrProperty, [ - String value, - ]); + external bool supports(String conditionTextOrProperty, [String value]); external void registerProperty(PropertyDefinition definition); external CSSUnitValue number(num value); external CSSUnitValue percent(num value); diff --git a/web/lib/src/dom/cssom_view.dart b/web/lib/src/dom/cssom_view.dart index cce7e7c0..c5b0daa6 100644 --- a/web/lib/src/dom/cssom_view.dart +++ b/web/lib/src/dom/cssom_view.dart @@ -196,8 +196,9 @@ extension type Screen._(JSObject _) implements JSObject { external ScreenOrientation get orientation; } extension type CaretPositionFromPointOptions._(JSObject _) implements JSObject { - external factory CaretPositionFromPointOptions( - {JSArray shadowRoots}); + external factory CaretPositionFromPointOptions({ + JSArray shadowRoots, + }); external JSArray get shadowRoots; external set shadowRoots(JSArray value); diff --git a/web/lib/src/dom/digital_credentials.dart b/web/lib/src/dom/digital_credentials.dart index 59093638..72ecbe91 100644 --- a/web/lib/src/dom/digital_credentials.dart +++ b/web/lib/src/dom/digital_credentials.dart @@ -17,8 +17,9 @@ import 'dart:js_interop'; extension type DigitalCredentialRequestOptions._(JSObject _) implements JSObject { - external factory DigitalCredentialRequestOptions( - {JSArray requests}); + external factory DigitalCredentialRequestOptions({ + JSArray requests, + }); external JSArray get requests; external set requests(JSArray value); diff --git a/web/lib/src/dom/dom.dart b/web/lib/src/dom/dom.dart index a9a06ad0..43959fa6 100644 --- a/web/lib/src/dom/dom.dart +++ b/web/lib/src/dom/dom.dart @@ -83,10 +83,7 @@ typedef SlotAssignmentMode = String; /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Event). extension type Event._(JSObject _) implements JSObject { - external factory Event( - String type, [ - EventInit eventInitDict, - ]); + external factory Event(String type, [EventInit eventInitDict]); static const int NONE = 0; @@ -160,11 +157,7 @@ extension type Event._(JSObject _) implements JSObject { /// > The page on /// > [Creating and triggering events](https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events) /// > gives more information about the way to use these. - external void initEvent( - String type, [ - bool bubbles, - bool cancelable, - ]); + external void initEvent(String type, [bool bubbles, bool cancelable]); /// The **`type`** read-only property of the [Event] /// interface returns a string containing the event's type. It is set when the @@ -310,11 +303,7 @@ extension type Event._(JSObject _) implements JSObject { external double get timeStamp; } extension type EventInit._(JSObject _) implements JSObject { - external factory EventInit({ - bool bubbles, - bool cancelable, - bool composed, - }); + external factory EventInit({bool bubbles, bool cancelable, bool composed}); external bool get bubbles; external set bubbles(bool value); @@ -340,10 +329,7 @@ extension type EventInit._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). extension type CustomEvent._(JSObject _) implements Event, JSObject { - external factory CustomEvent( - String type, [ - CustomEventInit eventInitDict, - ]); + external factory CustomEvent(String type, [CustomEventInit eventInitDict]); /// The **`CustomEvent.initCustomEvent()`** method initializes a [CustomEvent] /// object. @@ -792,10 +778,7 @@ extension type MutationObserver._(JSObject _) implements JSObject { /// /// To stop the `MutationObserver` (so that none of its callbacks will be /// triggered any longer), call [MutationObserver.disconnect]. - external void observe( - Node target, [ - MutationObserverInit options, - ]); + external void observe(Node target, [MutationObserverInit options]); /// The [MutationObserver] method /// **`disconnect()`** tells the observer to stop watching for @@ -1097,10 +1080,7 @@ extension type Node._(JSObject _) implements EventTarget, JSObject { /// If the given child is a [DocumentFragment], the entire contents of the /// `DocumentFragment` are moved into the child list of the specified parent /// node. - external Node insertBefore( - Node node, - Node? child, - ); + external Node insertBefore(Node node, Node? child); /// The **`appendChild()`** method of the [Node] interface adds a node to the /// end of the list of children of a specified parent node. @@ -1124,10 +1104,7 @@ extension type Node._(JSObject _) implements EventTarget, JSObject { /// The **`replaceChild()`** method of the [Node] interface replaces a child /// node within the given (parent) node. - external Node replaceChild( - Node node, - Node child, - ); + external Node replaceChild(Node node, Node child); /// The **`removeChild()`** method of the [Node] interface /// removes a child node from the DOM and returns the removed node. @@ -1433,10 +1410,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// the **`document.createElement()`** method creates the HTML element /// specified by `localName`, or an [HTMLUnknownElement] if `localName` isn't /// recognized. - external Element createElement( - String localName, [ - JSAny options, - ]); + external Element createElement(String localName, [JSAny options]); /// Creates an element with the specified namespace URI and qualified name. /// @@ -1487,10 +1461,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// /// Unlike [document.adoptNode], the original node is not removed from its /// original document. The imported node is a clone of the original. - external Node importNode( - Node node, [ - bool subtree, - ]); + external Node importNode(Node node, [bool subtree]); /// **`Document.adoptNode()`** transfers a from another [Document] into the /// method's document. @@ -1515,10 +1486,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// The object created is a node implementing the /// [Attr] interface. The DOM does not enforce what sort of attributes can be /// added to a particular element in this manner. - external Attr createAttributeNS( - String? namespace, - String qualifiedName, - ); + external Attr createAttributeNS(String? namespace, String qualifiedName); /// > [!WARNING] /// > Many methods used with `createEvent`, such as `initCustomEvent`, are @@ -1591,10 +1559,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// /// If you need to find the specific position inside the element, use /// [Document.caretPositionFromPoint]. - external Element? elementFromPoint( - num x, - num y, - ); + external Element? elementFromPoint(num x, num y); /// The **`elementsFromPoint()`** method /// of the [Document] interface returns an array of all elements @@ -1603,10 +1568,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// viewport. /// /// It operates in a similar way to the [Document.elementFromPoint] method. - external JSArray elementsFromPoint( - num x, - num y, - ); + external JSArray elementsFromPoint(num x, num y); /// The **`caretPositionFromPoint()`** method of the [Document] interface /// returns a [CaretPosition] object, containing the DOM node, along with the @@ -1682,20 +1644,10 @@ extension type Document._(JSObject _) implements Node, JSObject { /// > `document.write()` on a closed (loaded) document automatically calls /// > `document.open()`, /// > [which will clear the document](https://developer.mozilla.org/en-US/docs/Web/API/Document/open#notes). - external void write([ - JSAny text1, - JSAny text2, - JSAny text3, - JSAny text4, - ]); + external void write([JSAny text1, JSAny text2, JSAny text3, JSAny text4]); /// Writes a string of text followed by a newline character to a document. - external void writeln([ - JSAny text1, - JSAny text2, - JSAny text3, - JSAny text4, - ]); + external void writeln([JSAny text1, JSAny text2, JSAny text3, JSAny text4]); /// The **`hasFocus()`** method of the [Document] interface returns a boolean /// value indicating whether the document or any element inside the document @@ -1741,11 +1693,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// > `execCommand()` in response to these events. From Firefox 82, nested /// > `execCommand()` calls will always fail, see /// > [bug 1634262](https://bugzil.la/1634262). - external bool execCommand( - String commandId, [ - bool showUI, - String value, - ]); + external bool execCommand(String commandId, [bool showUI, String value]); external bool queryCommandIndeterm(String commandId); external String queryCommandValue(String commandId); @@ -1867,12 +1815,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// /// This method prepends a child to a `Document`. To prepend to an arbitrary /// element in the tree, see [Element.prepend]. - external void prepend([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void prepend([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Document.append()`** method /// inserts a set of [Node] objects or strings after @@ -1881,12 +1824,7 @@ extension type Document._(JSObject _) implements Node, JSObject { /// /// This method appends a child to a `Document`. To append to an arbitrary /// element in the tree, see [Element.append]. - external void append([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void append([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Document.replaceChildren()`** method replaces the /// existing children of a `Document` with a specified new set of children. @@ -2729,23 +2667,13 @@ extension type DocumentType._(JSObject _) implements Node, JSObject { /// [Node] objects or strings in the children list of the /// `DocumentType`'s parent, just before the `DocumentType`. /// Strings are inserted as equivalent [Text] nodes. - external void before([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void before([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`DocumentType.after()`** method inserts a set of /// [Node] objects or strings in the children list of the /// `DocumentType`'s parent, just after the `DocumentType`. /// Strings are inserted as equivalent [Text] nodes. - external void after([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void after([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`DocumentType.replaceWith()`** method replaces the document type /// with a set of given nodes. @@ -2822,12 +2750,7 @@ extension type DocumentFragment._(JSObject _) implements Node, JSObject { /// /// This method prepends a child to a `DocumentFragment`. To prepend to an /// arbitrary element in the tree, see [Element.prepend]. - external void prepend([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void prepend([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`DocumentFragment.append()`** method /// inserts a set of [Node] objects or strings after @@ -2836,12 +2759,7 @@ extension type DocumentFragment._(JSObject _) implements Node, JSObject { /// /// This method appends a child to a `DocumentFragment`. To append to an /// arbitrary element in the tree, see [Element.append]. - external void append([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void append([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`DocumentFragment.replaceChildren()`** method replaces the /// existing children of a `DocumentFragment` with a specified new set of @@ -3174,10 +3092,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// If you are working with HTML documents and you don't need to specify the /// requested attribute as being part of a specific namespace, use the /// [Element.getAttribute] method instead. - external String? getAttributeNS( - String? namespace, - String localName, - ); + external String? getAttributeNS(String? namespace, String localName); /// The **`setAttribute()`** method of the [Element] interface sets the value /// of an attribute on the specified element. If the attribute already exists, @@ -3190,10 +3105,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// If you need to work with the [Attr] node (such as cloning from another /// element) before adding it, you can use the [Element.setAttributeNode] /// method instead. - external void setAttribute( - String qualifiedName, - String value, - ); + external void setAttribute(String qualifiedName, String value); /// `setAttributeNS` adds a new attribute or changes the value of an attribute /// with the given namespace and name. @@ -3219,18 +3131,12 @@ extension type Element._(JSObject _) implements Node, JSObject { /// If you are working with HTML and you don't need to specify the requested /// attribute as being part of a specific namespace, use the /// [Element.removeAttribute] method instead. - external void removeAttributeNS( - String? namespace, - String localName, - ); + external void removeAttributeNS(String? namespace, String localName); /// The **`toggleAttribute()`** method of the /// [Element] interface toggles a Boolean attribute (removing it if it is /// present and adding it if it is not present) on the given element. - external bool toggleAttribute( - String qualifiedName, [ - bool force, - ]); + external bool toggleAttribute(String qualifiedName, [bool force]); /// The **`Element.hasAttribute()`** method returns a /// **Boolean** value indicating whether the specified element has the @@ -3244,10 +3150,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// If you are working with HTML documents and you don't need to specify the /// requested attribute as being part of a specific namespace, use the /// [Element.hasAttribute] method instead. - external bool hasAttributeNS( - String? namespace, - String localName, - ); + external bool hasAttributeNS(String? namespace, String localName); /// Returns the specified attribute of the specified element, as an [Attr] /// node. @@ -3269,10 +3172,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// If you need the [Attr] node of an element in HTML documents and the /// attribute is not namespaced, use the [Element.getAttributeNode] method /// instead. - external Attr? getAttributeNodeNS( - String? namespace, - String localName, - ); + external Attr? getAttributeNodeNS(String? namespace, String localName); /// The **`setAttributeNode()`** method of the [Element] interface adds a new /// [Attr] node to the specified element. @@ -3365,18 +3265,12 @@ extension type Element._(JSObject _) implements Node, JSObject { /// The **`insertAdjacentElement()`** method of the /// [Element] interface inserts a given element node at a given position /// relative to the element it is invoked upon. - external Element? insertAdjacentElement( - String where, - Element element, - ); + external Element? insertAdjacentElement(String where, Element element); /// The **`insertAdjacentText()`** method of the [Element] interface, given a /// relative position and a string, inserts a new text node at the given /// position relative to the element it is called from. - external void insertAdjacentText( - String where, - String data, - ); + external void insertAdjacentText(String where, String data); /// The **`computedStyleMap()`** method of /// the [Element] interface returns a [StylePropertyMapReadOnly] @@ -3438,25 +3332,16 @@ extension type Element._(JSObject _) implements Node, JSObject { /// interface scrolls the element to a particular set of coordinates inside a /// given /// element. - external void scroll([ - JSAny optionsOrX, - num y, - ]); + external void scroll([JSAny optionsOrX, num y]); /// The **`scrollTo()`** method of the [Element] /// interface scrolls to a particular set of coordinates inside a given /// element. - external void scrollTo([ - JSAny optionsOrX, - num y, - ]); + external void scrollTo([JSAny optionsOrX, num y]); /// The **`scrollBy()`** method of the [Element] /// interface scrolls an element by the given amount. - external void scrollBy([ - JSAny optionsOrX, - num y, - ]); + external void scrollBy([JSAny optionsOrX, num y]); /// The **`Element.requestFullscreen()`** /// method issues an asynchronous request to make the element be displayed in @@ -3514,10 +3399,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// The **`insertAdjacentHTML()`** method of the /// [Element] interface parses the specified text as HTML or XML and inserts /// the resulting nodes into the DOM tree at a specified position. - external void insertAdjacentHTML( - String position, - JSAny string, - ); + external void insertAdjacentHTML(String position, JSAny string); /// The **`setPointerCapture()`** method of the /// [Element] interface is used to designate a specific element as the @@ -3569,12 +3451,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// [Node] objects or strings before the first child /// of the [Element]. Strings are inserted as /// equivalent [Text] nodes. - external void prepend([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void prepend([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Element.append()`** method /// inserts a set of [Node] objects or strings after @@ -3590,12 +3467,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// `Node.appendChild()` returns the appended [Node] object. /// - `Element.append()` can append several nodes and strings, whereas /// `Node.appendChild()` can only append one node. - external void append([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void append([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Element.replaceChildren()`** method replaces the /// existing children of a [Node] with a specified new set of children. These @@ -3624,23 +3496,13 @@ extension type Element._(JSObject _) implements Node, JSObject { /// [Node] objects or strings in the children list of this /// `Element`'s parent, just before this `Element`. /// Strings are inserted as equivalent [Text] nodes. - external void before([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void before([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Element.after()`** method inserts a set of /// [Node] objects or strings in the children list of the /// `Element`'s parent, just after the `Element`. /// Strings are inserted as equivalent [Text] nodes. - external void after([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void after([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`Element.replaceWith()`** method replaces this /// `Element` in the children list of its parent with a set of @@ -3665,10 +3527,7 @@ extension type Element._(JSObject _) implements Node, JSObject { /// > Elements can have multiple animations applied to them. You can get a /// > list of the /// > animations that affect an element by calling [Element.getAnimations]. - external Animation animate( - JSObject? keyframes, [ - JSAny options, - ]); + external Animation animate(JSObject? keyframes, [JSAny options]); /// The `getAnimations()` method of the [Element] interface /// (specified on the `Animatable` mixin) returns an array of all @@ -4488,10 +4347,7 @@ extension type NamedNodeMap._(JSObject _) implements JSObject { /// The **`getNamedItemNS()`** method of the [NamedNodeMap] interface returns /// the [Attr] corresponding to the given local name in the given namespace, /// or `null` if there is no corresponding attribute. - external Attr? getNamedItemNS( - String? namespace, - String localName, - ); + external Attr? getNamedItemNS(String? namespace, String localName); /// The **`setNamedItem()`** method of the [NamedNodeMap] interface /// puts the [Attr] identified by its name in the map. @@ -4516,10 +4372,7 @@ extension type NamedNodeMap._(JSObject _) implements JSObject { /// The **`removeNamedItemNS()`** method of the [NamedNodeMap] interface /// removes the [Attr] corresponding to the given namespace and local name /// from the map. - external Attr removeNamedItemNS( - String? namespace, - String localName, - ); + external Attr removeNamedItemNS(String? namespace, String localName); /// The read-only **`length`** property of the [NamedNodeMap] interface /// is the number of objects stored in the map. @@ -4658,10 +4511,7 @@ extension type CharacterData._(JSObject _) implements Node, JSObject { /// returns a portion of the existing data, /// starting at the specified index /// and extending for a given number of characters afterwards. - external String substringData( - int offset, - int count, - ); + external String substringData(int offset, int count); /// The **`appendData()`** method of the [CharacterData] interface /// adds the provided data to the end of the node's current data. @@ -4671,26 +4521,16 @@ extension type CharacterData._(JSObject _) implements Node, JSObject { /// inserts the provided data into this `CharacterData` node's current data, /// at the provided offset from the start of the existing data. /// The provided data is spliced into the existing data. - external void insertData( - int offset, - String data, - ); + external void insertData(int offset, String data); /// The **`deleteData()`** method of the [CharacterData] interface /// removes all or part of the data from this `CharacterData` node. - external void deleteData( - int offset, - int count, - ); + external void deleteData(int offset, int count); /// The **`replaceData()`** method of the [CharacterData] interface removes a /// certain number of characters of the existing text in a given /// `CharacterData` node and replaces those characters with the text provided. - external void replaceData( - int offset, - int count, - String data, - ); + external void replaceData(int offset, int count, String data); /// The **`before()`** method of the [CharacterData] interface /// inserts a set of [Node] objects and strings @@ -4699,12 +4539,7 @@ extension type CharacterData._(JSObject _) implements Node, JSObject { /// /// Strings are inserted as [Text] nodes; the string is being passed as /// argument to the [Text.Text] constructor. - external void before([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void before([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`after()`** method of the [CharacterData] interface /// inserts a set of [Node] objects or strings in the children list of the @@ -4712,12 +4547,7 @@ extension type CharacterData._(JSObject _) implements Node, JSObject { /// /// Strings are inserted as [Text] nodes; the string is being passed as /// argument to the [Text.Text] constructor. - external void after([ - JSAny node1, - JSAny node2, - JSAny node3, - JSAny node4, - ]); + external void after([JSAny node1, JSAny node2, JSAny node3, JSAny node4]); /// The **`replaceWith()`** method of the [CharacterData] interface /// replaces this node in the children list of its parent @@ -5049,10 +4879,7 @@ extension type Range._(JSObject _) implements AbstractRange, JSObject { /// result in a /// collapsed range with the start and end points both set to the specified /// start position. - external void setStart( - Node node, - int offset, - ); + external void setStart(Node node, int offset); /// The **`Range.setEnd()`** method sets the end position of a [Range] to be /// located at the given offset into the specified node x.Setting @@ -5060,10 +4887,7 @@ extension type Range._(JSObject _) implements AbstractRange, JSObject { /// result in a /// collapsed range with the start and end points both set to the specified /// end position. - external void setEnd( - Node node, - int offset, - ); + external void setEnd(Node node, int offset); /// The **`Range.setStartBefore()`** method sets the start position /// of a [Range] relative to another [Node]. The parent @@ -5116,10 +4940,7 @@ extension type Range._(JSObject _) implements AbstractRange, JSObject { /// The /// **`Range.compareBoundaryPoints()`** method compares the /// boundary points of the [Range] with those of another range. - external int compareBoundaryPoints( - int how, - Range sourceRange, - ); + external int compareBoundaryPoints(int how, Range sourceRange); /// The **`Range.deleteContents()`** method removes the contents of /// the [Range] from the [Document]. @@ -5212,10 +5033,7 @@ extension type Range._(JSObject _) implements AbstractRange, JSObject { /// indicating whether the given point is in the [Range]. It returns /// `true` if the point (cursor position) at `offset` within /// `ReferenceNode` is within this range. - external bool isPointInRange( - Node node, - int offset, - ); + external bool isPointInRange(Node node, int offset); /// The **`Range.comparePoint()`** method returns `-1`, /// `0`, or `1` depending on whether the `referenceNode` is @@ -5227,10 +5045,7 @@ extension type Range._(JSObject _) implements AbstractRange, JSObject { /// types, offset is the number of child nodes between the start of the /// _reference /// node_. - external int comparePoint( - Node node, - int offset, - ); + external int comparePoint(Node node, int offset); /// The **`Range.intersectsNode()`** method returns a boolean /// indicating whether the given [Node] intersects the [Range]. @@ -5602,19 +5417,13 @@ extension type DOMTokenList._(JSObject _) implements JSObject { /// The **`toggle()`** method of the [DOMTokenList] interface /// removes an existing token from the list and returns `false`. /// If the token doesn't exist it's added and the function returns `true`. - external bool toggle( - String token, [ - bool force, - ]); + external bool toggle(String token, [bool force]); /// The **`replace()`** method of the [DOMTokenList] interface /// replaces an existing token with a new token. /// If the first token doesn't exist, `replace()` returns `false` immediately, /// without adding the new token to the token list. - external bool replace( - String token, - String newToken, - ); + external bool replace(String token, String newToken); /// The **`supports()`** method of the [DOMTokenList] interface /// returns `true` if a given `token` is in the associated attribute's @@ -5801,10 +5610,7 @@ extension type XSLTProcessor._(JSObject _) implements JSObject { /// The `transformToFragment()` method of the [XSLTProcessor] interface /// transforms a provided [Node] source to a [DocumentFragment] using the XSLT /// stylesheet associated with the `XSLTProcessor`. - external DocumentFragment transformToFragment( - Node source, - Document output, - ); + external DocumentFragment transformToFragment(Node source, Document output); /// The `transformToDocument()` method of the [XSLTProcessor] interface /// transforms the provided [Node] source to a [Document] using the XSLT @@ -5823,18 +5629,12 @@ extension type XSLTProcessor._(JSObject _) implements JSObject { /// The `getParameter()` method of the [XSLTProcessor] interface returns the /// value of a parameter (``) from the stylesheet imported in the /// processor. - external JSAny? getParameter( - String namespaceURI, - String localName, - ); + external JSAny? getParameter(String namespaceURI, String localName); /// The `removeParameter()` method of the [XSLTProcessor] interface removes /// the parameter (``) and its value from the stylesheet imported /// in the processor. - external void removeParameter( - String namespaceURI, - String localName, - ); + external void removeParameter(String namespaceURI, String localName); /// The `clearParameters()` method of the [XSLTProcessor] interface removes /// all parameters (``) and their values from the stylesheet diff --git a/web/lib/src/dom/encoding.dart b/web/lib/src/dom/encoding.dart index 449ea0a5..49ac49e6 100644 --- a/web/lib/src/dom/encoding.dart +++ b/web/lib/src/dom/encoding.dart @@ -19,10 +19,7 @@ import 'streams.dart'; import 'webidl.dart'; extension type TextDecoderOptions._(JSObject _) implements JSObject { - external factory TextDecoderOptions({ - bool fatal, - bool ignoreBOM, - }); + external factory TextDecoderOptions({bool fatal, bool ignoreBOM}); external bool get fatal; external set fatal(bool value); @@ -45,10 +42,7 @@ extension type TextDecodeOptions._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder). extension type TextDecoder._(JSObject _) implements JSObject { - external factory TextDecoder([ - String label, - TextDecoderOptions options, - ]); + external factory TextDecoder([String label, TextDecoderOptions options]); /// The **`TextDecoder.decode()`** method returns a string containing text /// decoded from the buffer passed as a parameter. @@ -88,10 +82,7 @@ extension type TextDecoder._(JSObject _) implements JSObject { external bool get ignoreBOM; } extension type TextEncoderEncodeIntoResult._(JSObject _) implements JSObject { - external factory TextEncoderEncodeIntoResult({ - int read, - int written, - }); + external factory TextEncoderEncodeIntoResult({int read, int written}); external int get read; external set read(int value); diff --git a/web/lib/src/dom/encrypted_media.dart b/web/lib/src/dom/encrypted_media.dart index 1cf1b8ef..edd50be8 100644 --- a/web/lib/src/dom/encrypted_media.dart +++ b/web/lib/src/dom/encrypted_media.dart @@ -146,7 +146,8 @@ extension type MediaKeys._(JSObject _) implements JSObject { /// provides a server certificate to be used to encrypt messages to the /// license server. external JSPromise setServerCertificate( - BufferSource serverCertificate); + BufferSource serverCertificate, + ); } extension type MediaKeysPolicy._(JSObject _) implements JSObject { external factory MediaKeysPolicy({String minHdcpVersion}); diff --git a/web/lib/src/dom/entries_api.dart b/web/lib/src/dom/entries_api.dart index 109ecfca..a4d40d39 100644 --- a/web/lib/src/dom/entries_api.dart +++ b/web/lib/src/dom/entries_api.dart @@ -143,10 +143,7 @@ extension type FileSystemDirectoryEntry._(JSObject _) ]); } extension type FileSystemFlags._(JSObject _) implements JSObject { - external factory FileSystemFlags({ - bool create, - bool exclusive, - }); + external factory FileSystemFlags({bool create, bool exclusive}); external bool get create; external set create(bool value); diff --git a/web/lib/src/dom/ext_disjoint_timer_query.dart b/web/lib/src/dom/ext_disjoint_timer_query.dart index b32fd08d..a580d3b1 100644 --- a/web/lib/src/dom/ext_disjoint_timer_query.dart +++ b/web/lib/src/dom/ext_disjoint_timer_query.dart @@ -36,21 +36,9 @@ extension type EXT_disjoint_timer_query._(JSObject _) implements JSObject { external WebGLTimerQueryEXT createQueryEXT(); external void deleteQueryEXT(WebGLTimerQueryEXT? query); external bool isQueryEXT(WebGLTimerQueryEXT? query); - external void beginQueryEXT( - GLenum target, - WebGLTimerQueryEXT query, - ); + external void beginQueryEXT(GLenum target, WebGLTimerQueryEXT query); external void endQueryEXT(GLenum target); - external void queryCounterEXT( - WebGLTimerQueryEXT query, - GLenum target, - ); - external JSAny? getQueryEXT( - GLenum target, - GLenum pname, - ); - external JSAny? getQueryObjectEXT( - WebGLTimerQueryEXT query, - GLenum pname, - ); + external void queryCounterEXT(WebGLTimerQueryEXT query, GLenum target); + external JSAny? getQueryEXT(GLenum target, GLenum pname); + external JSAny? getQueryObjectEXT(WebGLTimerQueryEXT query, GLenum pname); } diff --git a/web/lib/src/dom/ext_disjoint_timer_query_webgl2.dart b/web/lib/src/dom/ext_disjoint_timer_query_webgl2.dart index e94b4e51..4e8c001b 100644 --- a/web/lib/src/dom/ext_disjoint_timer_query_webgl2.dart +++ b/web/lib/src/dom/ext_disjoint_timer_query_webgl2.dart @@ -28,8 +28,5 @@ extension type EXT_disjoint_timer_query_webgl2._(JSObject _) static const GLenum GPU_DISJOINT_EXT = 36795; - external void queryCounterEXT( - WebGLQuery query, - GLenum target, - ); + external void queryCounterEXT(WebGLQuery query, GLenum target); } diff --git a/web/lib/src/dom/fetch.dart b/web/lib/src/dom/fetch.dart index e52b58ed..9a3c612b 100644 --- a/web/lib/src/dom/fetch.dart +++ b/web/lib/src/dom/fetch.dart @@ -10,6 +10,8 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +/// @docImport 'url.dart'; +/// @docImport 'webidl.dart'; @JS() library; @@ -24,9 +26,19 @@ import 'streams.dart'; import 'trust_token_api.dart'; import 'xhr.dart'; +/// Union of 2 types +/// +/// - [JSArray]\<[JSArray]\> +/// - [JSObject]\<[JSString], [JSString]\> typedef HeadersInit = JSObject; + +/// Union of: [Blob], [BufferSource], [FormData], [JSString], [URLSearchParams] typedef XMLHttpRequestBodyInit = JSAny; + +/// Union of: [ReadableStream], [XMLHttpRequestBodyInit] typedef BodyInit = JSAny; + +/// Union of: [JSString], [Request] typedef RequestInfo = JSAny; typedef RequestDestination = String; typedef RequestMode = String; @@ -77,10 +89,7 @@ extension type Headers._(JSObject _) implements JSObject { /// agent. These /// headers include the /// and . - external void append( - String name, - String value, - ); + external void append(String name, String value); /// The **`delete()`** method of the [Headers] /// interface deletes a header from the current `Headers` object. @@ -140,10 +149,7 @@ extension type Headers._(JSObject _) implements JSObject { /// agent. These /// headers include the /// and . - external void set( - String name, - String value, - ); + external void set(String name, String value); } /// The **`Request`** interface of the @@ -160,10 +166,7 @@ extension type Headers._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Request). extension type Request._(JSObject _) implements JSObject { - external factory Request( - RequestInfo input, [ - RequestInit init, - ]); + external factory Request(RequestInfo input, [RequestInit init]); /// The **`clone()`** method of the [Request] interface creates a copy of the /// current `Request` object. @@ -466,10 +469,7 @@ extension type RequestInit._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Response). extension type Response._(JSObject _) implements JSObject { - external factory Response([ - BodyInit? body, - ResponseInit init, - ]); + external factory Response([BodyInit? body, ResponseInit init]); /// The **`error()`** static method of the [Response] interface returns a new /// `Response` object associated with a network error. @@ -492,10 +492,7 @@ extension type Response._(JSObject _) implements JSObject { /// > redirect it as desired. /// > This will actually lead to a real redirect if a service worker sends it /// > upstream. - external static Response redirect( - String url, [ - int status, - ]); + external static Response redirect(String url, [int status]); /// The **`json()`** static method of the [Response] interface returns a /// `Response` that contains the provided JSON data as body, and a header @@ -514,10 +511,7 @@ extension type Response._(JSObject _) implements JSObject { /// [single page applications](https://developer.mozilla.org/en-US/docs/Glossary/SPA), /// and any other applications where a JSON response is expected. @JS('json') - external static Response json_( - JSAny? data, [ - ResponseInit init, - ]); + external static Response json_(JSAny? data, [ResponseInit init]); /// The **`clone()`** method of the [Response] interface creates a clone of a /// response object, identical in every way, but stored in a different diff --git a/web/lib/src/dom/fileapi.dart b/web/lib/src/dom/fileapi.dart index 9c78c350..b2fdf4bd 100644 --- a/web/lib/src/dom/fileapi.dart +++ b/web/lib/src/dom/fileapi.dart @@ -20,6 +20,7 @@ import 'html.dart'; import 'streams.dart'; import 'webidl.dart'; +/// Union of: [Blob], [BufferSource], [JSString] typedef BlobPart = JSAny; typedef EndingType = String; @@ -36,20 +37,13 @@ typedef EndingType = String; /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Blob). extension type Blob._(JSObject _) implements JSObject { - external factory Blob([ - JSArray blobParts, - BlobPropertyBag options, - ]); + external factory Blob([JSArray blobParts, BlobPropertyBag options]); /// The **`slice()`** method of the [Blob] interface /// creates and returns a new `Blob` object which contains data from a subset /// of /// the blob on which it's called. - external Blob slice([ - int start, - int end, - String contentType, - ]); + external Blob slice([int start, int end, String contentType]); /// The **`stream()`** method of the [Blob] interface returns a /// [ReadableStream] which upon reading returns the data contained within the @@ -93,10 +87,7 @@ extension type Blob._(JSObject _) implements JSObject { external String get type; } extension type BlobPropertyBag._(JSObject _) implements JSObject { - external factory BlobPropertyBag({ - String type, - EndingType endings, - }); + external factory BlobPropertyBag({String type, EndingType endings}); external String get type; external set type(String value); @@ -295,10 +286,7 @@ extension type FileReader._(JSObject _) implements EventTarget, JSObject { /// > This method loads the entire file's content into memory and is not /// > suitable for large files. Prefer [FileReader.readAsArrayBuffer] for /// > large files. - external void readAsText( - Blob blob, [ - String encoding, - ]); + external void readAsText(Blob blob, [String encoding]); /// The **`readAsDataURL()`** method of the [FileReader] interface is used to /// read the contents of the specified @@ -403,10 +391,7 @@ extension type FileReaderSync._(JSObject _) implements JSObject { /// [only available](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers) /// in [workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker) as /// it enables synchronous I/O that could potentially block. - external String readAsText( - Blob blob, [ - String encoding, - ]); + external String readAsText(Blob blob, [String encoding]); /// @AvailableInWorkers("worker_except_service") /// diff --git a/web/lib/src/dom/filter_effects.dart b/web/lib/src/dom/filter_effects.dart index f9795b64..bd0940d6 100644 --- a/web/lib/src/dom/filter_effects.dart +++ b/web/lib/src/dom/filter_effects.dart @@ -28,10 +28,7 @@ import 'svg.dart'; extension type SVGFilterElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFilterElement] using the tag 'filter'. SVGFilterElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'filter', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'filter'); /// The **`filterUnits`** read-only property of the [SVGFilterElement] /// interface reflects the `filterUnits` attribute of the given element. It @@ -107,10 +104,7 @@ extension type SVGFilterElement._(JSObject _) implements SVGElement, JSObject { extension type SVGFEBlendElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEBlendElement] using the tag 'feBlend'. SVGFEBlendElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feBlend', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feBlend'); static const int SVG_FEBLEND_MODE_UNKNOWN = 0; @@ -251,10 +245,10 @@ extension type SVGFEColorMatrixElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEColorMatrixElement] using the tag 'feColorMatrix'. SVGFEColorMatrixElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feColorMatrix', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feColorMatrix', + ); static const int SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0; @@ -368,10 +362,10 @@ extension type SVGFEComponentTransferElement._(JSObject _) /// Creates an [SVGFEComponentTransferElement] using the tag /// 'feComponentTransfer'. SVGFEComponentTransferElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feComponentTransfer', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feComponentTransfer', + ); /// The **`in1`** read-only property of the [SVGFEComponentTransferElement] /// interface reflects the `in` attribute of the given element. @@ -517,10 +511,7 @@ extension type SVGFEFuncRElement._(JSObject _) implements SVGComponentTransferFunctionElement, JSObject { /// Creates an [SVGFEFuncRElement] using the tag 'feFuncR'. SVGFEFuncRElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feFuncR', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feFuncR'); } /// The **`SVGFEFuncGElement`** interface corresponds to the element. @@ -533,10 +524,7 @@ extension type SVGFEFuncGElement._(JSObject _) implements SVGComponentTransferFunctionElement, JSObject { /// Creates an [SVGFEFuncGElement] using the tag 'feFuncG'. SVGFEFuncGElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feFuncG', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feFuncG'); } /// The **`SVGFEFuncBElement`** interface corresponds to the element. @@ -549,10 +537,7 @@ extension type SVGFEFuncBElement._(JSObject _) implements SVGComponentTransferFunctionElement, JSObject { /// Creates an [SVGFEFuncBElement] using the tag 'feFuncB'. SVGFEFuncBElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feFuncB', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feFuncB'); } /// The **`SVGFEFuncAElement`** interface corresponds to the element. @@ -565,10 +550,7 @@ extension type SVGFEFuncAElement._(JSObject _) implements SVGComponentTransferFunctionElement, JSObject { /// Creates an [SVGFEFuncAElement] using the tag 'feFuncA'. SVGFEFuncAElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feFuncA', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feFuncA'); } /// The **`SVGFECompositeElement`** interface corresponds to the element. @@ -581,10 +563,7 @@ extension type SVGFECompositeElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFECompositeElement] using the tag 'feComposite'. SVGFECompositeElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feComposite', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feComposite'); static const int SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0; @@ -714,10 +693,10 @@ extension type SVGFEConvolveMatrixElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEConvolveMatrixElement] using the tag 'feConvolveMatrix'. SVGFEConvolveMatrixElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feConvolveMatrix', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feConvolveMatrix', + ); static const int SVG_EDGEMODE_UNKNOWN = 0; @@ -893,10 +872,10 @@ extension type SVGFEDiffuseLightingElement._(JSObject _) /// Creates an [SVGFEDiffuseLightingElement] using the tag /// 'feDiffuseLighting'. SVGFEDiffuseLightingElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feDiffuseLighting', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feDiffuseLighting', + ); /// The **`in1`** read-only property of the [SVGFEDiffuseLightingElement] /// interface reflects the `in` attribute of the given element. @@ -1004,10 +983,10 @@ extension type SVGFEDistantLightElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEDistantLightElement] using the tag 'feDistantLight'. SVGFEDistantLightElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feDistantLight', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feDistantLight', + ); /// The **`azimuth`** read-only property of the [SVGFEDistantLightElement] /// interface reflects the `azimuth` attribute of the given element. @@ -1028,10 +1007,10 @@ extension type SVGFEPointLightElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEPointLightElement] using the tag 'fePointLight'. SVGFEPointLightElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'fePointLight', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'fePointLight', + ); /// The **`x`** read-only property of the [SVGFEPointLightElement] interface /// describes the horizontal coordinate of the position of an SVG filter @@ -1075,10 +1054,7 @@ extension type SVGFESpotLightElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFESpotLightElement] using the tag 'feSpotLight'. SVGFESpotLightElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feSpotLight', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feSpotLight'); /// The **`x`** read-only property of the [SVGFESpotLightElement] interface /// describes the horizontal coordinate of the position of an SVG filter @@ -1145,10 +1121,10 @@ extension type SVGFEDisplacementMapElement._(JSObject _) /// Creates an [SVGFEDisplacementMapElement] using the tag /// 'feDisplacementMap'. SVGFEDisplacementMapElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feDisplacementMap', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feDisplacementMap', + ); static const int SVG_CHANNEL_UNKNOWN = 0; @@ -1261,17 +1237,14 @@ extension type SVGFEDropShadowElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEDropShadowElement] using the tag 'feDropShadow'. SVGFEDropShadowElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feDropShadow', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feDropShadow', + ); /// The `setStdDeviation()` method of the [SVGFEDropShadowElement] interface /// sets the values for the `stdDeviation` attribute. - external void setStdDeviation( - num stdDeviationX, - num stdDeviationY, - ); + external void setStdDeviation(num stdDeviationX, num stdDeviationY); /// The **`in1`** read-only property of the [SVGFEDropShadowElement] interface /// reflects the `in` attribute of the given element. @@ -1372,10 +1345,7 @@ extension type SVGFEDropShadowElement._(JSObject _) extension type SVGFEFloodElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEFloodElement] using the tag 'feFlood'. SVGFEFloodElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feFlood', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feFlood'); /// The **`x`** read-only property of the [SVGFEFloodElement] interface /// describes the horizontal coordinate of the position of an SVG filter @@ -1459,10 +1429,10 @@ extension type SVGFEGaussianBlurElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEGaussianBlurElement] using the tag 'feGaussianBlur'. SVGFEGaussianBlurElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feGaussianBlur', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feGaussianBlur', + ); static const int SVG_EDGEMODE_UNKNOWN = 0; @@ -1474,10 +1444,7 @@ extension type SVGFEGaussianBlurElement._(JSObject _) /// The `setStdDeviation()` method of the [SVGFEGaussianBlurElement] interface /// sets the values for the `stdDeviation` attribute. - external void setStdDeviation( - num stdDeviationX, - num stdDeviationY, - ); + external void setStdDeviation(num stdDeviationX, num stdDeviationY); /// The **`in1`** read-only property of the [SVGFEGaussianBlurElement] /// interface reflects the `in` attribute of the given element. @@ -1574,10 +1541,7 @@ extension type SVGFEGaussianBlurElement._(JSObject _) extension type SVGFEImageElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEImageElement] using the tag 'feImage'. SVGFEImageElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feImage', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feImage'); /// The **`preserveAspectRatio`** read-only property of the /// [SVGFEImageElement] interface reflects the `preserveAspectRatio` attribute @@ -1670,10 +1634,7 @@ extension type SVGFEImageElement._(JSObject _) implements SVGElement, JSObject { extension type SVGFEMergeElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEMergeElement] using the tag 'feMerge'. SVGFEMergeElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feMerge', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feMerge'); /// The **`x`** read-only property of the [SVGFEMergeElement] interface /// describes the horizontal coordinate of the position of an SVG filter @@ -1752,10 +1713,7 @@ extension type SVGFEMergeNodeElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEMergeNodeElement] using the tag 'feMergeNode'. SVGFEMergeNodeElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feMergeNode', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode'); /// The **`in1`** read-only property of the [SVGFEMergeNodeElement] interface /// reflects the `in` attribute of the given element. @@ -1772,10 +1730,10 @@ extension type SVGFEMorphologyElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEMorphologyElement] using the tag 'feMorphology'. SVGFEMorphologyElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feMorphology', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feMorphology', + ); static const int SVG_MORPHOLOGY_OPERATOR_UNKNOWN = 0; @@ -1880,10 +1838,7 @@ extension type SVGFEOffsetElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFEOffsetElement] using the tag 'feOffset'. SVGFEOffsetElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feOffset', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feOffset'); /// The **`in1`** read-only property of the [SVGFEOffsetElement] interface /// reflects the `in` attribute of the given element. @@ -1981,10 +1936,10 @@ extension type SVGFESpecularLightingElement._(JSObject _) /// Creates an [SVGFESpecularLightingElement] using the tag /// 'feSpecularLighting'. SVGFESpecularLightingElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feSpecularLighting', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feSpecularLighting', + ); /// The **`in1`** read-only property of the [SVGFESpecularLightingElement] /// interface reflects the `in` attribute of the given element. @@ -2088,10 +2043,7 @@ extension type SVGFESpecularLightingElement._(JSObject _) extension type SVGFETileElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFETileElement] using the tag 'feTile'. SVGFETileElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feTile', - ); + : _ = document.createElementNS('http://www.w3.org/2000/svg', 'feTile'); /// The **`in1`** read-only property of the [SVGFETileElement] interface /// reflects the `in` attribute of the given element. @@ -2179,10 +2131,10 @@ extension type SVGFETurbulenceElement._(JSObject _) implements SVGElement, JSObject { /// Creates an [SVGFETurbulenceElement] using the tag 'feTurbulence'. SVGFETurbulenceElement() - : _ = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'feTurbulence', - ); + : _ = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feTurbulence', + ); static const int SVG_TURBULENCE_TYPE_UNKNOWN = 0; diff --git a/web/lib/src/dom/fs.dart b/web/lib/src/dom/fs.dart index 6255597a..9db1f513 100644 --- a/web/lib/src/dom/fs.dart +++ b/web/lib/src/dom/fs.dart @@ -19,6 +19,7 @@ import 'fileapi.dart'; import 'streams.dart'; import 'webidl.dart'; +/// Union of: [Blob], [BufferSource], [JSString], [WriteParams] typedef FileSystemWriteChunkType = JSAny; typedef FileSystemHandleKind = String; typedef WriteCommandType = String; @@ -96,8 +97,9 @@ extension type FileSystemFileHandle._(JSObject _) /// This is typically implemented by writing data to a temporary file, and /// only replacing the file represented by file handle with the temporary file /// when the writable file stream is closed. - external JSPromise createWritable( - [FileSystemCreateWritableOptions options]); + external JSPromise createWritable([ + FileSystemCreateWritableOptions options, + ]); /// @AvailableInWorkers("dedicated") /// @@ -181,7 +183,8 @@ extension type FileSystemDirectoryHandle._(JSObject _) /// the name of /// the child entry as the last array item. external JSPromise?> resolve( - FileSystemHandle possibleDescendant); + FileSystemHandle possibleDescendant, + ); } extension type WriteParams._(JSObject _) implements JSObject { external factory WriteParams({ diff --git a/web/lib/src/dom/gamepad.dart b/web/lib/src/dom/gamepad.dart index a7dcffec..df3b6985 100644 --- a/web/lib/src/dom/gamepad.dart +++ b/web/lib/src/dom/gamepad.dart @@ -212,10 +212,7 @@ extension type GamepadHapticActuator._(JSObject _) implements JSObject { /// The **`pulse()`** method of the [GamepadHapticActuator] interface makes /// the hardware pulse at a certain intensity for a specified duration. - external JSPromise pulse( - num value, - num duration, - ); + external JSPromise pulse(num value, num duration); } extension type GamepadEffectParameters._(JSObject _) implements JSObject { external factory GamepadEffectParameters({ @@ -251,10 +248,7 @@ extension type GamepadEffectParameters._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/GamepadEvent). extension type GamepadEvent._(JSObject _) implements Event, JSObject { - external factory GamepadEvent( - String type, - GamepadEventInit eventInitDict, - ); + external factory GamepadEvent(String type, GamepadEventInit eventInitDict); /// The **`GamepadEvent.gamepad`** property of the /// **[GamepadEvent] interface** returns a [Gamepad] diff --git a/web/lib/src/dom/geometry.dart b/web/lib/src/dom/geometry.dart index dc4c68a5..101557af 100644 --- a/web/lib/src/dom/geometry.dart +++ b/web/lib/src/dom/geometry.dart @@ -46,12 +46,7 @@ import 'dart:js_interop'; /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMPointReadOnly). extension type DOMPointReadOnly._(JSObject _) implements JSObject { - external factory DOMPointReadOnly([ - num x, - num y, - num z, - num w, - ]); + external factory DOMPointReadOnly([num x, num y, num z, num w]); /// The static **[DOMPointReadOnly]** /// method `fromPoint()` creates and returns a new @@ -139,12 +134,7 @@ extension type DOMPointReadOnly._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMPoint). extension type DOMPoint._(JSObject _) implements DOMPointReadOnly, JSObject { - external factory DOMPoint([ - num x, - num y, - num z, - num w, - ]); + external factory DOMPoint([num x, num y, num z, num w]); /// The **`fromPoint()`** static method of the [DOMPoint] interface creates /// and returns a new mutable `DOMPoint` object given a source point. @@ -195,12 +185,7 @@ extension type DOMPoint._(JSObject _) implements DOMPointReadOnly, JSObject { external set w(num value); } extension type DOMPointInit._(JSObject _) implements JSObject { - external factory DOMPointInit({ - num x, - num y, - num z, - num w, - }); + external factory DOMPointInit({num x, num y, num z, num w}); external double get x; external set x(num value); @@ -220,12 +205,7 @@ extension type DOMPointInit._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly). extension type DOMRectReadOnly._(JSObject _) implements JSObject { - external factory DOMRectReadOnly([ - num x, - num y, - num width, - num height, - ]); + external factory DOMRectReadOnly([num x, num y, num width, num height]); /// The **`fromRect()`** static method of the /// [DOMRectReadOnly] object creates a new `DOMRectReadOnly` @@ -287,12 +267,7 @@ extension type DOMRectReadOnly._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect). extension type DOMRect._(JSObject _) implements DOMRectReadOnly, JSObject { - external factory DOMRect([ - num x, - num y, - num width, - num height, - ]); + external factory DOMRect([num x, num y, num width, num height]); /// The **`fromRect()`** static method of the /// [DOMRect] object creates a new `DOMRect` @@ -330,12 +305,7 @@ extension type DOMRect._(JSObject _) implements DOMRectReadOnly, JSObject { external set height(num value); } extension type DOMRectInit._(JSObject _) implements JSObject { - external factory DOMRectInit({ - num x, - num y, - num width, - num height, - }); + external factory DOMRectInit({num x, num y, num width, num height}); external double get x; external set x(num value); @@ -465,11 +435,7 @@ extension type DOMMatrixReadOnly._(JSObject _) implements JSObject { /// The `translate()` method of the [DOMMatrixReadOnly] interface /// creates a new matrix being the result of the original matrix with a /// translation applied. - external DOMMatrix translate([ - num tx, - num ty, - num tz, - ]); + external DOMMatrix translate([num tx, num ty, num tz]); /// The **`scale()`** method of the /// [DOMMatrixReadOnly] interface creates a new matrix being the result of the @@ -482,10 +448,7 @@ extension type DOMMatrixReadOnly._(JSObject _) implements JSObject { num originY, num originZ, ]); - external DOMMatrix scaleNonUniform([ - num scaleX, - num scaleY, - ]); + external DOMMatrix scaleNonUniform([num scaleX, num scaleY]); /// The **`scale3d()`** method of the [DOMMatrixReadOnly] interface creates a /// new matrix which is the result of a 3D scale transform being applied @@ -507,11 +470,7 @@ extension type DOMMatrixReadOnly._(JSObject _) implements JSObject { /// by the specified number of degrees. The original matrix is not altered. /// /// To mutate the matrix as you rotate it, see [DOMMatrix.rotateSelf]. - external DOMMatrix rotate([ - num rotX, - num rotY, - num rotZ, - ]); + external DOMMatrix rotate([num rotX, num rotY, num rotZ]); /// The `rotateFromVector()` method of the [DOMMatrixReadOnly] interface is /// returns a new [DOMMatrix] created by rotating the source matrix by the @@ -522,10 +481,7 @@ extension type DOMMatrixReadOnly._(JSObject _) implements JSObject { /// /// To mutate the matrix as you rotate it by the angle between the specified /// vector and `(1, 0)`, see [DOMMatrix.rotateFromVectorSelf]. - external DOMMatrix rotateFromVector([ - num x, - num y, - ]); + external DOMMatrix rotateFromVector([num x, num y]); /// The `rotateAxisAngle()` method of the [DOMMatrixReadOnly] interface /// returns a new [DOMMatrix] created by rotating the source matrix by the @@ -533,12 +489,7 @@ extension type DOMMatrixReadOnly._(JSObject _) implements JSObject { /// /// To mutate the matrix as you rotate it, see /// [DOMMatrix.rotateAxisAngleSelf]. - external DOMMatrix rotateAxisAngle([ - num x, - num y, - num z, - num angle, - ]); + external DOMMatrix rotateAxisAngle([num x, num y, num z, num angle]); /// The `skewX()` method of the [DOMMatrixReadOnly] interface returns a new /// [DOMMatrix] created by applying the specified skew transformation to the @@ -713,11 +664,7 @@ extension type DOMMatrix._(JSObject _) implements DOMMatrixReadOnly, JSObject { /// /// To translate a matrix without mutating it, see /// [DOMMatrixReadOnly.translate] - external DOMMatrix translateSelf([ - num tx, - num ty, - num tz, - ]); + external DOMMatrix translateSelf([num tx, num ty, num tz]); external DOMMatrix scaleSelf([ num scaleX, num scaleY, @@ -747,11 +694,7 @@ extension type DOMMatrix._(JSObject _) implements DOMMatrixReadOnly, JSObject { /// rotated matrix. /// /// To rotate a matrix without mutating it, see [DOMMatrixReadOnly.rotate] - external DOMMatrix rotateSelf([ - num rotX, - num rotY, - num rotZ, - ]); + external DOMMatrix rotateSelf([num rotX, num rotY, num rotZ]); /// The `rotateFromVectorSelf()` method of the [DOMMatrix] interface is a /// mutable transformation method that modifies a matrix by rotating the @@ -764,10 +707,7 @@ extension type DOMMatrix._(JSObject _) implements DOMMatrixReadOnly, JSObject { /// To rotate a matrix from a vector without mutating it, see /// [DOMMatrixReadOnly.rotateFromVector], which creates a new rotated matrix /// while leaving the original unchanged. - external DOMMatrix rotateFromVectorSelf([ - num x, - num y, - ]); + external DOMMatrix rotateFromVectorSelf([num x, num y]); /// The `rotateAxisAngleSelf()` method of the [DOMMatrix] interface is a /// transformation method that rotates the source matrix by the given vector @@ -776,12 +716,7 @@ extension type DOMMatrix._(JSObject _) implements DOMMatrixReadOnly, JSObject { /// To rotate a matrix without mutating it, see /// [DOMMatrixReadOnly.rotateAxisAngle], which creates a new rotated matrix /// while leaving the original unchanged. - external DOMMatrix rotateAxisAngleSelf([ - num x, - num y, - num z, - num angle, - ]); + external DOMMatrix rotateAxisAngleSelf([num x, num y, num z, num angle]); /// The `skewXSelf()` method of the [DOMMatrix] interface is a mutable /// transformation method that modifies a matrix. It skews the source matrix diff --git a/web/lib/src/dom/hr_time.dart b/web/lib/src/dom/hr_time.dart index 5d4c4285..62b88788 100644 --- a/web/lib/src/dom/hr_time.dart +++ b/web/lib/src/dom/hr_time.dart @@ -129,10 +129,7 @@ extension type Performance._(JSObject _) implements EventTarget, JSObject { /// /// To access entries of these types, you must use a [PerformanceObserver] /// instead. - external PerformanceEntryList getEntriesByName( - String name, [ - String type, - ]); + external PerformanceEntryList getEntriesByName(String name, [String type]); /// The **`clearResourceTimings()`** method removes all performance entries /// with an [PerformanceEntry.entryType] of `"resource"` from the browser's diff --git a/web/lib/src/dom/html.dart b/web/lib/src/dom/html.dart index 58c78514..741329e3 100644 --- a/web/lib/src/dom/html.dart +++ b/web/lib/src/dom/html.dart @@ -10,6 +10,11 @@ // ignore_for_file: constant_identifier_names, non_constant_identifier_names +/// @docImport 'svg.dart'; +/// @docImport 'webcodecs.dart'; +/// @docImport 'webgl1.dart'; +/// @docImport 'webgl2.dart'; +/// @docImport 'webgpu.dart'; @JS() library; @@ -61,17 +66,53 @@ import 'webidl.dart'; import 'webmidi.dart'; import 'xhr.dart'; +/// Union of: [HTMLScriptElement], [SVGScriptElement] typedef HTMLOrSVGScriptElement = JSObject; + +/// Union of: [Blob], [MediaSource], [MediaStream] typedef MediaProvider = JSObject; + +/// Union of 5 types +/// +/// - [CanvasRenderingContext2D] +/// - [ImageBitmapRenderingContext] +/// - [WebGL2RenderingContext] +/// - [WebGLRenderingContext] +/// - `GPUCanvasContext` typedef RenderingContext = JSObject; + +/// Union of: [HTMLImageElement], [SVGImageElement] typedef HTMLOrSVGImageElement = JSObject; + +/// Union of 6 types +/// +/// - [HTMLCanvasElement] +/// - [HTMLOrSVGImageElement] +/// - [HTMLVideoElement] +/// - [ImageBitmap] +/// - [OffscreenCanvas] +/// - [VideoFrame] typedef CanvasImageSource = JSObject; + +/// Union of 5 types +/// +/// - [ImageBitmapRenderingContext] +/// - [OffscreenCanvasRenderingContext2D] +/// - [WebGL2RenderingContext] +/// - [WebGLRenderingContext] +/// - `GPUCanvasContext` typedef OffscreenRenderingContext = JSObject; typedef EventHandler = EventHandlerNonNull?; typedef OnErrorEventHandler = OnErrorEventHandlerNonNull?; typedef OnBeforeUnloadEventHandler = OnBeforeUnloadEventHandlerNonNull?; + +/// Union of: [JSFunction], [JSString], [TrustedScript] typedef TimerHandler = JSAny; + +/// Union of: [Blob], [CanvasImageSource], [ImageData] typedef ImageBitmapSource = JSObject; + +/// Union of: [MessagePort], [ServiceWorker], [Window] typedef MessageEventSource = JSObject; typedef BlobCallback = JSFunction; typedef CustomElementConstructor = JSFunction; @@ -202,18 +243,12 @@ extension type RadioNodeList._(JSObject _) implements NodeList, JSObject { /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionsCollection). extension type HTMLOptionsCollection._(JSObject _) implements HTMLCollection, JSObject { - external void operator []=( - int index, - HTMLOptionElement? option, - ); + external void operator []=(int index, HTMLOptionElement? option); /// The **`add()`** method of the [HTMLOptionsCollection] interface adds an /// [HTMLOptionElement] or [HTMLOptGroupElement] to this /// `HTMLOptionsCollection`. - external void add( - JSObject element, [ - JSAny? before, - ]); + external void add(JSObject element, [JSAny? before]); /// The **`remove()`** method of the [HTMLOptionsCollection] interface removes /// the `option` element specified by the index from this collection. @@ -1285,10 +1320,7 @@ extension type ShowPopoverOptions._(JSObject _) implements JSObject { } extension type TogglePopoverOptions._(JSObject _) implements ShowPopoverOptions, JSObject { - external factory TogglePopoverOptions({ - HTMLElement source, - bool force, - }); + external factory TogglePopoverOptions({HTMLElement source, bool force}); external bool get force; external set force(bool value); @@ -1338,10 +1370,7 @@ extension type HTMLUnknownElement._(JSObject _) /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMStringMap). extension type DOMStringMap._(JSObject _) implements JSObject { external String operator [](String name); - external void operator []=( - String name, - String value, - ); + external void operator []=(String name, String value); } /// The **`HTMLHtmlElement`** interface serves as the root node for a given HTML @@ -4495,10 +4524,7 @@ extension type TimeRanges._(JSObject _) implements JSObject { /// API documentation sourced from /// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/TrackEvent). extension type TrackEvent._(JSObject _) implements Event, JSObject { - external factory TrackEvent( - String type, [ - TrackEventInit eventInitDict, - ]); + external factory TrackEvent(String type, [TrackEventInit eventInitDict]); /// The read-only **`track`** property of /// the [TrackEvent] interface specifies the media track object to which the @@ -6054,11 +6080,7 @@ extension type HTMLInputElement._(JSObject _) implements HTMLElement, JSObject { /// If you wish to select **all** text of an input element, you can use the /// [HTMLInputElement.select()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select) /// method instead. - external void setSelectionRange( - int start, - int end, [ - String direction, - ]); + external void setSelectionRange(int start, int end, [String direction]); /// The **`HTMLInputElement.showPicker()`** method displays the browser picker /// for an `input` element. @@ -6994,19 +7016,13 @@ extension type HTMLSelectElement._(JSObject _) /// The **`HTMLSelectElement.add()`** method adds an element to the /// collection of `option` elements for this `select` element. - external void add( - JSObject element, [ - JSAny? before, - ]); + external void add(JSObject element, [JSAny? before]); /// The **`HTMLSelectElement.remove()`** method removes the element /// at the specified index from the options collection for this select /// element. external void remove([int index]); - external void operator []=( - int index, - HTMLOptionElement? option, - ); + external void operator []=(int index, HTMLOptionElement? option); /// The **`checkValidity()`** method of the [HTMLSelectElement] interface /// returns a boolean value which indicates if the element meets any @@ -7425,11 +7441,7 @@ extension type HTMLTextAreaElement._(JSObject _) /// /// To select **all** of the text of an `