@@ -15,9 +15,12 @@ import '../../cache.dart';
1515import '../../convert.dart' ;
1616import '../../dart/language_version.dart' ;
1717import '../../dart/package_map.dart' ;
18+ import '../../flutter_plugins.dart' ;
1819import '../../globals.dart' as globals;
1920import '../../project.dart' ;
20- import '../../web/flutter_js.dart' as flutter_js;
21+ import '../../web/file_generators/flutter_js.dart' as flutter_js;
22+ import '../../web/file_generators/flutter_service_worker_js.dart' ;
23+ import '../../web/file_generators/main_dart.dart' as main_dart;
2124import '../build_system.dart' ;
2225import '../depfile.dart' ;
2326import '../exceptions.dart' ;
@@ -50,15 +53,6 @@ const String kSourceMapsEnabled = 'SourceMaps';
5053/// Whether the dart2js native null assertions are enabled.
5154const String kNativeNullAssertions = 'NativeNullAssertions' ;
5255
53- /// The caching strategy for the generated service worker.
54- enum ServiceWorkerStrategy {
55- /// Download the app shell eagerly and all other assets lazily.
56- /// Prefer the offline cached version.
57- offlineFirst,
58- /// Do not generate a service worker,
59- none,
60- }
61-
6256const String kOfflineFirst = 'offline-first' ;
6357const String kNoneWorker = 'none' ;
6458
@@ -97,7 +91,6 @@ class WebEntrypointTarget extends Target {
9791 @override
9892 Future <void > build (Environment environment) async {
9993 final String ? targetFile = environment.defines[kTargetFile];
100- final bool hasWebPlugins = environment.defines[kHasWebPlugins] == 'true' ;
10194 final Uri importUri = environment.fileSystem.file (targetFile).absolute.uri;
10295 // TODO(zanderso): support configuration of this file.
10396 const String packageFile = '.packages' ;
@@ -124,50 +117,15 @@ class WebEntrypointTarget extends Target {
124117 final String importedEntrypoint = packageConfig.toPackageUri (importUri)? .toString ()
125118 ?? importUri.toString ();
126119
127- String ? generatedImport;
128- if (hasWebPlugins) {
129- final Uri generatedUri = environment.projectDir
130- .childDirectory ('lib' )
131- .childFile ('generated_plugin_registrant.dart' )
132- .absolute
133- .uri;
134- generatedImport = packageConfig.toPackageUri (generatedUri)? .toString ()
135- ?? generatedUri.toString ();
136- }
120+ await injectBuildTimePluginFiles (flutterProject, webPlatform: true , destination: environment.buildDir);
121+ // The below works because `injectBuildTimePluginFiles` is configured to write
122+ // the web_plugin_registrant.dart file alongside the generated main.dart
123+ const String generatedImport = 'web_plugin_registrant.dart' ;
137124
138- final String contents = < String > [
139- '// @dart=${languageVersion .major }.${languageVersion .minor }' ,
140- '// Flutter web bootstrap script for $importedEntrypoint .' ,
141- '' ,
142- "import 'dart:ui' as ui;" ,
143- "import 'dart:async';" ,
144- '' ,
145- "import '$importedEntrypoint ' as entrypoint;" ,
146- if (hasWebPlugins)
147- "import 'package:flutter_web_plugins/flutter_web_plugins.dart';" ,
148- if (hasWebPlugins)
149- "import '$generatedImport ';" ,
150- '' ,
151- 'typedef _UnaryFunction = dynamic Function(List<String> args);' ,
152- 'typedef _NullaryFunction = dynamic Function();' ,
153- '' ,
154- 'Future<void> main() async {' ,
155- ' await ui.webOnlyWarmupEngine(' ,
156- ' runApp: () {' ,
157- ' if (entrypoint.main is _UnaryFunction) {' ,
158- ' return (entrypoint.main as _UnaryFunction)(<String>[]);' ,
159- ' }' ,
160- ' return (entrypoint.main as _NullaryFunction)();' ,
161- ' },' ,
162- if (hasWebPlugins) ...< String > [
163- ' registerPlugins: () {' ,
164- ' registerPlugins(webPluginRegistrar);' ,
165- ' },' ,
166- ],
167- ' );' ,
168- '}' ,
169- '' ,
170- ].join ('\n ' );
125+ final String contents = main_dart.generateMainDartFile (importedEntrypoint,
126+ languageVersion: languageVersion,
127+ pluginRegistrantEntrypoint: generatedImport,
128+ );
171129
172130 environment.buildDir.childFile ('main.dart' )
173131 .writeAsStringSync (contents);
@@ -547,7 +505,6 @@ class WebServiceWorker extends Target {
547505 < String > [
548506 'main.dart.js' ,
549507 'index.html' ,
550- 'assets/NOTICES' ,
551508 if (urlToHash.containsKey ('assets/AssetManifest.json' ))
552509 'assets/AssetManifest.json' ,
553510 if (urlToHash.containsKey ('assets/FontManifest.json' ))
@@ -567,194 +524,3 @@ class WebServiceWorker extends Target {
567524 );
568525 }
569526}
570-
571- /// Generate a service worker with an app-specific cache name a map of
572- /// resource files.
573- ///
574- /// The tool embeds file hashes directly into the worker so that the byte for byte
575- /// invalidation will automatically reactivate workers whenever a new
576- /// version is deployed.
577- String generateServiceWorker (
578- Map <String , String > resources,
579- List <String > coreBundle, {
580- required ServiceWorkerStrategy serviceWorkerStrategy,
581- }) {
582- if (serviceWorkerStrategy == ServiceWorkerStrategy .none) {
583- return '' ;
584- }
585- return '''
586- 'use strict';
587- const MANIFEST = 'flutter-app-manifest';
588- const TEMP = 'flutter-temp-cache';
589- const CACHE_NAME = 'flutter-app-cache';
590- const RESOURCES = {
591- ${resources .entries .map ((MapEntry <String , String > entry ) => '"${entry .key }": "${entry .value }"' ).join (",\n " )}
592- };
593-
594- // The application shell files that are downloaded before a service worker can
595- // start.
596- const CORE = [
597- ${coreBundle .map ((String file ) => '"$file "' ).join (',\n ' )}];
598- // During install, the TEMP cache is populated with the application shell files.
599- self.addEventListener("install", (event) => {
600- self.skipWaiting();
601- return event.waitUntil(
602- caches.open(TEMP).then((cache) => {
603- return cache.addAll(
604- CORE.map((value) => new Request(value, {'cache': 'reload'})));
605- })
606- );
607- });
608-
609- // During activate, the cache is populated with the temp files downloaded in
610- // install. If this service worker is upgrading from one with a saved
611- // MANIFEST, then use this to retain unchanged resource files.
612- self.addEventListener("activate", function(event) {
613- return event.waitUntil(async function() {
614- try {
615- var contentCache = await caches.open(CACHE_NAME);
616- var tempCache = await caches.open(TEMP);
617- var manifestCache = await caches.open(MANIFEST);
618- var manifest = await manifestCache.match('manifest');
619- // When there is no prior manifest, clear the entire cache.
620- if (!manifest) {
621- await caches.delete(CACHE_NAME);
622- contentCache = await caches.open(CACHE_NAME);
623- for (var request of await tempCache.keys()) {
624- var response = await tempCache.match(request);
625- await contentCache.put(request, response);
626- }
627- await caches.delete(TEMP);
628- // Save the manifest to make future upgrades efficient.
629- await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
630- return;
631- }
632- var oldManifest = await manifest.json();
633- var origin = self.location.origin;
634- for (var request of await contentCache.keys()) {
635- var key = request.url.substring(origin.length + 1);
636- if (key == "") {
637- key = "/";
638- }
639- // If a resource from the old manifest is not in the new cache, or if
640- // the MD5 sum has changed, delete it. Otherwise the resource is left
641- // in the cache and can be reused by the new service worker.
642- if (!RESOURCES[key] || RESOURCES[key] != oldManifest[key]) {
643- await contentCache.delete(request);
644- }
645- }
646- // Populate the cache with the app shell TEMP files, potentially overwriting
647- // cache files preserved above.
648- for (var request of await tempCache.keys()) {
649- var response = await tempCache.match(request);
650- await contentCache.put(request, response);
651- }
652- await caches.delete(TEMP);
653- // Save the manifest to make future upgrades efficient.
654- await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
655- return;
656- } catch (err) {
657- // On an unhandled exception the state of the cache cannot be guaranteed.
658- console.error('Failed to upgrade service worker: ' + err);
659- await caches.delete(CACHE_NAME);
660- await caches.delete(TEMP);
661- await caches.delete(MANIFEST);
662- }
663- }());
664- });
665-
666- // The fetch handler redirects requests for RESOURCE files to the service
667- // worker cache.
668- self.addEventListener("fetch", (event) => {
669- if (event.request.method !== 'GET') {
670- return;
671- }
672- var origin = self.location.origin;
673- var key = event.request.url.substring(origin.length + 1);
674- // Redirect URLs to the index.html
675- if (key.indexOf('?v=') != -1) {
676- key = key.split('?v=')[0];
677- }
678- if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
679- key = '/';
680- }
681- // If the URL is not the RESOURCE list then return to signal that the
682- // browser should take over.
683- if (!RESOURCES[key]) {
684- return;
685- }
686- // If the URL is the index.html, perform an online-first request.
687- if (key == '/') {
688- return onlineFirst(event);
689- }
690- event.respondWith(caches.open(CACHE_NAME)
691- .then((cache) => {
692- return cache.match(event.request).then((response) => {
693- // Either respond with the cached resource, or perform a fetch and
694- // lazily populate the cache.
695- return response || fetch(event.request).then((response) => {
696- cache.put(event.request, response.clone());
697- return response;
698- });
699- })
700- })
701- );
702- });
703-
704- self.addEventListener('message', (event) => {
705- // SkipWaiting can be used to immediately activate a waiting service worker.
706- // This will also require a page refresh triggered by the main worker.
707- if (event.data === 'skipWaiting') {
708- self.skipWaiting();
709- return;
710- }
711- if (event.data === 'downloadOffline') {
712- downloadOffline();
713- return;
714- }
715- });
716-
717- // Download offline will check the RESOURCES for all files not in the cache
718- // and populate them.
719- async function downloadOffline() {
720- var resources = [];
721- var contentCache = await caches.open(CACHE_NAME);
722- var currentContent = {};
723- for (var request of await contentCache.keys()) {
724- var key = request.url.substring(origin.length + 1);
725- if (key == "") {
726- key = "/";
727- }
728- currentContent[key] = true;
729- }
730- for (var resourceKey of Object.keys(RESOURCES)) {
731- if (!currentContent[resourceKey]) {
732- resources.push(resourceKey);
733- }
734- }
735- return contentCache.addAll(resources);
736- }
737-
738- // Attempt to download the resource online before falling back to
739- // the offline cache.
740- function onlineFirst(event) {
741- return event.respondWith(
742- fetch(event.request).then((response) => {
743- return caches.open(CACHE_NAME).then((cache) => {
744- cache.put(event.request, response.clone());
745- return response;
746- });
747- }).catch((error) => {
748- return caches.open(CACHE_NAME).then((cache) => {
749- return cache.match(event.request).then((response) => {
750- if (response != null) {
751- return response;
752- }
753- throw error;
754- });
755- });
756- })
757- );
758- }
759- ''' ;
760- }
0 commit comments