Skip to content

Commit

Permalink
fix: algorithm for importing modules (#449)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi authored Dec 25, 2019
1 parent 1138ed7 commit 91ceaf2
Show file tree
Hide file tree
Showing 15 changed files with 290 additions and 36 deletions.
7 changes: 2 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,7 @@ if (content.locals) {
exported.use = function() {
if (!(refs++)) {
var id = ${loaderUtils.stringifyRequest(this, `!!${request}`)};
dispose = api(id, content, options);
dispose = api(module.id, content, options);
}
return exported;
Expand Down Expand Up @@ -251,8 +249,7 @@ var options = ${JSON.stringify(options)};
options.insert = ${insert};
options.singleton = ${isSingleton};
var id = ${loaderUtils.stringifyRequest(this, `!!${request}`)};
var update = api(id, content, options);
var update = api(module.id, content, options);
var exported = content.locals ? content.locals : {};
Expand Down
72 changes: 41 additions & 31 deletions src/runtime/injectStylesIntoStyleTag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const stylesInDom = {};

const isOldIE = (function isOldIE() {
let memo;

Expand Down Expand Up @@ -46,38 +44,22 @@ const getTarget = (function getTarget() {
};
})();

function addModulesToDom(id, list, options) {
if (Object.prototype.toString.call(list) !== '[object Array]') {
return;
}

id = options.base ? id + options.base : id;

if (!stylesInDom[id]) {
stylesInDom[id] = [];
}
const stylesInDom = {};

function modulesToDom(moduleId, list, options) {
for (let i = 0; i < list.length; i++) {
const item = list[i];
const part = { css: item[1], media: item[2], sourceMap: item[3] };
const styleInDomById = stylesInDom[id];
const part = {
css: list[i][1],
media: list[i][2],
sourceMap: list[i][3],
};

if (styleInDomById[i]) {
styleInDomById[i].updater(part);
if (stylesInDom[moduleId][i]) {
stylesInDom[moduleId][i](part);
} else {
styleInDomById.push({ updater: addStyle(part, options) });
stylesInDom[moduleId].push(addStyle(part, options));
}
}

for (let j = list.length; j < stylesInDom[id].length; j++) {
stylesInDom[id][j].updater();
}

stylesInDom[id].length = list.length;

if (stylesInDom[id].length === 0) {
delete stylesInDom[id];
}
}

function insertStyleElement(options) {
Expand Down Expand Up @@ -230,7 +212,7 @@ function addStyle(obj, options) {
};
}

module.exports = (id, list, options) => {
module.exports = (moduleId, list, options) => {
options = options || {};

// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
Expand All @@ -239,9 +221,37 @@ module.exports = (id, list, options) => {
options.singleton = isOldIE();
}

addModulesToDom(id, list, options);
moduleId = options.base ? moduleId + options.base : moduleId;

list = list || [];

if (!stylesInDom[moduleId]) {
stylesInDom[moduleId] = [];
}

modulesToDom(moduleId, list, options);

return function update(newList) {
addModulesToDom(id, newList || [], options);
newList = newList || [];

if (Object.prototype.toString.call(newList) !== '[object Array]') {
return;
}

if (!stylesInDom[moduleId]) {
stylesInDom[moduleId] = [];
}

modulesToDom(moduleId, newList, options);

for (let j = newList.length; j < stylesInDom[moduleId].length; j++) {
stylesInDom[moduleId][j]();
}

stylesInDom[moduleId].length = newList.length;

if (stylesInDom[moduleId].length === 0) {
delete stylesInDom[moduleId];
}
};
};
114 changes: 114 additions & 0 deletions test/__snapshots__/loader.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,30 @@ exports[`loader should work when the "injectType" option is "lazySingletonStyleT
exports[`loader should work when the "injectType" option is "lazySingletonStyleTag" and ES module syntax used: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazySingletonStyleTag" and files have same name: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<style>.foo {
color: red;
}
.bar {
color: blue;
}
</style></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`loader should work when the "injectType" option is "lazySingletonStyleTag" and files have same name: errors 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazySingletonStyleTag" and files have same name: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazySingletonStyleTag": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down Expand Up @@ -434,6 +458,30 @@ exports[`loader should work when the "injectType" option is "lazyStyleTag" and E
exports[`loader should work when the "injectType" option is "lazyStyleTag" and ES module syntax used: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazyStyleTag" and files have same name: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<style>.foo {
color: red;
}
</style><style>.bar {
color: blue;
}
</style></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`loader should work when the "injectType" option is "lazyStyleTag" and files have same name: errors 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazyStyleTag" and files have same name: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "lazyStyleTag": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down Expand Up @@ -494,6 +542,24 @@ exports[`loader should work when the "injectType" option is "linkTag" and ES mod
exports[`loader should work when the "injectType" option is "linkTag" and ES module syntax used: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "linkTag" and files have same name: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<link rel=\\"stylesheet\\" href=\\"04ef9d7491cb6b87d905831b3cb52fdc.css\\"><link rel=\\"stylesheet\\" href=\\"a948740e081d1358c62c536683d63685.css\\"></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`loader should work when the "injectType" option is "linkTag" and files have same name: errors 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "linkTag" and files have same name: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "linkTag": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down Expand Up @@ -560,6 +626,30 @@ exports[`loader should work when the "injectType" option is "singletonStyleTag"
exports[`loader should work when the "injectType" option is "singletonStyleTag" and ES module syntax used: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "singletonStyleTag" and files have same name: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<style>.foo {
color: red;
}
.bar {
color: blue;
}
</style></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`loader should work when the "injectType" option is "singletonStyleTag" and files have same name: errors 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "singletonStyleTag" and files have same name: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "singletonStyleTag": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down Expand Up @@ -632,6 +722,30 @@ exports[`loader should work when the "injectType" option is "styleTag" and ES mo
exports[`loader should work when the "injectType" option is "styleTag" and ES module syntax used: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "styleTag" and files have same name: DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
<style id=\\"existing-style\\">.existing { color: yellow }</style>
<style>.foo {
color: red;
}
</style><style>.bar {
color: blue;
}
</style></head>
<body>
<h1>Body</h1>
<div class=\\"target\\"></div>
<iframe class=\\"iframeTarget\\"></iframe>
</body></html>"
`;
exports[`loader should work when the "injectType" option is "styleTag" and files have same name: errors 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "styleTag" and files have same name: warnings 1`] = `Array []`;
exports[`loader should work when the "injectType" option is "styleTag": DOM 1`] = `
"<!DOCTYPE html><html><head>
<title>style-loader test</title>
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/lazy-multiple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import style from './nested/style.css';
import otherStyle from './other-nested/style.css';

style.use();
otherStyle.use();
2 changes: 2 additions & 0 deletions test/fixtures/multiple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import './nested/style.css';
import './other-nested/style.css';
3 changes: 3 additions & 0 deletions test/fixtures/nested/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
color: red;
}
3 changes: 3 additions & 0 deletions test/fixtures/other-nested/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.bar {
color: blue;
}
15 changes: 15 additions & 0 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,21 @@ describe('loader', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it(`should work when the "injectType" option is "${injectType}" and files have same name`, async () => {
expect.assertions(3);

const entry = getEntryByInjectType('multiple.js', injectType);
const compiler = getCompiler(entry, { injectType });
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');
});

if (['lazyStyleTag', 'lazySingletonStyleTag'].includes(injectType)) {
it(`should work when ref is negative when the "injectType" option is "${injectType}"`, async () => {
expect.assertions(3);
Expand Down
5 changes: 5 additions & 0 deletions test/manual/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ <h2>Order</h2>
<div class="order">BACKGROUND SHOULD BE RED</div>
</section>

<section>
<h2>Duplicate</h2>
<div class="duplicate">SHOULD BE BLUE</div>
</section>

<section>
<h2>Custom element</h2>
<custom-square l="100" c="red"></custom-square>
Expand Down
3 changes: 3 additions & 0 deletions test/manual/src/duplicate.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.duplicate {
color: red;
}
2 changes: 2 additions & 0 deletions test/manual/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import otherStyleLazy from './other-style.lazy.scss';
import componentLazy from './component.lazy.module.css';
import './style.link.css';
import './order.css';
import './nested.css';
import './nested/style.css';
import './custom-square';

console.log('___LOCALS___');
Expand Down
2 changes: 2 additions & 0 deletions test/manual/src/nested.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import './nested/style.css';
@import './duplicate.css';
5 changes: 5 additions & 0 deletions test/manual/src/nested/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '../duplicate.css';

.duplicate {
color: blue;
}
26 changes: 26 additions & 0 deletions test/runtime/__snapshots__/injectStylesIntoStyleTag.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`addStyle issue 447 #2 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;

exports[`addStyle issue 447 #2 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;

exports[`addStyle issue 447 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;

exports[`addStyle should throw error with incorrect "insert" option 1`] = `"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."`;
Expand Down Expand Up @@ -32,6 +36,24 @@ exports[`addStyle should work with "insert" option 1`] = `"<head><title>Title</t
exports[`addStyle should work with "nonce" attribute and "__webpack_nonce__" variable 1`] = `"<head><title>Title</title><style nonce=\\"87654321\\">.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #2 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #2 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #2 3`] = `"<head><title>Title</title><style>.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #3 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #3 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #3 3`] = `"<head><title>Title</title><style>.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list #3 4`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with empty modules list 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with media 1`] = `"<head><title>Title</title><style media=\\"screen and (min-width:320px)\\">.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with multiple styles 1`] = `"<head><title>Title</title><style>.foo { color: red }</style><style>.bar { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
Expand Down Expand Up @@ -128,3 +150,7 @@ exports[`addStyle should work with updates #14 5`] = `"<head><title>Title</title
exports[`addStyle should work with updates 1`] = `"<head><title>Title</title><style>.foo { color: red }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work with updates 2`] = `"<head><title>Title</title><style>.foo { color: blue }</style></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work without modules 1`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
exports[`addStyle should work without modules 2`] = `"<head><title>Title</title></head><body><h1>Hello world</h1></body>"`;
Loading

0 comments on commit 91ceaf2

Please sign in to comment.