Skip to content

Commit

Permalink
feat: linkType option (#634)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito authored Oct 22, 2020
1 parent 1bff4f5 commit a89c0f9
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 20 deletions.
76 changes: 68 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<link>` 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 `<link>` 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`

Expand Down Expand Up @@ -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 (`<link>` elements) to `document.head` of the current `window`.

Expand Down Expand Up @@ -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 <link type="text/css" ...>.

##### `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 |
Expand Down
4 changes: 3 additions & 1 deletion src/CssLoadingRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
18 changes: 13 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,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,
Expand All @@ -60,6 +63,7 @@ class MiniCssExtractPlugin {

this.runtimeOptions = {
insert,
linkType,
};

this.runtimeOptions.attributes = Template.asString(
Expand Down Expand Up @@ -394,7 +398,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([
Expand Down
10 changes: 10 additions & 0 deletions src/plugin-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
}
}
55 changes: 55 additions & 0 deletions test/__snapshots__/linkTag-option.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`linkType option should work when linkType option is "false": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<link rel=\\"stylesheet\\" href=\\"simple.css\\"><script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
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`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\"><script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
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`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"simple.css\\"><script charset=\\"utf-8\\" src=\\"simple.bundle.js\\"></script></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`linkType option should work without linkType option: errors 1`] = `Array []`;
exports[`linkType option should work without linkType option: warnings 1`] = `Array []`;
36 changes: 36 additions & 0 deletions test/__snapshots__/validate-plugin-options.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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."
`;
3 changes: 1 addition & 2 deletions test/cases/hmr/expected/webpack-5/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
3 changes: 1 addition & 2 deletions test/cases/insert-undefined/expected/webpack-4/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/******/ }));
Expand Down
3 changes: 1 addition & 2 deletions test/cases/insert-undefined/expected/webpack-5/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
97 changes: 97 additions & 0 deletions test/linkTag-option.test.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
Loading

0 comments on commit a89c0f9

Please sign in to comment.