1 | import browserSync from "browser-sync";
|
2 | import {
|
3 | concat,
|
4 | dropWhile,
|
5 | isObject,
|
6 | map,
|
7 | mapValues,
|
8 | reduce
|
9 | } from "lodash";
|
10 | import MemoryFS from "memory-fs";
|
11 | import path from "path";
|
12 | import webpack from "webpack";
|
13 | import webpackDevMiddleware from "webpack-dev-middleware";
|
14 | import webpackHotMiddleware from "webpack-hot-middleware";
|
15 |
|
16 | export const HMR_ENTRY = "webpack-hot-middleware/client";
|
17 |
|
18 | export interface IPlugin extends webpack.Plugin {
|
19 | new(): IPlugin;
|
20 | }
|
21 |
|
22 | export const getHmrPluginsByVersion = (): IPlugin[] => {
|
23 | const version = require("webpack/package.json").version;
|
24 | const majarVersion = String(version).split(".")[0];
|
25 |
|
26 | switch (majarVersion) {
|
27 | case "1":
|
28 | throw new Error("not support webpack@1");
|
29 | case "2":
|
30 | default:
|
31 | return [
|
32 | webpack.HotModuleReplacementPlugin
|
33 | ] as IPlugin[];
|
34 | }
|
35 | };
|
36 |
|
37 | const concatHMREntry = (entry: string): string[] => [HMR_ENTRY].concat(entry);
|
38 |
|
39 | const isOneOfPlugins = (PluginList: IPlugin[], plugin: webpack.Plugin) =>
|
40 | reduce(PluginList, (result, Plugin) => (result || (plugin instanceof Plugin)), false);
|
41 |
|
42 | export const patchEntryWithHMR = (entry: string | { [k: string]: string }): string[] | { [k: string]: string[] } => {
|
43 | if (isObject(entry)) {
|
44 | return mapValues(entry as { [k: string]: string }, concatHMREntry);
|
45 | }
|
46 | return concatHMREntry(entry as string);
|
47 | };
|
48 |
|
49 | export const patchPlugins = (plugins: IPlugin[]) => {
|
50 | const hmrPlugins = getHmrPluginsByVersion();
|
51 | const cleanedPlugins = dropWhile(plugins, (plugin) => isOneOfPlugins(hmrPlugins, plugin));
|
52 | return concat(cleanedPlugins, map(hmrPlugins, (Plugin: IPlugin) => new Plugin()));
|
53 | };
|
54 |
|
55 | export const patchWebConfigWithHMR = (webpackConfig: webpack.Configuration): webpack.Configuration => ({
|
56 | ...webpackConfig,
|
57 | entry: patchEntryWithHMR(webpackConfig.entry as string),
|
58 | plugins: patchPlugins(webpackConfig.plugins as IPlugin[])
|
59 | });
|
60 |
|
61 | export const createMiddlewaresForWebpack = (
|
62 | webpackConfig: webpack.Configuration,
|
63 | index: string,
|
64 | hot: boolean = false
|
65 | ) => {
|
66 |
|
67 | const patchedWebpackConfig = hot
|
68 | ? patchWebConfigWithHMR(webpackConfig)
|
69 | : webpackConfig;
|
70 |
|
71 | const bundler = webpack(patchedWebpackConfig);
|
72 |
|
73 | const fs = new MemoryFS();
|
74 |
|
75 | bundler.outputFileSystem = fs;
|
76 |
|
77 | const devMiddleware = webpackDevMiddleware(bundler, {
|
78 | publicPath: (patchedWebpackConfig.output || {}).publicPath!,
|
79 |
|
80 | stats: patchedWebpackConfig.stats || {
|
81 | colors: true,
|
82 | reasons: false,
|
83 | hash: false,
|
84 | version: false,
|
85 | timings: true,
|
86 | chunks: false,
|
87 | chunkModules: false,
|
88 | cached: false
|
89 | }
|
90 | });
|
91 |
|
92 | const devServerMiddlewares = [
|
93 | devMiddleware,
|
94 | ((req, res, next) => {
|
95 | if (req.method === "GET" && req.url === "/") {
|
96 | devMiddleware.waitUntilValid(() => {
|
97 | const indexFile = path.join(webpackConfig.output!.path!, index);
|
98 | res.end(fs.readFileSync(indexFile));
|
99 | });
|
100 | } else {
|
101 | next();
|
102 | }
|
103 | }) as browserSync.MiddlewareHandler
|
104 | ];
|
105 |
|
106 | if (hot) {
|
107 | return [
|
108 | ...devServerMiddlewares,
|
109 | webpackHotMiddleware(bundler)
|
110 | ];
|
111 | }
|
112 |
|
113 | return devServerMiddlewares;
|
114 | };
|