From c2e45680535eab7dee260bf23dd4961253277b2f Mon Sep 17 00:00:00 2001 From: MarkZ Date: Fri, 5 Dec 2025 13:06:40 -0800 Subject: [PATCH 01/10] Adding support for the DDC Library Bundle module system. --- build_modules/lib/src/module_builder.dart | 10 +++++----- build_web_compilers/lib/builders.dart | 14 ++++++++++++-- .../lib/src/dev_compiler_bootstrap.dart | 6 ++++-- .../lib/src/dev_compiler_builder.dart | 14 +++++++------- .../lib/src/sdk_js_compile_builder.dart | 15 +++++++-------- .../lib/src/web_entrypoint_builder.dart | 17 +++++++++++++++-- .../lib/src/web_entrypoint_marker_builder.dart | 2 +- 7 files changed, 51 insertions(+), 27 deletions(-) diff --git a/build_modules/lib/src/module_builder.dart b/build_modules/lib/src/module_builder.dart index 380c9ab5f5..8b7285f367 100644 --- a/build_modules/lib/src/module_builder.dart +++ b/build_modules/lib/src/module_builder.dart @@ -24,12 +24,12 @@ String moduleExtension(DartPlatform platform) => '.${platform.name}.module'; class ModuleBuilder implements Builder { final DartPlatform _platform; - /// Emits DDC code with the Library Bundle module system, which supports hot - /// reload. - /// /// If set, this builder will consume raw meta modules (instead of clean). - /// Clean meta modules are only used for DDC's AMD module system due its - /// requirement that self-referential libraries be bundled. + /// + /// Clean meta modules cannot be used when compiling with the Frontend Server + /// due to potentially divergent bundling strategies between it and + /// build_runner. Additionally, bundling isn't required in DDC's Library + /// Bundle module system. final bool usesWebHotReload; ModuleBuilder(this._platform, {this.usesWebHotReload = false}) diff --git a/build_web_compilers/lib/builders.dart b/build_web_compilers/lib/builders.dart index f11ddac3e8..456ec17f4c 100644 --- a/build_web_compilers/lib/builders.dart +++ b/build_web_compilers/lib/builders.dart @@ -59,7 +59,9 @@ Builder ddcBuilder(BuilderOptions options) { generateFullDill: _readGenerateFullDillOption(options), emitDebugSymbols: _readEmitDebugSymbolsOption(options), canaryFeatures: _readCanaryOption(options), - ddcModules: _readWebHotReloadOption(options), + ddcLibraryBundle: + _readDdcLibraryBundleOption(options) || + _readWebHotReloadOption(options), sdkKernelPath: sdkDdcKernelPath, trackUnusedInputs: _readTrackInputsCompilerOption(options), platform: ddcPlatform, @@ -92,7 +94,9 @@ Builder sdkJsCompile(BuilderOptions options) { outputPath: 'lib/src/dev_compiler/dart_sdk.js', canaryFeatures: _readWebHotReloadOption(options) || _readCanaryOption(options), - usesWebHotReload: _readWebHotReloadOption(options), + ddcLibraryBundle: + _readDdcLibraryBundleOption(options) || + _readWebHotReloadOption(options), ); } @@ -194,6 +198,10 @@ bool _readWebHotReloadOption(BuilderOptions options) { return options.config[_webHotReloadOption] as bool? ?? false; } +bool _readDdcLibraryBundleOption(BuilderOptions options) { + return options.config[_ddcLibraryBundleOption] as bool? ?? false; +} + Map _readEnvironmentOption(BuilderOptions options) { final environment = options.config[_environmentOption] as Map? ?? const {}; return environment.map((key, value) => MapEntry('$key', '$value')); @@ -208,6 +216,7 @@ const _canaryOption = 'canary'; const _trackUnusedInputsCompilerOption = 'track-unused-inputs'; const _environmentOption = 'environment'; const _webHotReloadOption = 'web-hot-reload'; +const _ddcLibraryBundleOption = 'ddc-library-bundle'; const _supportedOptions = [ _environmentOption, @@ -217,4 +226,5 @@ const _supportedOptions = [ _canaryOption, _trackUnusedInputsCompilerOption, _webHotReloadOption, + _ddcLibraryBundleOption, ]; diff --git a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart index bb71c44f94..e1591f1e27 100644 --- a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart +++ b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart @@ -38,6 +38,7 @@ Future bootstrapDdc( String entrypointExtension = jsEntrypointExtension, required bool? nativeNullAssertions, bool usesWebHotReload = false, + bool ddcLibraryBundle = false, }) async { platform = ddcPlatform; // Ensures that the sdk resources are built and available. @@ -110,7 +111,7 @@ $librariesString final dartEntrypointParts = _context.split(dartEntrypointId.path); final packageName = module.primarySource.package; final entrypointLibraryName = - usesWebHotReload + ddcLibraryBundle ? _context.joinAll([ // Convert to a package: uri for files under lib. if (dartEntrypointParts.first == 'lib') 'package:$packageName', @@ -130,7 +131,8 @@ $librariesString String entrypointJsContent; String bootstrapContent; String bootstrapEndContent; - if (usesWebHotReload) { + print(ddcLibraryBundle); + if (ddcLibraryBundle) { final ddcSdkUrl = r'packages/build_web_compilers/src/dev_compiler/dart_sdk.js'; modulePaths['dart_sdk'] = ddcSdkUrl; diff --git a/build_web_compilers/lib/src/dev_compiler_builder.dart b/build_web_compilers/lib/src/dev_compiler_builder.dart index 66cbe235cd..f78deb8ca9 100644 --- a/build_web_compilers/lib/src/dev_compiler_builder.dart +++ b/build_web_compilers/lib/src/dev_compiler_builder.dart @@ -42,8 +42,8 @@ class DevCompilerBuilder implements Builder { /// Enables canary features in DDC. final bool canaryFeatures; - /// Emits code with the DDC module system. - final bool ddcModules; + /// Emits code with the DDC Library Bundle module system. + final bool ddcLibraryBundle; final bool trackUnusedInputs; @@ -74,7 +74,7 @@ class DevCompilerBuilder implements Builder { this.generateFullDill = false, this.emitDebugSymbols = false, this.canaryFeatures = false, - this.ddcModules = false, + this.ddcLibraryBundle = false, this.trackUnusedInputs = false, required this.platform, String? sdkKernelPath, @@ -129,7 +129,7 @@ class DevCompilerBuilder implements Builder { generateFullDill, emitDebugSymbols, canaryFeatures, - ddcModules, + ddcLibraryBundle, trackUnusedInputs, platformSdk, sdkKernelPath, @@ -152,7 +152,7 @@ Future _createDevCompilerModule( bool generateFullDill, bool emitDebugSymbols, bool canaryFeatures, - bool ddcModules, + bool ddcLibraryBundle, bool trackUnusedInputs, String dartSdk, String sdkKernelPath, @@ -202,11 +202,11 @@ Future _createDevCompilerModule( WorkRequest() ..arguments.addAll([ '--dart-sdk-summary=$sdkSummary', - '--modules=${ddcModules ? 'ddc' : 'amd'}', + '--modules=${ddcLibraryBundle ? 'ddc' : 'amd'}', '--no-summarize', if (generateFullDill) '--experimental-output-compiled-kernel', if (emitDebugSymbols) '--emit-debug-symbols', - if (canaryFeatures) '--canary', + if (canaryFeatures || ddcLibraryBundle) '--canary', '-o', jsOutputFile.path, debugMode ? '--source-map' : '--no-source-map', diff --git a/build_web_compilers/lib/src/sdk_js_compile_builder.dart b/build_web_compilers/lib/src/sdk_js_compile_builder.dart index 824fc89836..f0bd4ff35a 100644 --- a/build_web_compilers/lib/src/sdk_js_compile_builder.dart +++ b/build_web_compilers/lib/src/sdk_js_compile_builder.dart @@ -42,9 +42,8 @@ class SdkJsCompileBuilder implements Builder { /// Enables canary features in DDC. final bool canaryFeatures; - /// Emits DDC code with the Library Bundle module system, which supports hot - /// reload. - final bool usesWebHotReload; + /// Emits DDC code using its Library Bundle module system. + final bool ddcLibraryBundle; SdkJsCompileBuilder({ required this.sdkKernelPath, @@ -52,7 +51,7 @@ class SdkJsCompileBuilder implements Builder { String? librariesPath, String? platformSdk, required this.canaryFeatures, - required this.usesWebHotReload, + required this.ddcLibraryBundle, }) : platformSdk = platformSdk ?? sdkDir, librariesPath = librariesPath ?? @@ -77,7 +76,7 @@ class SdkJsCompileBuilder implements Builder { librariesPath, jsOutputId, canaryFeatures, - usesWebHotReload, + ddcLibraryBundle, ); } } @@ -90,7 +89,7 @@ Future _createDevCompilerModule( String librariesPath, AssetId jsOutputId, bool canaryFeatures, - bool usesWebHotReload, + bool ddcLibraryBundle, ) async { final scratchSpace = await buildStep.fetchResource(scratchSpaceResource); final jsOutputFile = scratchSpace.fileFor(jsOutputId); @@ -119,8 +118,8 @@ Future _createDevCompilerModule( result = await Process.run(dartPath, [ snapshotPath, '--multi-root-scheme=org-dartlang-sdk', - '--modules=${usesWebHotReload ? 'ddc' : 'amd'}', - if (canaryFeatures || usesWebHotReload) '--canary', + '--modules=${ddcLibraryBundle ? 'ddc' : 'amd'}', + if (canaryFeatures || ddcLibraryBundle) '--canary', '--module-name=dart_sdk', '-o', jsOutputFile.path, diff --git a/build_web_compilers/lib/src/web_entrypoint_builder.dart b/build_web_compilers/lib/src/web_entrypoint_builder.dart index 8452e7590d..d0d48e73e1 100644 --- a/build_web_compilers/lib/src/web_entrypoint_builder.dart +++ b/build_web_compilers/lib/src/web_entrypoint_builder.dart @@ -126,14 +126,19 @@ final class EntrypointBuilderOptions { /// Whether or not to emit DDC entrypoints that support web hot reload. /// - /// Web hot reload is only supported for DDC's Library Bundle module system. + /// Only supported for DDC's Library Bundle module system. final bool usesWebHotReload; + /// Whether or not to emit DDC entrypoints that target the DDC Library Bundle + /// module system. + final bool ddcLibraryBundle; + EntrypointBuilderOptions({ required this.compilers, this.nativeNullAssertions, this.loaderExtension, this.usesWebHotReload = false, + this.ddcLibraryBundle = false, }); factory EntrypointBuilderOptions.fromOptions(BuilderOptions options) { @@ -146,6 +151,7 @@ final class EntrypointBuilderOptions { const nativeNullAssertionsOption = 'native_null_assertions'; const loaderOption = 'loader'; const webHotReloadOption = 'web-hot-reload'; + const ddcLibraryBundleOption = 'ddc-library-bundle'; String? defaultLoaderOption; const supportedOptions = [ @@ -156,12 +162,15 @@ final class EntrypointBuilderOptions { dart2wasmArgsOption, loaderOption, webHotReloadOption, + ddcLibraryBundleOption, ]; final config = options.config; final nativeNullAssertions = options.config[nativeNullAssertionsOption] as bool?; final usesWebHotReload = options.config[webHotReloadOption] as bool?; + final usesDdcLibraryBundle = + usesWebHotReload ?? options.config[ddcLibraryBundleOption] as bool?; final compilers = []; validateOptions( @@ -249,6 +258,7 @@ final class EntrypointBuilderOptions { ? config[loaderOption] as String? : defaultLoaderOption, usesWebHotReload: usesWebHotReload ?? false, + ddcLibraryBundle: usesDdcLibraryBundle ?? false, ); } @@ -330,14 +340,17 @@ class WebEntrypointBuilder implements Builder { Future(() async { try { final usesWebHotReload = options.usesWebHotReload; + final usesDdcLibraryBundle = + options.ddcLibraryBundle || usesWebHotReload; await bootstrapDdc( buildStep, nativeNullAssertions: options.nativeNullAssertions, requiredAssets: - usesWebHotReload + usesDdcLibraryBundle ? _ddcLibraryBundleSdkResources : _ddcSdkResources, usesWebHotReload: usesWebHotReload, + ddcLibraryBundle: usesDdcLibraryBundle, ); } on MissingModulesException catch (e) { log.severe('$e'); diff --git a/build_web_compilers/lib/src/web_entrypoint_marker_builder.dart b/build_web_compilers/lib/src/web_entrypoint_marker_builder.dart index cc2dda9ad7..90ac8fee41 100644 --- a/build_web_compilers/lib/src/web_entrypoint_marker_builder.dart +++ b/build_web_compilers/lib/src/web_entrypoint_marker_builder.dart @@ -11,7 +11,7 @@ import 'package:glob/glob.dart'; /// A builder that gathers information about a web target's 'main' entrypoint. class WebEntrypointMarkerBuilder implements Builder { /// Records state (such as the web entrypoint) required when compiling DDC - /// with the Library Bundle module system. + /// with the Frontend Server, which supports hot reload. /// /// A no-op if [usesWebHotReload] is not set. final bool usesWebHotReload; From f5cf2d683b9cba31a800d52de7ae88305da87ef1 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Fri, 5 Dec 2025 18:09:03 -0800 Subject: [PATCH 02/10] adding tests --- .../lib/src/dev_compiler_bootstrap.dart | 5 +- .../ddc_library_bundle_bootstrap_test.dart | 275 ++++++++++++ .../test/ddc_library_bundle_builder_test.dart | 412 ++++++++++++++++++ 3 files changed, 689 insertions(+), 3 deletions(-) create mode 100644 build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart create mode 100644 build_web_compilers/test/ddc_library_bundle_builder_test.dart diff --git a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart index e1591f1e27..a4f461d1f7 100644 --- a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart +++ b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart @@ -131,7 +131,6 @@ $librariesString String entrypointJsContent; String bootstrapContent; String bootstrapEndContent; - print(ddcLibraryBundle); if (ddcLibraryBundle) { final ddcSdkUrl = r'packages/build_web_compilers/src/dev_compiler/dart_sdk.js'; @@ -148,8 +147,8 @@ $librariesString : _context.joinAll(_context.split(jsId.path).skip(1)); } final bootstrapEndModuleName = _context.relative( - bootstrapId.path, - from: _context.dirname(bootstrapEndId.path), + bootstrapEndId.path, + from: _context.dirname(bootstrapId.path), ); bootstrapContent = generateDDCLibraryBundleMainModule( entrypoint: entrypointLibraryName, diff --git a/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart new file mode 100644 index 0000000000..86d6e6bfb6 --- /dev/null +++ b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart @@ -0,0 +1,275 @@ +// 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 'package:build/build.dart'; +import 'package:build_modules/build_modules.dart'; +import 'package:build_test/build_test.dart'; +import 'package:build_web_compilers/build_web_compilers.dart'; +import 'package:build_web_compilers/builders.dart'; +import 'package:test/test.dart'; + +final defaultBuilderOptions = const BuilderOptions({ + 'compiler': 'dartdevc', + 'ddc-library-bundle': true, + 'native_null_assertions': false, +}); + +void main() { + final startingBuilders = { + // Uses the real sdk copy builder to copy required files from the SDK. + sdkJsCopyRequirejs(const BuilderOptions({})), + sdkJsCompile(defaultBuilderOptions), + const ModuleLibraryBuilder(), + MetaModuleBuilder(ddcPlatform), + MetaModuleCleanBuilder(ddcPlatform), + ModuleBuilder(ddcPlatform), + ddcKernelBuilder(const BuilderOptions({})), + DevCompilerBuilder(platform: ddcPlatform, ddcLibraryBundle: true), + }; + group('DDC Library Bundle:', () { + group('simple project', () { + final startingAssets = { + 'a|lib/a.dart': ''' + import 'package:b/b.dart'; + final hello = world; + ''', + 'a|web/index.dart': ''' + import "package:a/a.dart"; + main() { + print(hello); + } + ''', + 'b|lib/b.dart': '''final world = 'world';''', + // Add a fake asset so that the build_web_compilers package exists. + 'build_web_compilers|fake.txt': '', + }; + final startingExpectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|lib/a.ddc.dill': isNotNull, + 'a|lib/a.ddc.js.map': isNotNull, + 'a|lib/a.ddc.js.metadata': isNotNull, + 'a|lib/a.ddc.js': isNotNull, + 'a|lib/a.ddc.module': isNotNull, + 'a|lib/a.module.library': isNotNull, + 'a|web/index.ddc.dill': isNotNull, + 'a|web/index.ddc.js.map': isNotNull, + 'a|web/index.ddc.js.metadata': isNotNull, + 'a|web/index.ddc.js': isNotNull, + 'a|web/index.ddc.module': isNotNull, + 'a|web/index.module.library': isNotNull, + 'b|lib/.ddc.meta_module.clean': isNotNull, + 'b|lib/.ddc.meta_module.raw': isNotNull, + 'b|lib/b.ddc.dill': isNotNull, + 'b|lib/b.ddc.js.map': isNotNull, + 'b|lib/b.ddc.js.metadata': isNotNull, + 'b|lib/b.ddc.js': isNotNull, + 'b|lib/b.ddc.module': isNotNull, + 'b|lib/b.module.library': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.clean': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.raw': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/ddc_module_loader.js': + isNotNull, + 'build_web_compilers|lib/src/dev_compiler/require.js': isNotNull, + }; + + test('base build', () async { + await testBuilders( + startingBuilders, + startingAssets, + outputs: startingExpectedOutputs, + ); + }); + + test('can bootstrap dart entrypoints', () async { + // Just do some basic sanity checking, integration tests will validate + // things actually work. + final builder = WebEntrypointBuilder.fromOptions(defaultBuilderOptions); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|web/index.dart.bootstrap.js': decodedMatches( + allOf([ + // Calls 'main' via the embedder. + contains('dartDevEmbedder.runMain'), + ]), + ), + 'a|web/index.dart.bootstrap.end.js': isNotEmpty, + 'a|web/index.dart.ddc_merged_metadata': isNotEmpty, + 'a|web/index.ddc.js': decodedMatches( + // Contains the library declaration of the entrypoint library. + contains( + 'dartDevEmbedder.defineLibrary("org-dartlang-app:///web/index.dart"', + ), + ), + 'a|web/index.dart.js': decodedMatches( + allOf([ + // Contains a script pointer to main's bootstrap.js file. + contains('"src": "index.dart.bootstrap.js", "id": "data-main"'), + // Maps non-lib modules to remove the top level dir. + contains( + '"src": "index.ddc.js", "id": "org-dartlang-app:///web/index.dart"', + ), + // Maps lib modules to packages path + contains( + '"src": "packages/a/a.ddc.js", "id": "package:a/a.dart"', + ), + contains( + '"src": "packages/b/b.ddc.js", "id": "package:b/b.dart"', + ), + // Imports the dart sdk. + contains('"id": "dart_sdk"'), + isNot(contains('lib/a')), + ]), + ), + 'a|web/index.digests': decodedMatches(contains('packages/')), + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + }); + group('regression tests', () { + test('root dart file is not the primary source, #2269', () async { + final builder = WebEntrypointBuilder.fromOptions(defaultBuilderOptions); + final assets = { + // Becomes the primary source for the module, since it we alpha-sort. + 'a|web/a.dart': ''' + final hello = 'hello'; + ''', + // Rolled into the module for `a.dart`, as a normal source. + 'a|web/b.dart': ''' + import 'a.dart'; + main() { + print(hello); + } + ''', + // Add a fake asset so that the build_web_compilers package exists. + 'build_web_compilers|fake.txt': '', + }; + // Check that we are invoking the correct + final expectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|web/a.ddc.dill': isNotNull, + 'a|web/a.ddc.js.map': isNotNull, + 'a|web/a.ddc.js.metadata': isNotNull, + 'a|web/a.ddc.js': isNotNull, + 'a|web/a.ddc.module': isNotNull, + 'a|web/a.module.library': isNotNull, + 'a|web/b.dart.bootstrap.js': isNotEmpty, + 'a|web/b.dart.bootstrap.end.js': isNotEmpty, + 'a|web/b.dart.ddc_merged_metadata': isNotNull, + 'a|web/b.dart.js': decodedMatches( + allOf([ + // Confirm that `a.dart` is the actual primary source. + contains( + '"src": "a.ddc.js", "id": "org-dartlang-app:///web/a.dart"', + ), + // And `b.dart` is the application whose 'main' is being invoked. + contains('"src": "b.dart.bootstrap.js", "id": "data-main'), + ]), + ), + 'a|web/b.ddc.module': isNotNull, + 'a|web/b.digests': isNotNull, + 'a|web/b.module.library': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.clean': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.raw': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/ddc_module_loader.js': + isNotNull, + 'build_web_compilers|lib/src/dev_compiler/require.js': isNotNull, + }; + + await testBuilders( + [...startingBuilders, builder], + assets, + outputs: expectedOutputs, + ); + }); + + test('root dart file is under lib', () async { + final builder = WebEntrypointBuilder.fromOptions(defaultBuilderOptions); + final assets = { + 'a|lib/app.dart': 'void main() {}', + // Add a fake asset so that the build_web_compilers package exists. + 'build_web_compilers|fake.txt': '', + }; + final expectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|lib/app.dart.bootstrap.js': isNotNull, + 'a|lib/app.dart.bootstrap.end.js': isNotEmpty, + 'a|lib/app.dart.ddc_merged_metadata': isNotEmpty, + 'a|lib/app.dart.js': decodedMatches( + // Confirm that the child name is referenced via a package: uri + // and not relative path to the root dir being served. + contains( + '"src": "packages/a/app.ddc.js", "id": "package:a/app.dart"', + ), + ), + 'a|lib/app.ddc.dill': isNotNull, + 'a|lib/app.ddc.js.map': isNotNull, + 'a|lib/app.ddc.js.metadata': isNotNull, + 'a|lib/app.ddc.js': isNotNull, + 'a|lib/app.ddc.module': isNotNull, + 'a|lib/app.digests': isNotEmpty, + 'a|lib/app.module.library': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.clean': isNotNull, + 'build_web_compilers|lib/.ddc.meta_module.raw': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': isNotNull, + 'build_web_compilers|lib/src/dev_compiler/ddc_module_loader.js': + isNotNull, + 'build_web_compilers|lib/src/dev_compiler/require.js': isNotNull, + }; + + await testBuilders( + [...startingBuilders, builder], + assets, + outputs: expectedOutputs, + ); + }); + + test('can enable canary features for SDK', () async { + final builder = sdkJsCompile( + const BuilderOptions({'canary': true, 'ddc-library-bundle': true}), + ); + final sdkAssets = {'build_web_compilers|fake.txt': ''}; + final expectedOutputs = { + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': + decodedMatches(contains('canary')), + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': + isNotEmpty, + }; + await testBuilder(builder, sdkAssets, outputs: expectedOutputs); + }); + + test( + 'does not enable canary features for SDK by default', + () async { + final builder = sdkJsCompile( + const BuilderOptions({'ddc-library-bundle': true}), + ); + final sdkAssets = { + 'build_web_compilers|fake.txt': '', + }; + final expectedOutputs = { + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': + decodedMatches(isNot(contains('canary'))), + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': + isNotEmpty, + }; + await testBuilder(builder, sdkAssets, outputs: expectedOutputs); + }, + skip: + 'Enable this test when the library bundle module system is no ' + 'longer locked behind the --canary flag', + ); + }); + }); +} diff --git a/build_web_compilers/test/ddc_library_bundle_builder_test.dart b/build_web_compilers/test/ddc_library_bundle_builder_test.dart new file mode 100644 index 0000000000..9199376ce0 --- /dev/null +++ b/build_web_compilers/test/ddc_library_bundle_builder_test.dart @@ -0,0 +1,412 @@ +// 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 'package:build/build.dart'; +import 'package:build_modules/build_modules.dart'; +import 'package:build_test/build_test.dart'; +import 'package:build_web_compilers/build_web_compilers.dart'; +import 'package:build_web_compilers/builders.dart'; +import 'package:logging/logging.dart'; +import 'package:test/test.dart'; + +final builderOptions = const BuilderOptions({ + 'track-unused-inputs': false, + 'ddc-library-bundle': true, +}); + +void main() { + group('DDC Library Bundle:', () { + group('error free project', () { + final startingAssets = { + 'a|lib/a.dart': r''' + // @dart=2.12 + import 'package:b/b.dart'; + final hello = 'hello $world'; + ''', + 'a|web/index.dart': ''' + // @dart=2.12 + import "package:a/a.dart"; + void main() { + print(hello); + print(const String.fromEnvironment('foo', defaultValue: 'bar')); + } + ''', + 'b|lib/b.dart': ''' + // @dart=2.12 + final world = 'world';''', + }; + final startingBuilders = [ + const ModuleLibraryBuilder(), + MetaModuleBuilder(ddcPlatform), + MetaModuleCleanBuilder(ddcPlatform), + ModuleBuilder(ddcPlatform), + ddcKernelBuilder(builderOptions), + ]; + final startingExpectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|lib/a.ddc.dill': isNotNull, + 'a|lib/a.ddc.module': isNotNull, + 'a|lib/a.module.library': isNotNull, + 'a|web/index.ddc.dill': isNotNull, + 'a|web/index.ddc.module': isNotNull, + 'a|web/index.module.library': isNotNull, + 'b|lib/.ddc.meta_module.clean': isNotNull, + 'b|lib/.ddc.meta_module.raw': isNotNull, + 'b|lib/b.ddc.dill': isNotNull, + 'b|lib/b.ddc.module': isNotNull, + 'b|lib/b.module.library': isNotNull, + }; + + setUp(() async { + final listener = Logger.root.onRecord.listen( + (r) => printOnFailure('$r\n${r.error}\n${r.stackTrace}'), + ); + addTearDown(listener.cancel); + }); + + test('base build', () async { + await testBuilders( + startingBuilders, + startingAssets, + outputs: startingExpectedOutputs, + ); + }); + + for (final trackUnusedInputs in [true, false]) { + test('can compile ddc modules under lib and web and ' + '${trackUnusedInputs ? 'track' : 'not track'} ' + 'unused inputs', () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + useIncrementalCompiler: trackUnusedInputs, + trackUnusedInputs: trackUnusedInputs, + ); + + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': decodedMatches(contains('hello')), + 'a|lib/a$jsSourceMapExtension': decodedMatches(contains('a.dart')), + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': decodedMatches(contains('main')), + 'a|web/index$jsSourceMapExtension': decodedMatches( + contains('index.dart'), + ), + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': decodedMatches(contains('world')), + 'b|lib/b$jsSourceMapExtension': decodedMatches(contains('b.dart')), + 'b|lib/b$metadataExtension': isNotNull, + }); + + final reportedUnused = >{}; + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + reportUnusedAssetsForInput: (input, unused) { + reportedUnused[input] = unused; + }, + ); + + expect( + reportedUnused[AssetId( + 'a', + 'web/index${moduleExtension(ddcPlatform)}', + )], + equals( + trackUnusedInputs + ? [AssetId('b', 'lib/b$ddcKernelExtension')] + : null, + ), + reason: + 'Should${trackUnusedInputs ? '' : ' not'} report unused ' + 'transitive deps.', + ); + }); + } + + test('allows a custom environment', () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + environment: {'foo': 'zap'}, + ); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': decodedMatches( + contains('print("zap")'), + ), + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('can enable DDC canary features', () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + canaryFeatures: true, + ); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': decodedMatches(contains('canary')), + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('does not enable DDC canary features by default', () async { + final builder = DevCompilerBuilder(platform: ddcPlatform); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': decodedMatches( + isNot(contains('canary')), + ), + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('generates full dill when enabled', () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + generateFullDill: true, + ); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$fullKernelExtension': isNotNull, + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$fullKernelExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$fullKernelExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('does not generate full dill by default', () async { + final builder = DevCompilerBuilder(platform: ddcPlatform); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('emits debug symbols when enabled', () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + emitDebugSymbols: true, + ); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|lib/a$symbolsExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'a|web/index$symbolsExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + 'b|lib/b$symbolsExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('does not emit debug symbols by default', () async { + final builder = DevCompilerBuilder(platform: ddcPlatform); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + + test('strips scratch paths from metadata', () async { + final builder = DevCompilerBuilder(platform: ddcPlatform); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': isNotNull, + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': decodedMatches( + isNot(contains('scratch')), + ), + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': decodedMatches( + isNot(contains('scratch')), + ), + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': decodedMatches( + isNot(contains('scratch')), + ), + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }); + }); + + group('projects with errors due to', () { + group('invalid assignements', () { + test('reports useful messages', () async { + final assets = { + 'a|web/index.dart': 'int x = "hello";', + 'build_modules|lib/src/analysis_options.default.yaml': '', + }; + final expectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|web/index.ddc.dill': isNotNull, + 'a|web/index.ddc.module': isNotNull, + 'a|web/index$jsModuleErrorsExtension': decodedMatches( + allOf(contains('String'), contains('assigned'), contains('int')), + ), + 'a|web/index.module.library': isNotNull, + 'build_modules|lib/.ddc.meta_module.clean': isNotNull, + 'build_modules|lib/.ddc.meta_module.raw': isNotNull, + }; + final logs = []; + await testBuilders( + [ + const ModuleLibraryBuilder(), + MetaModuleBuilder(ddcPlatform), + MetaModuleCleanBuilder(ddcPlatform), + ModuleBuilder(ddcPlatform), + ddcKernelBuilder(builderOptions), + DevCompilerBuilder(platform: ddcPlatform), + ], + assets, + outputs: expectedOutputs, + onLog: logs.add, + ); + expect( + logs, + contains( + predicate( + (record) => + record.level == Level.SEVERE && + record.message.contains('String') && + record.message.contains('assigned') && + record.message.contains('int'), + ), + ), + ); + }); + }); + + group('invalid imports', () { + test('reports useful messages', () async { + final assets = { + 'a|web/index.dart': "import 'package:a/a.dart'", + 'build_modules|lib/src/analysis_options.default.yaml': '', + }; + final expectedOutputs = { + 'a|lib/.ddc.meta_module.clean': isNotNull, + 'a|lib/.ddc.meta_module.raw': isNotNull, + 'a|web/index$jsModuleErrorsExtension': decodedMatches( + contains('Unable to find modules for some sources'), + ), + 'a|web/index.ddc.module': isNotNull, + 'a|web/index.module.library': isNotNull, + 'build_modules|lib/.ddc.meta_module.clean': isNotNull, + 'build_modules|lib/.ddc.meta_module.raw': isNotNull, + }; + final logs = []; + await testBuilders( + [ + const ModuleLibraryBuilder(), + MetaModuleBuilder(ddcPlatform), + MetaModuleCleanBuilder(ddcPlatform), + ModuleBuilder(ddcPlatform), + ddcKernelBuilder(builderOptions), + DevCompilerBuilder(platform: ddcPlatform), + ], + assets, + outputs: expectedOutputs, + onLog: logs.add, + ); + expect( + logs, + contains( + predicate( + (record) => + record.level == Level.SEVERE && + record.message.contains( + 'Unable to find modules for some sources', + ), + ), + ), + ); + }); + }); + }); + }); +} From efad8ae7a3f70bd89addbc88a9187c0608e652b5 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Fri, 5 Dec 2025 18:11:09 -0800 Subject: [PATCH 03/10] bumping vers --- build_modules/CHANGELOG.md | 4 ++++ build_modules/pubspec.yaml | 2 +- build_web_compilers/CHANGELOG.md | 4 ++++ build_web_compilers/pubspec.yaml | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build_modules/CHANGELOG.md b/build_modules/CHANGELOG.md index cde4e243ec..4c600108e7 100644 --- a/build_modules/CHANGELOG.md +++ b/build_modules/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.5 + +- Adding support for DDC's Library Bundle module system. + ## 5.1.4 - Fix module_builder reading DDC modules for non-primary dart libraries. diff --git a/build_modules/pubspec.yaml b/build_modules/pubspec.yaml index b606c2af8d..81e3b59bee 100644 --- a/build_modules/pubspec.yaml +++ b/build_modules/pubspec.yaml @@ -1,5 +1,5 @@ name: build_modules -version: 5.1.4 +version: 5.1.5 description: >- Builders to analyze and split Dart code into individually compilable modules based on imports. diff --git a/build_web_compilers/CHANGELOG.md b/build_web_compilers/CHANGELOG.md index dc0def7012..8d7d70f0d2 100644 --- a/build_web_compilers/CHANGELOG.md +++ b/build_web_compilers/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.6 + +- Adding support for DDC's Library Bundle module system. + ## 4.4.5 - Updating DDC's bootstrapper to be consistent with the rest of the ecosystem. diff --git a/build_web_compilers/pubspec.yaml b/build_web_compilers/pubspec.yaml index cc952ef333..85918a4da2 100644 --- a/build_web_compilers/pubspec.yaml +++ b/build_web_compilers/pubspec.yaml @@ -1,5 +1,5 @@ name: build_web_compilers -version: 4.4.5 +version: 4.4.6 description: Builder implementations wrapping the dart2js and DDC compilers. repository: https://github.com/dart-lang/build/tree/master/build_web_compilers resolution: workspace From eb473267fdee673603ac33b6ab7aa348d4659621 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Mon, 8 Dec 2025 10:18:22 -0800 Subject: [PATCH 04/10] piping more settings through --- build_web_compilers/lib/builders.dart | 5 +- .../test/ddc_library_bundle_builder_test.dart | 75 ++++++++++++------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/build_web_compilers/lib/builders.dart b/build_web_compilers/lib/builders.dart index 456ec17f4c..034afb9a0a 100644 --- a/build_web_compilers/lib/builders.dart +++ b/build_web_compilers/lib/builders.dart @@ -58,7 +58,8 @@ Builder ddcBuilder(BuilderOptions options) { useIncrementalCompiler: _readUseIncrementalCompilerOption(options), generateFullDill: _readGenerateFullDillOption(options), emitDebugSymbols: _readEmitDebugSymbolsOption(options), - canaryFeatures: _readCanaryOption(options), + canaryFeatures: + _readCanaryOption(options) || _readWebHotReloadOption(options), ddcLibraryBundle: _readDdcLibraryBundleOption(options) || _readWebHotReloadOption(options), @@ -93,7 +94,7 @@ Builder sdkJsCompile(BuilderOptions options) { sdkKernelPath: 'lib/_internal/ddc_platform.dill', outputPath: 'lib/src/dev_compiler/dart_sdk.js', canaryFeatures: - _readWebHotReloadOption(options) || _readCanaryOption(options), + _readCanaryOption(options) || _readWebHotReloadOption(options), ddcLibraryBundle: _readDdcLibraryBundleOption(options) || _readWebHotReloadOption(options), diff --git a/build_web_compilers/test/ddc_library_bundle_builder_test.dart b/build_web_compilers/test/ddc_library_bundle_builder_test.dart index 9199376ce0..9b850dd5cc 100644 --- a/build_web_compilers/test/ddc_library_bundle_builder_test.dart +++ b/build_web_compilers/test/ddc_library_bundle_builder_test.dart @@ -82,6 +82,7 @@ void main() { platform: ddcPlatform, useIncrementalCompiler: trackUnusedInputs, trackUnusedInputs: trackUnusedInputs, + ddcLibraryBundle: true, ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ @@ -129,6 +130,7 @@ void main() { final builder = DevCompilerBuilder( platform: ddcPlatform, environment: {'foo': 'zap'}, + ddcLibraryBundle: true, ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$jsModuleExtension': isNotNull, @@ -154,6 +156,7 @@ void main() { final builder = DevCompilerBuilder( platform: ddcPlatform, canaryFeatures: true, + ddcLibraryBundle: true, ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$jsModuleExtension': decodedMatches(contains('canary')), @@ -173,32 +176,42 @@ void main() { ); }); - test('does not enable DDC canary features by default', () async { - final builder = DevCompilerBuilder(platform: ddcPlatform); - final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ - 'a|lib/a$jsModuleExtension': decodedMatches( - isNot(contains('canary')), - ), - 'a|lib/a$jsSourceMapExtension': isNotNull, - 'a|lib/a$metadataExtension': isNotNull, - 'a|web/index$jsModuleExtension': isNotNull, - 'a|web/index$jsSourceMapExtension': isNotNull, - 'a|web/index$metadataExtension': isNotNull, - 'b|lib/b$jsModuleExtension': isNotNull, - 'b|lib/b$jsSourceMapExtension': isNotNull, - 'b|lib/b$metadataExtension': isNotNull, - }); - await testBuilders( - [...startingBuilders, builder], - startingAssets, - outputs: expectedOutputs, - ); - }); + test( + 'does not enable DDC canary features by default', + () async { + final builder = DevCompilerBuilder( + platform: ddcPlatform, + ddcLibraryBundle: true, + ); + final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ + 'a|lib/a$jsModuleExtension': decodedMatches( + isNot(contains('canary')), + ), + 'a|lib/a$jsSourceMapExtension': isNotNull, + 'a|lib/a$metadataExtension': isNotNull, + 'a|web/index$jsModuleExtension': isNotNull, + 'a|web/index$jsSourceMapExtension': isNotNull, + 'a|web/index$metadataExtension': isNotNull, + 'b|lib/b$jsModuleExtension': isNotNull, + 'b|lib/b$jsSourceMapExtension': isNotNull, + 'b|lib/b$metadataExtension': isNotNull, + }); + await testBuilders( + [...startingBuilders, builder], + startingAssets, + outputs: expectedOutputs, + ); + }, + skip: + 'Enable this test when the library bundle module system is no ' + 'longer locked behind the --canary flag', + ); test('generates full dill when enabled', () async { final builder = DevCompilerBuilder( platform: ddcPlatform, generateFullDill: true, + ddcLibraryBundle: true, ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$fullKernelExtension': isNotNull, @@ -222,7 +235,10 @@ void main() { }); test('does not generate full dill by default', () async { - final builder = DevCompilerBuilder(platform: ddcPlatform); + final builder = DevCompilerBuilder( + platform: ddcPlatform, + ddcLibraryBundle: true, + ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$jsModuleExtension': isNotNull, 'a|lib/a$jsSourceMapExtension': isNotNull, @@ -245,6 +261,7 @@ void main() { final builder = DevCompilerBuilder( platform: ddcPlatform, emitDebugSymbols: true, + ddcLibraryBundle: true, ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$jsModuleExtension': isNotNull, @@ -268,7 +285,10 @@ void main() { }); test('does not emit debug symbols by default', () async { - final builder = DevCompilerBuilder(platform: ddcPlatform); + final builder = DevCompilerBuilder( + platform: ddcPlatform, + ddcLibraryBundle: true, + ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'b|lib/b$jsModuleExtension': isNotNull, 'b|lib/b$jsSourceMapExtension': isNotNull, @@ -288,7 +308,10 @@ void main() { }); test('strips scratch paths from metadata', () async { - final builder = DevCompilerBuilder(platform: ddcPlatform); + final builder = DevCompilerBuilder( + platform: ddcPlatform, + ddcLibraryBundle: true, + ); final expectedOutputs = Map.of(startingExpectedOutputs)..addAll({ 'a|lib/a$jsModuleExtension': isNotNull, 'a|lib/a$jsSourceMapExtension': isNotNull, @@ -341,7 +364,7 @@ void main() { MetaModuleCleanBuilder(ddcPlatform), ModuleBuilder(ddcPlatform), ddcKernelBuilder(builderOptions), - DevCompilerBuilder(platform: ddcPlatform), + DevCompilerBuilder(platform: ddcPlatform, ddcLibraryBundle: true), ], assets, outputs: expectedOutputs, @@ -387,7 +410,7 @@ void main() { MetaModuleCleanBuilder(ddcPlatform), ModuleBuilder(ddcPlatform), ddcKernelBuilder(builderOptions), - DevCompilerBuilder(platform: ddcPlatform), + DevCompilerBuilder(platform: ddcPlatform, ddcLibraryBundle: true), ], assets, outputs: expectedOutputs, From cfca7e6c7b66dba760dd06c9c0acd84dabb98859 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 15:57:09 -0800 Subject: [PATCH 05/10] using named params --- .../lib/src/dev_compiler_builder.dart | 36 +++++++++---------- .../lib/src/sdk_js_compile_builder.dart | 24 ++++++------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/build_web_compilers/lib/src/dev_compiler_builder.dart b/build_web_compilers/lib/src/dev_compiler_builder.dart index f78deb8ca9..3ca325b735 100644 --- a/build_web_compilers/lib/src/dev_compiler_builder.dart +++ b/build_web_compilers/lib/src/dev_compiler_builder.dart @@ -125,16 +125,16 @@ class DevCompilerBuilder implements Builder { await _createDevCompilerModule( module, buildStep, - useIncrementalCompiler, - generateFullDill, - emitDebugSymbols, - canaryFeatures, - ddcLibraryBundle, - trackUnusedInputs, - platformSdk, - sdkKernelPath, - librariesPath, environment, + useIncrementalCompiler: useIncrementalCompiler, + generateFullDill: generateFullDill, + emitDebugSymbols: emitDebugSymbols, + canaryFeatures: canaryFeatures, + ddcLibraryBundle: ddcLibraryBundle, + trackUnusedInputs: trackUnusedInputs, + dartSdk: platformSdk, + sdkKernelPath: sdkKernelPath, + librariesPath: librariesPath, ); } on DartDevcCompilationException catch (e) { await handleError(e); @@ -148,16 +148,16 @@ class DevCompilerBuilder implements Builder { Future _createDevCompilerModule( Module module, BuildStep buildStep, - bool useIncrementalCompiler, - bool generateFullDill, - bool emitDebugSymbols, - bool canaryFeatures, - bool ddcLibraryBundle, - bool trackUnusedInputs, - String dartSdk, - String sdkKernelPath, - String librariesPath, Map environment, { + required bool useIncrementalCompiler, + required bool generateFullDill, + required bool emitDebugSymbols, + required bool canaryFeatures, + required bool ddcLibraryBundle, + required bool trackUnusedInputs, + required String dartSdk, + required String sdkKernelPath, + required String librariesPath, bool debugMode = true, }) async { final transitiveDeps = await buildStep.trackStage( diff --git a/build_web_compilers/lib/src/sdk_js_compile_builder.dart b/build_web_compilers/lib/src/sdk_js_compile_builder.dart index 9f5888d3c1..e5b71f64eb 100644 --- a/build_web_compilers/lib/src/sdk_js_compile_builder.dart +++ b/build_web_compilers/lib/src/sdk_js_compile_builder.dart @@ -81,12 +81,12 @@ class SdkJsCompileBuilder implements Builder { } else { await _createDevCompilerModule( buildStep, - platformSdk, - sdkKernelPath, - librariesPath, jsOutputId, - canaryFeatures, - ddcLibraryBundle, + dartSdk: platformSdk, + sdkKernelPath: sdkKernelPath, + librariesPath: librariesPath, + canaryFeatures: canaryFeatures, + ddcLibraryBundle: ddcLibraryBundle, ); } } @@ -95,13 +95,13 @@ class SdkJsCompileBuilder implements Builder { /// Compile the sdk module with the dev compiler. Future _createDevCompilerModule( BuildStep buildStep, - String dartSdk, - String sdkKernelPath, - String librariesPath, - AssetId jsOutputId, - bool canaryFeatures, - bool ddcLibraryBundle, -) async { + AssetId jsOutputId, { + required String dartSdk, + required String sdkKernelPath, + required String librariesPath, + required bool canaryFeatures, + required bool ddcLibraryBundle, +}) async { final scratchSpace = await buildStep.fetchResource(scratchSpaceResource); final jsOutputFile = scratchSpace.fileFor(jsOutputId); From 8b48e68977e97ad0f1ce44f835f54a4238c3620d Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 15:57:56 -0800 Subject: [PATCH 06/10] adding newline --- build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart index 86d6e6bfb6..0d188231af 100644 --- a/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart +++ b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart @@ -132,6 +132,7 @@ void main() { ); }); }); + group('regression tests', () { test('root dart file is not the primary source, #2269', () async { final builder = WebEntrypointBuilder.fromOptions(defaultBuilderOptions); From 42527d57b67876b8b374b94301d4c46a3e5e4c30 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 15:58:34 -0800 Subject: [PATCH 07/10] rephrasing --- build_modules/CHANGELOG.md | 2 +- build_web_compilers/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build_modules/CHANGELOG.md b/build_modules/CHANGELOG.md index 4c600108e7..3007782150 100644 --- a/build_modules/CHANGELOG.md +++ b/build_modules/CHANGELOG.md @@ -1,6 +1,6 @@ ## 5.1.5 -- Adding support for DDC's Library Bundle module system. +- Add support for DDC's Library Bundle module system. ## 5.1.4 diff --git a/build_web_compilers/CHANGELOG.md b/build_web_compilers/CHANGELOG.md index 7110b249c8..5498e774e5 100644 --- a/build_web_compilers/CHANGELOG.md +++ b/build_web_compilers/CHANGELOG.md @@ -1,5 +1,5 @@ ## 4.4.7 -- Adding support for DDC's Library Bundle module system. +- Add support for DDC's Library Bundle module system. ## 4.4.6 - Add build options to customize the SDK used for compiling to js and wasm. From 59bd51ba39c996541c09775f800f3263c02459d6 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 16:09:54 -0800 Subject: [PATCH 08/10] rephrasing again --- build_modules/CHANGELOG.md | 2 +- build_web_compilers/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build_modules/CHANGELOG.md b/build_modules/CHANGELOG.md index 3007782150..c3f796cf77 100644 --- a/build_modules/CHANGELOG.md +++ b/build_modules/CHANGELOG.md @@ -1,6 +1,6 @@ ## 5.1.5 -- Add support for DDC's Library Bundle module system. +- Add support for DDC's Library Bundle module system, which is compatible with web hot reload. This is not yet enabled by default. ## 5.1.4 diff --git a/build_web_compilers/CHANGELOG.md b/build_web_compilers/CHANGELOG.md index 5498e774e5..30aa5f2e15 100644 --- a/build_web_compilers/CHANGELOG.md +++ b/build_web_compilers/CHANGELOG.md @@ -1,5 +1,5 @@ ## 4.4.7 -- Add support for DDC's Library Bundle module system. +- Add support for DDC's Library Bundle module system, which is compatible with web hot reload. This is not yet enabled by default. ## 4.4.6 - Add build options to customize the SDK used for compiling to js and wasm. From 2613661cc050be1a4ee20fd0e303f1a128d93fd7 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 16:29:35 -0800 Subject: [PATCH 09/10] adding init --- build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart index 0d188231af..c0274305a3 100644 --- a/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart +++ b/build_web_compilers/test/ddc_library_bundle_bootstrap_test.dart @@ -16,6 +16,8 @@ final defaultBuilderOptions = const BuilderOptions({ }); void main() { + initializePlatforms(); + final startingBuilders = { // Uses the real sdk copy builder to copy required files from the SDK. sdkJsCopyRequirejs(const BuilderOptions({})), From 64ff28756d3624a6391d31db79785bc31ad57901 Mon Sep 17 00:00:00 2001 From: MarkZ Date: Tue, 9 Dec 2025 16:39:32 -0800 Subject: [PATCH 10/10] adding other init --- build_web_compilers/test/ddc_library_bundle_builder_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_web_compilers/test/ddc_library_bundle_builder_test.dart b/build_web_compilers/test/ddc_library_bundle_builder_test.dart index 9b850dd5cc..b6b16fde4f 100644 --- a/build_web_compilers/test/ddc_library_bundle_builder_test.dart +++ b/build_web_compilers/test/ddc_library_bundle_builder_test.dart @@ -16,6 +16,8 @@ final builderOptions = const BuilderOptions({ }); void main() { + initializePlatforms(); + group('DDC Library Bundle:', () { group('error free project', () { final startingAssets = {