Skip to content

Commit 65519d0

Browse files
authored
feat: add function support for locals (loader) (#985)
1 parent 418fd09 commit 65519d0

File tree

9 files changed

+177
-6
lines changed

9 files changed

+177
-6
lines changed

src/loader.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
BASE_URI,
99
SINGLE_DOT_PATH_SEGMENT,
1010
stringifyRequest,
11+
stringifyLocal,
1112
} = require("./utils");
1213
const schema = require("./loader-options.json");
1314

@@ -22,6 +23,7 @@ const MiniCssExtractPlugin = require("./index");
2223
/** @typedef {import("webpack").AssetInfo} AssetInfo */
2324
/** @typedef {import("webpack").NormalModule} NormalModule */
2425
/** @typedef {import("./index.js").LoaderOptions} LoaderOptions */
26+
/** @typedef {{ [key: string]: string | function }} Locals */
2527

2628
/** @typedef {any} TODO */
2729

@@ -38,7 +40,7 @@ const MiniCssExtractPlugin = require("./index");
3840

3941
/**
4042
* @param {string} content
41-
* @param {{ loaderContext: import("webpack").LoaderContext<LoaderOptions>, options: LoaderOptions, locals: {[key: string]: string } | undefined }} context
43+
* @param {{ loaderContext: import("webpack").LoaderContext<LoaderOptions>, options: LoaderOptions, locals: Locals | undefined }} context
4244
* @returns {string}
4345
*/
4446
function hotLoader(content, context) {
@@ -95,7 +97,7 @@ function pitch(request) {
9597
* @returns {void}
9698
*/
9799
const handleExports = (originalExports, compilation, assets, assetsInfo) => {
98-
/** @type {{[key: string]: string } | undefined} */
100+
/** @type {Locals | undefined} */
99101
let locals;
100102
let namedExport;
101103

@@ -170,7 +172,7 @@ function pitch(request) {
170172
locals = {};
171173
}
172174

173-
locals[key] = originalExports[key];
175+
/** @type {Locals} */ (locals)[key] = originalExports[key];
174176
}
175177
});
176178
} else {
@@ -228,9 +230,8 @@ function pitch(request) {
228230
? Object.keys(locals)
229231
.map(
230232
(key) =>
231-
`\nexport var ${key} = ${JSON.stringify(
232-
/** @type {{[key: string]: string }} */
233-
(locals)[key]
233+
`\nexport var ${key} = ${stringifyLocal(
234+
/** @type {Locals} */ (locals)[key]
234235
)};`
235236
)
236237
.join("")

src/utils.js

+10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ function getUndoPath(filename, outputPath, enforceRelative) {
205205
: append;
206206
}
207207

208+
/**
209+
*
210+
* @param {string | function} value
211+
* @returns {string}
212+
*/
213+
function stringifyLocal(value) {
214+
return typeof value === "function" ? value.toString() : JSON.stringify(value);
215+
}
216+
208217
module.exports = {
209218
trueFn,
210219
findModuleById,
@@ -216,5 +225,6 @@ module.exports = {
216225
BASE_URI,
217226
SINGLE_DOT_PATH_SEGMENT,
218227
stringifyRequest,
228+
stringifyLocal,
219229
getUndoPath,
220230
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { cnA, cnB } from "./style.css";
2+
3+
// eslint-disable-next-line no-console
4+
console.log(cnA(), cnB());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default function loader() {
2+
const callback = this.async();
3+
4+
callback(
5+
null,
6+
`export default [
7+
[module.id, ".class-name-a {background: red;}", ""],
8+
[module.id, ".class-name-b {background: blue;}", ""],
9+
];
10+
11+
export var cnA = () => "class-name-a";
12+
export var cnB = () => "class-name-b";`
13+
);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.class-name-a {
2+
background: red;
3+
}
4+
5+
.class-name-b {
6+
background: blue;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.class-name-a {background: red;}
2+
.class-name-b {background: blue;}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/******/ (() => { // webpackBootstrap
2+
/******/ "use strict";
3+
/******/ var __webpack_modules__ = ([
4+
/* 0 */,
5+
/* 1 */
6+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7+
8+
__webpack_require__.r(__webpack_exports__);
9+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
10+
/* harmony export */ "cnA": () => (/* binding */ cnA),
11+
/* harmony export */ "cnB": () => (/* binding */ cnB)
12+
/* harmony export */ });
13+
// extracted by mini-css-extract-plugin
14+
var cnA = () => "class-name-a";
15+
var cnB = () => "class-name-b";
16+
17+
/***/ })
18+
/******/ ]);
19+
/************************************************************************/
20+
/******/ // The module cache
21+
/******/ var __webpack_module_cache__ = {};
22+
/******/
23+
/******/ // The require function
24+
/******/ function __webpack_require__(moduleId) {
25+
/******/ // Check if module is in cache
26+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
27+
/******/ if (cachedModule !== undefined) {
28+
/******/ return cachedModule.exports;
29+
/******/ }
30+
/******/ // Create a new module (and put it into the cache)
31+
/******/ var module = __webpack_module_cache__[moduleId] = {
32+
/******/ // no module.id needed
33+
/******/ // no module.loaded needed
34+
/******/ exports: {}
35+
/******/ };
36+
/******/
37+
/******/ // Execute the module function
38+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
39+
/******/
40+
/******/ // Return the exports of the module
41+
/******/ return module.exports;
42+
/******/ }
43+
/******/
44+
/************************************************************************/
45+
/******/ /* webpack/runtime/define property getters */
46+
/******/ (() => {
47+
/******/ // define getter functions for harmony exports
48+
/******/ __webpack_require__.d = (exports, definition) => {
49+
/******/ for(var key in definition) {
50+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
51+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
52+
/******/ }
53+
/******/ }
54+
/******/ };
55+
/******/ })();
56+
/******/
57+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
58+
/******/ (() => {
59+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
60+
/******/ })();
61+
/******/
62+
/******/ /* webpack/runtime/make namespace object */
63+
/******/ (() => {
64+
/******/ // define __esModule on exports
65+
/******/ __webpack_require__.r = (exports) => {
66+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
67+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
68+
/******/ }
69+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
70+
/******/ };
71+
/******/ })();
72+
/******/
73+
/************************************************************************/
74+
var __webpack_exports__ = {};
75+
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
76+
(() => {
77+
__webpack_require__.r(__webpack_exports__);
78+
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
79+
80+
81+
// eslint-disable-next-line no-console
82+
console.log((0,_style_css__WEBPACK_IMPORTED_MODULE_0__.cnA)(), (0,_style_css__WEBPACK_IMPORTED_MODULE_0__.cnB)());
83+
84+
})();
85+
86+
/******/ })()
87+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import path from "path";
2+
3+
import Self from "../../../src";
4+
5+
module.exports = {
6+
entry: "./index.js",
7+
context: path.resolve(__dirname, "app"),
8+
module: {
9+
rules: [
10+
{
11+
test: /\.css$/,
12+
use: [Self.loader, "./mockLoader"],
13+
},
14+
],
15+
},
16+
plugins: [
17+
new Self({
18+
filename: "[name].css",
19+
}),
20+
],
21+
};

test/stringifyLocal.test.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { stringifyLocal } from "../src/utils";
2+
3+
describe("stringifyLocal", () => {
4+
it(`primitive`, async () => {
5+
const testObj = "classA";
6+
7+
expect(stringifyLocal(testObj)).toBe('"classA"');
8+
});
9+
10+
it(`arrow function`, async () => {
11+
const testFn = () => "classA";
12+
13+
expect(stringifyLocal(testFn)).toBe('() => "classA"');
14+
});
15+
16+
it(`function`, async () => {
17+
const testFn = function () {
18+
return "classA";
19+
};
20+
21+
expect(stringifyLocal(testFn)).toBe(
22+
'function () {\n return "classA";\n }'
23+
);
24+
});
25+
});

0 commit comments

Comments
 (0)