Skip to content

Commit 98d4e63

Browse files
committed
fix: resolve alias
Less automatically adds a .less file extension if no extension was given. This is problematic if there is a module request like @import "~some-module"; because in this case Less will call our file manager with `~some-module.less`. Since dots in module names are highly discouraged, we can safely assume that this is an error and we need to remove the .less extension again. This means that aliases configured in the `webpack.config.js` are now resolved correctly if the webpack resolver is used. BREAKING CHANGE: If you've already configured your `resolve.alias` with a `.less` extension, you can now remove that wrong extension.
1 parent c88832f commit 98d4e63

File tree

6 files changed

+39
-6
lines changed

6 files changed

+39
-6
lines changed

src/createWebpackLessPlugin.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@ const loaderUtils = require('loader-utils');
33
const pify = require('pify');
44

55
const stringifyLoader = require.resolve('./stringifyLoader.js');
6-
const trailingSlash = /[\\/]$/;
6+
const trailingSlash = /[/\\]$/;
77
const isLessCompatible = /\.(le|c)ss$/;
8+
// Less automatically adds a .less file extension if no extension was given.
9+
// This is problematic if there is a module request like @import "~some-module";
10+
// because in this case Less will call our file manager with `~some-module.less`.
11+
// Since dots in module names are highly discouraged, we can safely assume that
12+
// this is an error and we need to remove the .less extension again.
13+
// However, we must not match something like @import "~some-module/file.less";
14+
const matchMalformedModuleFilename = /(~[^/\\]+)\.less$/;
815

916
/**
1017
* Creates a Less plugin that uses webpack's resolving engine that is provided by the loaderContext.
@@ -26,7 +33,8 @@ function createWebpackLessPlugin(loaderContext) {
2633
}
2734

2835
loadFile(filename, currentDirectory /* , options, environment */) { // eslint-disable-line class-methods-use-this
29-
const moduleRequest = loaderUtils.urlToRequest(filename);
36+
const url = filename.replace(matchMalformedModuleFilename, '$1');
37+
const moduleRequest = loaderUtils.urlToRequest(url);
3038
// Less is giving us trailing slashes, but the context should have no trailing slash
3139
const context = currentDirectory.replace(trailingSlash, '');
3240
let resolvedFilename;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import "~some"; // test if package.json's are correctly resolved
2+
3+
.some-class {
4+
background: hotpink;
5+
}
6+
7+
@import "~aliased-some"; // should also resolve to ~some

test/fixtures/node_modules/some/package.json

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

test/helpers/compile.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const webpack = require('webpack');
44
const fixturePath = path.resolve(__dirname, '..', 'fixtures');
55
const outputPath = path.resolve(__dirname, '..', 'output');
66

7-
function compile(fixture, moduleRules) {
7+
function compile(fixture, moduleRules, resolveAlias = {}) {
88
return new Promise((resolve, reject) => {
99
const entry = path.resolve(fixturePath, 'less', `${fixture}.less`);
1010

@@ -17,6 +17,9 @@ function compile(fixture, moduleRules) {
1717
module: {
1818
rules: moduleRules,
1919
},
20+
resolve: {
21+
alias: resolveAlias,
22+
},
2023
}, (err, stats) => {
2124
const problem = err || stats.compilation.errors[0] || stats.compilation.warnings[0];
2225

test/helpers/createSpec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const ignore = [
1515
];
1616
const lessReplacements = [
1717
[/~some\//g, '../node_modules/some/'],
18+
[/~(aliased-)?some"/g, '../node_modules/some/module.less"'],
1819
];
1920
const cssReplacements = [
2021
[/\.\.\/node_modules\/some\//g, '~some/'],

test/index.test.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ const { readCssFixture, readSourceMap } = require('./helpers/readFixture');
55

66
const nodeModulesPath = path.resolve(__dirname, 'fixtures', 'node_modules');
77

8-
async function compileAndCompare(fixture, lessLoaderOptions, lessLoaderContext) {
8+
async function compileAndCompare(fixture, { lessLoaderOptions, lessLoaderContext, resolveAlias } = {}) {
99
let inspect;
1010
const rules = moduleRules.basic(lessLoaderOptions, lessLoaderContext, (i) => {
1111
inspect = i;
1212
});
1313
const [expectedCss] = await Promise.all([
1414
readCssFixture(fixture),
15-
compile(fixture, rules),
15+
compile(fixture, rules, resolveAlias),
1616
]);
1717
const [actualCss] = inspect.arguments;
1818

@@ -31,8 +31,18 @@ test('should resolve all imports from node_modules using webpack\'s resolver', a
3131
await compileAndCompare('import-webpack');
3232
});
3333

34+
test('should resolve aliases as configured', async () => {
35+
await compileAndCompare('import-webpack-alias', {
36+
resolveAlias: {
37+
'aliased-some': 'some',
38+
},
39+
});
40+
});
41+
3442
test('should resolve all imports from the given paths using Less\' resolver', async () => {
35-
await compileAndCompare('import-paths', { paths: [__dirname, nodeModulesPath] });
43+
await compileAndCompare('import-paths', {
44+
lessLoaderOptions: { paths: [__dirname, nodeModulesPath] },
45+
});
3646
});
3747

3848
test('should allow to disable webpack\'s resolver by passing an empty paths array', async () => {

0 commit comments

Comments
 (0)