Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/basic-plugins.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@plugin "plugin-1";

.webpackLoaderContext {
isDefined: run();
}
11 changes: 11 additions & 0 deletions test/fixtures/plugin-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
registerPlugin({
install: function(less, pluginManager, functions) {
functions.add('run', function() {
if (typeof less.webpackLoaderContext !== 'undefined') {
return 'true';
}

return 'false';
});
}
})
46 changes: 46 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});