Skip to content

Commit 42ca0ca

Browse files
feat: styleTagTransform option processed absolute path (#522)
1 parent 56fc8f0 commit 42ca0ca

10 files changed

+185
-34
lines changed

README.md

+34-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ module.exports = {
6666
| [**`injectType`**](#injecttype) | `{String}` | `styleTag` | Allows to setup how styles will be injected into the DOM |
6767
| [**`attributes`**](#attributes) | `{Object}` | `{}` | Adds custom attributes to tag |
6868
| [**`insert`**](#insert) | `{String\|Function}` | `head` | Inserts tag at the given position into the DOM |
69-
| [**`styleTagTransform`**](#styleTagTransform) | `{Function}` | `undefined` | Transform tag and css when insert 'style' tag into the DOM |
69+
| [**`styleTagTransform`**](#styleTagTransform) | `{String\|Function}` | `undefined` | Transform tag and css when insert 'style' tag into the DOM |
7070
| [**`base`**](#base) | `{Number}` | `true` | Sets module ID base (DLLPlugin) |
7171
| [**`esModule`**](#esmodule) | `{Boolean}` | `true` | Use ES modules syntax |
7272

@@ -542,9 +542,41 @@ Insert styles at top of `head` tag.
542542

543543
### `styleTagTransform`
544544

545-
Type: `Function`
545+
Type: `String | Function`
546546
Default: `undefined`
547547

548+
#### `String`
549+
550+
Allows to setup absolute path to custom function that allows to override default behavior styleTagTransform.
551+
552+
> ⚠ Do not forget that this code will be used in the browser and not all browsers support latest ECMA features like `let`, `const`, `arrow function expression` and etc, we recommend use only ECMA 5 features, but it is depends what browsers you want to support
553+
554+
**webpack.config.js**
555+
556+
```js
557+
module.exports = {
558+
module: {
559+
rules: [
560+
{
561+
test: /\.css$/i,
562+
use: [
563+
{
564+
loader: "style-loader",
565+
options: {
566+
injectType: "styleTag",
567+
styleTagTransform: require.resolve("module-path"),
568+
},
569+
},
570+
"css-loader",
571+
],
572+
},
573+
],
574+
},
575+
};
576+
```
577+
578+
#### `Function`
579+
548580
Transform tag and css when insert 'style' tag into the DOM.
549581

550582
> ⚠ Do not forget that this code will be used in the browser and not all browsers support latest ECMA features like `let`, `const`, `arrow function expression` and etc, we recommend use only ECMA 5 features, but it is depends what browsers you want to support

src/index.js

+23-17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
getExportLazyStyleCode,
1818
getSetAttributesCode,
1919
getInsertOptionCode,
20+
getStyleTagTransformFnCode,
2021
} from "./utils";
2122

2223
import schema from "./options.json";
@@ -26,7 +27,6 @@ const loaderAPI = () => {};
2627
loaderAPI.pitch = function loader(request) {
2728
const options = this.getOptions(schema);
2829
const injectType = options.injectType || "styleTag";
29-
const { styleTagTransform } = options;
3030
const esModule =
3131
typeof options.esModule !== "undefined" ? options.esModule : true;
3232
const runtimeOptions = {};
@@ -46,20 +46,12 @@ loaderAPI.pitch = function loader(request) {
4646
? "module-path"
4747
: "selector";
4848

49-
const styleTagTransformFn =
50-
typeof styleTagTransform === "function"
51-
? styleTagTransform.toString()
52-
: `function(css, style){
53-
if (style.styleSheet) {
54-
style.styleSheet.cssText = css;
55-
} else {
56-
while (style.firstChild) {
57-
style.removeChild(style.firstChild);
58-
}
59-
60-
style.appendChild(document.createTextNode(css));
61-
}
62-
}`;
49+
const styleTagTransformType =
50+
typeof options.styleTagTransform === "function"
51+
? "function"
52+
: options.styleTagTransform && path.isAbsolute(options.styleTagTransform)
53+
? "module-path"
54+
: "default";
6355

6456
switch (injectType) {
6557
case "linkTag": {
@@ -103,6 +95,13 @@ ${esModule ? "export default {}" : ""}`;
10395
${getImportInsertBySelectorCode(esModule, this, insertType, options)}
10496
${getSetAttributesCode(esModule, this, options)}
10597
${getImportInsertStyleElementCode(esModule, this)}
98+
${getStyleTagTransformFnCode(
99+
esModule,
100+
this,
101+
options,
102+
isSingleton,
103+
styleTagTransformType
104+
)}
106105
${getImportStyleContentCode(esModule, this, request)}
107106
${isAuto ? getImportIsOldIECode(esModule, this) : ""}
108107
${
@@ -120,7 +119,7 @@ var refs = 0;
120119
var update;
121120
var options = ${JSON.stringify(runtimeOptions)};
122121
123-
${getStyleTagTransformFn(styleTagTransformFn, isSingleton)};
122+
${getStyleTagTransformFn(options, isSingleton)};
124123
options.setAttributes = setAttributes;
125124
${getInsertOptionCode(insertType, options)}
126125
options.domAPI = ${getdomAPI(isAuto)};
@@ -162,6 +161,13 @@ ${getExportLazyStyleCode(esModule, this, request)}
162161
${getImportInsertBySelectorCode(esModule, this, insertType, options)}
163162
${getSetAttributesCode(esModule, this, options)}
164163
${getImportInsertStyleElementCode(esModule, this)}
164+
${getStyleTagTransformFnCode(
165+
esModule,
166+
this,
167+
options,
168+
isSingleton,
169+
styleTagTransformType
170+
)}
165171
${getImportStyleContentCode(esModule, this, request)}
166172
${isAuto ? getImportIsOldIECode(esModule, this) : ""}
167173
${
@@ -172,7 +178,7 @@ ${getExportLazyStyleCode(esModule, this, request)}
172178
173179
var options = ${JSON.stringify(runtimeOptions)};
174180
175-
${getStyleTagTransformFn(styleTagTransformFn, isSingleton)};
181+
${getStyleTagTransformFn(options, isSingleton)};
176182
options.setAttributes = setAttributes;
177183
${getInsertOptionCode(insertType, options)}
178184
options.domAPI = ${getdomAPI(isAuto)};

src/options.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@
3939
},
4040
"styleTagTransform": {
4141
"description": "Transform tag and css when insert 'style' tag into the DOM",
42-
"instanceof": "Function"
42+
"anyOf": [
43+
{
44+
"type": "string"
45+
},
46+
{
47+
"instanceof": "Function"
48+
}
49+
]
4350
}
4451
},
4552
"additionalProperties": false

src/runtime/styleTagTransform.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* istanbul ignore next */
2+
function styleTagTransform(css, style) {
3+
if (style.styleSheet) {
4+
style.styleSheet.cssText = css;
5+
} else {
6+
while (style.firstChild) {
7+
style.removeChild(style.firstChild);
8+
}
9+
10+
style.appendChild(document.createTextNode(css));
11+
}
12+
}
13+
14+
module.exports = styleTagTransform;

src/utils.js

+42-2
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,49 @@ function getImportIsOldIECode(esModule, loaderContext) {
284284
: `var isOldIE = require(${modulePath});`;
285285
}
286286

287-
function getStyleTagTransformFn(styleTagTransformFn, isSingleton) {
287+
function getStyleTagTransformFnCode(
288+
esModule,
289+
loaderContext,
290+
options,
291+
isSingleton,
292+
styleTagTransformType
293+
) {
294+
if (isSingleton) {
295+
return "";
296+
}
297+
298+
if (styleTagTransformType === "default") {
299+
const modulePath = stringifyRequest(
300+
loaderContext,
301+
`!${path.join(__dirname, "runtime/styleTagTransform.js")}`
302+
);
303+
304+
return esModule
305+
? `import styleTagTransformFn from ${modulePath};`
306+
: `var styleTagTransformFn = require(${modulePath});`;
307+
}
308+
309+
if (styleTagTransformType === "module-path") {
310+
const modulePath = stringifyRequest(
311+
loaderContext,
312+
`${options.styleTagTransform}`
313+
);
314+
315+
return esModule
316+
? `import styleTagTransformFn from ${modulePath};`
317+
: `var styleTagTransformFn = require(${modulePath});`;
318+
}
319+
320+
return "";
321+
}
322+
323+
function getStyleTagTransformFn(options, isSingleton) {
324+
// Todo remove "function" type for styleTagTransform option in next major release, because code duplication occurs. Leave require.resolve()
288325
return isSingleton
289326
? ""
290-
: `options.styleTagTransform = ${styleTagTransformFn}`;
327+
: typeof options.styleTagTransform === "function"
328+
? `options.styleTagTransform = ${options.styleTagTransform.toString()}`
329+
: `options.styleTagTransform = styleTagTransformFn`;
291330
}
292331

293332
function getExportStyleCode(esModule, loaderContext, request) {
@@ -356,4 +395,5 @@ export {
356395
getExportLazyStyleCode,
357396
getSetAttributesCode,
358397
getInsertOptionCode,
398+
getStyleTagTransformFnCode,
359399
};

test/__snapshots__/styleTagTransform-option.test.js.snap

+26
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,32 @@ exports[`"styleTagTransform" option should work when the "styleTagTransform" opt
5050
5151
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is not specify: warnings 1`] = `Array []`;
5252
53+
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: DOM 1`] = `
54+
"<!DOCTYPE html><html><head>
55+
<title>style-loader test</title>
56+
<style id=\\"existing-style\\">.existing { color: yellow }</style>
57+
<style>body {
58+
color: red;
59+
}
60+
.modify{}
61+
</style><style>h1 {
62+
color: blue;
63+
}
64+
.modify{}
65+
</style></head>
66+
<body>
67+
<h1>Body</h1>
68+
<div class=\\"target\\"></div>
69+
<iframe class=\\"iframeTarget\\"></iframe>
70+
71+
72+
</body></html>"
73+
`;
74+
75+
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: errors 1`] = `Array []`;
76+
77+
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: warnings 1`] = `Array []`;
78+
5379
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify and injectType lazyStyleTag: DOM 1`] = `
5480
"<!DOCTYPE html><html><head>
5581
<title>style-loader test</title>

test/__snapshots__/validate-options.test.js.snap

+12-10
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,22 @@ exports[`validate options should throw an error on the "insert" option with "tru
3232
3333
exports[`validate options should throw an error on the "styleTagTransform" option with "[]" value 1`] = `
3434
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
35-
- options.styleTagTransform should be an instance of function.
36-
-> Transform tag and css when insert 'style' tag into the DOM"
35+
- options.styleTagTransform should be one of these:
36+
string | function
37+
-> Transform tag and css when insert 'style' tag into the DOM
38+
Details:
39+
* options.styleTagTransform should be a string.
40+
* options.styleTagTransform should be an instance of function."
3741
`;
3842
3943
exports[`validate options should throw an error on the "styleTagTransform" option with "true" value 1`] = `
4044
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
41-
- options.styleTagTransform should be an instance of function.
42-
-> Transform tag and css when insert 'style' tag into the DOM"
43-
`;
44-
45-
exports[`validate options should throw an error on the "styleTagTransform" option with "true" value 2`] = `
46-
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
47-
- options.styleTagTransform should be an instance of function.
48-
-> Transform tag and css when insert 'style' tag into the DOM"
45+
- options.styleTagTransform should be one of these:
46+
string | function
47+
-> Transform tag and css when insert 'style' tag into the DOM
48+
Details:
49+
* options.styleTagTransform should be a string.
50+
* options.styleTagTransform should be an instance of function."
4951
`;
5052
5153
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `

test/fixtures/styleTagTransform.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
function styleTagTransform(css, style) {
2+
// eslint-disable-next-line no-param-reassign
3+
style.innerHTML = `${css}.modify{}\n`;
4+
5+
document.head.appendChild(style);
6+
}
7+
8+
module.exports = styleTagTransform;

test/styleTagTransform-option.test.js

+16
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,20 @@ describe('"styleTagTransform" option', () => {
8383
expect(getWarnings(stats)).toMatchSnapshot("warnings");
8484
expect(getErrors(stats)).toMatchSnapshot("errors");
8585
});
86+
87+
it(`should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag`, async () => {
88+
const entry = getEntryByInjectType("simple.js", "lazyStyleTag");
89+
const compiler = getCompiler(entry, {
90+
injectType: "lazyStyleTag",
91+
styleTagTransform: require.resolve("./fixtures/styleTagTransform"),
92+
});
93+
const stats = await compile(compiler);
94+
95+
runInJsDom("main.bundle.js", compiler, stats, (dom) => {
96+
expect(dom.serialize()).toMatchSnapshot("DOM");
97+
});
98+
99+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
100+
expect(getErrors(stats)).toMatchSnapshot("errors");
101+
});
86102
});

test/validate-options.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ describe("validate options", () => {
2828
},
2929
styleTagTransform: {
3030
// eslint-disable-next-line func-names
31-
success: [function () {}],
32-
failure: ["true", true, []],
31+
success: [function () {}, require.resolve("path")],
32+
failure: [true, []],
3333
},
3434
unknown: {
3535
success: [],

0 commit comments

Comments
 (0)