Skip to content

Commit 877d99a

Browse files
fix: normalize sources in source maps
1 parent 62e268a commit 877d99a

10 files changed

+555
-102
lines changed

src/LessError.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ class LessError extends Error {
1616
this.hideStack = true;
1717
}
1818

19-
static getFileExcerptIfPossible(lessErr) {
20-
if (typeof lessErr.extract === 'undefined') {
19+
static getFileExcerptIfPossible(lessError) {
20+
if (typeof lessError.extract === 'undefined') {
2121
return [];
2222
}
2323

24-
const excerpt = lessErr.extract.slice(0, 2);
25-
const column = Math.max(lessErr.column - 1, 0);
24+
const excerpt = lessError.extract.slice(0, 2);
25+
const column = Math.max(lessError.column - 1, 0);
2626

2727
if (typeof excerpt[0] === 'undefined') {
2828
excerpt.shift();

src/index.js

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { getOptions } from 'loader-utils';
66
import validateOptions from 'schema-utils';
77

88
import schema from './options.json';
9-
import { getLessOptions, isUnsupportedUrl } from './utils';
9+
import { getLessOptions, isUnsupportedUrl, normalizeSourceMap } from './utils';
1010
import LessError from './LessError';
1111

12-
function lessLoader(source) {
12+
async function lessLoader(source) {
1313
const options = getOptions(this);
1414

1515
validateOptions(schema, options, {
@@ -19,6 +19,14 @@ function lessLoader(source) {
1919

2020
const callback = this.async();
2121
const lessOptions = getLessOptions(this, options);
22+
const useSourceMap =
23+
typeof options.sourceMap === 'boolean' ? options.sourceMap : this.sourceMap;
24+
25+
if (useSourceMap) {
26+
lessOptions.sourceMap = {
27+
outputSourceFiles: true,
28+
};
29+
}
2230

2331
let data = source;
2432

@@ -29,30 +37,42 @@ function lessLoader(source) {
2937
: `${options.additionalData}\n${data}`;
3038
}
3139

32-
less
33-
.render(data, lessOptions)
34-
.then(({ css, map, imports }) => {
35-
imports.forEach((item) => {
36-
if (isUnsupportedUrl(item)) {
37-
return;
38-
}
39-
40-
// `less` return forward slashes on windows when `webpack` resolver return an absolute windows path in `WebpackFileManager`
41-
// Ref: https://github.com/webpack-contrib/less-loader/issues/357
42-
this.addDependency(path.normalize(item));
43-
});
44-
45-
callback(null, css, typeof map === 'string' ? JSON.parse(map) : map);
46-
})
47-
.catch((lessError) => {
48-
if (lessError.filename) {
49-
// `less` return forward slashes on windows when `webpack` resolver return an absolute windows path in `WebpackFileManager`
50-
// Ref: https://github.com/webpack-contrib/less-loader/issues/357
51-
this.addDependency(path.normalize(lessError.filename));
52-
}
53-
54-
callback(new LessError(lessError));
55-
});
40+
let result;
41+
42+
try {
43+
result = await less.render(data, lessOptions);
44+
} catch (error) {
45+
if (error.filename) {
46+
// `less` return forward slashes on windows when `webpack` resolver return an absolute windows path in `WebpackFileManager`
47+
// Ref: https://github.com/webpack-contrib/less-loader/issues/357
48+
this.addDependency(path.normalize(error.filename));
49+
}
50+
51+
callback(new LessError(error));
52+
53+
return;
54+
}
55+
56+
const { css, imports } = result;
57+
58+
imports.forEach((item) => {
59+
if (isUnsupportedUrl(item)) {
60+
return;
61+
}
62+
63+
// `less` return forward slashes on windows when `webpack` resolver return an absolute windows path in `WebpackFileManager`
64+
// Ref: https://github.com/webpack-contrib/less-loader/issues/357
65+
this.addDependency(path.normalize(item));
66+
});
67+
68+
let map =
69+
typeof result.map === 'string' ? JSON.parse(result.map) : result.map;
70+
71+
if (map && useSourceMap) {
72+
map = normalizeSourceMap(map, this.rootContext);
73+
}
74+
75+
callback(null, css, map);
5676
}
5777

5878
export default lessLoader;

src/utils.js

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ const trailingSlash = /[/\\]$/;
1111
// This somewhat changed in Less 3.x. Now the file name comes without the
1212
// automatically added extension whereas the extension is passed in as `options.ext`.
1313
// So, if the file name matches this regexp, we simply ignore the proposed extension.
14-
const isModuleImport = /^~([^/]+|[^/]+\/|@[^/]+[/][^/]+|@[^/]+\/?|@[^/]+[/][^/]+\/)$/;
14+
const IS_SPECIAL_MODULE_IMPORT = /^~[^/]+$/;
1515

1616
// `[drive_letter]:\` + `\\[server]\[sharename]\`
17-
const isNativeWin32Path = /^[a-zA-Z]:[/\\]|^\\\\/i;
17+
const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
1818

1919
/**
2020
* Creates a Less plugin that uses webpack's resolving engine that is provided by the loaderContext.
@@ -32,7 +32,7 @@ function createWebpackLessPlugin(loaderContext) {
3232

3333
class WebpackFileManager extends less.FileManager {
3434
supports(filename) {
35-
if (filename[0] === '/' || isNativeWin32Path.test(filename)) {
35+
if (filename[0] === '/' || IS_NATIVE_WIN32_PATH.test(filename)) {
3636
return true;
3737
}
3838

@@ -89,7 +89,7 @@ function createWebpackLessPlugin(loaderContext) {
8989
let result;
9090

9191
try {
92-
if (isModuleImport.test(filename)) {
92+
if (IS_SPECIAL_MODULE_IMPORT.test(filename)) {
9393
const error = new Error();
9494

9595
error.type = 'Next';
@@ -171,23 +171,12 @@ function getLessOptions(loaderContext, loaderOptions) {
171171
},
172172
});
173173

174-
const useSourceMap =
175-
typeof loaderOptions.sourceMap === 'boolean'
176-
? loaderOptions.sourceMap
177-
: loaderContext.sourceMap;
178-
179-
if (useSourceMap) {
180-
lessOptions.sourceMap = {
181-
outputSourceFiles: true,
182-
};
183-
}
184-
185174
return lessOptions;
186175
}
187176

188177
function isUnsupportedUrl(url) {
189-
// Is Windows paths `c:\`
190-
if (/^[a-zA-Z]:\\/.test(url)) {
178+
// Is Windows path
179+
if (IS_NATIVE_WIN32_PATH.test(url)) {
191180
return false;
192181
}
193182

@@ -196,4 +185,24 @@ function isUnsupportedUrl(url) {
196185
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url);
197186
}
198187

199-
export { getLessOptions, isUnsupportedUrl };
188+
function normalizeSourceMap(map) {
189+
const newMap = map;
190+
191+
// map.file is an optional property that provides the output filename.
192+
// Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
193+
// eslint-disable-next-line no-param-reassign
194+
delete newMap.file;
195+
196+
// eslint-disable-next-line no-param-reassign
197+
newMap.sourceRoot = '';
198+
199+
// `less` returns POSIX paths, that's why we need to transform them back to native paths.
200+
// eslint-disable-next-line no-param-reassign
201+
newMap.sources = newMap.sources.map((source) => {
202+
return path.normalize(source);
203+
});
204+
205+
return newMap;
206+
}
207+
208+
export { getLessOptions, isUnsupportedUrl, normalizeSourceMap };
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`additionalData option should work additionalData data as function: css 1`] = `
3+
exports[`"additionalData" option should work additionalData data as function: css 1`] = `
44
"/* RelativePath: additional-data.less; */
55
.background {
66
color: coral;
@@ -11,17 +11,17 @@ exports[`additionalData option should work additionalData data as function: css
1111
"
1212
`;
1313

14-
exports[`additionalData option should work additionalData data as function: errors 1`] = `Array []`;
14+
exports[`"additionalData" option should work additionalData data as function: errors 1`] = `Array []`;
1515

16-
exports[`additionalData option should work additionalData data as function: warnings 1`] = `Array []`;
16+
exports[`"additionalData" option should work additionalData data as function: warnings 1`] = `Array []`;
1717

18-
exports[`additionalData option should work additionalData data as string: css 1`] = `
18+
exports[`"additionalData" option should work additionalData data as string: css 1`] = `
1919
".background {
2020
color: coral;
2121
}
2222
"
2323
`;
2424

25-
exports[`additionalData option should work additionalData data as string: errors 1`] = `Array []`;
25+
exports[`"additionalData" option should work additionalData data as string: errors 1`] = `Array []`;
2626

27-
exports[`additionalData option should work additionalData data as string: warnings 1`] = `Array []`;
27+
exports[`"additionalData" option should work additionalData data as string: warnings 1`] = `Array []`;

0 commit comments

Comments
 (0)