diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart index 5cf6e09508..9268cd02ab 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -39,6 +39,9 @@ const String kCspMode = 'cspMode'; /// The caching strategy to use for service worker generation. const String kServiceWorkerStrategy = 'ServiceWorkerStratgey'; +/// Whether the dart2js build should output source maps. +const String kSourceMapsEnabled = 'SourceMaps'; + /// The caching strategy for the generated service worker. enum ServiceWorkerStrategy { /// Download the app shell eagerly and all other assets lazily. @@ -194,6 +197,7 @@ class Dart2JSTarget extends Target { @override Future build(Environment environment) async { final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); + final bool sourceMapsEnabled = environment.defines[kSourceMapsEnabled] == 'true'; final List sharedCommandOptions = [ globals.artifacts.getArtifactPath(Artifact.engineDartBinary), @@ -207,6 +211,8 @@ class Dart2JSTarget extends Target { '-Ddart.vm.product=true', for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines)) '-D$dartDefine', + if (!sourceMapsEnabled) + '--no-source-maps', ]; // Run the dart2js compilation in two stages, so that icon tree shaking can @@ -227,7 +233,7 @@ class Dart2JSTarget extends Target { final File outputJSFile = environment.buildDir.childFile('main.dart.js'); final bool csp = environment.defines[kCspMode] == 'true'; - final ProcessResult javaScriptResult = await globals.processManager.run([ + final ProcessResult javaScriptResult = await environment.processManager.run([ ...sharedCommandOptions, if (dart2jsOptimization != null) '-$dart2jsOptimization' else '-O4', if (buildMode == BuildMode.profile) '--no-minify', diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index 36b8a5eb34..2e9b8342cf 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -37,6 +37,13 @@ class BuildWebCommand extends BuildSubCommand { help: 'Disable dynamic generation of code in the generated output. ' 'This is necessary to satisfy CSP restrictions (see http://www.w3.org/TR/CSP/).' ); + argParser.addFlag( + 'source-maps', + defaultsTo: false, + help: 'Whether to generate a sourcemap file. These can be used by browsers ' + 'To view and debug the original source code of a compiled and minified Dart ' + 'application. Defaults to false (no sourcemaps produced).' + ); argParser.addOption('pwa-strategy', defaultsTo: kOfflineFirst, help: @@ -88,6 +95,7 @@ class BuildWebCommand extends BuildSubCommand { boolArg('web-initialize-platform'), boolArg('csp'), stringArg('pwa-strategy'), + boolArg('source-maps') ); return FlutterCommandResult.success(); } diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index 49a0d25d7e..4db840cf37 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -531,6 +531,7 @@ class _ResidentWebRunner extends ResidentWebRunner { debuggingOptions.initializePlatform, false, kNoneWorker, + true, ); } await device.device.startApp( @@ -601,6 +602,7 @@ class _ResidentWebRunner extends ResidentWebRunner { debuggingOptions.initializePlatform, false, kNoneWorker, + true, ); } on ToolExit { return OperationResult(1, 'Failed to recompile application.'); diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 3ca679d3c7..c0fe6d860d 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -29,19 +29,25 @@ Future buildWeb( bool initializePlatform, bool csp, String serviceWorkerStrategy, + bool sourceMaps, ) async { if (!flutterProject.web.existsSync()) { throwToolExit('Missing index.html.'); } final bool hasWebPlugins = (await findPlugins(flutterProject)) .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey)); + final Directory outputDirectory = globals.fs.directory(getWebBuildDirectory()); + if (outputDirectory.existsSync()) { + outputDirectory.deleteSync(recursive: true); + outputDirectory.createSync(recursive: true); + } await injectPlugins(flutterProject, checkProjects: true); final Status status = globals.logger.startProgress('Compiling $target for the Web...', timeout: null); final Stopwatch sw = Stopwatch()..start(); try { final BuildResult result = await globals.buildSystem.build(const WebServiceWorker(), Environment( projectDir: globals.fs.currentDirectory, - outputDir: globals.fs.directory(getWebBuildDirectory()), + outputDir: outputDirectory, buildDir: flutterProject.directory .childDirectory('.dart_tool') .childDirectory('flutter_build'), @@ -53,6 +59,7 @@ Future buildWeb( kDartDefines: encodeDartDefines(buildInfo.dartDefines), kCspMode: csp.toString(), kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(), + kSourceMapsEnabled: sourceMaps.toString(), if (serviceWorkerStrategy != null) kServiceWorkerStrategy: serviceWorkerStrategy, if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false) diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index 8029f5ffc9..038f2af55a 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -57,6 +57,7 @@ void main() { false, false, null, + true, ), throwsToolExit()); }, overrides: { Platform: () => fakePlatform, diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart index 65956510ac..dcc2d8fd1f 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart @@ -305,6 +305,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.profile=true', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -316,6 +317,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.profile=true', + '--no-source-maps', '-O4', '--no-minify', '--csp', @@ -339,6 +341,7 @@ void main() { ...kDart2jsLinuxArgs, '--enable-experiment=non-nullable', '-Ddart.vm.profile=true', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -351,6 +354,7 @@ void main() { ...kDart2jsLinuxArgs, '--enable-experiment=non-nullable', '-Ddart.vm.profile=true', + '--no-source-maps', '-O4', '--no-minify', '-o', @@ -370,6 +374,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.profile=true', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -381,6 +386,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.profile=true', + '--no-source-maps', '-O4', '--no-minify', '-o', @@ -400,6 +406,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.product=true', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -411,6 +418,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.product=true', + '--no-source-maps', '-O4', '-o', environment.buildDir.childFile('main.dart.js').absolute.path, @@ -430,6 +438,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.product=true', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -441,6 +450,7 @@ void main() { command: [ ...kDart2jsLinuxArgs, '-Ddart.vm.product=true', + '--no-source-maps', '-O3', '-o', environment.buildDir.childFile('main.dart.js').absolute.path, @@ -481,6 +491,7 @@ void main() { '-Ddart.vm.product=true', '-DFOO=bar', '-DBAZ=qux', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -494,6 +505,7 @@ void main() { '-Ddart.vm.product=true', '-DFOO=bar', '-DBAZ=qux', + '--no-source-maps', '-O4', '-o', environment.buildDir.childFile('main.dart.js').absolute.path, @@ -506,6 +518,37 @@ void main() { ProcessManager: () => processManager, })); + test('Dart2JSTarget can enable source maps', () => testbed.run(() async { + environment.defines[kBuildMode] = 'release'; + environment.defines[kSourceMapsEnabled] = 'true'; + processManager.addCommand(FakeCommand( + command: [ + ...kDart2jsLinuxArgs, + '-Ddart.vm.product=true', + '-o', + environment.buildDir.childFile('app.dill').absolute.path, + '--packages=.packages', + '--cfe-only', + environment.buildDir.childFile('main.dart').absolute.path, + ] + )); + processManager.addCommand(FakeCommand( + command: [ + ...kDart2jsLinuxArgs, + '-Ddart.vm.product=true', + '-O4', + '-o', + environment.buildDir.childFile('main.dart.js').absolute.path, + environment.buildDir.childFile('app.dill').absolute.path, + ] + )); + + await const Dart2JSTarget().build(environment); + }, overrides: { + ProcessManager: () => processManager, + })); + + test('Dart2JSTarget calls dart2js with Dart defines in profile mode', () => testbed.run(() async { environment.defines[kBuildMode] = 'profile'; environment.defines[kDartDefines] = 'FOO=bar,BAZ=qux'; @@ -515,6 +558,7 @@ void main() { '-Ddart.vm.profile=true', '-DFOO=bar', '-DBAZ=qux', + '--no-source-maps', '-o', environment.buildDir.childFile('app.dill').absolute.path, '--packages=.packages', @@ -528,6 +572,7 @@ void main() { '-Ddart.vm.profile=true', '-DFOO=bar', '-DBAZ=qux', + '--no-source-maps', '-O4', '--no-minify', '-o',