@tarojs/cli-convertor 3.6.19 → 3.6.21

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
@@ -13,8 +13,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  const template_1 = require("@babel/template");
14
14
  const traverse_1 = require("@babel/traverse");
15
15
  const t = require("@babel/types");
16
+ const binding_1 = require("@tarojs/binding");
16
17
  const cli_1 = require("@tarojs/cli");
17
18
  const helper_1 = require("@tarojs/helper");
19
+ const shared_1 = require("@tarojs/shared");
18
20
  const taroize = require("@tarojs/taroize");
19
21
  const transformer_wx_1 = require("@tarojs/transformer-wx");
20
22
  const path = require("path");
@@ -42,7 +44,7 @@ const babylonConfig = {
42
44
  ],
43
45
  };
44
46
  const OUTPUT_STYLE_EXTNAME = '.scss';
45
- const WX_GLOBAL_FN = new Set(['getApp', 'getCurrentPages', 'requirePlugin', 'Behavior']);
47
+ const WX_GLOBAL_FN = new Set(['getApp', 'getCurrentPages', 'Behavior']);
46
48
  function processStyleImports(content, processFn) {
47
49
  // 获取css中的引用样式文件路径集合
48
50
  const imports = (0, util_1.getWxssImports)(content);
@@ -88,35 +90,178 @@ class Convertor {
88
90
  this.hadBeenBuiltComponents = new Set();
89
91
  this.hadBeenBuiltImports = new Set();
90
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
+ };
91
102
  this.init();
92
103
  }
93
104
  init() {
94
105
  console.log(helper_1.chalk.green('开始代码转换...'));
95
- this.initConvert();
96
- this.getConvertConfig();
97
- this.getApp();
98
- this.getPages();
99
- this.getSitemapLocation();
100
- 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
+ }
101
116
  }
102
117
  initConvert() {
103
- if (helper_1.fs.existsSync(this.convertRoot)) {
104
- (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
+ }
105
139
  }
106
- else {
107
- helper_1.fs.ensureDirSync(this.convertRoot);
140
+ catch (error) {
141
+ throw new Error(`初始化convert失败 ${(0, util_1.getLineBreak)()} ${error.message}`);
108
142
  }
109
- this.convertSelfDefinedConfig();
110
- // 创建.convert目录,存放转换中间数据
111
- (0, util_1.generateDir)(path.join(this.convertRoot, '.convert'));
112
- global_1.globals.logFilePath = path.join(this.convertRoot, '.convert', 'convert.log');
113
143
  }
114
- 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, }) {
115
258
  const scriptFiles = new Set();
116
259
  // eslint-disable-next-line @typescript-eslint/no-this-alias
117
260
  const self = this;
118
261
  // 转换后js页面的所有自定义标签
119
262
  const scriptComponents = [];
263
+ // js页面所有的导入模块
264
+ const scriptImports = [];
120
265
  let componentClassName;
121
266
  let needInsertImportTaro = false;
122
267
  let hasCacheOptionsRequired = false;
@@ -147,6 +292,7 @@ class Convertor {
147
292
  }
148
293
  },
149
294
  ClassExpression(astPath) {
295
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析ClassExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
150
296
  const node = astPath.node;
151
297
  if (node.superClass) {
152
298
  let isTaroComponent = false;
@@ -202,13 +348,20 @@ class Convertor {
202
348
  const node = astPath.node;
203
349
  const source = node.source;
204
350
  const value = source.value;
205
- (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, source, value, self.isTsProject);
351
+ (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, source, value, self.isTsProject, self.pluginInfo.pluginName);
352
+ // 获取导入语句中的所有导入名称(importName)并将其添加到scriptImports里面
353
+ const specifiers = node.specifiers;
354
+ specifiers.forEach((specifier) => {
355
+ const importName = specifier.local.name;
356
+ scriptImports.push(importName);
357
+ });
206
358
  },
207
359
  CallExpression(astPath) {
208
- (0, util_1.printToLogFile)(`解析CallExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
360
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析CallExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
209
361
  const node = astPath.node;
210
362
  const calleePath = astPath.get('callee');
211
363
  const callee = calleePath.node;
364
+ const args = astPath.get('arguments');
212
365
  if (callee.type === 'Identifier') {
213
366
  if (callee.name === 'require') {
214
367
  const args = node.arguments;
@@ -221,7 +374,7 @@ class Convertor {
221
374
  throw new Error(`require暂不支持动态导入, filePath: ${sourceFilePath}, context: ${astPath}`);
222
375
  }
223
376
  const value = args[0].value;
224
- (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, args[0], value, self.isTsProject);
377
+ (0, util_1.analyzeImportUrl)(self.root, sourceFilePath, scriptFiles, args[0], value, self.isTsProject, self.pluginInfo.pluginName);
225
378
  }
226
379
  else if (WX_GLOBAL_FN.has(callee.name)) {
227
380
  calleePath.replaceWith(t.memberExpression(t.identifier('Taro'), callee));
@@ -244,6 +397,17 @@ class Convertor {
244
397
  hasCacheOptionsRequired = true;
245
398
  }
246
399
  }
400
+ else if (callee.name === 'requirePlugin') {
401
+ if (args.length === 1 && args[0].isStringLiteral()) {
402
+ // 在小程序中调用plugin中模块,相对路径需要使用转换后的
403
+ const pluginEntryFilePathTrans = self.pluginInfo.entryFilePath.replace(self.pluginInfo.pluginRoot, path.join(self.convertDir, self.pluginInfo.pluginName));
404
+ const sourceFilePathTarns = self.getDistFilePath(sourceFilePath);
405
+ const newRequire = t.callExpression(t.identifier('require'), [
406
+ t.stringLiteral((0, helper_1.normalizePath)(path.relative(path.dirname(sourceFilePathTarns), pluginEntryFilePathTrans))),
407
+ ]);
408
+ astPath.replaceWith(newRequire);
409
+ }
410
+ }
247
411
  }
248
412
  },
249
413
  MemberExpression(astPath) {
@@ -270,6 +434,7 @@ class Convertor {
270
434
  }
271
435
  },
272
436
  OptionalMemberExpression(astPath) {
437
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 解析OptionalMemberExpression: ${astPath} ${(0, util_1.getLineBreak)()}`);
273
438
  const node = astPath.node;
274
439
  const object = node.object;
275
440
  const prettier = node.property;
@@ -295,6 +460,10 @@ class Convertor {
295
460
  if (jsxName.isJSXIdentifier()) {
296
461
  const componentName = jsxName.node.name;
297
462
  if (!util_1.DEFAULT_Component_SET.has(componentName) && scriptComponents.indexOf(componentName) === -1) {
463
+ // 比较引入组件名和标签名是否同名,若同名,则在组件名上加入后缀Component
464
+ if (scriptImports.includes(componentName)) {
465
+ jsxName.node.name = `${componentName}Component`;
466
+ }
298
467
  scriptComponents.push(componentName);
299
468
  }
300
469
  if (/^\S(\S)*Tmpl$/.test(componentName)) {
@@ -451,23 +620,22 @@ class Convertor {
451
620
  }
452
621
  if (imports && imports.length) {
453
622
  imports.forEach(({ name, ast, wxs }) => {
454
- const importName = wxs ? name : (0, helper_1.pascalCase)(name);
455
- if (componentClassName === importName) {
623
+ if (componentClassName === name) {
456
624
  return;
457
625
  }
458
- const importPath = path.join(self.importsDir, importName + (wxs ? self.wxsIncrementId() : '') + (self.isTsProject ? '.ts' : '.js'));
626
+ const importPath = path.join(self.importsDir, name + (wxs ? self.wxsIncrementId() : '') + (self.isTsProject ? '.ts' : '.js'));
459
627
  if (!self.hadBeenBuiltImports.has(importPath)) {
460
628
  self.hadBeenBuiltImports.add(importPath);
461
629
  self.writeFileToTaro(importPath, prettier.format((0, astConvert_1.generateMinimalEscapeCode)(ast), prettierJSConfig));
462
630
  }
463
- if (scriptComponents.indexOf(importName) !== -1 || (wxs && wxs === true)) {
464
- lastImport.insertAfter((0, template_1.default)(`import ${importName} from '${(0, helper_1.promoteRelativePath)(path.relative(outputFilePath, importPath))}'`, babylonConfig)());
631
+ if (scriptComponents.indexOf(name) !== -1 || (wxs && wxs === true)) {
632
+ lastImport.insertAfter((0, template_1.default)(`import ${name} from '${(0, helper_1.promoteRelativePath)(path.relative(outputFilePath, importPath))}'`, babylonConfig)());
465
633
  }
466
634
  });
467
635
  }
468
636
  if (depComponents && depComponents.size) {
469
637
  depComponents.forEach((componentObj) => {
470
- const name = (0, helper_1.pascalCase)(componentObj.name.toLowerCase());
638
+ let name = (0, helper_1.pascalCase)(componentObj.name.toLowerCase());
471
639
  // 如果不是js页面用到的组件,无需导入
472
640
  if (scriptComponents.indexOf(name) === -1) {
473
641
  return;
@@ -475,17 +643,40 @@ class Convertor {
475
643
  // 如果用到了,从scriptComponents中移除
476
644
  const index = scriptComponents.indexOf(name);
477
645
  scriptComponents.splice(index, 1);
646
+ // 同名的自定义组件名称加后缀区分后,其组件标签也要加上Component保持统一
647
+ if (scriptImports.includes(name)) {
648
+ name = `${name}Component`;
649
+ }
478
650
  let componentPath = componentObj.path;
479
- if (componentPath.indexOf(self.root) !== -1) {
480
- componentPath = path.relative(sourceFilePath, componentPath);
651
+ if (!componentPath.startsWith(self.root) && !componentPath.startsWith(self.pluginInfo.pluginRoot)) {
652
+ console.error(`exception: 无效的组件路径,componentPath: ${componentPath}, 请在${outputFilePath}中手动引入`);
653
+ return;
481
654
  }
655
+ componentPath = path.relative(sourceFilePath, componentPath);
482
656
  lastImport.insertAfter((0, template_1.default)(`import ${name} from '${(0, helper_1.promoteRelativePath)(componentPath)}'`, babylonConfig)());
483
657
  });
484
658
  }
659
+ // 页面中引用的插件组件增加引用信息
660
+ if (pluginComponents && pluginComponents.size) {
661
+ pluginComponents.forEach((pluginComponent) => {
662
+ const componentName = (0, helper_1.pascalCase)(pluginComponent.name.toLowerCase());
663
+ const componentPath = pluginComponent.path;
664
+ if (!componentPath.startsWith(self.pluginInfo.pluginRoot)) {
665
+ console.error(`exception: 在页面${sourceFilePath}引用了无效的插件组件路径${componentPath}, 请在${outputFilePath}中手动引入`);
666
+ return;
667
+ }
668
+ // 插件组件转换后的绝对路径
669
+ const conponentTransPath = componentPath.replace(self.pluginInfo.pluginRoot, path.join(self.convertDir, self.pluginInfo.pluginName));
670
+ // 由于插件转换后路径的变化,此处需根据转换后的路径获取相对路径
671
+ const componentRelPath = path.relative(self.getDistFilePath(sourceFilePath), conponentTransPath);
672
+ lastImport.insertAfter((0, template_1.default)(`import ${componentName} from '${(0, helper_1.promoteRelativePath)(componentRelPath)}'`, babylonConfig)());
673
+ });
674
+ }
485
675
  }
486
676
  },
487
677
  },
488
678
  });
679
+ this.convertToOptional(ast);
489
680
  // 遍历 ast ,将多次 const { xxx } = require('@tarojs/with-weapp') 引入压缩为一次引入
490
681
  (0, traverse_1.default)(ast, {
491
682
  VariableDeclaration(astPath) {
@@ -590,21 +781,36 @@ class Convertor {
590
781
  }
591
782
  }
592
783
  }
593
- getApp() {
594
- try {
595
- const projectConfigPath = path.join(this.root, `project.config${this.fileTypes.CONFIG}`); // project.config.json 文件路径
596
- // 解析 project.config.json 文件,获取 miniprogramRoot 字段的值
597
- const projectConfig = JSON.parse(helper_1.fs.readFileSync(projectConfigPath, 'utf8'));
598
- this.miniprogramRoot = projectConfig.miniprogramRoot;
599
- }
600
- catch (err) {
601
- console.error('读取 project.config.json 文件失败:', err);
602
- process.exit(1);
603
- }
604
- // 如果找到 miniprogramRoot字段,则以对应目录作为小程序逻辑目录
605
- if (this.miniprogramRoot) {
606
- this.root = path.resolve(this.miniprogramRoot);
784
+ /**
785
+ * 解析project.config.json配置文件
786
+ *
787
+ */
788
+ parseProjectConfig() {
789
+ // 处理project.config.json,并存储到projectConfig
790
+ const projectConfigFilePath = path.join(this.root, `project.config${this.fileTypes.CONFIG}`);
791
+ if (helper_1.fs.existsSync(projectConfigFilePath)) {
792
+ try {
793
+ const projectConfigJson = JSON.parse(String(helper_1.fs.readFileSync(projectConfigFilePath, 'utf8')));
794
+ if (projectConfigJson && projectConfigJson.compileType === "plugin" /* Constants.PLUGIN */) {
795
+ const pluginRoot = projectConfigJson.pluginRoot;
796
+ if (pluginRoot === '' || (0, shared_1.isNull)(pluginRoot) || (0, shared_1.isUndefined)(pluginRoot)) {
797
+ console.log('project.config,json中pluginRoot为空或未配置,请确认配置是否正确');
798
+ process.exit(1);
799
+ }
800
+ this.projectConfig = Object.assign({}, projectConfigJson);
801
+ this.pluginInfo.pluginRoot = path.join(this.root, projectConfigJson.pluginRoot.replace(/\/+$/, ''));
802
+ }
803
+ // 解析miniprogramRoot字段,如果存在则更新小程序root
804
+ if (projectConfigJson.miniprogramRoot) {
805
+ this.root = path.join(this.root, projectConfigJson.miniprogramRoot.replace(/\/+$/, ''));
806
+ }
807
+ }
808
+ catch (err) {
809
+ throw new Error(`project.config${this.fileTypes.CONFIG} 解析失败,请检查!`);
810
+ }
607
811
  }
812
+ }
813
+ getApp() {
608
814
  if (this.isTsProject) {
609
815
  this.entryJSPath = path.join(this.miniprogramRoot, `app${this.fileTypes.SCRIPT}`);
610
816
  this.entryJSONPath = path.join(this.miniprogramRoot, `app${this.fileTypes.CONFIG}`);
@@ -615,12 +821,6 @@ class Convertor {
615
821
  this.entryJSONPath = path.join(this.root, `app${this.fileTypes.CONFIG}`);
616
822
  this.entryStylePath = path.join(this.root, `app${this.fileTypes.STYLE}`);
617
823
  }
618
- // 如果在 miniprogramRoot 目录下找到 app.json 文件,则将入口文件和配置文件路径修改为对应的路径
619
- if (this.miniprogramRoot && helper_1.fs.existsSync(path.join(this.root, `app${this.fileTypes.CONFIG}`))) {
620
- this.entryJSPath = path.join(this.root, `app${this.fileTypes.SCRIPT}`);
621
- this.entryJSONPath = path.join(this.root, `app${this.fileTypes.CONFIG}`);
622
- this.entryStylePath = path.join(this.root, `app${this.fileTypes.STYLE}`);
623
- }
624
824
  try {
625
825
  this.entryJSON = JSON.parse(String(helper_1.fs.readFileSync(this.entryJSONPath)));
626
826
  const using = this.entryJSON.usingComponents;
@@ -637,6 +837,10 @@ class Convertor {
637
837
  this.entryUsingComponents = using;
638
838
  delete this.entryJSON.usingComponents;
639
839
  }
840
+ // 当小程序中包含plugin时,从app.json中解析pluginName,当前只支持一个plugin
841
+ if (this.projectConfig && this.projectConfig.compileType === "plugin" /* Constants.PLUGIN */) {
842
+ this.parsePluginName(this.entryJSON);
843
+ }
640
844
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '入口文件', this.generateShowPath(this.entryJSPath));
641
845
  (0, helper_1.printLog)("convert" /* processTypeEnum.CONVERT */, '入口配置', this.generateShowPath(this.entryJSONPath));
642
846
  if (helper_1.fs.existsSync(this.entryStylePath)) {
@@ -650,6 +854,21 @@ class Convertor {
650
854
  process.exit(1);
651
855
  }
652
856
  }
857
+ /**
858
+ * 从app.json中解析pluginName,当前只支持一个plugin
859
+ *
860
+ * @param app.json
861
+ */
862
+ parsePluginName(entryJSON) {
863
+ const plugins = entryJSON.plugins;
864
+ if (plugins && Object.keys(plugins).length) {
865
+ this.pluginInfo.pluginName = Object.keys(plugins)[0];
866
+ }
867
+ else {
868
+ console.log('当前应用没有注册插件,请检查app.json中的plugins字段是否配置正确');
869
+ process.exit(1);
870
+ }
871
+ }
653
872
  getPages() {
654
873
  const pages = this.entryJSON.pages;
655
874
  if (!pages || !pages.length) {
@@ -708,7 +927,7 @@ class Convertor {
708
927
  }
709
928
  try {
710
929
  const code = helper_1.fs.readFileSync(file).toString();
711
- let outputFilePath = file.replace(this.isTsProject ? this.miniprogramRoot : this.root, this.convertDir);
930
+ let outputFilePath = this.getDistFilePath(file);
712
931
  const extname = path.extname(outputFilePath);
713
932
  if (/\.wxs/.test(extname)) {
714
933
  outputFilePath += '.js';
@@ -718,6 +937,7 @@ class Convertor {
718
937
  sourcePath: file,
719
938
  isNormal: true,
720
939
  isTyped: helper_1.REG_TYPESCRIPT.test(file),
940
+ logFilePath: global_1.globals.logFilePath,
721
941
  });
722
942
  const { ast, scriptFiles } = this.parseAst({
723
943
  ast: transformResult.ast,
@@ -747,12 +967,22 @@ class Convertor {
747
967
  else
748
968
  return component + '/index' + extname;
749
969
  }
970
+ /**
971
+ * 根据源文件路径获取转换后文件路径
972
+ *
973
+ * @param { string } src 源文件路径
974
+ * @param { 文件后缀 } extname
975
+ * @returns { string } 转换后文件路径
976
+ */
750
977
  getDistFilePath(src, extname) {
751
- if (!extname)
752
- return src.replace(this.isTsProject ? this.miniprogramRoot : this.root, this.convertDir);
753
- return src
754
- .replace(this.isTsProject ? this.miniprogramRoot : this.root, this.convertDir)
755
- .replace(path.extname(src), extname);
978
+ let filePath;
979
+ if (this.isTraversePlugin) {
980
+ filePath = src.replace(this.pluginInfo.pluginRoot, path.join(this.convertDir, this.pluginInfo.pluginName));
981
+ }
982
+ else {
983
+ filePath = src.replace(this.root, this.convertDir);
984
+ }
985
+ return extname ? filePath.replace(path.extname(src), extname) : filePath;
756
986
  }
757
987
  getConfigFilePath(src) {
758
988
  const { dir, name } = path.parse(src);
@@ -769,7 +999,7 @@ class Convertor {
769
999
  formatFile(jsCode, template = '') {
770
1000
  let code = jsCode;
771
1001
  const config = Object.assign({}, prettierJSConfig);
772
- if (this.framework === 'vue') {
1002
+ if (this.framework === "Vue" /* FrameworkType.Vue */) {
773
1003
  code = `
774
1004
  ${template}
775
1005
  <script>
@@ -785,7 +1015,7 @@ ${code}
785
1015
  generateEntry() {
786
1016
  try {
787
1017
  const entryJS = String(helper_1.fs.readFileSync(this.entryJSPath));
788
- const entryJSON = JSON.stringify(this.entryJSON);
1018
+ let entryJSON = JSON.stringify(this.entryJSON);
789
1019
  const entryDistJSPath = this.getDistFilePath(this.entryJSPath);
790
1020
  const taroizeResult = taroize({
791
1021
  json: entryJSON,
@@ -806,6 +1036,10 @@ ${code}
806
1036
  : null,
807
1037
  isApp: true,
808
1038
  });
1039
+ // 将插件信息转换为子包信息添加到入口配置文件中
1040
+ if (this.projectConfig.compileType === "plugin" /* Constants.PLUGIN */) {
1041
+ entryJSON = this.addSubpackages(this.entryJSON);
1042
+ }
809
1043
  const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
810
1044
  this.writeFileToTaro(entryDistJSPath, jsCode);
811
1045
  this.writeFileToConfig(entryDistJSPath, entryJSON);
@@ -823,6 +1057,33 @@ ${code}
823
1057
  console.log(err);
824
1058
  }
825
1059
  }
1060
+ /**
1061
+ * 将plugin信息转换为subpackage并添加到入口配置文件中
1062
+ *
1063
+ * @param entryJSON
1064
+ * @returns
1065
+ */
1066
+ addSubpackages(entryJSON) {
1067
+ // 删除plugins字段
1068
+ if (entryJSON && entryJSON.plugins) {
1069
+ delete entryJSON.plugins;
1070
+ }
1071
+ const subPackageInfo = {
1072
+ root: `${this.pluginInfo.pluginName}/`,
1073
+ pages: [...this.pluginInfo.pages],
1074
+ };
1075
+ // 子包的字段可以为subpackages 或 subPackages
1076
+ if (entryJSON.subpackages) {
1077
+ entryJSON.subpackages.push(subPackageInfo);
1078
+ }
1079
+ else if (entryJSON.subPackages) {
1080
+ entryJSON.subPackages.push(subPackageInfo);
1081
+ }
1082
+ else {
1083
+ entryJSON.subPackages = [subPackageInfo];
1084
+ }
1085
+ return JSON.stringify(entryJSON);
1086
+ }
826
1087
  generateTabBarIcon(tabBar) {
827
1088
  const { list = [] } = tabBar;
828
1089
  const icons = new Set();
@@ -855,7 +1116,7 @@ ${code}
855
1116
  }
856
1117
  }
857
1118
  getComponentDest(file) {
858
- if (this.framework === 'react') {
1119
+ if (this.framework === "React" /* FrameworkType.React */) {
859
1120
  return file;
860
1121
  }
861
1122
  return path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.vue');
@@ -895,15 +1156,15 @@ ${code}
895
1156
  }
896
1157
  return true;
897
1158
  }
898
- traversePages() {
899
- this.pages.forEach((page) => {
1159
+ traversePages(root, pages) {
1160
+ pages.forEach((page) => {
900
1161
  var _a;
901
1162
  (0, util_1.printToLogFile)(`开始转换页面 ${page} ${(0, util_1.getLineBreak)()}`);
902
- const pagePath = this.isTsProject ? path.join(this.miniprogramRoot, page) : path.join(this.root, page);
1163
+ const pagePath = this.isTsProject ? path.join(this.miniprogramRoot, page) : path.join(root, page);
903
1164
  // 处理不转换的页面,可在convert.config.json中external字段配置
904
1165
  const matchUnconvertDir = (0, util_1.getMatchUnconvertDir)(pagePath, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.external);
905
1166
  if (matchUnconvertDir !== null) {
906
- (0, util_1.handleUnconvertDir)(matchUnconvertDir, this.root, this.convertDir);
1167
+ (0, util_1.handleUnconvertDir)(matchUnconvertDir, root, this.convertDir);
907
1168
  return;
908
1169
  }
909
1170
  const pageJSPath = pagePath + this.fileTypes.SCRIPT;
@@ -912,7 +1173,6 @@ ${code}
912
1173
  const pageStylePath = pagePath + this.fileTypes.STYLE;
913
1174
  const pageTemplPath = pagePath + this.fileTypes.TEMPL;
914
1175
  try {
915
- const depComponents = new Set();
916
1176
  if (!helper_1.fs.existsSync(pageJSPath)) {
917
1177
  throw new Error(`页面 ${page} 没有 JS 文件!`);
918
1178
  }
@@ -927,8 +1187,11 @@ ${code}
927
1187
  else if (this.entryUsingComponents) {
928
1188
  pageConfig = {};
929
1189
  }
1190
+ const depComponents = new Set();
1191
+ const pluginComponents = new Set();
930
1192
  if (pageConfig) {
931
- if (this.entryUsingComponents) {
1193
+ // app.json中注册的组件为公共组件
1194
+ if (this.entryUsingComponents && !this.isTraversePlugin) {
932
1195
  pageConfig.usingComponents = Object.assign(Object.assign({}, pageConfig.usingComponents), this.entryUsingComponents);
933
1196
  }
934
1197
  const pageUsingComponents = pageConfig.usingComponents;
@@ -938,22 +1201,26 @@ ${code}
938
1201
  Object.keys(pageUsingComponents).forEach((component) => {
939
1202
  var _a;
940
1203
  const unResolveComponentPath = pageUsingComponents[component];
1204
+ let componentPath;
941
1205
  if (unResolveComponentPath.startsWith('plugin://')) {
942
- usingComponents[component] = unResolveComponentPath;
1206
+ componentPath = (0, util_1.replacePluginComponentUrl)(unResolveComponentPath, this.pluginInfo);
1207
+ pluginComponents.add({
1208
+ name: component,
1209
+ path: componentPath,
1210
+ });
943
1211
  }
944
1212
  else if (this.isThirdPartyLib(unResolveComponentPath, path.resolve(pagePath, '..'))) {
945
- (0, util_1.handleThirdPartyLib)(unResolveComponentPath, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.nodePath, this.root, this.convertRoot);
1213
+ (0, util_1.handleThirdPartyLib)(unResolveComponentPath, (_a = this.convertConfig) === null || _a === void 0 ? void 0 : _a.nodePath, root, this.convertRoot);
946
1214
  }
947
1215
  else {
948
- let componentPath;
949
- if (unResolveComponentPath.startsWith(this.root)) {
1216
+ if (unResolveComponentPath.startsWith(root)) {
950
1217
  componentPath = unResolveComponentPath;
951
1218
  }
952
1219
  else {
953
- componentPath = path.resolve(pageConfigPath, '..', pageUsingComponents[component]);
1220
+ componentPath = path.join(pageConfigPath, '..', pageUsingComponents[component]);
954
1221
  // 支持将组件库放在工程根目录下
955
1222
  if (!helper_1.fs.existsSync((0, helper_1.resolveScriptPath)(componentPath))) {
956
- componentPath = path.join(this.root, pageUsingComponents[component]);
1223
+ componentPath = path.join(root, pageUsingComponents[component]);
957
1224
  }
958
1225
  }
959
1226
  depComponents.add({
@@ -983,7 +1250,8 @@ ${code}
983
1250
  pageStyle = String(helper_1.fs.readFileSync(pageStylePath));
984
1251
  }
985
1252
  param.path = path.dirname(pageJSPath);
986
- param.rootPath = this.root;
1253
+ param.rootPath = root;
1254
+ param.pluginInfo = this.pluginInfo;
987
1255
  param.logFilePath = global_1.globals.logFilePath;
988
1256
  const taroizeResult = taroize(Object.assign(Object.assign({}, param), { framework: this.framework }));
989
1257
  const { ast, scriptFiles } = this.parseAst({
@@ -991,8 +1259,9 @@ ${code}
991
1259
  sourceFilePath: pageJSPath,
992
1260
  outputFilePath: pageDistJSPath,
993
1261
  importStylePath: pageStyle ? pageStylePath.replace(path.extname(pageStylePath), OUTPUT_STYLE_EXTNAME) : null,
994
- depComponents,
1262
+ depComponents: depComponents,
995
1263
  imports: taroizeResult.imports,
1264
+ pluginComponents: pluginComponents,
996
1265
  });
997
1266
  const jsCode = (0, astConvert_1.generateMinimalEscapeCode)(ast);
998
1267
  this.writeFileToTaro(this.getComponentDest(pageDistJSPath), this.formatFile(jsCode, taroizeResult.template));
@@ -1008,7 +1277,7 @@ ${code}
1008
1277
  catch (err) {
1009
1278
  (0, helper_1.printLog)("error" /* processTypeEnum.ERROR */, '页面转换', this.generateShowPath(pageJSPath));
1010
1279
  console.log(err);
1011
- (0, util_1.printToLogFile)(`转换页面异常 ${err.stack} ${(0, util_1.getLineBreak)()}`);
1280
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 转换页面异常 ${err.stack} ${(0, util_1.getLineBreak)()}`);
1012
1281
  }
1013
1282
  });
1014
1283
  }
@@ -1017,6 +1286,7 @@ ${code}
1017
1286
  return;
1018
1287
  }
1019
1288
  components.forEach((componentObj) => {
1289
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 开始转换组件 ${componentObj.path} ${(0, util_1.getLineBreak)()}`);
1020
1290
  const component = componentObj.path;
1021
1291
  if (this.hadBeenBuiltComponents.has(component))
1022
1292
  return;
@@ -1093,6 +1363,7 @@ ${code}
1093
1363
  catch (err) {
1094
1364
  (0, helper_1.printLog)("error" /* processTypeEnum.ERROR */, '组件转换', this.generateShowPath(componentJSPath));
1095
1365
  console.log(err);
1366
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 转换组件异常 ${err.stack} ${(0, util_1.getLineBreak)()}`);
1096
1367
  }
1097
1368
  });
1098
1369
  }
@@ -1133,6 +1404,7 @@ ${code}
1133
1404
  }
1134
1405
  traverseStyle(filePath, style) {
1135
1406
  return __awaiter(this, void 0, void 0, function* () {
1407
+ (0, util_1.printToLogFile)(`package: taro-cli-convertor, 开始转换样式 ${filePath} ${(0, util_1.getLineBreak)()}`);
1136
1408
  const { imports, content } = processStyleImports(style, (str, stylePath) => {
1137
1409
  let relativePath = stylePath;
1138
1410
  if (path.isAbsolute(relativePath)) {
@@ -1158,71 +1430,109 @@ ${code}
1158
1430
  }
1159
1431
  });
1160
1432
  }
1433
+ /**
1434
+ * 转换插件
1435
+ */
1436
+ traversePlugin() {
1437
+ if (this.projectConfig.compileType !== "plugin" /* Constants.PLUGIN */) {
1438
+ return;
1439
+ }
1440
+ this.isTraversePlugin = true;
1441
+ // 转换插件plugin.json中导出的页面
1442
+ this.traversePages(this.pluginInfo.pluginRoot, this.pluginInfo.pages);
1443
+ // 转换插件plugin.json中导出的组件
1444
+ this.traverseComponents(this.pluginInfo.publicComponents);
1445
+ // 转换插件的工具文件
1446
+ this.generateScriptFiles(new Set([this.pluginInfo.entryFilePath]));
1447
+ }
1448
+ /**
1449
+ * 解析插件配置信息
1450
+ *
1451
+ * @param pluginInfo
1452
+ */
1453
+ parsePluginConfig(pluginInfo) {
1454
+ // 处理plugin.json,并存储到pluginInfo中
1455
+ const pluginConfigPath = path.join(pluginInfo.pluginRoot, "plugin.json" /* Constants.PLUGIN_JSON */);
1456
+ if (helper_1.fs.existsSync(pluginConfigPath)) {
1457
+ try {
1458
+ const pluginConfigJson = JSON.parse(String(helper_1.fs.readFileSync(pluginConfigPath)));
1459
+ if (!pluginConfigJson) {
1460
+ console.log('插件配置信息为空,请检查!');
1461
+ return;
1462
+ }
1463
+ // 解析publicComponents信息
1464
+ const publicComponents = pluginConfigJson.publicComponents;
1465
+ if (publicComponents && Object.keys(publicComponents).length) {
1466
+ for (const key in publicComponents) {
1467
+ pluginInfo.publicComponents.add({
1468
+ name: key,
1469
+ path: path.join(pluginInfo.pluginRoot, publicComponents[key]),
1470
+ });
1471
+ }
1472
+ }
1473
+ // 解析pages信息
1474
+ const pages = pluginConfigJson.pages;
1475
+ if (pages && Object.keys(pages).length) {
1476
+ for (const pageName in pages) {
1477
+ const pagePath = pages[pageName];
1478
+ pluginInfo.pages.add(pagePath);
1479
+ pluginInfo.pagesMap.set(pageName, pagePath);
1480
+ }
1481
+ }
1482
+ // 解析入口文件信息
1483
+ const entryFilePath = pluginConfigJson.main;
1484
+ if (entryFilePath) {
1485
+ pluginInfo.entryFilePath = path.join(pluginInfo.pluginRoot, entryFilePath);
1486
+ }
1487
+ }
1488
+ catch (err) {
1489
+ console.log('解析plugin.json失败,请检查!');
1490
+ process.exit(1);
1491
+ }
1492
+ }
1493
+ }
1161
1494
  generateConfigFiles() {
1162
- const creator = new cli_1.Creator();
1495
+ const creator = new binding_1.Creator((0, cli_1.getRootPath)(), this.convertRoot);
1496
+ const dateObj = new Date();
1497
+ const date = `${dateObj.getFullYear()}-${dateObj.getMonth() + 1}-${dateObj.getDate()}`;
1163
1498
  const templateName = 'default';
1164
- const configDir = path.join(this.convertRoot, 'config');
1165
- const pkgPath = path.join(this.convertRoot, 'package.json');
1166
1499
  const projectName = 'taroConvert';
1167
- const description = '';
1168
1500
  const version = (0, util_1.getPkgVersion)();
1169
- const dateObj = new Date();
1170
- const date = `${dateObj.getFullYear()}-${dateObj.getMonth() + 1}-${dateObj.getDate()}`;
1171
- creator.template(templateName, 'package.json.tmpl', pkgPath, {
1501
+ const description = '';
1502
+ const ps = [];
1503
+ const createOpts = {
1504
+ css: "Sass" /* CSSType.Sass */,
1505
+ cssExt: '.scss',
1506
+ framework: this.framework,
1172
1507
  description,
1173
1508
  projectName,
1174
1509
  version,
1175
- css: 'sass',
1176
- typescript: false,
1177
- template: templateName,
1178
- framework: this.framework,
1179
- compiler: 'webpack5',
1180
- });
1181
- creator.template(templateName, path.join('config', 'index.js'), path.join(configDir, 'index.js'), {
1182
1510
  date,
1183
- projectName,
1184
- framework: this.framework,
1185
- compiler: 'webpack5',
1186
- typescript: false,
1187
- });
1188
- creator.template(templateName, path.join('config', 'dev.js'), path.join(configDir, 'dev.js'), {
1189
- framework: this.framework,
1190
- compiler: 'webpack5',
1191
- typescript: false,
1192
- });
1193
- creator.template(templateName, path.join('config', 'prod.js'), path.join(configDir, 'prod.js'), {
1194
- framework: this.framework,
1195
- typescript: false,
1196
- });
1197
- creator.template(templateName, 'project.config.json', path.join(this.convertRoot, 'project.config.json'), {
1198
- description,
1199
- projectName,
1200
- framework: this.framework,
1201
- });
1202
- creator.template(templateName, '.gitignore', path.join(this.convertRoot, '.gitignore'));
1203
- creator.template(templateName, '.editorconfig', path.join(this.convertRoot, '.editorconfig'));
1204
- creator.template(templateName, '.eslintrc.js', path.join(this.convertRoot, '.eslintrc.js'), {
1205
1511
  typescript: false,
1206
- framework: this.framework,
1207
- });
1208
- creator.template(templateName, 'babel.config.js', path.join(this.convertRoot, 'babel.config.js'), {
1209
- typescript: false,
1210
- framework: this.framework,
1211
- });
1212
- creator.template(templateName, path.join('src', 'index.html'), path.join(this.convertDir, 'index.html'), {
1213
- projectName,
1214
- });
1215
- creator.fs.commit(() => {
1216
- const pkgObj = JSON.parse(helper_1.fs.readFileSync(pkgPath).toString());
1512
+ template: templateName,
1513
+ compiler: "Webpack5" /* CompilerType.Webpack5 */,
1514
+ };
1515
+ ps.push(creator.createFileFromTemplate(templateName, 'package.json.tmpl', 'package.json', createOpts));
1516
+ ps.push(creator.createFileFromTemplate(templateName, 'config/index.js', 'config/index.js', createOpts));
1517
+ ps.push(creator.createFileFromTemplate(templateName, 'config/dev.js', 'config/dev.js', createOpts));
1518
+ ps.push(creator.createFileFromTemplate(templateName, 'config/prod.js', 'config/prod.js', createOpts));
1519
+ ps.push(creator.createFileFromTemplate(templateName, 'project.config.json', 'project.config.json', createOpts));
1520
+ ps.push(creator.createFileFromTemplate(templateName, '.gitignore', '.gitignore', createOpts));
1521
+ ps.push(creator.createFileFromTemplate(templateName, '.editorconfig', '.editorconfig', createOpts));
1522
+ ps.push(creator.createFileFromTemplate(templateName, '.eslintrc.js', '.eslintrc.js', createOpts));
1523
+ ps.push(creator.createFileFromTemplate(templateName, 'babel.config.js', 'babel.config.js', createOpts));
1524
+ ps.push(creator.createFileFromTemplate(templateName, 'src/index.html', 'src/index.html', createOpts));
1525
+ Promise.all(ps).then(() => {
1526
+ const pkgObj = JSON.parse(helper_1.fs.readFileSync(path.join(this.convertRoot, 'package.json')).toString());
1217
1527
  pkgObj.dependencies['@tarojs/with-weapp'] = `^${version}`;
1218
- helper_1.fs.writeJSONSync(pkgPath, pkgObj, {
1528
+ helper_1.fs.writeJSONSync(path.join(this.convertRoot, 'package.json'), pkgObj, {
1219
1529
  spaces: 2,
1220
- EOL: '\n',
1530
+ EOL: '\n'
1221
1531
  });
1222
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'index.js')));
1223
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'dev.js')));
1224
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(configDir, 'prod.js')));
1225
- (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(pkgPath));
1532
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'package.json')));
1533
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/index.js')));
1534
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/dev.js')));
1535
+ (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'config/prod')));
1226
1536
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, 'project.config.json')));
1227
1537
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, '.gitignore')));
1228
1538
  (0, helper_1.printLog)("generate" /* processTypeEnum.GENERATE */, '文件', this.generateShowPath(path.join(this.convertRoot, '.editorconfig')));
@@ -1247,9 +1557,10 @@ ${code}
1247
1557
  console.log(`转换报告已生成,请在浏览器中打开 ${path.join(this.convertRoot, 'report', 'report.html')} 查看转换报告`);
1248
1558
  }
1249
1559
  run() {
1250
- this.framework = 'react';
1560
+ this.framework = "React" /* FrameworkType.React */;
1251
1561
  this.generateEntry();
1252
- this.traversePages();
1562
+ this.traversePages(this.root, this.pages);
1563
+ this.traversePlugin();
1253
1564
  this.generateConfigFiles();
1254
1565
  this.generateReport();
1255
1566
  }