Skip to content

Commit

Permalink
fix: absolute path for sources and add sources in dependendencies
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed May 22, 2020
1 parent c18d1f9 commit b64f7d8
Show file tree
Hide file tree
Showing 14 changed files with 274 additions and 91 deletions.
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Expand Up @@ -43,7 +43,6 @@
"dependencies": {
"data-urls": "^2.0.0",
"loader-utils": "^2.0.0",
"neo-async": "^2.6.1",
"schema-utils": "^2.6.6",
"source-map": "^0.6.0",
"whatwg-encoding": "^1.0.5"
Expand Down
157 changes: 99 additions & 58 deletions src/index.js
Expand Up @@ -4,16 +4,22 @@
*/
import fs from 'fs';
import path from 'path';
import urlUtils from 'url';

import validateOptions from 'schema-utils';
import async from 'neo-async';
import parseDataURL from 'data-urls';

import { SourceMapConsumer } from 'source-map';
import { labelToName, decode } from 'whatwg-encoding';
import { getOptions, urlToRequest } from 'loader-utils';

import flattenSourceMap from './utils/flatten';

import schema from './options.json';
import {
flattenSourceMap,
readFile,
getContentFromSourcesContent,
isUrlRequest,
} from './utils';

// Matches only the last occurrence of sourceMappingURL
const baseRegex =
Expand All @@ -40,7 +46,7 @@ export default function loader(input, inputMap) {
return;
}

const [, url] = match;
let [, url] = match;

const dataURL = parseDataURL(url);

Expand Down Expand Up @@ -78,6 +84,28 @@ export default function loader(input, inputMap) {
return;
}

if (!isUrlRequest(url)) {
const { protocol } = urlUtils.parse(url);

if (protocol !== 'file:') {
emitWarning(`URL scheme not supported: ${protocol}`);

callback(null, input, inputMap);

return;
}

try {
url = urlUtils.fileURLToPath(url);
} catch (error) {
emitWarning(error);

callback(null, input, inputMap);

return;
}
}

resolve(context, urlToRequest(url, true), (resolveError, result) => {
if (resolveError) {
emitWarning(`Cannot find SourceMap '${url}': ${resolveError}`);
Expand Down Expand Up @@ -121,70 +149,83 @@ export default function loader(input, inputMap) {
map = await flattenSourceMap(map);
}

if (map.sourcesContent && map.sourcesContent.length >= map.sources.length) {
callback(null, input.replace(match[0], ''), map);

return;
}

const sourcePrefix = map.sourceRoot ? `${map.sourceRoot}${path.sep}` : '';
const mapConsumer = await new SourceMapConsumer(map);

// eslint-disable-next-line no-param-reassign
map.sources = map.sources.map((s) => sourcePrefix + s);
let resolvedSources;

// eslint-disable-next-line no-param-reassign
delete map.sourceRoot;

const missingSources = map.sourcesContent
? map.sources.slice(map.sourcesContent.length)
: map.sources;
try {
resolvedSources = await Promise.all(
map.sources.map(async (source) => {
const fullPath = map.sourceRoot
? `${map.sourceRoot}${path.sep}${source}`
: source;

const originalData = getContentFromSourcesContent(
mapConsumer,
source
);

if (path.isAbsolute(fullPath)) {
return originalData
? { source: fullPath, content: originalData }
: readFile(fullPath, 'utf-8', emitWarning);
}

async.map(
missingSources,
// eslint-disable-next-line no-shadow
(source, callback) => {
resolve(context, urlToRequest(source, true), (resolveError, result) => {
if (resolveError) {
emitWarning(`Cannot find source file '${source}': ${resolveError}`);
return new Promise((promiseResolve) => {
resolve(
context,
urlToRequest(fullPath, true),
(resolveError, result) => {
if (resolveError) {
emitWarning(
`Cannot find source file '${source}': ${resolveError}`
);

return originalData
? promiseResolve({
source: fullPath,
content: originalData,
})
: promiseResolve({ source: fullPath, content: null });
}

return originalData
? promiseResolve({ source: result, content: originalData })
: promiseResolve(readFile(result, 'utf-8', emitWarning));
}
);
});
})
);
} catch (error) {
emitWarning(error);

callback(null, null);
callback(null, input, inputMap);
}

return;
}
const resultMap = { ...map };
resultMap.sources = [];
resultMap.sourcesContent = [];

addDependency(result);
delete resultMap.sourceRoot;

fs.readFile(result, 'utf-8', (readFileError, content) => {
if (readFileError) {
emitWarning(
`Cannot open source file '${result}': ${readFileError}`
);
resolvedSources.forEach((res) => {
// eslint-disable-next-line no-param-reassign
resultMap.sources.push(path.normalize(res.source));
resultMap.sourcesContent.push(res.content);

callback(null, null);
if (res.source) {
addDependency(res.source);
}
});

return;
}
const sourcesContentIsEmpty =
resultMap.sourcesContent.filter((entry) => !!entry).length === 0;

callback(null, { source: result, content });
});
});
},
(err, info) => {
// eslint-disable-next-line no-param-reassign
map.sourcesContent = map.sourcesContent || [];

info.forEach((res) => {
if (res) {
// eslint-disable-next-line no-param-reassign
map.sources[map.sourcesContent.length] = res.source;
map.sourcesContent.push(res.content);
} else {
map.sourcesContent.push(null);
}
});
if (sourcesContentIsEmpty) {
delete resultMap.sourcesContent;
}

processMap(map, context, callback);
}
);
callback(null, input.replace(match[0], ''), resultMap);
}
}
46 changes: 44 additions & 2 deletions src/utils/flatten.js → src/utils.js
@@ -1,6 +1,9 @@
import fs from 'fs';
import path from 'path';

import sourceMap from 'source-map';

async function FlattenSourceMap(map) {
async function flattenSourceMap(map) {
const consumer = await new sourceMap.SourceMapConsumer(map);
let generatedMap;

Expand Down Expand Up @@ -43,4 +46,43 @@ async function FlattenSourceMap(map) {
return generatedMap.toJSON();
}

module.exports = FlattenSourceMap;
function readFile(fullPath, charset, emitWarning) {
return new Promise((resolve) => {
fs.readFile(fullPath, charset, (readFileError, content) => {
if (readFileError) {
emitWarning(`Cannot open source file '${fullPath}': ${readFileError}`);

resolve({ source: null, content: null });
}

resolve({ source: fullPath, content });
});
});
}

function getContentFromSourcesContent(consumer, source) {
return consumer.sourceContentFor(source, true);
}

function isUrlRequest(url) {
// An URL is not an request if

// 1. It's an absolute url and it is not `windows` path like `C:\dir\file`
if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !path.win32.isAbsolute(url)) {
return false;
}

// 2. It's a protocol-relative
if (/^\/\//.test(url)) {
return false;
}

return true;
}

export {
flattenSourceMap,
readFile,
getContentFromSourcesContent,
isUrlRequest,
};
50 changes: 41 additions & 9 deletions test/__snapshots__/loader.test.js.snap
Expand Up @@ -50,7 +50,12 @@ Object {
}
`;

exports[`source-map-loader should process external SourceMaps: warnings 1`] = `Array []`;
exports[`source-map-loader should process external SourceMaps: warnings 1`] = `
Array [
"ModuleWarning: Module Warning (from \`replaced original path\`):
(Emitted value instead of an instance of Error) Cannot find source file 'external-source-map.txt': Error: Can't resolve './external-source-map.txt' in '/test/fixtures'",
]
`;

exports[`source-map-loader should process inlined SourceMaps with charset: css 1`] = `
"with SourceMap
Expand All @@ -64,7 +69,7 @@ Object {
"file": "charset-inline-source-map.js",
"mappings": "AAAA",
"sources": Array [
"charset-inline-source-map.txt",
"/test/fixtures/charset-inline-source-map.txt - (normalized for test)",
],
"sourcesContent": Array [
"with SourceMap",
Expand Down Expand Up @@ -96,7 +101,32 @@ Object {
}
`;

exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `Array []`;
exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `
Array [
"ModuleWarning: Module Warning (from \`replaced original path\`):
(Emitted value instead of an instance of Error) Cannot find source file 'inline-source-map.txt': Error: Can't resolve './inline-source-map.txt' in '/test/fixtures'",
]
`;

exports[`source-map-loader should reject http SourceMaps: css 1`] = `
"with SourceMap
//#sourceMappingURL=http://sampledomain.com/external-source-map2.map
// comment
"
`;

exports[`source-map-loader should reject http SourceMaps: errors 1`] = `Array []`;

exports[`source-map-loader should reject http SourceMaps: warnings 1`] = `
Array [
"ModuleWarning: Module Warning (from \`replaced original path\`):
(Emitted value instead of an instance of Error) URL scheme not supported: http:",
]
`;

exports[`source-map-loader should reject not exist file: SourceMaps: errors 1`] = `Array []`;

exports[`source-map-loader should reject not exist file: SourceMaps: warnings 1`] = `"TypeError [ERR_INVALID_FILE"`;

exports[`source-map-loader should skip invalid base64 SourceMap: css 1`] = `
"without SourceMap
Expand Down Expand Up @@ -133,7 +163,7 @@ Object {
exports[`source-map-loader should support absolute sourceRoot paths in sourcemaps: warnings 1`] = `Array []`;

exports[`source-map-loader should support indexed sourcemaps: css 1`] = `
"with SourceMap
"console.log('with SourceMap')
// Map taken from here
// https://github.com/mozilla/source-map/blob/master/test/util.js - indexedTestMapDifferentSourceRoots
"
Expand All @@ -147,7 +177,7 @@ Object {
"mappings": "CAAC,IAAI,IAAM,SAAU,GAClB,OAAO,IAAI;CCDb,IAAI,IAAM,SAAU,GAClB,OAAO",
"names": Array [],
"sources": Array [
"/the/root/nested1.js",
"/test/fixtures/indexed-sourcemap/nested1.js - (normalized for test)",
"/different/root/nested2.js",
],
"sourcesContent": Array [
Expand Down Expand Up @@ -210,7 +240,12 @@ Object {
}
`;

exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `Array []`;
exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `
Array [
"ModuleWarning: Module Warning (from \`replaced original path\`):
(Emitted value instead of an instance of Error) Cannot find source file 'inline-source-map.txt': Error: Can't resolve './inline-source-map.txt' in '/test/fixtures'",
]
`;

exports[`source-map-loader should warn on invalid SourceMap: css 1`] = `
"with SourceMap
Expand Down Expand Up @@ -271,9 +306,6 @@ Object {
"sources": Array [
"missing-source-map2.txt",
],
"sourcesContent": Array [
null,
],
"version": 3,
}
`;
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/charset-inline-source-map.txt
@@ -0,0 +1 @@
// Content
3 changes: 3 additions & 0 deletions test/fixtures/file-source-map-windows.js
@@ -0,0 +1,3 @@
with SourceMap
// #sourceMappingURL=file:///unresolvedPath/map.map
// comment
3 changes: 3 additions & 0 deletions test/fixtures/file-source-map.js
@@ -0,0 +1,3 @@
with SourceMap
// #sourceMappingURL=file://relative-sourceRoot-source-map.map
// comment
3 changes: 3 additions & 0 deletions test/fixtures/http-source-map.js
@@ -0,0 +1,3 @@
with SourceMap
//#sourceMappingURL=http://sampledomain.com/external-source-map2.map
// comment
2 changes: 1 addition & 1 deletion test/fixtures/indexed-sourcemap/file.js
@@ -1,4 +1,4 @@
with SourceMap
console.log('with SourceMap')
//#sourceMappingURL=file.js.map
// Map taken from here
// https://github.com/mozilla/source-map/blob/master/test/util.js - indexedTestMapDifferentSourceRoots

0 comments on commit b64f7d8

Please sign in to comment.