Skip to content

Commit 7b7fc5e

Browse files
feat: add loader context in less plugins (#378)
1 parent 12dca5b commit 7b7fc5e

File tree

6 files changed

+95
-2
lines changed

6 files changed

+95
-2
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,7 @@ module.exports = {
476476

477477
### Plugins
478478

479-
In order to use [plugins](http://lesscss.org/usage/#plugins), simply set the
480-
`plugins` option like this:
479+
In order to use [plugins](http://lesscss.org/usage/#plugins), simply set the `plugins` option like this:
481480

482481
```js
483482
// webpack.config.js
@@ -499,6 +498,20 @@ module.exports = {
499498
};
500499
```
501500

501+
> ℹ️ Access to the [loader context](https://webpack.js.org/api/loaders/#the-loader-context) inside the custom plugin can be done using the `less.webpackLoaderContext` property.
502+
503+
```js
504+
module.exports = {
505+
install: function (less, pluginManager, functions) {
506+
functions.add('pi', function () {
507+
// Loader context is available in `less.webpackLoaderContext`
508+
509+
return Math.PI;
510+
});
511+
},
512+
};
513+
```
514+
502515
### Extracting style sheets
503516

504517
Bundling CSS with webpack has some nice advantages like referencing images and fonts with hashed urls or [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/) in development. In production, on the other hand, it's not a good idea to apply your style sheets depending on JS execution. Rendering may be delayed or even a [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) might be visible. Thus it's often still better to have them as separate files in your final production build.

src/utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ function getLessOptions(loaderContext, loaderOptions) {
166166
lessOptions.plugins.unshift(createWebpackLessPlugin(loaderContext));
167167
}
168168

169+
lessOptions.plugins.unshift({
170+
install(lessProcessor) {
171+
// eslint-disable-next-line no-param-reassign
172+
lessProcessor.webpackLoaderContext = loaderContext;
173+
},
174+
});
175+
169176
const useSourceMap =
170177
typeof loaderOptions.sourceMap === 'boolean'
171178
? loaderOptions.sourceMap

test/__snapshots__/loader.test.js.snap

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,17 @@ exports[`loader should watch imports correctly: errors 1`] = `Array []`;
511511
512512
exports[`loader should watch imports correctly: warnings 1`] = `Array []`;
513513
514+
exports[`loader should work loaderContext in less plugins: css 1`] = `
515+
".webpackLoaderContext {
516+
isDefined: true;
517+
}
518+
"
519+
`;
520+
521+
exports[`loader should work loaderContext in less plugins: errors 1`] = `Array []`;
522+
523+
exports[`loader should work loaderContext in less plugins: warnings 1`] = `Array []`;
524+
514525
exports[`loader should work third-party plugins as fileLoader: css 1`] = `
515526
".file-loader {
516527
background: coral;

test/fixtures/basic-plugins.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@plugin "plugin-1";
2+
3+
.webpackLoaderContext {
4+
isDefined: run();
5+
}

test/fixtures/plugin-1.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
registerPlugin({
2+
install: function(less, pluginManager, functions) {
3+
functions.add('run', function() {
4+
if (typeof less.webpackLoaderContext !== 'undefined') {
5+
return 'true';
6+
}
7+
8+
return 'false';
9+
});
10+
}
11+
})

test/loader.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,4 +727,50 @@ describe('loader', () => {
727727
expect(getWarnings(stats)).toMatchSnapshot('warnings');
728728
expect(getErrors(stats)).toMatchSnapshot('errors');
729729
});
730+
731+
it('should work loaderContext in less plugins', async () => {
732+
let contextInClass;
733+
let contextInObject;
734+
735+
// eslint-disable-next-line global-require
736+
class Plugin extends require('less').FileManager {
737+
constructor(less) {
738+
super();
739+
740+
if (typeof less.webpackLoaderContext !== 'undefined') {
741+
contextInClass = true;
742+
}
743+
}
744+
}
745+
746+
class CustomClassPlugin {
747+
// eslint-disable-next-line class-methods-use-this
748+
install(less, pluginManager) {
749+
pluginManager.addFileManager(new Plugin(less));
750+
}
751+
}
752+
753+
const customObjectPlugin = {
754+
install(less) {
755+
if (typeof less.webpackLoaderContext !== 'undefined') {
756+
contextInObject = true;
757+
}
758+
},
759+
};
760+
761+
const testId = './basic-plugins.less';
762+
const compiler = getCompiler(testId, {
763+
lessOptions: {
764+
plugins: [new CustomClassPlugin(), customObjectPlugin],
765+
},
766+
});
767+
const stats = await compile(compiler);
768+
const codeFromBundle = getCodeFromBundle(stats, compiler);
769+
770+
expect(contextInClass).toBe(true);
771+
expect(contextInObject).toBe(true);
772+
expect(codeFromBundle.css).toMatchSnapshot('css');
773+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
774+
expect(getErrors(stats)).toMatchSnapshot('errors');
775+
});
730776
});

0 commit comments

Comments
 (0)