Skip to content
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

feat: preload css #300

Merged
merged 3 commits into from Oct 15, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/index.js
Expand Up @@ -323,10 +323,13 @@ class ExtractCssChunksPlugin {
}
);
const { insert } = this.options;
const supportsPreload =
'(function() { try { return document.createElement("link").relList.supports("preload"); } catch(e) { return false; }}());';
return Template.asString([
source,
'',
`// ${pluginName} CSS loading`,
`var supportsPreload = ${supportsPreload}`,
`var cssChunks = ${JSON.stringify(chunkMap)};`,
'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);',
'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {',
Expand All @@ -340,8 +343,7 @@ class ExtractCssChunksPlugin {
Template.indent([
'var tag = existingLinkTags[i];',
'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");',
'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();',
]),
'if((tag.rel === "stylesheet" || tag.rel === "preload") && (dataHref === href || dataHref === fullhref)) return resolve();', ]),
'}',
'var existingStyleTags = document.getElementsByTagName("style");',
'for(var i = 0; i < existingStyleTags.length; i++) {',
Expand All @@ -352,8 +354,8 @@ class ExtractCssChunksPlugin {
]),
'}',
'var linkTag = document.createElement("link");',
'linkTag.rel = "stylesheet";',
'linkTag.type = "text/css";',
'linkTag.rel = supportsPreload ? "preload": "stylesheet";',
'supportsPreload ? linkTag.as = "style" : linkTag.type = "text/css";',
'linkTag.onload = resolve;',
'linkTag.onerror = function(event) {',
Template.indent([
Expand Down Expand Up @@ -383,7 +385,18 @@ class ExtractCssChunksPlugin {
: 'var head = document.getElementsByTagName("head")[0]; head.appendChild(linkTag)',
]),
'}).then(function() {',
Template.indent(['installedCssChunks[chunkId] = 0;']),
Template.indent([
'installedCssChunks[chunkId] = 0;',
'if(supportsPreload) {',
Template.indent([
'var execLinkTag = document.createElement("link");',
`execLinkTag.href = ${mainTemplate.requireFn}.p + ${linkHrefPath};`,
'execLinkTag.rel = "stylesheet";',
'execLinkTag.type = "text/css";',
'document.body.appendChild(execLinkTag);',
]),
'}',
]),
'}));',
]),
'}',
Expand Down
3 changes: 2 additions & 1 deletion test/HMR.test.js
Expand Up @@ -30,7 +30,8 @@ describe('HMR', () => {

jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000);

document.head.innerHTML = '<link rel="stylesheet" href="/dist/main.css" />';
document.head.innerHTML =
'<link rel="preload" as="style" href="/dist/main.css" />';
document.body.innerHTML = '<script src="/dist/main.js"></script>';
});

Expand Down
12 changes: 6 additions & 6 deletions test/__snapshots__/HMR.test.js.snap
Expand Up @@ -2,7 +2,7 @@

exports[`HMR should handle error event 1`] = `"[HMR] css reload %s"`;

exports[`HMR should handle error event 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should handle error event 2`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"/dist/main.css\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;

exports[`HMR should reloads with # link href 1`] = `"[HMR] css reload %s"`;

Expand All @@ -18,22 +18,22 @@ exports[`HMR should reloads with link without href 2`] = `"<link rel=\\"styleshe

exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`;

exports[`HMR should reloads with locals 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with locals 2`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"/dist/main.css\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;

exports[`HMR should reloads with non http/https link href 1`] = `"[HMR] css reload %s"`;

exports[`HMR should reloads with non http/https link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"shortcut icon\\" href=\\"data:;base64,=\\">"`;

exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`;

exports[`HMR should reloads with reloadAll option 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with reloadAll option 2`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"/dist/main.css\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;

exports[`HMR should works 1`] = `"[HMR] css reload %s"`;

exports[`HMR should works 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should works 2`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"/dist/main.css\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;

exports[`HMR should works with multiple updates 1`] = `"[HMR] css reload %s"`;

exports[`HMR should works with multiple updates 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should works with multiple updates 2`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"/dist/main.css\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;

exports[`HMR should works with multiple updates 3`] = `"<link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200001\\">"`;
exports[`HMR should works with multiple updates 3`] = `"<link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"preload\\" as=\\"style\\" href=\\"http://localhost/dist/main.css?1479427200001\\">"`;
23 changes: 12 additions & 11 deletions test/cases/insert-function/expected/main.js
Expand Up @@ -82,6 +82,7 @@
/******/
/******/
/******/ // extract-css-chunks-webpack-plugin CSS loading
/******/ var supportsPreload = (function() { try { return document.createElement("link").relList.supports("preload"); } catch(e) { return false; }}());
/******/ var cssChunks = {"1":1};
/******/ if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);
/******/ else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {
Expand All @@ -92,7 +93,7 @@
/******/ for(var i = 0; i < existingLinkTags.length; i++) {
/******/ var tag = existingLinkTags[i];
/******/ var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");
/******/ if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();
/******/ if((tag.rel === "stylesheet" || tag.rel === "preload") && (dataHref === href || dataHref === fullhref)) return resolve();
/******/ }
/******/ var existingStyleTags = document.getElementsByTagName("style");
/******/ for(var i = 0; i < existingStyleTags.length; i++) {
Expand All @@ -101,8 +102,8 @@
/******/ if(dataHref === href || dataHref === fullhref) return resolve();
/******/ }
/******/ var linkTag = document.createElement("link");
/******/ linkTag.rel = "stylesheet";
/******/ linkTag.type = "text/css";
/******/ linkTag.rel = supportsPreload ? "preload": "stylesheet";
/******/ supportsPreload ? linkTag.as = "style" : linkTag.type = "text/css";
/******/ linkTag.onload = resolve;
/******/ linkTag.onerror = function(event) {
/******/ var request = event && event.target && event.target.src || fullhref;
Expand All @@ -122,16 +123,16 @@
/******/ reference.parentNode.insertBefore(linkTag, reference);
/******/ }
/******/ };
/******/ if (typeof insert === 'function') { insert(linkTag); }
/******/ else { var target = document.querySelector(function insert(linkTag) {
/******/ const reference = document.querySelector('.hot-reload');
/******/
/******/ if (reference) {
/******/ reference.parentNode.insertBefore(linkTag, reference);
/******/ }
/******/ }); target && insert === 'body' ? target && target.insertBefore(linkTag,target.firstChild) : target.appendChild(linkTag); }
/******/ insert(linkTag);
/******/ }).then(function() {
/******/ installedCssChunks[chunkId] = 0;
/******/ if(supportsPreload) {
/******/ var execLinkTag = document.createElement("link");
/******/ execLinkTag.href = __webpack_require__.p + "" + chunkId + ".css";
/******/ execLinkTag.rel = "stylesheet";
/******/ execLinkTag.type = "text/css";
/******/ document.body.appendChild(execLinkTag);
/******/ }
/******/ }));
/******/ }
/******/
Expand Down
4 changes: 3 additions & 1 deletion test/cases/insert-function/webpack.config.e2e.js
Expand Up @@ -64,7 +64,9 @@ module.exports = {
new Self({
filename: '[name].css',
chunkFilename: '[id].css',
insert: 'body',
insert: (linkTag) => {
document.head.appendChild(linkTag)
},
}),
],
devServer: {
Expand Down
19 changes: 13 additions & 6 deletions test/cases/insert-string/expected/main.js
Expand Up @@ -82,6 +82,7 @@
/******/
/******/
/******/ // extract-css-chunks-webpack-plugin CSS loading
/******/ var supportsPreload = (function() { try { return document.createElement("link").relList.supports("preload"); } catch(e) { return false; }}());
/******/ var cssChunks = {"1":1};
/******/ if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);
/******/ else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {
Expand All @@ -92,7 +93,7 @@
/******/ for(var i = 0; i < existingLinkTags.length; i++) {
/******/ var tag = existingLinkTags[i];
/******/ var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");
/******/ if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();
/******/ if((tag.rel === "stylesheet" || tag.rel === "preload") && (dataHref === href || dataHref === fullhref)) return resolve();
/******/ }
/******/ var existingStyleTags = document.getElementsByTagName("style");
/******/ for(var i = 0; i < existingStyleTags.length; i++) {
Expand All @@ -101,8 +102,8 @@
/******/ if(dataHref === href || dataHref === fullhref) return resolve();
/******/ }
/******/ var linkTag = document.createElement("link");
/******/ linkTag.rel = "stylesheet";
/******/ linkTag.type = "text/css";
/******/ linkTag.rel = supportsPreload ? "preload": "stylesheet";
/******/ supportsPreload ? linkTag.as = "style" : linkTag.type = "text/css";
/******/ linkTag.onload = resolve;
/******/ linkTag.onerror = function(event) {
/******/ var request = event && event.target && event.target.src || fullhref;
Expand All @@ -115,11 +116,17 @@
/******/ };
/******/ linkTag.href = fullhref;
/******/
/******/ var insert = "body";
/******/ if (typeof insert === 'function') { insert(linkTag); }
/******/ else { var target = document.querySelector("body"); target && insert === 'body' ? target && target.insertBefore(linkTag,target.firstChild) : target.appendChild(linkTag); }
/******/ var insert = body;
/******/ insert(linkTag);
/******/ }).then(function() {
/******/ installedCssChunks[chunkId] = 0;
/******/ if(supportsPreload) {
/******/ var execLinkTag = document.createElement("link");
/******/ execLinkTag.href = __webpack_require__.p + "" + chunkId + ".css";
/******/ execLinkTag.rel = "stylesheet";
/******/ execLinkTag.type = "text/css";
/******/ document.body.appendChild(execLinkTag);
/******/ }
/******/ }));
/******/ }
/******/
Expand Down
2 changes: 1 addition & 1 deletion test/cases/insert-string/webpack.config.e2e.js
Expand Up @@ -64,7 +64,7 @@ module.exports = {
new Self({
filename: '[name].css',
chunkFilename: '[id].css',
insert: 'body',
insert: '(linkTag) => { document.head.appendChild(linkTag) }',
}),
],
devServer: {
Expand Down
2 changes: 1 addition & 1 deletion test/cases/publicpath-emptystring/expected/main.css
@@ -1,5 +1,5 @@
body {
background: red;
background-image: url(cd0bb358c45b584743d8ce4991777c42.svg);
background-image: url(c9e192c015437a21dea1faa1d30f4941.svg);
}

@@ -1,5 +1,5 @@
body {
background: green;
background-image: url(../../cd0bb358c45b584743d8ce4991777c42.svg);
background-image: url(../../c9e192c015437a21dea1faa1d30f4941.svg);
}

2 changes: 1 addition & 1 deletion test/cases/publicpath-function/expected/nested/style.css
@@ -1,5 +1,5 @@
body {
background: red;
background-image: url(../cd0bb358c45b584743d8ce4991777c42.svg);
background-image: url(../c9e192c015437a21dea1faa1d30f4941.svg);
}

2 changes: 1 addition & 1 deletion test/cases/publicpath-trailing-slash/expected/main.css
@@ -1,5 +1,5 @@
body {
background: red;
background-image: url(/static/img/cd0bb358c45b584743d8ce4991777c42.svg);
background-image: url(/static/img/c9e192c015437a21dea1faa1d30f4941.svg);
}

2 changes: 1 addition & 1 deletion test/cases/simple-publicpath/expected/main.css
@@ -1,5 +1,5 @@
body {
background: red;
background-image: url(/static/img/cd0bb358c45b584743d8ce4991777c42.svg);
background-image: url(/static/img/c9e192c015437a21dea1faa1d30f4941.svg);
}

2 changes: 1 addition & 1 deletion test/cases/split-chunks/index.js
@@ -1,3 +1,3 @@
// eslint-disable-next-line import/no-extraneous-dependencies
// eslint-disable-next-line
import 'bootstrap.css';
import './style.css';
24 changes: 13 additions & 11 deletions test/inject-option.test.js
Expand Up @@ -13,14 +13,14 @@ describe('insert-options', () => {
});
await page.goto('http://localhost:5000/');
});
it('stylesheet was injected into body', async () => {
await page.waitFor(3000);
const bodyHTML = await page.evaluate(() => document.body.innerHTML);

await expect(bodyHTML.indexOf('type="text/css"') > 0).toBe(true);
it('style preload was injected into body', async () => {
// preloaded1 + main + inject
await expect(await page.$$eval('[type="text/css"]', links => links.length)).toEqual(3);
// inject
await expect(await page.$$eval('[rel="preload"]', preloads => preloads.length)).toEqual(1);
});

it('body background style set correctly', async () => {
it('body background style was not set', async () => {
const bodyStyle = await page.evaluate(() =>
getComputedStyle(document.body).getPropertyValue('background-color')
);
Expand All @@ -38,13 +38,14 @@ describe('insert-options', () => {
});
await page.goto('http://localhost:3001/');
});
it('stylesheet was injected into body', async () => {
const bodyHTML = await page.evaluate(() => document.body.innerHTML);

await expect(bodyHTML.indexOf('type="text/css"') > 0).toBe(true);
it('style preload was injected into body', async () => {
// preloaded1 + main + inject
await expect(await page.$$eval('[type="text/css"]', links => links.length)).toEqual(3);
// inject
await expect(await page.$$eval('[rel="preload"]', preloads => preloads.length)).toEqual(1);
});

it('body background style set correctly', async () => {
it('body background style was not set', async () => {
await page.waitFor(4000);
const bodyStyle = await page.evaluate(() =>
getComputedStyle(document.body).getPropertyValue('background-color')
Expand All @@ -55,6 +56,7 @@ describe('insert-options', () => {
});

afterAll(() => {
// eslint-disable-next-line
const childProcess = require('child_process').exec;
childProcess(`kill $(lsof -t -i:3001)`);
childProcess(`kill $(lsof -t -i:5000)`);
Expand Down