blob: 7e6639924af86b114304f96038f231bf36ecb409 [file]
// 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 '../base/common.dart';
import '../base/file_system.dart';
import '../build_info.dart';
import '../features.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart';
import '../web/compile.dart';
import '../web/file_generators/flutter_service_worker_js.dart';
import '../web/web_constants.dart';
import '../web/web_options.dart';
import '../web_template.dart';
import 'build.dart';
class BuildWebCommand extends BuildSubCommand {
BuildWebCommand({
required super.logger,
required FileSystem fileSystem,
required super.verboseHelp,
}) : _fileSystem = fileSystem {
registerOptionBundles(const <OptionBundle>[
CommonBuildOptionsBundle(),
BuildModeOptionsBundle(),
DartCompileOptionsBundle(),
WebOptionsBundle(),
]);
}
final FileSystem _fileSystem;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.web,
};
@override
final name = 'web';
@override
bool get hidden => !featureFlags.isWebEnabled;
@override
final description = 'Build a web application bundle.';
@override
Future<FlutterCommandResult> runCommand() async {
if (!featureFlags.isWebEnabled) {
throwToolExit(
'"build web" is not currently supported. To enable, run "flutter config --enable-web".',
);
}
final String? optimizationLevelArg = getValue(WebOptions.optimizationLevel);
final int? optimizationLevel = optimizationLevelArg != null
? int.parse(optimizationLevelArg)
: null;
final String? dart2jsOptimizationLevelValue = getValue(WebOptions.dart2jsOptimization);
final int? jsOptimizationLevel = dart2jsOptimizationLevelValue != null
? int.parse(dart2jsOptimizationLevelValue.substring(1))
: optimizationLevel;
final List<String> dartDefines = extractDartDefines(
defineConfigJsonMap: extractDartDefineConfigJsonMap(),
);
final bool useWasm = getValue(WebOptions.wasm);
// See also: RunCommandBase.webRenderer and TestCommand.webRenderer.
final webRenderer = WebRendererMode.fromDartDefines(dartDefines, useWasm: useWasm);
final bool sourceMaps = getValue(WebOptions.sourceMaps);
final bool? minifyJs = getValue(WebOptions.minifyJs);
final bool? minifyWasm = getValue(WebOptions.minifyWasm);
final List<WebCompilerConfig> compilerConfigs;
if (useWasm) {
if (webRenderer != WebRendererMode.getDefault(useWasm: true)) {
throwToolExit(
'Do not attempt to set a web renderer when using "--${FlutterOptions.kWebWasmFlag}"',
);
}
globals.logger.printBox(title: 'New feature', '''
WebAssembly compilation is new. Understand the details before deploying to production.
$kWasmMoreInfo''');
compilerConfigs = <WebCompilerConfig>[
WasmCompilerConfig(
optimizationLevel: optimizationLevel,
stripWasm: getValue(WebOptions.stripWasm),
sourceMaps: sourceMaps,
minify: minifyWasm,
enableWasmDeferredLoading: getValue(WebOptions.enableWasmDeferredLoading),
),
JsCompilerConfig(
csp: getValue(WebOptions.csp),
dumpInfo: getValue(WebOptions.dumpInfo),
minify: minifyJs,
nativeNullAssertions: getValue(CommonOptions.nativeNullAssertions),
useFrequencyBasedMinification: !getValue(WebOptions.noFrequencyBasedMinification),
optimizationLevel: jsOptimizationLevel,
sourceMaps: sourceMaps,
),
];
} else {
compilerConfigs = <WebCompilerConfig>[
JsCompilerConfig(
csp: getValue(WebOptions.csp),
dumpInfo: getValue(WebOptions.dumpInfo),
minify: minifyJs,
nativeNullAssertions: getValue(CommonOptions.nativeNullAssertions),
useFrequencyBasedMinification: !getValue(WebOptions.noFrequencyBasedMinification),
optimizationLevel: jsOptimizationLevel,
sourceMaps: sourceMaps,
renderer: webRenderer,
),
if (getValue(WebOptions.wasmDryRun))
WasmCompilerConfig(
optimizationLevel: optimizationLevel,
stripWasm: getValue(WebOptions.stripWasm),
sourceMaps: sourceMaps,
minify: minifyWasm,
enableWasmDeferredLoading: getValue(WebOptions.enableWasmDeferredLoading),
dryRun: true,
),
];
}
final BuildInfo buildInfo = await getBuildInfo();
final String? baseHref = getValue(WebOptions.baseHref);
final String? staticAssetsUrl = getValue(WebOptions.staticAssetsUrl);
if (baseHref != null && !(baseHref.startsWith('/') && baseHref.endsWith('/'))) {
throwToolExit(
'Received a --base-href value of "$baseHref"\n'
'--base-href should start and end with /',
);
}
if (staticAssetsUrl != null && !staticAssetsUrl.endsWith('/')) {
throwToolExit(
'Received a --static-assets-url value of "$staticAssetsUrl"\n'
'--static-assets-url should end with /',
);
}
if (!project.web.existsSync()) {
throwToolExit(
'This project is not configured for the web.\n'
'To configure this project for the web, run flutter create . --platforms web',
);
}
if (!_fileSystem.currentDirectory
.childDirectory('web')
.childFile('index.html')
.readAsStringSync()
.contains(kBaseHrefPlaceholder) &&
baseHref != null) {
throwToolExit(
"Couldn't find the placeholder for base href. "
'Please add `<base href="$kBaseHrefPlaceholder">` to web/index.html',
);
}
final String? outputDirectoryPath = getValue(CommonOptions.outputDir);
final Map<String, String> webDefines = extractWebDefines();
final webBuilder = WebBuilder(
logger: globals.logger,
processManager: globals.processManager,
buildSystem: globals.buildSystem,
fileSystem: globals.fs,
flutterVersion: globals.flutterVersion,
analytics: globals.analytics,
);
await webBuilder.buildWeb(
project,
targetFile,
buildInfo,
ServiceWorkerStrategy.fromCliName(getValue(WebOptions.pwaStrategy)),
compilerConfigs: compilerConfigs,
baseHref: baseHref,
staticAssetsUrl: staticAssetsUrl,
outputDirectoryPath: outputDirectoryPath,
webDefines: webDefines,
);
return FlutterCommandResult.success();
}
}