diff --git a/.gitignore b/.gitignore index 05252a1..2960678 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ test/fixtures/file-protocol-path.js.map test/fixtures/map-with-sourceroot.js.map test/fixtures/map-without-sourceroot.js.map test/fixtures/normal-map.js.map +/test/fixtures/server-relative-url-path.js.map logs *.log npm-debug.log* diff --git a/src/utils.js b/src/utils.js index 736460d..ec93695 100644 --- a/src/utils.js +++ b/src/utils.js @@ -143,6 +143,41 @@ async function fetchFromFilesystem(loaderContext, sourceURL) { return buffer.toString(); } +async function fetchPathsFromFilesystem( + loaderContext, + possibleRequests, + errorsAccumulator = '' +) { + let result; + + try { + result = await fetchFromFilesystem( + loaderContext, + possibleRequests[0], + errorsAccumulator + ); + } catch (error) { + // eslint-disable-next-line no-param-reassign + errorsAccumulator += `${error.message}\n\n`; + + const [, ...tailPossibleRequests] = possibleRequests; + + if (tailPossibleRequests.length === 0) { + error.message = errorsAccumulator; + + throw error; + } + + return fetchPathsFromFilesystem( + loaderContext, + tailPossibleRequests, + errorsAccumulator + ); + } + + return result; +} + async function fetchFromURL( loaderContext, context, @@ -191,7 +226,18 @@ async function fetchFromURL( let sourceContent; if (!skipReading) { - sourceContent = await fetchFromFilesystem(loaderContext, sourceURL); + const possibleRequests = [sourceURL]; + + if (url.startsWith('/')) { + possibleRequests.push( + getAbsolutePath(context, sourceURL.slice(1), sourceRoot) + ); + } + + sourceContent = await fetchPathsFromFilesystem( + loaderContext, + possibleRequests + ); } return { sourceURL, sourceContent }; diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index faa14bd..c14acc5 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -15,6 +15,21 @@ Failed to parse source map from \\"data\\" URL: data:application/source-map;base ] `; +exports[`source-map-loader should emit warning when unresolved server-relative-url-path: css 1`] = ` +"with SourceMap +// #sourceMappingURL=/unresolved-server-relative-url-path.js.map +// comment +" +`; + +exports[`source-map-loader should emit warning when unresolved server-relative-url-path: errors 1`] = `Array []`; + +exports[`source-map-loader should emit warning when unresolved server-relative-url-path: warnings 1`] = ` +Array [ + "ModuleWarning: Module Warning (from \`replaced original path\`):", +] +`; + exports[`source-map-loader should leave normal files untouched: css 1`] = `"without SourceMap"`; exports[`source-map-loader should leave normal files untouched: errors 1`] = `Array []`; @@ -269,6 +284,33 @@ Failed to parse source map: \\"//sampledomain.com/external-source-map2.map\\" UR ] `; +exports[`source-map-loader should process server-relative-url-path: css 1`] = ` +"with SourceMap +// comment +" +`; + +exports[`source-map-loader should process server-relative-url-path: errors 1`] = `Array []`; + +exports[`source-map-loader should process server-relative-url-path: map 1`] = ` +Object { + "file": "server-relative-url-path.js", + "mappings": "AAAA", + "sources": Array [ + "/test/fixtures/server-relative-url-path.js - (normalized for test)", + ], + "sourcesContent": Array [ + "with SourceMap +// #sourceMappingURL=/server-relative-url-path.js.map +// comment +", + ], + "version": 3, +} +`; + +exports[`source-map-loader should process server-relative-url-path: warnings 1`] = `Array []`; + exports[`source-map-loader should reject http SourceMaps: css 1`] = ` "with SourceMap //#sourceMappingURL=http://sampledomain.com/external-source-map2.map diff --git a/test/fixtures/server-relative-url-path.js b/test/fixtures/server-relative-url-path.js new file mode 100644 index 0000000..1902707 --- /dev/null +++ b/test/fixtures/server-relative-url-path.js @@ -0,0 +1,3 @@ +with SourceMap +// #sourceMappingURL=/server-relative-url-path.js.map +// comment diff --git a/test/fixtures/unresolved-server-relative-url-path.js b/test/fixtures/unresolved-server-relative-url-path.js new file mode 100644 index 0000000..9f0b10a --- /dev/null +++ b/test/fixtures/unresolved-server-relative-url-path.js @@ -0,0 +1,3 @@ +with SourceMap +// #sourceMappingURL=/unresolved-server-relative-url-path.js.map +// comment diff --git a/test/helpers/getWarnings.js b/test/helpers/getWarnings.js index f5e5ab1..7c987d5 100644 --- a/test/helpers/getWarnings.js +++ b/test/helpers/getWarnings.js @@ -1,5 +1,5 @@ import normalizeErrors from './normalizeErrors'; -export default (stats) => { - return normalizeErrors(stats.compilation.warnings.sort()); +export default (stats, shortError) => { + return normalizeErrors(stats.compilation.warnings.sort(), shortError); }; diff --git a/test/helpers/normalizeErrors.js b/test/helpers/normalizeErrors.js index 1b41f3f..da0d6b3 100644 --- a/test/helpers/normalizeErrors.js +++ b/test/helpers/normalizeErrors.js @@ -14,8 +14,14 @@ function removeCWD(str) { .replace(new RegExp(cwd, 'g'), ''); } -export default (errors) => { - return errors.map((error) => - removeCWD(error.toString().split('\n').slice(0, 2).join('\n')) - ); +export default (errors, shortError) => { + return errors.map((error) => { + let errorMessage = error.toString(); + + if (shortError) { + errorMessage = errorMessage.split('\n').slice(0, 1).join('\n'); + } + + return removeCWD(errorMessage.split('\n').slice(0, 2).join('\n')); + }); }; diff --git a/test/loader.test.js b/test/loader.test.js index fb69023..e071ac5 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -617,4 +617,45 @@ describe('source-map-loader', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should process server-relative-url-path', async () => { + const sourceRoot = path.resolve(__dirname, 'fixtures'); + const javaScriptFilename = 'server-relative-url-path.js'; + const sourceFilename = 'server-relative-url-path.js'; + const sourceMapPath = path.join( + sourceRoot, + 'server-relative-url-path.js.map' + ); + + // Create the sourcemap file + const rawSourceMap = { + version: 3, + file: javaScriptFilename, + sourceRoot, + sources: [sourceFilename], + mappings: 'AAAA', + }; + fs.writeFileSync(sourceMapPath, JSON.stringify(rawSourceMap)); + + const testId = 'server-relative-url-path.js'; + const compiler = getCompiler(testId); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(normalizeMap(codeFromBundle.map)).toMatchSnapshot('map'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + + it('should emit warning when unresolved server-relative-url-path', async () => { + const testId = 'unresolved-server-relative-url-path.js'; + const compiler = getCompiler(testId); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats, true)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); });