@yukihong/schema-admin-x 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.
- package/.eslintignore +3 -0
- package/.eslintrc +55 -0
- package/README.md +208 -0
- package/app/controller/base.js +39 -0
- package/app/controller/project.js +80 -0
- package/app/controller/view.js +20 -0
- package/app/extend/logger.js +39 -0
- package/app/middleware/api-params-verify.js +79 -0
- package/app/middleware/api-sign-verify.js +34 -0
- package/app/middleware/error-handler.js +34 -0
- package/app/middleware/project-handler.js +27 -0
- package/app/middleware.js +40 -0
- package/app/pages/asserts/custom.css +13 -0
- package/app/pages/boot.js +53 -0
- package/app/pages/common/curl.js +91 -0
- package/app/pages/common/utils.js +3 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +21 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +127 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +44 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +41 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +131 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +23 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +100 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +106 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +131 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +126 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +102 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +26 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +128 -0
- package/app/pages/dashboard/dashboard.vue +99 -0
- package/app/pages/dashboard/entry.dashboard.js +44 -0
- package/app/pages/store/index.js +4 -0
- package/app/pages/store/menu.js +61 -0
- package/app/pages/store/project.js +17 -0
- package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
- package/app/pages/widgets/header-container/asserts/logo.png +0 -0
- package/app/pages/widgets/header-container/header-container.vue +109 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +146 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +140 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +121 -0
- package/app/pages/widgets/schema-form/form-item-config.js +23 -0
- package/app/pages/widgets/schema-form/schema-form.vue +131 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +50 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +65 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +44 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +51 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +127 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +248 -0
- package/app/pages/widgets/sider-container/sider-container.vue +28 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +239 -0
- package/app/router/project.js +7 -0
- package/app/router/view.js +10 -0
- package/app/router-schema/project.js +30 -0
- package/app/service/base.js +13 -0
- package/app/service/project.js +50 -0
- package/app/view/entry.tpl +25 -0
- package/app/webpack/config/webpack.base.js +213 -0
- package/app/webpack/config/webpack.dev.js +61 -0
- package/app/webpack/config/webpack.prod.js +124 -0
- package/app/webpack/dev.js +63 -0
- package/app/webpack/libs/blank.js +1 -0
- package/app/webpack/prod.js +19 -0
- package/config/config.beta.js +3 -0
- package/config/config.default.js +3 -0
- package/config/config.prod.js +3 -0
- package/elpis-core/env.js +20 -0
- package/elpis-core/index.js +99 -0
- package/elpis-core/loader/config.js +52 -0
- package/elpis-core/loader/controller.js +75 -0
- package/elpis-core/loader/extend.js +55 -0
- package/elpis-core/loader/middleware.js +64 -0
- package/elpis-core/loader/router-schema.js +47 -0
- package/elpis-core/loader/router.js +59 -0
- package/elpis-core/loader/service.js +75 -0
- package/index.js +40 -0
- package/model/index.js +102 -0
- package/package.json +93 -0
- package/test/controller/project.test.js +217 -0
|
@@ -0,0 +1,213 @@
|
|
|
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 { VueLoaderPlugin } = require('vue-loader');
|
|
7
|
+
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
8
|
+
|
|
9
|
+
const babelRuntimePath = path.dirname(
|
|
10
|
+
require.resolve('@babel/runtime/package.json')
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// 动态构造 elpisPageEntries elpisHtmlWebPackPluginList
|
|
14
|
+
const elpisPageEntries = {};
|
|
15
|
+
const elpisHtmlWebPackPluginList = [];
|
|
16
|
+
|
|
17
|
+
// 获取 elpis/app/pages 目录下所有入口文件 (entry.xx.js)
|
|
18
|
+
const elpisEntryList = path.resolve(__dirname, '../../pages/**/entry.*.js');
|
|
19
|
+
glob.sync(elpisEntryList).forEach(file => {
|
|
20
|
+
handleFile(file, elpisPageEntries, elpisHtmlWebPackPluginList);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 动态构造 elpisPageEntries elpisHtmlWebPackPluginList
|
|
24
|
+
const businessPageEntries = {};
|
|
25
|
+
const businessHtmlWebPackPluginList = [];
|
|
26
|
+
|
|
27
|
+
// 获取 业务/app/pages 目录下所有入口文件 (entry.xx.js)
|
|
28
|
+
const businessEntryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
|
|
29
|
+
glob.sync(businessEntryList).forEach(file => {
|
|
30
|
+
handleFile(file, businessPageEntries, businessHtmlWebPackPluginList);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// 构造相关 webpack 处理数据机构
|
|
35
|
+
function handleFile(file, entries = {}, htmlWebPackPluginList = []) {
|
|
36
|
+
const entryName = path.basename(file, '.js');
|
|
37
|
+
// 构造entry
|
|
38
|
+
entries[entryName] = file;
|
|
39
|
+
// 构造最终渲染的页面文件
|
|
40
|
+
htmlWebPackPluginList.push(
|
|
41
|
+
// html-webpack-plugin 辅助注入打包后的 bundle 文件到 tpl 文件中
|
|
42
|
+
new HtmlWebpackPlugin({
|
|
43
|
+
// 产物(最终模板)输出路径
|
|
44
|
+
filename: path.resolve(process.cwd(), './app/public/dist', `${entryName}.tpl`),
|
|
45
|
+
// 指定要使用的模板文件
|
|
46
|
+
template: path.resolve(__dirname, '../../view/entry.tpl'),
|
|
47
|
+
// 要注入的代码块
|
|
48
|
+
chunks: [entryName]
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 加载业务 webpack 配置
|
|
54
|
+
let buinessWebpackConfig = {};
|
|
55
|
+
try {
|
|
56
|
+
buinessWebpackConfig = require(`${process.cwd()}/app/webpack.config.js`);
|
|
57
|
+
} catch (error) {}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* webpack 基础配置
|
|
62
|
+
*/
|
|
63
|
+
module.exports = merge.smart({
|
|
64
|
+
// 入口配置
|
|
65
|
+
entry: Object.assign({}, elpisPageEntries, businessPageEntries),
|
|
66
|
+
// 模块解析配置(决定了要加载解析哪些模块,以及用什么方式去解析)
|
|
67
|
+
module: {
|
|
68
|
+
rules: [{
|
|
69
|
+
test: /\.vue$/,
|
|
70
|
+
use: {
|
|
71
|
+
loader: require.resolve('vue-loader')
|
|
72
|
+
}
|
|
73
|
+
}, {
|
|
74
|
+
test: /\.js$/,
|
|
75
|
+
include: [
|
|
76
|
+
// 对 elpis 代码进行 babel,加快 webpack 打包速度
|
|
77
|
+
path.resolve(__dirname, '../../pages'),
|
|
78
|
+
// 对 业务 代码进行 babel,加快 webpack 打包速度
|
|
79
|
+
path.resolve(process.cwd(), './app/pages')
|
|
80
|
+
],
|
|
81
|
+
use: {
|
|
82
|
+
loader: require.resolve('babel-loader')
|
|
83
|
+
}
|
|
84
|
+
},{
|
|
85
|
+
test: /\.(png|jpe?g|gif)(\?.+)?$/,
|
|
86
|
+
use: {
|
|
87
|
+
loader: require.resolve('url-loader'),
|
|
88
|
+
options: {
|
|
89
|
+
limit: 300,
|
|
90
|
+
esModule: false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},{
|
|
94
|
+
test: /\.css$/,
|
|
95
|
+
use: [
|
|
96
|
+
require.resolve('style-loader'),
|
|
97
|
+
require.resolve('css-loader')
|
|
98
|
+
]
|
|
99
|
+
},{
|
|
100
|
+
test: /\.less$/,
|
|
101
|
+
use: [
|
|
102
|
+
require.resolve('style-loader'),
|
|
103
|
+
require.resolve('css-loader'),
|
|
104
|
+
require.resolve('less-loader')
|
|
105
|
+
]
|
|
106
|
+
},{
|
|
107
|
+
test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
|
|
108
|
+
use: require.resolve('file-loader')
|
|
109
|
+
}]
|
|
110
|
+
},
|
|
111
|
+
// 产物输出路径,因为开发和生产环境输出不一致,所以在各自环境中配置
|
|
112
|
+
output: {},
|
|
113
|
+
// 配置模块解析的具体行为(定义 webpack 在打包时,如何找到并解析具体模块的路径)
|
|
114
|
+
resolve: {
|
|
115
|
+
extensions: ['.js', '.vue', '.less', '.css'],
|
|
116
|
+
alias:(() => {
|
|
117
|
+
const aliasMap = {};
|
|
118
|
+
const blankModulePath = path.resolve(__dirname, '../libs/blank.js');
|
|
119
|
+
|
|
120
|
+
// dashboard 路由扩展配置
|
|
121
|
+
const businessDashboardRouterConfig = path.resolve(process.cwd(), './app/pages/dashboard/router.js')
|
|
122
|
+
aliasMap['$businessDashboardRouterConfig'] = fs.existsSync(businessDashboardRouterConfig)
|
|
123
|
+
? businessDashboardRouterConfig
|
|
124
|
+
: blankModulePath;
|
|
125
|
+
|
|
126
|
+
// schema-view component 扩展配置
|
|
127
|
+
const businessComponentConfig = path.resolve(process.cwd(), './app/pages/dashboard/complex-view/schema-view/components/component-config.js');
|
|
128
|
+
aliasMap['$businessComponentConfig'] = fs.existsSync(businessComponentConfig) ? businessComponentConfig : blankModulePath;
|
|
129
|
+
|
|
130
|
+
// schema-form 扩展配置
|
|
131
|
+
const businessFormItemConfig = path.resolve(process.cwd(), './app/pages/widgets/schema-form/form-item-config.js');
|
|
132
|
+
aliasMap['$businessFormItemConfig'] = fs.existsSync(businessFormItemConfig) ? businessFormItemConfig : blankModulePath;
|
|
133
|
+
|
|
134
|
+
// schema-search-bar 扩展配置
|
|
135
|
+
const businessSearchItemConfig = path.resolve(process.cwd(), './app/pages/widgets/schema-search-bar/search-item-config.js');
|
|
136
|
+
aliasMap['$businessSearchItemConfig'] = fs.existsSync(businessSearchItemConfig) ? businessSearchItemConfig : blankModulePath;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
'vue': require.resolve('vue'),
|
|
140
|
+
'@babel/runtime': babelRuntimePath,
|
|
141
|
+
$elpisPages: path.resolve(__dirname, '../../pages'),
|
|
142
|
+
$elpisCommon: path.resolve(__dirname, '../../pages/common'),
|
|
143
|
+
$elpisCurl: path.resolve(__dirname, '../../pages/common/curl.js'),
|
|
144
|
+
$elpisUtils: path.resolve(__dirname, '../../pages/common/utils.js'),
|
|
145
|
+
$elpisWidgets: path.resolve(__dirname, '../../pages/widgets'),
|
|
146
|
+
$elpisHeaderContainer: path.resolve(__dirname, '../../pages/widgets/header-container/header-container.vue'),
|
|
147
|
+
$elpisSiderContainer: path.resolve(__dirname, '../../pages/widgets/sider-container/sider-container.vue'),
|
|
148
|
+
$elpisSchemaTable: path.resolve(__dirname, '../../pages/widgets/schema-table/schema-table.vue'),
|
|
149
|
+
$elpisSchemaForm: path.resolve(__dirname, '../../pages/widgets/schema-form/schema-form.vue'),
|
|
150
|
+
$elpisSchemaSearchBar: path.resolve(__dirname, '../../pages/widgets/schema-search-bar/schema-search-bar.vue'),
|
|
151
|
+
$elpisStore: path.resolve(__dirname, '../../pages/store'),
|
|
152
|
+
$elpisBoot: path.resolve(__dirname, '../../pages/boot.js'),
|
|
153
|
+
...aliasMap
|
|
154
|
+
}
|
|
155
|
+
})()
|
|
156
|
+
},
|
|
157
|
+
// 配置 webpack 插件
|
|
158
|
+
plugins: [
|
|
159
|
+
// 处理 .vue 文件,这个插件是必须的
|
|
160
|
+
// 它只是能将你定义过的其他规则赋值并应用到 .vue文件里。
|
|
161
|
+
// 例如,如果有一条匹配规则 /\.js$/ 的规则,那么它会应用到 .vue 文件中的 <script> 板块中
|
|
162
|
+
new VueLoaderPlugin(),
|
|
163
|
+
// 把第三方库暴露到 window context 下
|
|
164
|
+
new webpack.ProvidePlugin({
|
|
165
|
+
Vue: 'vue',
|
|
166
|
+
axios: 'axios',
|
|
167
|
+
_: 'lodash'
|
|
168
|
+
}),
|
|
169
|
+
// 定义全局常量
|
|
170
|
+
new webpack.DefinePlugin({
|
|
171
|
+
__VUE_OPTIONS_API__: 'true', // 支持 vue 解析 optionsApi
|
|
172
|
+
__VUE_PROD_DEVTOOLS: 'false', // 禁用 Vue 调试工具
|
|
173
|
+
__VUE_PROD_HYDRATION_MISMATCH_DETAILS: 'false', // 禁用生产环境显示“水合”信息
|
|
174
|
+
}),
|
|
175
|
+
|
|
176
|
+
// 构造最终渲染的页面模板
|
|
177
|
+
...elpisHtmlWebPackPluginList,
|
|
178
|
+
...businessHtmlWebPackPluginList
|
|
179
|
+
],
|
|
180
|
+
// 配置打包输出优化(配置代码分割、模块合并、缓存、TreeShaing、压缩等优化策略)
|
|
181
|
+
optimization: {
|
|
182
|
+
/**
|
|
183
|
+
* 把 js 文件打包成3种类型
|
|
184
|
+
* 1. vendor: 第三方 lib 库,基本不会改动,除非依赖版本升级
|
|
185
|
+
* 2. common:业务组件代码的公共部分抽取出来,改动较少
|
|
186
|
+
* 3. entry.{page}:不用页面 entry 里的业务组件代码的差异部分,会经常改动
|
|
187
|
+
* 目的:把改动和引用频率不一样的 js 区分出来,以达到更好利用浏览器缓存的效果
|
|
188
|
+
*/
|
|
189
|
+
splitChunks: {
|
|
190
|
+
chunks: 'all', // 对同步和异步模块都进行分割
|
|
191
|
+
maxAsyncRequests: 10, // 每次异步加载的最大并行请求数
|
|
192
|
+
maxInitialRequests: 10, // 入口点的最大并行请求数
|
|
193
|
+
cacheGroups: {
|
|
194
|
+
vendor: { // 第三方库依赖
|
|
195
|
+
test: /[\\/]node_modules[\\/]/, // 打包 node_module 中的文件
|
|
196
|
+
name: 'vendor', // 模块名称
|
|
197
|
+
priority: 20, // 优先级,数字越大,优先级越高
|
|
198
|
+
enforce: true, // 强制执行
|
|
199
|
+
reuseExistingChunk: true, // 复用已有的公共 chunk
|
|
200
|
+
},
|
|
201
|
+
common: {
|
|
202
|
+
test: /[\\/]common|widgets[\\/]/,
|
|
203
|
+
name: 'common', // 模块名称
|
|
204
|
+
minChunks: 2, // 被两处引用即被归为公共模块
|
|
205
|
+
minSize: 1, // 最小分割文件大小 (1 byte)
|
|
206
|
+
priority: 10, // 优先级,数字越大,优先级越高
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
// 将 webpack 运行时生成的代码打包到 runtime.js
|
|
211
|
+
runtimeChunk: true
|
|
212
|
+
}
|
|
213
|
+
}, buinessWebpackConfig);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const merge = require('webpack-merge');
|
|
3
|
+
const webpack = require('webpack');
|
|
4
|
+
|
|
5
|
+
// 基类配置
|
|
6
|
+
const baseConfig = require('./webpack.base.js');
|
|
7
|
+
|
|
8
|
+
// dev-server 配置
|
|
9
|
+
const DEV_SERVER_CONFIG = {
|
|
10
|
+
HOST: '127.0.0.1',
|
|
11
|
+
PORT: 9002,
|
|
12
|
+
HMR_PATH: '__webpack_hmr', // 官方规定
|
|
13
|
+
TIMEOUT: 20000,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const { HOST, PORT, HMR_PATH, TIMEOUT } = DEV_SERVER_CONFIG
|
|
17
|
+
|
|
18
|
+
// 开发阶段的 entry 配置需要加入 hmr
|
|
19
|
+
Object.keys(baseConfig.entry).forEach(v => {
|
|
20
|
+
// 第三方包不作为 hmr 入口
|
|
21
|
+
if( v !== 'vendor') {
|
|
22
|
+
baseConfig.entry[v] = [
|
|
23
|
+
// 主入口文件
|
|
24
|
+
baseConfig.entry[v],
|
|
25
|
+
// hmr 更新入口,官方指定的 hmr 路径
|
|
26
|
+
`${require.resolve(`webpack-hot-middleware/client`)}?path=http://${HOST}:${PORT}/${HMR_PATH}?timeout=${TIMEOUT}&reload=true`
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
// 开发环境 webpack 配置
|
|
33
|
+
const webpackConfig = merge.smart(baseConfig, {
|
|
34
|
+
// 指定开发环境模式
|
|
35
|
+
mode: 'development',
|
|
36
|
+
// source-map 开发工具,呈现代码的映射关系,便于再开发过程中调试代码
|
|
37
|
+
devtool: 'eval-cheap-module-source-map',
|
|
38
|
+
// 开发环境的 output 配置
|
|
39
|
+
output: {
|
|
40
|
+
filename: 'js/[name]_[chunkhash:8].bundle.js',
|
|
41
|
+
path: path.resolve(process.cwd(), './app/public/dist/dev/'), // 输出文件存储路径
|
|
42
|
+
publicPath: `http://${HOST}:${PORT}/public/dist/dev/`, // 外部资源公共路径
|
|
43
|
+
globalObject: 'this'
|
|
44
|
+
},
|
|
45
|
+
// 开发阶段插件
|
|
46
|
+
plugins: [
|
|
47
|
+
// HotModuleReplacementPlugin 用于实现热模块替换 简称 HMR
|
|
48
|
+
// 模块热替换允许在应用程序运行时替换模块
|
|
49
|
+
// 极大的提升开发效率,因为能让应用程序一直保持运行状态
|
|
50
|
+
new webpack.HotModuleReplacementPlugin({
|
|
51
|
+
multiStep: false
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
// webpack 配置
|
|
58
|
+
webpackConfig,
|
|
59
|
+
// devServer 配置,暴露给 dev.js 使用
|
|
60
|
+
DEV_SERVER_CONFIG
|
|
61
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const merge = require('webpack-merge');
|
|
3
|
+
const HappyPack = require('happypack');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const TerserPlugin = require('terser-webpack-plugin');
|
|
7
|
+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
8
|
+
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
|
9
|
+
const CSSMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
|
10
|
+
const HtmlWebpackInjectAttributesPlugin = require('html-webpack-inject-attributes-plugin');
|
|
11
|
+
|
|
12
|
+
// 多线程 build 设置
|
|
13
|
+
const happypackCommonConfig = {
|
|
14
|
+
debug: false,
|
|
15
|
+
threadPool: HappyPack.ThreadPool({ size: os.cpus().length })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 基类配置
|
|
19
|
+
const baseConfig = require('./webpack.base.js');
|
|
20
|
+
|
|
21
|
+
// 生产环境 webpack 配置
|
|
22
|
+
const webPackConfig = merge.smart(baseConfig, {
|
|
23
|
+
// 指定生产环境模式
|
|
24
|
+
mode: 'production',
|
|
25
|
+
// 生产环境的 output 配置
|
|
26
|
+
output: {
|
|
27
|
+
filename: 'js/[name]_[chunkhash:8].bundle.js',
|
|
28
|
+
path: path.join(process.cwd(), './app/public/dist/prod'),
|
|
29
|
+
publicPath: '/dist/prod/',
|
|
30
|
+
crossOriginLoading: 'anonymous'
|
|
31
|
+
},
|
|
32
|
+
module: {
|
|
33
|
+
rules: [{
|
|
34
|
+
test: /\.css$/,
|
|
35
|
+
use: [
|
|
36
|
+
MiniCssExtractPlugin.loader,
|
|
37
|
+
`${require.resolve(`happypack/loader`)}?id=css`
|
|
38
|
+
]
|
|
39
|
+
}, {
|
|
40
|
+
test: /\.js$/,
|
|
41
|
+
include: [
|
|
42
|
+
// 对 elpis 代码进行 babel,加快 webpack 打包速度
|
|
43
|
+
path.resolve(__dirname, '../../pages'),
|
|
44
|
+
// 对 业务 代码进行 babel,加快 webpack 打包速度
|
|
45
|
+
path.resolve(process.cwd(), './app/pages')
|
|
46
|
+
],
|
|
47
|
+
use: [
|
|
48
|
+
`${require.resolve(`happypack/loader`)}?id=js`
|
|
49
|
+
]
|
|
50
|
+
}]
|
|
51
|
+
},
|
|
52
|
+
// webpack 不会有大量 hints 信息,默认为 warning
|
|
53
|
+
performance: {
|
|
54
|
+
hints: false,
|
|
55
|
+
},
|
|
56
|
+
plugins: [
|
|
57
|
+
// 每次 build 前,清空 public/dist 目录
|
|
58
|
+
new CleanWebpackPlugin(['public/dist'], {
|
|
59
|
+
root: path.resolve(process.cwd(), './app/'), // 插件的根目录
|
|
60
|
+
exclude: [], // 要排除的文件 / 目录
|
|
61
|
+
verbose: true, // 在终端打印删除文件的日志
|
|
62
|
+
dry: false // 是否仅 “模拟删除”
|
|
63
|
+
}),
|
|
64
|
+
// 提前 css 的公共部分,有效利用缓存
|
|
65
|
+
new MiniCssExtractPlugin({
|
|
66
|
+
chunkFilename: 'css/[name]_[contenthash:8].bundle.css'
|
|
67
|
+
}),
|
|
68
|
+
// 优化并压缩 css 资源
|
|
69
|
+
new CSSMinimizerPlugin(),
|
|
70
|
+
// 多线程打包 js,加快打包速度
|
|
71
|
+
new HappyPack({
|
|
72
|
+
...happypackCommonConfig,
|
|
73
|
+
id: 'js',
|
|
74
|
+
loaders: [`${require.resolve(`babel-loader`)}?${JSON.stringify({
|
|
75
|
+
presets: [require.resolve('@babel/preset-env')],
|
|
76
|
+
plugins: [
|
|
77
|
+
require.resolve('@babel/plugin-transform-runtime')
|
|
78
|
+
]
|
|
79
|
+
})}`]
|
|
80
|
+
}),
|
|
81
|
+
// 多线程打包 css,加快打包速度
|
|
82
|
+
new HappyPack({
|
|
83
|
+
...happypackCommonConfig,
|
|
84
|
+
id: 'css',
|
|
85
|
+
loaders: [{
|
|
86
|
+
path: require.resolve('css-loader'),
|
|
87
|
+
options: {
|
|
88
|
+
importLoaders: 1
|
|
89
|
+
}
|
|
90
|
+
}]
|
|
91
|
+
}),
|
|
92
|
+
// 浏览器在请求资源时不发送用户的身份凭证
|
|
93
|
+
new HtmlWebpackInjectAttributesPlugin({
|
|
94
|
+
crossorigin: 'anonymous'
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
],
|
|
98
|
+
optimization: {
|
|
99
|
+
// 使用 TerserPlugin 的并发和缓存,提升压缩阶段的性能
|
|
100
|
+
// 清除 console.log
|
|
101
|
+
minimize: true,
|
|
102
|
+
minimizer: [
|
|
103
|
+
new TerserPlugin({
|
|
104
|
+
//cache: true, // v5版本已废弃,Webpack 5 内置缓存
|
|
105
|
+
parallel: true, // 利用多核 CPU的优势来加快压缩速度
|
|
106
|
+
terserOptions: {
|
|
107
|
+
compress: {
|
|
108
|
+
drop_console: true, // 去掉 console.log 内容
|
|
109
|
+
drop_debugger: true, // 建议添加:移除 debugger
|
|
110
|
+
pure_funcs: ['console.log'] //彻底清除 console.log 相关代码
|
|
111
|
+
},
|
|
112
|
+
ecma: 2020,
|
|
113
|
+
mangle: {
|
|
114
|
+
safari10: true // 兼容 Safari 10
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
module.exports = webPackConfig
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// 本地开发启动 devServer
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const webpack = require('webpack');
|
|
5
|
+
const devMiddleware = require('webpack-dev-middleware');
|
|
6
|
+
const hotMiddleware = require('webpack-hot-middleware');
|
|
7
|
+
|
|
8
|
+
module.exports = () => {
|
|
9
|
+
// 从 webpack.dev.js 获取 webpack 配置和 devServer 配置
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
webpackConfig,
|
|
13
|
+
DEV_SERVER_CONFIG
|
|
14
|
+
} = require('./config/webpack.dev.js');
|
|
15
|
+
|
|
16
|
+
const app = express();
|
|
17
|
+
|
|
18
|
+
const compiler = webpack(webpackConfig);
|
|
19
|
+
|
|
20
|
+
// 指定静态文件目录
|
|
21
|
+
app.use(express.static(path.join(__dirname, '../public/dist')));
|
|
22
|
+
|
|
23
|
+
// 引用 divMiddleware 中间件(监控文件改动)
|
|
24
|
+
app.use(devMiddleware(compiler, {
|
|
25
|
+
// 落地文件 (编译后的资源)
|
|
26
|
+
writeToDisk: (filePath) => filePath.endsWith('.tpl'),
|
|
27
|
+
|
|
28
|
+
// 资源路径 (编译后的资源)
|
|
29
|
+
publicPath: webpackConfig.output.publicPath,
|
|
30
|
+
|
|
31
|
+
// headers 配置 解决跨域等。。
|
|
32
|
+
headers: {
|
|
33
|
+
'Access-Control-Allow-Origin': '*',
|
|
34
|
+
'Access-Control-Allow-Methods': 'GET, POSY, PUT, DELETE, PATCH, OPTIONS',
|
|
35
|
+
'Access-Control-Allow-Headers': 'X-Request-Width, content-type, Authorization'
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
stats: {
|
|
39
|
+
colors: true,
|
|
40
|
+
modules: false, // 不显示模块更新日志
|
|
41
|
+
children: false, // 不显示子模块日志
|
|
42
|
+
chunks: false, // 不显示 chunk 日志
|
|
43
|
+
chunkModules: false, // 不显示 chunk 模块日志
|
|
44
|
+
entrypoints: false, // 不显示入口文件日志
|
|
45
|
+
assets: false // 不显示资源文件日志
|
|
46
|
+
}
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
// 引用 hotMiddleware 中间件(实现热更新通讯)
|
|
50
|
+
app.use(hotMiddleware(compiler, {
|
|
51
|
+
path: `/${DEV_SERVER_CONFIG.HMR_PATH}`,
|
|
52
|
+
log: () => {}
|
|
53
|
+
}))
|
|
54
|
+
|
|
55
|
+
console.info('请等待webpack初次构建完成提示...')
|
|
56
|
+
|
|
57
|
+
// 启动 devServer
|
|
58
|
+
const port = DEV_SERVER_CONFIG.PORT;
|
|
59
|
+
app.listen(port, () => {
|
|
60
|
+
console.log(`app listening on port ${port}`)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const webpack = require('webpack');
|
|
2
|
+
const webpackProdConfig = require('./config/webpack.prod.js');
|
|
3
|
+
|
|
4
|
+
module.exports = () => {
|
|
5
|
+
console.log('\nbuilding... \n');
|
|
6
|
+
|
|
7
|
+
webpack(webpackProdConfig, (err, stats) => {
|
|
8
|
+
if(err) { console.log(err); return; }
|
|
9
|
+
|
|
10
|
+
process.stdout.write(`${stats.toString({
|
|
11
|
+
colors: true, // 在控制台输出色彩信息
|
|
12
|
+
modules: false, // 不显示每个模块的打包信息
|
|
13
|
+
children: false, // 不显示子编译任务的信息
|
|
14
|
+
chunks: false, // 不显示每个代码块的信息
|
|
15
|
+
chunkModules: true // 显示代码块中模块的信息
|
|
16
|
+
})}`)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = (app) => {
|
|
2
|
+
return {
|
|
3
|
+
// 判断是否本地环境
|
|
4
|
+
isLocal() {
|
|
5
|
+
return process.env._ENV === 'local';
|
|
6
|
+
},
|
|
7
|
+
// 判断是否测试环境
|
|
8
|
+
isBeta() {
|
|
9
|
+
return process.env._ENV === 'beta';
|
|
10
|
+
},
|
|
11
|
+
// 判断是否生成环境
|
|
12
|
+
isProduction() {
|
|
13
|
+
return process.env._ENV === 'production';
|
|
14
|
+
},
|
|
15
|
+
// 获取当前环境
|
|
16
|
+
get() {
|
|
17
|
+
return process.env._ENV ?? 'local';
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const Koa = require('koa');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { sep } = path; // 兼容不同操作系统上的斜杠
|
|
4
|
+
|
|
5
|
+
const env = require('./env.js');
|
|
6
|
+
|
|
7
|
+
const middlewareLoader = require('./loader/middleware.js');
|
|
8
|
+
const routerSchemaLoader = require('./loader/router-schema.js');
|
|
9
|
+
const routerLoader = require('./loader/router.js');
|
|
10
|
+
const controllerLoader = require('./loader/controller.js');
|
|
11
|
+
const extendLoader = require('./loader/extend.js');
|
|
12
|
+
const configLoader = require('./loader/config.js');
|
|
13
|
+
const serviceLoader = require('./loader/service.js');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
/**
|
|
18
|
+
* 启动项目
|
|
19
|
+
* @params options 项目配置
|
|
20
|
+
* options = {
|
|
21
|
+
name // 项目名称
|
|
22
|
+
homePath // 项目首页
|
|
23
|
+
}
|
|
24
|
+
*/
|
|
25
|
+
start(options = {}) {
|
|
26
|
+
// koa 实例
|
|
27
|
+
const app = new Koa();
|
|
28
|
+
|
|
29
|
+
// 应用配置
|
|
30
|
+
app.options = options;
|
|
31
|
+
|
|
32
|
+
// 基础路径
|
|
33
|
+
app.baseDir = process.cwd();
|
|
34
|
+
|
|
35
|
+
// 业务文件路径
|
|
36
|
+
app.businessPath = path.resolve(app.baseDir, `.${sep}app`);
|
|
37
|
+
|
|
38
|
+
// 初始化环境配置
|
|
39
|
+
app.env = env();
|
|
40
|
+
console.log(`-- [start] env:${app.env.get()} --`);
|
|
41
|
+
|
|
42
|
+
// 加载 middleware
|
|
43
|
+
middlewareLoader(app);
|
|
44
|
+
console.log(`-- [start] load middleware done --`);
|
|
45
|
+
|
|
46
|
+
// 加载 routerSchema
|
|
47
|
+
routerSchemaLoader(app);
|
|
48
|
+
console.log(`-- [start] load routerSchema done --`);
|
|
49
|
+
|
|
50
|
+
// 加载 controller
|
|
51
|
+
controllerLoader(app);
|
|
52
|
+
console.log(`-- [start] load controller done --`);
|
|
53
|
+
|
|
54
|
+
// 加载 service
|
|
55
|
+
serviceLoader(app);
|
|
56
|
+
console.log(`-- [start] load serviceLoader done --`);
|
|
57
|
+
|
|
58
|
+
// 加载 config
|
|
59
|
+
configLoader(app);
|
|
60
|
+
console.log(`-- [start] load configLoader done --`);
|
|
61
|
+
|
|
62
|
+
// 加载 extend
|
|
63
|
+
extendLoader(app);
|
|
64
|
+
console.log(`-- [start] load extendLoader done --`);
|
|
65
|
+
|
|
66
|
+
// 注册 elpis 全局中间件
|
|
67
|
+
// 中间件按 app.use() 的顺序执行,所以静态文件中间件必须放在路由中间件之前
|
|
68
|
+
const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}app${sep}middleware.js`);
|
|
69
|
+
const elpisMiddleware = require(elpisMiddlewarePath);
|
|
70
|
+
elpisMiddleware(app);
|
|
71
|
+
console.log(`-- [start] load global elpis middleware done --`);
|
|
72
|
+
|
|
73
|
+
// 注册业务全局中间件
|
|
74
|
+
// 中间件按 app.use() 的顺序执行,所以静态文件中间件必须放在路由中间件之前
|
|
75
|
+
try {
|
|
76
|
+
require(`${app.businessPath}${sep}middleware.js`)(app);
|
|
77
|
+
console.log(`-- [start] load global business Middleware done --`);
|
|
78
|
+
} catch(e) {
|
|
79
|
+
console.log('[exception] there is no global business middleware file.')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 注册路由
|
|
83
|
+
routerLoader(app);
|
|
84
|
+
console.log(`-- [start] load routerLoader done --`);
|
|
85
|
+
|
|
86
|
+
// console.dir(app, { depth: null, colors: true });
|
|
87
|
+
// 启动服务
|
|
88
|
+
try {
|
|
89
|
+
const port = process.env.PORT || 8080;
|
|
90
|
+
const host = process.env.IP || '0.0.0.0';
|
|
91
|
+
app.listen(port, host);
|
|
92
|
+
console.log(`1Server runnin on port:${port}`);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.log(error);
|
|
95
|
+
}
|
|
96
|
+
return app;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { sep } = path;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* config loader
|
|
6
|
+
* @param {object} app koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 配置区分 本地/测试/生产,通过 env 环境读取补贴文件配置 env.config
|
|
9
|
+
* 通过 env.config 覆盖 default.config 加载到 app.config 中
|
|
10
|
+
*
|
|
11
|
+
* 目录下对应的 config 配置
|
|
12
|
+
* 默认配置 config/config.default.js
|
|
13
|
+
* 本地配置 config/config.local.js
|
|
14
|
+
* 测试配置 config/config.beta.js
|
|
15
|
+
* 生产配置 config/config.prod.js
|
|
16
|
+
*/
|
|
17
|
+
module.exports = (app) => {
|
|
18
|
+
// elpis config 目录及相关文件
|
|
19
|
+
const elpisConfigPath = path.resolve(__dirname, `..${sep}..${sep}config`);
|
|
20
|
+
let defaultConfig = require(path.resolve(elpisConfigPath, `.${sep}config.default.js`));
|
|
21
|
+
|
|
22
|
+
// 找到 config/ 目录
|
|
23
|
+
const businessConfigPath = path.resolve(process.cwd(), `.${sep}config`);
|
|
24
|
+
const businessDefaultConfig = require(path.resolve(businessConfigPath, `.${sep}config.default.js`));
|
|
25
|
+
|
|
26
|
+
// 业务 config 目录及相关文件
|
|
27
|
+
try {
|
|
28
|
+
defaultConfig = {
|
|
29
|
+
...defaultConfig,
|
|
30
|
+
...businessDefaultConfig
|
|
31
|
+
}
|
|
32
|
+
} catch(e) {
|
|
33
|
+
console.log('[exception] there is no default.config file');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 获取 env.config
|
|
37
|
+
let envConfig = {}
|
|
38
|
+
try {
|
|
39
|
+
if(app.env.isLocal()) { // 本地环境
|
|
40
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.local.js`));
|
|
41
|
+
} else if(app.env.isBeta()) { // 测试环境
|
|
42
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.beta.js`));
|
|
43
|
+
} else if(app.env.isProduction()) { // 生产环境
|
|
44
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.prod.js`));
|
|
45
|
+
}
|
|
46
|
+
} catch(e) {
|
|
47
|
+
console.log('[exception] there is no env.config file');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 覆盖并加载 config配置
|
|
51
|
+
app.config = Object.assign({}, defaultConfig, envConfig);
|
|
52
|
+
}
|