Adding support for DDC modules when running Flutter Web in debug mode (#141423)

### Context:

DDC modules are abstractions over how libraries are loaded/updated. The entirety of google3 uses the DDC/legacy module system due to its flexibility extensibility over the other two (ES6 and AMD/RequireJS). Unifying DDC's module system saves us from duplicating work and will allow us to have finer grained control over how JS modules are loaded. This is a a prerequisite to features such as hot reload.

### Overview:

This change plumbs a boolean flag through flutter_tools that switches between DDC (new) and AMD (current) modules. This mode is automatically applied when `--extra-front-end-options=--dartdevc-module-format=ddc` is specified alongside `flutter run`. Other important additions include:
* Splitting Flutter artifacts between DDC and AMD modules
* Adding unit tests for the DDC module system
* Additional bootstrapper logic for the DDC module system

We don't expect to see any user-visible behavior or performance differences.

This is dependent on [incoming module system support in DWDS](https://github.com/dart-lang/webdev/pull/2295) and [additional artifacts in the engine](https://github.com/flutter/engine/pull/47783).

This is part of a greater effort to deprecate the AMD module system: https://github.com/dart-lang/sdk/issues/52361
This commit is contained in:
MarkZ 2024-02-23 16:26:04 -08:00 committed by GitHub
parent b781da9b58
commit ee94fe262b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 2458 additions and 135 deletions

View file

@ -94,19 +94,34 @@ enum HostArtifact {
/// The summary dill with null safety enabled for the dartdevc target. /// The summary dill with null safety enabled for the dartdevc target.
webPlatformDart2JSSoundKernelDill, webPlatformDart2JSSoundKernelDill,
/// The precompiled SDKs and sourcemaps for web debug builds. /// The precompiled SDKs and sourcemaps for web debug builds with the AMD module system.
webPrecompiledSdk, // TODO(markzipan): delete these when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060.
webPrecompiledSdkSourcemaps, webPrecompiledAmdSdk,
webPrecompiledCanvaskitSdk, webPrecompiledAmdSdkSourcemaps,
webPrecompiledCanvaskitSdkSourcemaps, webPrecompiledAmdCanvaskitSdk,
webPrecompiledCanvaskitAndHtmlSdk, webPrecompiledAmdCanvaskitSdkSourcemaps,
webPrecompiledCanvaskitAndHtmlSdkSourcemaps, webPrecompiledAmdCanvaskitAndHtmlSdk,
webPrecompiledSoundSdk, webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps,
webPrecompiledSoundSdkSourcemaps, webPrecompiledAmdSoundSdk,
webPrecompiledCanvaskitSoundSdk, webPrecompiledAmdSoundSdkSourcemaps,
webPrecompiledCanvaskitSoundSdkSourcemaps, webPrecompiledAmdCanvaskitSoundSdk,
webPrecompiledCanvaskitAndHtmlSoundSdk, webPrecompiledAmdCanvaskitSoundSdkSourcemaps,
webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps, webPrecompiledAmdCanvaskitAndHtmlSoundSdk,
webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps,
/// The precompiled SDKs and sourcemaps for web debug builds with the DDC module system.
webPrecompiledDdcSdk,
webPrecompiledDdcSdkSourcemaps,
webPrecompiledDdcCanvaskitSdk,
webPrecompiledDdcCanvaskitSdkSourcemaps,
webPrecompiledDdcCanvaskitAndHtmlSdk,
webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps,
webPrecompiledDdcSoundSdk,
webPrecompiledDdcSoundSdkSourcemaps,
webPrecompiledDdcCanvaskitSoundSdk,
webPrecompiledDdcCanvaskitSoundSdkSourcemaps,
webPrecompiledDdcCanvaskitAndHtmlSoundSdk,
webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps,
iosDeploy, iosDeploy,
idevicesyslog, idevicesyslog,
@ -256,19 +271,31 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) {
return 'dart2js_platform.dill'; return 'dart2js_platform.dill';
case HostArtifact.flutterWebLibrariesJson: case HostArtifact.flutterWebLibrariesJson:
return 'libraries.json'; return 'libraries.json';
case HostArtifact.webPrecompiledSdk: case HostArtifact.webPrecompiledAmdSdk:
case HostArtifact.webPrecompiledCanvaskitSdk: case HostArtifact.webPrecompiledAmdCanvaskitSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledAmdSoundSdk:
case HostArtifact.webPrecompiledCanvaskitSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledDdcSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledDdcSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk:
return 'dart_sdk.js'; return 'dart_sdk.js';
case HostArtifact.webPrecompiledSdkSourcemaps: case HostArtifact.webPrecompiledAmdSdkSourcemaps:
case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps:
case HostArtifact.webPrecompiledSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdSoundSdkSourcemaps:
case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps:
case HostArtifact.webPrecompiledDdcSdkSourcemaps:
case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps:
case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps:
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps:
return 'dart_sdk.js.map'; return 'dart_sdk.js.map';
case HostArtifact.impellerc: case HostArtifact.impellerc:
return 'impellerc$exe'; return 'impellerc$exe';
@ -454,30 +481,54 @@ class CachedArtifacts implements Artifacts {
case HostArtifact.webPlatformDart2JSSoundKernelDill: case HostArtifact.webPlatformDart2JSSoundKernelDill:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSdk: case HostArtifact.webPrecompiledAmdSdk:
case HostArtifact.webPrecompiledSdkSourcemaps: case HostArtifact.webPrecompiledAmdSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSdk: case HostArtifact.webPrecompiledAmdCanvaskitSdk:
case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledAmdSoundSdk:
case HostArtifact.webPrecompiledSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdk:
case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSdk:
case HostArtifact.webPrecompiledDdcSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSoundSdk:
case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.idevicesyslog: case HostArtifact.idevicesyslog:
case HostArtifact.idevicescreenshot: case HostArtifact.idevicescreenshot:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
@ -886,7 +937,9 @@ class CachedLocalEngineArtifacts implements Artifacts {
final Artifacts _backupCache; final Artifacts _backupCache;
@override @override
FileSystemEntity getHostArtifact(HostArtifact artifact) { FileSystemEntity getHostArtifact(
HostArtifact artifact,
) {
switch (artifact) { switch (artifact) {
case HostArtifact.flutterWebSdk: case HostArtifact.flutterWebSdk:
final String path = _getFlutterWebSdkPath(); final String path = _getFlutterWebSdkPath();
@ -906,30 +959,54 @@ class CachedLocalEngineArtifacts implements Artifacts {
case HostArtifact.webPlatformDart2JSSoundKernelDill: case HostArtifact.webPlatformDart2JSSoundKernelDill:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSdk: case HostArtifact.webPrecompiledAmdSdk:
case HostArtifact.webPrecompiledSdkSourcemaps: case HostArtifact.webPrecompiledAmdSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSdk: case HostArtifact.webPrecompiledAmdCanvaskitSdk:
case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledAmdSoundSdk:
case HostArtifact.webPrecompiledSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdk:
case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSdk:
case HostArtifact.webPrecompiledDdcSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSoundSdk:
case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.idevicesyslog: case HostArtifact.idevicesyslog:
case HostArtifact.idevicescreenshot: case HostArtifact.idevicescreenshot:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
@ -1196,7 +1273,9 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
String getEngineType(TargetPlatform platform, [BuildMode? mode]) => _parent.getEngineType(platform, mode); String getEngineType(TargetPlatform platform, [BuildMode? mode]) => _parent.getEngineType(platform, mode);
@override @override
FileSystemEntity getHostArtifact(HostArtifact artifact) { FileSystemEntity getHostArtifact(
HostArtifact artifact,
) {
switch (artifact) { switch (artifact) {
case HostArtifact.flutterWebSdk: case HostArtifact.flutterWebSdk:
final String path = _getFlutterWebSdkPath(); final String path = _getFlutterWebSdkPath();
@ -1216,30 +1295,54 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case HostArtifact.webPlatformDart2JSSoundKernelDill: case HostArtifact.webPlatformDart2JSSoundKernelDill:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSdk: case HostArtifact.webPrecompiledAmdSdk:
case HostArtifact.webPrecompiledSdkSourcemaps: case HostArtifact.webPrecompiledAmdSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSdk: case HostArtifact.webPrecompiledAmdCanvaskitSdk:
case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledSoundSdk: case HostArtifact.webPrecompiledAmdSoundSdk:
case HostArtifact.webPrecompiledSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdk:
case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform)); final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path); return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSdk:
case HostArtifact.webPrecompiledDdcSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcSoundSdk:
case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk:
case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps:
final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'ddc-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
return _fileSystem.file(path);
case HostArtifact.iosDeploy: case HostArtifact.iosDeploy:
case HostArtifact.idevicesyslog: case HostArtifact.idevicesyslog:
case HostArtifact.idevicescreenshot: case HostArtifact.idevicescreenshot:

View file

@ -222,6 +222,10 @@ class BuildInfo {
/// so the uncapitalized flavor name is used to compute the output file name /// so the uncapitalized flavor name is used to compute the output file name
String? get uncapitalizedFlavor => _uncapitalize(flavor); String? get uncapitalizedFlavor => _uncapitalize(flavor);
/// The module system DDC is targeting, or null if not using DDC.
// TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060.
DdcModuleFormat? get ddcModuleFormat => _ddcModuleFormatFromFrontEndArgs(extraFrontEndOptions);
/// Convert to a structured string encoded structure appropriate for usage /// Convert to a structured string encoded structure appropriate for usage
/// in build system [Environment.defines]. /// in build system [Environment.defines].
/// ///
@ -1044,6 +1048,28 @@ enum NullSafetyMode {
autodetect, autodetect,
} }
/// Indicates the module system DDC is targeting.
enum DdcModuleFormat {
amd,
ddc,
}
// TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060.
DdcModuleFormat? _ddcModuleFormatFromFrontEndArgs(List<String>? extraFrontEndArgs) {
if (extraFrontEndArgs == null) {
return null;
}
const String ddcModuleFormatString = '--dartdevc-module-format=';
for (final String flag in extraFrontEndArgs) {
if (flag.startsWith(ddcModuleFormatString)) {
final String moduleFormatString = flag
.substring(ddcModuleFormatString.length, flag.length);
return DdcModuleFormat.values.byName(moduleFormatString);
}
}
return null;
}
String _getCurrentHostPlatformArchName() { String _getCurrentHostPlatformArchName() {
final HostPlatform hostPlatform = getCurrentHostPlatform(); final HostPlatform hostPlatform = getCurrentHostPlatform();
return hostPlatform.platformName; return hostPlatform.platformName;

View file

@ -117,7 +117,8 @@ class WebAssetServer implements AssetReader {
this.internetAddress, this.internetAddress,
this._modules, this._modules,
this._digests, this._digests,
this._nullSafetyMode, { this._nullSafetyMode,
this._ddcModuleSystem, {
required this.webRenderer, required this.webRenderer,
}) : basePath = _getIndexHtml().getBaseHref(); }) : basePath = _getIndexHtml().getBaseHref();
@ -181,6 +182,8 @@ class WebAssetServer implements AssetReader {
required WebRendererMode webRenderer, required WebRendererMode webRenderer,
bool testMode = false, bool testMode = false,
DwdsLauncher dwdsLauncher = Dwds.start, DwdsLauncher dwdsLauncher = Dwds.start,
// TODO(markzipan): Make sure this default value aligns with that in the debugger options.
bool ddcModuleSystem = false,
}) async { }) async {
InternetAddress address; InternetAddress address;
if (hostname == 'any') { if (hostname == 'any') {
@ -227,6 +230,7 @@ class WebAssetServer implements AssetReader {
modules, modules,
digests, digests,
nullSafetyMode, nullSafetyMode,
ddcModuleSystem,
webRenderer: webRenderer, webRenderer: webRenderer,
); );
if (testMode) { if (testMode) {
@ -285,7 +289,8 @@ class WebAssetServer implements AssetReader {
return chromium.chromeConnection; return chromium.chromeConnection;
}, },
toolConfiguration: ToolConfiguration( toolConfiguration: ToolConfiguration(
loadStrategy: FrontendServerRequireStrategyProvider( loadStrategy: ddcModuleSystem
? FrontendServerLegacyStrategyProvider(
ReloadConfiguration.none, ReloadConfiguration.none,
server, server,
PackageUriMapper(packageConfig), PackageUriMapper(packageConfig),
@ -293,8 +298,17 @@ class WebAssetServer implements AssetReader {
BuildSettings( BuildSettings(
appEntrypoint: packageConfig.toPackageUri( appEntrypoint: packageConfig.toPackageUri(
globals.fs.file(entrypoint).absolute.uri, globals.fs.file(entrypoint).absolute.uri,
), )),
), ).strategy
: FrontendServerRequireStrategyProvider(
ReloadConfiguration.none,
server,
PackageUriMapper(packageConfig),
digestProvider,
BuildSettings(
appEntrypoint: packageConfig.toPackageUri(
globals.fs.file(entrypoint).absolute.uri,
)),
).strategy, ).strategy,
debugSettings: DebugSettings( debugSettings: DebugSettings(
enableDebugExtension: true, enableDebugExtension: true,
@ -328,6 +342,7 @@ class WebAssetServer implements AssetReader {
} }
final NullSafetyMode _nullSafetyMode; final NullSafetyMode _nullSafetyMode;
final bool _ddcModuleSystem;
final HttpServer _httpServer; final HttpServer _httpServer;
final WebMemoryFS _webMemoryFS = WebMemoryFS(); final WebMemoryFS _webMemoryFS = WebMemoryFS();
final PackageConfig _packages; final PackageConfig _packages;
@ -510,9 +525,7 @@ class WebAssetServer implements AssetReader {
final WebRendererMode webRenderer; final WebRendererMode webRenderer;
shelf.Response _serveIndex() { shelf.Response _serveIndex() {
final IndexHtml indexHtml = _getIndexHtml(); final IndexHtml indexHtml = _getIndexHtml();
final Map<String, dynamic> buildConfig = <String, dynamic>{ final Map<String, dynamic> buildConfig = <String, dynamic>{
'engineRevision': globals.flutterVersion.engineRevision, 'engineRevision': globals.flutterVersion.engineRevision,
'builds': <dynamic>[ 'builds': <dynamic>[
@ -597,15 +610,22 @@ class WebAssetServer implements AssetReader {
return webSdkFile; return webSdkFile;
} }
File get _resolveDartSdkJsFile => File get _resolveDartSdkJsFile {
globals.fs.file(globals.artifacts!.getHostArtifact( final Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>>
kDartSdkJsArtifactMap[webRenderer]![_nullSafetyMode]! dartSdkArtifactMap =
)); _ddcModuleSystem ? kDdcDartSdkJsArtifactMap : kAmdDartSdkJsArtifactMap;
return globals.fs.file(globals.artifacts!
.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!));
}
File get _resolveDartSdkJsMapFile => File get _resolveDartSdkJsMapFile {
globals.fs.file(globals.artifacts!.getHostArtifact( final Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>>
kDartSdkJsMapArtifactMap[webRenderer]![_nullSafetyMode]! dartSdkArtifactMap = _ddcModuleSystem
)); ? kDdcDartSdkJsMapArtifactMap
: kAmdDartSdkJsMapArtifactMap;
return globals.fs.file(globals.artifacts!
.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!));
}
@override @override
Future<String?> dartSourceContents(String serverPath) async { Future<String?> dartSourceContents(String serverPath) async {
@ -679,6 +699,7 @@ class WebDevFS implements DevFS {
required this.nullAssertions, required this.nullAssertions,
required this.nativeNullAssertions, required this.nativeNullAssertions,
required this.nullSafetyMode, required this.nullSafetyMode,
required this.ddcModuleSystem,
required this.webRenderer, required this.webRenderer,
this.testMode = false, this.testMode = false,
}) : _port = port; }) : _port = port;
@ -695,6 +716,7 @@ class WebDevFS implements DevFS {
final bool enableDds; final bool enableDds;
final Map<String, String> extraHeaders; final Map<String, String> extraHeaders;
final bool testMode; final bool testMode;
final bool ddcModuleSystem;
final ExpressionCompiler? expressionCompiler; final ExpressionCompiler? expressionCompiler;
final ChromiumLauncher? chromiumLauncher; final ChromiumLauncher? chromiumLauncher;
final bool nullAssertions; final bool nullAssertions;
@ -805,15 +827,16 @@ class WebDevFS implements DevFS {
nullSafetyMode, nullSafetyMode,
webRenderer: webRenderer, webRenderer: webRenderer,
testMode: testMode, testMode: testMode,
ddcModuleSystem: ddcModuleSystem,
); );
final int selectedPort = webAssetServer.selectedPort; final int selectedPort = webAssetServer.selectedPort;
String url = '$hostname:$selectedPort'; String url = '$hostname:$selectedPort';
if (hostname == 'any') { if (hostname == 'any') {
url ='localhost:$selectedPort'; url = 'localhost:$selectedPort';
} }
_baseUri = Uri.http(url, webAssetServer.basePath); _baseUri = Uri.http(url, webAssetServer.basePath);
if (tlsCertPath != null && tlsCertKeyPath!= null) { if (tlsCertPath != null && tlsCertKeyPath != null) {
_baseUri = Uri.https(url, webAssetServer.basePath); _baseUri = Uri.https(url, webAssetServer.basePath);
} }
return _baseUri!; return _baseUri!;
@ -866,7 +889,12 @@ class WebDevFS implements DevFS {
generator.addFileSystemRoot(outputDirectoryPath); generator.addFileSystemRoot(outputDirectoryPath);
final String entrypoint = globals.fs.path.basename(mainFile.path); final String entrypoint = globals.fs.path.basename(mainFile.path);
webAssetServer.writeBytes(entrypoint, mainFile.readAsBytesSync()); webAssetServer.writeBytes(entrypoint, mainFile.readAsBytesSync());
webAssetServer.writeBytes('require.js', requireJS.readAsBytesSync()); if (ddcModuleSystem) {
webAssetServer.writeBytes(
'ddc_module_loader.js', ddcModuleLoaderJS.readAsBytesSync());
} else {
webAssetServer.writeBytes('require.js', requireJS.readAsBytesSync());
}
webAssetServer.writeBytes('flutter.js', flutterJs.readAsBytesSync()); webAssetServer.writeBytes('flutter.js', flutterJs.readAsBytesSync());
webAssetServer.writeBytes( webAssetServer.writeBytes(
'stack_trace_mapper.js', stackTraceMapper.readAsBytesSync()); 'stack_trace_mapper.js', stackTraceMapper.readAsBytesSync());
@ -878,7 +906,14 @@ class WebDevFS implements DevFS {
'version.json', FlutterProject.current().getVersionInfo()); 'version.json', FlutterProject.current().getVersionInfo());
webAssetServer.writeFile( webAssetServer.writeFile(
'main.dart.js', 'main.dart.js',
generateBootstrapScript( ddcModuleSystem
? generateDDCBootstrapScript(
entrypoint: entrypoint,
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'stack_trace_mapper.js',
generateLoadingIndicator: enableDwds,
)
: generateBootstrapScript(
requireUrl: 'require.js', requireUrl: 'require.js',
mapperUrl: 'stack_trace_mapper.js', mapperUrl: 'stack_trace_mapper.js',
generateLoadingIndicator: enableDwds, generateLoadingIndicator: enableDwds,
@ -886,7 +921,14 @@ class WebDevFS implements DevFS {
); );
webAssetServer.writeFile( webAssetServer.writeFile(
'main_module.bootstrap.js', 'main_module.bootstrap.js',
generateMainModule( ddcModuleSystem
? generateDDCMainModule(
entrypoint: entrypoint,
nullAssertions: nullAssertions,
nativeNullAssertions: nativeNullAssertions,
exportedMain: pathToJSIdentifier(entrypoint.split('.')[0]),
)
: generateMainModule(
entrypoint: entrypoint, entrypoint: entrypoint,
nullAssertions: nullAssertions, nullAssertions: nullAssertions,
nativeNullAssertions: nativeNullAssertions, nativeNullAssertions: nativeNullAssertions,
@ -968,6 +1010,16 @@ class WebDevFS implements DevFS {
'require.js', 'require.js',
)); ));
@visibleForTesting
final File ddcModuleLoaderJS = globals.fs.file(globals.fs.path.join(
globals.artifacts!.getArtifactPath(Artifact.engineDartSdkPath,
platform: TargetPlatform.web_javascript),
'lib',
'dev_compiler',
'ddc',
'ddc_module_loader.js',
));
@visibleForTesting @visibleForTesting
final File flutterJs = globals.fs.file(globals.fs.path.join( final File flutterJs = globals.fs.file(globals.fs.path.join(
globals.artifacts!.getHostArtifact(HostArtifact.flutterJsDirectory).path, globals.artifacts!.getHostArtifact(HostArtifact.flutterJsDirectory).path,

View file

@ -166,11 +166,12 @@ class ResidentWebRunner extends ResidentRunner {
if (_instance != null) { if (_instance != null) {
return _instance!; return _instance!;
} }
final vmservice.VmService? service =_connectionResult?.vmService; final vmservice.VmService? service = _connectionResult?.vmService;
final Uri websocketUri = Uri.parse(_connectionResult!.debugConnection!.uri); final Uri websocketUri = Uri.parse(_connectionResult!.debugConnection!.uri);
final Uri httpUri = _httpUriFromWebsocketUri(websocketUri); final Uri httpUri = _httpUriFromWebsocketUri(websocketUri);
return _instance ??= FlutterVmService(service!, wsAddress: websocketUri, httpAddress: httpUri); return _instance ??= FlutterVmService(service!, wsAddress: websocketUri, httpAddress: httpUri);
} }
FlutterVmService? _instance; FlutterVmService? _instance;
@override @override
@ -289,6 +290,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
debuggingOptions.webEnableExpressionEvaluation debuggingOptions.webEnableExpressionEvaluation
? WebExpressionCompiler(device!.generator!, fileSystem: _fileSystem) ? WebExpressionCompiler(device!.generator!, fileSystem: _fileSystem)
: null; : null;
device!.devFS = WebDevFS( device!.devFS = WebDevFS(
hostname: debuggingOptions.hostname ?? 'localhost', hostname: debuggingOptions.hostname ?? 'localhost',
port: await getPort(), port: await getPort(),
@ -309,6 +311,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
nullAssertions: debuggingOptions.nullAssertions, nullAssertions: debuggingOptions.nullAssertions,
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode, nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
nativeNullAssertions: debuggingOptions.nativeNullAssertions, nativeNullAssertions: debuggingOptions.nativeNullAssertions,
ddcModuleSystem: debuggingOptions.buildInfo.ddcModuleFormat == DdcModuleFormat.ddc,
webRenderer: debuggingOptions.webRenderer, webRenderer: debuggingOptions.webRenderer,
); );
Uri url = await device!.devFS!.create(); Uri url = await device!.devFS!.create();
@ -605,7 +608,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
_connectionResult = await webDevFS.connect(useDebugExtension); _connectionResult = await webDevFS.connect(useDebugExtension);
unawaited(_connectionResult!.debugConnection!.onDone.whenComplete(_cleanupAndExit)); unawaited(_connectionResult!.debugConnection!.onDone.whenComplete(_cleanupAndExit));
void onLogEvent(vmservice.Event event) { void onLogEvent(vmservice.Event event) {
final String message = processVmServiceMessage(event); final String message = processVmServiceMessage(event);
_logger.printStatus(message); _logger.printStatus(message);
} }
@ -640,7 +643,6 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
vmService: _vmService.service, vmService: _vmService.service,
); );
websocketUri = Uri.parse(_connectionResult!.debugConnection!.uri); websocketUri = Uri.parse(_connectionResult!.debugConnection!.uri);
device!.vmService = _vmService; device!.vmService = _vmService;
@ -651,8 +653,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
_connectionResult!.appConnection!.runMain(); _connectionResult!.appConnection!.runMain();
} else { } else {
late StreamSubscription<void> resumeSub; late StreamSubscription<void> resumeSub;
resumeSub = _vmService.service.onDebugEvent resumeSub = _vmService.service.onDebugEvent.listen((vmservice.Event event) {
.listen((vmservice.Event event) {
if (event.type == vmservice.EventKind.kResume) { if (event.type == vmservice.EventKind.kResume) {
_connectionResult!.appConnection!.runMain(); _connectionResult!.appConnection!.runMain();
resumeSub.cancel(); resumeSub.cancel();
@ -674,7 +675,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
..writeAsStringSync(websocketUri.toString()); ..writeAsStringSync(websocketUri.toString());
} }
_logger.printStatus('Debug service listening on $websocketUri'); _logger.printStatus('Debug service listening on $websocketUri');
if (debuggingOptions.buildInfo.nullSafetyMode != NullSafetyMode.sound) { if (debuggingOptions.buildInfo.nullSafetyMode != NullSafetyMode.sound) {
_logger.printStatus(''); _logger.printStatus('');
_logger.printStatus( _logger.printStatus(
'Running without sound null safety ⚠️', 'Running without sound null safety ⚠️',

View file

@ -236,6 +236,15 @@ class FlutterWebPlatform extends PlatformPlugin {
'require.js', 'require.js',
)); ));
/// The ddc module loader js binary.
File get _ddcModuleLoaderJs => _fileSystem.file(_fileSystem.path.join(
_artifacts!.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript),
'lib',
'dev_compiler',
'ddc',
'ddc_module_loader.js',
));
/// The ddc to dart stack trace mapper. /// The ddc to dart stack trace mapper.
File get _stackTraceMapper => _fileSystem.file(_fileSystem.path.join( File get _stackTraceMapper => _fileSystem.file(_fileSystem.path.join(
_artifacts!.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript), _artifacts!.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript),
@ -245,11 +254,15 @@ class FlutterWebPlatform extends PlatformPlugin {
'dart_stack_trace_mapper.js', 'dart_stack_trace_mapper.js',
)); ));
File get _dartSdk => _fileSystem.file( File get _dartSdk {
_artifacts!.getHostArtifact(kDartSdkJsArtifactMap[webRenderer]![_nullSafetyMode]!)); final Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> dartSdkArtifactMap = buildInfo.ddcModuleFormat == DdcModuleFormat.ddc ? kDdcDartSdkJsArtifactMap : kAmdDartSdkJsArtifactMap;
return _fileSystem.file(_artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!));
}
File get _dartSdkSourcemaps => _fileSystem.file( File get _dartSdkSourcemaps {
_artifacts!.getHostArtifact(kDartSdkJsMapArtifactMap[webRenderer]![_nullSafetyMode]!)); final Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> dartSdkArtifactMap = buildInfo.ddcModuleFormat == DdcModuleFormat.ddc ? kDdcDartSdkJsMapArtifactMap : kAmdDartSdkJsMapArtifactMap;
return _fileSystem.file(_artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!));
}
File _canvasKitFile(String relativePath) { File _canvasKitFile(String relativePath) {
final String canvasKitPath = _fileSystem.path.join( final String canvasKitPath = _fileSystem.path.join(
@ -303,6 +316,11 @@ class FlutterWebPlatform extends PlatformPlugin {
_requireJs.openRead(), _requireJs.openRead(),
headers: <String, String>{'Content-Type': 'text/javascript'}, headers: <String, String>{'Content-Type': 'text/javascript'},
); );
} else if (request.requestedUri.path.contains('ddc_module_loader.js')) {
return shelf.Response.ok(
_ddcModuleLoaderJs.openRead(),
headers: <String, String>{'Content-Type': 'text/javascript'},
);
} else if (request.requestedUri.path.contains('ahem.ttf')) { } else if (request.requestedUri.path.contains('ahem.ttf')) {
return shelf.Response.ok(_ahem.openRead()); return shelf.Response.ok(_ahem.openRead());
} else if (request.requestedUri.path.contains('dart_sdk.js')) { } else if (request.requestedUri.path.contains('dart_sdk.js')) {

View file

@ -4,6 +4,136 @@
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
String generateDDCBootstrapScript({
required String entrypoint,
required String ddcModuleLoaderUrl,
required String mapperUrl,
required bool generateLoadingIndicator,
String appRootDirectory = '/',
}) {
return '''
${generateLoadingIndicator ? _generateLoadingIndicator() : ""}
// TODO(markzipan): This is safe if Flutter app roots are always equal to the
// host root '/'. Validate if this is true.
var _currentDirectory = "$appRootDirectory";
window.\$dartCreateScript = (function() {
// Find the nonce value. (Note, this is only computed once.)
var scripts = Array.from(document.getElementsByTagName("script"));
var nonce;
scripts.some(
script => (nonce = script.nonce || script.getAttribute("nonce")));
// If present, return a closure that automatically appends the nonce.
if (nonce) {
return function() {
var script = document.createElement("script");
script.nonce = nonce;
return script;
};
} else {
return function() {
return document.createElement("script");
};
}
})();
// Loads a module [relativeUrl] relative to [root].
//
// If not specified, [root] defaults to the directory serving the main app.
var forceLoadModule = function (relativeUrl, root) {
var actualRoot = root ?? _currentDirectory;
return new Promise(function(resolve, reject) {
var script = self.\$dartCreateScript();
let policy = {
createScriptURL: function(src) {return src;}
};
if (self.trustedTypes && self.trustedTypes.createPolicy) {
policy = self.trustedTypes.createPolicy('dartDdcModuleUrl', policy);
}
script.onload = resolve;
script.onerror = reject;
script.src = policy.createScriptURL(actualRoot + relativeUrl);
document.head.appendChild(script);
});
};
// A map containing the URLs for the bootstrap scripts in debug.
let _scriptUrls = {
"mapper": "$mapperUrl",
"moduleLoader": "$ddcModuleLoaderUrl"
};
(function() {
let appName = "$entrypoint";
// A uuid that identifies a subapp.
// Stubbed out since subapps aren't supported in Flutter.
let uuid = "00000000-0000-0000-0000-000000000000";
window.postMessage(
{type: "DDC_STATE_CHANGE", state: "initial_load", targetUuid: uuid}, "*");
// Load pre-requisite DDC scripts.
// We intentionally use invalid names to avoid namespace clashes.
let prerequisiteScripts = [
{
"src": "$ddcModuleLoaderUrl",
"id": "ddc_module_loader \x00"
},
{
"src": "$mapperUrl",
"id": "dart_stack_trace_mapper \x00"
}
];
// Load ddc_module_loader.js to access DDC's module loader API.
let prerequisiteLoads = [];
for (let i = 0; i < prerequisiteScripts.length; i++) {
prerequisiteLoads.push(forceLoadModule(prerequisiteScripts[i].src));
}
Promise.all(prerequisiteLoads).then((_) => afterPrerequisiteLogic());
// Save the current script so we can access it in a closure.
var _currentScript = document.currentScript;
var afterPrerequisiteLogic = function() {
window.\$dartLoader.rootDirectories.push(_currentDirectory);
let scripts = [
{
"src": "dart_sdk.js",
"id": "dart_sdk"
},
{
"src": "main_module.bootstrap.js",
"id": "data-main"
}
];
let loadConfig = new window.\$dartLoader.LoadConfiguration();
loadConfig.bootstrapScript = scripts[scripts.length - 1];
loadConfig.loadScriptFn = function(loader) {
loader.addScriptsToQueue(scripts, null);
loader.loadEnqueuedModules();
}
loadConfig.ddcEventForLoadStart = /* LOAD_ALL_MODULES_START */ 1;
loadConfig.ddcEventForLoadedOk = /* LOAD_ALL_MODULES_END_OK */ 2;
loadConfig.ddcEventForLoadedError = /* LOAD_ALL_MODULES_END_ERROR */ 3;
let loader = new window.\$dartLoader.DDCLoader(loadConfig);
// Record prerequisite scripts' fully resolved URLs.
prerequisiteScripts.forEach(script => loader.registerScript(script));
// Note: these variables should only be used in non-multi-app scenarios since
// they can be arbitrarily overridden based on multi-app load order.
window.\$dartLoader.loadConfig = loadConfig;
window.\$dartLoader.loader = loader;
loader.nextAttempt();
}
})();
''';
}
/// The JavaScript bootstrap script to support in-browser hot restart. /// The JavaScript bootstrap script to support in-browser hot restart.
/// ///
/// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl] /// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl]
@ -157,6 +287,39 @@ document.addEventListener('dart-app-ready', function (e) {
'''; ''';
} }
String generateDDCMainModule({
required String entrypoint,
required bool nullAssertions,
required bool nativeNullAssertions,
String? exportedMain,
}) {
final String entrypointMainName = exportedMain ?? entrypoint.split('.')[0];
// The typo below in "EXTENTION" is load-bearing, package:build depends on it.
return '''
/* ENTRYPOINT_EXTENTION_MARKER */
(function() {
// Flutter Web uses a generated main entrypoint, which shares app and module names.
let appName = "$entrypoint";
let moduleName = "$entrypoint";
// Use a dummy UUID since multi-apps are not supported on Flutter Web.
let uuid = "00000000-0000-0000-0000-000000000000";
let child = {};
child.main = function() {
let dart = self.dart_library.import('dart_sdk', appName).dart;
dart.nonNullAsserts($nullAssertions);
dart.nativeNonNullAsserts($nativeNullAssertions);
self.dart_library.start(appName, uuid, moduleName, "$entrypointMainName");
}
/* MAIN_EXTENSION_MARKER */
child.main();
})();
''';
}
/// Generate a synthetic main module which captures the application's main /// Generate a synthetic main module which captures the application's main
/// method. /// method.
/// ///

View file

@ -219,35 +219,69 @@ enum WebRendererMode implements CliEnum {
} }
} }
/// The correct precompiled artifact to use for each build and render mode. /// The correct precompiled artifact to use for each build and render mode for DDC with AMD modules.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kDartSdkJsArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{ // TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kAmdDartSdkJsArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{
WebRendererMode.auto: <NullSafetyMode, HostArtifact> { WebRendererMode.auto: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitAndHtmlSdk, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk,
}, },
WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> { WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitSoundSdk, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdCanvaskitSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitSdk, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdCanvaskitSdk,
}, },
WebRendererMode.html: <NullSafetyMode, HostArtifact> { WebRendererMode.html: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledSoundSdk, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledSdk, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdSdk,
}, },
}; };
/// The correct source map artifact to use for each build and render mode. /// The correct source map artifact to use for each build and render mode for DDC with AMD modules.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kDartSdkJsMapArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{ // TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kAmdDartSdkJsMapArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{
WebRendererMode.auto: <NullSafetyMode, HostArtifact> { WebRendererMode.auto: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdkSourcemaps,
}, },
WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> { WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledCanvaskitSdkSourcemaps, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps,
}, },
WebRendererMode.html: <NullSafetyMode, HostArtifact> { WebRendererMode.html: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledSoundSdkSourcemaps, NullSafetyMode.sound: HostArtifact.webPrecompiledAmdSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledSdkSourcemaps, NullSafetyMode.unsound: HostArtifact.webPrecompiledAmdSdkSourcemaps,
},
};
/// The correct precompiled artifact to use for each build and render mode for DDC with DDC modules.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kDdcDartSdkJsArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{
WebRendererMode.auto: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk,
},
WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitSdk,
},
WebRendererMode.html: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcSoundSdk,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcSdk,
},
};
/// The correct source map artifact to use for each build and render mode for DDC with DDC modules.
const Map<WebRendererMode, Map<NullSafetyMode, HostArtifact>> kDdcDartSdkJsMapArtifactMap = <WebRendererMode, Map<NullSafetyMode, HostArtifact>>{
WebRendererMode.auto: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps,
},
WebRendererMode.canvaskit: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps,
},
WebRendererMode.html: <NullSafetyMode, HostArtifact> {
NullSafetyMode.sound: HostArtifact.webPrecompiledDdcSoundSdkSourcemaps,
NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcSdkSourcemaps,
}, },
}; };

View file

@ -50,12 +50,18 @@ void main() {
operatingSystemUtils = FakeOperatingSystemUtils(); operatingSystemUtils = FakeOperatingSystemUtils();
for (final HostArtifact artifact in <HostArtifact>[ for (final HostArtifact artifact in <HostArtifact>[
HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk, HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk,
HostArtifact.webPrecompiledCanvaskitAndHtmlSdk, HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSdk,
HostArtifact.webPrecompiledCanvaskitSoundSdk, HostArtifact.webPrecompiledAmdCanvaskitSoundSdk,
HostArtifact.webPrecompiledCanvaskitSdk, HostArtifact.webPrecompiledAmdCanvaskitSdk,
HostArtifact.webPrecompiledSoundSdk, HostArtifact.webPrecompiledAmdSoundSdk,
HostArtifact.webPrecompiledSdk, HostArtifact.webPrecompiledAmdSdk,
HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk,
HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk,
HostArtifact.webPrecompiledDdcCanvaskitSoundSdk,
HostArtifact.webPrecompiledDdcCanvaskitSdk,
HostArtifact.webPrecompiledDdcSoundSdk,
HostArtifact.webPrecompiledDdcSdk,
]) { ]) {
final File artifactFile = artifacts.getHostArtifact(artifact) as File; final File artifactFile = artifacts.getHostArtifact(artifact) as File;
artifactFile.createSync(); artifactFile.createSync();
@ -63,7 +69,51 @@ void main() {
} }
}); });
testUsingContext('FlutterWebPlatform serves the correct dart_sdk.js for the passed web renderer', () async { testUsingContext(
'FlutterWebPlatform serves the correct dart_sdk.js (amd module system) for the passed web renderer',
() async {
final ChromiumLauncher chromiumLauncher = ChromiumLauncher(
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
operatingSystemUtils: operatingSystemUtils,
browserFinder: (Platform platform, FileSystem filesystem) => 'chrome',
logger: logger,
);
final MockServer server = MockServer();
fileSystem.directory('/test').createSync();
final FlutterWebPlatform webPlatform = await FlutterWebPlatform.start(
'ProjectRoot',
buildInfo: const BuildInfo(BuildMode.debug, '', treeShakeIcons: false),
webMemoryFS: WebMemoryFS(),
fileSystem: fileSystem,
logger: logger,
chromiumLauncher: chromiumLauncher,
artifacts: artifacts,
processManager: processManager,
webRenderer: WebRendererMode.canvaskit,
serverFactory: () async => server,
testPackageUri: Uri.parse('test'),
);
final shelf.Handler? handler = server.mountedHandler;
expect(handler, isNotNull);
handler!;
final shelf.Response response = await handler(shelf.Request(
'GET',
Uri.parse('http://localhost/dart_sdk.js'),
));
final String contents = await response.readAsString();
expect(contents, HostArtifact.webPrecompiledAmdCanvaskitSoundSdk.name);
await webPlatform.close();
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Logger: () => logger,
});
testUsingContext(
'FlutterWebPlatform serves the correct dart_sdk.js (ddc module system) for the passed web renderer',
() async {
final ChromiumLauncher chromiumLauncher = ChromiumLauncher( final ChromiumLauncher chromiumLauncher = ChromiumLauncher(
fileSystem: fileSystem, fileSystem: fileSystem,
platform: platform, platform: platform,
@ -79,7 +129,8 @@ void main() {
buildInfo: const BuildInfo( buildInfo: const BuildInfo(
BuildMode.debug, BuildMode.debug,
'', '',
treeShakeIcons: false treeShakeIcons: false,
extraFrontEndOptions: <String>['--dartdevc-module-format=ddc'],
), ),
webMemoryFS: WebMemoryFS(), webMemoryFS: WebMemoryFS(),
fileSystem: fileSystem, fileSystem: fileSystem,
@ -99,7 +150,7 @@ void main() {
Uri.parse('http://localhost/dart_sdk.js'), Uri.parse('http://localhost/dart_sdk.js'),
)); ));
final String contents = await response.readAsString(); final String contents = await response.readAsString();
expect(contents, HostArtifact.webPrecompiledCanvaskitSoundSdk.name); expect(contents, HostArtifact.webPrecompiledDdcCanvaskitSoundSdk.name);
await webPlatform.close(); await webPlatform.close();
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,

View file

@ -148,41 +148,76 @@ void main() {
); );
}); });
testWithoutContext('precompiled web artifact paths are correct', () { testWithoutContext('Precompiled web AMD module system artifact paths are correct', () {
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledSdk).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js', 'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledSdkSourcemaps).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js.map', 'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js.map',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdk).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js', 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdkSourcemaps).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js.map', 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js.map',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledSoundSdk).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdSoundSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js', 'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledSoundSdkSourcemaps).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdSoundSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js.map', 'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js.map',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdk).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSoundSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js', 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js',
); );
expect( expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps).path, artifacts.getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js.map', 'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js.map',
); );
}); });
testWithoutContext('Precompiled web DDC module system artifact paths are correct', () {
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc/dart_sdk.js',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc/dart_sdk.js.map',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit/dart_sdk.js',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit/dart_sdk.js.map',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-sound/dart_sdk.js',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-sound/dart_sdk.js.map',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdk).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit-sound/dart_sdk.js',
);
expect(
artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps).path,
'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit-sound/dart_sdk.js.map',
);
});
testWithoutContext('getEngineType', () { testWithoutContext('getEngineType', () {
expect( expect(
artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug), artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug),

View file

@ -170,4 +170,167 @@ void main() {
expect(result, contains("Uri.parse('file:///test/absolute_path.dart')")); expect(result, contains("Uri.parse('file:///test/absolute_path.dart')"));
}); });
group('Using the DDC module system', () {
test('generateDDCBootstrapScript embeds urls correctly', () {
final String result = generateDDCBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
// ddc module loader js source is interpolated correctly.
expect(result, contains('"moduleLoader": "ddc_module_loader.js"'));
expect(result, contains('"src": "ddc_module_loader.js"'));
// stack trace mapper source is interpolated correctly.
expect(result, contains('"mapper": "mapper.js"'));
expect(result, contains('"src": "mapper.js"'));
// data-main is set to correct bootstrap module.
expect(result, contains('"src": "main_module.bootstrap.js"'));
expect(result, contains('"id": "data-main"'));
});
test('generateDDCBootstrapScript initializes configuration objects', () {
final String result = generateDDCBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
// LoadConfiguration and DDCLoader objects must be constructed.
expect(result, contains(r'new window.$dartLoader.LoadConfiguration('));
expect(result, contains(r'new window.$dartLoader.DDCLoader('));
// Specific fields must be set on the LoadConfiguration.
expect(result, contains('.bootstrapScript ='));
expect(result, contains('.loadScriptFn ='));
// DDCLoader.nextAttempt must be invoked to begin loading.
expect(result, contains('nextAttempt()'));
// Proper window objects are initialized.
expect(result, contains(r'window.$dartLoader.loadConfig ='));
expect(result, contains(r'window.$dartLoader.loader ='));
});
test('generateDDCBootstrapScript includes loading indicator', () {
final String result = generateDDCBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
expect(result, contains('"flutter-loader"'));
expect(result, contains('"indeterminate"'));
});
test('generateDDCBootstrapScript does not include loading indicator', () {
final String result = generateDDCBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: false,
);
expect(result, isNot(contains('"flutter-loader"')));
expect(result, isNot(contains('"indeterminate"')));
});
// https://github.com/flutter/flutter/issues/107742
test('generateDDCBootstrapScript loading indicator does not trigger scrollbars', () {
final String result = generateDDCBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
// See: https://regexr.com/6q0ft
final RegExp regex = RegExp(r'(?:\.flutter-loader\s*\{)[^}]+(?:overflow\:\s*hidden;)[^}]+}');
expect(result, matches(regex), reason: '.flutter-loader must have overflow: hidden');
});
test('generateDDCMainModule embeds the entrypoint correctly', () {
final String result = generateDDCMainModule(
entrypoint: 'main.js',
nullAssertions: false,
nativeNullAssertions: false,
);
// bootstrap main module has correct defined module.
expect(result, contains('let appName = "main.js"'));
expect(result, contains('let moduleName = "main.js"'));
expect(result, contains('dart_library.start(appName, uuid, moduleName, "main");'));
});
test('generateDDCMainModule embeds its exported main correctly', () {
final String result = generateDDCMainModule(
entrypoint: 'foo/bar/main.js',
nullAssertions: false,
nativeNullAssertions: false,
exportedMain: 'foo__bar__main'
);
// bootstrap main module has correct defined module.
expect(result, contains('let appName = "foo/bar/main.js"'));
expect(result, contains('let moduleName = "foo/bar/main.js"'));
expect(result, contains('dart_library.start(appName, uuid, moduleName, "foo__bar__main");'));
});
test('generateDDCMainModule includes null safety switches', () {
final String result = generateDDCMainModule(
entrypoint: 'main.js',
nullAssertions: true,
nativeNullAssertions: true,
);
expect(result, contains('''dart.nonNullAsserts(true);'''));
expect(result, contains('''dart.nativeNonNullAsserts(true);'''));
});
test('generateDDCMainModule can disable null safety switches', () {
final String result = generateDDCMainModule(
entrypoint: 'main.js',
nullAssertions: false,
nativeNullAssertions: false,
);
expect(result, contains('''dart.nonNullAsserts(false);'''));
expect(result, contains('''dart.nativeNonNullAsserts(false);'''));
});
test('generateTestBootstrapFileContents embeds urls correctly', () {
final String result = generateTestBootstrapFileContents('foo.dart.js', 'require.js', 'mapper.js');
expect(result, contains('el.setAttribute("data-main", \'foo.dart.js\');'));
});
test('generateTestEntrypoint does not generate test config wrappers when testConfigPath is not passed', () {
final String result = generateTestEntrypoint(
relativeTestPath: 'relative_path.dart',
absolutePath: 'absolute_path.dart',
testConfigPath: null,
languageVersion: LanguageVersion(2, 8),
);
expect(result, isNot(contains('test_config.testExecutable')));
});
test('generateTestEntrypoint generates test config wrappers when testConfigPath is passed', () {
final String result = generateTestEntrypoint(
relativeTestPath: 'relative_path.dart',
absolutePath: 'absolute_path.dart',
testConfigPath: 'test_config_path.dart',
languageVersion: LanguageVersion(2, 8),
);
expect(result, contains('test_config.testExecutable'));
});
test('generateTestEntrypoint embeds urls correctly', () {
final String result = generateTestEntrypoint(
relativeTestPath: 'relative_path.dart',
absolutePath: '/test/absolute_path.dart',
testConfigPath: null,
languageVersion: LanguageVersion(2, 8),
);
expect(result, contains("Uri.parse('file:///test/absolute_path.dart')"));
});
});
} }

File diff suppressed because it is too large Load diff

View file

@ -46,6 +46,7 @@ void main() {
late Platform windows; late Platform windows;
late FakeHttpServer httpServer; late FakeHttpServer httpServer;
late BufferLogger logger; late BufferLogger logger;
const bool usesDdcModuleSystem = false;
setUpAll(() async { setUpAll(() async {
packages = PackageConfig(<Package>[ packages = PackageConfig(<Package>[
@ -66,6 +67,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.unsound, NullSafetyMode.unsound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
releaseAssetServer = ReleaseAssetServer( releaseAssetServer = ReleaseAssetServer(
@ -292,6 +294,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.unsound, NullSafetyMode.unsound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
@ -312,6 +315,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.unsound, NullSafetyMode.unsound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
@ -334,6 +338,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.unsound, NullSafetyMode.unsound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
), ),
throwsToolExit(), throwsToolExit(),
@ -355,6 +360,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.unsound, NullSafetyMode.unsound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
), ),
throwsToolExit(), throwsToolExit(),
@ -652,7 +658,7 @@ void main() {
expect(httpServer.closed, true); expect(httpServer.closed, true);
})); }));
test('Can start web server with specified assets', () => testbed.run(() async { test('Can start web server with specified AMD module system assets', () => testbed.run(() async {
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart')) final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
..createSync(recursive: true); ..createSync(recursive: true);
outputFile.parent.childFile('a.sources').writeAsStringSync(''); outputFile.parent.childFile('a.sources').writeAsStringSync('');
@ -689,6 +695,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound, nullSafetyMode: NullSafetyMode.unsound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.html, webRenderer: WebRendererMode.html,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -698,13 +705,13 @@ void main() {
final Uri uri = await webDevFS.create(); final Uri uri = await webDevFS.create();
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory; webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
final String webPrecompiledSdk = globals.artifacts! final String webPrecompiledSdk = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledSdk).path; .getHostArtifact(HostArtifact.webPrecompiledAmdSdk).path;
final String webPrecompiledSdkSourcemaps = globals.artifacts! final String webPrecompiledSdkSourcemaps = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledSdkSourcemaps).path; .getHostArtifact(HostArtifact.webPrecompiledAmdSdkSourcemaps).path;
final String webPrecompiledCanvaskitSdk = globals.artifacts! final String webPrecompiledCanvaskitSdk = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdk).path; .getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSdk).path;
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts! final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdkSourcemaps).path; .getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps).path;
globals.fs.currentDirectory globals.fs.currentDirectory
.childDirectory('lib') .childDirectory('lib')
.childFile('web_entrypoint.dart') .childFile('web_entrypoint.dart')
@ -799,6 +806,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound, nullSafetyMode: NullSafetyMode.sound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.html, webRenderer: WebRendererMode.html,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -813,13 +821,13 @@ void main() {
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('GENERATED'); ..writeAsStringSync('GENERATED');
final String webPrecompiledSdk = globals.artifacts! final String webPrecompiledSdk = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledSoundSdk).path; .getHostArtifact(HostArtifact.webPrecompiledAmdSoundSdk).path;
final String webPrecompiledSdkSourcemaps = globals.artifacts! final String webPrecompiledSdkSourcemaps = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledSoundSdkSourcemaps).path; .getHostArtifact(HostArtifact.webPrecompiledAmdSoundSdkSourcemaps).path;
final String webPrecompiledCanvaskitSdk = globals.artifacts! final String webPrecompiledCanvaskitSdk = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdk).path; .getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSoundSdk).path;
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts! final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts!
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps).path; .getHostArtifact(HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps).path;
globals.fs.file(webPrecompiledSdk) globals.fs.file(webPrecompiledSdk)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('HELLO'); ..writeAsStringSync('HELLO');
@ -908,6 +916,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound, nullSafetyMode: NullSafetyMode.sound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -970,6 +979,7 @@ void main() {
nullAssertions: true, nullAssertions: true,
nativeNullAssertions: true, nativeNullAssertions: true,
nullSafetyMode: NullSafetyMode.sound, nullSafetyMode: NullSafetyMode.sound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -1016,6 +1026,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound, nullSafetyMode: NullSafetyMode.sound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -1063,6 +1074,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound, nullSafetyMode: NullSafetyMode.sound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.auto, webRenderer: WebRendererMode.auto,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -1111,6 +1123,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound, nullSafetyMode: NullSafetyMode.unsound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);
@ -1219,6 +1232,7 @@ void main() {
<String, String>{}, <String, String>{},
<String, String>{}, <String, String>{},
NullSafetyMode.sound, NullSafetyMode.sound,
usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
@ -1262,6 +1276,7 @@ void main() {
extraHeaders: const <String, String>{}, extraHeaders: const <String, String>{},
chromiumLauncher: null, chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound, nullSafetyMode: NullSafetyMode.unsound,
ddcModuleSystem: usesDdcModuleSystem,
webRenderer: WebRendererMode.canvaskit, webRenderer: WebRendererMode.canvaskit,
); );
webDevFS.requireJS.createSync(recursive: true); webDevFS.requireJS.createSync(recursive: true);