Skip to content

Commit b64f7d8

Browse files
fix: absolute path for sources and add sources in dependendencies
1 parent c18d1f9 commit b64f7d8

14 files changed

+274
-91
lines changed

package-lock.json

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
"dependencies": {
4444
"data-urls": "^2.0.0",
4545
"loader-utils": "^2.0.0",
46-
"neo-async": "^2.6.1",
4746
"schema-utils": "^2.6.6",
4847
"source-map": "^0.6.0",
4948
"whatwg-encoding": "^1.0.5"

src/index.js

+99-58
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@
44
*/
55
import fs from 'fs';
66
import path from 'path';
7+
import urlUtils from 'url';
78

89
import validateOptions from 'schema-utils';
9-
import async from 'neo-async';
1010
import parseDataURL from 'data-urls';
11+
12+
import { SourceMapConsumer } from 'source-map';
1113
import { labelToName, decode } from 'whatwg-encoding';
1214
import { getOptions, urlToRequest } from 'loader-utils';
1315

14-
import flattenSourceMap from './utils/flatten';
15-
1616
import schema from './options.json';
17+
import {
18+
flattenSourceMap,
19+
readFile,
20+
getContentFromSourcesContent,
21+
isUrlRequest,
22+
} from './utils';
1723

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

43-
const [, url] = match;
49+
let [, url] = match;
4450

4551
const dataURL = parseDataURL(url);
4652

@@ -78,6 +84,28 @@ export default function loader(input, inputMap) {
7884
return;
7985
}
8086

87+
if (!isUrlRequest(url)) {
88+
const { protocol } = urlUtils.parse(url);
89+
90+
if (protocol !== 'file:') {
91+
emitWarning(`URL scheme not supported: ${protocol}`);
92+
93+
callback(null, input, inputMap);
94+
95+
return;
96+
}
97+
98+
try {
99+
url = urlUtils.fileURLToPath(url);
100+
} catch (error) {
101+
emitWarning(error);
102+
103+
callback(null, input, inputMap);
104+
105+
return;
106+
}
107+
}
108+
81109
resolve(context, urlToRequest(url, true), (resolveError, result) => {
82110
if (resolveError) {
83111
emitWarning(`Cannot find SourceMap '${url}': ${resolveError}`);
@@ -121,70 +149,83 @@ export default function loader(input, inputMap) {
121149
map = await flattenSourceMap(map);
122150
}
123151

124-
if (map.sourcesContent && map.sourcesContent.length >= map.sources.length) {
125-
callback(null, input.replace(match[0], ''), map);
126-
127-
return;
128-
}
129-
130-
const sourcePrefix = map.sourceRoot ? `${map.sourceRoot}${path.sep}` : '';
152+
const mapConsumer = await new SourceMapConsumer(map);
131153

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

135-
// eslint-disable-next-line no-param-reassign
136-
delete map.sourceRoot;
137-
138-
const missingSources = map.sourcesContent
139-
? map.sources.slice(map.sourcesContent.length)
140-
: map.sources;
156+
try {
157+
resolvedSources = await Promise.all(
158+
map.sources.map(async (source) => {
159+
const fullPath = map.sourceRoot
160+
? `${map.sourceRoot}${path.sep}${source}`
161+
: source;
162+
163+
const originalData = getContentFromSourcesContent(
164+
mapConsumer,
165+
source
166+
);
167+
168+
if (path.isAbsolute(fullPath)) {
169+
return originalData
170+
? { source: fullPath, content: originalData }
171+
: readFile(fullPath, 'utf-8', emitWarning);
172+
}
141173

142-
async.map(
143-
missingSources,
144-
// eslint-disable-next-line no-shadow
145-
(source, callback) => {
146-
resolve(context, urlToRequest(source, true), (resolveError, result) => {
147-
if (resolveError) {
148-
emitWarning(`Cannot find source file '${source}': ${resolveError}`);
174+
return new Promise((promiseResolve) => {
175+
resolve(
176+
context,
177+
urlToRequest(fullPath, true),
178+
(resolveError, result) => {
179+
if (resolveError) {
180+
emitWarning(
181+
`Cannot find source file '${source}': ${resolveError}`
182+
);
183+
184+
return originalData
185+
? promiseResolve({
186+
source: fullPath,
187+
content: originalData,
188+
})
189+
: promiseResolve({ source: fullPath, content: null });
190+
}
191+
192+
return originalData
193+
? promiseResolve({ source: result, content: originalData })
194+
: promiseResolve(readFile(result, 'utf-8', emitWarning));
195+
}
196+
);
197+
});
198+
})
199+
);
200+
} catch (error) {
201+
emitWarning(error);
149202

150-
callback(null, null);
203+
callback(null, input, inputMap);
204+
}
151205

152-
return;
153-
}
206+
const resultMap = { ...map };
207+
resultMap.sources = [];
208+
resultMap.sourcesContent = [];
154209

155-
addDependency(result);
210+
delete resultMap.sourceRoot;
156211

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

163-
callback(null, null);
217+
if (res.source) {
218+
addDependency(res.source);
219+
}
220+
});
164221

165-
return;
166-
}
222+
const sourcesContentIsEmpty =
223+
resultMap.sourcesContent.filter((entry) => !!entry).length === 0;
167224

168-
callback(null, { source: result, content });
169-
});
170-
});
171-
},
172-
(err, info) => {
173-
// eslint-disable-next-line no-param-reassign
174-
map.sourcesContent = map.sourcesContent || [];
175-
176-
info.forEach((res) => {
177-
if (res) {
178-
// eslint-disable-next-line no-param-reassign
179-
map.sources[map.sourcesContent.length] = res.source;
180-
map.sourcesContent.push(res.content);
181-
} else {
182-
map.sourcesContent.push(null);
183-
}
184-
});
225+
if (sourcesContentIsEmpty) {
226+
delete resultMap.sourcesContent;
227+
}
185228

186-
processMap(map, context, callback);
187-
}
188-
);
229+
callback(null, input.replace(match[0], ''), resultMap);
189230
}
190231
}

src/utils/flatten.js renamed to src/utils.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
14
import sourceMap from 'source-map';
25

3-
async function FlattenSourceMap(map) {
6+
async function flattenSourceMap(map) {
47
const consumer = await new sourceMap.SourceMapConsumer(map);
58
let generatedMap;
69

@@ -43,4 +46,43 @@ async function FlattenSourceMap(map) {
4346
return generatedMap.toJSON();
4447
}
4548

46-
module.exports = FlattenSourceMap;
49+
function readFile(fullPath, charset, emitWarning) {
50+
return new Promise((resolve) => {
51+
fs.readFile(fullPath, charset, (readFileError, content) => {
52+
if (readFileError) {
53+
emitWarning(`Cannot open source file '${fullPath}': ${readFileError}`);
54+
55+
resolve({ source: null, content: null });
56+
}
57+
58+
resolve({ source: fullPath, content });
59+
});
60+
});
61+
}
62+
63+
function getContentFromSourcesContent(consumer, source) {
64+
return consumer.sourceContentFor(source, true);
65+
}
66+
67+
function isUrlRequest(url) {
68+
// An URL is not an request if
69+
70+
// 1. It's an absolute url and it is not `windows` path like `C:\dir\file`
71+
if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !path.win32.isAbsolute(url)) {
72+
return false;
73+
}
74+
75+
// 2. It's a protocol-relative
76+
if (/^\/\//.test(url)) {
77+
return false;
78+
}
79+
80+
return true;
81+
}
82+
83+
export {
84+
flattenSourceMap,
85+
readFile,
86+
getContentFromSourcesContent,
87+
isUrlRequest,
88+
};

test/__snapshots__/loader.test.js.snap

+41-9
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ Object {
5050
}
5151
`;
5252

53-
exports[`source-map-loader should process external SourceMaps: warnings 1`] = `Array []`;
53+
exports[`source-map-loader should process external SourceMaps: warnings 1`] = `
54+
Array [
55+
"ModuleWarning: Module Warning (from \`replaced original path\`):
56+
(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'",
57+
]
58+
`;
5459

5560
exports[`source-map-loader should process inlined SourceMaps with charset: css 1`] = `
5661
"with SourceMap
@@ -64,7 +69,7 @@ Object {
6469
"file": "charset-inline-source-map.js",
6570
"mappings": "AAAA",
6671
"sources": Array [
67-
"charset-inline-source-map.txt",
72+
"/test/fixtures/charset-inline-source-map.txt - (normalized for test)",
6873
],
6974
"sourcesContent": Array [
7075
"with SourceMap",
@@ -96,7 +101,32 @@ Object {
96101
}
97102
`;
98103

99-
exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `Array []`;
104+
exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `
105+
Array [
106+
"ModuleWarning: Module Warning (from \`replaced original path\`):
107+
(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'",
108+
]
109+
`;
110+
111+
exports[`source-map-loader should reject http SourceMaps: css 1`] = `
112+
"with SourceMap
113+
//#sourceMappingURL=http://sampledomain.com/external-source-map2.map
114+
// comment
115+
"
116+
`;
117+
118+
exports[`source-map-loader should reject http SourceMaps: errors 1`] = `Array []`;
119+
120+
exports[`source-map-loader should reject http SourceMaps: warnings 1`] = `
121+
Array [
122+
"ModuleWarning: Module Warning (from \`replaced original path\`):
123+
(Emitted value instead of an instance of Error) URL scheme not supported: http:",
124+
]
125+
`;
126+
127+
exports[`source-map-loader should reject not exist file: SourceMaps: errors 1`] = `Array []`;
128+
129+
exports[`source-map-loader should reject not exist file: SourceMaps: warnings 1`] = `"TypeError [ERR_INVALID_FILE"`;
100130

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

135165
exports[`source-map-loader should support indexed sourcemaps: css 1`] = `
136-
"with SourceMap
166+
"console.log('with SourceMap')
137167
// Map taken from here
138168
// https://github.com/mozilla/source-map/blob/master/test/util.js - indexedTestMapDifferentSourceRoots
139169
"
@@ -147,7 +177,7 @@ Object {
147177
"mappings": "CAAC,IAAI,IAAM,SAAU,GAClB,OAAO,IAAI;CCDb,IAAI,IAAM,SAAU,GAClB,OAAO",
148178
"names": Array [],
149179
"sources": Array [
150-
"/the/root/nested1.js",
180+
"/test/fixtures/indexed-sourcemap/nested1.js - (normalized for test)",
151181
"/different/root/nested2.js",
152182
],
153183
"sourcesContent": Array [
@@ -210,7 +240,12 @@ Object {
210240
}
211241
`;
212242

213-
exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `Array []`;
243+
exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `
244+
Array [
245+
"ModuleWarning: Module Warning (from \`replaced original path\`):
246+
(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'",
247+
]
248+
`;
214249

215250
exports[`source-map-loader should warn on invalid SourceMap: css 1`] = `
216251
"with SourceMap
@@ -271,9 +306,6 @@ Object {
271306
"sources": Array [
272307
"missing-source-map2.txt",
273308
],
274-
"sourcesContent": Array [
275-
null,
276-
],
277309
"version": 3,
278310
}
279311
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Content
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
with SourceMap
2+
// #sourceMappingURL=file:///unresolvedPath/map.map
3+
// comment

test/fixtures/file-source-map.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
with SourceMap
2+
// #sourceMappingURL=file://relative-sourceRoot-source-map.map
3+
// comment

test/fixtures/http-source-map.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
with SourceMap
2+
//#sourceMappingURL=http://sampledomain.com/external-source-map2.map
3+
// comment
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
with SourceMap
1+
console.log('with SourceMap')
22
//#sourceMappingURL=file.js.map
33
// Map taken from here
44
// https://github.com/mozilla/source-map/blob/master/test/util.js - indexedTestMapDifferentSourceRoots

0 commit comments

Comments
 (0)