diff --git a/README.md b/README.md
index 9ccdc9e4..7640b6db 100644
--- a/README.md
+++ b/README.md
@@ -75,13 +75,14 @@ module.exports = {
### Plugin Options
-| Name | Type | Default | Description |
-| :-----------------------------------: | :------------------: | :------------------------------------------------------------------------------: | :------------------------------------------------------- |
-| **[`filename`](#filename)** | `{String\|Function}` | `[name].css` | This option determines the name of each output CSS file |
-| **[`chunkFilename`](#chunkFilename)** | `{String\|Function}` | `based on filename` | This option determines the name of non-entry chunk files |
-| **[`ignoreOrder`](#ignoreOrder)** | `{Boolean}` | `false` | Remove Order Warnings |
-| **[`insert`](#insert)** | `{String\|Function}` | `var head = document.getElementsByTagName("head")[0];head.appendChild(linkTag);` | Inserts `` at the given position |
-| **[`attributes`](#attributes)** | `{Object}` | `{}` | Adds custom attributes to tag |
+| Name | Type | Default | Description |
+| :-----------------------------------: | :------------------: | :-----------------------------------: | :--------------------------------------------------------- |
+| **[`filename`](#filename)** | `{String\|Function}` | `[name].css` | This option determines the name of each output CSS file |
+| **[`chunkFilename`](#chunkFilename)** | `{String\|Function}` | `based on filename` | This option determines the name of non-entry chunk files |
+| **[`ignoreOrder`](#ignoreOrder)** | `{Boolean}` | `false` | Remove Order Warnings |
+| **[`insert`](#insert)** | `{String\|Function}` | `document.head.appendChild(linkTag);` | Inserts `` at the given position |
+| **[`attributes`](#attributes)** | `{Object}` | `{}` | Adds custom attributes to tag |
+| **[`linkType`](#linkType)** | `{String\|Boolean}` | `text/css` | Allows loading asynchronous chunks with a custom link type |
#### `filename`
@@ -114,7 +115,7 @@ See [examples](#remove-order-warnings) below for details.
#### `insert`
Type: `String|Function`
-Default: `var head = document.getElementsByTagName("head")[0]; head.appendChild(linkTag);`
+Default: `document.head.appendChild(linkTag);`
By default, the `extract-css-chunks-plugin` appends styles (`` elements) to `document.head` of the current `window`.
@@ -196,6 +197,65 @@ module.exports = {
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)
+#### `linkType`
+
+Type: `String|Boolean`
+Default: `text/css`
+
+This option allows loading asynchronous chunks with a custom link type, such as .
+
+##### `String`
+
+Possible values: `text/css`
+
+**webpack.config.js**
+
+```js
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+
+module.exports = {
+ plugins: [
+ new MiniCssExtractPlugin({
+ linkType: 'text/css',
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: [MiniCssExtractPlugin.loader, 'css-loader'],
+ },
+ ],
+ },
+};
+```
+
+##### `Boolean`
+
+`false` disables the link `type` attribute
+
+**webpack.config.js**
+
+```js
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+
+module.exports = {
+ plugins: [
+ new MiniCssExtractPlugin({
+ linkType: false,
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: [MiniCssExtractPlugin.loader, 'css-loader'],
+ },
+ ],
+ },
+};
+```
+
### Loader Options
| Name | Type | Default | Description |
diff --git a/src/CssLoadingRuntimeModule.js b/src/CssLoadingRuntimeModule.js
index e259fc5d..be0ff24e 100644
--- a/src/CssLoadingRuntimeModule.js
+++ b/src/CssLoadingRuntimeModule.js
@@ -57,7 +57,9 @@ module.exports = class CssLoadingRuntimeModule extends RuntimeModule {
'var linkTag = document.createElement("link");',
this.runtimeOptions.attributes,
'linkTag.rel = "stylesheet";',
- 'linkTag.type = "text/css";',
+ this.runtimeOptions.linkType
+ ? `linkTag.type = ${JSON.stringify(this.runtimeOptions.linkType)};`
+ : '',
'linkTag.onload = resolve;',
'linkTag.onerror = function(event) {',
Template.indent([
diff --git a/src/index.js b/src/index.js
index 03acebe2..beecd4a9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -44,14 +44,17 @@ class MiniCssExtractPlugin {
`var target = document.querySelector("${options.insert}");`,
`target.parentNode.insertBefore(linkTag, target.nextSibling);`,
])
- : Template.asString([
- 'var head = document.getElementsByTagName("head")[0];',
- 'head.appendChild(linkTag);',
- ]);
+ : Template.asString(['document.head.appendChild(linkTag);']);
const attributes =
typeof options.attributes === 'object' ? options.attributes : {};
+ // Todo in next major release set default to "false"
+ const linkType =
+ options.linkType === true || typeof options.linkType === 'undefined'
+ ? 'text/css'
+ : options.linkType;
+
this.options = Object.assign(
{
filename: DEFAULT_FILENAME,
@@ -62,6 +65,7 @@ class MiniCssExtractPlugin {
this.runtimeOptions = {
insert,
+ linkType,
};
this.runtimeOptions.attributes = Template.asString(
@@ -398,7 +402,11 @@ class MiniCssExtractPlugin {
'var linkTag = document.createElement("link");',
this.runtimeOptions.attributes,
'linkTag.rel = "stylesheet";',
- 'linkTag.type = "text/css";',
+ this.runtimeOptions.linkType
+ ? `linkTag.type = ${JSON.stringify(
+ this.runtimeOptions.linkType
+ )};`
+ : '',
'linkTag.onload = resolve;',
'linkTag.onerror = function(event) {',
Template.indent([
diff --git a/src/plugin-options.json b/src/plugin-options.json
index 6fd30ee0..94aa6d8e 100644
--- a/src/plugin-options.json
+++ b/src/plugin-options.json
@@ -39,6 +39,16 @@
"attributes": {
"description": "Adds custom attributes to tag (https://github.com/webpack-contrib/mini-css-extract-plugin#attributes).",
"type": "object"
+ },
+ "linkType": {
+ "anyOf": [
+ {
+ "enum": ["text/css"]
+ },
+ {
+ "type": "boolean"
+ }
+ ]
}
}
}
diff --git a/test/__snapshots__/linkTag-option.test.js.snap b/test/__snapshots__/linkTag-option.test.js.snap
new file mode 100644
index 00000000..8a4e62d9
--- /dev/null
+++ b/test/__snapshots__/linkTag-option.test.js.snap
@@ -0,0 +1,55 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`linkType option should work when linkType option is "false": DOM 1`] = `
+"
+ style-loader test
+
+
+
+
Body
+
+
+
+
+"
+`;
+
+exports[`linkType option should work when linkType option is "false": errors 1`] = `Array []`;
+
+exports[`linkType option should work when linkType option is "false": warnings 1`] = `Array []`;
+
+exports[`linkType option should work when linkType option is "text/css": DOM 1`] = `
+"
+ style-loader test
+
+
+
+
Body
+
+
+
+
+"
+`;
+
+exports[`linkType option should work when linkType option is "text/css": errors 1`] = `Array []`;
+
+exports[`linkType option should work when linkType option is "text/css": warnings 1`] = `Array []`;
+
+exports[`linkType option should work without linkType option: DOM 1`] = `
+"
+ style-loader test
+
+
+
+
Body
+
+
+
+
+"
+`;
+
+exports[`linkType option should work without linkType option: errors 1`] = `Array []`;
+
+exports[`linkType option should work without linkType option: warnings 1`] = `Array []`;
diff --git a/test/__snapshots__/validate-plugin-options.test.js.snap b/test/__snapshots__/validate-plugin-options.test.js.snap
index 27b8b1a6..1a6b986c 100644
--- a/test/__snapshots__/validate-plugin-options.test.js.snap
+++ b/test/__snapshots__/validate-plugin-options.test.js.snap
@@ -59,3 +59,39 @@ exports[`validate options should throw an error on the "insert" option with "tru
* options.insert should be a string.
* options.insert should be an instance of function."
`;
+
+exports[`validate options should throw an error on the "linkType" option with "[]" value 1`] = `
+"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
+ - options.linkType should be one of these:
+ \\"text/css\\" | boolean
+ Details:
+ * options.linkType should be \\"text/css\\".
+ * options.linkType should be a boolean."
+`;
+
+exports[`validate options should throw an error on the "linkType" option with "{}" value 1`] = `
+"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
+ - options.linkType should be one of these:
+ \\"text/css\\" | boolean
+ Details:
+ * options.linkType should be \\"text/css\\".
+ * options.linkType should be a boolean."
+`;
+
+exports[`validate options should throw an error on the "linkType" option with "1" value 1`] = `
+"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
+ - options.linkType should be one of these:
+ \\"text/css\\" | boolean
+ Details:
+ * options.linkType should be \\"text/css\\".
+ * options.linkType should be a boolean."
+`;
+
+exports[`validate options should throw an error on the "linkType" option with "invalid/type" value 1`] = `
+"Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
+ - options.linkType should be one of these:
+ \\"text/css\\" | boolean
+ Details:
+ * options.linkType should be \\"text/css\\".
+ * options.linkType should be a boolean."
+`;
diff --git a/test/cases/hmr/expected/webpack-5/main.js b/test/cases/hmr/expected/webpack-5/main.js
index 661f7fdb..fea4f8c6 100644
--- a/test/cases/hmr/expected/webpack-5/main.js
+++ b/test/cases/hmr/expected/webpack-5/main.js
@@ -838,8 +838,7 @@ module.exports = function (urlString) {
/******/ };
/******/ linkTag.href = fullhref;
/******/
-/******/ var head = document.getElementsByTagName("head")[0];
-/******/ head.appendChild(linkTag);
+/******/ document.head.appendChild(linkTag);
/******/ return linkTag;
/******/ };
/******/ var findStylesheet = (href, fullhref) => {
diff --git a/test/cases/insert-undefined/expected/webpack-4/main.js b/test/cases/insert-undefined/expected/webpack-4/main.js
index 40e53f2a..4f296ab3 100644
--- a/test/cases/insert-undefined/expected/webpack-4/main.js
+++ b/test/cases/insert-undefined/expected/webpack-4/main.js
@@ -116,8 +116,7 @@
/******/ };
/******/ linkTag.href = fullhref;
/******/
-/******/ var head = document.getElementsByTagName("head")[0];
-/******/ head.appendChild(linkTag);
+/******/ document.head.appendChild(linkTag);
/******/ }).then(function() {
/******/ installedCssChunks[chunkId] = 0;
/******/ }));
diff --git a/test/cases/insert-undefined/expected/webpack-5/main.js b/test/cases/insert-undefined/expected/webpack-5/main.js
index 318b496c..d105a4b9 100644
--- a/test/cases/insert-undefined/expected/webpack-5/main.js
+++ b/test/cases/insert-undefined/expected/webpack-5/main.js
@@ -173,8 +173,7 @@
/******/ };
/******/ linkTag.href = fullhref;
/******/
-/******/ var head = document.getElementsByTagName("head")[0];
-/******/ head.appendChild(linkTag);
+/******/ document.head.appendChild(linkTag);
/******/ return linkTag;
/******/ };
/******/ var findStylesheet = (href, fullhref) => {
diff --git a/test/linkTag-option.test.js b/test/linkTag-option.test.js
new file mode 100644
index 00000000..1ab27bda
--- /dev/null
+++ b/test/linkTag-option.test.js
@@ -0,0 +1,97 @@
+/* eslint-env browser */
+import path from 'path';
+
+import MiniCssExtractPlugin from '../src/cjs';
+
+import {
+ compile,
+ getCompiler,
+ getErrors,
+ getWarnings,
+ runInJsDom,
+} from './helpers/index';
+
+describe('linkType option', () => {
+ it(`should work without linkType option`, async () => {
+ const compiler = getCompiler(
+ 'attributes.js',
+ {},
+ {
+ output: {
+ publicPath: '',
+ path: path.resolve(__dirname, '../outputs'),
+ filename: '[name].bundle.js',
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ filename: '[name].css',
+ }),
+ ],
+ }
+ );
+ 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');
+ });
+
+ it(`should work when linkType option is "false"`, async () => {
+ const compiler = getCompiler(
+ 'attributes.js',
+ {},
+ {
+ output: {
+ publicPath: '',
+ path: path.resolve(__dirname, '../outputs'),
+ filename: '[name].bundle.js',
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ linkType: false,
+ filename: '[name].css',
+ }),
+ ],
+ }
+ );
+ 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');
+ });
+
+ it(`should work when linkType option is "text/css"`, async () => {
+ const compiler = getCompiler(
+ 'attributes.js',
+ {},
+ {
+ output: {
+ publicPath: '',
+ path: path.resolve(__dirname, '../outputs'),
+ filename: '[name].bundle.js',
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ linkType: 'text/css',
+ filename: '[name].css',
+ }),
+ ],
+ }
+ );
+ 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');
+ });
+});
diff --git a/test/validate-plugin-options.test.js b/test/validate-plugin-options.test.js
index 808aa9e6..dfae508b 100644
--- a/test/validate-plugin-options.test.js
+++ b/test/validate-plugin-options.test.js
@@ -32,6 +32,10 @@ describe('validate options', () => {
success: [{}, { id: 'id' }],
failure: [true],
},
+ linkType: {
+ success: [true, false, 'text/css'],
+ failure: [1, {}, [], 'invalid/type'],
+ },
unknown: {
success: [],
// TODO failed in next release