Unverified Commit 0fb4406c authored by Kevin Chisholm's avatar Kevin Chisholm Committed by GitHub

Revert "[web] Move JS content to its own `.js` files (#117691)" (#120275)

This reverts commit e03029ef.
parent ef854a3d
......@@ -9,7 +9,6 @@ import 'base/common.dart';
import 'base/file_system.dart';
import 'base/os.dart';
import 'base/platform.dart';
import 'base/user_messages.dart';
import 'base/utils.dart';
import 'build_info.dart';
import 'cache.dart';
......@@ -63,9 +62,6 @@ enum Artifact {
/// Tools related to subsetting or icon font files.
fontSubset,
constFinder,
/// The location of file generators.
flutterToolsFileGenerators,
}
/// A subset of [Artifact]s that are platform and build mode independent
......@@ -206,8 +202,6 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'font-subset$exe';
case Artifact.constFinder:
return 'const_finder.dart.snapshot';
case Artifact.flutterToolsFileGenerators:
return '';
}
}
......@@ -531,8 +525,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
return _getHostArtifactPath(artifact, platform, mode);
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
}
}
......@@ -570,8 +562,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
return _getHostArtifactPath(artifact, platform, mode);
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
}
}
......@@ -621,8 +611,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
return _getHostArtifactPath(artifact, platform, mode);
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
}
}
......@@ -697,8 +685,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.fuchsiaFlutterRunner:
case Artifact.fuchsiaKernelCompiler:
throw StateError('Artifact $artifact not available for platform $platform.');
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
}
}
......@@ -966,8 +952,6 @@ class CachedLocalEngineArtifacts implements Artifacts {
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName);
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
}
}
......@@ -1115,7 +1099,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case Artifact.fuchsiaFlutterRunner:
case Artifact.fontSubset:
case Artifact.constFinder:
case Artifact.flutterToolsFileGenerators:
break;
}
}
......@@ -1315,11 +1298,6 @@ class _TestArtifacts implements Artifacts {
BuildMode? mode,
EnvironmentType? environmentType,
}) {
// The path to file generators is the same even in the test environment.
if (artifact == Artifact.flutterToolsFileGenerators) {
return _getFileGeneratorsPath();
}
final StringBuffer buffer = StringBuffer();
buffer.write(artifact);
if (platform != null) {
......@@ -1362,20 +1340,3 @@ class _TestLocalEngine extends _TestArtifacts {
@override
final LocalEngineInfo localEngineInfo;
}
String _getFileGeneratorsPath() {
final String flutterRoot = Cache.defaultFlutterRoot(
fileSystem: globals.localFileSystem,
platform: const LocalPlatform(),
userMessages: UserMessages(),
);
return globals.localFileSystem.path.join(
flutterRoot,
'packages',
'flutter_tools',
'lib',
'src',
'web',
'file_generators',
);
}
......@@ -529,10 +529,7 @@ class WebBuiltInAssets extends Target {
// Write the flutter.js file
final File flutterJsFile = environment.outputDir.childFile('flutter.js');
final String fileGeneratorsPath =
globals.artifacts!.getArtifactPath(Artifact.flutterToolsFileGenerators);
flutterJsFile.writeAsStringSync(
flutter_js.generateFlutterJsFile(fileGeneratorsPath));
flutterJsFile.writeAsStringSync(flutter_js.generateFlutterJsFile());
}
}
......@@ -601,10 +598,7 @@ class WebServiceWorker extends Target {
final ServiceWorkerStrategy serviceWorkerStrategy = _serviceWorkerStrategyFromString(
environment.defines[kServiceWorkerStrategy],
);
final String fileGeneratorsPath =
globals.artifacts!.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String serviceWorker = generateServiceWorker(
fileGeneratorsPath,
urlToHash,
<String>[
'main.dart.js',
......
......@@ -828,10 +828,7 @@ class WebDevFS implements DevFS {
'stack_trace_mapper.js', stackTraceMapper.readAsBytesSync());
webAssetServer.writeFile(
'manifest.json', '{"info":"manifest not generated in run mode."}');
final String fileGeneratorsPath = globals.artifacts!
.getArtifactPath(Artifact.flutterToolsFileGenerators);
webAssetServer.writeFile(
'flutter.js', flutter_js.generateFlutterJsFile(fileGeneratorsPath));
webAssetServer.writeFile('flutter.js', flutter_js.generateFlutterJsFile());
webAssetServer.writeFile('flutter_service_worker.js',
'// Service worker not loaded in run mode.');
webAssetServer.writeFile(
......
......@@ -2,14 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../globals.dart' as globals;
/// The caching strategy for the generated service worker.
enum ServiceWorkerStrategy {
/// Download the app shell eagerly and all other assets lazily.
/// Prefer the offline cached version.
offlineFirst,
/// Do not generate a service worker,
none,
}
......@@ -21,7 +18,6 @@ enum ServiceWorkerStrategy {
/// invalidation will automatically reactivate workers whenever a new
/// version is deployed.
String generateServiceWorker(
String fileGeneratorsPath,
Map<String, String> resources,
List<String> coreBundle, {
required ServiceWorkerStrategy serviceWorkerStrategy,
......@@ -29,21 +25,185 @@ String generateServiceWorker(
if (serviceWorkerStrategy == ServiceWorkerStrategy.none) {
return '';
}
return '''
'use strict';
const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
${resources.entries.map((MapEntry<String, String> entry) => '"${entry.key}": "${entry.value}"').join(",\n")}
};
// The application shell files that are downloaded before a service worker can
// start.
const CORE = [
${coreBundle.map((String file) => '"$file"').join(',\n')}];
// During install, the TEMP cache is populated with the application shell files.
self.addEventListener("install", (event) => {
self.skipWaiting();
return event.waitUntil(
caches.open(TEMP).then((cache) => {
return cache.addAll(
CORE.map((value) => new Request(value, {'cache': 'reload'})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self.addEventListener("activate", function(event) {
return event.waitUntil(async function() {
try {
var contentCache = await caches.open(CACHE_NAME);
var tempCache = await caches.open(TEMP);
var manifestCache = await caches.open(MANIFEST);
var manifest = await manifestCache.match('manifest');
// When there is no prior manifest, clear the entire cache.
if (!manifest) {
await caches.delete(CACHE_NAME);
contentCache = await caches.open(CACHE_NAME);
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
// Claim client to enable caching on first launch
self.clients.claim();
return;
}
var oldManifest = await manifest.json();
var origin = self.location.origin;
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if (!RESOURCES[key] || RESOURCES[key] != oldManifest[key]) {
await contentCache.delete(request);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
// Claim client to enable caching on first launch
self.clients.claim();
return;
} catch (err) {
// On an unhandled exception the state of the cache cannot be guaranteed.
console.error('Failed to upgrade service worker: ' + err);
await caches.delete(CACHE_NAME);
await caches.delete(TEMP);
await caches.delete(MANIFEST);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self.addEventListener("fetch", (event) => {
if (event.request.method !== 'GET') {
return;
}
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list then return to signal that the
// browser should take over.
if (!RESOURCES[key]) {
return;
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache only if the resource was successfully fetched.
return response || fetch(event.request).then((response) => {
if (response && Boolean(response.ok)) {
cache.put(event.request, response.clone());
}
return response;
});
})
})
);
});
self.addEventListener('message', (event) => {
// SkipWaiting can be used to immediately activate a waiting service worker.
// This will also require a page refresh triggered by the main worker.
if (event.data === 'skipWaiting') {
self.skipWaiting();
return;
}
if (event.data === 'downloadOffline') {
downloadOffline();
return;
}
});
final String flutterServiceWorkerJsPath = globals.localFileSystem.path.join(
fileGeneratorsPath,
'js',
'flutter_service_worker.js',
// Download offline will check the RESOURCES for all files not in the cache
// and populate them.
async function downloadOffline() {
var resources = [];
var contentCache = await caches.open(CACHE_NAME);
var currentContent = {};
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
currentContent[key] = true;
}
for (var resourceKey of Object.keys(RESOURCES)) {
if (!currentContent[resourceKey]) {
resources.push(resourceKey);
}
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
}
throw error;
});
});
})
);
return globals.localFileSystem
.file(flutterServiceWorkerJsPath)
.readAsStringSync()
.replaceAll(
r'$$RESOURCES_MAP',
'{${resources.entries.map((MapEntry<String, String> entry) => '"${entry.key}": "${entry.value}"').join(",\n")}}',
)
.replaceAll(
r'$$CORE_LIST',
'[${coreBundle.map((String file) => '"$file"').join(',\n')}]',
);
}
''';
}
'use strict';
const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = $$RESOURCES_MAP;
// The application shell files that are downloaded before a service worker can
// start.
const CORE = $$CORE_LIST;
// During install, the TEMP cache is populated with the application shell files.
self.addEventListener("install", (event) => {
self.skipWaiting();
return event.waitUntil(
caches.open(TEMP).then((cache) => {
return cache.addAll(
CORE.map((value) => new Request(value, {'cache': 'reload'})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self.addEventListener("activate", function(event) {
return event.waitUntil(async function() {
try {
var contentCache = await caches.open(CACHE_NAME);
var tempCache = await caches.open(TEMP);
var manifestCache = await caches.open(MANIFEST);
var manifest = await manifestCache.match('manifest');
// When there is no prior manifest, clear the entire cache.
if (!manifest) {
await caches.delete(CACHE_NAME);
contentCache = await caches.open(CACHE_NAME);
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
// Claim client to enable caching on first launch
self.clients.claim();
return;
}
var oldManifest = await manifest.json();
var origin = self.location.origin;
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if (!RESOURCES[key] || RESOURCES[key] != oldManifest[key]) {
await contentCache.delete(request);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
// Claim client to enable caching on first launch
self.clients.claim();
return;
} catch (err) {
// On an unhandled exception the state of the cache cannot be guaranteed.
console.error('Failed to upgrade service worker: ' + err);
await caches.delete(CACHE_NAME);
await caches.delete(TEMP);
await caches.delete(MANIFEST);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self.addEventListener("fetch", (event) => {
if (event.request.method !== 'GET') {
return;
}
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list then return to signal that the
// browser should take over.
if (!RESOURCES[key]) {
return;
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache only if the resource was successfully fetched.
return response || fetch(event.request).then((response) => {
if (response && Boolean(response.ok)) {
cache.put(event.request, response.clone());
}
return response;
});
})
})
);
});
self.addEventListener('message', (event) => {
// SkipWaiting can be used to immediately activate a waiting service worker.
// This will also require a page refresh triggered by the main worker.
if (event.data === 'skipWaiting') {
self.skipWaiting();
return;
}
if (event.data === 'downloadOffline') {
downloadOffline();
return;
}
});
// Download offline will check the RESOURCES for all files not in the cache
// and populate them.
async function downloadOffline() {
var resources = [];
var contentCache = await caches.open(CACHE_NAME);
var currentContent = {};
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
currentContent[key] = true;
}
for (var resourceKey of Object.keys(RESOURCES)) {
if (!currentContent[resourceKey]) {
resources.push(resourceKey);
}
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
}
throw error;
});
});
})
);
}
......@@ -785,40 +785,19 @@ void main() {
}));
test('Generated service worker is empty with none-strategy', () {
final String fileGeneratorsPath =
environment.artifacts.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String result = generateServiceWorker(
fileGeneratorsPath,
<String, String>{'/foo': 'abcd'},
<String>[],
serviceWorkerStrategy: ServiceWorkerStrategy.none,
);
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.none);
expect(result, '');
});
test('Generated service worker correctly inlines file hashes', () {
final String fileGeneratorsPath =
environment.artifacts.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String result = generateServiceWorker(
fileGeneratorsPath,
<String, String>{'/foo': 'abcd'},
<String>[],
serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst,
);
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
expect(result, contains('{"/foo": "abcd"};'));
expect(result, contains('{\n "/foo": "abcd"\n};'));
});
test('Generated service worker includes core files', () {
final String fileGeneratorsPath =
environment.artifacts.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String result = generateServiceWorker(
fileGeneratorsPath,
<String, String>{'/foo': 'abcd'},
<String>['foo', 'bar'],
serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst,
);
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>['foo', 'bar'], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
expect(result, contains('"foo",\n"bar"'));
});
......@@ -875,10 +854,7 @@ void main() {
}));
test('flutter.js sanity checks', () {
final String fileGeneratorsPath = environment.artifacts
.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String flutterJsContents =
flutter_js.generateFlutterJsFile(fileGeneratorsPath);
final String flutterJsContents = flutter_js.generateFlutterJsFile();
expect(flutterJsContents, contains('"use strict";'));
expect(flutterJsContents, contains('main.dart.js'));
expect(flutterJsContents, contains('flutter_service_worker.js?v='));
......@@ -897,14 +873,8 @@ void main() {
await WebBuiltInAssets(globals.fs, globals.cache, false).build(environment);
// No caching of source maps.
final String fileGeneratorsPath = environment.artifacts
.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String flutterJsContents =
flutter_js.generateFlutterJsFile(fileGeneratorsPath);
expect(
environment.outputDir.childFile('flutter.js').readAsStringSync(),
equals(flutterJsContents),
);
expect(environment.outputDir.childFile('flutter.js').readAsStringSync(),
equals(flutter_js.generateFlutterJsFile()));
}));
test('wasm build copies and generates specific files', () => testbed.run(() async {
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'package:file/file.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/web/file_generators/flutter_js.dart';
import '../test_utils.dart';
......@@ -60,13 +59,9 @@ abstract class Project {
}
deferredComponents?.setUpIn(dir);
final String fileGeneratorsPath =
Artifacts.test().getArtifactPath(Artifact.flutterToolsFileGenerators);
final String flutterJsContents = generateFlutterJsFile(fileGeneratorsPath);
// Setup for different flutter web initializations
writeFile(fileSystem.path.join(dir.path, 'web', 'index.html'), indexHtml);
writeFile(fileSystem.path.join(dir.path, 'web', 'flutter.js'), flutterJsContents);
writeFile(fileSystem.path.join(dir.path, 'web', 'flutter.js'), generateFlutterJsFile());
writeFile(fileSystem.path.join(dir.path, 'web', 'flutter_service_worker.js'), '');
writePackages(dir.path);
await getPackages(dir.path);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment