@wiajs/core 1.1.29 → 1.1.31

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/util/wiafile.js CHANGED
@@ -1,155 +1,338 @@
1
- /* eslint-disable import/no-extraneous-dependencies */
2
1
  /**
3
- * 自动根据srcdist目录文件生成 wiafile.yml 文件映射
4
- * build时,比较当前文件hash与上次build时的hash值,
5
- * 变化则放入 update中,再针对 update部分文件进行build,
2
+ * 自动根据dircfg参数生成 wiafile.yml 文件映射
3
+
4
+ * 一、编译
5
+ *
6
+ * 1. 扫描项目所有文件,保存文件hash到building字段,等待编译
7
+ * 2. 比较build 和 building的hash值,更新文件放入update字段
8
+ * 3. 对更新的js、less按文件进行独立编译(js可使用rspack或webpack)
9
+ * 4. 根据编译后文件,生成应用index.js、页面打包文件及未包含到共用wia中的wia包
10
+ * 5. 编译完成,building 替换 build,避免重复编译
11
+ *
12
+ * 二、发布
6
13
  *
14
+ * 1. 编译完成后,发布文件hash存入pubing字段
15
+ * 2. 比较 pub 和 pubing的hash值,更新文件放入 update字段
16
+ * 3. 更新文件发布到cos服务器,发布成功 标记为 1
17
+ * 4. 发布完成后,自动刷新CDN,刷新成功 标记为 2
18
+ * 5. 发布完毕,所有update文件全部为2,删除update字段,pubing 替换pub,避免重复发布
19
+
20
+ * 比如,增量编译less为css:
21
+ const rs = await wiafile.build(dir, wiacfg)
22
+ if (rs && rs.update && rs.update.less) {
23
+ buildCss(rs.update.less, () => {
24
+ wiafile.builded(dir, true)
25
+ cb && cb()
26
+ })
27
+ } else {
28
+ wiafile.builded(dir, true)
29
+ console.log('less: no change.')
30
+ cb && cb()
31
+ }
7
32
  *
8
33
  */
9
- const _ = require('lodash');
10
- const path = require('path');
11
- const crypto = require('crypto');
12
- const utils = require('utility');
13
- const fs = require('fs');
14
- const yaml = require('js-yaml');
34
+ import path from 'node:path'
35
+ import crypto from 'node:crypto'
36
+ import fs from 'fs-extra'
37
+ import ld from 'lodash'
38
+ import * as util from 'utility'
39
+ import yaml from 'js-yaml'
40
+
41
+ /** @enum {number} */
42
+ const MakeType = {
43
+ build: 0, // src 编译到 dist
44
+ builded: 1,
45
+ pack: 2, // dist 打包到 pack
46
+ packed: 3,
47
+ del: 4,
48
+ pub: 5, // pack 发布到 wia store
49
+ pubed: 6,
50
+ }
15
51
 
16
- let _cfg = {};
17
- let _src = '';
52
+ /** @typedef {object} MakeOpts
53
+ * @prop {string[]} [file] - 发布文件或路径
54
+ * @prop {string[]} [exclude] - 排除路径或文件
55
+ * @prop {boolean} [clear = false] - 忽略wiafile.yml,重新全部生成
56
+ * @prop {string} [ext] - 限定文件类型
57
+ */
18
58
 
19
- function promisify(f) {
20
- return (...arg) =>
21
- new Promise((res, rej) => {
22
- f(...arg, (err, rs) => {
23
- if (err) rej(err);
24
- else res(rs);
25
- });
26
- });
59
+ /** @typedef {object} MakeOpt
60
+ * @prop {string[]} file - 发布文件或路径
61
+ * @prop {string[]} [files] - 发布文件
62
+ * @prop {string[]} [dirs] - 发布路径
63
+ * @prop {string[]} exclude - 排除路径或文件
64
+ * @prop {string[]} [xfiles] - 排除文件
65
+ * @prop {string[]} [xdirs] - 排除路径
66
+ * @prop {boolean} clear - 忽略wiafile.yml,重新全部生成
67
+ * @prop {string} [src] - 处理代码路径 src/dist/pub
68
+ * @prop {string} [ext] - 限定文件类型
69
+ */
70
+
71
+ const makeDef = {
72
+ file: ['index.*', 'page', 'img'], // 打包的入口,文件或路径
73
+ // @ts-ignore
74
+ files: [],
75
+ // @ts-ignore
76
+ dirs: [],
77
+ exclude: ['*.ts', '*.json'], // 排除
78
+ // @ts-ignore
79
+ xfiles: [],
80
+ // @ts-ignore
81
+ xdirs: [],
82
+ clear: false,
27
83
  }
28
84
 
29
85
  /**
30
- * 获取wia文件,保存到 wiafile.yml 文件,返回更新的文件集到 update
31
- * 便于编译或者发布。
32
- * @param {*} dir 文件map路径
33
- * @param {*} act build or pub,build 时,读取src,pub时,读取 dist
86
+ * 获取wia文件,保存到 wiafile.yml 文件,比较文件hash,将更新的文件放入 update
87
+ * 便于增量(更新的文件)编译或发布
88
+ * @param {string} dir 文件map路径
89
+ * @param {MakeType} type build or pub,build 时,读取src,pub时,读取 dist
90
+ * @param {MakeOpts} [opts]
34
91
  */
35
- async function make(dir, cfg, act = 'build', all = false) {
36
- let R = {};
37
-
92
+ async function make(dir, type = MakeType.build, opts = {}) {
93
+ let R = {}
38
94
  try {
39
- dir = dir || process.cwd(); // 默认指向运行目录
40
- _src = act === 'pub' ? path.join(dir, './dist') : path.join(dir, './src');
41
- _cfg = cfg;
95
+ /** @type {MakeOpt} */
96
+ const opt = {...makeDef, ...opts}
97
+ const {clear} = opt
98
+
99
+ if (opt.file) {
100
+ opt.files = opt.file.filter(v => v.includes('.'))
101
+ opt.dirs = opt.file.filter(v => !v.includes('.'))
102
+ }
103
+
104
+ if (opt.exclude) {
105
+ opt.xfiles = opt.exclude.filter(v => v.includes('.'))
106
+ opt.xdirs = opt.exclude.filter(v => !v.includes('.'))
107
+ }
108
+
109
+ dir = dir || process.cwd() // 默认指向运行目录
110
+
111
+ let src
112
+ if (type === MakeType.build) src = path.join(dir, './src')
113
+ else if (type === MakeType.pack) src = path.join(dir, './dist')
114
+ else if (type === MakeType.pub) {
115
+ src = path.join(dir, './pub')
116
+ opt.ext = 'zip' // 只发布 zip 文件
117
+ }
118
+
119
+ opt.src = src
42
120
  // require(path.join(dir, './wiaconfig.js')); // eslint-disable-line
43
121
 
44
- const {ver} = _cfg;
45
122
  // const rs = await getFiles(dir, ver); // 获得该项目所有文件,变化的文件放入f.R 中
46
123
  // 获取上次自动上传文件清单
47
- let rs = {};
48
- let r = {};
49
- const f = path.resolve(dir, './wiafile.yml');
50
- if (!all && fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')); // eslint-disable-line
124
+ let rs = {}
125
+ /** @type {*} */
126
+ let r = {}
127
+ const f = path.resolve(dir, './wiafile.yml')
128
+ // 读取现有 wiafile.yml
129
+ try {
130
+ if (!clear && fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8'))
131
+ } catch {
132
+ r = {}
133
+ }
51
134
 
52
- // r = yaml.load(fs.readFileSync(file, 'utf8'));
53
135
  // if (checkVer(r, ver))
54
136
  // rs = {};
55
137
  // 正在编译、发布的,直接返回正在编译、发布文件,不重新扫描
56
138
  // 调用者需决定,是重新编译还是等待,发起编译、发布者,需处理编译完成或失败。
57
- let skip = false;
58
- if (r) {
59
- if (r.pub && r.pub.pubing && act === 'pub') {
60
- R = {pubing: r.wia.pubing, update: r.wia.update};
61
- skip = true;
62
- } else if (r.local && r.local.building && act === 'build') {
63
- R = {building: r.local.building, update: r.local.update};
64
- skip = true; // 正在编译,跳过
65
- }
139
+ let skip = false
140
+ if (type === MakeType.build && r?.local?.building) {
141
+ R = {building: r.local.building, update: r.local.update}
142
+ skip = true // 正在编译,跳过
143
+ } else if (type === MakeType.pack && r?.release?.packing) {
144
+ R = {packing: r.release.packing, update: r.release.update}
145
+ skip = true
146
+ } else if (type === MakeType.pub && r?.wia?.pubing) {
147
+ R = {pubing: r.wia.pubing, update: r.wia.update}
148
+ skip = true
149
+ }
66
150
 
67
- // 重新扫描文件,去掉之前的 update
68
- if (!skip) {
69
- if (act === 'build') {
70
- if (r.local) {
71
- delete r.local.update;
72
- rs = r.local;
73
- }
74
- } else if (act === 'pub') {
75
- if (r.pub) {
76
- delete r.pub.update;
77
- rs = r.pub;
78
- }
151
+ // 重新扫描文件,去掉之前的 update
152
+ if (!skip) {
153
+ if (type === MakeType.build) {
154
+ if (r.local) {
155
+ delete r.local.update
156
+ rs = r.local
157
+ }
158
+ } else if (type === MakeType.pack) {
159
+ if (r.release) {
160
+ delete r.release.update
161
+ rs = r.release
162
+ }
163
+ } else if (type === MakeType.pub) {
164
+ if (r.wia) {
165
+ delete r.wia.update
166
+ rs = r.wia
79
167
  }
80
168
  }
81
- }
82
169
 
83
- if (!skip) {
84
170
  // 获取目标项目目录、子目录下的文件MD5对象
85
- await getFile(_src, rs, act);
171
+ await getFile(src, rs, type, opt)
86
172
  // console.log('wiafile make getFile', {dir, rs, act});
87
173
 
88
- if (!_.isEmpty(rs)) {
89
- R = rs; // .update || {};
90
- if (act === 'build') rs = {local: rs, wia: r && r.pub ? r.pub : {}};
91
- else rs = {pub: rs, local: r && r.local ? r.local : {}};
174
+ if (!ld.isEmpty(rs)) {
175
+ R = rs // .update || {};
176
+ if (type === MakeType.build) rs = {local: rs, release: r?.release ?? {}, wia: r?.wia ?? {}}
177
+ else if (type === MakeType.pack)
178
+ rs = {release: rs, local: r?.local ?? {}, wia: r?.wia ?? {}}
179
+ else if (type === MakeType.pub)
180
+ rs = {wia: rs, release: r?.release ?? {}, local: r?.local ?? {}}
92
181
  // console.log('wiafile', {dir, rs, act});
93
- save(rs, f);
182
+ save(rs, f)
94
183
  }
95
184
  }
96
185
  } catch (ex) {
97
- console.log(`wiafile make exp:${ex.message}`);
186
+ console.log(`make exp:${ex.message}`)
98
187
  }
99
188
 
100
- return R;
189
+ return R
190
+ }
191
+
192
+ /**
193
+ * 对指定文件夹下的子文件夹和文件进行递归,生成带MD5的hash值列表对象
194
+ * 对于子目录文件, 生成嵌套对象, 如
195
+ * 通过比较当前项目文件和文件记录中的文件MD5值,
196
+ * 将变化的文件放入update,用于build or publish
197
+ * @param {string} dir 文件路径
198
+ * @param {*} rs 保存所有文件对象
199
+ * @param {MakeType} type build or pub操作,最新的文件列表写入building or pubing
200
+ * @param {MakeOpt} opt - opt.file 限定发布文件或路径,不传获取所有
201
+ */
202
+ async function getFile(dir, rs, type, opt) {
203
+ try {
204
+ // 获得当前文件夹下的所有的文件夹和文件,赋值给目录和文件数组变量
205
+ const [dirs, files] = ld.partition(fs.readdirSync(dir), p =>
206
+ fs.statSync(path.join(dir, p)).isDirectory()
207
+ )
208
+
209
+ // 对子文件夹进行递归,使用了await,串行同步执行
210
+ let pms = []
211
+ let r
212
+ for (let d of dirs || []) {
213
+ let name = path.join(dir, d).replace(opt.src, '')
214
+ if (name.startsWith(path.sep)) name = name.slice(1)
215
+ if (path.sep !== '/') name = name.replace(/\\/gim, '/') // 统一路径为/
216
+
217
+ let mk = false
218
+ // 限定发布文件或目录
219
+ for (const v of opt.dirs) {
220
+ if (new RegExp(`^${v}$|^${v}/`, 'i').test(name)) {
221
+ mk = true
222
+ break
223
+ }
224
+ }
225
+ // 排除根目录
226
+ if (mk) {
227
+ for (const v of opt.xdirs) {
228
+ if (new RegExp(`^${v}$|^${v}/`, 'i').test(name)) {
229
+ mk = false
230
+ break
231
+ }
232
+ }
233
+ }
234
+ if (mk) {
235
+ // tree[d] = await getDir(path.join(dir, d), last); // 路径创建对象
236
+ pms.push(getFile(path.join(dir, d), rs, type, opt))
237
+ }
238
+ }
239
+
240
+ if (!ld.isEmpty(pms)) r = await Promise.all(pms)
241
+
242
+ pms = []
243
+ // 当前目录下所有文件名进行同步hash计算
244
+ for (const f of files || []) {
245
+ let name = path.join(dir, f).replace(opt.src, '')
246
+ if (name.startsWith(path.sep)) name = name.slice(1)
247
+ if (path.sep !== '/') name = name.replace(/\\/gim, '/') // 统一路径为/
248
+
249
+ let mk = true
250
+ // src 目录 中的文件,不带/
251
+ if (!name.includes('/')) {
252
+ mk = false
253
+ for (let v of opt.files) {
254
+ v = v.replace('.', '\\.').replace('*', '\\w+')
255
+ if (new RegExp(`^${v}$`, 'i').test(name)) {
256
+ mk = true
257
+ break
258
+ }
259
+ }
260
+ }
261
+
262
+ // 限定文件扩展名
263
+ if (mk && opt.ext && !new RegExp(`\.${opt.ext}$`, 'i').test(name)) mk = false
264
+
265
+ // (pf.includes('.js') && v === pf)
266
+ // tree[f] = await promisify(hashFile)(path.join(dir, f), rs, last); // eslint-disable-line
267
+ // 排除文件
268
+ if (mk) {
269
+ for (let v of opt.xfiles) {
270
+ v = v.replace('.', '\\.').replace('*', '\\w+')
271
+ if (new RegExp(`^${v}$`, 'i').test(name)) {
272
+ console.log('getFile exclude', {pf: v, name})
273
+ mk = false
274
+ break
275
+ }
276
+ }
277
+ }
278
+ if (mk) pms.push(hashFile(name, path.join(dir, f), rs, type))
279
+ }
280
+ if (!ld.isEmpty(pms)) r = await Promise.all(pms)
281
+ } catch (e) {
282
+ console.log(`getFile exp:${e.message}`)
283
+ }
101
284
  }
102
285
 
103
286
  /**
104
287
  * 处理js更新文件
105
288
  * @param {*} rs
106
289
  */
107
- function js(rs) {
290
+ function js(rs, opt) {
108
291
  // 有js文件变化,html、less 暂未处理
109
- if (rs && rs.R && !_.isEmpty(rs.R.JS)) {
110
- const pf = {}; // page file
292
+ if (rs && rs.R && !ld.isEmpty(rs.R.JS)) {
293
+ const pf = {} // page file
111
294
  // f.R.JS.forEach((v) => { // forEach 回调函数是同步执行的,不用担心jf没有准备好!
112
295
  for (let v of rs.R.JS) {
113
296
  // eslint-disable-line
114
- let pk = false; // 是否需重新编译 console.log('pages', {js: f.R.JS});
297
+ let pk = false // 是否需重新编译 console.log('pages', {js: f.R.JS});
115
298
 
116
- for (let pf of _cfg.file) {
299
+ for (let pf of opt.file) {
117
300
  // eslint-disable-line
118
301
  if (
119
302
  (pf.includes('.js') && v === pf) ||
120
303
  (!pf.includes('.js') && new RegExp(`^/?${pf}/`, 'i').test(v))
121
304
  ) {
122
- pk = true;
123
- break;
305
+ pk = true
306
+ break
124
307
  }
125
308
  }
126
309
 
127
310
  // 排除
128
311
  if (pk) {
129
- for (let pf of _cfg.exclude) {
312
+ for (let pf of opt.exclude) {
130
313
  // eslint-disable-line
131
314
  if (
132
315
  (pf.includes('.js') && v === pf) ||
133
316
  (!pf.includes('.js') && new RegExp(`^${pf}/`, 'i').test(v))
134
317
  ) {
135
- pk = false;
136
- break;
318
+ pk = false
319
+ break
137
320
  }
138
321
  }
139
322
  }
140
323
 
141
324
  // 需编译文件
142
325
  if (pk) {
143
- let name = '';
144
- let file = v;
326
+ let name = ''
327
+ let file = v
145
328
  if (v.includes('/')) {
146
- const dir = path.dirname(v);
147
- file = path.basename(v);
148
- name = `${prj}/${dir}/${file.substring(0, file.indexOf('.'))}`;
149
- uf[name] = `./src/${prj}/${dir}/${file}`;
329
+ const dir = path.dirname(v)
330
+ file = path.basename(v)
331
+ name = `${prj}/${dir}/${file.substring(0, file.indexOf('.'))}`
332
+ uf[name] = `./src/${prj}/${dir}/${file}`
150
333
  } else {
151
- name = `${prj}/${file.substring(0, file.indexOf('.'))}`;
152
- uf[name] = `./src/${prj}/${file}`;
334
+ name = `${prj}/${file.substring(0, file.indexOf('.'))}`
335
+ uf[name] = `./src/${prj}/${file}`
153
336
  }
154
337
 
155
338
  // 去掉包含page路径
@@ -171,8 +354,8 @@ function js(rs) {
171
354
  }
172
355
 
173
356
  // 有更新则发布
174
- if (!_.isEmpty(uf)) {
175
- R.f = rs;
357
+ if (!ld.isEmpty(uf)) {
358
+ R.f = rs
176
359
  }
177
360
  // page下的文件写入pages.js文件
178
361
  //if (!_.isEmpty(pf))
@@ -181,17 +364,17 @@ function js(rs) {
181
364
  }
182
365
 
183
366
  function checkVer(rs, ver) {
184
- let R = false;
367
+ let R = false
185
368
  // 比较主、中版本号,发现config.js与wiamap.js中的不一致,则全部重新发布!
186
369
  if (rs && ver) {
187
370
  if (rs.R && rs.R.ver) {
188
- let rg = /(\d*)\.(\d*)\.(\d+)/.exec(rs.R.ver);
189
- const rv = (rg && `${rg[1]}.${rg[2]}`) || '';
190
- rg = /(\d*)\.(\d*)\.(\d+)/.exec(ver);
191
- const pv = (rg && `${rg[1]}.${rg[2]}`) || '';
192
- if (rv !== pv) rs = {};
371
+ let rg = /(\d*)\.(\d*)\.(\d+)/.exec(rs.R.ver)
372
+ const rv = (rg && `${rg[1]}.${rg[2]}`) || ''
373
+ rg = /(\d*)\.(\d*)\.(\d+)/.exec(ver)
374
+ const pv = (rg && `${rg[1]}.${rg[2]}`) || ''
375
+ if (rv !== pv) rs = {}
193
376
  } else {
194
- R = true;
377
+ R = true
195
378
  }
196
379
  }
197
380
  }
@@ -201,298 +384,248 @@ function checkVer(rs, ver) {
201
384
  * @param {*} prj 项目名称
202
385
  */
203
386
  async function addVer(prj, rf, ver) {
204
- let R = '';
387
+ let R = ''
205
388
 
206
389
  try {
207
390
  // 参数配置文件
208
- const f = path.join(__dirname, '/src/config/app.js');
209
- const rv = /(\d*)\.(\d*)\.(\d+)/.exec(ver);
210
- const nv = (rv && `${rv[1]}.${rv[2]}.${parseInt(rv[3], 10) + 1}`) || '';
391
+ const f = path.join(__dirname, '/src/config/app.js')
392
+ const rv = /(\d*)\.(\d*)\.(\d+)/.exec(ver)
393
+ const nv = (rv && `${rv[1]}.${rv[2]}.${parseInt(rv[3], 10) + 1}`) || ''
211
394
  if (nv) {
212
- const re = new RegExp(`ver\\s*:\\s*['"]${ver}['"]`, 'i');
213
- let pcs = fs.readFileSync(f, 'utf8');
214
- pcs = pcs.replace(re, `ver: '${nv}'`);
215
- fs.writeFileSync(
216
- f,
217
- pcs,
218
- e => e && console.log(`save ${f} exp:${e.message}`)
219
- );
395
+ const re = new RegExp(`ver\\s*:\\s*['"]${ver}['"]`, 'i')
396
+ let pcs = fs.readFileSync(f, 'utf8')
397
+ pcs = pcs.replace(re, `ver: '${nv}'`)
398
+ fs.writeFileSync(f, pcs, e => e && console.log(`save ${f} exp:${e.message}`))
220
399
 
221
400
  // 更新配置文件后,更新f,便于后续处理
222
401
  if (rf) {
223
- rf['config.js'] = await promisify(hashFile)(f);
402
+ rf['config.js'] = await promisify(hashFile)(f)
224
403
  // // config.js 必须包含在 app.js 中!
225
404
  // if (rf.R && rf.R.JS.includes('config.js'))
226
405
  // delete rf.R.JS['config.js'];
227
406
  }
228
407
 
229
- console.warn(`addVer config ${ver} -> ${nv}`);
230
- R = nv;
408
+ console.warn(`addVer config ${ver} -> ${nv}`)
409
+ R = nv
231
410
  }
232
411
  } catch (e) {
233
- console.log(` exp:${e.message}`);
412
+ console.log(` exp:${e.message}`)
234
413
  }
235
414
 
236
- return R;
415
+ return R
237
416
  }
238
417
 
239
418
  /**
240
- * 生成指定文件的 MD5值,用于判断文件内容是否变化
241
- * @param {*} file
242
- * @param {*} cb
419
+ * 获取更新后需要编译的文件
420
+ * @param {string} dir - 代码路径
421
+ * @param {MakeOpt} opt - 代码路径
422
+ * 数组:[page/attend/index.js]
423
+ * 返回对象 uf: 更新文件 pf:页面文件
424
+ * entry: {index: './src/index.js',
425
+ * 'page/attend/index', './src/page/attend/index.js'}
243
426
  */
244
- function hash(tx) {
245
- let R = crypto.createHash('md5');
246
- R.update(tx);
247
- R = R.digest('hex');
248
- return R;
249
- }
250
-
251
- /**
252
- * 将page中的js文件创建到pages.js中,方便调试
253
- * @param {*} pf
254
- */
255
- function pagefile(pf) {
256
- if (_.isEmpty(pf)) return '';
257
-
258
- const p = [];
259
- Object.keys(pf).forEach(k => {
260
- const f = pf[k].replace(/.js$/, '');
261
- p.push(`import ${k} from '${f}';`);
262
- });
263
-
264
- const n = [];
265
- Object.keys(pf).forEach(k => {
266
- const name = k[0].toLowerCase() + k.slice(1);
267
- n.push(` const ${name} = new ${k}();`);
268
- });
269
-
270
- p.push(`
271
- export default class Pages {
272
- init() {
273
- ${n.join('\n')}
274
- }
275
- }
276
- `);
277
-
278
- // 将内容写入page.js 文件
279
- const ps = p.join('\n');
280
- if (!_.isEmpty(ps)) {
281
- const f = path.join(_src, './pages.js');
282
- // 获取当前页面共用模块
283
- let lastH = '';
284
- if (fs.existsSync(f)) {
285
- const tx = fs.readFileSync(f, 'utf8');
286
- lastH = hash(tx);
287
- }
288
- // 有变化,则更新pages文件
289
- if (hash(ps) !== lastH)
290
- fs.writeFileSync(
291
- f,
292
- ps,
293
- e => e && console.log(`save ${f} exp:${e.message}`)
294
- );
295
- }
296
-
297
- return ps;
298
- }
299
-
300
- /**
301
- * 对指定文件夹下的子文件夹和文件进行递归,生成带MD5的hash值列表对象
302
- * 对于子目录文件, 生成嵌套对象, 如
303
- * 通过比较当前项目文件和文件记录中的文件MD5值,
304
- * 将变化的文件放入update,用于build or publish
305
- * @param {*} dir 文件路径
306
- * @param {*} rs 保存所有文件对象
307
- * @param {*} act build or pub操作,最新的文件列表写入building or pubing
308
- */
309
- async function getFile(dir, rs, act) {
427
+ async function getUpdate(dir, opt) {
428
+ let R
310
429
  try {
311
- // 获得当前文件夹下的所有的文件夹和文件,赋值给目录和文件数组变量
312
- const [dirs, files] = _.partition(fs.readdirSync(dir), p =>
313
- fs.statSync(path.join(dir, p)).isDirectory()
314
- );
430
+ // 获取上次自动上传文件清单
431
+ const file = path.resolve(dir, './wiafile.yml')
432
+ let last = null
433
+ if (fs.existsSync(file)) last = yaml.load(fs.readFileSync(file, 'utf8'))
434
+
435
+ // 比较主、中版本号,与发布文件不一致,则全部重新发布!
436
+ // const {ver} = _appcfg
437
+ // let clear = false
438
+ // const lastVer = last?.wia?.pub?.ver
439
+ // if (ver && lastVer) {
440
+ // let rg = /(\d*)\.(\d*)\.(\d+)/.exec(lastVer)
441
+ // const lv = (rg && `${rg[1]}.${rg[2]}`) || ''
442
+ // rg = /(\d*)\.(\d*)\.(\d+)/.exec(ver)
443
+ // const v = (rg && `${rg[1]}.${rg[2]}`) || ''
444
+ // clear = v !== lv
445
+ // }
446
+
447
+ const rs = await build(dir, opt)
448
+ if (rs?.building && rs?.update) {
449
+ // 更新文件
450
+ const {less, html} = rs.update
451
+ let {js} = rs.update
452
+ // index 作为入口文件,必须包含,放到uf的最后
453
+ // webpack 入口文件
454
+ const uf = {less, html, js: {index: `./${owner}/${app}/src/index.js`}}
455
+ if (js) {
456
+ for (const v of js) {
457
+ let pk = false // 是否需重新编译
458
+ // eslint-disable-next-line
459
+ for (let pf of opt.file) {
460
+ if (
461
+ (pf.includes('.js') && v === pf) ||
462
+ (!pf.includes('.js') && new RegExp(`^${pf}/`, 'i').test(v))
463
+ ) {
464
+ pk = true
465
+ break
466
+ }
467
+ }
315
468
 
316
- // 对子文件夹进行递归,使用了await,串行同步执行
317
- let pms = [];
318
- let r;
319
- if (dirs) {
320
- for (let i = 0, ilen = dirs.length; i < ilen; i++) {
321
- const d = dirs[i];
322
- // eslint-disable-line
323
- let pk = true;
324
- // 排除根目录
325
- if (dir === _src && _cfg.exclude) {
326
- for (let j = 0, jlen = _cfg.exclude.length; j < jlen; j++) {
327
- const pf = _cfg.exclude[j];
328
- // eslint-disable-line
329
- if (
330
- !pf.includes('*.') &&
331
- !pf.includes('.js') &&
332
- new RegExp(`^${pf}$`, 'i').test(d)
333
- ) {
334
- pk = false;
335
- break;
469
+ // 需编译文件
470
+ if (pk) {
471
+ const n = v.replace(/\.js$/i, '')
472
+ uf.js[n] = `./${owner}/${app}/src/${v}`
336
473
  }
337
474
  }
338
475
  }
339
- if (pk) {
340
- // tree[d] = await getDir(path.join(dir, d), last); // 路径创建对象
341
- pms.push(getFile(path.join(dir, d), rs, act));
342
- }
343
- }
344
-
345
- if (!_.isEmpty(pms)) r = await Promise.all(pms);
346
- }
347
476
 
348
- pms = [];
349
- // 当前目录下所有文件名进行同步hash计算
350
- if (files) {
351
- // eslint-disable-next-line no-restricted-syntax
352
- for (const f of files) {
353
- // (pf.includes('.js') && v === pf)
354
- // tree[f] = await promisify(hashFile)(path.join(dir, f), rs, last); // eslint-disable-line
355
- let pk = true;
356
- // 排除根目录
357
- if (_cfg.exclude) {
358
- for (let j = 0, jlen = _cfg.exclude.length; j < jlen; j++) {
359
- const pf = _cfg.exclude[j];
360
- // eslint-disable-line
361
- if (
362
- pf.includes('*.') &&
363
- new RegExp(`${pf.replace('*.', '[\\s\\S]+\\.')}$`, 'i').test(f)
364
- ) {
365
- console.log('getFile', {pf, f});
366
- pk = false;
367
- break;
477
+ let page = true
478
+ // 仅仅配置文件修改,不重新发布page
479
+ if (js.length === 1 && js.includes('config/app.js')) page = false
480
+ const pf = {}
481
+ // page 需将所有页面及app.js的公共js打包,无论是否更改
482
+ if (page) {
483
+ js = rs.building
484
+ Object.keys(js).forEach(v => {
485
+ if (/^page\/.+\.js$/.test(v)) {
486
+ // if (v.startsWith('page/') && v.endsWith('.js')) {
487
+ const n = v.replace(/\.js$/i, '')
488
+ pf[n] = `./${owner}/${app}/src/${v}`
368
489
  }
369
- }
490
+ })
370
491
  }
371
- if (pk) pms.push(hashFile(path.join(dir, f), rs, act));
372
- }
373
- if (!_.isEmpty(pms)) r = await Promise.all(pms);
492
+ R = {uf, pf}
374
493
  }
375
494
  } catch (e) {
376
- console.log(`getFile exp:${e.message}`);
495
+ console.log('getFile exp:', e.message)
377
496
  }
497
+
498
+ return R
378
499
  }
379
500
 
380
501
  /**
381
- * 设置变更文件列表
502
+ * 按文件后缀,设置变更文件列表
382
503
  * @param {*} rs 写入数据集,变更文件写入 rs.update
383
504
  * @param {*} f 当前文件,含路径,含后缀,不含src 根路径
384
505
  */
385
506
  function setUpdate(rs, f) {
386
- let R = null;
507
+ let R = null
387
508
 
388
- if (!rs) return null;
509
+ if (!rs) return null
389
510
 
390
511
  try {
391
- if (!rs.update) rs.update = {time: utils.logDate(), tick: Date.now()};
512
+ if (!rs.update) rs.update = {time: util.logDate(), tick: Date.now()}
392
513
 
393
- let r = rs.update;
394
- r.js = r.js || [];
395
- r.html = r.html || [];
396
- r.css = r.css || [];
397
- r.less = r.less || [];
398
- r.img = r.img || [];
399
- r.asset = r.asset || [];
514
+ let r = rs.update
515
+ r.js = r.js || []
516
+ r.html = r.html || []
517
+ r.css = r.css || []
518
+ r.less = r.less || []
519
+ r.img = r.img || []
520
+ r.asset = r.asset || []
521
+ r.zip = r.zip || []
400
522
 
401
523
  // 'mall/page/login': './src/mall/page/login.js'
402
524
  switch (path.extname(f).toLowerCase()) {
403
525
  case '.js':
404
- r = r.js;
405
- break;
526
+ r = r.js
527
+ break
406
528
  case '.html':
407
- r = r.html;
408
- break;
529
+ r = r.html
530
+ break
409
531
  case '.less':
410
- r = r.less;
411
- break;
532
+ r = r.less
533
+ break
412
534
  case '.css':
413
- r = r.css;
414
- break;
535
+ r = r.css
536
+ break
537
+ case '.zip':
538
+ r = r.zip
539
+ break
415
540
  case '.jpg':
416
- r = r.img;
417
- break;
541
+ r = r.img
542
+ break
418
543
  case '.png':
419
- r = r.img;
420
- break;
544
+ r = r.img
545
+ break
421
546
  default:
422
- r = r.asset;
547
+ r = r.asset
423
548
  }
424
549
 
425
- if (r) r.push(f);
550
+ if (r) r.push(f)
426
551
 
427
- R = r;
552
+ R = r
428
553
  } catch (e) {
429
- console.log(`setUpdate exp:${e.message}`);
554
+ console.log(`setUpdate exp:${e.message}`)
430
555
  }
431
556
 
432
- return R;
557
+ return R
433
558
  }
434
559
 
435
560
  /**
436
561
  * 生成指定文件的 MD5值,写入building 或 pubing对象
437
562
  * 通过 MD5判断文件是否变化,变化文件写入 update,用于重新编译或发布
438
- * @param {*} f 文件
563
+ * @param {string} name 去掉路径的文件名
564
+ * @param {string} f 文件
439
565
  * @param {*} rs 写入的数据集
566
+ * @param {MakeType} type 写入的数据集
567
+ * @returns {Promise<*>}
440
568
  */
441
- function hashFile(f, rs, act = 'build') {
569
+ function hashFile(name, f, rs, type = MakeType.build) {
442
570
  return new Promise((res, rej) => {
443
- const r = fs.createReadStream(f);
444
- const md5 = crypto.createHash('md5');
445
- let hex = '';
571
+ const r = fs.createReadStream(f)
572
+ const md5 = crypto.createHash('md5')
573
+ let hex = ''
446
574
 
447
- r.on('data', md5.update.bind(md5));
575
+ r.on('data', md5.update.bind(md5))
448
576
  r.on('end', () => {
449
- hex = md5.digest('hex');
577
+ hex = md5.digest('hex')
450
578
  if (rs) {
451
- // 去掉项目根路径
452
- f = f.replace(_src, '');
453
- if (f.startsWith(path.sep)) f = f.substr(1);
454
-
455
- if (path.sep !== '/') f = f.replace(/\\/gim, '/'); // 统一路径为/,方便处理
456
-
457
579
  // console.log('hashFile', {file, act});
458
- let hs = null;
459
- switch (act) {
460
- case 'build': {
580
+ let hs = null
581
+ switch (type) {
582
+ case MakeType.build: {
461
583
  if (!rs.building)
462
584
  rs.building = {
463
- time: utils.logDate(),
585
+ time: util.logDate(),
464
586
  tick: Date.now(),
465
- };
466
- rs.building[f] = hex;
467
- hs = rs.build || {};
468
- break;
587
+ }
588
+ rs.building[name] = hex
589
+ hs = rs.build || {}
590
+ break
469
591
  }
470
592
 
471
- case 'pub': {
593
+ case MakeType.pack: {
594
+ if (!rs.packing)
595
+ rs.packing = {
596
+ time: util.logDate(),
597
+ tick: Date.now(),
598
+ }
599
+ rs.packing[name] = hex
600
+ hs = rs.build || {}
601
+ break
602
+ }
603
+
604
+ case MakeType.pub: {
472
605
  if (!rs.pubing)
473
606
  rs.pubing = {
474
- time: utils.logDate(),
607
+ time: util.logDate(),
475
608
  tick: Date.now(),
476
- };
477
- rs.pubing[f] = hex;
478
- hs = rs.pub || {};
479
- break;
609
+ }
610
+ rs.pubing[name] = hex
611
+ hs = rs.pub || {}
612
+ break
480
613
  }
481
614
 
482
615
  // 默认放入 last
483
616
  default: {
484
- break;
617
+ break
485
618
  }
486
619
  }
487
620
 
488
- const h = hs && hs[f];
621
+ const h = hs && hs[name]
489
622
  // getLastHash(last, f);
490
623
  // 变化的文件,记录到update中
491
- if (!h || h !== hex) setUpdate(rs, f);
624
+ if (!h || h !== hex) setUpdate(rs, name)
492
625
  }
493
- res(hex);
494
- });
495
- });
626
+ res(hex)
627
+ })
628
+ })
496
629
  }
497
630
 
498
631
  /**
@@ -501,87 +634,203 @@ function hashFile(f, rs, act = 'build') {
501
634
  * @param {string} f
502
635
  */
503
636
  function save(rs, f) {
504
- const yml = yaml.dump(rs);
505
- fs.writeFileSync(f, yml, err => {
506
- if (err) console.log(`save exp:${err.message}`);
507
- });
637
+ try {
638
+ const yml = yaml.dump(rs)
639
+ fs.writeFile(f, yml)
640
+ } catch (e) {
641
+ console.log(`save exp:${e.message}`)
642
+ }
508
643
  }
509
644
 
510
645
  /**
511
- * 编译完成,building文件列表变为build或删除
512
- * 之前的build清除
513
- * @param {*} dir
514
- * @param {*} succ 编译成功还是失败
646
+ * 编译完成,building文件列表替换之前的build,update保留
647
+ * @param {string} dir
648
+ * @param {boolean} succ 编译成功还是失败
649
+ * @returns {object}
515
650
  */
516
651
  function builded(dir, succ = true) {
517
- let R = {};
652
+ let R = {}
518
653
  try {
519
- dir = dir || process.cwd(); // 默认指向运行目录
520
- const f = path.resolve(dir, './wiafile.yml');
521
- let r = {};
522
- if (fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')); // require(file); // eslint-disable-line
523
- if (r && r.local && r.local.building) {
524
- const rs = {local: {}, wia: r.wia ? r.wia : {}};
525
- if (succ) {
526
- // const {building: build, build: x, ...z} = r;
527
- // rs = {build, ...z};
528
- rs.local.build = r.local.building || {};
529
- rs.local.update = r.local.update || {};
530
- } else {
531
- rs.local.build = r.local.build || {};
532
- rs.local.update = r.local.update || {};
654
+ dir = dir || process.cwd() // 默认指向运行目录
655
+ const f = path.resolve(dir, './wiafile.yml')
656
+ /** @type {*} */
657
+ let r = {}
658
+ if (fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')) // require(file); // eslint-disable-line
659
+ if (r?.local?.building) {
660
+ const rs = {
661
+ local: {build: {}, update: r.local.update || {}},
662
+ release: r.release ? r.release : {},
663
+ wia: r.wia ? r.wia : {},
533
664
  }
534
- save(rs, f);
665
+ rs.local.build = succ ? r.local.building || {} : r.local.build || {}
666
+ save(rs, f)
535
667
 
536
- R = rs.local.update;
668
+ R = rs.local.update
537
669
  }
538
670
  } catch (e) {
539
- console.log(`wiafile builded exp:${e.message}`);
671
+ console.log(`wiafile builded exp:${e.message}`)
540
672
  }
541
673
 
542
- return R;
674
+ return R
675
+ }
676
+
677
+ /**
678
+ * 打包完成,packing 变为 pack 或 被放弃
679
+ * @param {*} dir
680
+ * @param {boolean} succ 编译成功还是失败
681
+ * @returns {object}
682
+ */
683
+ function packed(dir, succ = true) {
684
+ /** {object} */
685
+ let R = {}
686
+ try {
687
+ dir = dir || process.cwd() // 默认指向运行目录
688
+ const f = path.resolve(dir, './wiafile.yml')
689
+ /** @type {*} */
690
+ let r = {}
691
+ if (fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')) // require(file); // eslint-disable-line
692
+ if (r?.release?.packing) {
693
+ const rs = {
694
+ release: {pack: {}, update: r.release.update},
695
+ local: r.local ? r.local : {},
696
+ wia: r.wia ? r.wia : {},
697
+ }
698
+ rs.release.pack = succ ? r.release.packing || {} : r.release.pack || {}
699
+ save(rs, f)
700
+
701
+ R = rs.release.update
702
+ }
703
+ } catch (e) {
704
+ console.log(`pubed exp:${e.message}`)
705
+ }
706
+
707
+ return R
543
708
  }
544
709
 
545
710
  /**
546
711
  * 发布完成,pubing 变为 pub 或 被放弃
547
712
  * @param {*} dir
713
+ * @param {boolean} succ 编译成功还是失败
714
+ * @returns {object}
548
715
  */
549
716
  function pubed(dir, succ = true) {
550
- let R = {};
717
+ /** {object} */
718
+ let R = {}
551
719
  try {
552
- dir = dir || process.cwd(); // 默认指向运行目录
553
- const f = path.resolve(dir, './wiafile.yml');
554
- let r = {};
555
- if (fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')); // require(file); // eslint-disable-line
556
- if (r && r.wia && r.wia.pubing) {
557
- const rs = {wia: {}, local: r.local ? r.local : {}};
558
- if (succ) {
559
- // const {pubing: pub, pub: x, ...z} = r;
560
- // rs = {pub, ...z};
561
- rs.wia.pub = r.pub.pubing;
562
- rs.wia.update = r.wia.update;
563
- } else {
564
- rs.wia.pub = r.wia.pub;
565
- rs.iwa.update = r.wia.update;
720
+ dir = dir || process.cwd() // 默认指向运行目录
721
+ const f = path.resolve(dir, './wiafile.yml')
722
+ /** @type {*} */
723
+ let r = {}
724
+ if (fs.existsSync(f)) r = yaml.load(fs.readFileSync(f, 'utf8')) // require(file); // eslint-disable-line
725
+ if (r?.wia?.pubing) {
726
+ const rs = {
727
+ wia: {pub: {}, update: r.wia.update},
728
+ release: r.release ? r.release : {},
729
+ local: r.local ? r.local : {},
566
730
  }
731
+ rs.wia.pub = succ ? r.pub.pubing || {} : r.wia.pub || {}
732
+ save(rs, f)
567
733
 
568
- save(rs, f);
569
- R = rs.update;
734
+ R = rs.wia.update
570
735
  }
571
736
  } catch (e) {
572
- console.log(`pubed exp:${e.message}`);
737
+ console.log(`pubed exp:${e.message}`)
573
738
  }
574
739
 
575
- return R;
740
+ return R
576
741
  }
577
742
 
578
- // make(); // 单独调试用
579
- async function build(dir, cfg, all) {
580
- return make(dir, cfg, 'build', all);
743
+ /**
744
+ *
745
+ * @param {*} f
746
+ * @returns {(...any) => Promise<*>}
747
+ */
748
+ function promisify(f) {
749
+ return (...arg) =>
750
+ new Promise((res, rej) => {
751
+ f(...arg, (err, rs) => {
752
+ if (err) rej(err)
753
+ else res(rs)
754
+ })
755
+ })
581
756
  }
582
757
 
583
- async function pub(dir, cfg) {
584
- return make(dir, cfg, 'pub');
758
+ /**
759
+ * src 编译到 dist
760
+ * @param {*} dir
761
+ * @param {MakeOpts} opts
762
+ * @returns {Promise<*>}
763
+ */
764
+ async function build(dir, opts) {
765
+ return make(dir, MakeType.build, opts)
766
+ }
767
+
768
+ /**
769
+ * 获取dist 打包到 pack 的文件列表
770
+ * @param {*} dir
771
+ * @param {MakeOpts} opts
772
+ * @returns {Promise<*>}
773
+ */
774
+ async function pack(dir, opts) {
775
+ return make(dir, MakeType.pack, opts)
776
+ }
777
+
778
+ /**
779
+ * pack 发布到 wia
780
+ * @param {*} dir
781
+ * @param {MakeOpts} opts
782
+ * @returns {Promise<{packing: string[], update: {js: string[],}}>}
783
+ */
784
+ async function pub(dir, opts) {
785
+ return make(dir, MakeType.pub, opts)
786
+ }
787
+
788
+ /**
789
+ * 从pack结果中获取需发布打包的文件列表,便于发布打包
790
+ * 【注意】基于效率,增量生成规则如下:
791
+ * 1. index 引用的文件修改,rspack 会自动更新到项目index.js文件
792
+ * 2. page 页面文件(js、html、less、css)修改,需重新生成到 页面文件
793
+ * 3. 页面引用的文件修改,rspack 会自动更新对应页面js文件
794
+ * 4. clear 参数为true,不比较差异,全量生成,否则只生成变化文件(增量)
795
+ * @param {{packing: string[], update: *}} rs - pack 返回数据集
796
+ * @returns {Promise<{uf:*, pf:*}>} - uf为更新文件,pf为page全量文件
797
+ */
798
+ async function getPack(rs) {
799
+ let R
800
+ try {
801
+ if (!rs) return
802
+
803
+ /** @type {{js: *}} */
804
+ const uf = {js: {index: `./dist/index.js`}} // 无论是否修改(页面修改会影响index),均纳入生成
805
+ const {js, html, less, css} = rs.update // 更新文件
806
+ let n
807
+ // 所有更新文件均需打包到js文件后再压缩
808
+ for (const v of js) uf.js[v.replace(/\.js$/i, '')] = `./dist/${v}`
809
+ for (const v of less) (n = v.replace(/\.less$/i, '')), (uf.js[n] = `./dist/${n}.js`)
810
+ for (const v of css) (n = v.replace(/\.css$/i, '')), (uf.js[n] = `./dist/${n}.js`)
811
+ for (const v of html) (n = v.replace(/\.html$/i, '')), (uf.js[n] = `./dist/${n}.js`)
812
+
813
+ let page = true
814
+ // 仅仅index文件修改,不重新发布page
815
+ if (Object.keys(uf.js).length === 1) page = false
816
+ /** @type {*} */
817
+ const pf = {}
818
+ // page 需将所有页面及app.js的公共js打包,无论是否更改
819
+ if (page) {
820
+ const ps = rs.packing // 所有页面js文件
821
+ for (const v of Object.keys(ps || {})) {
822
+ if (/^page\/\S+\.js$/.test(v)) {
823
+ const n = v.replace(/\.js$/i, '')
824
+ pf[n] = `./dist/${v}`
825
+ }
826
+ }
827
+ }
828
+ R = {uf, pf}
829
+ } catch (e) {
830
+ console.log('getPack exp:', e.message)
831
+ }
832
+
833
+ return R
585
834
  }
586
835
 
587
- module.exports = {build, builded, pub, pubed};
836
+ export {build, builded, pack, packed, getPack, pub, pubed, MakeType}