[flutter_tools] support sound null-safety mode for the web (#60570)
In web debug mode, infer sound null safety by default. When sound null safety is enabled, provide a separate dill and precompiled Dart SDK. Release builds do not need this setting since we run dart2js from source. Fixes #59873
This commit is contained in:
parent
2188196125
commit
7ca324ac01
@ -39,9 +39,22 @@ enum Artifact {
|
||||
kernelWorkerSnapshot,
|
||||
/// The root of the web implementation of the dart SDK.
|
||||
flutterWebSdk,
|
||||
/// The libraries JSON file for web release builds.
|
||||
flutterWebLibrariesJson,
|
||||
/// The summary dill for the dartdevc target.
|
||||
webPlatformKernelDill,
|
||||
/// The summary dill with null safety enabled for the dartdevc target.
|
||||
webPlatformSoundKernelDill,
|
||||
/// The precompiled SDKs and sourcemaps for web debug builds.
|
||||
webPrecompiledSdk,
|
||||
webPrecompiledSdkSourcemaps,
|
||||
webPrecompiledCanvaskitSdk,
|
||||
webPrecompiledCanvaskitSdkSourcemaps,
|
||||
webPrecompiledSoundSdk,
|
||||
webPrecompiledSoundSdkSourcemaps,
|
||||
webPrecompiledCanvaskitSoundSdk,
|
||||
webPrecompiledCanvaskitSoundSdkSourcemaps,
|
||||
|
||||
iosDeploy,
|
||||
idevicesyslog,
|
||||
idevicescreenshot,
|
||||
@ -129,6 +142,8 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
|
||||
return 'FlutterMacOS.podspec';
|
||||
case Artifact.webPlatformKernelDill:
|
||||
return 'flutter_ddc_sdk.dill';
|
||||
case Artifact.webPlatformSoundKernelDill:
|
||||
return 'flutter_ddc_sdk_sound.dill';
|
||||
case Artifact.fuchsiaKernelCompiler:
|
||||
return 'kernel_compiler.snapshot';
|
||||
case Artifact.fuchsiaFlutterRunner:
|
||||
@ -141,6 +156,16 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
|
||||
return 'const_finder.dart.snapshot';
|
||||
case Artifact.flutterWebLibrariesJson:
|
||||
return 'libraries.json';
|
||||
case Artifact.webPrecompiledSdk:
|
||||
case Artifact.webPrecompiledCanvaskitSdk:
|
||||
case Artifact.webPrecompiledSoundSdk:
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdk:
|
||||
return 'dart_sdk.js';
|
||||
case Artifact.webPrecompiledSdkSourcemaps:
|
||||
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
|
||||
case Artifact.webPrecompiledSoundSdkSourcemaps:
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
|
||||
return 'dart_sdk.js.map';
|
||||
}
|
||||
assert(false, 'Invalid artifact $artifact.');
|
||||
return null;
|
||||
@ -348,6 +373,8 @@ class CachedArtifacts implements Artifacts {
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), _artifactToFileName(artifact));
|
||||
case Artifact.webPlatformKernelDill:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
|
||||
case Artifact.webPlatformSoundKernelDill:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
|
||||
case Artifact.dart2jsSnapshot:
|
||||
return _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', 'snapshots', _artifactToFileName(artifact));
|
||||
case Artifact.dartdevcSnapshot:
|
||||
@ -380,6 +407,22 @@ class CachedArtifacts implements Artifacts {
|
||||
.childDirectory(getNameForTargetPlatform(platform))
|
||||
.childFile(_artifactToFileName(artifact, platform, mode))
|
||||
.path;
|
||||
case Artifact.webPrecompiledSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledCanvaskitSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledSoundSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledSoundSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _artifactToFileName(artifact, platform, mode));
|
||||
default:
|
||||
assert(false, 'Artifact $artifact not available for platform $platform.');
|
||||
return null;
|
||||
@ -542,6 +585,8 @@ class LocalEngineArtifacts implements Artifacts {
|
||||
return _fileSystem.path.join(_hostEngineOutPath, _artifactToFileName(artifact));
|
||||
case Artifact.webPlatformKernelDill:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
|
||||
case Artifact.webPlatformSoundKernelDill:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
|
||||
case Artifact.fuchsiaKernelCompiler:
|
||||
final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
|
||||
final String modeName = mode.isRelease ? 'release' : mode.toString();
|
||||
@ -557,6 +602,22 @@ class LocalEngineArtifacts implements Artifacts {
|
||||
return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName);
|
||||
case Artifact.flutterWebLibrariesJson:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), artifactFileName);
|
||||
case Artifact.webPrecompiledSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', artifactFileName);
|
||||
case Artifact.webPrecompiledSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', artifactFileName);
|
||||
case Artifact.webPrecompiledCanvaskitSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', artifactFileName);
|
||||
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', artifactFileName);
|
||||
case Artifact.webPrecompiledSoundSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', artifactFileName);
|
||||
case Artifact.webPrecompiledSoundSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', artifactFileName);
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdk:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', artifactFileName);
|
||||
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
|
||||
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', artifactFileName);
|
||||
}
|
||||
assert(false, 'Invalid artifact $artifact.');
|
||||
return null;
|
||||
|
@ -30,10 +30,16 @@ class BuildInfo {
|
||||
@required this.treeShakeIcons,
|
||||
this.performanceMeasurementFile,
|
||||
this.packagesPath = '.packages',
|
||||
this.nullSafetyMode = NullSafetyMode.autodetect,
|
||||
});
|
||||
|
||||
final BuildMode mode;
|
||||
|
||||
/// The null safety mode the application should be run in.
|
||||
///
|
||||
/// If not provided, defaults to [NullSafetyMode.autodetect].
|
||||
final NullSafetyMode nullSafetyMode;
|
||||
|
||||
/// Whether the build should subdset icon fonts.
|
||||
final bool treeShakeIcons;
|
||||
|
||||
@ -688,3 +694,10 @@ List<String> decodeDartDefines(Map<String, String> environmentDefines, String ke
|
||||
.cast<String>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// The null safety runtime mode the app should be built in.
|
||||
enum NullSafetyMode {
|
||||
sound,
|
||||
unsound,
|
||||
autodetect,
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ class WebAssetServer implements AssetReader {
|
||||
this.internetAddress,
|
||||
this._modules,
|
||||
this._digests,
|
||||
this._buildInfo,
|
||||
);
|
||||
|
||||
// Fallback to "application/octet-stream" on null which
|
||||
@ -167,6 +168,7 @@ class WebAssetServer implements AssetReader {
|
||||
address,
|
||||
modules,
|
||||
digests,
|
||||
buildInfo,
|
||||
);
|
||||
if (testMode) {
|
||||
return server;
|
||||
@ -263,6 +265,7 @@ class WebAssetServer implements AssetReader {
|
||||
return null;
|
||||
}
|
||||
|
||||
final BuildInfo _buildInfo;
|
||||
final HttpServer _httpServer;
|
||||
// If holding these in memory is too much overhead, this can be switched to a
|
||||
// RandomAccessFile and read on demand.
|
||||
@ -458,50 +461,31 @@ class WebAssetServer implements AssetReader {
|
||||
/// Whether to use the cavaskit SDK for rendering.
|
||||
bool canvasKitRendering = false;
|
||||
|
||||
@visibleForTesting
|
||||
final File dartSdk = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd',
|
||||
'dart_sdk.js',
|
||||
));
|
||||
|
||||
@visibleForTesting
|
||||
final File canvasKitDartSdk = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd-canvaskit',
|
||||
'dart_sdk.js',
|
||||
));
|
||||
|
||||
@visibleForTesting
|
||||
final File dartSdkSourcemap = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd',
|
||||
'dart_sdk.js.map',
|
||||
));
|
||||
|
||||
@visibleForTesting
|
||||
final File canvasKitDartSdkSourcemap = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd-canvaskit',
|
||||
'dart_sdk.js.map',
|
||||
));
|
||||
|
||||
// Attempt to resolve `path` to a dart file.
|
||||
File _resolveDartFile(String path) {
|
||||
// Return the actual file objects so that local engine changes are automatically picked up.
|
||||
switch (path) {
|
||||
case 'dart_sdk.js':
|
||||
return canvasKitRendering
|
||||
? canvasKitDartSdk
|
||||
: dartSdk;
|
||||
if (_buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
|
||||
return globals.fs.file(canvasKitRendering
|
||||
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk)
|
||||
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSdk));
|
||||
} else {
|
||||
return globals.fs.file(canvasKitRendering
|
||||
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk)
|
||||
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdk));
|
||||
}
|
||||
break;
|
||||
case 'dart_sdk.js.map':
|
||||
return canvasKitRendering
|
||||
? canvasKitDartSdkSourcemap
|
||||
: dartSdkSourcemap;
|
||||
if (_buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
|
||||
return globals.fs.file(canvasKitRendering
|
||||
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps)
|
||||
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps));
|
||||
} else {
|
||||
return globals.fs.file(canvasKitRendering
|
||||
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps)
|
||||
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps));
|
||||
}
|
||||
}
|
||||
// This is the special generated entrypoint.
|
||||
if (path == 'web_entrypoint.dart') {
|
||||
|
@ -92,6 +92,20 @@ class FlutterDevice {
|
||||
// a warning message and dump some debug information which can be
|
||||
// used to file a bug, but the compiler will still start up correctly.
|
||||
if (targetPlatform == TargetPlatform.web_javascript) {
|
||||
Artifact platformDillArtifact;
|
||||
List<String> extraFrontEndOptions;
|
||||
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
|
||||
platformDillArtifact = Artifact.webPlatformKernelDill;
|
||||
extraFrontEndOptions = buildInfo.extraFrontEndOptions;
|
||||
} else {
|
||||
platformDillArtifact = Artifact.webPlatformSoundKernelDill;
|
||||
extraFrontEndOptions = <String>[
|
||||
...?buildInfo?.extraFrontEndOptions,
|
||||
if (!(buildInfo?.extraFrontEndOptions?.contains('--sound-null-safety') ?? false))
|
||||
'--sound-null-safety'
|
||||
];
|
||||
}
|
||||
|
||||
generator = ResidentCompiler(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: buildInfo.mode),
|
||||
buildMode: buildInfo.mode,
|
||||
@ -105,9 +119,9 @@ class FlutterDevice {
|
||||
dartDefines: buildInfo.dartDefines,
|
||||
),
|
||||
targetModel: TargetModel.dartdevc,
|
||||
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
|
||||
extraFrontEndOptions: extraFrontEndOptions,
|
||||
platformDill: globals.fs.file(globals.artifacts
|
||||
.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode))
|
||||
.getArtifactPath(platformDillArtifact, mode: buildInfo.mode))
|
||||
.absolute.uri.toString(),
|
||||
dartDefines: buildInfo.dartDefines,
|
||||
librariesSpec: globals.fs.file(globals.artifacts
|
||||
|
@ -479,8 +479,9 @@ abstract class FlutterCommand extends Command<void> {
|
||||
help:
|
||||
'Whether to override the inferred null safety mode. This allows null-safe '
|
||||
'libraries to depend on un-migrated (non-null safe) libraries. By default, '
|
||||
'Flutter applications will attempt to run at the null safety level of their '
|
||||
'entrypoint library (usually lib/main.dart).',
|
||||
'Flutter mobile & desktop applications will attempt to run at the null safety '
|
||||
'level of their entrypoint library (usually lib/main.dart). Flutter web '
|
||||
'applications will default to sound null-safety, unless specifically configured.',
|
||||
defaultsTo: null,
|
||||
hide: hide,
|
||||
);
|
||||
@ -617,15 +618,20 @@ abstract class FlutterCommand extends Command<void> {
|
||||
}
|
||||
}
|
||||
|
||||
NullSafetyMode nullSafetyMode = NullSafetyMode.unsound;
|
||||
if (argParser.options.containsKey(FlutterOptions.kNullSafety)) {
|
||||
final bool nullSafety = boolArg(FlutterOptions.kNullSafety);
|
||||
// Explicitly check for `true` and `false` so that `null` results in not
|
||||
// passing a flag. This will use the automatically detected null-safety
|
||||
// value based on the entrypoint
|
||||
if (nullSafety == true) {
|
||||
nullSafetyMode = NullSafetyMode.sound;
|
||||
extraFrontEndOptions.add('--sound-null-safety');
|
||||
} else if (nullSafety == false) {
|
||||
nullSafetyMode = NullSafetyMode.unsound;
|
||||
extraFrontEndOptions.add('--no-sound-null-safety');
|
||||
} else if (extraFrontEndOptions.contains('--enable-experiment=non-nullable')) {
|
||||
nullSafetyMode = NullSafetyMode.autodetect;
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,7 +691,8 @@ abstract class FlutterCommand extends Command<void> {
|
||||
bundleSkSLPath: bundleSkSLPath,
|
||||
dartExperiments: experiments,
|
||||
performanceMeasurementFile: performanceMeasurementFile,
|
||||
packagesPath: globalResults['packages'] as String ?? '.packages'
|
||||
packagesPath: globalResults['packages'] as String ?? '.packages',
|
||||
nullSafetyMode: nullSafetyMode,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ Future<void> buildWeb(
|
||||
kCspMode: csp.toString(),
|
||||
kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
|
||||
if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false)
|
||||
kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(',')
|
||||
kExtraFrontEndOptions: encodeDartDefines(buildInfo.extraFrontEndOptions),
|
||||
},
|
||||
artifacts: globals.artifacts,
|
||||
fileSystem: globals.fs,
|
||||
|
@ -52,6 +52,41 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('precompiled web artifact paths are correct', () {
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledSdk),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js.map',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js.map',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdk),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js.map',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js',
|
||||
);
|
||||
expect(
|
||||
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps),
|
||||
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js.map',
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('getEngineType', () {
|
||||
expect(
|
||||
artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug),
|
||||
|
@ -1540,7 +1540,7 @@ void main() {
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
testUsingContext('FlutterDevice uses dartdevc configuration when targeting web', () => testbed.run(() async {
|
||||
testUsingContext('FlutterDevice uses dartdevc configuration when targeting web', () async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
|
||||
@ -1549,7 +1549,12 @@ void main() {
|
||||
|
||||
final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create(
|
||||
mockDevice,
|
||||
buildInfo: BuildInfo.debug,
|
||||
buildInfo: const BuildInfo(
|
||||
BuildMode.debug,
|
||||
'',
|
||||
treeShakeIcons: false,
|
||||
nullSafetyMode: NullSafetyMode.unsound,
|
||||
),
|
||||
flutterProject: FlutterProject.current(),
|
||||
target: null,
|
||||
)).generator as DefaultResidentCompiler;
|
||||
@ -1562,12 +1567,46 @@ void main() {
|
||||
expect(residentCompiler.targetModel, TargetModel.dartdevc);
|
||||
expect(residentCompiler.sdkRoot,
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/');
|
||||
expect(
|
||||
residentCompiler.platformDill,
|
||||
globals.fs.file(globals.artifacts.getArtifactPath(Artifact.webPlatformKernelDill, mode: BuildMode.debug))
|
||||
.absolute.uri.toString(),
|
||||
);
|
||||
}));
|
||||
expect(residentCompiler.platformDill, 'file:///Artifact.webPlatformKernelDill.debug');
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => Artifacts.test(),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('FlutterDevice uses dartdevc configuration when targeting web with null-safety autodetected', () async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
|
||||
return TargetPlatform.web_javascript;
|
||||
});
|
||||
|
||||
final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create(
|
||||
mockDevice,
|
||||
buildInfo: const BuildInfo(
|
||||
BuildMode.debug,
|
||||
'',
|
||||
treeShakeIcons: false,
|
||||
extraFrontEndOptions: <String>['--enable-experiment=non-nullable'],
|
||||
),
|
||||
flutterProject: FlutterProject.current(),
|
||||
target: null,
|
||||
)).generator as DefaultResidentCompiler;
|
||||
|
||||
expect(residentCompiler.initializeFromDill,
|
||||
globals.fs.path.join(getBuildDirectory(), 'cache.dill'));
|
||||
expect(residentCompiler.librariesSpec,
|
||||
globals.fs.file(globals.artifacts.getArtifactPath(Artifact.flutterWebLibrariesJson))
|
||||
.uri.toString());
|
||||
expect(residentCompiler.targetModel, TargetModel.dartdevc);
|
||||
expect(residentCompiler.sdkRoot,
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/');
|
||||
expect(residentCompiler.platformDill, 'file:///Artifact.webPlatformSoundKernelDill.debug');
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => Artifacts.test(),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('connect sets up log reader', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
|
@ -764,7 +764,7 @@ void main() {
|
||||
testUsingContext('web resident runner can toggle CanvasKit', () async {
|
||||
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null);
|
||||
final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null, null);
|
||||
when(mockWebDevFS.webAssetServer).thenReturn(webAssetServer);
|
||||
|
||||
expect(residentWebRunner.supportsCanvasKit, true);
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
@ -53,6 +54,8 @@ void main() {
|
||||
InternetAddress.loopbackIPv4,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -416,7 +419,12 @@ void main() {
|
||||
packagesFilePath: '.packages',
|
||||
urlTunneller: null,
|
||||
useSseForDebugProxy: true,
|
||||
buildInfo: BuildInfo.debug,
|
||||
buildInfo: const BuildInfo(
|
||||
BuildMode.debug,
|
||||
'',
|
||||
treeShakeIcons: false,
|
||||
nullSafetyMode: NullSafetyMode.unsound,
|
||||
),
|
||||
enableDwds: false,
|
||||
entrypoint: Uri.base,
|
||||
testMode: true,
|
||||
@ -428,24 +436,31 @@ void main() {
|
||||
|
||||
final Uri uri = await webDevFS.create();
|
||||
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
|
||||
final String webPrecompiledSdk = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledSdk);
|
||||
final String webPrecompiledSdkSourcemaps = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps);
|
||||
final String webPrecompiledCanvaskitSdk = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk);
|
||||
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps);
|
||||
globals.fs.currentDirectory
|
||||
.childDirectory('lib')
|
||||
.childFile('web_entrypoint.dart')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('GENERATED');
|
||||
webDevFS.webAssetServer.dartSdk
|
||||
globals.fs.file(webPrecompiledSdk)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('HELLO');
|
||||
webDevFS.webAssetServer.dartSdkSourcemap
|
||||
globals.fs.file(webPrecompiledSdkSourcemaps)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('THERE');
|
||||
webDevFS.webAssetServer.canvasKitDartSdk
|
||||
globals.fs.file(webPrecompiledCanvaskitSdk)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('OL');
|
||||
webDevFS.webAssetServer.canvasKitDartSdkSourcemap
|
||||
globals.fs.file(webPrecompiledCanvaskitSdkSourcemaps)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('CHUM');
|
||||
webDevFS.webAssetServer.dartSdkSourcemap.createSync(recursive: true);
|
||||
|
||||
await webDevFS.update(
|
||||
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
|
||||
@ -465,7 +480,7 @@ void main() {
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
|
||||
|
||||
// Update to the SDK.
|
||||
webDevFS.webAssetServer.dartSdk.writeAsStringSync('BELLOW');
|
||||
globals.fs.file(webPrecompiledSdk).writeAsStringSync('BELLOW');
|
||||
|
||||
// New SDK should be visible..
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
|
||||
@ -483,6 +498,116 @@ void main() {
|
||||
expect(uri, Uri.http('localhost:0', ''));
|
||||
|
||||
await webDevFS.destroy();
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => Artifacts.test(),
|
||||
}));
|
||||
|
||||
test('Can start web server with specified assets in sound null safety mode', () => testbed.run(() async {
|
||||
globals.fs.file('.packages').writeAsStringSync('\n');
|
||||
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
||||
..createSync(recursive: true);
|
||||
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
||||
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
||||
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
||||
outputFile.parent.childFile('.packages').writeAsStringSync('\n');
|
||||
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.recompile(
|
||||
any,
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packageConfig: anyNamed('packageConfig'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
|
||||
final WebDevFS webDevFS = WebDevFS(
|
||||
hostname: 'localhost',
|
||||
port: 0,
|
||||
packagesFilePath: '.packages',
|
||||
urlTunneller: null,
|
||||
useSseForDebugProxy: true,
|
||||
buildInfo: const BuildInfo(
|
||||
BuildMode.debug,
|
||||
'',
|
||||
treeShakeIcons: false,
|
||||
nullSafetyMode: NullSafetyMode.autodetect,
|
||||
),
|
||||
enableDwds: false,
|
||||
entrypoint: Uri.base,
|
||||
testMode: true,
|
||||
expressionCompiler: null,
|
||||
chromiumLauncher: null,
|
||||
);
|
||||
webDevFS.requireJS.createSync(recursive: true);
|
||||
webDevFS.stackTraceMapper.createSync(recursive: true);
|
||||
|
||||
final Uri uri = await webDevFS.create();
|
||||
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
|
||||
globals.fs.currentDirectory
|
||||
.childDirectory('lib')
|
||||
.childFile('web_entrypoint.dart')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('GENERATED');
|
||||
final String webPrecompiledSoundSdk = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledSoundSdk);
|
||||
final String webPrecompiledSoundSdkSourcemaps = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps);
|
||||
final String webPrecompiledCanvaskitSoundSdk = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk);
|
||||
final String webPrecompiledCanvaskitSoundSdkSourcemaps = globals.artifacts
|
||||
.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps);
|
||||
globals.fs.file(webPrecompiledSoundSdk)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('HELLO');
|
||||
globals.fs.file(webPrecompiledSoundSdkSourcemaps)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('THERE');
|
||||
globals.fs.file(webPrecompiledCanvaskitSoundSdk)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('OL');
|
||||
globals.fs.file(webPrecompiledCanvaskitSoundSdkSourcemaps)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('CHUM');
|
||||
|
||||
await webDevFS.update(
|
||||
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
|
||||
generator: residentCompiler,
|
||||
trackWidgetCreation: true,
|
||||
bundleFirstUpload: true,
|
||||
invalidatedFiles: <Uri>[],
|
||||
packageConfig: PackageConfig.empty,
|
||||
);
|
||||
|
||||
expect(webDevFS.webAssetServer.getFile('require.js'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('stack_trace_mapper.js'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('main.dart'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('manifest.json'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('flutter_service_worker.js'), isNotNull);
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'HELLO');
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
|
||||
|
||||
// Update to the SDK.
|
||||
globals.fs.file(webPrecompiledSoundSdk).writeAsStringSync('BELLOW');
|
||||
|
||||
// New SDK should be visible..
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
|
||||
|
||||
// Toggle CanvasKit
|
||||
webDevFS.webAssetServer.canvasKitRendering = true;
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
|
||||
|
||||
// Generated entrypoint.
|
||||
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
|
||||
contains('GENERATED'));
|
||||
|
||||
// served on localhost
|
||||
expect(uri, Uri.http('localhost:0', ''));
|
||||
|
||||
await webDevFS.destroy();
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => Artifacts.test(),
|
||||
}));
|
||||
|
||||
test('Can start web server with hostname any', () => testbed.run(() async {
|
||||
|
Loading…
x
Reference in New Issue
Block a user