
*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* *List which issues are fixed by this PR. You must list at least one issue.* *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
628 lines
22 KiB
Dart
628 lines
22 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:math';
|
|
|
|
import 'package:crypto/crypto.dart';
|
|
import 'package:package_config/package_config.dart';
|
|
|
|
import '../../artifacts.dart';
|
|
import '../../base/file_system.dart';
|
|
import '../../base/process.dart';
|
|
import '../../build_info.dart';
|
|
import '../../cache.dart';
|
|
import '../../convert.dart';
|
|
import '../../dart/language_version.dart';
|
|
import '../../dart/package_map.dart';
|
|
import '../../flutter_plugins.dart';
|
|
import '../../globals.dart' as globals;
|
|
import '../../html_utils.dart';
|
|
import '../../project.dart';
|
|
import '../../web/compile.dart';
|
|
import '../../web/file_generators/flutter_js.dart' as flutter_js;
|
|
import '../../web/file_generators/flutter_service_worker_js.dart';
|
|
import '../../web/file_generators/main_dart.dart' as main_dart;
|
|
import '../../web/file_generators/wasm_bootstrap.dart' as wasm_bootstrap;
|
|
import '../build_system.dart';
|
|
import '../depfile.dart';
|
|
import '../exceptions.dart';
|
|
import 'assets.dart';
|
|
import 'localizations.dart';
|
|
import 'shader_compiler.dart';
|
|
|
|
/// Whether the application has web plugins.
|
|
const String kHasWebPlugins = 'HasWebPlugins';
|
|
|
|
/// Base href to set in index.html in flutter build command
|
|
const String kBaseHref = 'baseHref';
|
|
|
|
/// The caching strategy to use for service worker generation.
|
|
const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
|
|
|
|
/// Generates an entry point for a web target.
|
|
// Keep this in sync with build_runner/resident_web_runner.dart
|
|
class WebEntrypointTarget extends Target {
|
|
const WebEntrypointTarget();
|
|
|
|
@override
|
|
String get name => 'web_entrypoint';
|
|
|
|
@override
|
|
List<Target> get dependencies => const <Target>[];
|
|
|
|
@override
|
|
List<Source> get inputs => const <Source>[
|
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/web.dart'),
|
|
];
|
|
|
|
@override
|
|
List<Source> get outputs => const <Source>[
|
|
Source.pattern('{BUILD_DIR}/main.dart'),
|
|
];
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
final String? targetFile = environment.defines[kTargetFile];
|
|
final Uri importUri = environment.fileSystem.file(targetFile).absolute.uri;
|
|
// TODO(zanderso): support configuration of this file.
|
|
const String packageFile = '.packages';
|
|
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
|
|
environment.fileSystem.file(packageFile),
|
|
logger: environment.logger,
|
|
);
|
|
final FlutterProject flutterProject = FlutterProject.current();
|
|
final LanguageVersion languageVersion = determineLanguageVersion(
|
|
environment.fileSystem.file(targetFile),
|
|
packageConfig[flutterProject.manifest.appName],
|
|
Cache.flutterRoot!,
|
|
);
|
|
|
|
// Use the PackageConfig to find the correct package-scheme import path
|
|
// for the user application. If the application has a mix of package-scheme
|
|
// and relative imports for a library, then importing the entrypoint as a
|
|
// file-scheme will cause said library to be recognized as two distinct
|
|
// libraries. This can cause surprising behavior as types from that library
|
|
// will be considered distinct from each other.
|
|
// By construction, this will only be null if the .packages file does not
|
|
// have an entry for the user's application or if the main file is
|
|
// outside of the lib/ directory.
|
|
final String importedEntrypoint = packageConfig.toPackageUri(importUri)?.toString()
|
|
?? importUri.toString();
|
|
|
|
await injectBuildTimePluginFiles(flutterProject, webPlatform: true, destination: environment.buildDir);
|
|
// The below works because `injectBuildTimePluginFiles` is configured to write
|
|
// the web_plugin_registrant.dart file alongside the generated main.dart
|
|
const String generatedImport = 'web_plugin_registrant.dart';
|
|
|
|
final String contents = main_dart.generateMainDartFile(importedEntrypoint,
|
|
languageVersion: languageVersion,
|
|
pluginRegistrantEntrypoint: generatedImport,
|
|
);
|
|
|
|
environment.buildDir.childFile('main.dart').writeAsStringSync(contents);
|
|
}
|
|
}
|
|
|
|
/// Compiles a web entry point with dart2js.
|
|
abstract class Dart2WebTarget extends Target {
|
|
const Dart2WebTarget(this.webRenderer);
|
|
|
|
final WebRendererMode webRenderer;
|
|
Source get compilerSnapshot;
|
|
|
|
@override
|
|
List<Target> get dependencies => const <Target>[
|
|
WebEntrypointTarget(),
|
|
GenerateLocalizationsTarget(),
|
|
];
|
|
|
|
@override
|
|
List<Source> get inputs => <Source>[
|
|
const Source.hostArtifact(HostArtifact.flutterWebSdk),
|
|
compilerSnapshot,
|
|
const Source.artifact(Artifact.engineDartBinary),
|
|
const Source.pattern('{BUILD_DIR}/main.dart'),
|
|
const Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'),
|
|
];
|
|
|
|
@override
|
|
List<Source> get outputs => const <Source>[];
|
|
}
|
|
|
|
class Dart2JSTarget extends Dart2WebTarget {
|
|
Dart2JSTarget(super.webRenderer);
|
|
|
|
@override
|
|
String get name => 'dart2js';
|
|
|
|
@override
|
|
Source get compilerSnapshot => const Source.artifact(Artifact.dart2jsSnapshot);
|
|
|
|
@override
|
|
List<String> get depfiles => const <String>[
|
|
'dart2js.d',
|
|
];
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
final String? buildModeEnvironment = environment.defines[kBuildMode];
|
|
if (buildModeEnvironment == null) {
|
|
throw MissingDefineException(kBuildMode, name);
|
|
}
|
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
|
final JsCompilerConfig compilerConfig = JsCompilerConfig.fromBuildSystemEnvironment(environment.defines);
|
|
final Artifacts artifacts = environment.artifacts;
|
|
final String platformBinariesPath = getWebPlatformBinariesDirectory(artifacts, webRenderer).path;
|
|
final List<String> sharedCommandOptions = <String>[
|
|
artifacts.getArtifactPath(Artifact.engineDartBinary, platform: TargetPlatform.web_javascript),
|
|
'--disable-dart-dev',
|
|
artifacts.getArtifactPath(Artifact.dart2jsSnapshot, platform: TargetPlatform.web_javascript),
|
|
'--platform-binaries=$platformBinariesPath',
|
|
'--invoker=flutter_tool',
|
|
...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions),
|
|
if (buildMode == BuildMode.profile)
|
|
'-Ddart.vm.profile=true'
|
|
else
|
|
'-Ddart.vm.product=true',
|
|
for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
|
|
'-D$dartDefine',
|
|
];
|
|
|
|
final List<String> compilationArgs = <String>[
|
|
...sharedCommandOptions,
|
|
...compilerConfig.toSharedCommandOptions(),
|
|
'-o',
|
|
environment.buildDir.childFile('app.dill').path,
|
|
'--packages=.dart_tool/package_config.json',
|
|
'--cfe-only',
|
|
environment.buildDir.childFile('main.dart').path, // dartfile
|
|
];
|
|
|
|
final ProcessUtils processUtils = ProcessUtils(
|
|
logger: environment.logger,
|
|
processManager: environment.processManager,
|
|
);
|
|
|
|
// Run the dart2js compilation in two stages, so that icon tree shaking can
|
|
// parse the kernel file for web builds.
|
|
await processUtils.run(compilationArgs, throwOnError: true);
|
|
|
|
final File outputJSFile = environment.buildDir.childFile('main.dart.js');
|
|
|
|
await processUtils.run(
|
|
throwOnError: true,
|
|
<String>[
|
|
...sharedCommandOptions,
|
|
if (buildMode == BuildMode.profile) '--no-minify',
|
|
...compilerConfig.toCommandOptions(),
|
|
'-o',
|
|
outputJSFile.path,
|
|
environment.buildDir.childFile('app.dill').path, // dartfile
|
|
],
|
|
);
|
|
final File dart2jsDeps = environment.buildDir.childFile('app.dill.deps');
|
|
if (!dart2jsDeps.existsSync()) {
|
|
environment.logger.printWarning(
|
|
'Warning: dart2js did not produce expected deps list at '
|
|
'${dart2jsDeps.path}',
|
|
);
|
|
return;
|
|
}
|
|
final DepfileService depFileService = environment.depFileService;
|
|
final Depfile depFile = depFileService.parseDart2js(
|
|
environment.buildDir.childFile('app.dill.deps'),
|
|
outputJSFile,
|
|
);
|
|
depFileService.writeToFile(
|
|
depFile,
|
|
environment.buildDir.childFile('dart2js.d'),
|
|
);
|
|
}
|
|
}
|
|
|
|
class Dart2WasmTarget extends Dart2WebTarget {
|
|
Dart2WasmTarget(super.webRenderer);
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
final String? buildModeEnvironment = environment.defines[kBuildMode];
|
|
if (buildModeEnvironment == null) {
|
|
throw MissingDefineException(kBuildMode, name);
|
|
}
|
|
final WasmCompilerConfig compilerConfig = WasmCompilerConfig.fromBuildSystemEnvironment(environment.defines);
|
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
|
final Artifacts artifacts = environment.artifacts;
|
|
final File outputWasmFile = environment.buildDir.childFile(
|
|
compilerConfig.runWasmOpt ? 'main.dart.unopt.wasm' : 'main.dart.wasm'
|
|
);
|
|
final File depFile = environment.buildDir.childFile('dart2wasm.d');
|
|
final String dartSdkPath = artifacts.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript);
|
|
final String dartSdkRoot = environment.fileSystem.directory(dartSdkPath).parent.path;
|
|
|
|
final List<String> compilationArgs = <String>[
|
|
artifacts.getArtifactPath(Artifact.engineDartAotRuntime, platform: TargetPlatform.web_javascript),
|
|
'--disable-dart-dev',
|
|
artifacts.getArtifactPath(Artifact.dart2wasmSnapshot, platform: TargetPlatform.web_javascript),
|
|
'--packages=.dart_tool/package_config.json',
|
|
'--dart-sdk=$dartSdkPath',
|
|
'--multi-root-scheme',
|
|
'org-dartlang-sdk',
|
|
'--multi-root',
|
|
artifacts.getHostArtifact(HostArtifact.flutterWebSdk).path,
|
|
'--multi-root',
|
|
dartSdkRoot,
|
|
'--libraries-spec',
|
|
artifacts.getHostArtifact(HostArtifact.flutterWebLibrariesJson).path,
|
|
if (buildMode == BuildMode.profile)
|
|
'-Ddart.vm.profile=true'
|
|
else
|
|
'-Ddart.vm.product=true',
|
|
...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions),
|
|
for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
|
|
'-D$dartDefine',
|
|
...compilerConfig.toCommandOptions(),
|
|
if (webRenderer == WebRendererMode.skwasm)
|
|
...<String>[
|
|
'--import-shared-memory',
|
|
'--shared-memory-max-pages=32768',
|
|
],
|
|
'--depfile=${depFile.path}',
|
|
|
|
environment.buildDir.childFile('main.dart').path, // dartfile
|
|
outputWasmFile.path,
|
|
];
|
|
|
|
final ProcessUtils processUtils = ProcessUtils(
|
|
logger: environment.logger,
|
|
processManager: environment.processManager,
|
|
);
|
|
|
|
await processUtils.run(
|
|
throwOnError: true,
|
|
compilationArgs,
|
|
);
|
|
if (compilerConfig.runWasmOpt) {
|
|
final String wasmOptBinary = artifacts.getArtifactPath(
|
|
Artifact.wasmOptBinary,
|
|
platform: TargetPlatform.web_javascript
|
|
);
|
|
final File optimizedOutput = environment.buildDir.childFile('main.dart.wasm');
|
|
final List<String> optimizeArgs = <String>[
|
|
wasmOptBinary,
|
|
'--all-features',
|
|
'--closed-world',
|
|
'--traps-never-happen',
|
|
'-O3',
|
|
'--type-ssa',
|
|
'--gufa',
|
|
'-O3',
|
|
'--type-merging',
|
|
if (compilerConfig.wasmOpt == WasmOptLevel.debug)
|
|
'--debuginfo',
|
|
outputWasmFile.path,
|
|
'-o',
|
|
optimizedOutput.path,
|
|
];
|
|
await processUtils.run(
|
|
throwOnError: true,
|
|
optimizeArgs,
|
|
);
|
|
|
|
// Rename the .mjs file not to have the `.unopt` bit
|
|
final File jsRuntimeFile = environment.buildDir.childFile('main.dart.unopt.mjs');
|
|
await jsRuntimeFile.rename(environment.buildDir.childFile('main.dart.mjs').path);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Source get compilerSnapshot => const Source.artifact(Artifact.dart2wasmSnapshot);
|
|
|
|
@override
|
|
String get name => 'dart2wasm';
|
|
|
|
@override
|
|
List<String> get depfiles => const <String>[
|
|
'dart2wasm.d',
|
|
];
|
|
|
|
@override
|
|
List<Source> get outputs => const <Source>[
|
|
Source.pattern('{OUTPUT_DIR}/main.dart.wasm'),
|
|
Source.pattern('{OUTPUT_DIR}/main.dart.mjs'),
|
|
];
|
|
}
|
|
|
|
/// Unpacks the dart2js or dart2wasm compilation and resources to a given
|
|
/// output directory.
|
|
class WebReleaseBundle extends Target {
|
|
const WebReleaseBundle(this.webRenderer, {required this.isWasm});
|
|
|
|
final WebRendererMode webRenderer;
|
|
final bool isWasm;
|
|
|
|
String get outputFileNameNoSuffix => 'main.dart';
|
|
String get outputFileName => '$outputFileNameNoSuffix${isWasm ? '.wasm' : '.js'}';
|
|
String get wasmJSRuntimeFileName => '$outputFileNameNoSuffix.mjs';
|
|
|
|
@override
|
|
String get name => 'web_release_bundle';
|
|
|
|
@override
|
|
List<Target> get dependencies => <Target>[
|
|
if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
|
|
];
|
|
|
|
@override
|
|
List<Source> get inputs => <Source>[
|
|
Source.pattern('{BUILD_DIR}/$outputFileName'),
|
|
const Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
|
|
if (isWasm) Source.pattern('{BUILD_DIR}/$wasmJSRuntimeFileName'),
|
|
];
|
|
|
|
@override
|
|
List<Source> get outputs => <Source>[
|
|
Source.pattern('{OUTPUT_DIR}/$outputFileName'),
|
|
if (isWasm) Source.pattern('{OUTPUT_DIR}/$wasmJSRuntimeFileName'),
|
|
];
|
|
|
|
@override
|
|
List<String> get depfiles => const <String>[
|
|
'dart2js.d',
|
|
'flutter_assets.d',
|
|
'web_resources.d',
|
|
];
|
|
|
|
bool shouldCopy(String name) =>
|
|
// Do not copy the deps file.
|
|
(name.contains(outputFileName) && !name.endsWith('.deps')) ||
|
|
(isWasm && name == wasmJSRuntimeFileName);
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
for (final File outputFile in environment.buildDir.listSync(recursive: true).whereType<File>()) {
|
|
final String basename = environment.fileSystem.path.basename(outputFile.path);
|
|
if (shouldCopy(basename)) {
|
|
outputFile.copySync(
|
|
environment.outputDir.childFile(environment.fileSystem.path.basename(outputFile.path)).path
|
|
);
|
|
}
|
|
}
|
|
|
|
if (isWasm) {
|
|
// TODO(jacksongardner): Enable icon tree shaking once dart2wasm can do a two-phase compile.
|
|
// https://github.com/flutter/flutter/issues/117248
|
|
environment.defines[kIconTreeShakerFlag] = 'false';
|
|
}
|
|
|
|
createVersionFile(environment, environment.defines);
|
|
final Directory outputDirectory = environment.outputDir.childDirectory('assets');
|
|
outputDirectory.createSync(recursive: true);
|
|
final Depfile depfile = await copyAssets(
|
|
environment,
|
|
environment.outputDir.childDirectory('assets'),
|
|
targetPlatform: TargetPlatform.web_javascript,
|
|
shaderTarget: ShaderTarget.sksl,
|
|
);
|
|
final DepfileService depfileService = environment.depFileService;
|
|
depfileService.writeToFile(
|
|
depfile,
|
|
environment.buildDir.childFile('flutter_assets.d'),
|
|
);
|
|
|
|
final Directory webResources = environment.projectDir
|
|
.childDirectory('web');
|
|
final List<File> inputResourceFiles = webResources
|
|
.listSync(recursive: true)
|
|
.whereType<File>()
|
|
.toList();
|
|
|
|
// Copy other resource files out of web/ directory.
|
|
final List<File> outputResourcesFiles = <File>[];
|
|
for (final File inputFile in inputResourceFiles) {
|
|
final File outputFile = environment.fileSystem.file(environment.fileSystem.path.join(
|
|
environment.outputDir.path,
|
|
environment.fileSystem.path.relative(inputFile.path, from: webResources.path)));
|
|
if (!outputFile.parent.existsSync()) {
|
|
outputFile.parent.createSync(recursive: true);
|
|
}
|
|
outputResourcesFiles.add(outputFile);
|
|
// insert a random hash into the requests for service_worker.js. This is not a content hash,
|
|
// because it would need to be the hash for the entire bundle and not just the resource
|
|
// in question.
|
|
if (environment.fileSystem.path.basename(inputFile.path) == 'index.html') {
|
|
final IndexHtml indexHtml = IndexHtml(inputFile.readAsStringSync());
|
|
indexHtml.applySubstitutions(
|
|
baseHref: environment.defines[kBaseHref] ?? '/',
|
|
serviceWorkerVersion: Random().nextInt(4294967296).toString(),
|
|
);
|
|
outputFile.writeAsStringSync(indexHtml.content);
|
|
continue;
|
|
}
|
|
inputFile.copySync(outputFile.path);
|
|
}
|
|
final Depfile resourceFile = Depfile(inputResourceFiles, outputResourcesFiles);
|
|
depfileService.writeToFile(
|
|
resourceFile,
|
|
environment.buildDir.childFile('web_resources.d'),
|
|
);
|
|
}
|
|
|
|
/// Create version.json file that contains data about version for package_info
|
|
void createVersionFile(Environment environment, Map<String, String> defines) {
|
|
final Map<String, dynamic> versionInfo =
|
|
jsonDecode(FlutterProject.current().getVersionInfo())
|
|
as Map<String, dynamic>;
|
|
|
|
if (defines.containsKey(kBuildNumber)) {
|
|
versionInfo['build_number'] = defines[kBuildNumber];
|
|
}
|
|
|
|
if (defines.containsKey(kBuildName)) {
|
|
versionInfo['version'] = defines[kBuildName];
|
|
}
|
|
|
|
environment.outputDir
|
|
.childFile('version.json')
|
|
.writeAsStringSync(jsonEncode(versionInfo));
|
|
}
|
|
}
|
|
|
|
/// Static assets provided by the Flutter SDK that do not change, such as
|
|
/// CanvasKit.
|
|
///
|
|
/// These assets can be cached until a new version of the flutter web sdk is
|
|
/// downloaded.
|
|
class WebBuiltInAssets extends Target {
|
|
const WebBuiltInAssets(this.fileSystem, this.webRenderer, {required this.isWasm});
|
|
|
|
final FileSystem fileSystem;
|
|
final WebRendererMode webRenderer;
|
|
final bool isWasm;
|
|
|
|
@override
|
|
String get name => 'web_static_assets';
|
|
|
|
@override
|
|
List<Target> get dependencies => const <Target>[];
|
|
|
|
@override
|
|
List<String> get depfiles => const <String>[];
|
|
|
|
@override
|
|
List<Source> get inputs => const <Source>[
|
|
Source.hostArtifact(HostArtifact.flutterWebSdk),
|
|
];
|
|
|
|
Directory get _canvasKitDirectory =>
|
|
globals.fs.directory(
|
|
fileSystem.path.join(
|
|
globals.artifacts!.getHostArtifact(HostArtifact.flutterWebSdk).path,
|
|
'canvaskit',
|
|
)
|
|
);
|
|
|
|
List<File> get _canvasKitFiles => _canvasKitDirectory.listSync(recursive: true).whereType<File>().toList();
|
|
|
|
String _filePathRelativeToCanvasKitDirectory(File file) =>
|
|
fileSystem.path.relative(file.path, from: _canvasKitDirectory.path);
|
|
|
|
@override
|
|
List<Source> get outputs => <Source>[
|
|
if (isWasm) const Source.pattern('{BUILD_DIR}/main.dart.js'),
|
|
const Source.pattern('{BUILD_DIR}/flutter.js'),
|
|
for (final File file in _canvasKitFiles)
|
|
Source.pattern('{BUILD_DIR}/canvaskit/${_filePathRelativeToCanvasKitDirectory(file)}'),
|
|
];
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
for (final File file in _canvasKitFiles) {
|
|
final String relativePath = _filePathRelativeToCanvasKitDirectory(file);
|
|
final String targetPath = fileSystem.path.join(environment.outputDir.path, 'canvaskit', relativePath);
|
|
file.copySync(targetPath);
|
|
}
|
|
|
|
if (isWasm) {
|
|
final File bootstrapFile = environment.outputDir.childFile('main.dart.js');
|
|
bootstrapFile.writeAsStringSync(
|
|
wasm_bootstrap.generateWasmBootstrapFile(webRenderer == WebRendererMode.skwasm)
|
|
);
|
|
}
|
|
|
|
// Write the flutter.js file
|
|
final File flutterJsFile = environment.outputDir.childFile('flutter.js');
|
|
final String fileGeneratorsPath =
|
|
environment.artifacts.getArtifactPath(Artifact.flutterToolsFileGenerators);
|
|
flutterJsFile.writeAsStringSync(
|
|
flutter_js.generateFlutterJsFile(fileGeneratorsPath));
|
|
}
|
|
}
|
|
|
|
/// Generate a service worker for a web target.
|
|
class WebServiceWorker extends Target {
|
|
const WebServiceWorker(this.fileSystem, this.webRenderer, {required this.isWasm});
|
|
|
|
final FileSystem fileSystem;
|
|
final WebRendererMode webRenderer;
|
|
final bool isWasm;
|
|
|
|
@override
|
|
String get name => 'web_service_worker';
|
|
|
|
@override
|
|
List<Target> get dependencies => <Target>[
|
|
if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
|
|
WebReleaseBundle(webRenderer, isWasm: isWasm),
|
|
WebBuiltInAssets(fileSystem, webRenderer, isWasm: isWasm),
|
|
];
|
|
|
|
@override
|
|
List<String> get depfiles => const <String>[
|
|
'service_worker.d',
|
|
];
|
|
|
|
@override
|
|
List<Source> get inputs => const <Source>[];
|
|
|
|
@override
|
|
List<Source> get outputs => const <Source>[];
|
|
|
|
@override
|
|
Future<void> build(Environment environment) async {
|
|
final List<File> contents = environment.outputDir
|
|
.listSync(recursive: true)
|
|
.whereType<File>()
|
|
.where((File file) => !file.path.endsWith('flutter_service_worker.js')
|
|
&& !environment.fileSystem.path.basename(file.path).startsWith('.'))
|
|
.toList();
|
|
|
|
final Map<String, String> urlToHash = <String, String>{};
|
|
for (final File file in contents) {
|
|
// Do not force caching of source maps.
|
|
if (file.path.endsWith('main.dart.js.map') ||
|
|
file.path.endsWith('.part.js.map')) {
|
|
continue;
|
|
}
|
|
final String url = environment.fileSystem.path.toUri(
|
|
environment.fileSystem.path.relative(
|
|
file.path,
|
|
from: environment.outputDir.path),
|
|
).toString();
|
|
final String hash = md5.convert(await file.readAsBytes()).toString();
|
|
urlToHash[url] = hash;
|
|
// Add an additional entry for the base URL.
|
|
if (environment.fileSystem.path.basename(url) == 'index.html') {
|
|
urlToHash['/'] = hash;
|
|
}
|
|
}
|
|
|
|
final File serviceWorkerFile = environment.outputDir
|
|
.childFile('flutter_service_worker.js');
|
|
final Depfile depfile = Depfile(contents, <File>[serviceWorkerFile]);
|
|
final ServiceWorkerStrategy serviceWorkerStrategy =
|
|
ServiceWorkerStrategy.fromCliName(environment.defines[kServiceWorkerStrategy]);
|
|
final String fileGeneratorsPath =
|
|
environment.artifacts.getArtifactPath(Artifact.flutterToolsFileGenerators);
|
|
final String serviceWorker = generateServiceWorker(
|
|
fileGeneratorsPath,
|
|
urlToHash,
|
|
<String>[
|
|
'main.dart.js',
|
|
'index.html',
|
|
if (urlToHash.containsKey('assets/AssetManifest.json'))
|
|
'assets/AssetManifest.json',
|
|
if (urlToHash.containsKey('assets/FontManifest.json'))
|
|
'assets/FontManifest.json',
|
|
],
|
|
serviceWorkerStrategy: serviceWorkerStrategy,
|
|
);
|
|
serviceWorkerFile
|
|
.writeAsStringSync(serviceWorker);
|
|
environment.depFileService.writeToFile(
|
|
depfile,
|
|
environment.buildDir.childFile('service_worker.d'),
|
|
);
|
|
}
|
|
}
|