Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@
"rimraf": "^2.6.1",
"script-ext-html-webpack-plugin": "^1.8.0",
"simplehttp2server": "^2.0.0",
"source-map": "^0.5.6",
"stack-trace": "0.0.10",
"sw-precache-webpack-plugin": "^0.11.2",
"tmp": "0.0.31",
"unfetch": "^3.0.0",
Expand Down
62 changes: 58 additions & 4 deletions src/lib/webpack/prerender.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { resolve } from 'path';
import fs from 'fs';
import stackTrace from 'stack-trace';
import { SourceMapConsumer } from 'source-map';
import chalk from 'chalk';

export default function prerender(outputDir, params) {
export default function prerender(env, params) {
params = params || {};

let entry = resolve(outputDir, './ssr-build/ssr-bundle.js'),
let entry = resolve(env.dest, './ssr-build/ssr-bundle.js'),
url = params.url || '/';

global.location = { href:url, pathname:url };
Expand All @@ -21,7 +25,57 @@ export default function prerender(outputDir, params) {
let preact = require('preact'),
renderToString = require('preact-render-to-string');

let html = renderToString(preact.h(app, { url }));
try {
return renderToString(preact.h(app, { url }));
} catch (err) {
let stack = stackTrace.parse(err).filter(s => s.getFileName() === entry)[0];
if (!stack) {
throw err;
}

return html;
handlePrerenderError(err, env, stack, entry);
}
}

const handlePrerenderError = (err, env, stack, entry) => {
let errorMessage = err.toString();
let isReferenceError = errorMessage.startsWith('ReferenceError');
let sourceMapContent = JSON.parse(fs.readFileSync(`${entry}.map`));
let sourceMapConsumer = new SourceMapConsumer(sourceMapContent);
let position = sourceMapConsumer.originalPositionFor({
line: stack.getLineNumber(),
column: stack.getColumnNumber()
});

position.source = position.source.replace('webpack://', '.');

let sourcePath = resolve(env.src, position.source);
let sourceLines = fs.readFileSync(sourcePath, 'utf-8').split('\n');
let methodName = stack.getMethodName();
let sourceCodeHighlight = '';

for (var i = -4; i <= 4; i++) {
let color = i === 0 ? chalk.red : chalk.yellow;
let line = position.line + i;
sourceCodeHighlight += `${color(sourceLines[line - 1])}\n`;
}

process.stderr.write('\n');
process.stderr.write(chalk.red(`${errorMessage}\n`));
process.stderr.write(`method: ${methodName}\n`);
process.stderr.write(`at: ${sourcePath}\n`);
process.stderr.write('\n');
process.stderr.write('Source code:\n\n');
process.stderr.write(sourceCodeHighlight);
process.stderr.write('\n');
process.stderr.write(`This ${isReferenceError ? 'is most likely' : 'could be'} caused by using DOM or Web APIs.\n`);
process.stderr.write(`Pre-render runs in node and has no access to globals available in browsers.\n\n`);
process.stderr.write(`Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }'\n`);

if (methodName === 'componentWillMount') {
process.stderr.write(`or place logic in 'componentDidMount' method.\n`);
}
process.stderr.write('\n');
process.stderr.write(`Alternatively use 'preact build --no-prerender' to disable prerendering.\n\n`);
process.exit(1);
};
39 changes: 1 addition & 38 deletions src/lib/webpack/webpack-base-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default (env) => {
env.src = '.';
}

env.dest = resolve(cwd, env.dest || 'build');
env.manifest = readJson(src('manifest.json')) || {};
env.pkg = readJson(resolve(cwd, 'package.json')) || {};

Expand Down Expand Up @@ -271,44 +272,6 @@ const production = () => addPlugins([
value: s => `return;${ Array(s.length-7).join(' ') }(`
}]
}),

new webpack.optimize.UglifyJsPlugin({
output: {
comments: false
},
mangle: true,
sourceMap: true,
compress: {
properties: true,
keep_fargs: false,
pure_getters: true,
collapse_vars: true,
warnings: false,
screw_ie8: true,
sequences: true,
dead_code: true,
drop_debugger: true,
comparisons: true,
conditionals: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
hoist_funs: true,
if_return: true,
join_vars: true,
cascade: true,
drop_console: false,
pure_funcs: [
'classCallCheck',
'_classCallCheck',
'_possibleConstructorReturn',
'Object.freeze',
'invariant',
'warning'
]
}
}),
]);

export function helpers(env) {
Expand Down
49 changes: 43 additions & 6 deletions src/lib/webpack/webpack-client-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import baseConfig, { exists, helpers } from './webpack-base-config';
import prerender from './prerender';

export default env => {
let { isProd, cwd, src } = helpers(env);
let outputDir = resolve(cwd, env.dest || 'build');
let { isProd, src } = helpers(env);

return createConfig.vanilla([
baseConfig(env),
entryPoint({
'bundle': resolve(__dirname, './../entry'),
'polyfills': resolve(__dirname, './polyfills'),
}),
setOutput({
path: outputDir,
path: env.dest,
publicPath: '/',
filename: '[name].js',
chunkFilename: '[name].chunk.[chunkhash:5].js',
Expand Down Expand Up @@ -94,7 +94,7 @@ export default env => {
new PushManifestPlugin()
]),

htmlPlugin(env, outputDir),
htmlPlugin(env, src('.')),

isProd ? production(env) : development(env),

Expand Down Expand Up @@ -150,6 +150,43 @@ const development = config => {
};

const production = config => addPlugins([
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false
},
mangle: true,
sourceMap: true,
compress: {
properties: true,
keep_fargs: false,
pure_getters: true,
collapse_vars: true,
warnings: false,
screw_ie8: true,
sequences: true,
dead_code: true,
drop_debugger: true,
comparisons: true,
conditionals: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
hoist_funs: true,
if_return: true,
join_vars: true,
cascade: true,
drop_console: false,
pure_funcs: [
'classCallCheck',
'_classCallCheck',
'_possibleConstructorReturn',
'Object.freeze',
'invariant',
'warning'
]
}
}),
new SWPrecacheWebpackPlugin({
filename: 'sw.js',
navigateFallback: 'index.html',
Expand All @@ -164,7 +201,7 @@ const production = config => addPlugins([
})
]);

const htmlPlugin = (config, outputDir) => addPlugins([
const htmlPlugin = (config, src) => addPlugins([
new HtmlWebpackPlugin({
filename: 'index.html',
template: `!!ejs-loader!${config.template || resolve(__dirname, '../../resources/template.html')}`,
Expand All @@ -184,7 +221,7 @@ const htmlPlugin = (config, outputDir) => addPlugins([
excludeAssets: [/(bundle|polyfills)(\..*)?\.js$/],
config,
ssr(params) {
return config.prerender ? prerender(outputDir, params) : '';
return config.prerender ? prerender({ dest: config.dest, src }, params) : '';
}
}),
new HtmlWebpackExcludeAssetsPlugin(),
Expand Down
5 changes: 2 additions & 3 deletions src/lib/webpack/webpack-server-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import {
customConfig
} from '@webpack-blocks/webpack2';
import { resolve } from 'path';
import baseConfig, { helpers } from './webpack-base-config';
import baseConfig from './webpack-base-config';

export default (env) => {
let { cwd } = helpers(env);
return createConfig.vanilla([
baseConfig(env),
entryPoint(resolve(env.cwd, env.src || 'src', 'index.js')),
setOutput({
path: resolve(cwd, env.dest || 'build', 'ssr-build'),
path: resolve(env.dest, 'ssr-build'),
publicPath: '/',
filename: 'ssr-bundle.js',
chunkFilename: '[name].chunk.[chunkhash:5].js',
Expand Down
16 changes: 8 additions & 8 deletions tests/build.snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export default {
'style.css': { size: 131 },
'style.css.map': { size: 359 },
'ssr-build': {
'ssr-bundle.js': { size: 9450 },
'ssr-bundle.js.map': { size: 42461 },
'ssr-bundle.js': { size: 16245 },
'ssr-bundle.js.map': { size: 31821 },
'style.css': { size: 130 },
'style.css.map': { size: 360 },
}
Expand All @@ -55,8 +55,8 @@ export default {
'style.css.map': { size: 621 },
'manifest.json': { size: 290 },
'ssr-build': {
'ssr-bundle.js': { size: 10100 },
'ssr-bundle.js.map': { size: 46466 },
'ssr-bundle.js': { size: 18205 },
'ssr-bundle.js.map': { size: 33478 },
'style.css': { size: 296 },
'style.css.map': { size: 621 },
}
Expand All @@ -74,8 +74,8 @@ export default {
'style.css': { size: 1065 },
'style.css.map': { size: 2246 },
'ssr-build': {
'ssr-bundle.js': { size: 18960 },
'ssr-bundle.js.map': { size: 97403 },
'ssr-bundle.js': { size: 39459 },
'ssr-bundle.js.map': { size: 65629 },
'style.css': { size: 1065 },
'style.css.map': { size: 2250 },
}
Expand All @@ -93,8 +93,8 @@ export default {
'style.css': { size: 1065 },
'style.css.map': { size: 2345 },
'ssr-build': {
'ssr-bundle.js': { size: 19820 },
'ssr-bundle.js.map': { size: 101502 },
'ssr-bundle.js': { size: 41715 },
'ssr-bundle.js.map': { size: 66661 },
'style.css': { size: 1065 },
'style.css.map': { size: 2345 },
}
Expand Down