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
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