UNPKG

15.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const fs = require('fs-extra');
4const path = require('path');
5const chalk = require('chalk');
6const cwd = process.cwd();
7const merge = require('lodash.mergewith');
8const shelljs = require('shelljs');
9const mergeDir = path.join(cwd, '.CACHE/nanachi');
10let mergeFilesQueue = require('./mergeFilesQueue');
11let diff = require('deep-diff');
12const buildType = process.argv[2].split(':')[1];
13const ignoreExt = ['.tgz'];
14const ANU_ENV = buildType
15 ? buildType === 'h5'
16 ? 'web'
17 : buildType
18 : 'wx';
19function 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}
54function 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}
61function 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}
150function 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}
160function customizer(objValue, srcValue) {
161 if (Array.isArray(objValue)) {
162 return Array.from(new Set(objValue.concat(srcValue)));
163 }
164}
165function 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}
176function 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}
190function getMergedData(configList) {
191 return xDiff(configList);
192}
193function 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}
207function 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}
282function 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}
295function 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}
306function 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}
326function 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}
335function 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}
355function 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}
430exports.default = default_1;
431;