1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const fs = require('fs-extra');
|
4 | const path = require('path');
|
5 | const chalk = require('chalk');
|
6 | const cwd = process.cwd();
|
7 | const merge = require('lodash.mergewith');
|
8 | const shelljs = require('shelljs');
|
9 | const mergeDir = path.join(cwd, '.CACHE/nanachi');
|
10 | let mergeFilesQueue = require('./mergeFilesQueue');
|
11 | let diff = require('deep-diff');
|
12 | const buildType = process.argv[2].split(':')[1];
|
13 | const ignoreExt = ['.tgz'];
|
14 | const ANU_ENV = buildType
|
15 | ? buildType === 'h5'
|
16 | ? 'web'
|
17 | : buildType
|
18 | : 'wx';
|
19 | function getMergedAppJsConent(appJsSrcPath, pages = [], importSyntax = []) {
|
20 | function getAppImportSyntaxCode(importSyntax = []) {
|
21 | let importSyntaxList = importSyntax.map(function (curEl) {
|
22 | curEl = curEl.trim();
|
23 | if (!/;$/.test(curEl)) {
|
24 | curEl = curEl + ';';
|
25 | }
|
26 | return curEl;
|
27 | });
|
28 | return importSyntaxList.length ? importSyntaxList.join("\n") + '\n' : '';
|
29 | }
|
30 | let allRoutesStr = pages.map(function (pageRoute) {
|
31 | if (!(/^\.\//.test(pageRoute))) {
|
32 | pageRoute = './' + pageRoute;
|
33 | }
|
34 | pageRoute = `import '${pageRoute}';\n`;
|
35 | return pageRoute;
|
36 | }).join('');
|
37 | allRoutesStr += getAppImportSyntaxCode(importSyntax);
|
38 | return new Promise(function (rel, rej) {
|
39 | let appJsSrcContent = '';
|
40 | let appJsDist = path.join(mergeDir, 'source', 'app.js');
|
41 | try {
|
42 | appJsSrcContent = fs.readFileSync(appJsSrcPath).toString();
|
43 | }
|
44 | catch (err) {
|
45 | rej(err);
|
46 | }
|
47 | appJsSrcContent = allRoutesStr + appJsSrcContent;
|
48 | rel({
|
49 | content: appJsSrcContent,
|
50 | dist: appJsDist
|
51 | });
|
52 | });
|
53 | }
|
54 | function getAppJsSourcePath(queue = []) {
|
55 | let appJsSourcePath = queue.filter(function (file) {
|
56 | file = file.replace(/\\/g, '/');
|
57 | return /\/app\.js$/.test(file);
|
58 | })[0];
|
59 | return appJsSourcePath;
|
60 | }
|
61 | function getFilesMap(queue = []) {
|
62 | let map = {};
|
63 | let env = ANU_ENV;
|
64 | queue.forEach(function (file) {
|
65 | file = file.replace(/\\/g, '/');
|
66 | if (/\/package\.json$/.test(file)) {
|
67 | let { dependencies = {}, devDependencies = {} } = require(file);
|
68 | if (dependencies) {
|
69 | delete dependencies['@qnpm/chaika-patch'];
|
70 | map['pkgDependencies'] = map['pkgDependencies'] || [];
|
71 | map['pkgDependencies'].push({
|
72 | id: file,
|
73 | content: dependencies,
|
74 | type: 'dependencies'
|
75 | });
|
76 | }
|
77 | if (devDependencies) {
|
78 | delete devDependencies['node-sass'];
|
79 | delete devDependencies['@qnpm/chaika-patch'];
|
80 | map['pkgDevDep'] = map['pkgDevDep'] || [];
|
81 | map['pkgDevDep'].push({
|
82 | id: file,
|
83 | content: devDependencies,
|
84 | type: 'devDependencies'
|
85 | });
|
86 | }
|
87 | return;
|
88 | }
|
89 | if (/\/app\.json$/.test(file)) {
|
90 | var { alias = {}, pages = [], imports = [], order = 0 } = require(file);
|
91 | if (alias) {
|
92 | map['alias'] = map['alias'] || [];
|
93 | map['alias'].push({
|
94 | id: file,
|
95 | content: alias,
|
96 | type: 'alias'
|
97 | });
|
98 | }
|
99 | if (pages.length) {
|
100 | let allInjectRoutes = pages.reduce(function (ret, route) {
|
101 | let injectRoute = '';
|
102 | if ('[object Object]' === Object.prototype.toString.call(route)) {
|
103 | var supportPlat = route.platform.replace(/\s*/g, '').split(',');
|
104 | if (supportPlat.includes(env)) {
|
105 | injectRoute = route.route;
|
106 | }
|
107 | }
|
108 | else {
|
109 | injectRoute = route;
|
110 | }
|
111 | if (injectRoute) {
|
112 | ret.add(injectRoute);
|
113 | }
|
114 | return ret;
|
115 | }, new Set());
|
116 | map['pages'] = map['pages'] || [];
|
117 | map['pages'].push({
|
118 | routes: Array.from(allInjectRoutes),
|
119 | order: order
|
120 | });
|
121 | }
|
122 | map['importSyntax'] = map['importSyntax'] || [];
|
123 | map['importSyntax'] = map['importSyntax'].concat(imports);
|
124 | return;
|
125 | }
|
126 | if (/\/project\.config\.json$/.test(file)) {
|
127 | map['projectConfigJson'] = map['projectConfigJson'] || [];
|
128 | map['projectConfigJson'].push(file);
|
129 | return;
|
130 | }
|
131 | var reg = new RegExp(env + 'Config.json$');
|
132 | map['xconfig'] = map['xconfig'] || [];
|
133 | if (reg.test(file)) {
|
134 | try {
|
135 | var config = require(file);
|
136 | if (config) {
|
137 | map['xconfig'].push({
|
138 | id: file,
|
139 | content: config
|
140 | });
|
141 | }
|
142 | }
|
143 | catch (err) {
|
144 | }
|
145 | }
|
146 | });
|
147 | map = orderRouteByOrder(map);
|
148 | return map;
|
149 | }
|
150 | function orderRouteByOrder(map) {
|
151 | map['pages'] = map['pages'].sort(function (a, b) {
|
152 | return b.order - a.order;
|
153 | });
|
154 | map['pages'] = map['pages'].map(function (pageEl) {
|
155 | return pageEl.routes;
|
156 | });
|
157 | map['pages'] = [].concat(...map['pages']);
|
158 | return map;
|
159 | }
|
160 | function customizer(objValue, srcValue) {
|
161 | if (Array.isArray(objValue)) {
|
162 | return Array.from(new Set(objValue.concat(srcValue)));
|
163 | }
|
164 | }
|
165 | function getUniqueSubPkgConfig(list = []) {
|
166 | return list.reduce(function (initList, curEle) {
|
167 | let curName = curEle.name;
|
168 | let hasEle = initList.some(function (el) {
|
169 | return el.name === curName;
|
170 | });
|
171 | if (!hasEle)
|
172 | initList.push(curEle);
|
173 | return initList;
|
174 | }, []);
|
175 | }
|
176 | function getMergedXConfigContent(config) {
|
177 | let env = ANU_ENV;
|
178 | let xConfigJsonDist = path.join(mergeDir, 'source', `${env}Config.json`);
|
179 | let ret = xDiff(config);
|
180 | for (let i in ret) {
|
181 | if (i.toLocaleLowerCase() === 'subpackages') {
|
182 | ret[i] = getUniqueSubPkgConfig(ret[i]);
|
183 | }
|
184 | }
|
185 | return Promise.resolve({
|
186 | dist: xConfigJsonDist,
|
187 | content: JSON.stringify(ret, null, 4)
|
188 | });
|
189 | }
|
190 | function getMergedData(configList) {
|
191 | return xDiff(configList);
|
192 | }
|
193 | function getValueByPath(path, data) {
|
194 | path = path.slice(0);
|
195 | var ret;
|
196 | while (path.length) {
|
197 | var key = path.shift();
|
198 | if (!ret) {
|
199 | ret = data[key] || '';
|
200 | }
|
201 | else {
|
202 | ret = ret[key] || '';
|
203 | }
|
204 | }
|
205 | return ret;
|
206 | }
|
207 | function xDiff(list) {
|
208 | if (!list.length)
|
209 | return {};
|
210 | let first = list[0];
|
211 | let confictQueue = [];
|
212 | let other = list.slice(1);
|
213 | let isConfict = false;
|
214 | for (let i = 0; i < other.length; i++) {
|
215 | let x = diff(first.content, other[i].content) || [];
|
216 | x = x.filter(function (el) {
|
217 | return el.kind === 'E'
|
218 | && el.path.every(function (el) {
|
219 | return typeof el === 'string';
|
220 | });
|
221 | });
|
222 | if (x.length) {
|
223 | isConfict = true;
|
224 | confictQueue = [...x];
|
225 | break;
|
226 | }
|
227 | }
|
228 | if (isConfict) {
|
229 | var errList = [];
|
230 | confictQueue.forEach(function (confictEl) {
|
231 | let kind = [];
|
232 | list.forEach(function (el) {
|
233 | let confictValue = getValueByPath(confictEl.path, el.content);
|
234 | if (confictValue) {
|
235 | let errorItem = {};
|
236 | errorItem.confictFile = el.id.replace(/\\/g, '/').split(/\/download\//).pop();
|
237 | errorItem.confictValue = confictValue || '';
|
238 | if (el.type === 'dependencies') {
|
239 | errorItem.confictKeyPath = ['dependencies', ...confictEl.path];
|
240 | }
|
241 | else if (el.type === 'devDependencies') {
|
242 | errorItem.confictKeyPath = ['devDependencies', ...confictEl.path];
|
243 | }
|
244 | else if (el.type === 'alias') {
|
245 | errorItem.confictKeyPath = ['nanachi', 'alias', ...confictEl.path];
|
246 | }
|
247 | else {
|
248 | errorItem.confictKeyPath = confictEl.path;
|
249 | }
|
250 | errorItem.confictKeyPath = JSON.stringify(errorItem.confictKeyPath);
|
251 | kind.push(errorItem);
|
252 | }
|
253 | });
|
254 | errList.push(kind);
|
255 | });
|
256 | var msg = '';
|
257 | errList.forEach(function (errEl) {
|
258 | let kindErr = '';
|
259 | errEl.forEach(function (errItem) {
|
260 | var tpl = `
|
261 | 冲突文件: ${(errItem.confictFile)}
|
262 | 冲突路径 ${errItem.confictKeyPath}
|
263 | 冲突详情:${JSON.stringify({ [JSON.parse(errItem.confictKeyPath).pop()]: errItem.confictValue }, null, 4)}
|
264 | `;
|
265 | kindErr += tpl;
|
266 | });
|
267 | msg = msg + kindErr + '\n--------------------------------------------------\n';
|
268 | });
|
269 | console.log(chalk.bold.red('⚠️ 发现冲突! 请先解决冲突。\n\n' + msg));
|
270 | process.exit(1);
|
271 | }
|
272 | isConfict = false;
|
273 | if (!isConfict) {
|
274 | return list.reduce(function (ret, el) {
|
275 | return merge(ret, el.content, customizer);
|
276 | }, {});
|
277 | }
|
278 | else {
|
279 | return {};
|
280 | }
|
281 | }
|
282 | function getMergedPkgJsonContent(alias) {
|
283 | let currentPkg = require(path.join(cwd, 'package.json'));
|
284 | let distContent = Object.assign({}, currentPkg, {
|
285 | nanachi: {
|
286 | alias: alias
|
287 | }
|
288 | });
|
289 | let dist = path.join(mergeDir, 'package.json');
|
290 | return {
|
291 | dist: dist,
|
292 | content: JSON.stringify(distContent, null, 4)
|
293 | };
|
294 | }
|
295 | function getMiniAppProjectConfigJson(projectConfigQueue = []) {
|
296 | let dist = path.join(mergeDir, 'project.config.json');
|
297 | let distContent = '';
|
298 | if (projectConfigQueue.length) {
|
299 | distContent = JSON.stringify(require(projectConfigQueue[0]), null, 4);
|
300 | }
|
301 | return {
|
302 | dist: dist,
|
303 | content: distContent
|
304 | };
|
305 | }
|
306 | function validateAppJsFileCount(queue) {
|
307 | let appJsFileCount = queue
|
308 | .filter(function (el) {
|
309 | return /\/app\.js$/.test(el);
|
310 | })
|
311 | .map(function (el) {
|
312 | return el.replace(/\\/g, '/').split('/download/').pop();
|
313 | });
|
314 | if (!appJsFileCount.length || appJsFileCount.length > 1) {
|
315 | let msg = '';
|
316 | if (!appJsFileCount.length) {
|
317 | msg = '校验到无 app.js 文件的拆库工程,请检查是否安装了该包含 app.js 文件的拆库工程.';
|
318 | }
|
319 | else if (appJsFileCount.length > 1) {
|
320 | msg = '校验到多个拆库仓库中存在app.js. 在业务线的拆库工程中,有且只能有一个拆库需要包含app.js' + '\n' + JSON.stringify(appJsFileCount, null, 4);
|
321 | }
|
322 | console.log(chalk.bold.red(msg));
|
323 | process.exit(1);
|
324 | }
|
325 | }
|
326 | function validateMiniAppProjectConfigJson(queue) {
|
327 | let projectConfigJsonList = queue.filter(function (el) {
|
328 | return /\/project\.config\.json$/.test(el);
|
329 | });
|
330 | if (projectConfigJsonList.length > 1) {
|
331 | console.log(chalk.bold.red('校验到多个拆库仓库中存在project.config.json. 在业务线的拆库工程中,最多只能有一个拆库需要包含project.config.jon:'), chalk.bold.red('\n' + JSON.stringify(projectConfigJsonList, null, 4)));
|
332 | process.exit(1);
|
333 | }
|
334 | }
|
335 | function validateConfigFileCount(queue) {
|
336 | let configFiles = queue.filter(function (el) {
|
337 | return /Config\.json$/.test(el);
|
338 | });
|
339 | let errorFiles = [];
|
340 | configFiles.forEach(function (el) {
|
341 | el = el.replace(/\\/g, '/');
|
342 | let projectName = el.replace(/\\/g, '/').split('/download/')[1].split('/')[0];
|
343 | let reg = new RegExp(projectName + '/' + ANU_ENV + 'Config.json$');
|
344 | let dir = path.dirname(el);
|
345 | if (reg.test(el) && !fs.existsSync(path.join(dir, 'app.js'))) {
|
346 | errorFiles.push(el);
|
347 | }
|
348 | });
|
349 | if (errorFiles.length) {
|
350 | console.log(chalk.bold.red('⚠️ 校验到拆库仓库中配置文件路径错误,请将该配置文件放到 source 目录中:'));
|
351 | console.log(chalk.bold.red(errorFiles.join('\n')) + '\n');
|
352 | process.exit(1);
|
353 | }
|
354 | }
|
355 | function default_1() {
|
356 | let queue = Array.from(mergeFilesQueue);
|
357 | validateAppJsFileCount(queue);
|
358 | validateConfigFileCount(queue);
|
359 | validateMiniAppProjectConfigJson(queue);
|
360 | let map = getFilesMap(queue);
|
361 | let tasks = [
|
362 | getMergedAppJsConent(getAppJsSourcePath(queue), map.pages, map.importSyntax),
|
363 | getMergedXConfigContent(map.xconfig),
|
364 | getMergedPkgJsonContent(getMergedData(map.alias)),
|
365 | getMiniAppProjectConfigJson(map.projectConfigJson)
|
366 | ];
|
367 | function getNodeModulesList(config) {
|
368 | let mergeData = getMergedData(config);
|
369 | return Object.keys(mergeData).reduce(function (ret, key) {
|
370 | ret.push(key + '@' + mergeData[key]);
|
371 | return ret;
|
372 | }, []);
|
373 | }
|
374 | var installList = [...getNodeModulesList(map.pkgDependencies), ...getNodeModulesList(map.pkgDevDep)];
|
375 | installList = Array.from(new Set(installList));
|
376 | if (ANU_ENV !== 'quick') {
|
377 | installList = installList.filter((dep) => {
|
378 | return !/hap\-toolkit/.test(dep);
|
379 | });
|
380 | }
|
381 | var installPkgList = installList.reduce(function (needInstall, pkg) {
|
382 | var pkgMeta = pkg.split('@');
|
383 | var pkgName = pkgMeta[0] === '' ? '@' + pkgMeta[1] : pkgMeta[0];
|
384 | var p = path.join(cwd, 'node_modules', pkgName, 'package.json');
|
385 | var isExit = fs.existsSync(p);
|
386 | if (!isExit) {
|
387 | needInstall.push(pkg);
|
388 | }
|
389 | return needInstall;
|
390 | }, []);
|
391 | installPkgList = installPkgList.filter(function (dep) {
|
392 | return !ignoreExt.includes('.' + dep.split('.').pop());
|
393 | });
|
394 | if (installPkgList.length) {
|
395 | let installList = installPkgList.join(' ');
|
396 | let installListLog = installPkgList.join('\n');
|
397 | console.log(chalk.bold.green(`🚚 正在安装拆库依赖, 请稍候...\n${installListLog}`));
|
398 | fs.ensureDir(path.join(cwd, 'node_modules'));
|
399 | let cmd = `npm install ${installList} --no-save`;
|
400 | let std = shelljs.exec(cmd, {
|
401 | silent: false
|
402 | });
|
403 | if (/npm ERR/.test(std.stderr)) {
|
404 | console.log(chalk.red(std.stderr));
|
405 | process.exit(1);
|
406 | }
|
407 | }
|
408 | return Promise.all(tasks)
|
409 | .then(function (queue) {
|
410 | queue = queue.map(function ({ dist, content }) {
|
411 | return new Promise(function (rel, rej) {
|
412 | if (!content) {
|
413 | rel(1);
|
414 | return;
|
415 | }
|
416 | fs.ensureFileSync(dist);
|
417 | fs.writeFile(dist, content, function (err) {
|
418 | if (err) {
|
419 | rej(err);
|
420 | }
|
421 | else {
|
422 | rel(1);
|
423 | }
|
424 | });
|
425 | });
|
426 | });
|
427 | return Promise.all(queue);
|
428 | });
|
429 | }
|
430 | exports.default = default_1;
|
431 | ;
|