diff --git a/.gitignore b/.gitignore
index 3bd0e8b70..9135188c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ tmp-*.json
.idea/
.vscode
+*.tmp.*
diff --git a/.prettierignore b/.prettierignore
index 8741598a4..321f64a39 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,3 +1,4 @@
**/node_modules
**/tests/output
**/package.json
+**/*.ejs
diff --git a/packages/cli/lib/lib/webpack/empty.html b/packages/cli/lib/lib/webpack/empty.html
new file mode 100644
index 000000000..1a4a02127
--- /dev/null
+++ b/packages/cli/lib/lib/webpack/empty.html
@@ -0,0 +1 @@
+
diff --git a/packages/cli/lib/lib/webpack/inline-loader.js b/packages/cli/lib/lib/webpack/inline-loader.js
new file mode 100644
index 000000000..53700c564
--- /dev/null
+++ b/packages/cli/lib/lib/webpack/inline-loader.js
@@ -0,0 +1,8 @@
+import loaderUtils from 'loader-utils';
+
+module.exports = function() {
+ let { code, filename, cacheable } = loaderUtils.getOptions(this);
+ if (cacheable === false) this.cacheable(false);
+ this.resourcePath = filename;
+ return code.replace(/__PREACT__BANG__/g, '!');
+};
diff --git a/packages/cli/lib/lib/webpack/render-html-plugin.js b/packages/cli/lib/lib/webpack/render-html-plugin.js
index 7a54bb093..7288172b6 100644
--- a/packages/cli/lib/lib/webpack/render-html-plugin.js
+++ b/packages/cli/lib/lib/webpack/render-html-plugin.js
@@ -1,24 +1,57 @@
const { resolve } = require('path');
-const { existsSync } = require('fs');
+const { existsSync, readFileSync } = require('fs');
const HtmlWebpackExcludeAssetsPlugin = require('html-webpack-exclude-assets-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const prerender = require('./prerender');
const createLoadManifest = require('./create-load-manifest');
const { warn } = require('../../util');
const { info } = require('../../util');
-let template = resolve(__dirname, '../../resources/template.html');
+let defaultTemplate = resolve(__dirname, '../../resources/template.html');
+
+function read(path) {
+ return readFileSync(resolve(__dirname, path), 'utf-8');
+}
module.exports = async function(config) {
const { cwd, dest, isProd, src } = config;
const inProjectTemplatePath = resolve(src, 'template.html');
+ let template = defaultTemplate;
if (existsSync(inProjectTemplatePath)) {
template = inProjectTemplatePath;
}
+
+ template = config.template || template;
+
+ let content = read(template);
+ let hasAbtraction = false;
+ if (/preact\.headEnd|preact\.bodyEnd/.test(content)) {
+ hasAbtraction = true;
+ const headEnd = read('../../resources/head-end.ejs');
+ const bodyEnd = read('../../resources/body-end.ejs');
+ content = content
+ .replace(
+ /<%\s+preact\.title\s+%>/,
+ '<%= htmlWebpackPlugin.options.title %>'
+ )
+ .replace(/<%\s+preact\.headEnd\s+%>/, headEnd)
+ .replace(/<%\s+preact\.bodyEnd\s+%>/, bodyEnd);
+
+ // webpack treats every ! as a loader call. We'll replace it back to the
+ // original in our loader.
+ content = content.replace(/!/g, '__PREACT__BANG__');
+ template = `!!ejs-loader!inline-loader?filename=template.js&code=${content}!${resolve(
+ __dirname,
+ 'empty.html'
+ )}`;
+ // template = resolve(__dirname, 'template.tmp.ejs');
+ // writeFileSync(template, content);
+ }
+
const htmlWebpackConfig = values => {
const { url, title, ...routeData } = values;
return Object.assign(values, {
filename: resolve(dest, url.substring(1), 'index.html'),
- template: `!!ejs-loader!${config.template || template}`,
+ template: !hasAbtraction ? `!!ejs-loader!${template}` : template,
minify: isProd && {
collapseWhitespace: true,
removeScriptTypeAttributes: true,
diff --git a/packages/cli/lib/lib/webpack/webpack-base-config.js b/packages/cli/lib/lib/webpack/webpack-base-config.js
index 9f5abb0d9..3b907007f 100644
--- a/packages/cli/lib/lib/webpack/webpack-base-config.js
+++ b/packages/cli/lib/lib/webpack/webpack-base-config.js
@@ -139,6 +139,7 @@ module.exports = function(env) {
modules: [...nodeModules],
alias: {
'proxy-loader': require.resolve('./proxy-loader'),
+ 'inline-loader': require.resolve('./inline-loader'),
},
},
diff --git a/packages/cli/lib/resources/body-end.ejs b/packages/cli/lib/resources/body-end.ejs
new file mode 100644
index 000000000..e325d0210
--- /dev/null
+++ b/packages/cli/lib/resources/body-end.ejs
@@ -0,0 +1,18 @@
+<%= htmlWebpackPlugin.options.ssr() %>
+<% if (webpack.assets.filter(entry => entry.name.match(/bundle(\.\w{5})?.esm.js$/)).length > 0) { %>
+ <% /* Fix for safari < 11 nomodule bug. TODO: Do the following only for safari. */ %>
+
+
+ <%
+ /*Fetch and Promise polyfills are not needed for browsers that support type=module
+ Please re-evaluate below line if adding more polyfills.*/
+ %>
+
+
+<% } else { %>
+
+
+<% } %>
+
diff --git a/packages/cli/lib/resources/head-end.ejs b/packages/cli/lib/resources/head-end.ejs
new file mode 100644
index 000000000..b5bb729aa
--- /dev/null
+++ b/packages/cli/lib/resources/head-end.ejs
@@ -0,0 +1,12 @@
+
+<% if (htmlWebpackPlugin.options.manifest.theme_color) { %>
+
+<% } %>
+<% const loadManifest = htmlWebpackPlugin.options.createLoadManifest(compilation.assets, webpack.namedChunkGroups);%>
+<% const filesRegexp = htmlWebpackPlugin.options.inlineCss ? /\.(chunk\.\w{5}\.css|js)$/ : /\.(css|js)$/;%>
+<% for (const file in loadManifest[htmlWebpackPlugin.options.url]) { %>
+ <% if (htmlWebpackPlugin.options.preload && file && file.match(filesRegexp)) { %>
+ <% /* crossorigin for main bundle as that is loaded from `