-
Notifications
You must be signed in to change notification settings - Fork 59
/
webpack.config.ts
251 lines (219 loc) · 7.62 KB
/
webpack.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// tslint:disable:no-var-requires
// tslint:disable:no-console
import chalk from 'chalk';
import * as CompressionWebpackPlugin from 'compression-webpack-plugin';
import * as HTMLWebpackPlugin from 'html-webpack-plugin';
import * as path from 'path';
import * as webpack from 'webpack';
const { WebpackPluginServe: ServePlugin } = require('webpack-plugin-serve');
const { StatsWriterPlugin } = require('webpack-stats-plugin');
const FavIconWebpackPlugin = require('favicons-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
import { processEnv as env } from './env';
const packageJson: {
dependencies: { [libName: string]: string };
devDependencies: { [libName: string]: string };
} = require(require.resolve('./package.json'));
/** Current service name */
export const serviceName = process.env.SERVICE_NAME || 'not set';
/** Absolute path to webpack output folder */
export const dist = path.join(__dirname, 'dist');
/** Webpack public path. All emitted assets will have relative path to this path */
export const publicPath = `${env.BASE_URL}/assets/`;
/** True if we are in development mode */
export const isDev = env.NODE_ENV === 'development';
/** True if we are in production mode */
export const isProd = env.NODE_ENV === 'production';
/** CSS module class name pattern */
export const localIdentName = isDev ? '[local]_[fullhash:base64:3]' : '[fullhash:base64:6]';
/** Sourcemap configuration */
const devtool = isDev ? 'cheap-source-map' : undefined;
// Report current configuration
console.log(chalk.cyan('Exporting Webpack config with following configurations:'));
console.log(chalk.blue('Environment:'), chalk.green(env.NODE_ENV));
console.log(chalk.blue('Output directory:'), chalk.green(path.resolve(dist)));
console.log(chalk.blue('Public path:'), chalk.green(publicPath));
/** Common webpack resolve options */
export const resolve: webpack.ResolveOptions = {
/** Base directories that Webpack will look to resolve absolutely imported modules */
modules: ['src', 'node_modules'],
/** Extension that are allowed to be omitted from import statements */
extensions: ['.ts', '.tsx', '.js', '.jsx'],
/** "main" fields in package.json files to resolve a CommonJS module for */
mainFields: ['browser', 'module', 'main']
};
/** Get clean version of a version string of package.json entry for a package by
* extracting only alphanumerics, hyphen, and period. Note that this won't
* produce a valid URL for all possible NPM version strings, but should be fine
* on those that are absolute version references.
* Examples: '1', '1.0', '1.2.3', '1.2.3-alpha.0'
*/
export function absoluteVersion(version: string) {
return version.replace(/[^\d.\-a-z]/g, '');
}
/** CDN path in case we would use minified react and react-DOM */
const cdnReact = `https://unpkg.com/react@${absoluteVersion(
packageJson.devDependencies.react
)}/umd/react.production.min.js`;
const cdnReactDOM = `https://unpkg.com/react-dom@${absoluteVersion(
packageJson.devDependencies['react-dom']
)}/umd/react-dom.production.min.js`;
/** Minification options for HTMLWebpackPlugin */
export const htmlMinifyConfig: HTMLWebpackPlugin.MinifyOptions = {
minifyCSS: true,
minifyJS: false,
removeComments: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true
};
/** Adds sourcemap support */
export const sourceMapRule: webpack.RuleSetRule = {
test: /\.js$/,
enforce: 'pre',
use: ['source-map-loader']
};
/** Rule for images, icons and fonts */
export const imageAndFontsRule: webpack.RuleSetRule = {
test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
type: 'asset/resource'
};
/** Generates HTML file that includes webpack assets */
export const htmlPlugin = new HTMLWebpackPlugin({
template: './src/assets/index.html',
inject: 'body',
minify: isProd ? htmlMinifyConfig : false,
hash: false,
showErrors: isDev
});
export const favIconPlugin = new FavIconWebpackPlugin({
logo: path.resolve(__dirname, 'src/assets/favicon.png'), //'./src/assets/favicon.png', - narusina
prefix: '[fullhash:8]/'
});
/** Write client stats to a JSON file for production */
export const statsWriterPlugin = new StatsWriterPlugin({
filename: 'client-stats.json',
fields: ['chunks', 'publicPath', 'assets', 'assetsByChunkName', 'assetsByChunkId']
});
/** Gzip assets */
export const compressionPlugin = new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240,
minRatio: 0.8
});
/** Define "process.env" in client app. Only provide things that can be public */
export const getDefinePlugin = (isServer: boolean) =>
new webpack.DefinePlugin({
'process.env': isServer
? 'process.env'
: Object.keys(env).reduce(
(result, key: string) => ({
...result,
[key]: JSON.stringify((env as any)[key])
}),
{}
),
__isServer: isServer
});
/** Enables Webpack HMR */
export const hmrPlugin = new webpack.HotModuleReplacementPlugin();
/** Limit server chunks to be only one. No need to split code in server */
export const limitChunksPlugin = new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
});
const typescriptRule = {
test: /\.tsx?$/,
exclude: /node_modules/,
use: ['babel-loader', { loader: 'ts-loader', options: { transpileOnly: true } }]
};
/**
* Client configuration
*
* Client is compiled into multiple chunks that are result to dynamic imports.
*/
export const clientConfig: webpack.Configuration = {
devtool,
resolve,
name: 'client',
target: 'web',
get entry() {
const entry = ['babel-polyfill', './src/client'];
if (isDev) {
return ['webpack-hot-middleware/client', ...entry];
}
return entry;
},
mode: isDev ? 'development' : 'production',
module: {
rules: [sourceMapRule, typescriptRule, imageAndFontsRule]
},
optimization: {
emitOnErrors: isProd,
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
enforce: true,
name: 'vendor',
priority: 10,
test: /[\\/]node_modules/
}
}
}
},
output: {
publicPath,
path: dist,
filename: '[name]-[fullhash:8].js',
chunkFilename: '[name]-[chunkhash].chunk.js',
crossOriginLoading: 'anonymous'
},
get plugins() {
const plugins: webpack.WebpackPluginInstance[] = [
htmlPlugin,
new ForkTsCheckerWebpackPlugin(),
favIconPlugin,
statsWriterPlugin,
getDefinePlugin(false),
new ServePlugin({
middleware: (app, builtins) =>
app.use(async (ctx, next) => {
ctx.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
await next();
}),
port: 7777
})
];
// Apply production specific configs
if (isProd) {
plugins.push(compressionPlugin);
}
return plugins;
}
};
/**
* Server configuration
*
* Server bundle is compiled as a CommonJS package that exports an Express middleware
*/
export const serverConfig: webpack.Configuration = {
resolve,
name: 'server',
target: 'node',
mode: isDev ? 'development' : 'production',
devtool: isProd ? devtool : undefined,
entry: ['babel-polyfill', './src/server'],
module: {
rules: [sourceMapRule, typescriptRule, imageAndFontsRule]
},
externals: [nodeExternals({ allowlist: /lyft/ })],
output: {
path: dist,
filename: 'server.js',
libraryTarget: 'commonjs2'
// assetModuleFilename: `${publicPath}/[fullhash:8].[ext]`
},
plugins: [limitChunksPlugin, new ForkTsCheckerWebpackPlugin(), getDefinePlugin(true)]
};
export default [clientConfig, serverConfig];