Skip to content

Commit

Permalink
Treat a missing sourcemap as a warning (#2959)
Browse files Browse the repository at this point in the history
* Missing sourcemap warning

* Linting
  • Loading branch information
jeffposnick committed Oct 13, 2021
1 parent cef23f1 commit 97fc646
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 21 deletions.
40 changes: 19 additions & 21 deletions packages/workbox-build/src/inject-manifest.ts
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
@@ -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
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
@@ -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;
});
});

0 comments on commit 97fc646

Please sign in to comment.