Skip to content

Commit 53da71a

Browse files
refactor: sourcemap paths
BREAKING CHANGE: source map contains absolute `sources`
1 parent d7bc470 commit 53da71a

File tree

9 files changed

+303
-24
lines changed

9 files changed

+303
-24
lines changed

package-lock.json

Lines changed: 56 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"dependencies": {
4545
"cosmiconfig": "^7.0.0",
4646
"loader-utils": "^2.0.0",
47+
"normalize-path": "^3.0.0",
4748
"postcss": "^7.0.0",
4849
"schema-utils": "^2.7.0"
4950
},
@@ -67,6 +68,7 @@
6768
"jest": "^26.2.2",
6869
"jest-junit": "^11.1.0",
6970
"jsdoc-to-markdown": "^6.0.1",
71+
"less-loader": "^6.2.0",
7072
"lint-staged": "^10.2.11",
7173
"memfs": "^3.2.0",
7274
"midas": "^2.0.3",

src/index.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ import postcss from 'postcss';
88
import Warning from './Warning';
99
import SyntaxError from './Error';
1010
import schema from './options.json';
11-
import { exec, loadConfig, getArrayPlugins } from './utils';
11+
import {
12+
exec,
13+
loadConfig,
14+
getArrayPlugins,
15+
getSourceMapAbsolutePath,
16+
getSourceMapRelativePath,
17+
normalizeSourceMap,
18+
} from './utils';
1219

1320
/**
1421
* **PostCSS Loader**
@@ -103,8 +110,18 @@ export default async function loader(content, sourceMap, meta = {}) {
103110
? options.sourceMap
104111
: this.sourceMap;
105112

113+
const sourceMapNormalized =
114+
sourceMap && useSourceMap ? normalizeSourceMap(sourceMap) : null;
115+
116+
if (sourceMapNormalized) {
117+
sourceMapNormalized.sources = sourceMapNormalized.sources.map((src) =>
118+
getSourceMapRelativePath(src, path.dirname(file))
119+
);
120+
}
121+
106122
const postcssOptions = {
107123
from: file,
124+
to: file,
108125
map: useSourceMap
109126
? options.sourceMap === 'inline'
110127
? { inline: true, annotation: false }
@@ -115,9 +132,8 @@ export default async function loader(content, sourceMap, meta = {}) {
115132
stringifier,
116133
};
117134

118-
if (postcssOptions.map && sourceMap) {
119-
postcssOptions.map.prev =
120-
typeof sourceMap === 'string' ? JSON.parse(sourceMap) : sourceMap;
135+
if (postcssOptions.map && sourceMapNormalized) {
136+
postcssOptions.map.prev = sourceMapNormalized;
121137
}
122138

123139
// Loader Exec (Deprecated)
@@ -210,8 +226,13 @@ export default async function loader(content, sourceMap, meta = {}) {
210226
map = map ? map.toJSON() : null;
211227

212228
if (map) {
213-
map.file = path.resolve(map.file);
214-
map.sources = map.sources.map((src) => path.resolve(src));
229+
if (typeof map.file !== 'undefined') {
230+
delete map.file;
231+
}
232+
233+
map.sources = map.sources.map((src) =>
234+
getSourceMapAbsolutePath(src, postcssOptions.to)
235+
);
215236
}
216237

217238
const ast = {

src/utils.js

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import path from 'path';
22

33
import Module from 'module';
44

5+
import normalizePath from 'normalize-path';
6+
57
import postcssPkg from 'postcss/package.json';
68
import { cosmiconfig } from 'cosmiconfig';
79

@@ -242,4 +244,75 @@ function getArrayPlugins(plugins, file, disabledPlugins, loaderContext) {
242244
return pluginsProcessing(plugins, file, disabledPlugins);
243245
}
244246

245-
export { exec, loadConfig, getArrayPlugins };
247+
// TODO Remove, when postcss 8 will be released
248+
function normalizeSourceMap(map) {
249+
let newMap = map;
250+
251+
// Some loader emit source map as string
252+
// Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
253+
if (typeof newMap === 'string') {
254+
newMap = JSON.parse(newMap);
255+
}
256+
257+
// Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
258+
// We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
259+
260+
if (newMap.file) {
261+
delete newMap.file;
262+
}
263+
264+
const { sourceRoot } = newMap;
265+
266+
if (newMap.sourceRoot) {
267+
delete newMap.sourceRoot;
268+
}
269+
270+
if (newMap.sources) {
271+
newMap.sources = newMap.sources.map((source) => {
272+
return !sourceRoot
273+
? normalizePath(source)
274+
: normalizePath(path.resolve(sourceRoot, source));
275+
});
276+
}
277+
278+
return newMap;
279+
}
280+
281+
function getSourceMapRelativePath(file, from) {
282+
if (file.indexOf('<') === 0) return file;
283+
if (/^\w+:\/\//.test(file)) return file;
284+
285+
const result = path.relative(from, file);
286+
287+
if (path.sep === '\\') {
288+
return result.replace(/\\/g, '/');
289+
}
290+
291+
return result;
292+
}
293+
294+
function getSourceMapAbsolutePath(file, to) {
295+
if (file.indexOf('<') === 0) return file;
296+
if (/^\w+:\/\//.test(file)) return file;
297+
298+
if (typeof to === 'undefined') return file;
299+
300+
const dirname = path.dirname(to);
301+
302+
const result = path.resolve(dirname, file);
303+
304+
if (path.sep === '\\') {
305+
return result.replace(/\\/g, '/');
306+
}
307+
308+
return result;
309+
}
310+
311+
export {
312+
exec,
313+
loadConfig,
314+
getArrayPlugins,
315+
getSourceMapAbsolutePath,
316+
getSourceMapRelativePath,
317+
normalizeSourceMap,
318+
};

test/fixtures/less/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import style from './style.less'
2+
3+
export default style

test/fixtures/less/style.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a { color: coral }

test/helpers/getCodeFromBundle.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import normalizeMap from './normalizeMap';
22

3-
export default (id, stats) => {
3+
export default (id, stats, processMap = true) => {
44
const { modules } = stats.compilation;
55
const module = modules.find((m) => m.id.endsWith(id));
66
const { _source } = module;
@@ -21,5 +21,5 @@ export default (id, stats) => {
2121

2222
const { css, map } = result;
2323

24-
return { css, map: normalizeMap(map) };
24+
return { css, map: processMap ? normalizeMap(map) : map };
2525
};

test/options/__snapshots__/sourceMap.test.js.snap

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Options Sourcemap should generated absolute paths in sourcemap: css 1`] = `
4+
"a { color: black }
5+
"
6+
`;
7+
8+
exports[`Options Sourcemap should generated absolute paths in sourcemap: errors 1`] = `Array []`;
9+
10+
exports[`Options Sourcemap should generated absolute paths in sourcemap: map 1`] = `
11+
Object {
12+
"mappings": "AAAA,IAAI,aAAa",
13+
"names": Array [],
14+
"sources": Array [
15+
"xxx",
16+
],
17+
"sourcesContent": Array [
18+
"a { color: black }
19+
",
20+
],
21+
"version": 3,
22+
}
23+
`;
24+
25+
exports[`Options Sourcemap should generated absolute paths in sourcemap: warnings 1`] = `Array []`;
26+
327
exports[`Options Sourcemap should work Sourcemap - {Boolean}: css 1`] = `
428
"a { color: black }
529
"
@@ -9,7 +33,6 @@ exports[`Options Sourcemap should work Sourcemap - {Boolean}: errors 1`] = `Arra
933

1034
exports[`Options Sourcemap should work Sourcemap - {Boolean}: map 1`] = `
1135
Object {
12-
"file": "x",
1336
"mappings": "AAAA,IAAI,aAAa",
1437
"names": Array [],
1538
"sources": Array [
@@ -28,7 +51,7 @@ exports[`Options Sourcemap should work Sourcemap - {Boolean}: warnings 1`] = `Ar
2851
exports[`Options Sourcemap should work Sourcemap - {String}: css 1`] = `
2952
"a { color: black }
3053
31-
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvZml4dHVyZXMvY3NzL3N0eWxlLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxJQUFJLGFBQWEiLCJmaWxlIjoidGVzdC9maXh0dXJlcy9jc3Mvc3R5bGUuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiYSB7IGNvbG9yOiBibGFjayB9XG4iXX0= */"
54+
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0eWxlLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxJQUFJLGFBQWEiLCJmaWxlIjoic3R5bGUuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiYSB7IGNvbG9yOiBibGFjayB9XG4iXX0= */"
3255
`;
3356

3457
exports[`Options Sourcemap should work Sourcemap - {String}: errors 1`] = `Array []`;
@@ -44,32 +67,53 @@ exports[`Options Sourcemap should work disable Sourcemap - {Boolean}: errors 1`]
4467

4568
exports[`Options Sourcemap should work disable Sourcemap - {Boolean}: warnings 1`] = `Array []`;
4669

47-
exports[`Options Sourcemap should work with prev sourceMap: css 1`] = `
70+
exports[`Options Sourcemap should work with prev sourceMap (less-loader): css 1`] = `
4871
"a {
4972
color: coral;
50-
}"
73+
}
74+
"
5175
`;
5276

53-
exports[`Options Sourcemap should work with prev sourceMap: errors 1`] = `Array []`;
77+
exports[`Options Sourcemap should work with prev sourceMap (less-loader): errors 1`] = `Array []`;
5478

55-
exports[`Options Sourcemap should work with prev sourceMap: map 1`] = `
79+
exports[`Options Sourcemap should work with prev sourceMap (less-loader): map 1`] = `
5680
Object {
57-
"file": "x",
58-
"mappings": "AAAA;EAAI,YAAA;ACEJ",
81+
"mappings": "AAAA;EAAI,YAAA;AAEJ",
5982
"names": Array [],
6083
"sources": Array [
6184
"xxx",
62-
"xxx",
6385
],
6486
"sourcesContent": Array [
6587
"a { color: coral }
6688
",
67-
"a {
89+
],
90+
"version": 3,
91+
}
92+
`;
93+
94+
exports[`Options Sourcemap should work with prev sourceMap (less-loader): warnings 1`] = `Array []`;
95+
96+
exports[`Options Sourcemap should work with prev sourceMap (sass-loader): css 1`] = `
97+
"a {
6898
color: coral;
69-
}",
99+
}"
100+
`;
101+
102+
exports[`Options Sourcemap should work with prev sourceMap (sass-loader): errors 1`] = `Array []`;
103+
104+
exports[`Options Sourcemap should work with prev sourceMap (sass-loader): map 1`] = `
105+
Object {
106+
"mappings": "AAAA;EAAI,YAAA;AAEJ",
107+
"names": Array [],
108+
"sources": Array [
109+
"xxx",
110+
],
111+
"sourcesContent": Array [
112+
"a { color: coral }
113+
",
70114
],
71115
"version": 3,
72116
}
73117
`;
74118

75-
exports[`Options Sourcemap should work with prev sourceMap: warnings 1`] = `Array []`;
119+
exports[`Options Sourcemap should work with prev sourceMap (sass-loader): warnings 1`] = `Array []`;

0 commit comments

Comments
 (0)