Skip to content

Commit

Permalink
feat: add insertInto option (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahstro authored and michael-ciniawsky committed Mar 17, 2017
1 parent 71e0908 commit 6636868
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 13 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ Note: Behavior is undefined when `unuse`/`unref` is called more often than `use`

#### `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`

Expand Down
30 changes: 22 additions & 8 deletions addStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ var stylesInDom = {},
isOldIE = memoize(function() {
return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase());
}),
getHeadElement = memoize(function () {
return document.head || document.getElementsByTagName("head")[0];
getElement = (function(fn) {
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,
singletonCounter = 0,
Expand All @@ -33,7 +41,10 @@ module.exports = function(list, options) {
// tags it will allow on a page
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";

var styles = listToStyles(list);
Expand Down Expand Up @@ -103,19 +114,22 @@ function listToStyles(list) {
}

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];
if (options.insertAt === "top") {
if(!lastStyleElementInsertedAtTop) {
head.insertBefore(styleElement, head.firstChild);
styleTarget.insertBefore(styleElement, styleTarget.firstChild);
} else if(lastStyleElementInsertedAtTop.nextSibling) {
head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
} else {
head.appendChild(styleElement);
styleTarget.appendChild(styleElement);
}
styleElementsInsertedAtTop.push(styleElement);
} else if (options.insertAt === "bottom") {
head.appendChild(styleElement);
styleTarget.appendChild(styleElement);
} else {
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
}
Expand Down
15 changes: 14 additions & 1 deletion test/basicTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ describe("basic tests", function() {
localScopedCss = ":local(.className) { background: red; }",
requiredStyle = `<style type="text/css">${requiredCss}</style>`,
existingStyle = "<style>.existing { color: yellow }</style>",
checkValue = '<div class="check">check</div>',
rootDir = path.resolve(__dirname + "/../") + "/",
jsdomHtml = [
"<html>",
"<head>",
existingStyle,
"</head>",
"<body>",
"<div class='target'>",
checkValue,
"</div>",
"</body>",
"</html>"
].join("\n");
Expand Down Expand Up @@ -83,6 +87,15 @@ describe("basic tests", function() {
runCompilerTest(expected, done);
}); // 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) {
// Setup
styleLoaderOptions.singleton = true;
Expand Down Expand Up @@ -218,4 +231,4 @@ describe("basic tests", function() {
runCompilerTest(expected, done, function() { return this.css.locals.className; });
}); // it local scope

}); // describe
}); // describe
7 changes: 4 additions & 3 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ module.exports = {
* @param {function} done - Async callback from Mocha.
* @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) {
if (stats.compilation.errors.length) {
throw new Error(stats.compilation.errors);
Expand All @@ -73,7 +74,7 @@ module.exports = {
if (typeof actual === 'function') {
assert.equal(actual.apply(window), expected);
} else {
assert.equal(window.document.head.innerHTML.trim(), expected);
assert.equal(window.document.querySelector(selector).innerHTML.trim(), expected);
}
// free memory associated with the window
window.close();
Expand All @@ -83,4 +84,4 @@ module.exports = {
});
});
}
};
};

0 comments on commit 6636868

Please sign in to comment.