Skip to content

Commit

Permalink
fix: perf and crashing (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed May 22, 2020
1 parent b64f7d8 commit 4c39c22
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 15 deletions.
20 changes: 6 additions & 14 deletions src/index.js
Expand Up @@ -19,16 +19,9 @@ import {
readFile,
getContentFromSourcesContent,
isUrlRequest,
getSourceMappingUrl,
} from './utils';

// Matches only the last occurrence of sourceMappingURL
const baseRegex =
'\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)(?![\\S\\s]*sourceMappingURL)';
// Matches /* ... */ comments
const regex1 = new RegExp(`/\\*${baseRegex}\\s*\\*/`);
// Matches // .... comments
const regex2 = new RegExp(`//${baseRegex}($|\n|\r\n?)`);

export default function loader(input, inputMap) {
const options = getOptions(this);

Expand All @@ -37,17 +30,16 @@ export default function loader(input, inputMap) {
baseDataPath: 'options',
});

const match = input.match(regex1) || input.match(regex2);
let { url } = getSourceMappingUrl(input);
const { replacementString } = getSourceMappingUrl(input);
const callback = this.async();

if (!match) {
if (!url) {
callback(null, input, inputMap);

return;
}

let [, url] = match;

const dataURL = parseDataURL(url);

const { context, resolve, addDependency, emitWarning } = this;
Expand All @@ -60,7 +52,7 @@ export default function loader(input, inputMap) {
labelToName(dataURL.mimeType.parameters.get('charset')) || 'UTF-8';

map = decode(dataURL.body, dataURL.encodingName);
map = JSON.parse(map);
map = JSON.parse(map.replace(/^\)\]\}'/, ''));
} catch (error) {
emitWarning(
`Cannot parse inline SourceMap with Charset ${dataURL.encodingName}: ${error}`
Expand Down Expand Up @@ -226,6 +218,6 @@ export default function loader(input, inputMap) {
delete resultMap.sourcesContent;
}

callback(null, input.replace(match[0], ''), resultMap);
callback(null, input.replace(replacementString, ''), resultMap);
}
}
40 changes: 40 additions & 0 deletions src/utils.js
Expand Up @@ -3,6 +3,28 @@ import path from 'path';

import sourceMap from 'source-map';

// Matches only the last occurrence of sourceMappingURL
const innerRegex = /\s*[#@]\s*sourceMappingURL\s*=\s*([^\s'"]*)\s*/;

/* eslint-disable prefer-template */
const sourceMappingURLRegex = RegExp(
'(?:' +
'/\\*' +
'(?:\\s*\r?\n(?://)?)?' +
'(?:' +
innerRegex.source +
')' +
'\\s*' +
'\\*/' +
'|' +
'//(?:' +
innerRegex.source +
')' +
')' +
'\\s*'
);
/* eslint-enable prefer-template */

async function flattenSourceMap(map) {
const consumer = await new sourceMap.SourceMapConsumer(map);
let generatedMap;
Expand Down Expand Up @@ -80,9 +102,27 @@ function isUrlRequest(url) {
return true;
}

function getSourceMappingUrl(code) {
const lines = code.split(/^/m);
let match;

for (let i = lines.length - 1; i >= 0; i--) {
match = lines[i].match(sourceMappingURLRegex);
if (match) {
break;
}
}

return {
url: match ? match[1] || match[2] || '' : null,
replacementString: match ? match[0] : null,
};
}

export {
flattenSourceMap,
readFile,
getContentFromSourcesContent,
isUrlRequest,
getSourceMappingUrl,
};
18 changes: 17 additions & 1 deletion test/__snapshots__/loader.test.js.snap
Expand Up @@ -6,6 +6,17 @@ exports[`source-map-loader should leave normal files untouched: errors 1`] = `Ar

exports[`source-map-loader should leave normal files untouched: warnings 1`] = `Array []`;

exports[`source-map-loader should leave normal files with fake source-map untouched: css 1`] = `
"// without SourceMap
anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\\" */\\";
// comment
"
`;

exports[`source-map-loader should leave normal files with fake source-map untouched: errors 1`] = `Array []`;

exports[`source-map-loader should leave normal files with fake source-map untouched: warnings 1`] = `Array []`;

exports[`source-map-loader should process external SourceMaps (external sources): css 1`] = `
"with SourceMap
// comment"
Expand Down Expand Up @@ -136,7 +147,12 @@ exports[`source-map-loader should skip invalid base64 SourceMap: css 1`] = `

exports[`source-map-loader should skip invalid base64 SourceMap: errors 1`] = `Array []`;

exports[`source-map-loader should skip invalid base64 SourceMap: warnings 1`] = `Array []`;
exports[`source-map-loader should skip invalid base64 SourceMap: warnings 1`] = `
Array [
"ModuleWarning: Module Warning (from \`replaced original path\`):
(Emitted value instead of an instance of Error) Cannot parse inline SourceMap with Charset UTF-8: SyntaxError: Unexpected end of JSON input",
]
`;

exports[`source-map-loader should support absolute sourceRoot paths in sourcemaps: css 1`] = `
"with SourceMap
Expand Down
27 changes: 27 additions & 0 deletions test/__snapshots__/sourceMapperRegexp.test.js.snap
@@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`source-map-loader should be null: result 1`] = `null`;

exports[`source-map-loader should be null: result 2`] = `null`;

exports[`source-map-loader should find last map: result 1`] = `"/sample-source-map-last.map"`;

exports[`source-map-loader should not include quotes: result 1`] = `"data:application/json;base64,"`;

exports[`source-map-loader should work: result 1`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 2`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 3`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 4`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 5`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 6`] = `"absolute-sourceRoot-source-map.map"`;

exports[`source-map-loader should work: result 7`] = `"http://sampledomain.com/external-source-map2.map"`;

exports[`source-map-loader should work: result 8`] = `"data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ=="`;

exports[`source-map-loader should work: result 9`] = `"/sample-source-map.map"`;
3 changes: 3 additions & 0 deletions test/fixtures/normal-file2.js
@@ -0,0 +1,3 @@
// without SourceMap
anInvalidDirective = "\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";
// comment
12 changes: 12 additions & 0 deletions test/loader.test.js
Expand Up @@ -26,6 +26,18 @@ describe('source-map-loader', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should leave normal files with fake source-map untouched', async () => {
const testId = 'normal-file2.js';
const compiler = getCompiler(testId);
const stats = await compile(compiler);
const codeFromBundle = getCodeFromBundle(stats, compiler);

expect(codeFromBundle.map).toBeUndefined();
expect(codeFromBundle.css).toMatchSnapshot('css');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should process inlined SourceMaps', async () => {
const testId = 'inline-source-map.js';
const compiler = getCompiler(testId);
Expand Down
106 changes: 106 additions & 0 deletions test/sourceMapperRegexp.test.js
@@ -0,0 +1,106 @@
import { getSourceMappingUrl } from '../src/utils';

describe('source-map-loader', () => {
it('should work', async () => {
const code = `/*#sourceMappingURL=absolute-sourceRoot-source-map.map*/`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `/* #sourceMappingURL=absolute-sourceRoot-source-map.map */`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `//#sourceMappingURL=absolute-sourceRoot-source-map.map`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `//@sourceMappingURL=absolute-sourceRoot-source-map.map`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = ` // #sourceMappingURL=absolute-sourceRoot-source-map.map`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = ` // # sourceMappingURL = absolute-sourceRoot-source-map.map `;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `// #sourceMappingURL = http://sampledomain.com/external-source-map2.map`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `// @sourceMappingURL=data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ==`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should work', async () => {
const code = `
with SourceMap
// #sourceMappingURL = /sample-source-map.map
// comment
`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should find last map', async () => {
const code = `
with SourceMap
// #sourceMappingURL = /sample-source-map-1.map
// #sourceMappingURL = /sample-source-map-2.map
// #sourceMappingURL = /sample-source-map-last.map
// comment
`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should be null', async () => {
const code = `"
/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should be null', async () => {
const code = `anInvalidDirective = "\\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});

it('should not include quotes', async () => {
const code = `// # sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+"`;
const { url } = getSourceMappingUrl(code);

expect(url).toMatchSnapshot('result');
});
});

0 comments on commit 4c39c22

Please sign in to comment.