Srujan Gaddam 0480b069e0
[flutter_tools] Use DWDS 25.0.1 (#173777)
DWDS 25.0.1 requires that a valid reloadedSourcesUri is passed and
updated for both a hot restart and a hot reload. Therefore, the
bootstrap scripts which use it as well as the code to write that file is
updated. Note that this file is read in both DWDS and in the bootstrap
script. Along with this, code is fixed to update modules and digests
regardless of a full restart. Currently, it makes no difference as
neither Flutter tools or DWDS makes use of the updated modules or
digests with the new library bundle format, but it's updated for
consistency.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
2025-08-18 19:16:21 +00:00

327 lines
11 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 'package:flutter_tools/src/web/bootstrap.dart';
import 'package:package_config/package_config.dart';
import '../../src/common.dart';
void main() {
test('generateBootstrapScript embeds urls correctly', () {
final String result = generateBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
// require js source is interpolated correctly.
expect(result, contains('"requireJs": "require.js"'));
expect(result, contains('requireEl.src = getTTScriptUrl("requireJs");'));
// stack trace mapper source is interpolated correctly.
expect(result, contains('"mapper": "mapper.js"'));
expect(result, contains('mapperEl.src = getTTScriptUrl("mapper");'));
// data-main is set to correct bootstrap module.
expect(result, contains('requireEl.setAttribute("data-main", "main_module.bootstrap");'));
});
test('generateBootstrapScript includes loading indicator', () {
final String result = generateBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
expect(result, contains('"flutter-loader"'));
expect(result, contains('"indeterminate"'));
});
test('generateBootstrapScript does not include loading indicator', () {
final String result = generateBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: false,
);
expect(result, isNot(contains('"flutter-loader"')));
expect(result, isNot(contains('"indeterminate"')));
});
// https://github.com/flutter/flutter/issues/107742
test('generateBootstrapScript loading indicator does not trigger scrollbars', () {
final String result = generateBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
);
// See: https://regexr.com/6q0ft
final regex = RegExp(r'(?:\.flutter-loader\s*\{)[^}]+(?:overflow\:\s*hidden;)[^}]+}');
expect(result, matches(regex), reason: '.flutter-loader must have overflow: hidden');
});
// https://github.com/flutter/flutter/issues/82524
test('generateMainModule removes timeout from requireJS', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
);
// See: https://regexr.com/6q0kp
final regex = RegExp(
r'(?:require\.config\(\{)(?:.|\s(?!\}\);))*'
r'(?:waitSeconds\:\s*0[,]?)'
r'(?:(?!\}\);).|\s)*\}\);',
);
expect(
result,
matches(regex),
reason: 'require.config must have a waitSeconds: 0 config entry',
);
});
test('generateMainModule hides requireJS injected by DDC', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
);
expect(
result,
contains('''define._amd = define.amd;'''),
reason: 'define.amd must be preserved as _amd so users may restore it if needed.',
);
expect(
result,
contains('''delete define.amd;'''),
reason: "define.amd must be removed so packages don't attempt to use Dart's instance.",
);
});
test('generateMainModule embeds urls correctly', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
);
// bootstrap main module has correct defined module.
expect(
result,
contains(
'define("main_module.bootstrap", ["foo/bar/main.js", "dart_sdk"], '
'function(app, dart_sdk) {',
),
);
});
test('generateMainModule can set bootstrap name', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
bootstrapModule: 'foo_module.bootstrap',
);
// bootstrap main module has correct defined module.
expect(
result,
contains(
'define("foo_module.bootstrap", ["foo/bar/main.js", "dart_sdk"], '
'function(app, dart_sdk) {',
),
);
});
test('generateMainModule includes null safety switches', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: true,
);
expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(true);'''));
});
test('generateMainModule can disable null safety switches', () {
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
);
expect(result, contains('''dart_sdk.dart.nativeNonNullAsserts(false);'''));
});
test('generateMainModule sets rootDirectories', () {
const root = 'http://localhost:12345';
final String result = generateMainModule(
entrypoint: 'foo/bar/main.js',
nativeNullAssertions: false,
loaderRootDirectory: root,
);
expect(result, contains('''window.\$dartLoader.rootDirectories = ["$root"];'''));
});
test('generateTestBootstrapFileContents embeds urls correctly', () {
final String result = generateTestBootstrapFileContents(
'foo.dart.js',
'require.js',
'mapper.js',
);
expect(result, contains('el.setAttribute("data-main", \'foo.dart.js\');'));
});
test('generateTestEntrypoint generates proper imports and mappings for tests', () {
final String result = generateTestEntrypoint(
testInfos: <WebTestInfo>[
(entryPoint: 'foo.dart', goldensUri: Uri.parse('foo.dart'), configFile: null),
(entryPoint: 'bar.dart', goldensUri: Uri.parse('bar.dart'), configFile: 'bar_config.dart'),
],
languageVersion: LanguageVersion(2, 8),
);
expect(result, contains("import 'org-dartlang-app:///foo.dart'"));
expect(result, contains("import 'org-dartlang-app:///bar.dart'"));
expect(result, contains("import 'org-dartlang-app:///bar_config.dart'"));
});
group('Using the DDC library bundle module system', () {
test('bootstrap script embeds urls correctly', () {
final String result = generateDDCLibraryBundleBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
isWindows: false,
);
// ddc module loader js source is interpolated correctly.
expect(result, contains('"src": "ddc_module_loader.js"'));
// stack trace mapper source is interpolated correctly.
expect(result, contains('"src": "mapper.js"'));
// data-main is set to correct bootstrap module.
expect(result, contains('"src": "main_module.bootstrap.js"'));
expect(result, contains('"id": "data-main"'));
});
test('bootstrap script initializes configuration objects', () {
final String result = generateDDCLibraryBundleBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
isWindows: false,
);
// LoadConfiguration and DDCLoader objects must be constructed.
expect(result, contains(r'new window.$dartLoader.LoadConfiguration('));
expect(result, contains(r'new window.$dartLoader.DDCLoader('));
// Specific fields must be set on the LoadConfiguration.
expect(result, contains('.bootstrapScript ='));
expect(result, contains('.loadScriptFn ='));
// DDCLoader.nextAttempt must be invoked to begin loading.
expect(result, contains('nextAttempt()'));
// Proper window objects are initialized.
expect(result, contains(r'window.$dartLoader.loadConfig ='));
expect(result, contains(r'window.$dartLoader.loader ='));
});
test('bootstrap script includes loading indicator', () {
final String result = generateDDCLibraryBundleBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
isWindows: false,
);
expect(result, contains('"flutter-loader"'));
expect(result, contains('"indeterminate"'));
});
test('bootstrap script does not include loading indicator', () {
final String result = generateDDCLibraryBundleBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: false,
isWindows: false,
);
expect(result, isNot(contains('"flutter-loader"')));
expect(result, isNot(contains('"indeterminate"')));
});
// https://github.com/flutter/flutter/issues/107742
test('bootstrap script loading indicator does not trigger scrollbars', () {
final String result = generateDDCLibraryBundleBootstrapScript(
entrypoint: 'foo/bar/main.js',
ddcModuleLoaderUrl: 'ddc_module_loader.js',
mapperUrl: 'mapper.js',
generateLoadingIndicator: true,
isWindows: false,
);
// See: https://regexr.com/6q0ft
final regex = RegExp(r'(?:\.flutter-loader\s*\{)[^}]+(?:overflow\:\s*hidden;)[^}]+}');
expect(result, matches(regex), reason: '.flutter-loader must have overflow: hidden');
});
test('generateDDCLibraryBundleMainModule embeds the entrypoint correctly', () {
final String result = generateDDCLibraryBundleMainModule(
entrypoint: 'main.js',
nativeNullAssertions: false,
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
isCi: true,
);
// bootstrap main module has correct defined module.
expect(result, contains('const appName = "org-dartlang-app:/main.js";'));
expect(result, contains('dartDevEmbedder.runMain(appName, sdkOptions);'));
});
test('generateDDCLibraryBundleMainModule includes null safety switches', () {
final String result = generateDDCLibraryBundleMainModule(
entrypoint: 'main.js',
nativeNullAssertions: true,
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
isCi: true,
);
expect(result, contains('nativeNonNullAsserts: true'));
});
test('generateDDCLibraryBundleMainModule can disable null safety switches', () {
final String result = generateDDCLibraryBundleMainModule(
entrypoint: 'main.js',
nativeNullAssertions: false,
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
isCi: true,
);
expect(result, contains('nativeNonNullAsserts: false'));
});
test('generateDDCLibraryBundleMainModule sets max requests when isCi only', () {
String result = generateDDCLibraryBundleMainModule(
entrypoint: 'main.js',
nativeNullAssertions: false,
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
isCi: true,
);
expect(result, contains('maxRequestPoolSize ='));
result = generateDDCLibraryBundleMainModule(
entrypoint: 'main.js',
nativeNullAssertions: false,
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
isCi: false,
);
expect(result, isNot(contains('maxRequestPoolSize =')));
});
test('generateTestBootstrapFileContents embeds urls correctly', () {
final String result = generateTestBootstrapFileContents(
'foo.dart.js',
'require.js',
'mapper.js',
);
expect(result, contains('el.setAttribute("data-main", \'foo.dart.js\');'));
});
});
}