Skip to content

Commit 7179273

Browse files
feat: add linkType option
1 parent 89e7a0a commit 7179273

File tree

11 files changed

+287
-19
lines changed

11 files changed

+287
-19
lines changed

Diff for: README.md

+67-7
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,14 @@ module.exports = {
7575

7676
### Plugin Options
7777

78-
| Name | Type | Default | Description |
79-
| :-----------------------------------: | :------------------: | :------------------------------------------------------------------------------: | :------------------------------------------------------- |
80-
| **[`filename`](#filename)** | `{String\|Function}` | `[name].css` | This option determines the name of each output CSS file |
81-
| **[`chunkFilename`](#chunkFilename)** | `{String\|Function}` | `based on filename` | This option determines the name of non-entry chunk files |
82-
| **[`ignoreOrder`](#ignoreOrder)** | `{Boolean}` | `false` | Remove Order Warnings |
83-
| **[`insert`](#insert)** | `{String\|Function}` | `var head = document.getElementsByTagName("head")[0];head.appendChild(linkTag);` | Inserts `<link>` at the given position |
84-
| **[`attributes`](#attributes)** | `{Object}` | `{}` | Adds custom attributes to tag |
78+
| Name | Type | Default | Description |
79+
| :-----------------------------------: | :------------------: | :------------------------------------------------------------------------------: | :--------------------------------------------------------- |
80+
| **[`filename`](#filename)** | `{String\|Function}` | `[name].css` | This option determines the name of each output CSS file |
81+
| **[`chunkFilename`](#chunkFilename)** | `{String\|Function}` | `based on filename` | This option determines the name of non-entry chunk files |
82+
| **[`ignoreOrder`](#ignoreOrder)** | `{Boolean}` | `false` | Remove Order Warnings |
83+
| **[`insert`](#insert)** | `{String\|Function}` | `var head = document.getElementsByTagName("head")[0];head.appendChild(linkTag);` | Inserts `<link>` at the given position |
84+
| **[`attributes`](#attributes)** | `{Object}` | `{}` | Adds custom attributes to tag |
85+
| **[`linkType`](#linkType)** | `{String\|Boolean}` | `text/css` | Allows loading asynchronous chunks with a custom link type |
8586

8687
#### `filename`
8788

@@ -196,6 +197,65 @@ module.exports = {
196197

197198
Note: It's only applied to dynamically loaded css chunks, if you want to modify link attributes inside html file, please using [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin)
198199

200+
#### `linkType`
201+
202+
Type: `String|Boolean`
203+
Default: `text/css`
204+
205+
This option allows loading asynchronous chunks with a custom link type, such as <link type="text/css" ...>.
206+
207+
##### `String`
208+
209+
Possible values: `text/css`
210+
211+
**webpack.config.js**
212+
213+
```js
214+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
215+
216+
module.exports = {
217+
plugins: [
218+
new MiniCssExtractPlugin({
219+
linkType: 'text/css',
220+
}),
221+
],
222+
module: {
223+
rules: [
224+
{
225+
test: /\.css$/i,
226+
use: [MiniCssExtractPlugin.loader, 'css-loader'],
227+
},
228+
],
229+
},
230+
};
231+
```
232+
233+
##### `Boolean`
234+
235+
`false` disables the link `type` attribute
236+
237+
**webpack.config.js**
238+
239+
```js
240+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
241+
242+
module.exports = {
243+
plugins: [
244+
new MiniCssExtractPlugin({
245+
linkType: false,
246+
}),
247+
],
248+
module: {
249+
rules: [
250+
{
251+
test: /\.css$/i,
252+
use: [MiniCssExtractPlugin.loader, 'css-loader'],
253+
},
254+
],
255+
},
256+
};
257+
```
258+
199259
### Loader Options
200260

201261
| Name | Type | Default | Description |

Diff for: src/CssLoadingRuntimeModule.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ module.exports = class CssLoadingRuntimeModule extends RuntimeModule {
5757
'var linkTag = document.createElement("link");',
5858
this.runtimeOptions.attributes,
5959
'linkTag.rel = "stylesheet";',
60-
'linkTag.type = "text/css";',
60+
this.runtimeOptions.linkType
61+
? `linkTag.type = ${JSON.stringify(this.runtimeOptions.linkType)};`
62+
: '',
6163
'linkTag.onload = resolve;',
6264
'linkTag.onerror = function(event) {',
6365
Template.indent([

Diff for: src/index.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,16 @@ class MiniCssExtractPlugin {
4444
`var target = document.querySelector("${options.insert}");`,
4545
`target.parentNode.insertBefore(linkTag, target.nextSibling);`,
4646
])
47-
: Template.asString([
48-
'var head = document.getElementsByTagName("head")[0];',
49-
'head.appendChild(linkTag);',
50-
]);
47+
: Template.asString(['document.head.appendChild(linkTag);']);
5148

5249
const attributes =
5350
typeof options.attributes === 'object' ? options.attributes : {};
5451

52+
const linkType =
53+
options.linkType === true || typeof options.linkType === 'undefined'
54+
? 'text/css'
55+
: options.linkType;
56+
5557
this.options = Object.assign(
5658
{
5759
filename: DEFAULT_FILENAME,
@@ -62,6 +64,7 @@ class MiniCssExtractPlugin {
6264

6365
this.runtimeOptions = {
6466
insert,
67+
linkType,
6568
};
6669

6770
this.runtimeOptions.attributes = Template.asString(
@@ -398,7 +401,11 @@ class MiniCssExtractPlugin {
398401
'var linkTag = document.createElement("link");',
399402
this.runtimeOptions.attributes,
400403
'linkTag.rel = "stylesheet";',
401-
'linkTag.type = "text/css";',
404+
this.runtimeOptions.linkType
405+
? `linkTag.type = ${JSON.stringify(
406+
this.runtimeOptions.linkType
407+
)};`
408+
: '',
402409
'linkTag.onload = resolve;',
403410
'linkTag.onerror = function(event) {',
404411
Template.indent([

Diff for: src/plugin-options.json

+10
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@
3939
"attributes": {
4040
"description": "Adds custom attributes to tag (https://github.com/webpack-contrib/mini-css-extract-plugin#attributes).",
4141
"type": "object"
42+
},
43+
"linkType": {
44+
"anyOf": [
45+
{
46+
"enum": ["text/css"]
47+
},
48+
{
49+
"type": "boolean"
50+
}
51+
]
4252
}
4353
}
4454
}

Diff for: test/__snapshots__/linkTag-option.test.js.snap

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`linkType option should work when linkType option is "false": DOM 1`] = `
4+
"<!DOCTYPE html><html><head>
5+
<title>style-loader test</title>
6+
<style id=\\"existing-style\\">.existing { color: yellow }</style>
7+
<link rel=\\"stylesheet\\" href=\\"simple.css\\"><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[`linkType option should work when linkType option is "false": errors 1`] = `Array []`;
18+
19+
exports[`linkType option should work when linkType option is "false": warnings 1`] = `Array []`;
20+
21+
exports[`linkType option should work when linkType option is "text/css": DOM 1`] = `
22+
"<!DOCTYPE html><html><head>
23+
<title>style-loader test</title>
24+
<style id=\\"existing-style\\">.existing { color: yellow }</style>
25+
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\"><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[`linkType option should work when linkType option is "text/css": errors 1`] = `Array []`;
36+
37+
exports[`linkType option should work when linkType option is "text/css": warnings 1`] = `Array []`;
38+
39+
exports[`linkType option should work without linkType 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[`linkType option should work without linkType option: errors 1`] = `Array []`;
54+
55+
exports[`linkType option should work without linkType option: warnings 1`] = `Array []`;

Diff for: test/__snapshots__/validate-plugin-options.test.js.snap

+36
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,39 @@ exports[`validate options should throw an error on the "insert" option with "tru
5959
* options.insert should be a string.
6060
* options.insert should be an instance of function."
6161
`;
62+
63+
exports[`validate options should throw an error on the "linkType" option with "[]" value 1`] = `
64+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
65+
- options.linkType should be one of these:
66+
\\"text/css\\" | boolean
67+
Details:
68+
* options.linkType should be \\"text/css\\".
69+
* options.linkType should be a boolean."
70+
`;
71+
72+
exports[`validate options should throw an error on the "linkType" option with "{}" value 1`] = `
73+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
74+
- options.linkType should be one of these:
75+
\\"text/css\\" | boolean
76+
Details:
77+
* options.linkType should be \\"text/css\\".
78+
* options.linkType should be a boolean."
79+
`;
80+
81+
exports[`validate options should throw an error on the "linkType" option with "1" value 1`] = `
82+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
83+
- options.linkType should be one of these:
84+
\\"text/css\\" | boolean
85+
Details:
86+
* options.linkType should be \\"text/css\\".
87+
* options.linkType should be a boolean."
88+
`;
89+
90+
exports[`validate options should throw an error on the "linkType" option with "invalid/type" value 1`] = `
91+
"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
92+
- options.linkType should be one of these:
93+
\\"text/css\\" | boolean
94+
Details:
95+
* options.linkType should be \\"text/css\\".
96+
* options.linkType should be a boolean."
97+
`;

Diff for: test/cases/hmr/expected/webpack-5/main.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,7 @@ module.exports = function (urlString) {
838838
/******/ };
839839
/******/ linkTag.href = fullhref;
840840
/******/
841-
/******/ var head = document.getElementsByTagName("head")[0];
842-
/******/ head.appendChild(linkTag);
841+
/******/ document.head.appendChild(linkTag);
843842
/******/ return linkTag;
844843
/******/ };
845844
/******/ var findStylesheet = (href, fullhref) => {

Diff for: test/cases/insert-undefined/expected/webpack-4/main.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@
116116
/******/ };
117117
/******/ linkTag.href = fullhref;
118118
/******/
119-
/******/ var head = document.getElementsByTagName("head")[0];
120-
/******/ head.appendChild(linkTag);
119+
/******/ document.head.appendChild(linkTag);
121120
/******/ }).then(function() {
122121
/******/ installedCssChunks[chunkId] = 0;
123122
/******/ }));

Diff for: test/cases/insert-undefined/expected/webpack-5/main.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@
173173
/******/ };
174174
/******/ linkTag.href = fullhref;
175175
/******/
176-
/******/ var head = document.getElementsByTagName("head")[0];
177-
/******/ head.appendChild(linkTag);
176+
/******/ document.head.appendChild(linkTag);
178177
/******/ return linkTag;
179178
/******/ };
180179
/******/ var findStylesheet = (href, fullhref) => {

Diff for: test/linkTag-option.test.js

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* eslint-env browser */
2+
import path from 'path';
3+
4+
import MiniCssExtractPlugin from '../src/cjs';
5+
6+
import {
7+
compile,
8+
getCompiler,
9+
getErrors,
10+
getWarnings,
11+
runInJsDom,
12+
} from './helpers/index';
13+
14+
describe('linkType option', () => {
15+
it(`should work without linkType option`, async () => {
16+
const compiler = getCompiler(
17+
'attributes.js',
18+
{},
19+
{
20+
output: {
21+
publicPath: '',
22+
path: path.resolve(__dirname, '../outputs'),
23+
filename: '[name].bundle.js',
24+
},
25+
plugins: [
26+
new MiniCssExtractPlugin({
27+
filename: '[name].css',
28+
}),
29+
],
30+
}
31+
);
32+
const stats = await compile(compiler);
33+
34+
runInJsDom('main.bundle.js', compiler, stats, (dom) => {
35+
expect(dom.serialize()).toMatchSnapshot('DOM');
36+
});
37+
38+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
39+
expect(getErrors(stats)).toMatchSnapshot('errors');
40+
});
41+
42+
it(`should work when linkType option is "false"`, async () => {
43+
const compiler = getCompiler(
44+
'attributes.js',
45+
{},
46+
{
47+
output: {
48+
publicPath: '',
49+
path: path.resolve(__dirname, '../outputs'),
50+
filename: '[name].bundle.js',
51+
},
52+
plugins: [
53+
new MiniCssExtractPlugin({
54+
linkType: false,
55+
filename: '[name].css',
56+
}),
57+
],
58+
}
59+
);
60+
const stats = await compile(compiler);
61+
62+
runInJsDom('main.bundle.js', compiler, stats, (dom) => {
63+
expect(dom.serialize()).toMatchSnapshot('DOM');
64+
});
65+
66+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
67+
expect(getErrors(stats)).toMatchSnapshot('errors');
68+
});
69+
70+
it(`should work when linkType option is "text/css"`, async () => {
71+
const compiler = getCompiler(
72+
'attributes.js',
73+
{},
74+
{
75+
output: {
76+
publicPath: '',
77+
path: path.resolve(__dirname, '../outputs'),
78+
filename: '[name].bundle.js',
79+
},
80+
plugins: [
81+
new MiniCssExtractPlugin({
82+
linkType: 'text/css',
83+
filename: '[name].css',
84+
}),
85+
],
86+
}
87+
);
88+
const stats = await compile(compiler);
89+
90+
runInJsDom('main.bundle.js', compiler, stats, (dom) => {
91+
expect(dom.serialize()).toMatchSnapshot('DOM');
92+
});
93+
94+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
95+
expect(getErrors(stats)).toMatchSnapshot('errors');
96+
});
97+
});

Diff for: test/validate-plugin-options.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ describe('validate options', () => {
3232
success: [{}, { id: 'id' }],
3333
failure: [true],
3434
},
35+
linkType: {
36+
success: [true, false, 'text/css'],
37+
failure: [1, {}, [], 'invalid/type'],
38+
},
3539
unknown: {
3640
success: [],
3741
// TODO failed in next release

0 commit comments

Comments
 (0)