diff --git a/README.md b/README.md index e624e831..17c25bac 100644 --- a/README.md +++ b/README.md @@ -476,8 +476,7 @@ module.exports = { ### Plugins -In order to use [plugins](http://lesscss.org/usage/#plugins), simply set the -`plugins` option like this: +In order to use [plugins](http://lesscss.org/usage/#plugins), simply set the `plugins` option like this: ```js // webpack.config.js @@ -499,6 +498,20 @@ module.exports = { }; ``` +> ℹ️ 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. + +```js +module.exports = { + install: function (less, pluginManager, functions) { + functions.add('pi', function () { + // Loader context is available in `less.webpackLoaderContext` + + return Math.PI; + }); + }, +}; +``` + ### Extracting style sheets 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. diff --git a/src/utils.js b/src/utils.js index b2113be6..400f1b5a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -166,6 +166,13 @@ function getLessOptions(loaderContext, loaderOptions) { lessOptions.plugins.unshift(createWebpackLessPlugin(loaderContext)); } + lessOptions.plugins.unshift({ + install(lessProcessor) { + // eslint-disable-next-line no-param-reassign + lessProcessor.webpackLoaderContext = loaderContext; + }, + }); + const useSourceMap = typeof loaderOptions.sourceMap === 'boolean' ? loaderOptions.sourceMap diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 6d4b7738..bc893e50 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -511,6 +511,17 @@ exports[`loader should watch imports correctly: errors 1`] = `Array []`; exports[`loader should watch imports correctly: warnings 1`] = `Array []`; +exports[`loader should work loaderContext in less plugins: css 1`] = ` +".webpackLoaderContext { + isDefined: true; +} +" +`; + +exports[`loader should work loaderContext in less plugins: errors 1`] = `Array []`; + +exports[`loader should work loaderContext in less plugins: warnings 1`] = `Array []`; + exports[`loader should work third-party plugins as fileLoader: css 1`] = ` ".file-loader { background: coral; diff --git a/test/fixtures/basic-plugins.less b/test/fixtures/basic-plugins.less new file mode 100644 index 00000000..45ce5338 --- /dev/null +++ b/test/fixtures/basic-plugins.less @@ -0,0 +1,5 @@ +@plugin "plugin-1"; + +.webpackLoaderContext { + isDefined: run(); +} diff --git a/test/fixtures/plugin-1.js b/test/fixtures/plugin-1.js new file mode 100644 index 00000000..eaaa5f3c --- /dev/null +++ b/test/fixtures/plugin-1.js @@ -0,0 +1,11 @@ +registerPlugin({ + install: function(less, pluginManager, functions) { + functions.add('run', function() { + if (typeof less.webpackLoaderContext !== 'undefined') { + return 'true'; + } + + return 'false'; + }); + } +}) diff --git a/test/loader.test.js b/test/loader.test.js index e8076eb8..37ecb68a 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -727,4 +727,50 @@ describe('loader', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work loaderContext in less plugins', async () => { + let contextInClass; + let contextInObject; + + // eslint-disable-next-line global-require + class Plugin extends require('less').FileManager { + constructor(less) { + super(); + + if (typeof less.webpackLoaderContext !== 'undefined') { + contextInClass = true; + } + } + } + + class CustomClassPlugin { + // eslint-disable-next-line class-methods-use-this + install(less, pluginManager) { + pluginManager.addFileManager(new Plugin(less)); + } + } + + const customObjectPlugin = { + install(less) { + if (typeof less.webpackLoaderContext !== 'undefined') { + contextInObject = true; + } + }, + }; + + const testId = './basic-plugins.less'; + const compiler = getCompiler(testId, { + lessOptions: { + plugins: [new CustomClassPlugin(), customObjectPlugin], + }, + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + + expect(contextInClass).toBe(true); + expect(contextInObject).toBe(true); + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); });