@isxiaoyuan/elpis 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc +52 -0
  3. package/.vscode/settings.json +8 -0
  4. package/REMADE.md +228 -0
  5. package/app/controller/base.js +43 -0
  6. package/app/controller/project.js +106 -0
  7. package/app/controller/view.js +21 -0
  8. package/app/extend/logger.js +36 -0
  9. package/app/middleware/api-params-verify.js +73 -0
  10. package/app/middleware/api-sign-verify.js +43 -0
  11. package/app/middleware/error-handler.js +33 -0
  12. package/app/middleware/project-handle.js +26 -0
  13. package/app/middleware.js +41 -0
  14. package/app/pages/asserts/custom.css +11 -0
  15. package/app/pages/boot.js +43 -0
  16. package/app/pages/common/curl.js +88 -0
  17. package/app/pages/common/utils.js +2 -0
  18. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +19 -0
  19. package/app/pages/dashboard/complex-view/header-view/header-view.vue +138 -0
  20. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +51 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +41 -0
  22. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +128 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +20 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +95 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +98 -0
  26. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +123 -0
  27. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +129 -0
  28. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +98 -0
  29. package/app/pages/dashboard/complex-view/silder-view/silder-view.vue +127 -0
  30. package/app/pages/dashboard/dashboard.vue +96 -0
  31. package/app/pages/dashboard/entry.dashboard.js +48 -0
  32. package/app/pages/store/index.js +5 -0
  33. package/app/pages/store/menu.js +70 -0
  34. package/app/pages/store/project.js +17 -0
  35. package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
  36. package/app/pages/widgets/header-container/asserts/logo.png +0 -0
  37. package/app/pages/widgets/header-container/header-container.vue +102 -0
  38. package/app/pages/widgets/schema-form/complex-view/input/input.vue +137 -0
  39. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +136 -0
  40. package/app/pages/widgets/schema-form/complex-view/select/select.vue +122 -0
  41. package/app/pages/widgets/schema-form/form-item-config.js +20 -0
  42. package/app/pages/widgets/schema-form/schema-form.vue +136 -0
  43. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +55 -0
  44. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +69 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +45 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +53 -0
  47. package/app/pages/widgets/schema-search-bar/schema-item-config.js +24 -0
  48. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +115 -0
  49. package/app/pages/widgets/schema-table/schema-table.vue +255 -0
  50. package/app/pages/widgets/sider-container/complex-view/sub-menu/sub-menu.vue +18 -0
  51. package/app/pages/widgets/sider-container/sider-container.vue +29 -0
  52. package/app/public/dist/entry.dashboard.tpl +27 -0
  53. package/app/public/dist/entry.page1.tpl +25 -0
  54. package/app/public/dist/entry.page2.tpl +25 -0
  55. package/app/public/dist/entry.project-list.tpl +27 -0
  56. package/app/public/static/logo.png +0 -0
  57. package/app/public/static/normalize.css +239 -0
  58. package/app/router/project.js +12 -0
  59. package/app/router/view.js +7 -0
  60. package/app/router-schema/project.js +30 -0
  61. package/app/service/base.js +13 -0
  62. package/app/service/project.js +58 -0
  63. package/app/view/entry.tpl +27 -0
  64. package/app/webpack/config/webpack.base.js +305 -0
  65. package/app/webpack/config/webpack.dev.js +62 -0
  66. package/app/webpack/config/webpack.prod.js +120 -0
  67. package/app/webpack/dev.js +62 -0
  68. package/app/webpack/libs/blank.js +1 -0
  69. package/app/webpack/prod.js +22 -0
  70. package/config/config.beta.js +3 -0
  71. package/config/config.default.js +3 -0
  72. package/config/config.prod.js +3 -0
  73. package/elpis-core/env.js +20 -0
  74. package/elpis-core/index.js +97 -0
  75. package/elpis-core/loader/config.js +66 -0
  76. package/elpis-core/loader/controller.js +86 -0
  77. package/elpis-core/loader/extend.js +66 -0
  78. package/elpis-core/loader/middleware.js +74 -0
  79. package/elpis-core/loader/router-schema.js +56 -0
  80. package/elpis-core/loader/router.js +49 -0
  81. package/elpis-core/loader/service.js +82 -0
  82. package/index.js +42 -0
  83. package/model/index.js +109 -0
  84. package/package.json +92 -0
  85. package/test/controller/project.test.js +188 -0
@@ -0,0 +1,30 @@
1
+ module.exports = {
2
+ "/api/project": {
3
+ get: {
4
+ query: {
5
+ type: "object",
6
+ properties: {
7
+ proj_key: {
8
+ type: "string",
9
+ },
10
+ },
11
+ required: ["proj_key"],
12
+ },
13
+ },
14
+ },
15
+ "/api/project/list": {
16
+ get: {
17
+ query: {
18
+ type: "object",
19
+ properties: {
20
+ proj_key: {
21
+ type: "string",
22
+ },
23
+ },
24
+ },
25
+ },
26
+ },
27
+ "/api/project/model_list": {
28
+ get: {},
29
+ },
30
+ };
@@ -0,0 +1,13 @@
1
+ const superagent = require("superagent");
2
+ module.exports = (app) =>
3
+ /**
4
+ * service 基础服务类
5
+ * 统一收拢所有 service 方法
6
+ */
7
+ class BaseService {
8
+ constructor() {
9
+ this.app = app;
10
+ this.config = app.config;
11
+ this.curl = superagent;
12
+ }
13
+ };
@@ -0,0 +1,58 @@
1
+ module.exports = (app) => {
2
+ const BaseService = require("./base")(app);
3
+ const modelList = require("../../model/index.js")(app);
4
+ return class ProjectService extends BaseService {
5
+ /**
6
+ * 根据 proj_Key 获取项目配置
7
+ * @returns {Array} 项目列表
8
+ */
9
+ get(projKey) {
10
+ let projConfig;
11
+ modelList.forEach((modelItem) => {
12
+ const { project } = modelItem;
13
+ if (project[projKey]) {
14
+ projConfig = project[projKey];
15
+ }
16
+ });
17
+
18
+ return projConfig;
19
+ }
20
+
21
+ /**
22
+ * 获取统一模型下的项目列表(如果无 projKey, 全量获取)
23
+ * @returns {Array} 项目列表
24
+ */
25
+
26
+ getList({ projKey }) {
27
+ return modelList.reduce((preList, modelItem) => {
28
+ const { project } = modelItem;
29
+
30
+ //如果有传 projKey 则只去当前同模型下的项目, 不传的情况下则取全量
31
+ if (projKey && !project[projKey]) return preList;
32
+
33
+ for (const pKey in project) {
34
+ preList.push(project[pKey]);
35
+ }
36
+ return preList;
37
+ }, []);
38
+ // modelList.forEach((modelItem) => {
39
+ // const { project } = modelItem;
40
+
41
+ // //如果有传 projKey 则只去当前同模型下的项目, 不传的情况下则取全量
42
+ // if (projKey && !project[projKey]) return;
43
+
44
+ // for (const pKey in project) {
45
+ // projectList.push(project[pKey]);
46
+ // }
47
+ // });
48
+ // return projectList;
49
+ }
50
+ /**
51
+ * 获取所有项目与项目的结构化数据
52
+ * @returns {Promise<Array>} 项目列表
53
+ */
54
+ async getModelList() {
55
+ return modelList;
56
+ }
57
+ };
58
+ };
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html>
2
+ <html class="dark">
3
+
4
+ <head>
5
+ <link href="/static/normalize.css" rel="stylesheet">
6
+ <link href="/static/logo.png" rel="icon" type="image/x-icon">
7
+ <title>{{name}}</title>
8
+ </head>
9
+
10
+ <body style="margin: 0;">
11
+ <div id="root"></div>
12
+ <input id="projKey" name="projKey" value="{{projKey}}" style="display:none;">
13
+ <input id="env" name="env" value="{{env}}" style="display:none;">
14
+ <input id="options" name="options" value="{{options}}" style="display:none;">
15
+ </body>
16
+ <script type="text/javascript">
17
+ try {
18
+ window.projKey = document.getElementById("projKey").value;
19
+ window.env = document.getElementById("env").value;
20
+ const options = document.getElementById("options").value;
21
+ window.options = JSON.parse(options);
22
+ } catch (e) {
23
+ console.log(e);
24
+ }
25
+ </script>
26
+
27
+ </html>
@@ -0,0 +1,305 @@
1
+ const glob = require("glob");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+ const webpack = require("webpack");
5
+ const merge = require("webpack-merge");
6
+ const HtmlWebpackPlugin = require("html-webpack-plugin");
7
+ const { VueLoaderPlugin } = require("vue-loader");
8
+
9
+ // 动态构造入口文件 和 页面模板
10
+ const elpisPageEntries = {};
11
+ const elpisHtmlWebpackPluginList = [];
12
+
13
+ // 获取 elpis/app/pages 目录下的所有 入口 文件 entry.xx.js
14
+ const elpisEntryList = path.resolve(__dirname, "../../pages/**/entry.*.js");
15
+
16
+ glob.sync(elpisEntryList).forEach((file) => {
17
+ handleFile(file, elpisPageEntries, elpisHtmlWebpackPluginList);
18
+ });
19
+
20
+ // 构造业务页面入口文件
21
+ const businessPageEntries = {};
22
+ const businessHtmlWebpackPluginList = [];
23
+
24
+ // 获取业务代码目录下的所有 入口 文件 entry.xx.js
25
+ const businessEntryList = path.resolve(
26
+ process.cwd(),
27
+ "./app/pages/**/entry.*.js",
28
+ );
29
+
30
+ glob.sync(businessEntryList).forEach((file) => {
31
+ handleFile(file, businessPageEntries, businessHtmlWebpackPluginList);
32
+ });
33
+
34
+ // 构造相关 webpack 处理数据结构
35
+ function handleFile(file, entries = {}, htmlWebpackPluginList = []) {
36
+ // basename 去掉路径和后缀
37
+ const entryName = path.basename(file, ".js");
38
+ // 构造 entry
39
+ entries[entryName] = file;
40
+
41
+ // 构造最终渲染的页面文件
42
+ htmlWebpackPluginList.push(
43
+ new HtmlWebpackPlugin({
44
+ //产物(最终模板) 输出路径
45
+ filename: path.resolve(
46
+ process.cwd(),
47
+ "./app/public/dist",
48
+ `${entryName}.tpl`,
49
+ ),
50
+ // 指定要使用的模板文件
51
+ template: path.resolve(__dirname, "../../view/entry.tpl"),
52
+
53
+ // 指定要引入的代码块(chunk)
54
+ chunks: [entryName],
55
+ }),
56
+ );
57
+ }
58
+
59
+ // 加载 业务 webpack 配置
60
+ let businessWebpackConfig = {};
61
+ try {
62
+ businessWebpackConfig = require(`${process.cwd()}/app/webpack.config.js`);
63
+ } catch (e) {}
64
+
65
+ /**
66
+ * webpack 基础配置
67
+ */
68
+
69
+ module.exports = merge.smart(
70
+ {
71
+ //入口配置
72
+ // entry: {
73
+ // "entry.page1": "./app/pages/page1/entry.page1.js",
74
+ // "entry.page2": "./app/pages/page2/entry.page2.js",
75
+ // },
76
+ entry: Object.assign({}, elpisPageEntries, businessPageEntries),
77
+
78
+ //模块解析配置(决定了要加载解析那些模块,以及用什么方式去解释)
79
+ module: {
80
+ rules: [
81
+ {
82
+ test: /\.vue$/,
83
+ loader: require.resolve("vue-loader"),
84
+ },
85
+ {
86
+ test: /\.js?$/,
87
+ //只对业务代码进行babel,加快webpack打包速度
88
+ include: [
89
+ path.resolve(__dirname, "../../pages"),
90
+ path.resolve(process.cwd(), "./app/pages"), // 业务目录处理
91
+ ],
92
+ loader: require.resolve("babel-loader"),
93
+ },
94
+ {
95
+ test: /\.(png|jpe?g|gif)(\?.*)?$/,
96
+ use: {
97
+ loader: require.resolve("url-loader"),
98
+ options: {
99
+ limit: 300,
100
+ esModule: false,
101
+ },
102
+ },
103
+ },
104
+ {
105
+ test: /\.css$/,
106
+ use: [require.resolve("style-loader"), require.resolve("css-loader")],
107
+ },
108
+ {
109
+ test: /\.less$/,
110
+ use: [
111
+ require.resolve("style-loader"),
112
+ require.resolve("css-loader"),
113
+ require.resolve("less-loader"),
114
+ ],
115
+ },
116
+ {
117
+ test: /\.(ttf|svg|otf|eot|woff2?)(\?.*)?$/,
118
+ loader: require.resolve("file-loader"),
119
+ },
120
+ ],
121
+ },
122
+ //输出路径 , 因为开发和生产环境输出不一致,所以在各自环境自行配置
123
+ output: {
124
+ filename: "js/[name]_[chunkhash:8].bundle.js",
125
+ path: path.resolve(process.cwd(), "./app/public/dist/prod"),
126
+ publicPath: "/dist/prod", //资源的路径
127
+ crossOriginLoading: "anonymous",
128
+ },
129
+ //配置模块解析的具体行为(定义 webpack 在打包时,如何找到并解析具体模块的路径)
130
+ resolve: {
131
+ extensions: [".js", ".vue", ".less", ".css"],
132
+ alias: (() => {
133
+ const aliasMap = {};
134
+ const blankModulePath = path.resolve(__dirname, "../libs/blank.js");
135
+
136
+ //dashboard 路由扩展配置
137
+ const businessDashboardRouterConfig = path.resolve(
138
+ process.cwd(),
139
+ "./app/pages/dashboard/router.js",
140
+ );
141
+ aliasMap["$businessDashboardRouterConfig"] = fs.existsSync(
142
+ businessDashboardRouterConfig,
143
+ )
144
+ ? businessDashboardRouterConfig
145
+ : blankModulePath;
146
+
147
+ // schema-view component 扩展配置
148
+ const businessComponentConfig = path.resolve(
149
+ process.cwd(),
150
+ "./app/pages/dashboard/complex-view/schema-view/components/component-config.js",
151
+ );
152
+ aliasMap["$businessComponentConfig"] = fs.existsSync(
153
+ businessComponentConfig,
154
+ )
155
+ ? businessComponentConfig
156
+ : blankModulePath;
157
+
158
+ // schema-form component 扩展配置
159
+ const businessFormItemConfig = path.resolve(
160
+ process.cwd(),
161
+ "./app/pages/widgets/schema-form/form-item-config.js",
162
+ );
163
+ aliasMap["$businessFormItemConfig"] = fs.existsSync(
164
+ businessFormItemConfig,
165
+ )
166
+ ? businessFormItemConfig
167
+ : blankModulePath;
168
+
169
+ // schema-search-bar component 扩展配置
170
+ const businessSearchItemConfig = path.resolve(
171
+ process.cwd(),
172
+ "./app/pages/widgets/schema-search-bar/schema-item-config.js",
173
+ );
174
+ aliasMap["$businessSearchItemConfig"] = fs.existsSync(
175
+ businessSearchItemConfig,
176
+ )
177
+ ? businessSearchItemConfig
178
+ : blankModulePath;
179
+
180
+ return {
181
+ vue: require.resolve("vue"),
182
+ "@babel/runtime/helpers/asyncToGenerator":
183
+ require.resolve("@babel/runtime/helpers/asyncToGenerator"),
184
+ "@babel/runtime/regenerator":
185
+ require.resolve("@babel/runtime/regenerator"),
186
+ "@babel/runtime/helpers/toConsumableArray":
187
+ require.resolve("@babel/runtime/helpers/toConsumableArray"),
188
+ $elpisPages: path.resolve(__dirname, "../../pages"),
189
+ $elpisCommon: path.resolve(__dirname, "../../pages/common"),
190
+ $elpisCurl: path.resolve(__dirname, "../../pages/common/curl.js"),
191
+ $elpisUtils: path.resolve(__dirname, "../../pages/common/utils.js"),
192
+ $elpisWidgets: path.resolve(__dirname, "../../pages/widgets"),
193
+ $elpisHeaderContainer: path.resolve(
194
+ __dirname,
195
+ "../../pages/widgets/header-container/header-container.vue",
196
+ ),
197
+ $elpisSiderContainer: path.resolve(
198
+ __dirname,
199
+ "../../pages/widgets/sider-container/sider-container.vue",
200
+ ),
201
+ $elpisSchemaTable: path.resolve(
202
+ __dirname,
203
+ "../../pages/widgets/schema-table/schema-table.vue",
204
+ ),
205
+ $elpisSchemaForm: path.resolve(
206
+ __dirname,
207
+ "../../pages/widgets/schema-form/schema-form.vue",
208
+ ),
209
+ $elpisSchemaSearchBar: path.resolve(
210
+ __dirname,
211
+ "../../pages/widgets/schema-search-bar/schema-search-bar.vue",
212
+ ),
213
+ $elpisStore: path.resolve(__dirname, "../../pages/store"),
214
+ $elpisBoot: path.resolve(__dirname, "../../pages/boot.js"),
215
+ ...aliasMap,
216
+ };
217
+ })(),
218
+ },
219
+ // 配置 webpack 插件
220
+ plugins: [
221
+ // 处理.vue文件 这个插件是必须的
222
+ // 职能是将你定义的其他规则复制并应用到.vue文件中
223
+ // 例如,如果有一条匹配规则 /\.js$/ 的规则, 它会被复制到.vue文件<script>板块中
224
+ new VueLoaderPlugin(),
225
+ // 把第三方库暴露到window context 下
226
+ new webpack.ProvidePlugin({
227
+ Vue: "vue",
228
+ axios: "axios",
229
+ _: "lodash",
230
+ }),
231
+ // 定义全局常量
232
+ new webpack.DefinePlugin({
233
+ __VUE_OPTIONS_API__: "true", // 开启 Vue 选项 API(options Api)
234
+ __VUE_PROD_DEVTOOLS__: "false", // 关闭 Vue 生产环境下的 devtools
235
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false", // 关闭 Vue 生产环境下的 hydration mismatch details
236
+ }),
237
+ // 构造最终渲染的页面模板
238
+ ...elpisHtmlWebpackPluginList,
239
+ ...businessHtmlWebpackPluginList,
240
+ // new HtmlWebpackPlugin({
241
+ // //产物(最终模板) 输出路径
242
+ // filename: path.resolve(
243
+ // process.cwd(),
244
+ // "./app/public/dist",
245
+ // "entry.page1.tpl",
246
+ // ),
247
+ // // 指定要使用的模板文件
248
+ // template: path.resolve(process.cwd(), "./app/view/entry.tpl"),
249
+ // // 指定要引入的代码块(chunk)
250
+ // chunks: ["entry.page1"],
251
+ // }),
252
+ // new HtmlWebpackPlugin({
253
+ // //产物(最终模板) 输出路径
254
+ // filename: path.resolve(
255
+ // process.cwd(),
256
+ // "./app/public/dist",
257
+ // "entry.page2.tpl",
258
+ // ),
259
+ // // 指定要使用的模板文件
260
+ // template: path.resolve(process.cwd(), "./app/view/entry.tpl"),
261
+ // // 指定要引入的代码块(chunk)
262
+ // chunks: ["entry.page2"],
263
+ // }),
264
+ ],
265
+ // 配置打包输出优化 (代码分割,模块合并,缓存,treeShaking,压缩等优化策略)
266
+ optimization: {
267
+ /**
268
+ * 把 js 文件打包成3种类型
269
+ * 1. vendor: 第三方 lib 库, 基本不会改动,触发依赖版本升级
270
+ * 2. common: 业务组件代码的公共部分抽取出来,改动较少
271
+ * 3. entry.{page}: 不同页面 entry 里的业务组件代码的差异部分,会经常改动
272
+ * 目的: 把改动和引入频率不一样的 js 区分出来,以达到更好利用浏览器缓存的效果
273
+ */
274
+ splitChunks: {
275
+ chunks: "all", // 对同步和异步模块都进行分割
276
+ maxAsyncRequests: 10, // 最大的异步加载并行请求数量
277
+ maxInitialRequests: 10, // 入口点的最大并行请求数量
278
+ cacheGroups: {
279
+ // 第三方库代码分割
280
+ vendor: {
281
+ test: /[\\/]node_modules[\\/]/,
282
+ name: "vendor", //模块名称
283
+ priority: 20, // 优先级,值越大,越优先被选中
284
+ enforce: true, // 是否强制执行
285
+ reuseExistingChunk: true, // 重用已存在的代码块,而不是创建新的
286
+ // chunks: "all", // 对所有代码块都生效
287
+ },
288
+ // 公共业务组件代码分割
289
+ common: {
290
+ test: /[\\/]common|widgets[\\/]/,
291
+ name: "common",
292
+ minChunks: 2, // 被引用次数大于等于2次,才会被提取出来
293
+ minSize: 1, // 模块大小大于等于1字节,才会被提取出来(最小分割文件大小 )
294
+ priority: 10, // 优先级,值越大,越优先被选中
295
+ reuseExistingChunk: true, // 重用已存在的代码块,而不是创建新的
296
+ // chunks: "all",
297
+ },
298
+ },
299
+ },
300
+ // 将webpack 运行时生成的代码打包到runtime.js 中
301
+ runtimeChunk: true,
302
+ },
303
+ },
304
+ businessWebpackConfig,
305
+ );
@@ -0,0 +1,62 @@
1
+ const merge = require("webpack-merge");
2
+ const path = require("path");
3
+ const webpack = require("webpack");
4
+
5
+ // 基类配置
6
+ const baseConfig = require("./webpack.base.js");
7
+
8
+ // dev-serve 配置
9
+ const DEV_SERVER_CONFIG = {
10
+ HOST: "127.0.0.2",
11
+ PORT: 9002,
12
+ HMR_PATH: "__webpack_hmr",
13
+ TIMEOUT: 20000,
14
+ };
15
+
16
+ // 开发阶段的 entry 配置需要加入 hmr
17
+ Object.keys(baseConfig.entry).forEach((key) => {
18
+ // 第三方包不作为 hmr 入口
19
+ if (key !== "vendor") {
20
+ baseConfig.entry[key] = [
21
+ // 主入口文件
22
+ baseConfig.entry[key],
23
+ // hmr更新入口,官方指定的 hmr
24
+ `${require.resolve("webpack-hot-middleware/client")}?path=http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/${DEV_SERVER_CONFIG.HMR_PATH}&timeout=${DEV_SERVER_CONFIG.TIMEOUT}&reload=true`,
25
+ ];
26
+ }
27
+ });
28
+
29
+ // 生产环境 webpack 配置
30
+ const webpackConfig = merge(baseConfig, {
31
+ mode: "development",
32
+ // 呈现代码开发工具 便于调试
33
+ devtool: "eval-cheap-module-source-map",
34
+ // 输出路径
35
+ output: {
36
+ filename: "js/[name]_[chunkhash:8].bundle.js",
37
+ path: path.resolve(process.cwd(), "./app/public/dist/dev/"), //输出文件存储路径
38
+ publicPath: `http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/public/dist/dev`, // 外部资源公共路径
39
+ globalObject: "this", //
40
+ // 强制使用绝对资源路径作为文件名,防止 eval 模式下生成 undefined
41
+ // devtoolModuleFilenameTemplate: (info) => {
42
+ // // 获取文件路径,如果不存在(就是那个导致 undefined 的元凶),就用 hash 给它造一个唯一名字
43
+ // const resPath = info.resourcePath || `generated-entry-${info.hash}`;
44
+ // return `webpack:///${resPath}`;
45
+ // },
46
+ },
47
+ //开发阶段插件
48
+ plugins: [
49
+ // 热更新插件 热替换
50
+ new webpack.HotModuleReplacementPlugin({
51
+ multiStep: false, // 单步更新,webpack 一次性完成「检测变更→准备更新→应用更新」全流程
52
+ }),
53
+ // 无刷新更新插件
54
+ // new webpack.NoEmitOnErrorsPlugin(),
55
+ ],
56
+ });
57
+
58
+ module.exports = {
59
+ webpackConfig,
60
+ // devServer 给dev.js
61
+ DEV_SERVER_CONFIG,
62
+ };
@@ -0,0 +1,120 @@
1
+ const path = require("path");
2
+ const merge = require("webpack-merge");
3
+ const os = require("os"); //多线程
4
+ const HappyPack = require("happypack");
5
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
6
+ const CleanWebpackPlugin = require("clean-webpack-plugin");
7
+ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
8
+ const HtmlWebpackInjectAttributesPlugin = require("html-webpack-inject-attributes-plugin");
9
+ const TerserPlugin = require("terser-webpack-plugin");
10
+
11
+ // 多线程 build 设置
12
+ const happyPackCommonConfig = {
13
+ debug: false, // 开启调试模式
14
+ // loaders: ["babel-loader"],
15
+ threadPool: HappyPack.ThreadPool({ size: os.cpus().length }), // cpu 核心数
16
+ };
17
+
18
+ // 基类配置
19
+ const baseConfig = require("./webpack.base.js");
20
+
21
+ // 生产环境 webpack 配置
22
+ const webpackConfig = merge(baseConfig, {
23
+ mode: "production",
24
+
25
+ // 输出路径
26
+ output: {
27
+ filename: "js/[name]_[chunkhash:8].bundle.js",
28
+ path: path.resolve(process.cwd(), "./app/public/dist/prod/"),
29
+ publicPath: "/dist/prod/", //资源的路径
30
+ crossOriginLoading: "anonymous",
31
+ },
32
+ module: {
33
+ rules: [
34
+ {
35
+ test: /\.css$/,
36
+ use: [
37
+ MiniCssExtractPlugin.loader,
38
+ `${require.resolve("css-loader")}?id=css`,
39
+ ],
40
+ },
41
+ {
42
+ test: /\.js$/,
43
+ include: [
44
+ path.resolve(__dirname, "../../pages"),
45
+ path.resolve(process.cwd(), "./app/pages"), // 业务目录处理
46
+ ],
47
+ use: {
48
+ loader: `${require.resolve("happypack/loader")}?id=js`,
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ // webpack 不会有大量 hints 信息,默认为 warning
54
+ performance: {
55
+ hints: false,
56
+ },
57
+ plugins: [
58
+ // 每次 build 前,清空 public/dist 目录
59
+ new CleanWebpackPlugin(["public/dist"], {
60
+ root: path.resolve(process.cwd(), "./app/"),
61
+ exclude: [],
62
+ verbose: true, // 输出日志
63
+ dry: false, // 实际删除
64
+ }),
65
+ // 提取 css 的公告部分,有效利用缓存,(非公共部分使用 inline)
66
+ new MiniCssExtractPlugin({
67
+ filename: "css/[name]_[chunkhash:8].bundle.css",
68
+ }),
69
+ // 优化并压缩css 资源
70
+ new CssMinimizerPlugin(),
71
+ // 多线程打包js,加快打包速度
72
+ new HappyPack({
73
+ id: "js",
74
+ ...happyPackCommonConfig,
75
+ loaders: [
76
+ `${require.resolve("babel-loader")}?${JSON.stringify({
77
+ presets: [require.resolve("@babel/preset-env")],
78
+ plugins: [require.resolve("@babel/plugin-transform-runtime")],
79
+ })}`,
80
+ ],
81
+ }),
82
+ // 多线程打包css,加快打包速度
83
+ new HappyPack({
84
+ id: "css",
85
+ ...happyPackCommonConfig,
86
+ loaders: [
87
+ {
88
+ path: require.resolve("css-loader"),
89
+ options: {
90
+ importLoaders: 1,
91
+ // sourceMap: true,
92
+ },
93
+ },
94
+ ],
95
+ }),
96
+ // 浏览器在请求资源时不发生用户的身份凭证
97
+ new HtmlWebpackInjectAttributesPlugin({
98
+ crossorigin: "anonymous", // 浏览器在请求资源时不发生用户的身份凭证
99
+ }),
100
+ ],
101
+ optimization: {
102
+ // 使用terserPlugin 的并发和缓存,提升压缩阶段的性能
103
+ // 清除console.log
104
+ minimize: true,
105
+ minimizer: [
106
+ new TerserPlugin({
107
+ parallel: true, // 利用多核 cpu 的优势来加快压缩速度
108
+ // 排除第三方 vendor 产物,避免老版本 terser 解析 node_modules 里的新语法
109
+ // exclude: /js\/vendor_.*\.bundle\.js$/,
110
+ terserOptions: {
111
+ compress: {
112
+ drop_console: true, // 清除console.log
113
+ },
114
+ },
115
+ }),
116
+ ],
117
+ },
118
+ });
119
+
120
+ module.exports = webpackConfig;
@@ -0,0 +1,62 @@
1
+ // 本地开发启动 devServer
2
+ const express = require("express");
3
+ const path = require("path");
4
+ const consoler = require("consoler");
5
+ const webpack = require("webpack");
6
+ const devMiddleware = require("webpack-dev-middleware");
7
+ const hotMiddleware = require("webpack-hot-middleware");
8
+
9
+ //提供给外部调用
10
+ module.exports = () => {
11
+ // 从 webpack.dev.js 配置文件中获取 webpack devServer 配置
12
+ const {
13
+ webpackConfig,
14
+ DEV_SERVER_CONFIG,
15
+ } = require("./config/webpack.dev.js");
16
+
17
+ const app = express();
18
+
19
+ const compiler = webpack(webpackConfig);
20
+
21
+ // 指定静态文件目录
22
+ app.use(express.static(path.join(__dirname, "../public/dist")));
23
+
24
+ // 引入 divMiddleware 中间件 (监控文件改动)
25
+ app.use(
26
+ devMiddleware(compiler, {
27
+ // 落地文件
28
+ writeToDisk: (filePath) => filePath.endsWith(".tpl"), //忽略tpl
29
+ // 资源路径
30
+ publicPath: webpackConfig.output.publicPath,
31
+
32
+ // headers 配置
33
+ headers: {
34
+ "Access-Control-Allow-Origin": "*",
35
+ "Access-Control-Allow-Methods":
36
+ "GET, POST, PUT, DELETE, PATCH, OPTIONS",
37
+ "Access-Control-Allow-Headers":
38
+ "X-Requested-With, content-type, Authorization",
39
+ },
40
+
41
+ stats: {
42
+ colors: true,
43
+ },
44
+ }),
45
+ );
46
+
47
+ // 引用 hotMiddleware 中间件 (热更新)
48
+ app.use(
49
+ hotMiddleware(compiler, {
50
+ path: `/${DEV_SERVER_CONFIG.HMR_PATH}`,
51
+ log: () => {},
52
+ }),
53
+ );
54
+
55
+ consoler.info("请等待webpack初次构建完成提示...");
56
+
57
+ const port = DEV_SERVER_CONFIG.PORT || 3000;
58
+
59
+ app.listen(port, () => {
60
+ console.log(`devServer 启动成功,监听端口 ${port}`);
61
+ });
62
+ };
@@ -0,0 +1 @@
1
+ module.exports = {};