Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat a missing sourcemap as a warning #2959

Merged
merged 2 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions packages/workbox-build/src/inject-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {escapeRegExp} from './lib/escape-regexp';
import {getFileManifestEntries} from './lib/get-file-manifest-entries';
import {rebasePath} from './lib/rebase-path';
import {replaceAndUpdateSourceMap} from './lib/replace-and-update-source-map';
import {translateURLToSourcemapPaths} from './lib/translate-url-to-sourcemap-paths';
import {validateInjectManifestOptions} from './lib/validate-options';

// eslint-disable-next-line jsdoc/newline-after-description
Expand Down Expand Up @@ -162,31 +163,28 @@ export async function injectManifest(config: unknown): Promise<BuildResult> {

const manifestString = stringify(manifestEntries);
const filesToWrite: {[key: string]: string} = {};
// sourceMapURL returns value type any and could be null.
// url is checked before it is used later.
const url: string = sourceMapURL.getFrom(swFileContents); // eslint-disable-line

const url = sourceMapURL.getFrom(swFileContents) as string; // eslint-disable-line
// See https://github.com/GoogleChrome/workbox/issues/2957
const {destPath, srcPath, warning} = translateURLToSourcemapPaths(
url,
options.swSrc,
options.swDest,
);
if (warning) {
warnings.push(warning);
}

// If our swSrc file contains a sourcemap, we would invalidate that
// mapping if we just replaced injectionPoint with the stringified manifest.
// Instead, we need to update the swDest contents as well as the sourcemap
// (assuming it's a real file, not a data: URL) at the same time.
// See https://github.com/GoogleChrome/workbox/issues/2235
// and https://github.com/GoogleChrome/workbox/issues/2648
if (url && !url.startsWith('data:')) {
const sourcemapSrcPath = upath.resolve(upath.dirname(options.swSrc), url);
const sourcemapDestPath = upath.resolve(upath.dirname(options.swDest), url);

let originalMap: RawSourceMap;
try {
// readJSON returns Promise<any>.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
originalMap = await fse.readJSON(sourcemapSrcPath, {encoding: 'utf8'});
} catch (error) {
throw new Error(
`${errors['cant-find-sourcemap']} ${
error instanceof Error && error.message ? error.message : ''
}`,
);
}
if (srcPath && destPath) {
const originalMap = (await fse.readJSON(srcPath, {
encoding: 'utf8',
})) as RawSourceMap;

const {map, source} = await replaceAndUpdateSourceMap({
originalMap,
Expand All @@ -197,7 +195,7 @@ export async function injectManifest(config: unknown): Promise<BuildResult> {
});

filesToWrite[options.swDest] = source;
filesToWrite[sourcemapDestPath] = map;
filesToWrite[destPath] = map;
} else {
// If there's no sourcemap associated with swSrc, a simple string
// replacement will suffice.
Expand All @@ -210,7 +208,7 @@ export async function injectManifest(config: unknown): Promise<BuildResult> {
for (const [file, contents] of Object.entries(filesToWrite)) {
try {
await fse.mkdirp(upath.dirname(file));
} catch (error) {
} catch (error: unknown) {
throw new Error(
errors['unable-to-make-sw-directory'] +
` '${error instanceof Error && error.message ? error.message : ''}'`,
Expand Down
38 changes: 38 additions & 0 deletions packages/workbox-build/src/lib/translate-url-to-sourcemap-paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright 2021 Google LLC

Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/

import fse from 'fs-extra';
import upath from 'upath';

import {errors} from './errors';

export function translateURLToSourcemapPaths(
url: string,
swSrc: string,
swDest: string,
): {
destPath: string | undefined;
srcPath: string | undefined;
warning: string | undefined;
} {
let destPath: string | undefined = undefined;
let srcPath: string | undefined = undefined;
let warning: string | undefined = undefined;

if (url && !url.startsWith('data:')) {
const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
if (fse.existsSync(possibleSrcPath)) {
srcPath = possibleSrcPath;
destPath = upath.resolve(upath.dirname(swDest), url);
} else {
warning = `${errors['cant-find-sourcemap']} ${possibleSrcPath}`;
}
}

return {destPath, srcPath, warning};
}
22 changes: 22 additions & 0 deletions test/workbox-build/node/inject-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,28 @@ describe(`[workbox-build] inject-manifest.js (End to End)`, function () {

// We can't validate the SW file contents.
});

it(`should perform injection, but report a warning if the sourcemap file can't be found`, async function () {
const outputDir = tempy.directory();
const swSrc = upath.join(
SW_SRC_DIR,
'basic-with-invalid-sourcemap.js.nolint',
);
const swDest = upath.join(outputDir, 'basic-with-sourcemap.js');
const options = Object.assign({}, BASE_OPTIONS, {
swDest,
swSrc,
});

const {count, size, warnings} = await injectManifest(options);
expect(warnings.length).to.eql(1);
expect(warnings[0]).to.include(errors['cant-find-sourcemap']);
expect(count).to.eql(6);
// Line ending differences lead to different sizes on Windows.
expect(size).to.be.oneOf([2782, 2698]);

// We can't validate the SW file contents.
});
});

describe(`[workbox-build] removed options`, function () {
Expand Down
87 changes: 87 additions & 0 deletions test/workbox-build/node/lib/translate-url-to-sourcemap-paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
Copyright 2018 Google LLC

Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/

const expect = require('chai').expect;
const proxyquire = require('proxyquire');

const {errors} = require('../../../../packages/workbox-build/build/lib/errors');

describe(`[workbox-build] lib/translate-url-to-sourcemap-paths.ts`, function () {
const MODULE_PATH =
'../../../../packages/workbox-build/build/lib/translate-url-to-sourcemap-paths';
const URL = 'sw.js.map';
const SWSRC = 'src/sw.js';
const SWDEST = 'dist/sw.js';

it(`should return undefined paths when url is undefined`, function () {
const {translateURLToSourcemapPaths} = require(MODULE_PATH);

const {destPath, srcPath, warning} = translateURLToSourcemapPaths(
undefined,
SWSRC,
SWDEST,
);

expect(destPath).to.be.undefined;
expect(srcPath).to.be.undefined;
expect(warning).to.be.undefined;
});

it(`should return undefined paths when url starts with data:`, function () {
const {translateURLToSourcemapPaths} = require(MODULE_PATH);

const {destPath, srcPath, warning} = translateURLToSourcemapPaths(
`data:${URL}`,
SWSRC,
SWDEST,
);

expect(destPath).to.be.undefined;
expect(srcPath).to.be.undefined;
expect(warning).to.be.undefined;
});

it(`should return undefined paths and a warning when the resolved URL path doesn't exist`, function () {
const {translateURLToSourcemapPaths} = proxyquire(MODULE_PATH, {
'fs-extra': {
existsSync: () => false,
},
});

const {destPath, srcPath, warning} = translateURLToSourcemapPaths(
URL,
SWSRC,
SWDEST,
);

expect(destPath).to.be.undefined;
expect(srcPath).to.be.undefined;
expect(warning).to.include(errors['cant-find-sourcemap']);
});

it(`should return valid paths and no warning when the resolved URL path exists`, function () {
const {translateURLToSourcemapPaths} = proxyquire(MODULE_PATH, {
'fs-extra': {
existsSync: () => true,
},
'upath': {
resolve: (...args) => args.join('/'),
},
});

const {destPath, srcPath, warning} = translateURLToSourcemapPaths(
URL,
SWSRC,
SWDEST,
);

expect(destPath).to.eql('dist/sw.js.map');
expect(srcPath).to.eq('src/sw.js.map');
expect(warning).to.be.undefined;
});
});
Loading