Skip to content

Commit 2d221cb

Browse files
feat: added insert option
1 parent 3d017a2 commit 2d221cb

34 files changed

+1362
-49
lines changed

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ module.exports = {
8080
| **[`filename`](#filename)** | `{String\|Function}` | `[name].css` | This option determines the name of each output CSS file |
8181
| **[`chunkFilename`](#chunkFilename)** | `{String\|Function}` | `based on filename` | This option determines the name of non-entry chunk files |
8282
| **[`ignoreOrder`](#ignoreOrder)** | `{Boolean}` | `false` | Remove Order Warnings |
83+
| **[`insert`](#insert)** | `{String\|Function}` | `undefined` | Inserts `<link>` at the given position |
8384

8485
#### `filename`
8586

@@ -109,6 +110,57 @@ Default: `false`
109110
Remove Order Warnings.
110111
See [examples](#remove-order-warnings) below for details.
111112

113+
#### `insert`
114+
115+
Type: `String|Function`
116+
Default: `undefined`
117+
118+
By default, the `extract-css-chunks-plugin` appends styles (`<link>` elements) to `document.head` of the current `window`.
119+
120+
However in some circumstances it might be necessary to have finer control over the append target or even delay `link` elements instertion.
121+
For example this is the case when you asynchronously load styles for an application that runs inside of an iframe.
122+
In such cases `insert` can be configured to be a function or a custom selector.
123+
124+
If you target an [iframe](https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement) make sure that the parent document has sufficient access rights to reach into the frame document and append elements to it.
125+
126+
##### `String`
127+
128+
Allows to setup custom [query selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
129+
A new `<link>` element will be inserted after the found item.
130+
131+
**webpack.config.js**
132+
133+
```js
134+
new MiniCssExtractPlugin({
135+
insert: '#some-element',
136+
});
137+
```
138+
139+
A new `<link>` element will be inserted after the element with id `some-element`.
140+
141+
##### `Function`
142+
143+
Allows to override default behavior and insert styles at any position.
144+
145+
> ⚠ Do not forget that this code will run in the browser alongside your application. Since not all browsers support latest ECMA features like `let`, `const`, `arrow function expression` and etc we recommend you to use only ECMA 5 features and syntax.
146+
147+
> > ⚠ The `insert` function is serialized to string and passed to the plugin. This means that it won't have access to the scope of the webpack configuration module.
148+
149+
**webpack.config.js**
150+
151+
```js
152+
new MiniCssExtractPlugin({
153+
insert: function insert(linkTag) {
154+
const reference = document.querySelector('#some-element');
155+
if (reference) {
156+
reference.parentNode.insertBefore(linkTag, reference);
157+
}
158+
},
159+
});
160+
```
161+
162+
A new `<link>` element will be inserted before the element with id `some-element`.
163+
112164
### Loader Options
113165

114166
| Name | Type | Default | Description |

package-lock.json

+38-45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"file-loader": "^6.1.0",
7070
"husky": "^4.3.0",
7171
"jest": "^26.5.2",
72+
"jsdom": "^16.4.0",
7273
"lint-staged": "^10.4.0",
7374
"memfs": "^3.0.2",
7475
"npm-run-all": "^4.1.5",

src/index.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,27 @@ class MiniCssExtractPlugin {
3636
baseDataPath: 'options',
3737
});
3838

39+
const insert =
40+
typeof options.insert !== 'undefined'
41+
? typeof options.insert === 'function'
42+
? Template.asString([options.insert, 'insert(linkTag);'])
43+
: Template.asString([
44+
`var target = document.querySelector("${options.insert}");`,
45+
"if (!target) {throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\")}",
46+
`target.parentNode.insertBefore(linkTag, target.nextSibling);`,
47+
])
48+
: Template.asString([
49+
'var head = document.getElementsByTagName("head")[0];',
50+
'head.appendChild(linkTag);',
51+
]);
52+
3953
this.options = Object.assign(
4054
{
4155
filename: DEFAULT_FILENAME,
4256
ignoreOrder: false,
4357
},
44-
options
58+
options,
59+
{ insert }
4560
);
4661

4762
if (!this.options.chunkFilename) {
@@ -392,8 +407,7 @@ class MiniCssExtractPlugin {
392407
'}',
393408
])
394409
: '',
395-
'var head = document.getElementsByTagName("head")[0];',
396-
'head.appendChild(linkTag);',
410+
this.options.insert,
397411
]),
398412
'}).then(function() {',
399413
Template.indent(['installedCssChunks[chunkId] = 0;']),

src/plugin-options.json

+11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424
},
2525
"ignoreOrder": {
2626
"type": "boolean"
27+
},
28+
"insert": {
29+
"description": "Inserts `<link>` at the given position (https://github.com/webpack-contrib/mini-css-extract-plugin#insert).",
30+
"anyOf": [
31+
{
32+
"type": "string"
33+
},
34+
{
35+
"instanceof": "Function"
36+
}
37+
]
2738
}
2839
}
2940
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`insert option should work when insert option is function: DOM 1`] = `
4+
"<!DOCTYPE html><html><head>
5+
<title>style-loader test</title>
6+
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\"><style id=\\"existing-style\\">.existing { color: yellow }</style>
7+
<script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
8+
<body>
9+
<h1>Body</h1>
10+
<div class=\\"target\\"></div>
11+
<iframe class=\\"iframeTarget\\"></iframe>
12+
13+
14+
</body></html>"
15+
`;
16+
17+
exports[`insert option should work when insert option is function: errors 1`] = `Array []`;
18+
19+
exports[`insert option should work when insert option is function: warnings 1`] = `Array []`;
20+
21+
exports[`insert option should work when insert option is string: DOM 1`] = `
22+
"<!DOCTYPE html><html><head>
23+
<title>style-loader test</title>
24+
<style id=\\"existing-style\\">.existing { color: yellow }</style><link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\">
25+
<script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
26+
<body>
27+
<h1>Body</h1>
28+
<div class=\\"target\\"></div>
29+
<iframe class=\\"iframeTarget\\"></iframe>
30+
31+
32+
</body></html>"
33+
`;
34+
35+
exports[`insert option should work when insert option is string: errors 1`] = `Array []`;
36+
37+
exports[`insert option should work when insert option is string: warnings 1`] = `Array []`;
38+
39+
exports[`insert option should work without insert option: DOM 1`] = `
40+
"<!DOCTYPE html><html><head>
41+
<title>style-loader test</title>
42+
<style id=\\"existing-style\\">.existing { color: yellow }</style>
43+
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\"><script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
44+
<body>
45+
<h1>Body</h1>
46+
<div class=\\"target\\"></div>
47+
<iframe class=\\"iframeTarget\\"></iframe>
48+
49+
50+
</body></html>"
51+
`;
52+
53+
exports[`insert option should work without insert option: errors 1`] = `Array []`;
54+
55+
exports[`insert option should work without insert option: warnings 1`] = `Array []`;

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

+30
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,33 @@ exports[`validate options should throw an error on the "ignoreOrder" option with
2222
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
2323
- options.ignoreOrder should be a boolean."
2424
`;
25+
26+
exports[`validate options should throw an error on the "insert" option with "{}" value 1`] = `
27+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
28+
- options.insert should be one of these:
29+
string | function
30+
-> Inserts \`<link>\` at the given position (https://github.com/webpack-contrib/mini-css-extract-plugin#insert).
31+
Details:
32+
* options.insert should be a string.
33+
* options.insert should be an instance of function."
34+
`;
35+
36+
exports[`validate options should throw an error on the "insert" option with "1" value 1`] = `
37+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
38+
- options.insert should be one of these:
39+
string | function
40+
-> Inserts \`<link>\` at the given position (https://github.com/webpack-contrib/mini-css-extract-plugin#insert).
41+
Details:
42+
* options.insert should be a string.
43+
* options.insert should be an instance of function."
44+
`;
45+
46+
exports[`validate options should throw an error on the "insert" option with "true" value 1`] = `
47+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
48+
- options.insert should be one of these:
49+
string | function
50+
-> Inserts \`<link>\` at the given position (https://github.com/webpack-contrib/mini-css-extract-plugin#insert).
51+
Details:
52+
* options.insert should be a string.
53+
* options.insert should be an instance of function."
54+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
body {
2+
background: red;
3+
}
4+

0 commit comments

Comments
 (0)