Skip to content

Commit

Permalink
feat: styleTagTransform option processed absolute path (#522)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito authored Jul 12, 2021
1 parent 56fc8f0 commit 42ca0ca
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 34 deletions.
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports = {
| [**`injectType`**](#injecttype) | `{String}` | `styleTag` | Allows to setup how styles will be injected into the DOM |
| [**`attributes`**](#attributes) | `{Object}` | `{}` | Adds custom attributes to tag |
| [**`insert`**](#insert) | `{String\|Function}` | `head` | Inserts tag at the given position into the DOM |
| [**`styleTagTransform`**](#styleTagTransform) | `{Function}` | `undefined` | Transform tag and css when insert 'style' tag into the DOM |
| [**`styleTagTransform`**](#styleTagTransform) | `{String\|Function}` | `undefined` | Transform tag and css when insert 'style' tag into the DOM |
| [**`base`**](#base) | `{Number}` | `true` | Sets module ID base (DLLPlugin) |
| [**`esModule`**](#esmodule) | `{Boolean}` | `true` | Use ES modules syntax |

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

### `styleTagTransform`

Type: `Function`
Type: `String | Function`
Default: `undefined`

#### `String`

Allows to setup absolute path to custom function that allows to override default behavior styleTagTransform.

> ⚠ 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
**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: "style-loader",
options: {
injectType: "styleTag",
styleTagTransform: require.resolve("module-path"),
},
},
"css-loader",
],
},
],
},
};
```

#### `Function`

Transform tag and css when insert 'style' tag into the DOM.

> ⚠ 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
Expand Down
40 changes: 23 additions & 17 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getExportLazyStyleCode,
getSetAttributesCode,
getInsertOptionCode,
getStyleTagTransformFnCode,
} from "./utils";

import schema from "./options.json";
Expand All @@ -26,7 +27,6 @@ const loaderAPI = () => {};
loaderAPI.pitch = function loader(request) {
const options = this.getOptions(schema);
const injectType = options.injectType || "styleTag";
const { styleTagTransform } = options;
const esModule =
typeof options.esModule !== "undefined" ? options.esModule : true;
const runtimeOptions = {};
Expand All @@ -46,20 +46,12 @@ loaderAPI.pitch = function loader(request) {
? "module-path"
: "selector";

const styleTagTransformFn =
typeof styleTagTransform === "function"
? styleTagTransform.toString()
: `function(css, style){
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
while (style.firstChild) {
style.removeChild(style.firstChild);
}
style.appendChild(document.createTextNode(css));
}
}`;
const styleTagTransformType =
typeof options.styleTagTransform === "function"
? "function"
: options.styleTagTransform && path.isAbsolute(options.styleTagTransform)
? "module-path"
: "default";

switch (injectType) {
case "linkTag": {
Expand Down Expand Up @@ -103,6 +95,13 @@ ${esModule ? "export default {}" : ""}`;
${getImportInsertBySelectorCode(esModule, this, insertType, options)}
${getSetAttributesCode(esModule, this, options)}
${getImportInsertStyleElementCode(esModule, this)}
${getStyleTagTransformFnCode(
esModule,
this,
options,
isSingleton,
styleTagTransformType
)}
${getImportStyleContentCode(esModule, this, request)}
${isAuto ? getImportIsOldIECode(esModule, this) : ""}
${
Expand All @@ -120,7 +119,7 @@ var refs = 0;
var update;
var options = ${JSON.stringify(runtimeOptions)};
${getStyleTagTransformFn(styleTagTransformFn, isSingleton)};
${getStyleTagTransformFn(options, isSingleton)};
options.setAttributes = setAttributes;
${getInsertOptionCode(insertType, options)}
options.domAPI = ${getdomAPI(isAuto)};
Expand Down Expand Up @@ -162,6 +161,13 @@ ${getExportLazyStyleCode(esModule, this, request)}
${getImportInsertBySelectorCode(esModule, this, insertType, options)}
${getSetAttributesCode(esModule, this, options)}
${getImportInsertStyleElementCode(esModule, this)}
${getStyleTagTransformFnCode(
esModule,
this,
options,
isSingleton,
styleTagTransformType
)}
${getImportStyleContentCode(esModule, this, request)}
${isAuto ? getImportIsOldIECode(esModule, this) : ""}
${
Expand All @@ -172,7 +178,7 @@ ${getExportLazyStyleCode(esModule, this, request)}
var options = ${JSON.stringify(runtimeOptions)};
${getStyleTagTransformFn(styleTagTransformFn, isSingleton)};
${getStyleTagTransformFn(options, isSingleton)};
options.setAttributes = setAttributes;
${getInsertOptionCode(insertType, options)}
options.domAPI = ${getdomAPI(isAuto)};
Expand Down
9 changes: 8 additions & 1 deletion src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@
},
"styleTagTransform": {
"description": "Transform tag and css when insert 'style' tag into the DOM",
"instanceof": "Function"
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Function"
}
]
}
},
"additionalProperties": false
Expand Down
14 changes: 14 additions & 0 deletions src/runtime/styleTagTransform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* istanbul ignore next */
function styleTagTransform(css, style) {
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
while (style.firstChild) {
style.removeChild(style.firstChild);
}

style.appendChild(document.createTextNode(css));
}
}

module.exports = styleTagTransform;
44 changes: 42 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,49 @@ function getImportIsOldIECode(esModule, loaderContext) {
: `var isOldIE = require(${modulePath});`;
}

function getStyleTagTransformFn(styleTagTransformFn, isSingleton) {
function getStyleTagTransformFnCode(
esModule,
loaderContext,
options,
isSingleton,
styleTagTransformType
) {
if (isSingleton) {
return "";
}

if (styleTagTransformType === "default") {
const modulePath = stringifyRequest(
loaderContext,
`!${path.join(__dirname, "runtime/styleTagTransform.js")}`
);

return esModule
? `import styleTagTransformFn from ${modulePath};`
: `var styleTagTransformFn = require(${modulePath});`;
}

if (styleTagTransformType === "module-path") {
const modulePath = stringifyRequest(
loaderContext,
`${options.styleTagTransform}`
);

return esModule
? `import styleTagTransformFn from ${modulePath};`
: `var styleTagTransformFn = require(${modulePath});`;
}

return "";
}

function getStyleTagTransformFn(options, isSingleton) {
// Todo remove "function" type for styleTagTransform option in next major release, because code duplication occurs. Leave require.resolve()
return isSingleton
? ""
: `options.styleTagTransform = ${styleTagTransformFn}`;
: typeof options.styleTagTransform === "function"
? `options.styleTagTransform = ${options.styleTagTransform.toString()}`
: `options.styleTagTransform = styleTagTransformFn`;
}

function getExportStyleCode(esModule, loaderContext, request) {
Expand Down Expand Up @@ -356,4 +395,5 @@ export {
getExportLazyStyleCode,
getSetAttributesCode,
getInsertOptionCode,
getStyleTagTransformFnCode,
};
26 changes: 26 additions & 0 deletions test/__snapshots__/styleTagTransform-option.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ exports[`"styleTagTransform" option should work when the "styleTagTransform" opt
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is not specify: warnings 1`] = `Array []`;
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<style>body {
color: red;
}
.modify{}
</style><style>h1 {
color: blue;
}
.modify{}
</style></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: errors 1`] = `Array []`;
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: warnings 1`] = `Array []`;
exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify and injectType lazyStyleTag: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down
22 changes: 12 additions & 10 deletions test/__snapshots__/validate-options.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,22 @@ exports[`validate options should throw an error on the "insert" option with "tru
exports[`validate options should throw an error on the "styleTagTransform" option with "[]" value 1`] = `
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
- options.styleTagTransform should be an instance of function.
-> Transform tag and css when insert 'style' tag into the DOM"
- options.styleTagTransform should be one of these:
string | function
-> Transform tag and css when insert 'style' tag into the DOM
Details:
* options.styleTagTransform should be a string.
* options.styleTagTransform should be an instance of function."
`;
exports[`validate options should throw an error on the "styleTagTransform" option with "true" value 1`] = `
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
- options.styleTagTransform should be an instance of function.
-> Transform tag and css when insert 'style' tag into the DOM"
`;
exports[`validate options should throw an error on the "styleTagTransform" option with "true" value 2`] = `
"Invalid options object. Style Loader has been initialized using an options object that does not match the API schema.
- options.styleTagTransform should be an instance of function.
-> Transform tag and css when insert 'style' tag into the DOM"
- options.styleTagTransform should be one of these:
string | function
-> Transform tag and css when insert 'style' tag into the DOM
Details:
* options.styleTagTransform should be a string.
* options.styleTagTransform should be an instance of function."
`;
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
Expand Down
8 changes: 8 additions & 0 deletions test/fixtures/styleTagTransform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function styleTagTransform(css, style) {
// eslint-disable-next-line no-param-reassign
style.innerHTML = `${css}.modify{}\n`;

document.head.appendChild(style);
}

module.exports = styleTagTransform;
16 changes: 16 additions & 0 deletions test/styleTagTransform-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,20 @@ describe('"styleTagTransform" option', () => {
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

it(`should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag`, async () => {
const entry = getEntryByInjectType("simple.js", "lazyStyleTag");
const compiler = getCompiler(entry, {
injectType: "lazyStyleTag",
styleTagTransform: require.resolve("./fixtures/styleTagTransform"),
});
const stats = await compile(compiler);

runInJsDom("main.bundle.js", compiler, stats, (dom) => {
expect(dom.serialize()).toMatchSnapshot("DOM");
});

expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});
});
4 changes: 2 additions & 2 deletions test/validate-options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ describe("validate options", () => {
},
styleTagTransform: {
// eslint-disable-next-line func-names
success: [function () {}],
failure: ["true", true, []],
success: [function () {}, require.resolve("path")],
failure: [true, []],
},
unknown: {
success: [],
Expand Down

0 comments on commit 42ca0ca

Please sign in to comment.