UNPKG

10 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt
7 * The complete set of authors may be found at
8 * http://polymer.github.io/AUTHORS.txt
9 * The complete set of contributors may be found at
10 * http://polymer.github.io/CONTRIBUTORS.txt
11 * Code distributed by Google as part of the polymer project is also
12 * subject to an additional IP rights grant found at
13 * http://polymer.github.io/PATENTS.txt
14 */
15var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16 return new (P || (P = Promise))(function (resolve, reject) {
17 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
20 step((generator = generator.apply(thisArg, _arguments || [])).next());
21 });
22};
23Object.defineProperty(exports, "__esModule", { value: true });
24/// <reference path="../custom_typings/sw-precache.d.ts" />
25const assert = require("assert");
26const fs_1 = require("fs");
27const path = require("path");
28const logging = require("plylog");
29const sw_precache_1 = require("sw-precache");
30const path_transformers_1 = require("./path-transformers");
31const logger = logging.getLogger('polymer-build.service-worker');
32/**
33 * Given a user-provided AddServiceWorkerOptions object, check for deprecated
34 * options. When one is found, warn the user and fix if possible.
35 */
36// tslint:disable-next-line: no-any Turned off for user input.
37function fixDeprecatedOptions(options) {
38 if (typeof options.serviceWorkerPath !== 'undefined') {
39 logger.warn('"serviceWorkerPath" config option has been renamed to "path" and will no longer be supported in future versions');
40 options.path = options.path || options.serviceWorkerPath;
41 }
42 if (typeof options.swConfig !== 'undefined') {
43 logger.warn('"swConfig" config option has been renamed to "swPrecacheConfig" and will no longer be supported in future versions');
44 options.swPrecacheConfig = options.swPrecacheConfig || options.swConfig;
45 }
46 return options;
47}
48/**
49 * Returns an array of file paths for the service worker to precache, based on
50 * the information provided in the DepsIndex object.
51 */
52function getPrecachedAssets(depsIndex, project) {
53 const precachedAssets = new Set(project.config.allFragments);
54 precachedAssets.add(project.config.entrypoint);
55 for (const depImports of depsIndex.fragmentToFullDeps.values()) {
56 depImports.imports.forEach((s) => precachedAssets.add(s));
57 depImports.scripts.forEach((s) => precachedAssets.add(s));
58 depImports.styles.forEach((s) => precachedAssets.add(s));
59 }
60 return Array.from(precachedAssets);
61}
62/**
63 * Returns an array of file paths for the service worker to precache for a
64 * BUNDLED build, based on the information provided in the DepsIndex object.
65 */
66function getBundledPrecachedAssets(project) {
67 const precachedAssets = new Set(project.config.allFragments);
68 precachedAssets.add(project.config.entrypoint);
69 return Array.from(precachedAssets);
70}
71// Matches URLs like "/foo.png/bar" but not "/foo/bar.png".
72exports.hasNoFileExtension = /\/[^\/\.]*(\?|$)/;
73/**
74 * Returns a promise that resolves with a generated service worker
75 * configuration.
76 */
77function generateServiceWorkerConfig(options) {
78 return __awaiter(this, void 0, void 0, function* () {
79 assert(!!options, '`project` & `buildRoot` options are required');
80 assert(!!options.project, '`project` option is required');
81 assert(!!options.buildRoot, '`buildRoot` option is required');
82 options = fixDeprecatedOptions(options);
83 options = Object.assign({}, options);
84 const project = options.project;
85 const buildRoot = options.buildRoot;
86 const swPrecacheConfig = Object.assign({}, options.swPrecacheConfig);
87 const depsIndex = yield project.analyzer.analyzeDependencies;
88 let staticFileGlobs = Array.from(swPrecacheConfig.staticFileGlobs || []);
89 const precachedAssets = (options.bundled) ?
90 getBundledPrecachedAssets(project) :
91 getPrecachedAssets(depsIndex, project);
92 staticFileGlobs = staticFileGlobs.concat(precachedAssets);
93 staticFileGlobs = staticFileGlobs.map((filePath) => {
94 if (filePath.startsWith(project.config.root)) {
95 filePath = filePath.substring(project.config.root.length);
96 }
97 return path.join(buildRoot, filePath);
98 });
99 if (swPrecacheConfig.navigateFallback === undefined) {
100 // Map all application routes to the entrypoint.
101 swPrecacheConfig.navigateFallback =
102 path.relative(project.config.root, project.config.entrypoint);
103 }
104 if (swPrecacheConfig.navigateFallbackWhitelist === undefined) {
105 // Don't fall back to the entrypoint if the URL looks like a static file.
106 // We want those to 404 instead, since they are probably missing assets,
107 // not application routes. Note it's important that this matches the
108 // behavior of prpl-server.
109 swPrecacheConfig.navigateFallbackWhitelist = [exports.hasNoFileExtension];
110 }
111 if (swPrecacheConfig.directoryIndex === undefined) {
112 // By default, sw-precache maps any path ending with "/" to "index.html".
113 // This is a reasonable default for matching application routes, but 1) our
114 // entrypoint might not be called "index.html", and 2) this case is already
115 // handled by the navigateFallback configuration above. Simplest to just
116 // disable this feature.
117 swPrecacheConfig.directoryIndex = '';
118 }
119 // swPrecache will determine the right urls by stripping buildRoot.
120 // NOTE:(usergenic) sw-precache generate() apparently replaces the
121 // prefix on an already posixified version of the path on win32.
122 //
123 // We include a trailing slash in `stripPrefix` so that we remove leading
124 // slashes on the pre-cache asset URLs, hence producing relative URLs
125 // instead of absolute. We want relative URLs for builds mounted at non-root
126 // paths. Note that service worker fetches are relative to its own URL.
127 swPrecacheConfig.stripPrefix = addTrailingSlash(path_transformers_1.posixifyPath(buildRoot));
128 if (options.basePath) {
129 // TODO Drop this feature once CLI doesn't depend on it.
130 let replacePrefix = path_transformers_1.posixifyPath(options.basePath);
131 if (!replacePrefix.endsWith('/')) {
132 replacePrefix = replacePrefix + '/';
133 }
134 if (swPrecacheConfig.replacePrefix) {
135 console.info(`Replacing service worker configuration's ` +
136 `replacePrefix option (${swPrecacheConfig.replacePrefix}) ` +
137 `with the build configuration's basePath (${replacePrefix}).`);
138 }
139 swPrecacheConfig.replacePrefix = replacePrefix;
140 }
141 // static files will be pre-cached
142 swPrecacheConfig.staticFileGlobs = staticFileGlobs;
143 // Log service-worker helpful output at the debug log level
144 swPrecacheConfig.logger = swPrecacheConfig.logger || logger.debug;
145 return swPrecacheConfig;
146 });
147}
148exports.generateServiceWorkerConfig = generateServiceWorkerConfig;
149/**
150 * Returns a promise that resolves with a generated service worker (the file
151 * contents), based off of the options provided.
152 */
153function generateServiceWorker(options) {
154 return __awaiter(this, void 0, void 0, function* () {
155 const swPrecacheConfig = yield generateServiceWorkerConfig(options);
156 return yield (new Promise((resolve, reject) => {
157 logger.debug(`writing service worker...`, swPrecacheConfig);
158 sw_precache_1.generate(swPrecacheConfig, (err, fileContents) => {
159 if (err || fileContents == null) {
160 reject(err || 'No file contents provided.');
161 }
162 else {
163 // Note: Node 10 Function.prototype.toString() produces output
164 // like `function() { }` where earlier versions produce
165 // `function () { }` (note the space between function keyword)
166 // and parentheses. To ensure the output is consistent across
167 // versions, we will correctively insert missing space here.
168 fileContents = fileContents.replace(/\bfunction\(/g, 'function (');
169 resolve(Buffer.from(fileContents));
170 }
171 });
172 }));
173 });
174}
175exports.generateServiceWorker = generateServiceWorker;
176/**
177 * Returns a promise that resolves when a service worker has been generated
178 * and written to the build directory. This uses generateServiceWorker() to
179 * generate a service worker, which it then writes to the file system based on
180 * the buildRoot & path (if provided) options.
181 */
182function addServiceWorker(options) {
183 return generateServiceWorker(options).then((fileContents) => {
184 return new Promise((resolve, reject) => {
185 const serviceWorkerPath = path.join(options.buildRoot, options.path || 'service-worker.js');
186 fs_1.writeFile(serviceWorkerPath, fileContents, (err) => {
187 if (err) {
188 reject(err);
189 }
190 else {
191 resolve();
192 }
193 });
194 });
195 });
196}
197exports.addServiceWorker = addServiceWorker;
198function addTrailingSlash(s) {
199 return s.endsWith('/') ? s : s + '/';
200}
201//# sourceMappingURL=service-worker.js.map
\No newline at end of file