1 | import * as FriendlyErrorsWebpackPlugin from "friendly-errors-webpack-plugin";
|
2 | import * as CopyWebpackPlugin from "copy-webpack-plugin";
|
3 | import * as OptimizeCssAssetsPlugin from "optimize-css-assets-webpack-plugin";
|
4 | import * as SpeedMeasurePlugin from "speed-measure-webpack-plugin";
|
5 | import * as CleanWebpackPlugin from "clean-webpack-plugin";
|
6 | import * as HtmlWebpackPlugin from "html-webpack-plugin";
|
7 | import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
|
8 | import { join, resolve } from "path";
|
9 | import * as webpack from "webpack";
|
10 | import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
11 | import * as webpackMerge from "webpack-merge";
|
12 | import * as nodeExternals from "webpack-node-externals";
|
13 | import * as WebpackBar from "webpackbar";
|
14 | import * as TerserPlugin from "terser-webpack-plugin";
|
15 | import {
|
16 | getFile,
|
17 | getPaths,
|
18 | isBackEndConfig,
|
19 | makeDevEntries,
|
20 | resolveContext,
|
21 | resolveHtmlTemplate,
|
22 | resolveLocal,
|
23 | rootPath
|
24 | } from "./utils";
|
25 |
|
26 | export interface IUserConfig {
|
27 | rules;
|
28 | plugins;
|
29 | mode: string;
|
30 | }
|
31 |
|
32 | interface IConfig {
|
33 | devServer: any;
|
34 | }
|
35 |
|
36 | const configuration = (
|
37 | env = "development",
|
38 | userConfig,
|
39 | isDebugging = false,
|
40 | port?
|
41 | ): Partial<webpack.Configuration & IConfig> => {
|
42 | const isDev = env !== "production";
|
43 | const context = rootPath;
|
44 | const paths = getPaths(userConfig.paths);
|
45 |
|
46 | const allConfigs: Partial<IUserConfig> = {
|
47 | rules: {
|
48 | typescript: {
|
49 | test: /\.tsx?$/,
|
50 | use: [
|
51 | {
|
52 | loader: require.resolve("ts-loader"),
|
53 | options: {
|
54 | context: rootPath,
|
55 | transpileOnly: true,
|
56 | experimentalWatchApi: true
|
57 | }
|
58 | }
|
59 | ],
|
60 | include: paths.src,
|
61 | exclude: /node_modules/
|
62 | },
|
63 | babel: {
|
64 | test: /\.jsx?$/,
|
65 | use: [
|
66 | {
|
67 | loader: require.resolve("babel-loader")
|
68 | }
|
69 | ],
|
70 | include: paths.src,
|
71 | exclude: /node_modules/
|
72 | },
|
73 | html: {
|
74 | test: /\.(html)$/,
|
75 | use: { loader: require.resolve("html-loader") },
|
76 | include: paths.src,
|
77 | exclude: /node_modules/
|
78 | },
|
79 | scss: {
|
80 | test: /\.(sa|sc|c)ss$/,
|
81 | use: [
|
82 | isDev ? require.resolve("style-loader") : MiniCssExtractPlugin.loader,
|
83 | {
|
84 | loader: require.resolve("css-loader"),
|
85 | options: { sourceMap: !isDev, importLoaders: 1 }
|
86 | },
|
87 | {
|
88 | loader: require.resolve("postcss-loader"),
|
89 | options: { sourceMap: !isDev, plugins: [require("autoprefixer")] }
|
90 | },
|
91 | {
|
92 | loader: require.resolve("sass-loader"),
|
93 | options: { sourceMap: !isDev }
|
94 | }
|
95 | ]
|
96 | },
|
97 | images: {
|
98 | test: /\.(png|svg|jpg|gif)$/,
|
99 | loader: require.resolve("url-loader"),
|
100 | options: { limit: 10000, name: "images/[name].[hash:7].[ext]" }
|
101 | },
|
102 | fonts: {
|
103 | test: /\.(woff|woff2|eot|ttf|otf)$/,
|
104 | loader: require.resolve("url-loader"),
|
105 | options: { limit: 10000, name: "fonts/[name].[hash:7].[ext]" }
|
106 | },
|
107 | media: {
|
108 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
109 | loader: require.resolve("url-loader"),
|
110 | options: { limit: 10000, name: "media/[name].[hash:7].[ext]" }
|
111 | }
|
112 | },
|
113 | plugins: {
|
114 | miniCssExtract: new MiniCssExtractPlugin({
|
115 | filename: isDev ? "[name].css" : "css/[name].[contenthash].css",
|
116 | chunkFilename: isDev ? "[id].css" : "css/[id].[chunkhash].css"
|
117 | }),
|
118 | friendlyErrors: new FriendlyErrorsWebpackPlugin(),
|
119 | define: new webpack.DefinePlugin({
|
120 | "process.env": isDev
|
121 | ? getFile("./env.development.ts")
|
122 | : getFile("./env.production.ts")
|
123 | }),
|
124 | html: new HtmlWebpackPlugin({
|
125 | filename: "index.html",
|
126 | template: resolveHtmlTemplate(),
|
127 | inject: true,
|
128 | minify: isDev
|
129 | ? false
|
130 | : {
|
131 | removeComments: true,
|
132 | collapseWhitespace: true,
|
133 | removeAttributeQuotes: true
|
134 | }
|
135 | }),
|
136 | clean: new CleanWebpackPlugin(["dist"], { root: rootPath }),
|
137 | hmr: new webpack.HotModuleReplacementPlugin(),
|
138 | webpackbar: new WebpackBar({
|
139 | name: "TypePack"
|
140 | }),
|
141 | analyze: new BundleAnalyzerPlugin(),
|
142 | copyStatic: new CopyWebpackPlugin([
|
143 | {
|
144 | from: join(rootPath, "static"),
|
145 | to: paths.outputFolder,
|
146 | ignore: [".*"]
|
147 | }
|
148 | ])
|
149 | }
|
150 | };
|
151 | const { rules, plugins }: IUserConfig = {
|
152 | ...allConfigs,
|
153 | ...userConfig
|
154 | };
|
155 |
|
156 | const commonConfig = {
|
157 | context,
|
158 | mode: isDev ? "development" : "production",
|
159 | devtool: isDev ? "cheap-module-eval-source-map" : "source-map",
|
160 | entry:
|
161 | isDev && !isBackEndConfig(userConfig.mode)
|
162 | ? makeDevEntries(paths.entry, port)
|
163 | : paths.entry,
|
164 | optimization: isDev
|
165 | ? {}
|
166 | : {
|
167 | removeAvailableModules: false,
|
168 | removeEmptyChunks: false,
|
169 | minimizer: [
|
170 | new TerserPlugin({
|
171 | cache: true,
|
172 | parallel: true,
|
173 | sourceMap: true
|
174 | }),
|
175 | new OptimizeCssAssetsPlugin({
|
176 | cssProcessor: require("cssnano"),
|
177 | cssProcessorPluginOptions: {
|
178 | preset: [
|
179 | "default",
|
180 | {
|
181 | discardComments: {
|
182 | removeAll: true
|
183 | }
|
184 | }
|
185 | ]
|
186 | },
|
187 | canPrint: true
|
188 | })
|
189 | ],
|
190 | splitChunks: {
|
191 | chunks: "all"
|
192 | }
|
193 | },
|
194 | resolve: {
|
195 | symlinks: false,
|
196 | extensions: [".tsx", ".ts", ".js", ".jsx", ".json"],
|
197 | modules: [
|
198 | "node_modules",
|
199 | resolveContext(context, "node_modules"),
|
200 | resolveLocal("node_modules")
|
201 | ],
|
202 | alias: {
|
203 | "@": paths.src
|
204 | }
|
205 | },
|
206 | resolveLoader: {
|
207 | modules: [
|
208 | "node_modules",
|
209 | resolveContext(context, "node_modules"),
|
210 | resolveLocal("node_modules")
|
211 | ]
|
212 | },
|
213 | module: {
|
214 | rules: [rules.typescript, rules.babel]
|
215 | },
|
216 | plugins: [
|
217 | plugins.friendlyErrors,
|
218 | plugins.webpackbar,
|
219 | plugins.clean,
|
220 | plugins.define,
|
221 | ...(process.env.BUNDLE_ANALYZE ? [plugins.analyze] : [])
|
222 | ],
|
223 | output: {
|
224 | publicPath: "/",
|
225 | filename: isDev ? "js/[name].js" : "js/[name].[chunkhash].js",
|
226 | chunkFilename: isDev ? "js/[id].js" : "js/[id].[chunkhash].js",
|
227 | path: paths.outputFolder,
|
228 | libraryTarget: "umd",
|
229 | pathinfo: false
|
230 | }
|
231 | };
|
232 |
|
233 | const webAppConfig = webpackMerge(commonConfig, {
|
234 | module: {
|
235 | rules: [rules.html, rules.scss, rules.images, rules.fonts, rules.media]
|
236 | },
|
237 | plugins: [
|
238 | plugins.html,
|
239 | ...(isDev ? [plugins.hmr] : [plugins.miniCssExtract])
|
240 | ]
|
241 | });
|
242 |
|
243 | const backEndConfig = webpackMerge(commonConfig, {
|
244 | target: "node",
|
245 | externals: [nodeExternals()],
|
246 | output: {
|
247 | filename: "index.js",
|
248 | path: resolve("./dist"),
|
249 | libraryTarget: "commonjs2"
|
250 | }
|
251 | });
|
252 |
|
253 | let resultConfig: webpack.Configuration = webAppConfig;
|
254 |
|
255 | if (isBackEndConfig(userConfig.mode)) {
|
256 | resultConfig = backEndConfig;
|
257 | }
|
258 |
|
259 | if (typeof userConfig.config === "function") {
|
260 | userConfig.config(resultConfig);
|
261 | }
|
262 |
|
263 | if (isDebugging) {
|
264 | console.log(JSON.stringify(resultConfig));
|
265 | }
|
266 |
|
267 | const smp = new SpeedMeasurePlugin({
|
268 | disable: !process.env.SMP
|
269 | });
|
270 | return smp.wrap(resultConfig);
|
271 | };
|
272 |
|
273 | export default configuration;
|