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