Skip to content

Commit dcde1e2

Browse files
authored
Merge pull request #172 from developit/feat/immutable-build
Feature: Immutable build
2 parents 32b31e8 + 7c4dcc3 commit dcde1e2

File tree

8 files changed

+92
-78
lines changed

8 files changed

+92
-78
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
"unfetch": "^3.0.0",
159159
"url-loader": "^0.5.8",
160160
"webpack": "^2.3.3",
161+
"webpack-chunk-hash": "^0.4.0",
161162
"webpack-dev-server": "^2.4.5",
162163
"webpack-plugin-replace": "^1.1.1",
163164
"which": "^1.2.14",

src/lib/webpack/async-component-loader.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
var loaderUtils = require('loader-utils'),
2-
path = require('path');
1+
var loaderUtils = require('loader-utils');
32

43
module.exports = function() {};
54
module.exports.pitch = function(remainingRequest) {
@@ -16,7 +15,7 @@ module.exports.pitch = function(remainingRequest) {
1615
}
1716

1817
return `
19-
import async from ${JSON.stringify(path.resolve(__dirname, '../../components/async'))};
18+
import async from 'preact-cli/async-component';
2019
2120
function load(cb) {
2221
require.ensure([], function(require) {

src/lib/webpack/push-manifest.js

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,45 @@
11
module.exports = class PushManifestPlugin {
22
apply(compiler) {
33
compiler.plugin('emit', function(compilation, callback) {
4-
let defaults = {
5-
'style.css': {
6-
type: 'style',
7-
weight: 1
8-
},
9-
'bundle.js': {
10-
type: 'script',
11-
weight: 1
12-
},
13-
},
14-
manifest = {
15-
'/': defaults
16-
};
4+
let routes = [], mainJs, mainCss;
175

186
for (let filename in compilation.assets) {
19-
if (/route-/.test(filename) && !/\.map$/.test(filename)) {
20-
let path = filename.replace(/route-/, '/').replace(/\.chunk(\.\w+)?\.js$/, '').replace(/\/home/, '/');
21-
manifest[path] = {
22-
...defaults,
23-
[filename]: {
24-
type: 'script',
25-
weight: .9
26-
}
27-
};
7+
if (!/\.map$/.test(filename)) {
8+
if (/route-/.test(filename)) {
9+
routes.push(filename);
10+
} else if (/^style(.+)\.css$/.test(filename)) {
11+
mainCss = filename;
12+
} else if (/^bundle(.+)\.js$/.test(filename)) {
13+
mainJs = filename;
14+
}
2815
}
2916
}
3017

18+
let defaults = {
19+
[mainCss]: {
20+
type: 'style',
21+
weight: 1
22+
},
23+
[mainJs]: {
24+
type: 'script',
25+
weight: 1
26+
},
27+
},
28+
manifest = {
29+
'/': defaults
30+
};
31+
32+
routes.forEach(filename => {
33+
let path = filename.replace(/route-/, '/').replace(/\.chunk(\.\w+)?\.js$/, '').replace(/\/home/, '/');
34+
manifest[path] = {
35+
...defaults,
36+
[filename]: {
37+
type: "script",
38+
weight: 0.9
39+
}
40+
};
41+
});
42+
3143
let output = JSON.stringify(manifest);
3244
compilation.assets['push-manifest.json'] = {
3345
source() {

src/lib/webpack/webpack-base-config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import ExtractTextPlugin from 'extract-text-webpack-plugin';
1313
import autoprefixer from 'autoprefixer';
1414
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
1515
import ReplacePlugin from 'webpack-plugin-replace';
16+
import WebpackChunkHash from 'webpack-chunk-hash';
1617
import requireRelative from 'require-relative';
1718
import createBabelConfig from '../babel-config';
1819

@@ -70,7 +71,8 @@ export default (env) => {
7071
react: 'preact-compat',
7172
'react-dom': 'preact-compat',
7273
'create-react-class': 'preact-compat/lib/create-react-class',
73-
'react-addons-css-transition-group': 'preact-css-transition-group'
74+
'react-addons-css-transition-group': 'preact-css-transition-group',
75+
'preact-cli/async-component': resolve(__dirname, '../../components/async')
7476
}
7577
},
7678
resolveLoader: {
@@ -238,7 +240,7 @@ export default (env) => {
238240
// produce HTML & CSS:
239241
addPlugins([
240242
new ExtractTextPlugin({
241-
filename: 'style.css',
243+
filename: isProd ? "style.[contenthash:5].css" : "style.css",
242244
disable: !isProd,
243245
allChunks: true
244246
})
@@ -277,6 +279,7 @@ const development = () => group([]);
277279

278280
const production = () => addPlugins([
279281
new webpack.HashedModuleIdsPlugin(),
282+
new WebpackChunkHash(),
280283
new webpack.LoaderOptionsPlugin({
281284
minimize: true
282285
}),

src/lib/webpack/webpack-client-config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default env => {
3232
setOutput({
3333
path: env.dest,
3434
publicPath: '/',
35-
filename: '[name].js',
35+
filename: isProd ? "[name].[chunkhash:5].js" : "[name].js",
3636
chunkFilename: '[name].chunk.[chunkhash:5].js',
3737
}),
3838

tests/build.snapshot.js

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11

2+
const commons = {
3+
'polyfills.*.js': { size: 4620 },
4+
'polyfills.*.js.map': { size: 31760 },
5+
'favicon.ico': { size: 15086 },
6+
};
7+
28
const smallBuildCommons = {
9+
...commons,
310
assets: {
411
'favicon.ico': { size: 15086 },
512
'icon.png': { size: 51484 }
613
},
7-
'polyfills.js': { size: 4620 },
8-
'polyfills.js.map': { size: 31760 },
9-
'favicon.ico': { size: 15086 },
1014
'sw.js': { size: 3330 },
1115
'manifest.json': { size: 298 },
12-
'push-manifest.json': { size: 88 },
16+
'push-manifest.json': { size: 100 },
1317
};
1418

1519
const fullBuildCommons = {
20+
...commons,
1621
assets: {
1722
'favicon.ico': { size: 15086 },
1823
icons: {
@@ -24,79 +29,75 @@ const fullBuildCommons = {
2429
'mstile-150x150.png': { size: 9050 }
2530
}
2631
},
27-
'polyfills.js': { size: 4620 },
28-
'push-manifest.json': { size: 303 },
29-
'favicon.ico': { size: 15086 },
32+
'push-manifest.json': { size: 327 },
3033
'manifest.json': { size: 426 },
3134
'sw.js': { size: 3850 }
3235
};
3336

3437
export default {
3538
empty: {
3639
...smallBuildCommons,
37-
'bundle.js': { size: 9810 },
38-
'bundle.js.map': { size: 44660 },
40+
'bundle.*.js': { size: 9810 },
41+
'bundle.*.js.map': { size: 44660 },
3942
'index.html': { size: 630 },
40-
'style.css': { size: 131 },
41-
'style.css.map': { size: 359 },
43+
'style.*.css': { size: 131 },
44+
'style.*.css.map': { size: 359 },
4245
'ssr-build': {
4346
'ssr-bundle.js': { size: 16245 },
4447
'ssr-bundle.js.map': { size: 31821 },
45-
'style.css': { size: 130 },
46-
'style.css.map': { size: 360 },
48+
'style.*.css': { size: 130 },
49+
'style.*.css.map': { size: 360 },
4750
}
4851
},
4952
simple: {
5053
...smallBuildCommons,
51-
'bundle.js': { size: 10460 },
52-
'bundle.js.map': { size: 48670 },
54+
'bundle.*.js': { size: 10460 },
55+
'bundle.*.js.map': { size: 48670 },
5356
'index.html': { size: 640 },
54-
'style.css': { size: 296},
55-
'style.css.map': { size: 621 },
57+
'style.*.css': { size: 296},
58+
'style.*.css.map': { size: 621 },
5659
'manifest.json': { size: 290 },
5760
'ssr-build': {
5861
'ssr-bundle.js': { size: 18205 },
5962
'ssr-bundle.js.map': { size: 33478 },
60-
'style.css': { size: 296 },
61-
'style.css.map': { size: 621 },
63+
'style.*.css': { size: 296 },
64+
'style.*.css.map': { size: 621 },
6265
}
6366
},
6467
root: {
6568
...fullBuildCommons,
66-
'bundle.js': { size: 18460 },
67-
'bundle.js.map': { size: 101500 },
69+
'bundle.*.js': { size: 18460 },
70+
'bundle.*.js.map': { size: 101500 },
6871
'route-home.chunk.*.js': { size: 1020 },
6972
'route-home.chunk.*.js.map': { size: 4977 },
7073
'route-profile.chunk.*.js': { size: 1660 },
7174
'route-profile.chunk.*.js.map': { size: 8607 },
72-
'polyfills.js.map': { size: 31750 },
7375
'index.html': { size: 870 },
74-
'style.css': { size: 1065 },
75-
'style.css.map': { size: 2246 },
76+
'style.*.css': { size: 1065 },
77+
'style.*.css.map': { size: 2246 },
7678
'ssr-build': {
7779
'ssr-bundle.js': { size: 39459 },
7880
'ssr-bundle.js.map': { size: 65629 },
79-
'style.css': { size: 1065 },
80-
'style.css.map': { size: 2250 },
81+
'style.*.css': { size: 1065 },
82+
'style.*.css.map': { size: 2250 },
8183
}
8284
},
8385
'full': {
8486
...fullBuildCommons,
85-
'bundle.js': { size: 19300 },
86-
'bundle.js.map': { size: 105590 },
87+
'bundle.*.js': { size: 19300 },
88+
'bundle.*.js.map': { size: 105590 },
8789
'route-home.chunk.*.js': { size: 1000 },
8890
'route-home.chunk.*.js.map': { size: 4981 },
8991
'route-profile.chunk.*.js': { size: 1650 },
9092
'route-profile.chunk.*.js.map': { size: 8609 },
91-
'polyfills.js.map': { size: 31800 },
9293
'index.html': { size: 850 },
93-
'style.css': { size: 1065 },
94-
'style.css.map': { size: 2345 },
94+
'style.*.css': { size: 1065 },
95+
'style.*.css.map': { size: 2345 },
9596
'ssr-build': {
9697
'ssr-bundle.js': { size: 41715 },
9798
'ssr-bundle.js.map': { size: 66661 },
98-
'style.css': { size: 1065 },
99-
'style.css.map': { size: 2345 },
99+
'style.*.css': { size: 1065 },
100+
'style.*.css.map': { size: 2345 },
100101
}
101102
}
102103
};
@@ -107,7 +108,6 @@ export const sassPrerendered = `
107108
<h1>Header on background</h1>
108109
<p>Paragraph on background</p>
109110
</div>
110-
<script src="/bundle.js" defer="defer"></script>
111111
{{ ... }}
112112
</body>
113113
`;
@@ -123,7 +123,7 @@ export const withCustomTemplate = `
123123
<body>
124124
<h1>Guess what</h1>
125125
<h2>This is an app with custom template</h2>
126-
<script src="/bundle.js" defer="defer"></script>
126+
{{ ... }}
127127
</body>
128128
</html>
129129
`;
@@ -133,7 +133,6 @@ export const multiplePrerenderingHome = `
133133
<div id="app">
134134
<div>Home</div>
135135
</div>
136-
<script defer="defer" src="/bundle.js"></script>
137136
{{ ... }}
138137
</body>
139138
`;
@@ -143,7 +142,6 @@ export const multiplePrerenderingRoute = `
143142
<div id="app">
144143
<div>Route66</div>
145144
</div>
146-
<script defer="defer" src="/bundle.js"></script>
147145
{{ ... }}
148146
</body>
149147
`;

tests/lib/filesMatchSnapshot.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { join } from 'path';
2+
import { Minimatch } from 'minimatch';
23

34
const minimumSizeDifference = 10;
45
const percentageThreshold = 0.05;
@@ -8,6 +9,7 @@ export default (actual, expected) => {
89
let normalizedExpected = normalize(expected);
910
let normalizedActual = normalize(actual);
1011

12+
let expectedPaths = Object.keys(normalizedExpected).map(p => new Minimatch(p));
1113
let expectedBoundaries = Object.keys(normalizedExpected)
1214
.reduce((acc, path) => Object.assign(acc, {
1315
[path]: {
@@ -17,11 +19,20 @@ export default (actual, expected) => {
1719
}), {});
1820

1921
let comparisonResult = Object.keys(normalizedActual).reduce((acc, path) => {
20-
let expectedValues = expectedBoundaries[path];
22+
let expectedFilePaths = expectedPaths.filter(p => p.match(path)).map(p => p.pattern);
23+
24+
if (expectedFilePaths > 1) {
25+
throw new Error(`Invalid snapshot configuration!
26+
Found duplicate matches for path: ${path}.
27+
Mathes: ${expectedFilePaths.join(',')}
28+
`);
29+
}
30+
let expectedPath = expectedFilePaths[0];
31+
let expectedValues = expectedBoundaries[expectedPath];
2132
let actualValue = normalizedActual[path].size;
2233

2334
if (expectedValues && expectedValues.min <= actualValue && actualValue <= expectedValues.max) {
24-
return Object.assign(acc, { [path]: expectedValues });
35+
return Object.assign(acc, { [expectedPath]: expectedValues });
2536
}
2637

2738
return Object.assign(acc, { [path]: actualValue });
@@ -37,15 +48,7 @@ const boundary = (direction, val) => {
3748
return Math.max(minimumFileSize, val + rounded);
3849
};
3950

40-
const normalize = obj => {
41-
let flat = flatten(obj, o => Object.keys(o).length === 1 && typeof o.size === 'number');
42-
43-
return Object.keys(flat).reduce((agg, key) => {
44-
let newKey = key.replace(/\.chunk\.\w+\./, '.chunk.*.');
45-
agg[newKey] = flat[key];
46-
return agg;
47-
}, {});
48-
};
51+
const normalize = obj => flatten(obj, o => Object.keys(o).length === 1 && typeof o.size === 'number');
4952

5053
const flatten = (obj, stop, path = '') => Object.keys(obj)
5154
.reduce((agg, key) => {

tests/serve.snapshot.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export const homePageHTML = `
1414
<p>This is the Home component.</p>
1515
</div>
1616
</div>
17-
<script src="/bundle.js" defer="defer"></script>
1817
{{ ... }}
1918
</body>
2019
`;
@@ -37,7 +36,6 @@ export const profilePageHtml = `
3736
<p><button>Click Me</button> Clicked 10 times.</p>
3837
</div>
3938
</div>
40-
<script src="/bundle.js" defer="defer"></script>
4139
{{ ... }}
4240
</body>
4341
`;

0 commit comments

Comments
 (0)