feat: add insertInto option (#135)
parent
71e0908536
commit
663686884b
|
@ -66,7 +66,10 @@ Note: Behavior is undefined when `unuse`/`unref` is called more often than `use`
|
||||||
|
|
||||||
#### `insertAt`
|
#### `insertAt`
|
||||||
|
|
||||||
By default, the style-loader appends `<style>` elements to the end of the `<head>` tag of the page. This will cause CSS created by the loader to take priority over CSS already present in the document head. To insert style elements at the beginning of the head, set this query parameter to 'top', e.g. `require('../style.css?insertAt=top')`.
|
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. `require('../style.css?insertAt=top')`.
|
||||||
|
|
||||||
|
#### `insertInto`
|
||||||
|
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, e.g. into a [ShadowRoot](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot), you can specify a CSS selector for that element here, e.g. `require('../style.css?insertInto=#host::shadow>#root')`.
|
||||||
|
|
||||||
#### `singleton`
|
#### `singleton`
|
||||||
|
|
||||||
|
|
30
addStyles.js
30
addStyles.js
|
@ -13,8 +13,16 @@ var stylesInDom = {},
|
||||||
isOldIE = memoize(function() {
|
isOldIE = memoize(function() {
|
||||||
return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase());
|
return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase());
|
||||||
}),
|
}),
|
||||||
getHeadElement = memoize(function () {
|
getElement = (function(fn) {
|
||||||
return document.head || document.getElementsByTagName("head")[0];
|
var memo = {};
|
||||||
|
return function(selector) {
|
||||||
|
if (typeof memo[selector] === "undefined") {
|
||||||
|
memo[selector] = fn.call(this, selector);
|
||||||
|
}
|
||||||
|
return memo[selector]
|
||||||
|
};
|
||||||
|
})(function (styleTarget) {
|
||||||
|
return document.querySelector(styleTarget)
|
||||||
}),
|
}),
|
||||||
singletonElement = null,
|
singletonElement = null,
|
||||||
singletonCounter = 0,
|
singletonCounter = 0,
|
||||||
|
@ -33,7 +41,10 @@ module.exports = function(list, options) {
|
||||||
// tags it will allow on a page
|
// tags it will allow on a page
|
||||||
if (typeof options.singleton === "undefined") options.singleton = isOldIE();
|
if (typeof options.singleton === "undefined") options.singleton = isOldIE();
|
||||||
|
|
||||||
// By default, add <style> tags to the bottom of <head>.
|
// By default, add <style> tags to the <head> element
|
||||||
|
if (typeof options.insertInto === "undefined") options.insertInto = "head";
|
||||||
|
|
||||||
|
// By default, add <style> tags to the bottom of the target
|
||||||
if (typeof options.insertAt === "undefined") options.insertAt = "bottom";
|
if (typeof options.insertAt === "undefined") options.insertAt = "bottom";
|
||||||
|
|
||||||
var styles = listToStyles(list);
|
var styles = listToStyles(list);
|
||||||
|
@ -103,19 +114,22 @@ function listToStyles(list) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertStyleElement(options, styleElement) {
|
function insertStyleElement(options, styleElement) {
|
||||||
var head = getHeadElement();
|
var styleTarget = getElement(options.insertInto)
|
||||||
|
if (!styleTarget) {
|
||||||
|
throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
|
||||||
|
}
|
||||||
var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
|
var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
|
||||||
if (options.insertAt === "top") {
|
if (options.insertAt === "top") {
|
||||||
if(!lastStyleElementInsertedAtTop) {
|
if(!lastStyleElementInsertedAtTop) {
|
||||||
head.insertBefore(styleElement, head.firstChild);
|
styleTarget.insertBefore(styleElement, styleTarget.firstChild);
|
||||||
} else if(lastStyleElementInsertedAtTop.nextSibling) {
|
} else if(lastStyleElementInsertedAtTop.nextSibling) {
|
||||||
head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
|
styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
|
||||||
} else {
|
} else {
|
||||||
head.appendChild(styleElement);
|
styleTarget.appendChild(styleElement);
|
||||||
}
|
}
|
||||||
styleElementsInsertedAtTop.push(styleElement);
|
styleElementsInsertedAtTop.push(styleElement);
|
||||||
} else if (options.insertAt === "bottom") {
|
} else if (options.insertAt === "bottom") {
|
||||||
head.appendChild(styleElement);
|
styleTarget.appendChild(styleElement);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
|
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe("basic tests", function() {
|
||||||
localScopedCss = ":local(.className) { background: red; }",
|
localScopedCss = ":local(.className) { background: red; }",
|
||||||
requiredStyle = `<style type="text/css">${requiredCss}</style>`,
|
requiredStyle = `<style type="text/css">${requiredCss}</style>`,
|
||||||
existingStyle = "<style>.existing { color: yellow }</style>",
|
existingStyle = "<style>.existing { color: yellow }</style>",
|
||||||
|
checkValue = '<div class="check">check</div>',
|
||||||
rootDir = path.resolve(__dirname + "/../") + "/",
|
rootDir = path.resolve(__dirname + "/../") + "/",
|
||||||
jsdomHtml = [
|
jsdomHtml = [
|
||||||
"<html>",
|
"<html>",
|
||||||
|
@ -21,6 +22,9 @@ describe("basic tests", function() {
|
||||||
existingStyle,
|
existingStyle,
|
||||||
"</head>",
|
"</head>",
|
||||||
"<body>",
|
"<body>",
|
||||||
|
"<div class='target'>",
|
||||||
|
checkValue,
|
||||||
|
"</div>",
|
||||||
"</body>",
|
"</body>",
|
||||||
"</html>"
|
"</html>"
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
@ -83,6 +87,15 @@ describe("basic tests", function() {
|
||||||
runCompilerTest(expected, done);
|
runCompilerTest(expected, done);
|
||||||
}); // it insert at top
|
}); // it insert at top
|
||||||
|
|
||||||
|
it("insert into", function(done) {
|
||||||
|
let selector = "div.target";
|
||||||
|
styleLoaderOptions.insertInto = selector;
|
||||||
|
|
||||||
|
let expected = [checkValue, requiredStyle].join("\n");
|
||||||
|
|
||||||
|
runCompilerTest(expected, done, undefined, selector);
|
||||||
|
}); // it insert into
|
||||||
|
|
||||||
it("singleton", function(done) {
|
it("singleton", function(done) {
|
||||||
// Setup
|
// Setup
|
||||||
styleLoaderOptions.singleton = true;
|
styleLoaderOptions.singleton = true;
|
||||||
|
@ -218,4 +231,4 @@ describe("basic tests", function() {
|
||||||
runCompilerTest(expected, done, function() { return this.css.locals.className; });
|
runCompilerTest(expected, done, function() { return this.css.locals.className; });
|
||||||
}); // it local scope
|
}); // it local scope
|
||||||
|
|
||||||
}); // describe
|
}); // describe
|
||||||
|
|
|
@ -57,7 +57,8 @@ module.exports = {
|
||||||
* @param {function} done - Async callback from Mocha.
|
* @param {function} done - Async callback from Mocha.
|
||||||
* @param {function} actual - Executed in the context of jsdom window, should return a string to compare to.
|
* @param {function} actual - Executed in the context of jsdom window, should return a string to compare to.
|
||||||
*/
|
*/
|
||||||
runCompilerTest: function(expected, done, actual) {
|
runCompilerTest: function(expected, done, actual, selector) {
|
||||||
|
selector = selector || "head"
|
||||||
compiler.run(function(err, stats) {
|
compiler.run(function(err, stats) {
|
||||||
if (stats.compilation.errors.length) {
|
if (stats.compilation.errors.length) {
|
||||||
throw new Error(stats.compilation.errors);
|
throw new Error(stats.compilation.errors);
|
||||||
|
@ -73,7 +74,7 @@ module.exports = {
|
||||||
if (typeof actual === 'function') {
|
if (typeof actual === 'function') {
|
||||||
assert.equal(actual.apply(window), expected);
|
assert.equal(actual.apply(window), expected);
|
||||||
} else {
|
} else {
|
||||||
assert.equal(window.document.head.innerHTML.trim(), expected);
|
assert.equal(window.document.querySelector(selector).innerHTML.trim(), expected);
|
||||||
}
|
}
|
||||||
// free memory associated with the window
|
// free memory associated with the window
|
||||||
window.close();
|
window.close();
|
||||||
|
@ -83,4 +84,4 @@ module.exports = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue