Unverified Commit 7ca324ac authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[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
parent 21881961
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment