Skip to content

Commit 8aa7efd

Browse files
Simplify preact-cli template injection
1 parent d6fd760 commit 8aa7efd

File tree

10 files changed

+106
-3
lines changed

10 files changed

+106
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ tmp-*.json
99

1010
.idea/
1111
.vscode
12+
*.tmp.*

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
**/node_modules
22
**/tests/output
33
**/package.json
4+
**/*.ejs

packages/cli/lib/lib/webpack/render-html-plugin.js

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
11
const { resolve } = require('path');
2-
const { existsSync } = require('fs');
2+
const { existsSync, readFileSync, writeFileSync } = require('fs');
33
const HtmlWebpackExcludeAssetsPlugin = require('html-webpack-exclude-assets-plugin');
44
const HtmlWebpackPlugin = require('html-webpack-plugin');
55
const prerender = require('./prerender');
66
const createLoadManifest = require('./create-load-manifest');
77
const { warn } = require('../../util');
88
const { info } = require('../../util');
9-
let template = resolve(__dirname, '../../resources/template.html');
9+
let defaultTemplate = resolve(__dirname, '../../resources/template.html');
10+
11+
function read(path) {
12+
return readFileSync(resolve(__dirname, path), 'utf-8');
13+
}
1014

1115
module.exports = async function(config) {
1216
const { cwd, dest, isProd, src } = config;
1317
const inProjectTemplatePath = resolve(src, 'template.html');
18+
let template = defaultTemplate;
1419
if (existsSync(inProjectTemplatePath)) {
1520
template = inProjectTemplatePath;
1621
}
22+
23+
template = config.template || template;
24+
25+
let content = read(template);
26+
if (/preact\.headEnd|preact\.bodyEnd/.test(content)) {
27+
const headEnd = read('../../resources/head-end.ejs');
28+
const bodyEnd = read('../../resources/body-end.ejs');
29+
content = content
30+
.replace(
31+
/<%\s+preact\.title\s+%>/,
32+
'<%= htmlWebpackPlugin.options.title %>'
33+
)
34+
.replace(/<%\s+preact\.headEnd\s+%>/, headEnd)
35+
.replace(/<%\s+preact\.bodyEnd\s+%>/, bodyEnd);
36+
37+
// Unfortunately html-webpack-plugin expects a true file,
38+
// so we'll create a temporary one.
39+
template = resolve(__dirname, 'template.tmp.ejs');
40+
writeFileSync(template, content);
41+
}
42+
1743
const htmlWebpackConfig = values => {
1844
const { url, title, ...routeData } = values;
1945
return Object.assign(values, {
2046
filename: resolve(dest, url.substring(1), 'index.html'),
21-
template: `!!ejs-loader!${config.template || template}`,
47+
template: `!!ejs-loader!${template}`,
2248
minify: isProd && {
2349
collapseWhitespace: true,
2450
removeScriptTypeAttributes: true,
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<%= htmlWebpackPlugin.options.ssr() %>
2+
<% if (webpack.assets.filter(entry => entry.name.match(/bundle(\.\w{5})?.esm.js$/)).length > 0) { %>
3+
<% /* Fix for safari < 11 nomodule bug. TODO: Do the following only for safari. */ %>
4+
<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
5+
<script crossorigin="anonymous" src="<%= htmlWebpackPlugin.files.publicPath %><%= webpack.assets.filter(entry => entry.name.match(/bundle(\.\w{5})?.esm.js$/))[0].name %>" type="module"></script>
6+
<%
7+
/*Fetch and Promise polyfills are not needed for browsers that support type=module
8+
Please re-evaluate below line if adding more polyfills.*/
9+
%>
10+
<script nomodule src="<%= htmlWebpackPlugin.files.chunks["polyfills"].entry %>"></script>
11+
<script nomodule defer src="<%= htmlWebpackPlugin.files.chunks['bundle'].entry %>"></script>
12+
<% } else { %>
13+
<script <%= htmlWebpackPlugin.options.scriptLoading %> src="<%= htmlWebpackPlugin.files.chunks['bundle'].entry %>"></script>
14+
<script nomodule src="<%= htmlWebpackPlugin.files.chunks["polyfills"].entry %>"></script>
15+
<% } %>
16+
<script type="__PREACT_CLI_DATA__">
17+
<%= JSON.stringify(htmlWebpackPlugin.options.CLI_DATA) %>
18+
</script>
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<link rel="manifest" href="<%= htmlWebpackPlugin.files.publicPath %>manifest.json">
2+
<% if (htmlWebpackPlugin.options.manifest.theme_color) { %>
3+
<meta name="theme-color" content="<%= htmlWebpackPlugin.options.manifest.theme_color %>">
4+
<% } %>
5+
<% const loadManifest = htmlWebpackPlugin.options.createLoadManifest(compilation.assets, webpack.namedChunkGroups);%>
6+
<% const filesRegexp = htmlWebpackPlugin.options.inlineCss ? /\.(chunk\.\w{5}\.css|js)$/ : /\.(css|js)$/;%>
7+
<% for (const file in loadManifest[htmlWebpackPlugin.options.url]) { %>
8+
<% if (htmlWebpackPlugin.options.preload && file && file.match(filesRegexp)) { %>
9+
<% /* crossorigin for main bundle as that is loaded from `<script type=module` tag, other lazy loaded bundles are from webpack so its not needed */ %>
10+
<link rel="preload" href="<%= htmlWebpackPlugin.files.publicPath + file %>" as="<%= file.match(/\.css$/)?'style':'script' %>" <%= file.match(/bundle\.\w{5}\.esm\.js$/)?'crossorigin="anonymous"':'' %>>
11+
<% } %>
12+
<% } %>

packages/cli/tests/build.test.js

+10
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,14 @@ describe('preact build', () => {
144144
let file = join(dir, 'build', '.htaccess');
145145
expect(existsSync(file)).toBe(true);
146146
});
147+
148+
it('should inject preact.* variables into template', async () => {
149+
let dir = await subject('custom-template-2');
150+
await build(dir);
151+
152+
let file = join(dir, 'build/index.html');
153+
let html = await readFile(file, 'utf-8');
154+
155+
looksLike(html, images.templateReplaced);
156+
});
147157
});

packages/cli/tests/images/build.js

+16
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,19 @@ exports.template = `
153153
</body>
154154
</html>
155155
`;
156+
157+
exports.templateReplaced = `
158+
<!DOCTYPE html>
159+
<html lang="en">
160+
<head>
161+
<meta charset="utf-8">
162+
<title>preact-webpack</title>
163+
<link rel="manifest" href="/manifest.json">
164+
</head>
165+
<body>
166+
<h1>Guess what</h1>
167+
<h2>This is an app with custom template</h2>
168+
{{ ... }}
169+
</body>
170+
</html>
171+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { h } from 'preact';
2+
3+
export default () => <h2>This is an app with custom template</h2>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"private": true,
3+
"name": "preact-webpack"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title><% preact.title %></title>
6+
<% preact.headEnd %>
7+
</head>
8+
<body>
9+
<h1>Guess what</h1>
10+
<% preact.bodyEnd %>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)