diff --git a/dev/bots/test.dart b/dev/bots/test.dart index e785f40cf5..3de74c6539 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -765,6 +765,16 @@ Future _runWebIntegrationTests() async { '--dart-define=test.valueB=Value', ] ); + await _runWebDebugTest('lib/sound_mode.dart', additionalArguments: [ + '--enable-experiment', + 'non-nullable', + '--sound-null-safety', + ]); + await _runWebReleaseTest('lib/sound_mode.dart', additionalArguments: [ + '--enable-experiment', + 'non-nullable', + '--sound-null-safety', + ]); } Future _runWebStackTraceTest(String buildMode) async { diff --git a/dev/integration_tests/web/analysis_options.yaml b/dev/integration_tests/web/analysis_options.yaml index 8c534b5588..a6c220d3b1 100644 --- a/dev/integration_tests/web/analysis_options.yaml +++ b/dev/integration_tests/web/analysis_options.yaml @@ -3,3 +3,4 @@ analyzer: exclude: - lib/null_safe_main.dart + - lib/sound_mode.dart diff --git a/dev/integration_tests/web/lib/sound_mode.dart b/dev/integration_tests/web/lib/sound_mode.dart new file mode 100644 index 0000000000..a507692f5a --- /dev/null +++ b/dev/integration_tests/web/lib/sound_mode.dart @@ -0,0 +1,24 @@ +// 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. + +// @dart=2.10 + +import 'dart:html' as html; + +// Verify that web applications can be run in sound mode. +void main() { + const isWeak = [] is List; + String output; + if (isWeak) { + output = '--- TEST FAILED ---'; + } else { + output = '--- TEST SUCCEEDED ---'; + } + print(output); + html.HttpRequest.request( + '/test-result', + method: 'POST', + sendData: '$output', + ); +} 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 84b8b360f0..5cf6e09508 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -12,6 +12,7 @@ import '../../artifacts.dart'; import '../../base/file_system.dart'; import '../../base/io.dart'; import '../../build_info.dart'; +import '../../dart/language_version.dart'; import '../../dart/package_map.dart'; import '../../globals.dart' as globals; import '../../project.dart'; @@ -94,6 +95,11 @@ class WebEntrypointTarget extends Target { environment.fileSystem.file(packageFile), logger: environment.logger, ); + final FlutterProject flutterProject = FlutterProject.current(); + final String languageVersion = determineLanguageVersion( + environment.fileSystem.file(targetFile), + packageConfig[flutterProject.manifest.appName], + ) ?? ''; // Use the PackageConfig to find the correct package-scheme import path // for the user application. If the application has a mix of package-scheme @@ -117,6 +123,8 @@ class WebEntrypointTarget extends Target { final String generatedImport = packageConfig.toPackageUri(generatedUri)?.toString() ?? generatedUri.toString(); contents = ''' +$languageVersion + import 'dart:ui' as ui; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -134,6 +142,8 @@ Future main() async { '''; } else { contents = ''' +$languageVersion + import 'dart:ui' as ui; import '$mainImport' as entrypoint; 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 4e0237fa86..65956510ac 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 @@ -72,6 +72,10 @@ void main() { }); test('WebEntrypointTarget generates an entrypoint with plugins and init platform', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; environment.defines[kHasWebPlugins] = 'true'; environment.defines[kInitializePlatform] = 'true'; await const WebEntrypointTarget().build(environment); @@ -144,7 +148,10 @@ void main() { })); test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async { - environment.defines[kTargetFile] = globals.fs.path.join('other', 'lib', 'main.dart'); + final File mainFile = globals.fs.file(globals.fs.path.join('other', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; await const WebEntrypointTarget().build(environment); final String generated = environment.buildDir.childFile('main.dart').readAsStringSync(); @@ -154,7 +161,10 @@ void main() { })); test('WebEntrypointTarget generates a plugin registrant for a file outside of main', () => testbed.run(() async { - environment.defines[kTargetFile] = globals.fs.path.join('other', 'lib', 'main.dart'); + final File mainFile = globals.fs.file(globals.fs.path.join('other', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; environment.defines[kHasWebPlugins] = 'true'; await const WebEntrypointTarget().build(environment); @@ -167,6 +177,11 @@ void main() { test('WebEntrypointTarget generates an entrypoint with plugins and init platform on windows', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; + environment.defines[kHasWebPlugins] = 'true'; environment.defines[kInitializePlatform] = 'true'; await const WebEntrypointTarget().build(environment); @@ -190,6 +205,10 @@ void main() { })); test('WebEntrypointTarget generates an entrypoint without plugins and init platform', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; environment.defines[kHasWebPlugins] = 'false'; environment.defines[kInitializePlatform] = 'true'; await const WebEntrypointTarget().build(environment); @@ -208,6 +227,10 @@ void main() { })); test('WebEntrypointTarget generates an entrypoint with plugins and without init platform', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; environment.defines[kHasWebPlugins] = 'true'; environment.defines[kInitializePlatform] = 'false'; await const WebEntrypointTarget().build(environment); @@ -225,7 +248,39 @@ void main() { expect(generated, contains('entrypoint.main();')); })); + test('WebEntrypointTarget generates an entrypoint with a language version', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('// @dart=2.8\nvoid main() {}'); + environment.defines[kTargetFile] = mainFile.path; + await const WebEntrypointTarget().build(environment); + + final String generated = environment.buildDir.childFile('main.dart').readAsStringSync(); + + // Language version + expect(generated, contains('// @dart=2.8')); + })); + + test('WebEntrypointTarget generates an entrypoint with a language version from a package config', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + globals.fs.file(globals.fs.path.join('pubspec.yaml')) + .writeAsStringSync('name: foo\n'); + environment.defines[kTargetFile] = mainFile.path; + await const WebEntrypointTarget().build(environment); + + final String generated = environment.buildDir.childFile('main.dart').readAsStringSync(); + + // Language version + expect(generated, contains('// @dart = 2.7')); + })); + test('WebEntrypointTarget generates an entrypoint without plugins and without init platform', () => testbed.run(() async { + final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void main() {}'); + environment.defines[kTargetFile] = mainFile.path; environment.defines[kHasWebPlugins] = 'false'; environment.defines[kInitializePlatform] = 'false'; await const WebEntrypointTarget().build(environment);