Skip to content

refactor: insert option #413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 2, 2019
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
213 changes: 135 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,19 @@ style.className === 'z849f98ca812';

## Options

| Name | Type | Default | Description |
| :--------------: | :------------------: | :--------: | :------------------------------------------------- |
| **`injectType`** | `{String}` | `styleTag` | Allows to setup how styles will be injected in DOM |
| **`attributes`** | `{Object}` | `{}` | Add custom attributes to tag |
| **`insertAt`** | `{String\|Object}` | `bottom` | Inserts tag at the given position |
| **`insertInto`** | `{String\|Function}` | `<head>` | Inserts tag into the given position |
| **`base`** | `{Number}` | `true` | Set module ID base (DLLPlugin) |
| Name | Type | Default | Description |
| :--------------: | :------------------: | :--------: | :--------------------------------------------------- |
| **`injectType`** | `{String}` | `styleTag` | Allows to setup how styles will be injected into DOM |
| **`attributes`** | `{Object}` | `{}` | Adds custom attributes to tag |
| **`insert`** | `{String\|Function}` | `head` | Inserts tag at the given position into DOM |
| **`base`** | `{Number}` | `true` | Sets module ID base (DLLPlugin) |

### `injectType`

Type: `String`
Default: `styleTag`

Allows to setup how styles will be injected in DOM.
Allows to setup how styles will be injected into DOM.

Possible values:

Expand Down Expand Up @@ -118,7 +117,7 @@ module.exports = {
{
test: /\.css$/i,
exclude: /\.lazy\.css$/i,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
use: ['style-loader', 'css-loader'],
},
{
test: /\.lazy\.css$/i,
Expand All @@ -130,7 +129,7 @@ module.exports = {
injectType: 'lazyStyleTag',
},
},
{ loader: 'css-loader' },
'css-loader',
],
},
],
Expand Down Expand Up @@ -381,34 +380,19 @@ module.exports = {
<style id="id"></style>
```

### `insertAt`
### `insert`

By default, the style-loader appends `<style>` elements to the end of the style target, which is the `<head>` tag of the page unless specified by `insertInto`. This will cause CSS created by the loader to take priority over CSS already present in the target. To insert style elements at the beginning of the target, set this query parameter to 'top', e.g
Type: `String|Function`
Default: `head`

**webpack.config.js**
By default, the `style-loader` appends `<style>`/`<link>` elements to the end of the style target, which is the `<head>` tag of the page unless specified by `insert`.
This will cause CSS created by the loader to take priority over CSS already present in the target.
You can use other values if the standard behavior is not suitable for you, but we do not recommend doing this.
If you target an [iframe](https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement) make sure you have sufficient access rights, the styles will be injected into the content document head.

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top',
},
},
{ loader: 'css-loader' },
],
},
],
},
};
```
#### `String`

A new `<style>` element can be inserted before a specific element by passing an object, e.g.
Allows to setup custom [query selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) where styles inject into DOM.

**webpack.config.js**

Expand All @@ -422,49 +406,25 @@ module.exports = {
{
loader: 'style-loader',
options: {
insertAt: {
before: '#id',
},
insert: 'body',
},
},
{ loader: 'css-loader' },
'css-loader',
],
},
],
},
};
```

### `insertInto`
A new `<style>`/`<link>` elements will be inserted into at bottom of `body` tag.

By default, the style-loader inserts the `<style>` elements into the `<head>` tag of the page. If you want the tags to be inserted somewhere else you can specify a CSS selector for that element here. If you target an [IFrame](https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement) make sure you have sufficient access rights, the styles will be injected into the content document head.
#### `Function`

You can also pass function to override default behavior and insert styles in your container, e.g
Allows to override default behavior and insert styles at any position.

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
options: {
insertInto: () => document.querySelector('#root'),
},
},
{ loader: 'css-loader' },
],
},
],
},
};
```

Using function you can insert the styles into a [ShadowRoot](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot), e.g
> ⚠ 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
> ⚠ Do not forget that some doom methods may not be available in older browsers, we recommended use only [DOM core level 2 properties](https://caniuse.com/#search=DOM%20Core), but it is depends what browsers you want to support

**webpack.config.js**

Expand All @@ -478,17 +438,35 @@ module.exports = {
{
loader: 'style-loader',
options: {
insertInto: () => document.querySelector('#root').shadowRoot,
insert: function insertAtTop(element) {
var parent = document.querySelector('head');
// eslint-disable-next-line no-underscore-dangle
var lastInsertedElement =
window._lastElementInsertedByStyleLoader;

if (!lastInsertedElement) {
parent.insertBefore(element, parent.firstChild);
} else if (lastInsertedElement.nextSibling) {
parent.insertBefore(element, lastInsertedElement.nextSibling);
} else {
parent.appendChild(element);
}

// eslint-disable-next-line no-underscore-dangle
window._lastElementInsertedByStyleLoader = element;
},
},
},
{ loader: 'css-loader' },
'css-loader',
],
},
],
},
};
```

Insert styles at top of `head` tag.

### `base`

This setting is primarily used as a workaround for [css clashes](https://github.com/webpack-contrib/style-loader/issues/163) when using one or more [DllPlugin](https://robertknight.github.io/posts/webpack-dll-plugins/)'s. `base` allows you to prevent either the _app_'s css (or _DllPlugin2_'s css) from overwriting _DllPlugin1_'s css by specifying a css module id base which is greater than the range used by _DllPlugin1_ e.g.:
Expand All @@ -501,12 +479,7 @@ module.exports = {
rules: [
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
},
{ loader: 'css-loader' },
],
use: ['style-loader', 'css-loader'],
},
],
},
Expand All @@ -523,7 +496,7 @@ module.exports = {
test: /\.css$/i,
use: [
{ loader: 'style-loader', options: { base: 1000 } },
{ loader: 'css-loader' },
'css-loader',
],
},
],
Expand All @@ -541,7 +514,7 @@ module.exports = {
test: /\.css$/i,
use: [
{ loader: 'style-loader', options: { base: 2000 } },
{ loader: 'css-loader' },
'css-loader',
],
},
],
Expand Down Expand Up @@ -581,9 +554,9 @@ There are two ways to work with `nonce`:
- using the `attirbutes` option
- using the `__webpack_nonce__` variable

> ⚠ the `__webpack_nonce__` variable takes precedence over the `attibutes` option, so if define the `__webpack_nonce__` variable the `attributes` option will not be used
> ⚠ the `attibutes` option takes precedence over the `__webpack_nonce__` variable

### `attirbutes`
#### `attirbutes`

**component.js**

Expand Down Expand Up @@ -626,7 +599,7 @@ The loader generate:
</style>
```

### `__webpack_nonce__`
#### `__webpack_nonce__`

**create-nonce.js**

Expand Down Expand Up @@ -676,6 +649,90 @@ The loader generate:
</style>
```

#### Insert styles at top

Inserts styles at top of `head` tag.

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
options: {
insert: function insertAtTop(element) {
var parent = document.querySelector('head');
var lastInsertedElement =
window._lastElementInsertedByStyleLoader;

if (!lastInsertedElement) {
parent.insertBefore(element, parent.firstChild);
} else if (lastInsertedElement.nextSibling) {
parent.insertBefore(element, lastInsertedElement.nextSibling);
} else {
parent.appendChild(element);
}

window._lastElementInsertedByStyleLoader = element;
},
},
},
'css-loader',
],
},
],
},
};
```

#### Insert styles before target element

Inserts styles before `#id` element.

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: 'style-loader',
options: {
insert: function insertBeforeAt(element) {
const parent = document.querySelector('head');
const target = document.querySelector('#id');

const lastInsertedElement =
window._lastElementInsertedByStyleLoader;

if (!lastInsertedElement) {
parent.insertBefore(element, target);
} else if (lastInsertedElement.nextSibling) {
parent.insertBefore(element, lastInsertedElement.nextSibling);
} else {
parent.appendChild(element);
}

window._lastElementInsertedByStyleLoader = element;
},
},
},
'css-loader',
],
},
],
},
};
```

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
34 changes: 15 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,12 @@ module.exports.pitch = function loader(request) {
baseDataPath: 'options',
});

// The variable is needed, because the function should be inlined.
// If is just stored it in options, JSON.stringify will quote
// the function and it would be just a string at runtime
let insertInto;

if (typeof options.insertInto === 'function') {
insertInto = options.insertInto.toString();
}

// We need to check if it a string, or variable will be "undefined"
// and the loader crashes
if (typeof options.insertInto === 'string') {
insertInto = `"${options.insertInto}"`;
}
const insert =
typeof options.insert === 'undefined'
? '"head"'
: typeof options.insert === 'string'
? JSON.stringify(options.insert)
: options.insert.toString();

const injectType = options.injectType || 'styleTag';

Expand All @@ -50,14 +42,18 @@ if (module.hot) {
}`
: '';

return `var update = require(${loaderUtils.stringifyRequest(
return `var options = ${JSON.stringify(options)};

options.insert = ${insert};

var update = require(${loaderUtils.stringifyRequest(
this,
`!${path.join(__dirname, 'runtime/injectStylesIntoLinkTag.js')}`
)})(require(${loaderUtils.stringifyRequest(
this,
`!!${request}`
)}), ${JSON.stringify(options)});
${hmrCode}`;
)}), options);
${hmrCode}`;
}

case 'lazyStyleTag':
Expand Down Expand Up @@ -95,7 +91,7 @@ var dispose;
var content = require(${loaderUtils.stringifyRequest(this, `!!${request}`)});
var options = ${JSON.stringify(options)};

options.insertInto = ${insertInto};
options.insert = ${insert};
options.singleton = ${isSingleton};

if (typeof content === 'string') content = [[module.id, content, '']];
Expand Down Expand Up @@ -179,7 +175,7 @@ var insertInto;

var options = ${JSON.stringify(options)}

options.insertInto = ${insertInto};
options.insert = ${insert};
options.singleton = ${isSingleton};

var update = require(${loaderUtils.stringifyRequest(
Expand Down
Loading