Skip to content

Commit daa6551

Browse files
authored
Service Worker file size reduction (Part 2) (#414)
* Separate build configs for SW (ES6) and SDK (ES5). * more descriptive names for tsconfig files
1 parent e530384 commit daa6551

File tree

7 files changed

+366
-4
lines changed

7 files changed

+366
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ node_modules/
33
dist/
44
build/ts-to-es6
55
build/bundles
6+
build/releases
67
build/size-analysis.html
78
/config.json
89
.vscode/

build/config/sdk.config.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
const webpack = require('webpack');
2+
const path = require('path');
3+
const ExtractTextPlugin = require('extract-text-webpack-plugin');
4+
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
5+
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
6+
const { CheckerPlugin } = require('awesome-typescript-loader');
7+
const dir = require('node-dir');
8+
const md5file = require('md5-file');
9+
const crypto = require('crypto');
10+
11+
const env = process.env.ENV || "production";
12+
const isProdBuild = process.env.ENV === "production";
13+
const nodeEnv = isProdBuild ? "production" : "development";
14+
15+
async function getStylesheetsHash() {
16+
const styleSheetsPath = "src/stylesheets";
17+
18+
return await new Promise((resolve, reject) => {
19+
dir.files(styleSheetsPath, async (err, files) => {
20+
if (err) throw err;
21+
const filteredFiles = files.filter(filePath => {
22+
console.log("CSS Stylesheet:", filePath);
23+
const fileName = path.basename(filePath);
24+
if (fileName.endsWith(".scss")) {
25+
// Only hash SCSS source files
26+
return true;
27+
}
28+
});
29+
if (filteredFiles.length === 0) {
30+
reject(
31+
`No .scss files were found in ${styleSheetsPath}, but SCSS files were expected. SCSS stylesheets in this directory are MD5 hashed and added as a build-time variable so loading the stylesheet from the global CDN always loads the correct version.`
32+
);
33+
}
34+
let hashes = [];
35+
for (let styleSheetPath of filteredFiles) {
36+
const hash = md5file.sync(styleSheetPath);
37+
hashes.push(hash);
38+
}
39+
// Strangely enough, the order is inconsistent so we have to sort the hashes
40+
hashes = hashes.sort();
41+
const joinedHashesStr = hashes.join("-");
42+
const combinedHash = crypto.createHash("md5").update(joinedHashesStr).digest("hex");
43+
console.log(`MD5 hash of SCSS source files in ${styleSheetsPath} is ${combinedHash}.`);
44+
resolve(combinedHash);
45+
});
46+
});
47+
}
48+
49+
async function getWebpackPlugins() {
50+
const plugins = [
51+
new CheckerPlugin(),
52+
new webpack.optimize.ModuleConcatenationPlugin(),
53+
new ExtractTextPlugin("OneSignalSDKStyles.css"),
54+
new webpack.DefinePlugin({
55+
__DEV__: env === "development",
56+
__TEST__: !!process.env.TESTS,
57+
__STAGING__: env === "staging",
58+
__VERSION__: process.env.npm_package_config_sdkVersion,
59+
__LOGGING__: env === "development",
60+
__SRC_STYLESHEETS_MD5_HASH__: JSON.stringify(await getStylesheetsHash()),
61+
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
62+
})
63+
];
64+
if (!!process.env.ANALYZE) {
65+
const sizeAnalysisReportPath = path.resolve(path.join('build', 'size-analysis.html'));
66+
plugins.push(
67+
new BundleAnalyzerPlugin({
68+
// Can be `server`, `static` or `disabled`.
69+
// In `server` mode analyzer will start HTTP server to show bundle report.
70+
// In `static` mode single HTML file with bundle report will be generated.
71+
// In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
72+
analyzerMode: 'static',
73+
// Path to bundle report file that will be generated in `static` mode.
74+
// Relative to bundles output directory.
75+
reportFilename: sizeAnalysisReportPath,
76+
// Module sizes to show in report by default.
77+
// Should be one of `stat`, `parsed` or `gzip`.
78+
// See "Definitions" section for more information.
79+
defaultSizes: 'gzip',
80+
// Automatically open report in default browser
81+
openAnalyzer: false,
82+
// If `true`, Webpack Stats JSON file will be generated in bundles output directory
83+
generateStatsFile: false,
84+
// Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
85+
// Relative to bundles output directory.
86+
statsFilename: 'stats.json',
87+
// Options for `stats.toJson()` method.
88+
// For example you can exclude sources of your modules from stats file with `source: false` option.
89+
// See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
90+
statsOptions: null,
91+
// Log level. Can be 'info', 'warn', 'error' or 'silent'.
92+
logLevel: 'info'
93+
})
94+
);
95+
}
96+
return plugins;
97+
}
98+
99+
async function generateWebpackConfig() {
100+
return {
101+
target: 'web',
102+
entry: {
103+
'OneSignalSDK.js': path.resolve('build/ts-to-es6/src/entries/sdk.js'),
104+
'OneSignalSDKStyles.css': path.resolve('src/entries/stylesheet.scss'),
105+
},
106+
output: {
107+
path: path.resolve('build/bundles'),
108+
filename: '[name]'
109+
},
110+
mode: process.env.ENV === "production" ? "production" : "development",
111+
optimization: {
112+
minimizer: [
113+
new UglifyJsPlugin({
114+
sourceMap: true,
115+
uglifyOptions: {
116+
sourceMap: true,
117+
compress: {
118+
drop_console: false,
119+
drop_debugger: false,
120+
warnings: false,
121+
},
122+
mangle: process.env.ENV === 'production' ? {
123+
reserved: [
124+
'AlreadySubscribedError',
125+
'InvalidArgumentError',
126+
'InvalidStateError',
127+
'NotSubscribedError',
128+
'PermissionMessageDismissedError',
129+
'PushNotSupportedError',
130+
'PushPermissionNotGrantedError',
131+
'SdkInitError',
132+
'TimeoutError'
133+
]
134+
} : false,
135+
output: {
136+
comments: false
137+
}
138+
}
139+
})
140+
]
141+
},
142+
module: {
143+
rules: [
144+
{
145+
test: /\.js$/,
146+
exclude: /node_modules/,
147+
use: [
148+
{
149+
loader: 'awesome-typescript-loader',
150+
options: {
151+
configFileName: "build/config/tsconfig.es5.json"
152+
}
153+
},
154+
]
155+
},
156+
{
157+
test: /\.scss$/,
158+
use: ExtractTextPlugin.extract({
159+
use: [
160+
{
161+
loader: 'css-loader',
162+
options: {
163+
sourceMap: true,
164+
minimize: true
165+
}
166+
},
167+
{
168+
loader: 'postcss-loader',
169+
options: {
170+
plugins: function() {
171+
return [require('autoprefixer')];
172+
}
173+
}
174+
},
175+
'sass-loader'
176+
]
177+
})
178+
}
179+
]
180+
},
181+
resolve: {
182+
extensions: ['.js', '.ts'],
183+
modules: [
184+
'build/ts-to-es6',
185+
'build/ts-to-es6/src',
186+
'node_modules'
187+
]
188+
},
189+
devtool: 'source-map',
190+
plugins: await getWebpackPlugins(),
191+
}
192+
}
193+
194+
module.exports = generateWebpackConfig();
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const webpack = require('webpack');
2+
const path = require('path');
3+
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
4+
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
5+
const { CheckerPlugin } = require('awesome-typescript-loader');
6+
7+
const env = process.env.ENV || "production";
8+
const isProdBuild = process.env.ENV === "production";
9+
const nodeEnv = isProdBuild ? "production" : "development";
10+
11+
async function getWebpackPlugins() {
12+
const plugins = [
13+
new CheckerPlugin(),
14+
new webpack.optimize.ModuleConcatenationPlugin(),
15+
new webpack.DefinePlugin({
16+
__DEV__: env === "development",
17+
__TEST__: !!process.env.TESTS,
18+
__STAGING__: env === "staging",
19+
__VERSION__: process.env.npm_package_config_sdkVersion,
20+
__LOGGING__: env === "development",
21+
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
22+
})
23+
];
24+
if (!!process.env.ANALYZE) {
25+
const sizeAnalysisReportPath = path.resolve(path.join('build', 'size-analysis.html'));
26+
plugins.push(
27+
new BundleAnalyzerPlugin({
28+
// Can be `server`, `static` or `disabled`.
29+
// In `server` mode analyzer will start HTTP server to show bundle report.
30+
// In `static` mode single HTML file with bundle report will be generated.
31+
// In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
32+
analyzerMode: 'static',
33+
// Path to bundle report file that will be generated in `static` mode.
34+
// Relative to bundles output directory.
35+
reportFilename: sizeAnalysisReportPath,
36+
// Module sizes to show in report by default.
37+
// Should be one of `stat`, `parsed` or `gzip`.
38+
// See "Definitions" section for more information.
39+
defaultSizes: 'gzip',
40+
// Automatically open report in default browser
41+
openAnalyzer: false,
42+
// If `true`, Webpack Stats JSON file will be generated in bundles output directory
43+
generateStatsFile: false,
44+
// Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
45+
// Relative to bundles output directory.
46+
statsFilename: 'stats.json',
47+
// Options for `stats.toJson()` method.
48+
// For example you can exclude sources of your modules from stats file with `source: false` option.
49+
// See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
50+
statsOptions: null,
51+
// Log level. Can be 'info', 'warn', 'error' or 'silent'.
52+
logLevel: 'info'
53+
})
54+
);
55+
}
56+
return plugins;
57+
}
58+
59+
async function generateWebpackConfig() {
60+
return {
61+
target: "web",
62+
entry: {
63+
"OneSignalSDKWorker.js": path.resolve("build/ts-to-es6/src/entries/worker.js"),
64+
},
65+
output: {
66+
path: path.resolve("build/bundles"),
67+
filename: "[name]"
68+
},
69+
mode: isProdBuild ? "production" : "development",
70+
optimization: {
71+
minimizer: [
72+
new UglifyJsPlugin({
73+
sourceMap: true,
74+
uglifyOptions: {
75+
sourceMap: true,
76+
compress: {
77+
drop_console: false,
78+
drop_debugger: false,
79+
warnings: false,
80+
},
81+
mangle: isProdBuild ? {
82+
reserved: [
83+
"AlreadySubscribedError",
84+
"InvalidArgumentError",
85+
"InvalidStateError",
86+
"NotSubscribedError",
87+
"PermissionMessageDismissedError",
88+
"PushNotSupportedError",
89+
"PushPermissionNotGrantedError",
90+
"SdkInitError",
91+
"TimeoutError"
92+
]
93+
} : false,
94+
output: {
95+
comments: false
96+
}
97+
}
98+
})
99+
]
100+
},
101+
module: {
102+
rules: [
103+
{
104+
test: /\.js$/,
105+
exclude: /node_modules/,
106+
use: [
107+
{
108+
loader: "awesome-typescript-loader",
109+
options: {
110+
configFileName: "build/config/tsconfig.es6.json"
111+
}
112+
},
113+
]
114+
}
115+
]
116+
},
117+
resolve: {
118+
extensions: [".js", ".ts"],
119+
modules: [
120+
"build/ts-to-es6",
121+
"build/ts-to-es6/src",
122+
"node_modules"
123+
]
124+
},
125+
devtool: "source-map",
126+
plugins: await getWebpackPlugins(),
127+
}
128+
}
129+
130+
module.exports = generateWebpackConfig();
File renamed without changes.

build/config/tsconfig.es6.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"compilerOptions": {
3+
"allowJs": true,
4+
"checkJs": false,
5+
"diagnostics": false,
6+
"forceConsistentCasingInFileNames": true,
7+
"importHelpers": false,
8+
"module": "ES6",
9+
"moduleResolution": "node",
10+
"noEmitHelpers": false,
11+
"pretty": true,
12+
"skipDefaultLibCheck": true,
13+
"skipLibCheck": true,
14+
"sourceMap": true,
15+
"outDir": "../bundles",
16+
"sourceRoot": "../..",
17+
"rootDir": "../..",
18+
"baseUrl": "./",
19+
"esModuleInterop": true,
20+
"types": [
21+
"node"
22+
],
23+
"lib": [
24+
"es6",
25+
"dom",
26+
"dom.iterable",
27+
"scripthost"
28+
],
29+
"target": "ES6",
30+
"mapRoot": "build/ts-to-es6"
31+
}
32+
}
33+

build/config/webpack.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ async function generateWebpackConfig() {
118118
{
119119
loader: 'awesome-typescript-loader',
120120
options: {
121-
configFileName: "build/config/tsconfig.transform.json"
121+
configFileName: "build/config/tsconfig.es5.json"
122122
}
123123
},
124124
]

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818
"transpile:sources": "$(yarn bin)/tsc --project build/config/tsconfig.json",
1919
"transpile:tests": "$(yarn bin)/tsc --project build/config/tsconfig.tests.json",
2020
"bundle": "TESTS=false $(yarn bin)/webpack --config build/config/webpack.config.js",
21+
"bundle-sw": "TESTS=false $(yarn bin)/webpack --config build/config/serviceworker.config.js",
22+
"bundle-sdk": "TESTS=false $(yarn bin)/webpack --config build/config/sdk.config.js",
2123
"bundle:analyze": "ANALYZE=true ENV=production TESTS=false $(yarn bin)/webpack --config build/config/webpack.config.js --profile --json > stats.json",
22-
"build:dev": "ENV=development yarn transpile:sources && ENV=development yarn bundle && ENV=development build/scripts/publish.sh",
23-
"build:staging": "ENV=staging yarn transpile:sources && ENV=staging yarn bundle && ENV=staging build/scripts/publish.sh",
24-
"build:prod": "ENV=production yarn transpile:sources && ENV=production yarn bundle && ENV=production build/scripts/publish.sh",
24+
"bundle-sw:analyze": "ANALYZE=true ENV=production TESTS=false $(yarn bin)/webpack --config build/config/serviceworker.config.js --profile --json > stats.json",
25+
"bundle-sdk:analyze": "ANALYZE=true ENV=production TESTS=false $(yarn bin)/webpack --config build/config/sdk.config.js --profile --json > stats.json",
26+
"build:dev": "ENV=development yarn transpile:sources && ENV=development yarn bundle-sw && ENV=development yarn bundle-sdk && ENV=development build/scripts/publish.sh",
27+
"build:staging": "ENV=staging yarn transpile:sources && ENV=staging yarn bundle-sw && ENV=staging yarn bundle-sdk && ENV=staging build/scripts/publish.sh",
28+
"build:prod": "ENV=production yarn transpile:sources && ENV=production yarn bundle-sw && ENV=production yarn bundle-sdk && ENV=production build/scripts/publish.sh",
2529
"test": "ENV=development yarn transpile:tests && ava --verbose --color --watch",
2630
"test:noWatch": "ENV=development yarn transpile:tests && ava --verbose --color --watch=false",
2731
"publish": "yarn clean && yarn build:prod && yarn "

0 commit comments

Comments
 (0)