Skip to content

Commit

Permalink
Adding blank page for fallback (#1441)
Browse files Browse the repository at this point in the history
* Adding blank page for fallback

* switching fallback to 200.html

* adding tests

* bug fix

* fixing tests

* Apply suggestions from code review

Co-authored-by: Leah <github.leah@hrmny.sh>

Co-authored-by: Jason Miller <developit@users.noreply.github.com>
Co-authored-by: Leah <github.leah@hrmny.sh>
  • Loading branch information
3 people committed Nov 6, 2020
1 parent 2c53b0a commit 4cc93ba
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 34 deletions.
37 changes: 30 additions & 7 deletions packages/cli/lib/lib/webpack/render-html-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const { warn } = require('../../util');
const { info } = require('../../util');
const { PRERENDER_DATA_FILE_NAME } = require('../constants');

const PREACT_FALLBACK_URL = '/200.html';

let defaultTemplate = resolve(__dirname, '../../resources/template.html');

function read(path) {
Expand Down Expand Up @@ -53,10 +55,14 @@ module.exports = async function (config) {
writeFileSync(template, content);
}

const htmlWebpackConfig = values => {
const htmlWebpackConfig = (values) => {
const { url, title, ...routeData } = values;
// Do not create a folder if the url is for a specific file.
const filename = url.endsWith('.html')
? resolve(dest, url.substring(1))
: resolve(dest, url.substring(1), 'index.html');
return Object.assign(values, {
filename: resolve(dest, url.substring(1), 'index.html'),
filename,
template: `!!ejs-loader?esModule=false!${template}`,
minify: isProd && {
collapseWhitespace: true,
Expand Down Expand Up @@ -90,7 +96,9 @@ module.exports = async function (config) {
config,
url,
ssr() {
return config.prerender ? prerender({ cwd, dest, src }, values) : '';
return config.prerender && url !== PREACT_FALLBACK_URL
? prerender({ cwd, dest, src }, values)
: '';
},
scriptLoading: 'defer',
CLI_DATA: { preRenderData: { url, ...routeData } },
Expand Down Expand Up @@ -137,12 +145,21 @@ module.exports = async function (config) {
);
}
}
/**
* We cache a non SSRed page in service worker so that there is
* no flash of content when user lands on routes other than `/`.
* And we dont have to cache every single html file.
* Go easy on network usage of clients.
*/
!pages.find((page) => page.url === PREACT_FALLBACK_URL) &&
pages.push({ url: PREACT_FALLBACK_URL });

return pages
const resultPages = pages
.map(htmlWebpackConfig)
.map(conf => new HtmlWebpackPlugin(conf))
.map((conf) => new HtmlWebpackPlugin(conf))
.concat([new HtmlWebpackExcludeAssetsPlugin()])
.concat([...pages.map(page => new PrerenderDataExtractPlugin(page))]);
.concat([...pages.map((page) => new PrerenderDataExtractPlugin(page))]);
return resultPages;
};

// Adds a preact_prerender_data in every folder so that the data could be fetched separately.
Expand All @@ -154,7 +171,11 @@ class PrerenderDataExtractPlugin {
this.data_ = JSON.stringify(cliData.preRenderData || {});
}
apply(compiler) {
compiler.hooks.emit.tap('PrerenderDataExtractPlugin', compilation => {
compiler.hooks.emit.tap('PrerenderDataExtractPlugin', (compilation) => {
if (this.location_ === `${PREACT_FALLBACK_URL}/`) {
// We dont build prerender data for `200.html`. It can re-use the one for homepage.
return;
}
let path = this.location_ + PRERENDER_DATA_FILE_NAME;
if (path.startsWith('/')) {
path = path.substr(1);
Expand All @@ -166,3 +187,5 @@ class PrerenderDataExtractPlugin {
});
}
}

exports.PREACT_FALLBACK_URL = PREACT_FALLBACK_URL;
9 changes: 2 additions & 7 deletions packages/cli/lib/lib/webpack/webpack-client-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ function isProd(config) {
swSrc: swPath,
swDest: 'sw-esm.js',
include: [
/^\/?index\.html$/,
/200\.html$/,
/\.esm.js$/,
/\.css$/,
/\.(png|jpg|svg|gif|webp)$/,
Expand All @@ -246,12 +246,7 @@ function isProd(config) {
prodConfig.plugins.push(
new InjectManifest({
swSrc: swPath,
include: [
/index\.html$/,
/\.js$/,
/\.css$/,
/\.(png|jpg|svg|gif|webp)$/,
],
include: [/200\.html$/, /\.js$/, /\.css$/, /\.(png|jpg|svg|gif|webp)$/],
exclude: [/\.esm\.js$/],
})
);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.6",
"preact-router": "^3.0.1",
"puppeteer": "^5.0.0",
"puppeteer": "^5.3.1",
"sass-loader": "^10.0.4",
"shelljs": "^0.8.3",
"sirv": "^1.0.0-next.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/sw/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function setupRouting() {

setCatchHandler(({ event }) => {
if (isNav(event)) {
return caches.match(getCacheKeyForURL('/index.html'));
return caches.match(getCacheKeyForURL('/200.html'));
}
return Response.error();
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli/tests/images/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exports.default = exports.full = Object.assign({}, common, {
'bundle.7e56a.css': 901,
'favicon.ico': 15086,
'index.html': 2034,
'200.html': 613,
'manifest.json': 455,
'preact_prerender_data.json': 11,
'push-manifest.json': 812,
Expand Down
56 changes: 49 additions & 7 deletions packages/cli/tests/service-worker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,26 @@ const { sleep } = require('./lib/utils');
const { getServer } = require('./server');
const startChrome = require('./lib/chrome');

async function enableOfflineMode(page, browser) {
await sleep(2000); // wait for service worker installation.
await page.setOfflineMode(true);
const targets = await browser.targets();
const serviceWorker = targets.find((t) => t.type() === 'service_worker');
const serviceWorkerConnection = await serviceWorker.createCDPSession();
await serviceWorkerConnection.send('Network.enable');
await serviceWorkerConnection.send('Network.emulateNetworkConditions', {
offline: true,
latency: 0,
downloadThroughput: 0,
uploadThroughput: 0,
});
}

describe('preact service worker tests', () => {
let server, browser, dir;

beforeAll(async () => {
dir = await create('default');
browser = await startChrome();
await build(dir, {
sw: true,
esm: true,
Expand All @@ -19,9 +33,16 @@ describe('preact service worker tests', () => {
server = getServer(dir);
});

beforeEach(async () => {
browser = await startChrome();
});

afterEach(async () => {
await browser.close();
});

afterAll(async () => {
await server.server.stop();
await browser.close();
});

it('works offline', async () => {
Expand All @@ -31,15 +52,14 @@ describe('preact service worker tests', () => {
waitUntil: 'networkidle0',
});
const initialContent = await page.content();
await sleep(2000); // wait for service worker installation.
await page.setOfflineMode(true);
await page.reload();
await enableOfflineMode(page, browser);
await page.reload({ waitUntil: 'networkidle0' });
const offlineContent = await page.content();
await page.waitForSelector('h1');
expect(
await page.$$eval('h1', nodes => nodes.map(n => n.innerText))
await page.$$eval('h1', (nodes) => nodes.map((n) => n.innerText))
).toEqual(['Preact App', 'Home']);
expect(offlineContent).toEqual(initialContent);
expect(offlineContent).not.toEqual(initialContent);
});

it('should fetch navigation requests with networkFirst', async () => {
Expand All @@ -64,4 +84,26 @@ describe('preact service worker tests', () => {
expect(initialContent).not.toEqual(refreshedContent);
expect(refreshedContent.includes(NEW_TITLE)).toEqual(true);
});

it('should respond with 200.html when offline', async () => {
const swText = await fetch('http://localhost:3000/sw-esm.js').then((res) =>
res.text()
);
// eslint-disable-next-line no-useless-escape
expect(swText).toContain(
'caches.match((t="/200.html",ce().getCacheKeyForURL(t)))'
);
const page = await browser.newPage();
await page.setCacheEnabled(false);
await page.goto('http://localhost:3000', {
waitUntil: 'networkidle0',
});
await enableOfflineMode(page, browser);
await page.reload({ waitUntil: 'networkidle0' });
expect(
await page.$$eval('script[type=__PREACT_CLI_DATA__]', (nodes) =>
nodes.map((n) => n.innerText)
)
).toEqual(['%7B%22preRenderData%22:%7B%22url%22:%22/200.html%22%7D%7D']);
});
});
21 changes: 10 additions & 11 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5343,10 +5343,10 @@ detect-node@^2.0.4:
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==

devtools-protocol@0.0.781568:
version "0.0.781568"
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.781568.tgz#4cdca90a952d2c77831096ff6cd32695d8715a04"
integrity sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==
devtools-protocol@0.0.799653:
version "0.0.799653"
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.799653.tgz#86fc95ce5bf4fdf4b77a58047ba9d2301078f119"
integrity sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==

dezalgo@^1.0.0, dezalgo@~1.0.3:
version "1.0.3"
Expand Down Expand Up @@ -9618,7 +9618,7 @@ mime@1.6.0:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==

mime@^2.0.3, mime@^2.3.1, mime@^2.4.4:
mime@^2.3.1, mime@^2.4.4:
version "2.4.6"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
Expand Down Expand Up @@ -11801,16 +11801,15 @@ pupa@^2.0.1:
dependencies:
escape-goat "^2.0.0"

puppeteer@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.2.1.tgz#7f0564f0a5384f352a38c8cc42af875cd87f4ea6"
integrity sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw==
puppeteer@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.3.1.tgz#324e190d89f25ac33dba539f57b82a18553f8646"
integrity sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==
dependencies:
debug "^4.1.0"
devtools-protocol "0.0.781568"
devtools-protocol "0.0.799653"
extract-zip "^2.0.0"
https-proxy-agent "^4.0.0"
mime "^2.0.3"
pkg-dir "^4.2.0"
progress "^2.0.1"
proxy-from-env "^1.0.0"
Expand Down

0 comments on commit 4cc93ba

Please sign in to comment.