Unverified Commit 418681d6 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] allow disabling pwa from build command, fix run release build caching (#64587)

Fix run release/profile modes generating a full service worker.
parent 0699c18e
......@@ -21,6 +21,7 @@ import '../base/net.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../build_system/targets/web.dart';
import '../cache.dart';
import '../dart/language_version.dart';
import '../dart/pub.dart';
......@@ -488,6 +489,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
debuggingOptions.buildInfo,
debuggingOptions.initializePlatform,
false,
kNoneWorker,
);
}
await device.device.startApp(
......@@ -557,6 +559,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
debuggingOptions.buildInfo,
debuggingOptions.initializePlatform,
false,
kNoneWorker,
);
} on ToolExit {
return OperationResult(1, 'Failed to recompile application.');
......
......@@ -5,6 +5,7 @@
import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import '../../artifacts.dart';
......@@ -33,6 +34,32 @@ const String kDart2jsOptimization = 'Dart2jsOptimization';
/// Whether to disable dynamic generation code to satisfy csp policies.
const String kCspMode = 'cspMode';
/// The caching strategy to use for service worker generation.
const String kServiceWorkerStrategy = 'ServiceWorkerStratgey';
/// 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,
}
const String kOfflineFirst = 'offline-first';
const String kNoneWorker = 'none';
/// Convert a [value] into a [ServiceWorkerStrategy].
ServiceWorkerStrategy _serviceWorkerStrategyfromString(String value) {
switch (value) {
case kNoneWorker:
return ServiceWorkerStrategy.none;
// offline-first is the default value for any invalid requests.
default:
return ServiceWorkerStrategy.offlineFirst;
}
}
/// Generates an entry point for a web target.
// Keep this in sync with build_runner/resident_web_runner.dart
class WebEntrypointTarget extends Target {
......@@ -384,16 +411,23 @@ class WebServiceWorker extends Target {
final File serviceWorkerFile = environment.outputDir
.childFile('flutter_service_worker.js');
final Depfile depfile = Depfile(contents, <File>[serviceWorkerFile]);
final String serviceWorker = generateServiceWorker(urlToHash, <String>[
'/',
'main.dart.js',
'index.html',
'assets/NOTICES',
if (urlToHash.containsKey('assets/AssetManifest.json'))
'assets/AssetManifest.json',
if (urlToHash.containsKey('assets/FontManifest.json'))
'assets/FontManifest.json',
]);
final ServiceWorkerStrategy serviceWorkerStrategy = _serviceWorkerStrategyfromString(
environment.defines[kServiceWorkerStrategy],
);
final String serviceWorker = generateServiceWorker(
urlToHash,
<String>[
'/',
'main.dart.js',
'index.html',
'assets/NOTICES',
if (urlToHash.containsKey('assets/AssetManifest.json'))
'assets/AssetManifest.json',
if (urlToHash.containsKey('assets/FontManifest.json'))
'assets/FontManifest.json',
],
serviceWorkerStrategy: serviceWorkerStrategy,
);
serviceWorkerFile
.writeAsStringSync(serviceWorker);
final DepfileService depfileService = DepfileService(
......@@ -413,7 +447,14 @@ class WebServiceWorker extends Target {
/// The tool embeds file hashes directly into the worker so that the byte for byte
/// invalidation will automatically reactivate workers whenever a new
/// version is deployed.
String generateServiceWorker(Map<String, String> resources, List<String> coreBundle) {
String generateServiceWorker(
Map<String, String> resources,
List<String> coreBundle, {
@required ServiceWorkerStrategy serviceWorkerStrategy,
}) {
if (serviceWorkerStrategy == ServiceWorkerStrategy.none) {
return '';
}
return '''
'use strict';
const MANIFEST = 'flutter-app-manifest';
......@@ -427,7 +468,6 @@ const RESOURCES = {
// 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) => {
return event.waitUntil(
......@@ -448,7 +488,6 @@ self.addEventListener("activate", function(event) {
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);
......@@ -462,7 +501,6 @@ self.addEventListener("activate", function(event) {
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
return;
}
var oldManifest = await manifest.json();
var origin = self.location.origin;
for (var request of await contentCache.keys()) {
......@@ -537,7 +575,6 @@ self.addEventListener('message', (event) => {
if (event.data === 'skipWaiting') {
return self.skipWaiting();
}
if (event.message === 'downloadOffline') {
downloadOffline();
}
......
......@@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
import '../base/common.dart';
import '../build_info.dart';
import '../build_system/targets/web.dart';
import '../features.dart';
import '../project.dart';
import '../runner/flutter_command.dart'
......@@ -38,6 +39,22 @@ 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.addOption('pwa-strategy',
defaultsTo: kOfflineFirst,
help:
'The caching strategy to be used by the PWA service worker.\n'
'offline-first will attempt to cache the app shell eagerly and '
'then lazily cache all subsequent assets as they are loaded. When '
'making a network request for an asset, the offline cache will be '
'preferred.\n'
'none will generate a service worker with no body. This is useful for '
'local testing or in cases where the service worker caching functionality '
'is not desirable',
allowed: <String>[
kOfflineFirst,
kNoneWorker,
]
);
}
@override
......@@ -72,6 +89,7 @@ class BuildWebCommand extends BuildSubCommand {
buildInfo,
boolArg('web-initialize-platform'),
boolArg('csp'),
stringArg('pwa-strategy'),
);
return FlutterCommandResult.success();
}
......
......@@ -28,6 +28,7 @@ Future<void> buildWeb(
BuildInfo buildInfo,
bool initializePlatform,
bool csp,
String serviceWorkerStrategy,
) async {
if (!flutterProject.web.existsSync()) {
throwToolExit('Missing index.html.');
......@@ -52,6 +53,8 @@ Future<void> buildWeb(
kDartDefines: encodeDartDefines(buildInfo.dartDefines),
kCspMode: csp.toString(),
kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
if (serviceWorkerStrategy != null)
kServiceWorkerStrategy: serviceWorkerStrategy,
if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false)
kExtraFrontEndOptions: encodeDartDefines(buildInfo.extraFrontEndOptions),
},
......
......@@ -56,6 +56,7 @@ void main() {
BuildInfo.debug,
false,
false,
null,
), throwsToolExit());
}, overrides: <Type, Generator>{
Platform: () => fakePlatform,
......
......@@ -475,14 +475,20 @@ void main() {
ProcessManager: () => processManager,
}));
test('Generated service worker is empty with none-strategy', () {
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.none);
expect(result, '');
});
test('Generated service worker correctly inlines file hashes', () {
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[]);
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
expect(result, contains('{\n "/foo": "abcd"\n};'));
});
test('Generated service worker includes core files', () {
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>['foo', 'bar']);
final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>['foo', 'bar'], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
expect(result, contains('"foo",\n"bar"'));
});
......
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