@tarojs/cli-convertor 3.7.0-alpha.26 → 3.7.0-alpha.27

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/dist/index.js CHANGED
@@ -9,11 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ // import { ProjectType } from './../../taro-plugin-mini-ci/src/BaseCi';
12
13
  const template_1 = require("@babel/template");
13
14
  const traverse_1 = require("@babel/traverse");
14
15
  const t = require("@babel/types");
16
+ const binding_1 = require("@tarojs/binding");
15
17
  const cli_1 = require("@tarojs/cli");
16
18
  const helper_1 = require("@tarojs/helper");
19
+ const shared_1 = require("@tarojs/shared");
17
20
  const taroize = require("@tarojs/taroize");
18
21
  const transformer_wx_1 = require("@tarojs/transformer-wx");
19
22
  const path = require("path");
@@ -22,10 +25,11 @@ const unitTransform = require("postcss-taro-unit-transform");
22
25
  const prettier = require("prettier");
23
26
  const util_1 = require("./util");
24
27
  const astConvert_1 = require("./util/astConvert");
28
+ const global_1 = require("./util/global");
25
29
  const prettierJSConfig = {
26
30
  semi: false,
27
31
  singleQuote: true,
28
- parser: 'babel'
32
+ parser: 'babel',
29
33
  };
30
34
  const babylonConfig = {
31
35
  sourceType: 'module',
@@ -36,19 +40,18 @@ const babylonConfig = {
36
40
  'asyncGenerators',
37
41
  'objectRestSpread',
38
42
  'decorators',
39
- 'dynamicImport'
40
- ]
43
+ 'dynamicImport',
44
+ ],
41
45
  };
42
46
  const OUTPUT_STYLE_EXTNAME = '.scss';
43
- const WX_GLOBAL_FN = new Set(['getApp', 'getCurrentPages', 'requirePlugin', 'Behavior']);
47
+ const WX_GLOBAL_FN = new Set(['getApp', 'getCurrentPages', 'Behavior']);
44
48
  function processStyleImports(content, processFn) {
45
- const style = [];
46
- const imports = [];
49
+ // 获取css中的引用样式文件路径集合
50
+ const imports = (0, util_1.getWxssImports)(content);
51
+ // 将引用的样式文件路径转换为相对路径,后缀名转换为.scss
47
52
  const styleReg = new RegExp('.wxss');
48
53
  content = content.replace(helper_1.CSS_IMPORT_REG, (m, _$1, $2) => {
49
54
  if (styleReg.test($2)) {
50
- style.push(m);
51
- imports.push($2);
52
55
  if (processFn) {
53
56
  return processFn(m, $2);
54
57
  }
@@ -61,52 +64,209 @@ function processStyleImports(content, processFn) {
61
64
  });
62
65
  return {
63
66
  content,
64
- style,
65
- imports
67
+ imports,
66
68
  };
67
69
  }
68
70
  class Convertor {
69
- constructor(root) {
71
+ constructor(root, isTsProject) {
70
72
  this.wxsIncrementId = (0, util_1.incrementId)();
71
73
  this.root = root;
72
74
  this.convertRoot = path.join(this.root, 'taroConvert');
73
75
  this.convertDir = path.join(this.convertRoot, 'src');
74
76
  this.importsDir = path.join(this.convertDir, 'imports');
77
+ this.isTsProject = isTsProject;
78
+ if (isTsProject) {
79
+ this.miniprogramRoot = path.join(this.root, 'miniprogram');
80
+ }
75
81
  this.fileTypes = {
76
82
  TEMPL: '.wxml',
77
83
  STYLE: '.wxss',
78
84
  CONFIG: '.json',
79
- SCRIPT: '.js'
85
+ SCRIPT: isTsProject ? '.ts' : '.js',
80
86
  };
81
87
  this.pages = new Set();
82
88
  this.components = new Set();
83
89
  this.hadBeenCopyedFiles = new Set();
84
90
  this.hadBeenBuiltComponents = new Set();
85
91
  this.hadBeenBuiltImports = new Set();
92
+ this.reportErroMsg = [];
93
+ this.projectConfig = { pluginRoot: '', compileType: '' };
94
+ this.pluginInfo = {
95
+ pluginRoot: '',
96
+ pluginName: '',
97
+ pages: new Set(),
98
+ pagesMap: new Map(),
99
+ publicComponents: new Set(),
100
+ entryFilePath: '',
101
+ };
86
102
  this.init();
87
103
  }
88
104
  init() {
89
105
  console.log(helper_1.chalk.green('开始代码转换...'));
90
- this.initConvert();
91
- this.getApp();
92
- this.getPages();
93
- this.getSitemapLocation();
94
- this.getSubPackages();
106
+ try {
107
+ this.initConvert();
108
+ this.getApp();
109
+ this.getPages();
110
+ this.getSitemapLocation();
111
+ this.getSubPackages();
112
+ }
113
+ catch (error) {
114
+ throw new Error(`初始化失败 ${(0, util_1.getLineBreak)()} ${error.message}`);
115
+ }
95
116
  }
96
117
  initConvert() {
97
- if (helper_1.fs.existsSync(this.convertRoot)) {
98
- (0, helper_1.emptyDirectory)(this.convertRoot, { excludes: ['node_modules'] });
118
+ try {
119
+ // 清空taroConvert目录,保留taroConvert下node_modules目录
120
+ if (helper_1.fs.existsSync(this.convertRoot)) {
121
+ (0, helper_1.emptyDirectory)(this.convertRoot, { excludes: ['node_modules'] });
122
+ }
123
+ else {
124
+ helper_1.fs.ensureDirSync(this.convertRoot);
125
+ }
126
+ // 转换自定义配置文件,如:tsconfig.json
127
+ this.convertSelfDefinedConfig();
128
+ // 创建.convert目录,存放转换中间数据,如日志数据
129
+ (0, util_1.generateDir)(path.join(this.convertRoot, '.convert'));
130
+ global_1.globals.logFilePath = path.join(this.convertRoot, '.convert', 'convert.log');
131
+ // 读取convert.config.json配置文件
132
+ this.getConvertConfig();
133
+ // 读取project.config.json文件
134
+ this.parseProjectConfig();
135
+ // 解析插件的配置信息
136
+ if (this.projectConfig.compileType === "plugin" /* Constants.PLUGIN */) {
137
+ this.parsePluginConfig(this.pluginInfo);
138
+ }
99
139
  }
100
- else {
101
- helper_1.fs.ensureDirSync(this.convertRoot);
140
+ catch (error) {
141
+ throw new Error(`初始化convert失败 ${(0, util_1.getLineBreak)()} ${error.message}`);
102
142
  }
103
143
  }
104
- parseAst({ ast, sourceFilePath, outputFilePath, importStylePath, depComponents, imports = [] }) {
144
+ /**
145
+ * 遍历AST,为元素或方法可能为undefined的数据添加可选链操作符
146
+ * @param ast
147
+ */
148
+ convertToOptional(ast) {
149
+ // 需要添加可选链运算符的数据
150
+ const optionalData = new Set();
151
+ const thisData = new Set();
152
+ (0, traverse_1.default)(ast, {
153
+ ObjectProperty(astPath) {
154
+ // xxx({ data: {...} }),获取data属性中符合的数据
155
+ const node = astPath.node;
156
+ const key = node.key;
157
+ if (!t.isIdentifier(key) || key.name !== 'data') {
158
+ return;
159
+ }
160
+ const value = node.value;
161
+ if (!t.isObjectExpression(value)) {
162
+ return;
163
+ }
164
+ const properties = value.properties;
165
+ properties.forEach((property) => {
166
+ if (!t.isObjectProperty(property)) {
167
+ return;
168
+ }
169
+ const key = property.key;
170
+ if (!t.isIdentifier(key)) {
171
+ return;
172
+ }
173
+ const data = key.name;
174
+ thisData.add(data);
175
+ // 数据初始化为undefined、空字符串''、空数组[],收集
176
+ const assign = property.value;
177
+ const isUndefined = t.isIdentifier(assign) && assign.name === 'undefined';
178
+ const isEmptyString = t.isStringLiteral(assign) && assign.value === '';
179
+ const isEmptyArray = t.isArrayExpression(assign) && assign.elements.length === 0;
180
+ if (isUndefined || isEmptyString || isEmptyArray) {
181
+ optionalData.add(data);
182
+ }
183
+ });
184
+ },
185
+ CallExpression(astPath) {
186
+ // 用setData进行初始化的数据
187
+ const node = astPath.node;
188
+ const callee = node.callee;
189
+ if (!t.isMemberExpression(callee)) {
190
+ return;
191
+ }
192
+ const property = callee.property;
193
+ if (!t.isIdentifier(property) || property.name !== 'setData') {
194
+ return;
195
+ }
196
+ if (node.arguments.length === 0) {
197
+ return;
198
+ }
199
+ const arg = node.arguments[0];
200
+ // 除去data中的数据,setData中其余全部的数据都收集
201
+ if (arg.type === 'ObjectExpression') {
202
+ arg.properties.forEach((property) => {
203
+ if (!t.isObjectProperty(property)) {
204
+ return;
205
+ }
206
+ const key = property.key;
207
+ if (!t.isIdentifier(key)) {
208
+ return;
209
+ }
210
+ const data = key.name;
211
+ if (thisData.has(data)) {
212
+ return;
213
+ }
214
+ optionalData.add(data);
215
+ });
216
+ }
217
+ },
218
+ ClassBody(astPath) {
219
+ astPath.traverse({
220
+ MemberExpression(path) {
221
+ var _a, _b, _c;
222
+ // 遇到成员表达式,抽取表达式的来源数据
223
+ const code = path.toString();
224
+ const optionMatch = (_a = code.match(/^(.*?)\./)) === null || _a === void 0 ? void 0 : _a[1];
225
+ let data;
226
+ if (optionMatch) {
227
+ const computedMatch = (_b = optionMatch.match(/^(.*?)\[/)) === null || _b === void 0 ? void 0 : _b[1];
228
+ data = computedMatch || optionMatch;
229
+ }
230
+ else {
231
+ const computedMatch = (_c = code.match(/^(.*?)\[/)) === null || _c === void 0 ? void 0 : _c[1];
232
+ if (!computedMatch) {
233
+ return;
234
+ }
235
+ data = computedMatch;
236
+ }
237
+ // 如果数据不需要添加可选链操作符,返回
238
+ if (!optionalData.has(data)) {
239
+ return;
240
+ }
241
+ // 利用正则表达式匹配,添加可选链操作符
242
+ const parentPath = path.parentPath;
243
+ if (parentPath.isCallExpression()) {
244
+ path.replaceWithSourceString(code.replace(/\./g, '?.').replace(/\[/g, '?.['));
245
+ const callee = parentPath.node.callee;
246
+ const args = parentPath.node.arguments;
247
+ parentPath.replaceWith(t.optionalCallExpression(callee, args, false));
248
+ }
249
+ else {
250
+ path.replaceWithSourceString(code.replace(/\./g, '?.').replace(/\[/g, '?.['));
251
+ }
252
+ },
253
+ });
254
+ },
255
+ });
256
+ }
257
+ parseAst({ ast, sourceFilePath, outputFilePath, importStylePath, depComponents, imports = [], pluginComponents, }) {
105
258
  const scriptFiles = new Set();
106
259
  // eslint-disable-next-line @typescript-eslint/no-this-alias
107
260
  const self = this;
261
+ // 转换后js页面的所有自定义标签
262
+ const scriptComponents = [];
263
+ // js页面所有的导入模块
264
+ const scriptImports = [];
108
265
  let componentClassName;
109
266
  let needInsertImportTaro = false;
267
+ let hasCacheOptionsRequired = false;
268
+ let hasDatasetRequired = false;
269
+ const set = new Set();
110
270
  (0, traverse_1.default)(ast, {
111
271
  Program: {
112
272
  enter(astPath) {
@@ -122,10 +282,10 @@ class Convertor {
122
282
  astPath.traverse({
123
283
  JSXElement() {
124
284
  isTaroComponent = true;
125
- }
285
+ },
126
286
  });
127
287
  }
128
- }
288
+ },
129
289
  });
130
290
  if (isTaroComponent) {
131
291
  componentClassName = ((_a = node.id) === null || _a === void 0 ? void 0 : _a.name) || '';
@@ -133,6 +293,7 @@ class Convertor {
133
293
  }
134
294
  },
135
295
  ClassExpression(astPath) {
296
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析ClassExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
136
297
  const node = astPath.node;
137
298
  if (node.superClass) {
138
299
  let isTaroComponent = false;
@@ -142,10 +303,10 @@ class Convertor {
142
303
  astPath.traverse({
143
304
  JSXElement() {
144
305
  isTaroComponent = true;
145
- }
306
+ },
146
307
  });
147
308
  }
148
- }
309
+ },
149
310
  });
150
311
  if (isTaroComponent) {
151
312
  if (node.id === null) {
@@ -173,10 +334,10 @@ class Convertor {
173
334
  astPath.traverse({
174
335
  JSXElement() {
175
336
  isTaroComponent = true;
176
- }
337
+ },
177
338
  });
178
339
  }
179
- }
340
+ },
180
341
  });
181
342
  if (isTaroComponent) {
182
343
  componentClassName = declaration.id.name;
@@ -188,40 +349,234 @@ class Convertor {
188
349
  const node = astPath.node;
189
350
  const source = node.source;
190
351
  const value = source.value;
191
- (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, source, value);
352
+ (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, source, value, self.isTsProject, self.pluginInfo.pluginName);
353
+ // 获取导入语句中的所有导入名称(importName)并将其添加到scriptImports里面
354
+ const specifiers = node.specifiers;
355
+ specifiers.forEach((specifier) => {
356
+ const importName = specifier.local.name;
357
+ scriptImports.push(importName);
358
+ });
192
359
  },
193
360
  CallExpression(astPath) {
361
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析CallExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
194
362
  const node = astPath.node;
195
363
  const calleePath = astPath.get('callee');
196
364
  const callee = calleePath.node;
365
+ const args = astPath.get('arguments');
197
366
  if (callee.type === 'Identifier') {
198
367
  if (callee.name === 'require') {
199
368
  const args = node.arguments;
369
+ if (args.length === 0) {
370
+ // require()
371
+ return;
372
+ }
373
+ if (!t.isStringLiteral(args[0])) {
374
+ // require 暂不支持动态导入,如require('aa' + aa),后续收录到报告中
375
+ throw new Error(`require暂不支持动态导入, filePath: ${sourceFilePath}, context: ${astPath}`);
376
+ }
200
377
  const value = args[0].value;
201
- (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, args[0], value);
378
+ (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, args[0], value, self.isTsProject, self.pluginInfo.pluginName);
202
379
  }
203
380
  else if (WX_GLOBAL_FN.has(callee.name)) {
204
381
  calleePath.replaceWith(t.memberExpression(t.identifier('Taro'), callee));
205
382
  needInsertImportTaro = true;
206
383
  }
384
+ else if (callee.name === 'Page' || callee.name === 'Component' || callee.name === 'App') {
385
+ // 将 App() Page() Component() 改为 cacheOptions.setOptionsToCache() 的形式去初始化页面数据
386
+ const arg = astPath.get('arguments')[0];
387
+ const cacheOptionsAstNode = t.callExpression(t.memberExpression(t.identifier('cacheOptions'), t.identifier('setOptionsToCache')), [arg.node]);
388
+ astPath.replaceWith(cacheOptionsAstNode);
389
+ // 创建导入 cacheOptions 对象的 ast 节点
390
+ const requireCacheOptionsAst = t.variableDeclaration('const', [
391
+ t.variableDeclarator(t.objectPattern([
392
+ t.objectProperty(t.identifier('cacheOptions'), t.identifier('cacheOptions'), false, true),
393
+ ]), t.callExpression(t.identifier('require'), [t.stringLiteral('@tarojs/with-weapp')])),
394
+ ]);
395
+ // 若已经引入过 cacheOptions 则不在引入,防止重复引入问题
396
+ if (!hasCacheOptionsRequired) {
397
+ ast.program.body.unshift(requireCacheOptionsAst);
398
+ hasCacheOptionsRequired = true;
399
+ }
400
+ }
401
+ else if (callee.name === 'requirePlugin') {
402
+ if (args.length === 1 && args[0].isStringLiteral()) {
403
+ // 在小程序中调用plugin中模块,相对路径需要使用转换后的
404
+ const pluginEntryFilePathTrans = self.pluginInfo.entryFilePath.replace(self.pluginInfo.pluginRoot, path.join(self.convertDir, self.pluginInfo.pluginName));
405
+ const sourceFilePathTarns = self.getDistFilePath(sourceFilePath);
406
+ const newRequire = t.callExpression(t.identifier('require'), [
407
+ t.stringLiteral((0, helper_1.normalizePath)(path.relative(path.dirname(sourceFilePathTarns), pluginEntryFilePathTrans))),
408
+ ]);
409
+ astPath.replaceWith(newRequire);
410
+ }
411
+ }
207
412
  }
208
413
  },
209
414
  MemberExpression(astPath) {
210
415
  const node = astPath.node;
211
416
  const object = node.object;
417
+ const prettier = node.property;
212
418
  if (t.isIdentifier(object) && object.name === 'wx') {
213
419
  node.object = t.identifier('Taro');
214
420
  needInsertImportTaro = true;
215
421
  }
216
- }
422
+ else if (t.isIdentifier(prettier) && prettier.name === 'dataset') {
423
+ node.object = t.callExpression(t.identifier('getTarget'), [object, t.identifier('Taro')]);
424
+ // 创建导入 cacheOptions 对象的 ast 节点
425
+ if (!hasDatasetRequired) {
426
+ const requireCacheOptionsAst = t.variableDeclaration('const', [
427
+ t.variableDeclarator(t.objectPattern([
428
+ t.objectProperty(t.identifier('getTarget'), t.identifier('getTarget'), false, true),
429
+ ]), t.callExpression(t.identifier('require'), [t.stringLiteral('@tarojs/with-weapp')])),
430
+ ]);
431
+ ast.program.body.unshift(requireCacheOptionsAst);
432
+ hasDatasetRequired = true;
433
+ needInsertImportTaro = true;
434
+ }
435
+ }
436
+ },
437
+ OptionalMemberExpression(astPath) {
438
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析OptionalMemberExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
439
+ const node = astPath.node;
440
+ const object = node.object;
441
+ const prettier = node.property;
442
+ if (t.isIdentifier(prettier) && prettier.name === 'dataset') {
443
+ node.object = t.callExpression(t.identifier('getTarget'), [object, t.identifier('Taro')]);
444
+ // 创建导入 getTarget 对象的 ast 节点, 并且防止重复引用
445
+ if (!hasDatasetRequired) {
446
+ const requireCacheOptionsAst = t.variableDeclaration('const', [
447
+ t.variableDeclarator(t.objectPattern([
448
+ t.objectProperty(t.identifier('getTarget'), t.identifier('getTarget'), false, true),
449
+ ]), t.callExpression(t.identifier('require'), [t.stringLiteral('@tarojs/with-weapp')])),
450
+ ]);
451
+ ast.program.body.unshift(requireCacheOptionsAst);
452
+ hasDatasetRequired = true;
453
+ needInsertImportTaro = true;
454
+ }
455
+ }
456
+ },
457
+ // 获取js界面所有用到的自定义标签,不重复
458
+ JSXElement(astPath) {
459
+ const openingElement = astPath.get('openingElement');
460
+ const jsxName = openingElement.get('name');
461
+ if (jsxName.isJSXIdentifier()) {
462
+ const componentName = jsxName.node.name;
463
+ if (!util_1.DEFAULT_Component_SET.has(componentName) && scriptComponents.indexOf(componentName) === -1) {
464
+ // 比较引入组件名和标签名是否同名,若同名,则在组件名上加入后缀Component
465
+ if (scriptImports.includes(componentName)) {
466
+ jsxName.node.name = `${componentName}Component`;
467
+ }
468
+ scriptComponents.push(componentName);
469
+ }
470
+ if (/^\S(\S)*Tmpl$/.test(componentName)) {
471
+ const templateImport = imports.find((tmplImport) => tmplImport.name === `${componentName}`);
472
+ const templateFuncs = templateImport === null || templateImport === void 0 ? void 0 : templateImport.funcs;
473
+ if (templateFuncs && templateFuncs.size > 0) {
474
+ const attributes = openingElement.node.attributes;
475
+ templateFuncs.forEach((templateFunc) => {
476
+ const memberExpression = t.memberExpression(t.thisExpression(), t.identifier(templateFunc));
477
+ const value = t.jsxExpressionContainer(memberExpression);
478
+ const name = t.jsxIdentifier(templateFunc);
479
+ // 传递的方法插入到Tmpl标签属性中
480
+ attributes.push(t.jsxAttribute(name, value));
481
+ });
482
+ }
483
+ }
484
+ else if (componentName === 'Template') {
485
+ // 处理没被成功转换的模板, 如果被转换了就不会还是Template
486
+ const attrs = openingElement.get('attributes');
487
+ const is = attrs.find((attr) => t.isJSXAttribute(attr) &&
488
+ t.isJSXIdentifier(attr.get('name')) &&
489
+ t.isJSXAttribute(attr.node) &&
490
+ attr.node.name.name === 'is');
491
+ // 处理<template is=包含变量的情况(组件的动态名称)
492
+ if (is && t.isJSXAttribute(is.node)) {
493
+ const value = is.node.value;
494
+ if (value && t.isJSXExpressionContainer(value)) {
495
+ const expression = value.expression;
496
+ // 1、<template is={{var}}> 2、<template is="string{{var}}">
497
+ if (t.isIdentifier(expression) ||
498
+ (t.isBinaryExpression(expression) && expression.operator === '+')) {
499
+ // 加上map, template原名和新名字的映射
500
+ const componentMapList = [];
501
+ for (const order in imports) {
502
+ for (const key in imports[order]) {
503
+ if (key === 'tmplName') {
504
+ const tmplName = imports[order][key];
505
+ const tmplLastName = imports[order].name;
506
+ // imports去重可能会把map里的去掉, 所以要加回去
507
+ if (!scriptComponents.includes(tmplLastName)) {
508
+ scriptComponents.push(tmplLastName);
509
+ }
510
+ componentMapList.push(t.objectProperty(t.stringLiteral('' + tmplName), t.identifier(tmplLastName)));
511
+ }
512
+ }
513
+ }
514
+ const withWeappPath = astPath.findParent((p) => p.isClassDeclaration());
515
+ if (withWeappPath) {
516
+ const MapVariableDeclaration = t.variableDeclaration('const', [
517
+ t.variableDeclarator(t.identifier('ComponentMap'), t.objectExpression(componentMapList)),
518
+ ]);
519
+ withWeappPath.insertBefore(MapVariableDeclaration);
520
+ }
521
+ // 加上用map给新标签赋值
522
+ const returnPath = astPath.findParent((p) => p.isReturnStatement());
523
+ if (returnPath) {
524
+ const ComponentNameVariableDeclaration = t.variableDeclaration('let', [
525
+ t.variableDeclarator(t.identifier('ComponentName'), t.memberExpression(t.identifier('ComponentMap'), expression, true)),
526
+ ]);
527
+ returnPath.insertBefore(ComponentNameVariableDeclaration);
528
+ }
529
+ // 标签非Template的情况下不会加spread, 删除spread属性; 更改开闭合标签为ComponentName
530
+ const attributes = [];
531
+ const data = attrs.find((attr) => t.isJSXAttribute(attr) &&
532
+ t.isJSXIdentifier(attr.get('name')) &&
533
+ t.isJSXAttribute(attr.node) &&
534
+ attr.node.name.name === 'data');
535
+ if (data && t.isJSXAttribute(data.node)) {
536
+ attributes.push(data.node);
537
+ }
538
+ astPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('ComponentName'), attributes), t.jSXClosingElement(t.jSXIdentifier('ComponentName')), [], true));
539
+ }
540
+ }
541
+ }
542
+ }
543
+ }
544
+ },
545
+ // 处理this.data.xx = XXX 的情况,因为此表达式在taro暂不支持, 转为setData
546
+ AssignmentExpression(astPath) {
547
+ const node = astPath.node;
548
+ if (t.isMemberExpression(node.left) &&
549
+ t.isMemberExpression(node.left.object) &&
550
+ t.isThisExpression(node.left.object.object) &&
551
+ t.isIdentifier(node.left.object.property)) {
552
+ // 确认左边是this.data
553
+ if (node.left.object.property.name === 'data' && t.isIdentifier(node.left.property)) {
554
+ const lastName = node.left.property.name;
555
+ // 右边不能确定数据类型,所以直接存整个对象
556
+ const rightValue = node.right;
557
+ // 由于合并setData会导致打乱代码顺序, 影响代码逻辑, 所以每一句this.data.xx单独转成一句setData
558
+ const memberExp = t.memberExpression(t.thisExpression(), t.identifier('setData'));
559
+ const objExp = t.objectExpression([t.objectProperty(t.identifier(lastName), rightValue)]);
560
+ astPath.replaceWith(t.expressionStatement(t.callExpression(memberExp, [objExp])));
561
+ console.log(`转换 语法 this.data.xx=XX暂不支持,原地替换为setData()`);
562
+ }
563
+ }
564
+ },
217
565
  });
218
566
  },
219
567
  exit(astPath) {
220
568
  const bodyNode = astPath.get('body');
221
- const lastImport = bodyNode.filter(p => p.isImportDeclaration()).pop();
222
- const hasTaroImport = bodyNode.some(p => p.isImportDeclaration() && p.node.source.value === '@tarojs/taro');
223
- if (needInsertImportTaro && !hasTaroImport) {
224
- astPath.node.body.unshift(t.importDeclaration([t.importDefaultSpecifier(t.identifier('Taro'))], t.stringLiteral('@tarojs/taro')));
569
+ const lastImport = bodyNode.filter((p) => p.isImportDeclaration() || (0, astConvert_1.isCommonjsImport)(p)).pop();
570
+ if (needInsertImportTaro && !(0, astConvert_1.hasTaroImport)(bodyNode)) {
571
+ // 根据模块类型(commonjs/es6) 确定导入taro模块的类型
572
+ if ((0, astConvert_1.isCommonjsModule)(ast.program.body)) {
573
+ astPath.node.body.unshift(t.variableDeclaration('const', [
574
+ t.variableDeclarator(t.identifier('Taro'), t.callExpression(t.identifier('require'), [t.stringLiteral('@tarojs/taro')])),
575
+ ]));
576
+ }
577
+ else {
578
+ astPath.node.body.unshift(t.importDeclaration([t.importDefaultSpecifier(t.identifier('Taro'))], t.stringLiteral('@tarojs/taro')));
579
+ }
225
580
  }
226
581
  astPath.traverse({
227
582
  StringLiteral(astPath) {
@@ -238,7 +593,7 @@ class Convertor {
238
593
  const imageRelativePath = (0, helper_1.promoteRelativePath)(path.relative(sourceFilePath, sourceImagePath));
239
594
  const outputImagePath = self.getDistFilePath(sourceImagePath);
240
595
  if (helper_1.fs.existsSync(sourceImagePath)) {
241
- self.copyFileToTaro(sourceImagePath, outputImagePath);
596
+ (0, util_1.copyFileToTaro)(sourceImagePath, outputImagePath);
242
597
  (0, helper_1.printLog)("copy" /* processTypeEnum.COPY */, '图片', self.generateShowPath(outputImagePath));
243
598
  }
244
599
  else if (!t.isBinaryExpression(astPath.parent) || astPath.parent.operator !== '+') {
@@ -251,46 +606,222 @@ class Convertor {
251
606
  astPath.replaceWith(t.jSXExpressionContainer(t.callExpression(t.identifier('require'), [t.stringLiteral(imageRelativePath)])));
252
607
  }
253
608
  }
254
- }
609
+ },
255
610
  });
256
611
  if (lastImport) {
257
612
  if (importStylePath) {
258
- lastImport.insertAfter(t.importDeclaration([], t.stringLiteral((0, helper_1.promoteRelativePath)(path.relative(sourceFilePath, importStylePath)))));
613
+ if ((0, astConvert_1.isCommonjsModule)(ast.program.body)) {
614
+ lastImport.insertAfter(t.callExpression(t.identifier('require'), [
615
+ t.stringLiteral((0, helper_1.promoteRelativePath)(path.relative(sourceFilePath, importStylePath))),
616
+ ]));
617
+ }
618
+ else {
619
+ lastImport.insertAfter(t.importDeclaration([], t.stringLiteral((0, helper_1.promoteRelativePath)(path.relative(sourceFilePath, importStylePath)))));
620
+ }
259
621
  }
260
622
  if (imports && imports.length) {
261
623
  imports.forEach(({ name, ast, wxs }) => {
262
- const importName = wxs ? name : (0, helper_1.pascalCase)(name);
263
- if (componentClassName === importName) {
624
+ if (componentClassName === name) {
264
625
  return;
265
626
  }
266
- const importPath = path.join(self.importsDir, importName + (wxs ? self.wxsIncrementId() : '') + '.js');
627
+ const importPath = path.join(self.importsDir, name + (wxs ? self.wxsIncrementId() : '') + (self.isTsProject ? '.ts' : '.js'));
267
628
  if (!self.hadBeenBuiltImports.has(importPath)) {
268
629
  self.hadBeenBuiltImports.add(importPath);
269
630
  self.writeFileToTaro(importPath, prettier.format((0, astConvert_1.generateMinimalEscapeCode)(ast), prettierJSConfig));
270
631
  }
271
- lastImport.insertAfter((0, template_1.default)(`import ${importName} from '${(0, helper_1.promoteRelativePath)(path.relative(outputFilePath, importPath))}'`, babylonConfig)());
632
+ if (scriptComponents.indexOf(name) !== -1 || (wxs && wxs === true)) {
633
+ lastImport.insertAfter((0, template_1.default)(`import ${name} from '${(0, helper_1.promoteRelativePath)(path.relative(outputFilePath, importPath))}'`, babylonConfig)());
634
+ }
272
635
  });
273
636
  }
274
637
  if (depComponents && depComponents.size) {
275
- depComponents.forEach(componentObj => {
276
- const name = (0, helper_1.pascalCase)(componentObj.name);
277
- const component = componentObj.path;
278
- lastImport.insertAfter((0, template_1.default)(`import ${name} from '${(0, helper_1.promoteRelativePath)(path.relative(sourceFilePath, component))}'`, babylonConfig)());
638
+ depComponents.forEach((componentObj) => {
639
+ let name = (0, helper_1.pascalCase)(componentObj.name.toLowerCase());
640
+ // 如果不是js页面用到的组件,无需导入
641
+ if (scriptComponents.indexOf(name) === -1) {
642
+ return;
643
+ }
644
+ // 如果用到了,从scriptComponents中移除
645
+ const index = scriptComponents.indexOf(name);
646
+ scriptComponents.splice(index, 1);
647
+ // 同名的自定义组件名称加后缀区分后,其组件标签也要加上Component保持统一
648
+ if (scriptImports.includes(name)) {
649
+ name = `${name}Component`;
650
+ }
651
+ let componentPath = componentObj.path;
652
+ if (!componentPath.startsWith(self.root) && !componentPath.startsWith(self.pluginInfo.pluginRoot)) {
653
+ console.error(`exception: 无效的组件路径,componentPath: ${componentPath}, 请在${outputFilePath}中手动引入`);
654
+ return;
655
+ }
656
+ componentPath = path.relative(sourceFilePath, componentPath);
657
+ lastImport.insertAfter((0, template_1.default)(`import ${name} from '${(0, helper_1.promoteRelativePath)(componentPath)}'`, babylonConfig)());
279
658
  });
280
659
  }
660
+ // 页面中引用的插件组件增加引用信息
661
+ if (pluginComponents && pluginComponents.size) {
662
+ pluginComponents.forEach((pluginComponent) => {
663
+ const componentName = (0, helper_1.pascalCase)(pluginComponent.name.toLowerCase());
664
+ const componentPath = pluginComponent.path;
665
+ if (!componentPath.startsWith(self.pluginInfo.pluginRoot)) {
666
+ console.error(`exception: 在页面${sourceFilePath}引用了无效的插件组件路径${componentPath}, 请在${outputFilePath}中手动引入`);
667
+ return;
668
+ }
669
+ // 插件组件转换后的绝对路径
670
+ const conponentTransPath = componentPath.replace(self.pluginInfo.pluginRoot, path.join(self.convertDir, self.pluginInfo.pluginName));
671
+ // 由于插件转换后路径的变化,此处需根据转换后的路径获取相对路径
672
+ const componentRelPath = path.relative(self.getDistFilePath(sourceFilePath), conponentTransPath);
673
+ lastImport.insertAfter((0, template_1.default)(`import ${componentName} from '${(0, helper_1.promoteRelativePath)(componentRelPath)}'`, babylonConfig)());
674
+ });
675
+ }
676
+ }
677
+ },
678
+ },
679
+ });
680
+ this.convertToOptional(ast);
681
+ // 遍历 ast ,将多次 const { xxx } = require('@tarojs/with-weapp') 引入压缩为一次引入
682
+ (0, traverse_1.default)(ast, {
683
+ VariableDeclaration(astPath) {
684
+ const { kind, declarations } = astPath.node;
685
+ let currentAstIsWithWeapp = false;
686
+ if (kind === 'const') {
687
+ declarations.forEach((declaration) => {
688
+ const { id, init } = declaration;
689
+ if (t.isObjectPattern(id) &&
690
+ t.isCallExpression(init) &&
691
+ t.isIdentifier(init.callee) &&
692
+ t.isStringLiteral(init.arguments[0])) {
693
+ const name = init.callee.name;
694
+ const args = init.arguments[0].value;
695
+ if (name === 'require' && args === '@tarojs/with-weapp') {
696
+ currentAstIsWithWeapp = true;
697
+ id.properties.forEach((propertie) => {
698
+ if (t.isObjectProperty(propertie) && t.isIdentifier(propertie.value)) {
699
+ set.add(propertie.value.name);
700
+ }
701
+ });
702
+ }
703
+ }
704
+ });
705
+ }
706
+ if (currentAstIsWithWeapp) {
707
+ astPath.remove();
708
+ }
709
+ },
710
+ });
711
+ // 若 set 为空则不引入 @tarojs/with-weapp
712
+ if (set.size !== 0) {
713
+ if ((0, astConvert_1.isCommonjsModule)(ast.program.body)) {
714
+ const objectPropertyArray = [];
715
+ set.forEach((key) => {
716
+ if (key === 'withWeapp') {
717
+ objectPropertyArray.push(t.objectProperty(t.identifier('default'), t.identifier('withWeapp'), false, true));
281
718
  }
719
+ else {
720
+ objectPropertyArray.push(t.objectProperty(t.identifier(key), t.identifier(key), false, true));
721
+ }
722
+ });
723
+ const requireWithWeappAst = t.variableDeclaration('const', [
724
+ t.variableDeclarator(t.objectPattern(objectPropertyArray), t.callExpression(t.identifier('require'), [t.stringLiteral('@tarojs/with-weapp')])),
725
+ ]);
726
+ ast.program.body.unshift(requireWithWeappAst);
727
+ }
728
+ else {
729
+ const objectPropertyArray = [];
730
+ let hasWithWeapp = false;
731
+ set.forEach((key) => {
732
+ if (key === 'withWeapp') {
733
+ hasWithWeapp = true;
734
+ }
735
+ else {
736
+ objectPropertyArray.push(t.importSpecifier(t.identifier(key), t.identifier(key)));
737
+ }
738
+ });
739
+ if (hasWithWeapp) {
740
+ objectPropertyArray.unshift(t.importDefaultSpecifier(t.identifier('withWeapp')));
282
741
  }
742
+ const importWithWeapp = t.importDeclaration(objectPropertyArray, t.stringLiteral('@tarojs/with-weapp'));
743
+ ast.program.body.unshift(importWithWeapp);
283
744
  }
284
- });
745
+ }
285
746
  return {
286
747
  ast,
287
- scriptFiles
748
+ scriptFiles,
288
749
  };
289
750
  }
751
+ convertSelfDefinedConfig() {
752
+ // 搬运自定义的配置文件
753
+ const selfDefinedConfig = [];
754
+ // 目前只有tsconfig.json,还有的话继续加到array里
755
+ selfDefinedConfig[0] = `tsconfig${this.fileTypes.CONFIG}`;
756
+ for (const tempConfig of selfDefinedConfig) {
757
+ const tempConfigPath = path.join(this.root, tempConfig);
758
+ if (helper_1.fs.existsSync(tempConfig)) {
759
+ try {
760
+ const outputFilePath = path.join(this.convertRoot, tempConfig);
761
+ (0, util_1.copyFileToTaro)(tempConfigPath, outputFilePath);
762
+ }
763
+ catch (err) {
764
+ // 失败不退出,仅提示
765
+ console.log(helper_1.chalk.red(`tsconfig${this.fileTypes.CONFIG} 拷贝失败,请检查!`));
766
+ }
767
+ }
768
+ }
769
+ }
770
+ getConvertConfig() {
771
+ // 处理convert.config.json,并存储到convertConfig中
772
+ const convertJsonPath = path.join(this.root, `convert.config${this.fileTypes.CONFIG}`);
773
+ if (helper_1.fs.existsSync(convertJsonPath)) {
774
+ try {
775
+ const convertJson = JSON.parse(String(helper_1.fs.readFileSync(convertJsonPath)));
776
+ this.convertConfig = Object.assign({}, convertJson);
777
+ this.convertConfig.external = (0, util_1.transRelToAbsPath)(convertJsonPath, this.convertConfig.external);
778
+ }
779
+ catch (err) {
780
+ console.log(helper_1.chalk.red(`convert.config${this.fileTypes.CONFIG} 读取失败,请检查!`));
781
+ process.exit(1);
782
+ }
783
+ }
784
+ }
785
+ /**
786
+ * 解析project.config.json配置文件
787
+ *
788
+ */
789
+ parseProjectConfig() {
790
+ // 处理project.config.json,并存储到projectConfig中
791
+ const projectConfigFilePath = path.join(this.root, `project.config${this.fileTypes.CONFIG}`);
792
+ if (helper_1.fs.existsSync(projectConfigFilePath)) {
793
+ try {
794
+ const projectConfigJson = JSON.parse(String(helper_1.fs.readFileSync(projectConfigFilePath, 'utf8')));
795
+ if (projectConfigJson && projectConfigJson.compileType === "plugin" /* Constants.PLUGIN */) {
796
+ const pluginRoot = projectConfigJson.pluginRoot;
797
+ if (pluginRoot === '' || (0, shared_1.isNull)(pluginRoot) || (0, shared_1.isUndefined)(pluginRoot)) {
798
+ console.log('project.config,json中pluginRoot为空或未配置,请确认配置是否正确');
799
+ process.exit(1);
800
+ }
801
+ this.projectConfig = Object.assign({}, projectConfigJson);
802
+ this.pluginInfo.pluginRoot = path.join(this.root, projectConfigJson.pluginRoot.replace(/\/+$/, ''));
803
+ }
804
+ // 解析miniprogramRoot字段,如果存在则更新小程序root
805
+ if (projectConfigJson.miniprogramRoot) {
806
+ this.root = path.join(this.root, projectConfigJson.miniprogramRoot.replace(/\/+$/, ''));
807
+ }
808
+ }
809
+ catch (err) {
810
+ throw new Error(`project.config${this.fileTypes.CONFIG} 解析失败,请检查!`);
811
+ }
812
+ }
813
+ }
290
814
  getApp() {
291
- this.entryJSPath = path.join(this.root, `app${this.fileTypes.SCRIPT}`);
292
- this.entryJSONPath = path.join(this.root, `app${this.fileTypes.CONFIG}`);
293
- this.entryStylePath = path.join(this.root, `app${this.fileTypes.STYLE}`);
815
+ if (this.isTsProject) {
816
+ this.entryJSPath = path.join(this.miniprogramRoot, `app${this.fileTypes.SCRIPT}`);
817
+ this.entryJSONPath = path.join(this.miniprogramRoot, `app${this.fileTypes.CONFIG}`);
818
+ this.entryStylePath = path.join(this.miniprogramRoot, `app${this.fileTypes.STYLE}`);
819
+ }
820
+ else {
821
+ this.entryJSPath = path.join(this.root, `app${this.fileTypes.SCRIPT}`);
822
+ this.entryJSONPath = path.join(this.root, `app${this.fileTypes.CONFIG}`);
823
+ this.entryStylePath = path.join(this.root, `app${this.fileTypes.STYLE}`);
824
+ }
294
825
  try {
295
826
  this.entryJSON = JSON.parse(String(helper_1.fs.readFileSync(this.entryJSONPath)));
296
827
  const using = this.entryJSON.usingComponents;
@@ -299,11 +830,18 @@ class Convertor {
299
830
  if (using[key].startsWith('plugin://'))
300
831
  continue;
301
832
  const componentPath = using[key];
302
- using[key] = path.join(this.root, componentPath);
833
+ // 非三方库的路径需要处理
834
+ if (!this.isThirdPartyLib(componentPath, this.root)) {
835
+ using[key] = path.join(this.root, componentPath);
836
+ }
303
837
  }
304
838
  this.entryUsingComponents = using;
305
839
  delete this.entryJSON.usingComponents;
306
840
  }
841
+ // 当小程序中包含plugin时,从app.json中解析pluginName,当前只支持一个plugin
842
+ if (this.projectConfig && this.projectConfig.compileType === "plugin" /* Constants.PLUGIN */) {
843
+ this.parsePluginName(this.entryJSON);
844
+ }
307
845
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '入口文件', this.generateShowPath(this.entryJSPath));
308
846
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '入口配置', this.generateShowPath(this.entryJSONPath));
309
847
  if (helper_1.fs.existsSync(this.entryStylePath)) {
@@ -317,6 +855,21 @@ class Convertor {
317
855
  process.exit(1);
318
856
  }
319
857
  }
858
+ /**
859
+ * 从app.json中解析pluginName,当前只支持一个plugin
860
+ *
861
+ * @param app.json
862
+ */
863
+ parsePluginName(entryJSON) {
864
+ const plugins = entryJSON.plugins;
865
+ if (plugins && Object.keys(plugins).length) {
866
+ this.pluginInfo.pluginName = Object.keys(plugins)[0];
867
+ }
868
+ else {
869
+ console.log('当前应用没有注册插件,请检查app.json中的plugins字段是否配置正确');
870
+ process.exit(1);
871
+ }
872
+ }
320
873
  getPages() {
321
874
  const pages = this.entryJSON.pages;
322
875
  if (!pages || !pages.length) {
@@ -330,10 +883,10 @@ class Convertor {
330
883
  if (!subPackages || !subPackages.length) {
331
884
  return;
332
885
  }
333
- subPackages.forEach(item => {
886
+ subPackages.forEach((item) => {
334
887
  if (item.pages && item.pages.length) {
335
888
  const root = item.root;
336
- item.pages.forEach(page => {
889
+ item.pages.forEach((page) => {
337
890
  let pagePath = `${root}/${page}`;
338
891
  pagePath = pagePath.replace(/\/{2,}/g, '/');
339
892
  this.pages.add(pagePath);
@@ -348,7 +901,7 @@ class Convertor {
348
901
  const sitemapFilePath = path.join(this.root, sitemapLocation);
349
902
  if (helper_1.fs.existsSync(sitemapFilePath)) {
350
903
  const outputFilePath = path.join(this.convertRoot, sitemapLocation);
351
- this.copyFileToTaro(sitemapFilePath, outputFilePath);
904
+ (0, util_1.copyFileToTaro)(sitemapFilePath, outputFilePath);
352
905
  }
353
906
  }
354
907
  }
@@ -357,32 +910,50 @@ class Convertor {
357
910
  return;
358
911
  }
359
912
  if (files.size) {
360
- files.forEach(file => {
913
+ files.forEach((file) => {
914
+ var _a, _b;
915
+ // 处理三方库引用,可在convert.config.json中nodePath字段自定义配置配置,默认node_modules
916
+ if (!path.isAbsolute(file)) {
917
+ (0, util_1.handleThirdPartyLib)(file, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.nodePath, this.root, this.convertRoot);
918
+ return;
919
+ }
361
920
  if (!helper_1.fs.existsSync(file) || this.hadBeenCopyedFiles.has(file)) {
362
921
  return;
363
922
  }
364
- const code = helper_1.fs.readFileSync(file).toString();
365
- let outputFilePath = file.replace(this.root, this.convertDir);
366
- const extname = path.extname(outputFilePath);
367
- if (/\.wxs/.test(extname)) {
368
- outputFilePath += '.js';
369
- }
370
- const transformResult = (0, transformer_wx_1.default)({
371
- code,
372
- sourcePath: file,
373
- isNormal: true,
374
- isTyped: helper_1.REG_TYPESCRIPT.test(file)
375
- });
376
- const { ast, scriptFiles } = this.parseAst({
377
- ast: transformResult.ast,
378
- outputFilePath,
379
- sourceFilePath: file
380
- });
381
- const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
382
- this.writeFileToTaro(outputFilePath, prettier.format(jsCode, prettierJSConfig));
383
- (0, helper_1.printLog)("copy" /* processTypeEnum.COPY */, 'JS 文件', this.generateShowPath(outputFilePath));
384
- this.hadBeenCopyedFiles.add(file);
385
- this.generateScriptFiles(scriptFiles);
923
+ // 处理不转换的目录,可在convert.config.json中external字段配置
924
+ const matchUnconvertDir = (0, util_1.getMatchUnconvertDir)(file, (_b = this.convertConfig) === null || _b === void 0 ? void 0 : _b.external);
925
+ if (matchUnconvertDir !== null) {
926
+ (0, util_1.handleUnconvertDir)(matchUnconvertDir, this.root, this.convertDir);
927
+ return;
928
+ }
929
+ try {
930
+ const code = helper_1.fs.readFileSync(file).toString();
931
+ let outputFilePath = this.getDistFilePath(file);
932
+ const extname = path.extname(outputFilePath);
933
+ if (/\.wxs/.test(extname)) {
934
+ outputFilePath += '.js';
935
+ }
936
+ const transformResult = (0, transformer_wx_1.default)({
937
+ code,
938
+ sourcePath: file,
939
+ isNormal: true,
940
+ isTyped: helper_1.REG_TYPESCRIPT.test(file),
941
+ logFilePath: global_1.globals.logFilePath,
942
+ });
943
+ const { ast, scriptFiles } = this.parseAst({
944
+ ast: transformResult.ast,
945
+ outputFilePath,
946
+ sourceFilePath: file,
947
+ });
948
+ const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
949
+ this.writeFileToTaro(outputFilePath, prettier.format(jsCode, prettierJSConfig));
950
+ (0, helper_1.printLog)("copy" /* processTypeEnum.COPY */, 'JS 文件', this.generateShowPath(outputFilePath));
951
+ this.hadBeenCopyedFiles.add(file);
952
+ this.generateScriptFiles(scriptFiles);
953
+ }
954
+ catch (error) {
955
+ console.log(`转换文件${file}异常,errorMessage:${error}`);
956
+ }
386
957
  });
387
958
  }
388
959
  }
@@ -390,19 +961,29 @@ class Convertor {
390
961
  helper_1.fs.ensureDirSync(path.dirname(dist));
391
962
  helper_1.fs.writeFileSync(dist, code);
392
963
  }
393
- copyFileToTaro(from, to, options) {
394
- const filename = path.basename(from);
395
- if (helper_1.fs.statSync(from).isFile() && !path.extname(to)) {
396
- helper_1.fs.ensureDir(to);
397
- return helper_1.fs.copySync(from, path.join(to, filename), options);
398
- }
399
- helper_1.fs.ensureDir(path.dirname(to));
400
- return helper_1.fs.copySync(from, to, options);
964
+ // 自定义组件,如果组件文件命名为index,引入时可省略index这一层,解析时需加上
965
+ getComponentPath(component, extname) {
966
+ if (helper_1.fs.existsSync(component + extname))
967
+ return component + extname;
968
+ else
969
+ return component + '/index' + extname;
401
970
  }
971
+ /**
972
+ * 根据源文件路径获取转换后文件路径
973
+ *
974
+ * @param { string } src 源文件路径
975
+ * @param { 文件后缀 } extname
976
+ * @returns { string } 转换后文件路径
977
+ */
402
978
  getDistFilePath(src, extname) {
403
- if (!extname)
404
- return src.replace(this.root, this.convertDir);
405
- return src.replace(this.root, this.convertDir).replace(path.extname(src), extname);
979
+ let filePath;
980
+ if (this.isTraversePlugin) {
981
+ filePath = src.replace(this.pluginInfo.pluginRoot, path.join(this.convertDir, this.pluginInfo.pluginName));
982
+ }
983
+ else {
984
+ filePath = src.replace(this.root, this.convertDir);
985
+ }
986
+ return extname ? filePath.replace(path.extname(src), extname) : filePath;
406
987
  }
407
988
  getConfigFilePath(src) {
408
989
  const { dir, name } = path.parse(src);
@@ -414,15 +995,12 @@ class Convertor {
414
995
  this.writeFileToTaro(configSrc, prettier.format(code, prettierJSConfig));
415
996
  }
416
997
  generateShowPath(filePath) {
417
- return filePath
418
- .replace(path.join(this.root, '/'), '')
419
- .split(path.sep)
420
- .join('/');
998
+ return filePath.replace(path.join(this.root, '/'), '').split(path.sep).join('/');
421
999
  }
422
1000
  formatFile(jsCode, template = '') {
423
1001
  let code = jsCode;
424
1002
  const config = Object.assign({}, prettierJSConfig);
425
- if (this.framework === 'vue') {
1003
+ if (this.framework === "Vue" /* FrameworkType.Vue */) {
426
1004
  code = `
427
1005
  ${template}
428
1006
  <script>
@@ -438,15 +1016,17 @@ ${code}
438
1016
  generateEntry() {
439
1017
  try {
440
1018
  const entryJS = String(helper_1.fs.readFileSync(this.entryJSPath));
441
- const entryJSON = JSON.stringify(this.entryJSON);
1019
+ let entryJSON = JSON.stringify(this.entryJSON);
442
1020
  const entryDistJSPath = this.getDistFilePath(this.entryJSPath);
443
1021
  const taroizeResult = taroize({
444
1022
  json: entryJSON,
445
1023
  script: entryJS,
1024
+ scriptPath: this.entryJSPath,
446
1025
  path: this.root,
447
1026
  rootPath: this.root,
448
1027
  framework: this.framework,
449
- isApp: true
1028
+ isApp: true,
1029
+ logFilePath: global_1.globals.logFilePath,
450
1030
  });
451
1031
  const { ast, scriptFiles } = this.parseAst({
452
1032
  ast: taroizeResult.ast,
@@ -455,8 +1035,12 @@ ${code}
455
1035
  importStylePath: this.entryStyle
456
1036
  ? this.entryStylePath.replace(path.extname(this.entryStylePath), OUTPUT_STYLE_EXTNAME)
457
1037
  : null,
458
- isApp: true
1038
+ isApp: true,
459
1039
  });
1040
+ // 将插件信息转换为子包信息添加到入口配置文件中
1041
+ if (this.projectConfig.compileType === "plugin" /* Constants.PLUGIN */) {
1042
+ entryJSON = this.addSubpackages(this.entryJSON);
1043
+ }
460
1044
  const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
461
1045
  this.writeFileToTaro(entryDistJSPath, jsCode);
462
1046
  this.writeFileToConfig(entryDistJSPath, entryJSON);
@@ -474,11 +1058,38 @@ ${code}
474
1058
  console.log(err);
475
1059
  }
476
1060
  }
1061
+ /**
1062
+ * 将plugin信息转换为subpackage并添加到入口配置文件中
1063
+ *
1064
+ * @param entryJSON
1065
+ * @returns
1066
+ */
1067
+ addSubpackages(entryJSON) {
1068
+ // 删除plugins字段
1069
+ if (entryJSON && entryJSON.plugins) {
1070
+ delete entryJSON.plugins;
1071
+ }
1072
+ const subPackageInfo = {
1073
+ root: `${this.pluginInfo.pluginName}/`,
1074
+ pages: [...this.pluginInfo.pages],
1075
+ };
1076
+ // 子包的字段可以为subpackages 或 subPackages
1077
+ if (entryJSON.subpackages) {
1078
+ entryJSON.subpackages.push(subPackageInfo);
1079
+ }
1080
+ else if (entryJSON.subPackages) {
1081
+ entryJSON.subPackages.push(subPackageInfo);
1082
+ }
1083
+ else {
1084
+ entryJSON.subPackages = [subPackageInfo];
1085
+ }
1086
+ return JSON.stringify(entryJSON);
1087
+ }
477
1088
  generateTabBarIcon(tabBar) {
478
1089
  const { list = [] } = tabBar;
479
1090
  const icons = new Set();
480
1091
  if (Array.isArray(list) && list.length) {
481
- list.forEach(item => {
1092
+ list.forEach((item) => {
482
1093
  if (typeof item.iconPath === 'string')
483
1094
  icons.add(item.iconPath);
484
1095
  if (typeof item.selectedIconPath === 'string')
@@ -486,10 +1097,10 @@ ${code}
486
1097
  });
487
1098
  if (icons.size > 0) {
488
1099
  Array.from(icons)
489
- .map(icon => path.join(this.root, icon))
490
- .forEach(iconPath => {
1100
+ .map((icon) => path.join(this.root, icon))
1101
+ .forEach((iconPath) => {
491
1102
  const iconDistPath = this.getDistFilePath(iconPath);
492
- this.copyFileToTaro(iconPath, iconDistPath);
1103
+ (0, util_1.copyFileToTaro)(iconPath, iconDistPath);
493
1104
  (0, helper_1.printLog)("copy" /* processTypeEnum.COPY */, 'TabBar 图标', this.generateShowPath(iconDistPath));
494
1105
  });
495
1106
  }
@@ -501,26 +1112,68 @@ ${code}
501
1112
  const customTabbarPath = path.join(this.root, 'custom-tab-bar');
502
1113
  if (helper_1.fs.existsSync(customTabbarPath)) {
503
1114
  const customTabbarDistPath = this.getDistFilePath(customTabbarPath);
504
- this.copyFileToTaro(customTabbarPath, customTabbarDistPath);
1115
+ (0, util_1.copyFileToTaro)(customTabbarPath, customTabbarDistPath);
505
1116
  (0, helper_1.printLog)("copy" /* processTypeEnum.COPY */, '自定义 TabBar', this.generateShowPath(customTabbarDistPath));
506
1117
  }
507
1118
  }
508
1119
  getComponentDest(file) {
509
- if (this.framework === 'react') {
1120
+ if (this.framework === "React" /* FrameworkType.React */) {
510
1121
  return file;
511
1122
  }
512
1123
  return path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.vue');
513
1124
  }
514
- traversePages() {
515
- this.pages.forEach(page => {
516
- const pagePath = path.join(this.root, page);
1125
+ // 判断是否是三方库
1126
+ isThirdPartyLib(modulePath, curPageDir) {
1127
+ // 相对路径和带根目录的路径都不是三方库
1128
+ if (modulePath.indexOf('.') === 0 || modulePath.indexOf('/') === 0 || modulePath.indexOf(this.root) !== -1) {
1129
+ return false;
1130
+ }
1131
+ // 通过格式如component/component引用组件
1132
+ // app.json中引用组件
1133
+ if (curPageDir === this.root) {
1134
+ if (helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(path.join(curPageDir, modulePath)))) {
1135
+ return false;
1136
+ }
1137
+ }
1138
+ else {
1139
+ // 页面中引用组件
1140
+ if (helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(path.join(curPageDir, modulePath))) ||
1141
+ helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(path.join(this.root, modulePath)))) {
1142
+ return false;
1143
+ }
1144
+ }
1145
+ return true;
1146
+ }
1147
+ // 判断三方库是否安装
1148
+ isInNodeModule(modulePath) {
1149
+ const nodeModules = path.resolve(this.root, 'node_modules');
1150
+ if (!helper_1.fs.existsSync(nodeModules)) {
1151
+ return false;
1152
+ }
1153
+ const modules = helper_1.fs.readdirSync(nodeModules);
1154
+ const parts = modulePath.split('/');
1155
+ if (modules.indexOf(parts[0]) === -1) {
1156
+ return false;
1157
+ }
1158
+ return true;
1159
+ }
1160
+ traversePages(root, pages) {
1161
+ pages.forEach((page) => {
1162
+ var _a;
1163
+ (0, util_1.printToLogFile)(`开始转换页面 ${page} ${(0, util_1.getLineBreak)()}`);
1164
+ const pagePath = this.isTsProject ? path.join(this.miniprogramRoot, page) : path.join(root, page);
1165
+ // 处理不转换的页面,可在convert.config.json中external字段配置
1166
+ const matchUnconvertDir = (0, util_1.getMatchUnconvertDir)(pagePath, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.external);
1167
+ if (matchUnconvertDir !== null) {
1168
+ (0, util_1.handleUnconvertDir)(matchUnconvertDir, root, this.convertDir);
1169
+ return;
1170
+ }
517
1171
  const pageJSPath = pagePath + this.fileTypes.SCRIPT;
518
1172
  const pageDistJSPath = this.getDistFilePath(pageJSPath);
519
1173
  const pageConfigPath = pagePath + this.fileTypes.CONFIG;
520
1174
  const pageStylePath = pagePath + this.fileTypes.STYLE;
521
1175
  const pageTemplPath = pagePath + this.fileTypes.TEMPL;
522
1176
  try {
523
- const depComponents = new Set();
524
1177
  if (!helper_1.fs.existsSync(pageJSPath)) {
525
1178
  throw new Error(`页面 ${page} 没有 JS 文件!`);
526
1179
  }
@@ -535,33 +1188,45 @@ ${code}
535
1188
  else if (this.entryUsingComponents) {
536
1189
  pageConfig = {};
537
1190
  }
1191
+ const depComponents = new Set();
1192
+ const pluginComponents = new Set();
538
1193
  if (pageConfig) {
539
- if (this.entryUsingComponents) {
1194
+ // app.json中注册的组件为公共组件
1195
+ if (this.entryUsingComponents && !this.isTraversePlugin) {
540
1196
  pageConfig.usingComponents = Object.assign(Object.assign({}, pageConfig.usingComponents), this.entryUsingComponents);
541
1197
  }
542
1198
  const pageUsingComponents = pageConfig.usingComponents;
543
1199
  if (pageUsingComponents) {
544
1200
  // 页面依赖组件
545
1201
  const usingComponents = {};
546
- Object.keys(pageUsingComponents).forEach(component => {
1202
+ Object.keys(pageUsingComponents).forEach((component) => {
1203
+ var _a;
547
1204
  const unResolveComponentPath = pageUsingComponents[component];
1205
+ let componentPath;
548
1206
  if (unResolveComponentPath.startsWith('plugin://')) {
549
- usingComponents[component] = unResolveComponentPath;
1207
+ componentPath = (0, util_1.replacePluginComponentUrl)(unResolveComponentPath, this.pluginInfo);
1208
+ pluginComponents.add({
1209
+ name: component,
1210
+ path: componentPath,
1211
+ });
1212
+ }
1213
+ else if (this.isThirdPartyLib(unResolveComponentPath, path.resolve(pagePath, '..'))) {
1214
+ (0, util_1.handleThirdPartyLib)(unResolveComponentPath, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.nodePath, root, this.convertRoot);
550
1215
  }
551
1216
  else {
552
- let componentPath;
553
- if (unResolveComponentPath.startsWith(this.root)) {
1217
+ if (unResolveComponentPath.startsWith(root)) {
554
1218
  componentPath = unResolveComponentPath;
555
1219
  }
556
1220
  else {
557
- componentPath = path.resolve(pageConfigPath, '..', pageUsingComponents[component]);
1221
+ componentPath = path.join(pageConfigPath, '..', pageUsingComponents[component]);
1222
+ // 支持将组件库放在工程根目录下
558
1223
  if (!helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(componentPath))) {
559
- componentPath = path.join(this.root, pageUsingComponents[component]);
1224
+ componentPath = path.join(root, pageUsingComponents[component]);
560
1225
  }
561
1226
  }
562
1227
  depComponents.add({
563
1228
  name: component,
564
- path: componentPath
1229
+ path: componentPath,
565
1230
  });
566
1231
  }
567
1232
  });
@@ -575,6 +1240,7 @@ ${code}
575
1240
  param.json = JSON.stringify(pageConfig);
576
1241
  }
577
1242
  param.script = String(helper_1.fs.readFileSync(pageJSPath));
1243
+ param.scriptPath = pageJSPath;
578
1244
  if (helper_1.fs.existsSync(pageTemplPath)) {
579
1245
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '页面模板', this.generateShowPath(pageTemplPath));
580
1246
  param.wxml = String(helper_1.fs.readFileSync(pageTemplPath));
@@ -585,18 +1251,22 @@ ${code}
585
1251
  pageStyle = String(helper_1.fs.readFileSync(pageStylePath));
586
1252
  }
587
1253
  param.path = path.dirname(pageJSPath);
588
- param.rootPath = this.root;
1254
+ param.rootPath = root;
1255
+ param.pluginInfo = this.pluginInfo;
1256
+ param.logFilePath = global_1.globals.logFilePath;
589
1257
  const taroizeResult = taroize(Object.assign(Object.assign({}, param), { framework: this.framework }));
590
1258
  const { ast, scriptFiles } = this.parseAst({
591
1259
  ast: taroizeResult.ast,
592
1260
  sourceFilePath: pageJSPath,
593
1261
  outputFilePath: pageDistJSPath,
594
1262
  importStylePath: pageStyle ? pageStylePath.replace(path.extname(pageStylePath), OUTPUT_STYLE_EXTNAME) : null,
595
- depComponents,
596
- imports: taroizeResult.imports
1263
+ depComponents: depComponents,
1264
+ imports: taroizeResult.imports,
1265
+ pluginComponents: pluginComponents,
597
1266
  });
598
1267
  const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
599
1268
  this.writeFileToTaro(this.getComponentDest(pageDistJSPath), this.formatFile(jsCode, taroizeResult.template));
1269
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, 'writeFileToTaro', this.generateShowPath(pageDistJSPath));
600
1270
  this.writeFileToConfig(pageDistJSPath, param.json);
601
1271
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '页面文件', this.generateShowPath(pageDistJSPath));
602
1272
  if (pageStyle) {
@@ -608,6 +1278,7 @@ ${code}
608
1278
  catch (err) {
609
1279
  (0, helper_1.printLog)("error" /* processTypeEnum.ERROR */, '页面转换', this.generateShowPath(pageJSPath));
610
1280
  console.log(err);
1281
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 转换页面异常 ${err.stack} ${(0, util_1.getLineBreak)()}`);
611
1282
  }
612
1283
  });
613
1284
  }
@@ -615,21 +1286,22 @@ ${code}
615
1286
  if (!components || !components.size) {
616
1287
  return;
617
1288
  }
618
- components.forEach(componentObj => {
1289
+ components.forEach((componentObj) => {
1290
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 开始转换组件 ${componentObj.path} ${(0, util_1.getLineBreak)()}`);
619
1291
  const component = componentObj.path;
620
1292
  if (this.hadBeenBuiltComponents.has(component))
621
1293
  return;
622
1294
  this.hadBeenBuiltComponents.add(component);
623
- const componentJSPath = component + this.fileTypes.SCRIPT;
1295
+ const componentJSPath = this.getComponentPath(component, this.fileTypes.SCRIPT);
624
1296
  const componentDistJSPath = this.getDistFilePath(componentJSPath);
625
- const componentConfigPath = component + this.fileTypes.CONFIG;
626
- const componentStylePath = component + this.fileTypes.STYLE;
627
- const componentTemplPath = component + this.fileTypes.TEMPL;
1297
+ const componentConfigPath = this.getComponentPath(component, this.fileTypes.CONFIG);
1298
+ const componentStylePath = this.getComponentPath(component, this.fileTypes.STYLE);
1299
+ const componentTemplPath = this.getComponentPath(component, this.fileTypes.TEMPL);
628
1300
  try {
629
1301
  const param = {};
630
1302
  const depComponents = new Set();
631
1303
  if (!helper_1.fs.existsSync(componentJSPath)) {
632
- throw new Error(`组件 ${component} 没有 JS 文件!`);
1304
+ throw new Error(`自定义组件 ${component} 没有 JS 文件!`);
633
1305
  }
634
1306
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '组件文件', this.generateShowPath(componentJSPath));
635
1307
  if (helper_1.fs.existsSync(componentConfigPath)) {
@@ -639,14 +1311,17 @@ ${code}
639
1311
  const componentUsingComponnets = componentConfig.usingComponents;
640
1312
  if (componentUsingComponnets) {
641
1313
  // 页面依赖组件
642
- Object.keys(componentUsingComponnets).forEach(component => {
1314
+ Object.keys(componentUsingComponnets).forEach((component) => {
643
1315
  let componentPath = path.resolve(componentConfigPath, '..', componentUsingComponnets[component]);
644
1316
  if (!helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(componentPath))) {
645
1317
  componentPath = path.join(this.root, componentUsingComponnets[component]);
646
1318
  }
1319
+ if (!helper_1.fs.existsSync(componentPath + this.fileTypes.SCRIPT)) {
1320
+ componentPath = path.join(componentPath, `/index`);
1321
+ }
647
1322
  depComponents.add({
648
1323
  name: component,
649
- path: componentPath
1324
+ path: componentPath,
650
1325
  });
651
1326
  });
652
1327
  delete componentConfig.usingComponents;
@@ -665,6 +1340,7 @@ ${code}
665
1340
  }
666
1341
  param.path = path.dirname(componentJSPath);
667
1342
  param.rootPath = this.root;
1343
+ param.logFilePath = global_1.globals.logFilePath;
668
1344
  const taroizeResult = taroize(Object.assign(Object.assign({}, param), { framework: this.framework }));
669
1345
  const { ast, scriptFiles } = this.parseAst({
670
1346
  ast: taroizeResult.ast,
@@ -674,7 +1350,7 @@ ${code}
674
1350
  ? componentStylePath.replace(path.extname(componentStylePath), OUTPUT_STYLE_EXTNAME)
675
1351
  : null,
676
1352
  depComponents,
677
- imports: taroizeResult.imports
1353
+ imports: taroizeResult.imports,
678
1354
  });
679
1355
  const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
680
1356
  this.writeFileToTaro(this.getComponentDest(componentDistJSPath), this.formatFile(jsCode, taroizeResult.template));
@@ -688,13 +1364,14 @@ ${code}
688
1364
  catch (err) {
689
1365
  (0, helper_1.printLog)("error" /* processTypeEnum.ERROR */, '组件转换', this.generateShowPath(componentJSPath));
690
1366
  console.log(err);
1367
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 转换组件异常 ${err.stack} ${(0, util_1.getLineBreak)()}`);
691
1368
  }
692
1369
  });
693
1370
  }
694
1371
  styleUnitTransform(filePath, content) {
695
1372
  return __awaiter(this, void 0, void 0, function* () {
696
1373
  const postcssResult = yield (0, postcss_1.default)([unitTransform()]).process(content, {
697
- from: filePath
1374
+ from: filePath,
698
1375
  });
699
1376
  return postcssResult;
700
1377
  });
@@ -706,10 +1383,7 @@ ${code}
706
1383
  styleDist = path.dirname(styleDist);
707
1384
  while (token === null || token === void 0 ? void 0 : token.length) {
708
1385
  let url = token[1];
709
- if (url &&
710
- url.indexOf('data:') !== 0 &&
711
- url.indexOf('#') !== 0 &&
712
- !(/^[a-z]+:\/\//.test(url))) {
1386
+ if (url && url.indexOf('data:') !== 0 && url.indexOf('#') !== 0 && !/^[a-z]+:\/\//.test(url)) {
713
1387
  url = url.trim();
714
1388
  url.replace(/[/\\]/g, path.sep);
715
1389
  url = url.split('?')[0];
@@ -731,6 +1405,7 @@ ${code}
731
1405
  }
732
1406
  traverseStyle(filePath, style) {
733
1407
  return __awaiter(this, void 0, void 0, function* () {
1408
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 开始转换样式 ${filePath} ${(0, util_1.getLineBreak)()}`);
734
1409
  const { imports, content } = processStyleImports(style, (str, stylePath) => {
735
1410
  let relativePath = stylePath;
736
1411
  if (path.isAbsolute(relativePath)) {
@@ -744,7 +1419,7 @@ ${code}
744
1419
  this.writeFileToTaro(styleDist, css);
745
1420
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '样式文件', this.generateShowPath(styleDist));
746
1421
  if (imports && imports.length) {
747
- imports.forEach(importItem => {
1422
+ imports.forEach((importItem) => {
748
1423
  const importPath = path.isAbsolute(importItem)
749
1424
  ? path.join(this.root, importItem)
750
1425
  : path.resolve(path.dirname(filePath), importItem);
@@ -756,67 +1431,109 @@ ${code}
756
1431
  }
757
1432
  });
758
1433
  }
1434
+ /**
1435
+ * 转换插件
1436
+ */
1437
+ traversePlugin() {
1438
+ if (this.projectConfig.compileType !== "plugin" /* Constants.PLUGIN */) {
1439
+ return;
1440
+ }
1441
+ this.isTraversePlugin = true;
1442
+ // 转换插件plugin.json中导出的页面
1443
+ this.traversePages(this.pluginInfo.pluginRoot, this.pluginInfo.pages);
1444
+ // 转换插件plugin.json中导出的组件
1445
+ this.traverseComponents(this.pluginInfo.publicComponents);
1446
+ // 转换插件的工具文件
1447
+ this.generateScriptFiles(new Set([this.pluginInfo.entryFilePath]));
1448
+ }
1449
+ /**
1450
+ * 解析插件配置信息
1451
+ *
1452
+ * @param pluginInfo
1453
+ */
1454
+ parsePluginConfig(pluginInfo) {
1455
+ // 处理plugin.json,并存储到pluginInfo中
1456
+ const pluginConfigPath = path.join(pluginInfo.pluginRoot, "plugin.json" /* Constants.PLUGIN_JSON */);
1457
+ if (helper_1.fs.existsSync(pluginConfigPath)) {
1458
+ try {
1459
+ const pluginConfigJson = JSON.parse(String(helper_1.fs.readFileSync(pluginConfigPath)));
1460
+ if (!pluginConfigJson) {
1461
+ console.log('插件配置信息为空,请检查!');
1462
+ return;
1463
+ }
1464
+ // 解析publicComponents信息
1465
+ const publicComponents = pluginConfigJson.publicComponents;
1466
+ if (publicComponents && Object.keys(publicComponents).length) {
1467
+ for (const key in publicComponents) {
1468
+ pluginInfo.publicComponents.add({
1469
+ name: key,
1470
+ path: path.join(pluginInfo.pluginRoot, publicComponents[key]),
1471
+ });
1472
+ }
1473
+ }
1474
+ // 解析pages信息
1475
+ const pages = pluginConfigJson.pages;
1476
+ if (pages && Object.keys(pages).length) {
1477
+ for (const pageName in pages) {
1478
+ const pagePath = pages[pageName];
1479
+ pluginInfo.pages.add(pagePath);
1480
+ pluginInfo.pagesMap.set(pageName, pagePath);
1481
+ }
1482
+ }
1483
+ // 解析入口文件信息
1484
+ const entryFilePath = pluginConfigJson.main;
1485
+ if (entryFilePath) {
1486
+ pluginInfo.entryFilePath = path.join(pluginInfo.pluginRoot, entryFilePath);
1487
+ }
1488
+ }
1489
+ catch (err) {
1490
+ console.log('解析plugin.json失败,请检查!');
1491
+ process.exit(1);
1492
+ }
1493
+ }
1494
+ }
759
1495
  generateConfigFiles() {
760
- const creator = new cli_1.Creator();
1496
+ const creator = new binding_1.Creator((0, cli_1.getRootPath)(), this.convertRoot);
1497
+ const dateObj = new Date();
1498
+ const date = `${dateObj.getFullYear()}-${dateObj.getMonth() + 1}-${dateObj.getDate()}`;
761
1499
  const templateName = 'default';
762
- const configDir = path.join(this.convertRoot, 'config');
763
- const pkgPath = path.join(this.convertRoot, 'package.json');
764
1500
  const projectName = 'taroConvert';
765
- const description = '';
766
1501
  const version = (0, util_1.getPkgVersion)();
767
- const dateObj = new Date();
768
- const date = `${dateObj.getFullYear()}-${dateObj.getMonth() + 1}-${dateObj.getDate()}`;
769
- creator.template(templateName, 'package.json.tmpl', pkgPath, {
1502
+ const description = '';
1503
+ const ps = [];
1504
+ const createOpts = {
1505
+ css: "Sass" /* CSSType.Sass */,
1506
+ cssExt: '.scss',
1507
+ framework: this.framework,
770
1508
  description,
771
1509
  projectName,
772
1510
  version,
773
- css: 'sass',
774
- typescript: false,
775
- template: templateName,
776
- framework: this.framework,
777
- compiler: 'webpack5'
778
- });
779
- creator.template(templateName, path.join('config', 'index.js'), path.join(configDir, 'index.js'), {
780
1511
  date,
781
- projectName,
782
- framework: this.framework,
783
- compiler: 'webpack5'
784
- });
785
- creator.template(templateName, path.join('config', 'dev.js'), path.join(configDir, 'dev.js'), {
786
- framework: this.framework
787
- });
788
- creator.template(templateName, path.join('config', 'prod.js'), path.join(configDir, 'prod.js'), {
789
- framework: this.framework
790
- });
791
- creator.template(templateName, 'project.config.json', path.join(this.convertRoot, 'project.config.json'), {
792
- description,
793
- projectName,
794
- framework: this.framework
795
- });
796
- creator.template(templateName, '.gitignore', path.join(this.convertRoot, '.gitignore'));
797
- creator.template(templateName, '.editorconfig', path.join(this.convertRoot, '.editorconfig'));
798
- creator.template(templateName, '.eslintrc.js', path.join(this.convertRoot, '.eslintrc.js'), {
799
1512
  typescript: false,
800
- framework: this.framework
801
- });
802
- creator.template(templateName, 'babel.config.js', path.join(this.convertRoot, 'babel.config.js'), {
803
- typescript: false,
804
- framework: this.framework
805
- });
806
- creator.template(templateName, path.join('src', 'index.html'), path.join(this.convertDir, 'index.html'), {
807
- projectName
808
- });
809
- creator.fs.commit(() => {
810
- const pkgObj = JSON.parse(helper_1.fs.readFileSync(pkgPath).toString());
1513
+ template: templateName,
1514
+ compiler: "Webpack5" /* CompilerType.Webpack5 */,
1515
+ };
1516
+ ps.push(creator.createFileFromTemplate(templateName, 'package.json.tmpl', 'package.json', createOpts));
1517
+ ps.push(creator.createFileFromTemplate(templateName, 'config/index.js', 'config/index.js', createOpts));
1518
+ ps.push(creator.createFileFromTemplate(templateName, 'config/dev.js', 'config/dev.js', createOpts));
1519
+ ps.push(creator.createFileFromTemplate(templateName, 'config/prod.js', 'config/prod.js', createOpts));
1520
+ ps.push(creator.createFileFromTemplate(templateName, 'project.config.json', 'project.config.json', createOpts));
1521
+ ps.push(creator.createFileFromTemplate(templateName, '.gitignore', '.gitignore', createOpts));
1522
+ ps.push(creator.createFileFromTemplate(templateName, '.editorconfig', '.editorconfig', createOpts));
1523
+ ps.push(creator.createFileFromTemplate(templateName, '.eslintrc.js', '.eslintrc.js', createOpts));
1524
+ ps.push(creator.createFileFromTemplate(templateName, 'babel.config.js', 'babel.config.js', createOpts));
1525
+ ps.push(creator.createFileFromTemplate(templateName, 'src/index.html', 'src/index.html', createOpts));
1526
+ Promise.all(ps).then(() => {
1527
+ const pkgObj = JSON.parse(helper_1.fs.readFileSync(path.join(this.convertRoot, 'package.json')).toString());
811
1528
  pkgObj.dependencies['@tarojs/with-weapp'] = `^${version}`;
812
- helper_1.fs.writeJSONSync(pkgPath, pkgObj, {
1529
+ helper_1.fs.writeJSONSync(path.join(this.convertRoot, 'package.json'), pkgObj, {
813
1530
  spaces: 2,
814
1531
  EOL: '\n'
815
1532
  });
816
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'index.js')));
817
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'dev.js')));
818
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'prod.js')));
819
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(pkgPath));
1533
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'package.json')));
1534
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/index.js')));
1535
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/dev.js')));
1536
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/prod')));
820
1537
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'project.config.json')));
821
1538
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, '.gitignore')));
822
1539
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, '.editorconfig')));
@@ -825,15 +1542,28 @@ ${code}
825
1542
  this.showLog();
826
1543
  });
827
1544
  }
1545
+ /**
1546
+ * generateReport: 为转换后的 taroConvert 工程添加转换报告
1547
+ */
1548
+ generateReport() {
1549
+ const reportDir = path.join(this.convertRoot, 'report');
1550
+ const reportBundleFilePath = path.resolve(__dirname, '../', 'report/bundle.js');
1551
+ const reportIndexFilePath = path.resolve(__dirname, '../', 'report/report.html');
1552
+ (0, util_1.generateReportFile)(reportBundleFilePath, reportDir, 'bundle.js', this.reportErroMsg);
1553
+ (0, util_1.generateReportFile)(reportIndexFilePath, reportDir, 'report.html');
1554
+ }
828
1555
  showLog() {
829
1556
  console.log();
830
1557
  console.log(`${helper_1.chalk.green('✔ ')} 转换成功,请进入 ${helper_1.chalk.bold('taroConvert')} 目录下使用 npm 或者 yarn 安装项目依赖后再运行!`);
1558
+ console.log(`转换报告已生成,请在浏览器中打开 ${path.join(this.convertRoot, 'report', 'report.html')} 查看转换报告`);
831
1559
  }
832
1560
  run() {
833
- this.framework = 'react';
1561
+ this.framework = "React" /* FrameworkType.React */;
834
1562
  this.generateEntry();
835
- this.traversePages();
1563
+ this.traversePages(this.root, this.pages);
1564
+ this.traversePlugin();
836
1565
  this.generateConfigFiles();
1566
+ this.generateReport();
837
1567
  }
838
1568
  }
839
1569
  exports.default = Convertor;