Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1a228f4
Squashed commit of the following:
nikeokoronkwo Mar 15, 2026
257fd7d
[generator] Added support for source map backed stack traces
nikeokoronkwo Mar 15, 2026
313d67b
Fixed analysis and format issues
nikeokoronkwo Mar 16, 2026
7fac81b
check processStderr before split
nikeokoronkwo Mar 16, 2026
5a259e6
fix JSDate test overflow and simplify transformer code (#523)
kevmoo Mar 30, 2026
e0ea82d
Bump dart-lang/setup-dart in the github-actions group (#524)
dependabot[bot] Apr 1, 2026
15599ee
Update min SDKs, regenerate (#526)
kevmoo Apr 13, 2026
b026317
generator: be more robust against NPM timing issues (#528)
kevmoo Apr 13, 2026
d7daf7d
generator: add some sanity to the language version logic (#530)
kevmoo Apr 14, 2026
3a7e37a
Stabilize JS Interop CI and decouple webref dependencies (#532)
kevmoo Apr 19, 2026
5e7285f
chore: update node invocation to use the source maps we already gener…
kevmoo Apr 24, 2026
db91caf
chore: write BCD version to README in web_generator (#537)
kevmoo Apr 27, 2026
2c47e36
feat: generate descriptive documentation for union typedefs (#534)
kevmoo Apr 28, 2026
5ed9bed
js_interop_gen: minimize the number of `@docImport` comments we creat…
kevmoo Apr 29, 2026
7c908b1
test(js_interop_gen): add integration tests (#541)
kevmoo Apr 29, 2026
41c4049
refactor: move most of the AST elements to their own file (#543)
kevmoo May 4, 2026
294391c
[chore] Sort the members in elements.dart (#544)
kevmoo May 4, 2026
5d4fd3c
refactor(js_interop_gen): extract pure utilities and data classes (#545)
kevmoo May 6, 2026
d9a042b
Squashed commit of the following:
nikeokoronkwo Mar 15, 2026
05e0405
[generator] Added support for source map backed stack traces
nikeokoronkwo Mar 15, 2026
91ed45c
Fixed analysis and format issues
nikeokoronkwo Mar 16, 2026
10e99d6
check processStderr before split
nikeokoronkwo Mar 16, 2026
3af5130
Merge branch 'feat/stack_traces' of https://github.com/nikeokoronkwo/…
nikeokoronkwo May 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/js_interop.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 }}
53 changes: 53 additions & 0 deletions .github/workflows/js_interop_gen.yaml
Original file line number Diff line number Diff line change
@@ -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 }}
5 changes: 4 additions & 1 deletion .github/workflows/web.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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.
Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/web_generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<!-- TODO(srujzs): Add links to CI once we have a badge that doesn't say "package:web" and is more generic. -->

## 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) |
2 changes: 1 addition & 1 deletion js_interop/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion js_interop/test/date_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -196,6 +199,7 @@ void main() {
test('toUtcString:', () => expect(date.toUtcString(), isA<String>()));

test('toDart', () {
var date = JSDate.nowAsDate();
var dartDate = date.toDart;
expect(dartDate, isA<DateTime>());
expect(dartDate.timeZoneName, equals(DateTime.now().timeZoneName));
Expand Down
3 changes: 1 addition & 2 deletions web_generator/CHANGELOG.md → js_interop_gen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
- Adapt paths for different development environments.
File renamed without changes.
112 changes: 112 additions & 0 deletions js_interop_gen/README.md
Original file line number Diff line number Diff line change
@@ -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 <input.d.ts>
dart run js_interop_gen -o <output> <input>
```

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 | <pre>`name: MyBindings`</pre> |
| `description` | A description of the bindings (optional) | <pre>`description: My awesome bindings`</pre> |
| `preamble` | Preamble text to insert before the bindings (optional) | <pre>`preamble: \|`<br>` // Generated. Do not edit.`</pre> |
| `input` | A file (single string) or set of files (array of strings) passed into the generator | <pre>`input: bindings.d.ts`</pre> <br/> or <br/> <pre>`input: `<br>` - bindings.d.ts`</pre> |
| `output` | The output file or directory to write the bindings to | <pre>`output: lib/src/js`</pre> |
| `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** | <pre>`include: `<br>` - myNumber`</pre> |
| `language_version` | The Dart Language Version to use, usually for formatting (optional) | <pre>`language_version: 3.6.0`</pre> |
| `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) | <pre>`ts_config: `<br>` compilerOptions: `<br>` target: es2020`</pre> |
| `ts_config_file` | The TS Configuration file (tsconfig.json) if any (optional) | <pre>`ts_config_file: tsconfig.json`</pre> |
| `generate_all` | Include generating declarations for code that isn't exported. Defaults to false | <pre>`generate_all: true`</pre> |
| `ignore_errors` | Ignore source code warnings and errors (they will still be printed). Defaults to false | <pre>`ignore_errors: true`</pre> |
| `functions.varargs` | The number of arguments that variable-argument functions should take. Defaults to 4 | <pre>`functions: `<br>` varargs: 6`</pre> |

### 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`
Loading
Loading