Skip to content

Commit

Permalink
feat: added pluginManager.webpackLoaderContext, deprecated `less.we…
Browse files Browse the repository at this point in the history
…bpackLoaderContext`, it fixed memory leak (#412)
  • Loading branch information
cap-Bernardito committed Apr 9, 2021
1 parent 265179f commit e576240
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 16 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ module.exports = {
rules: [
{
test: /\.less$/i,
loader: [ // compiles Less to CSS
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
Expand Down Expand Up @@ -529,13 +530,13 @@ 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.
> ℹ️ Access to the [loader context](https://webpack.js.org/api/loaders/#the-loader-context) inside the custom plugin can be done using the `pluginManager.webpackLoaderContext` property.
```js
module.exports = {
install: function (less, pluginManager, functions) {
functions.add("pi", function () {
// Loader context is available in `less.webpackLoaderContext`
// Loader context is available in `pluginManager.webpackLoaderContext`

return Math.PI;
});
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ async function lessLoader(source) {
return;
}

if ("webpackLoaderContext" in less) {
delete less.webpackLoaderContext;
}

const { css, imports } = result;

imports.forEach((item) => {
Expand Down
21 changes: 19 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from "path";
import util from "util";

import less from "less";
import { klona } from "klona/full";
Expand Down Expand Up @@ -181,10 +182,26 @@ function getLessOptions(loaderContext, loaderOptions) {
lessOptions.plugins.unshift(createWebpackLessPlugin(loaderContext));
}

const webpackContextDeprecated = util.deprecate(
(context) => context,
"less.webpackLoaderContext is deprecated and will be removed in next major release. Instead use pluginManager.webpackLoaderContext (https://webpack.js.org/loaders/less-loader/#plugins)"
);

lessOptions.plugins.unshift({
install(lessProcessor) {
install(lessProcessor, pluginManager) {
// eslint-disable-next-line no-param-reassign
lessProcessor.webpackLoaderContext = loaderContext;
pluginManager.webpackLoaderContext = loaderContext;

// Todo remove in next major release
if (typeof lessProcessor.webpackLoaderContext === "undefined") {
Object.defineProperty(lessProcessor, "webpackLoaderContext", {
configurable: true,

get() {
return webpackContextDeprecated(loaderContext);
},
});
}
},
});

Expand Down
29 changes: 20 additions & 9 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,26 @@ exports[`loader should delegate resolving (LESS) imports with URLs to "less" pac
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc9.ttf) format('truetype');
}
"
`;
Expand Down Expand Up @@ -157,25 +157,25 @@ exports[`loader should not add to dependencies imports with URLs: css 1`] = `
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: url(http://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
src: url(http://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
}
"
`;
Expand Down Expand Up @@ -579,6 +579,17 @@ exports[`loader should work lessOptions.relativeUrls is true: errors 1`] = `Arra
exports[`loader should work lessOptions.relativeUrls is true: warnings 1`] = `Array []`;
exports[`loader should work loaderContext in less plugins 2: css 1`] = `
".webpackLoaderContext {
isDefined: true;
}
"
`;
exports[`loader should work loaderContext in less plugins 2: errors 1`] = `Array []`;
exports[`loader should work loaderContext in less plugins 2: warnings 1`] = `Array []`;
exports[`loader should work loaderContext in less plugins: css 1`] = `
".webpackLoaderContext {
isDefined: true;
Expand All @@ -598,7 +609,7 @@ exports[`loader should work third-party plugins as fileLoader: css 1`] = `
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmEU9fBBc9.ttf) format('truetype');
}
.modules-dir-scope-module {
color: hotpink;
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/basic-plugins-2.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@plugin "plugin-2";

.webpackLoaderContext {
isDefined: run();
}
11 changes: 11 additions & 0 deletions test/fixtures/plugin-2.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 pluginManager.webpackLoaderContext !== 'undefined') {
return 'true';
}

return 'false';
});
}
})
50 changes: 48 additions & 2 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ describe("loader", () => {
constructor(less) {
super();

if (typeof less.webpackLoaderContext !== "undefined") {
if (typeof less.webpackLoaderContext.version !== "undefined") {
contextInClass = true;
}
}
Expand All @@ -817,7 +817,7 @@ describe("loader", () => {

const customObjectPlugin = {
install(less) {
if (typeof less.webpackLoaderContext !== "undefined") {
if (typeof less.webpackLoaderContext.version !== "undefined") {
contextInObject = true;
}
},
Expand All @@ -839,6 +839,52 @@ describe("loader", () => {
expect(getErrors(stats)).toMatchSnapshot("errors");
});

it("should work loaderContext in less plugins 2", async () => {
let contextInClass;
let contextInObject;

// eslint-disable-next-line global-require
class Plugin extends require("less").FileManager {
constructor(less, pluginManager) {
super();

if (typeof pluginManager.webpackLoaderContext !== "undefined") {
contextInClass = true;
}
}
}

class CustomClassPlugin {
// eslint-disable-next-line class-methods-use-this
install(less, pluginManager) {
pluginManager.addFileManager(new Plugin(less, pluginManager));
}
}

const customObjectPlugin = {
install(less, packageManager) {
if (typeof packageManager.webpackLoaderContext !== "undefined") {
contextInObject = true;
}
},
};

const testId = "./basic-plugins-2.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");
});

// TODO bug on windows
it.skip("should work with circular imports", async () => {
const testId = "./circular.less";
Expand Down

0 comments on commit e576240

Please sign in to comment.