@aiot-toolkit/parser 2.0.5-beta.1 → 2.0.5-beta.3

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.
@@ -93,6 +93,16 @@ const elementConfig = {
93
93
  }
94
94
  }
95
95
  },
96
+ 'arc-text': {
97
+ allowTextChildren: true,
98
+ attributes: {
99
+ value: {},
100
+ 'start-angle': {},
101
+ direction: {
102
+ enums: ['clockwise', 'counterclockwise']
103
+ }
104
+ }
105
+ },
96
106
  span: {
97
107
  allowTextChildren: true,
98
108
  attributes: {
@@ -8,6 +8,10 @@ export default interface ITranslateOption {
8
8
  */
9
9
  sourceRoot: string;
10
10
  enableE2e?: boolean;
11
+ /**
12
+ * e2e 配置文件的路径
13
+ */
14
+ e2eConfigPath?: string;
11
15
  enableStats?: boolean;
12
16
  optimizeCssAttr?: boolean;
13
17
  /**
@@ -67,7 +67,7 @@ class UxToTypescript {
67
67
  } = sourceTree;
68
68
  const importTarget = await this.importToTypescript(importList);
69
69
  const templateTarget = await new _TemplateToTypescript.default(this.options, this.compilerOption, importList).translate(template, offsetList);
70
- const scriptTarget = new _ScriptToTypescript.default(this.options, this.compilerOption, this.contxt).translate(script, []);
70
+ const scriptTarget = await new _ScriptToTypescript.default(this.options, this.compilerOption, this.contxt).translate(script, []);
71
71
  // 切割script中的转换结果和source map
72
72
  const spliteResult = _SourceMapUtil.default.splitSourceMap(scriptTarget.targetTree);
73
73
  const styleTarget = new _StyleToTypescript.default(this.options, this.compilerOption).translate(style, []);
@@ -1,4 +1,3 @@
1
- import { MapData } from '@aiot-toolkit/shared-utils';
2
1
  import { SourceFile } from 'ts-morph';
3
2
  import IOffset from '../../../interface/IOffset';
4
3
  import ITranslate from '../../../interface/ITranslate';
@@ -15,10 +14,10 @@ declare class ScriptToTypescript implements ITranslate<IScriptAst, SourceFile> {
15
14
  readonly context: IFileLaneContext;
16
15
  private _systemFeatures;
17
16
  constructor(options: IOptions, compilerOption: ITranslateOption, context: IFileLaneContext);
18
- translate(sourceTree: IScriptAst, offsetList: IOffset[]): {
17
+ translate(sourceTree: IScriptAst, offsetList: IOffset[]): Promise<{
19
18
  targetTree: SourceFile;
20
- mapList: MapData[];
21
- };
19
+ mapList: never[];
20
+ }>;
22
21
  /**
23
22
  * babel自定义插件实现替换
24
23
  * @param sourceCode
@@ -29,5 +28,9 @@ declare class ScriptToTypescript implements ITranslate<IScriptAst, SourceFile> {
29
28
  * 缓存并检查 feature
30
29
  */
31
30
  private cacheAndCheckFeature;
31
+ /**
32
+ * 检查代码
33
+ */
34
+ private checkCode;
32
35
  }
33
36
  export default ScriptToTypescript;
@@ -9,6 +9,7 @@ var babel = _interopRequireWildcard(require("@babel/core"));
9
9
  var _path = _interopRequireDefault(require("path"));
10
10
  var _ParserUtil = _interopRequireDefault(require("../../../utils/ParserUtil"));
11
11
  var _FeatureConfig = _interopRequireDefault(require("../../config/FeatureConfig"));
12
+ var _eslint = require("eslint");
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -24,9 +25,16 @@ class ScriptToTypescript {
24
25
  this.compilerOption = compilerOption;
25
26
  this.context = context;
26
27
  }
27
- translate(sourceTree,
28
+ async translate(sourceTree,
28
29
  // eslint-disable-next-line
29
30
  offsetList) {
31
+ // 检查代码错误
32
+ const logs = await this.checkCode(sourceTree);
33
+ if (logs) {
34
+ logs.forEach(log => {
35
+ this.options.onLog(log);
36
+ });
37
+ }
30
38
  // 使用babel实现替换,由此实现自动更新source map
31
39
  const content = this.babelScript(sourceTree);
32
40
  const fileName = this.options.filePath + '.js';
@@ -151,5 +159,51 @@ class ScriptToTypescript {
151
159
  });
152
160
  }
153
161
  }
162
+
163
+ /**
164
+ * 检查代码
165
+ */
166
+ async checkCode(sourceTree) {
167
+ const code = sourceTree.content;
168
+ if (!code) {
169
+ return;
170
+ }
171
+ const lintResult = await new _eslint.ESLint({
172
+ useEslintrc: false,
173
+ baseConfig: {
174
+ env: {
175
+ browser: true,
176
+ es6: true,
177
+ node: true
178
+ },
179
+ parserOptions: {
180
+ sourceType: 'module'
181
+ },
182
+ rules: {
183
+ '*': 'off',
184
+ // 检查未定义的变量
185
+ 'no-undef': 'error'
186
+ }
187
+ }
188
+ }).lintText(code);
189
+ if (lintResult?.length) {
190
+ const result = [];
191
+ const offsetLine = (sourceTree.sourceCodeLocation?.startLine || 0) - 1;
192
+ lintResult.forEach(item => {
193
+ item.messages.forEach(message => {
194
+ result.push({
195
+ filePath: this.options.filePath,
196
+ level: message.severity === 2 ? _sharedUtils.Loglevel.ERROR : _sharedUtils.Loglevel.WARN,
197
+ message: message.message,
198
+ position: {
199
+ startLine: message.line + offsetLine,
200
+ startColumn: message.column
201
+ }
202
+ });
203
+ });
204
+ });
205
+ return result;
206
+ }
207
+ }
154
208
  }
155
209
  var _default = exports.default = ScriptToTypescript;
@@ -321,7 +321,10 @@ class TemplateToTypescript {
321
321
  const {
322
322
  itemNode,
323
323
  indexNode
324
- } = _CfTranslate.default.extractForNodes(forAttribute, this.options);
324
+ } = _CfTranslate.default.extractForNodes(forAttribute, {
325
+ ...this.options,
326
+ onLog: () => {}
327
+ });
325
328
  [itemNode, indexNode].forEach(item => {
326
329
  if (item) {
327
330
  result.push(item.escapedText.toString());
@@ -27,7 +27,7 @@ class UxToTypescript {
27
27
  const appImport = this.importToTypescript(sourceTree.import);
28
28
  const appStyle = new _StyleToTypescript.default(this.options, this.compilerOption).translate(sourceTree.style, []);
29
29
  const appTemplate = await new _TemplateToTypescript.default(this.options, this.compilerOption, sourceTree.import.map(item => item.name.toLowerCase())).translate(sourceTree.template, []);
30
- const appScript = new _ScriptToTypescript.default(this.options, this.compilerOption, this.context).translate(sourceTree.script, []);
30
+ const appScript = await new _ScriptToTypescript.default(this.options, this.compilerOption, this.context).translate(sourceTree.script, []);
31
31
  // 切割出源码和map信息
32
32
  const spliteResult = _SourceMapUtil.default.splitSourceMap(appScript.targetTree);
33
33
  const integratedList = this.integratedFunction.bind(this, appImport, appStyle.targetTree, appTemplate.targetTree, spliteResult.newScriptTree, this.options)();
@@ -8,6 +8,7 @@ var t = _interopRequireWildcard(require("@babel/types"));
8
8
  var parser = _interopRequireWildcard(require("@babel/parser"));
9
9
  var _path = _interopRequireDefault(require("path"));
10
10
  var _fsExtra = _interopRequireDefault(require("fs-extra"));
11
+ var _sharedUtils = require("@aiot-toolkit/shared-utils");
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
13
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -24,10 +25,11 @@ function e2e() {
24
25
  let {
25
26
  opts
26
27
  } = _ref;
27
- const options = opts.options;
28
+ const opts2 = opts;
29
+ const options = opts2.options;
28
30
  switch (options.fileType) {
29
31
  case 'page':
30
- addPageTestCode(path, opts);
32
+ addPageTestCode(path, opts2);
31
33
  break;
32
34
  case 'app':
33
35
  addAppTestCode(path);
@@ -55,11 +57,17 @@ function addAppTestCode(path) {
55
57
 
56
58
  /**
57
59
  * 添加页面的测试代码
58
- * 1. 检查测试文件是否存在
59
- * 2. 如果存在,则
60
+ * 1. 如果测试文件存在,则
60
61
  * a. import 测试文件
61
62
  * b. 设置测试函数: 如果 onTestStart 方法已存在,则在其内部添加测试代码;否则,添加 onTestStart方法
62
63
  *
64
+ *
65
+ * 2. 如果启用自动路由,且当前页面是首页
66
+ * a. 从routerList 中过滤出有测试文件的路由
67
+ * b. 在末尾添加 $generateAutoRunTest
68
+ * c. 在onShow 方法中调用 $generateAutoRunTest
69
+ *
70
+ *
63
71
  * @description 示例
64
72
  *
65
73
  * 源码 `src/home/myHome.ux`
@@ -83,43 +91,155 @@ function addAppTestCode(path) {
83
91
  * @param opts
84
92
  */
85
93
  function addPageTestCode(path, opts) {
86
- const testFilePath = getTestFilePath(opts);
87
- // 1.
94
+ const e2eConfig = getE2eConfig(opts);
95
+ const testFilePath = getTestFilePath(opts.options.filePath, opts, e2eConfig);
96
+ // 1
88
97
  if (_fsExtra.default.existsSync(testFilePath)) {
89
- const funcName = 'onTestStart';
90
- const funcBody = generateTestFn();
91
- // 2.a
98
+ // 1.a
92
99
  const vmImport = t.importDeclaration([t.importDefaultSpecifier(t.identifier('fnTestCase'))], t.stringLiteral(testFilePath));
93
100
  if (path.parent.type === 'Program') {
94
101
  path.parent.body.unshift(vmImport);
95
102
  }
96
103
 
97
- // 2.b
104
+ // 1.b
98
105
  if (path.node.declaration.type === 'ObjectExpression') {
99
106
  const properties = path.node.declaration.properties;
100
- const testFunc = properties.find(p => p.type === 'ObjectMethod' && p.key.name === funcName);
101
- if (testFunc) {
102
- testFunc.body.body.push(...funcBody.body);
103
- } else {
104
- properties.push(t.objectMethod('method', {
105
- name: funcName,
106
- type: 'Identifier'
107
- }, [], funcBody));
107
+ insertFun('onTestStart', generateTestFn(), properties);
108
+ }
109
+ }
110
+
111
+ // 2
112
+ if (isIndexPage(opts) && e2eConfig.autoRoute) {
113
+ let {
114
+ routerList,
115
+ pageSleep
116
+ } = e2eConfig.autoRoute;
117
+ routerList = routerList?.filter(item => hasTestFile(item, opts, e2eConfig));
118
+ if (!routerList?.length) {
119
+ console.log('missing autoRoute.routerList');
120
+ return;
121
+ }
122
+ // 2.a
123
+
124
+ if (path.node.declaration.type === 'ObjectExpression') {
125
+ const properties = path.node.declaration.properties;
126
+
127
+ // 2.b
128
+ insertFun('$generateAutoRunTest', generateAutoRouterFn(pageSleep, routerList), properties);
129
+ // 2.c
130
+ insertFun('onShow', generateAutoRouterInOnShow(), properties);
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * 插入函数
137
+ *
138
+ * 如果已存在,把函数体插入到指定函数末尾;不存在,则添加
139
+ *
140
+ * @param name 函数名
141
+ * @param body 函数体
142
+ */
143
+ function insertFun(name, body, properties) {
144
+ const fun = properties.find(p => p.type === 'ObjectMethod' && p.key.name === name);
145
+ if (fun) {
146
+ fun.body.body.push(...body.body);
147
+ } else {
148
+ properties.push(t.objectMethod('method', {
149
+ name: name,
150
+ type: 'Identifier'
151
+ }, [], body));
152
+ }
153
+ }
154
+ function hasTestFile(router, opts, e2eConfig) {
155
+ const manifest = getManifest(opts);
156
+ if (manifest) {
157
+ const {
158
+ pages
159
+ } = manifest.router;
160
+ const pageRouter = pages[router];
161
+ if (!pageRouter) {
162
+ opts.options.onLog({
163
+ level: _sharedUtils.Loglevel.WARN,
164
+ message: [{
165
+ word: router
166
+ }, `missing router in manifest.json`]
167
+ });
168
+ return false;
169
+ }
170
+ const pagePath = `${opts.compilerOption.sourceRoot}/${router}/${pageRouter.component}`;
171
+ const testFilePath = getTestFilePath(pagePath, opts, e2eConfig);
172
+ return _fsExtra.default.existsSync(testFilePath);
173
+ }
174
+ return false;
175
+ }
176
+ function isIndexPage(opts) {
177
+ const manifest = getManifest(opts);
178
+ if (manifest) {
179
+ const {
180
+ entry,
181
+ pages
182
+ } = manifest.router;
183
+ const entryItem = pages[entry];
184
+ const entryPath = `${entry}/${entryItem.component}`;
185
+ const sourcePath = _path.default.join(opts.options.projectPath, opts.compilerOption.sourceRoot);
186
+ const filePath = _path.default.relative(sourcePath, opts.options.filePath);
187
+ const filePathParsed = _path.default.parse(filePath);
188
+ return entryPath === `${filePathParsed.dir}/${filePathParsed.name}`;
189
+ }
190
+ return false;
191
+ }
192
+ function getManifest(opts) {
193
+ const manifestPath = _path.default.resolve(opts.options.projectPath, opts.compilerOption.sourceRoot, 'manifest.json');
194
+ if (manifestPath) {
195
+ return _fsExtra.default.readJSONSync(manifestPath);
196
+ }
197
+ }
198
+ function getE2eConfig(opts) {
199
+ const defaultTestDir = 'test';
200
+ let result = {
201
+ testDir: _path.default.join(opts.options.projectPath, defaultTestDir)
202
+ };
203
+ const {
204
+ projectPath
205
+ } = opts.options;
206
+ const {
207
+ e2eConfigPath
208
+ } = opts.compilerOption;
209
+ if (e2eConfigPath) {
210
+ const configFilePath = _path.default.resolve(projectPath, e2eConfigPath);
211
+ const configFileDir = _path.default.dirname(configFilePath);
212
+ if (_fsExtra.default.existsSync(configFilePath)) {
213
+ result = _fsExtra.default.readJSONSync(configFilePath, 'utf-8');
214
+
215
+ // 目录转换为绝对路径
216
+ if (result) {
217
+ result.testDir = _path.default.resolve(configFileDir, result.testDir || defaultTestDir);
108
218
  }
219
+ } else {
220
+ throw new Error(`e2e Configuration file does not exist: ${configFilePath}`);
109
221
  }
110
222
  }
223
+ return result;
111
224
  }
112
- function getTestFilePath(opts) {
225
+
226
+ /**
227
+ * 获取源文件对应的测试文件地址
228
+ * @param sourceFile 源文件
229
+ * @param opts
230
+ * @param e2eConfig
231
+ * @returns
232
+ */
233
+ function getTestFilePath(sourceFile, opts, e2eConfig) {
113
234
  const {
114
- filePath,
115
235
  projectPath
116
236
  } = opts.options;
117
237
  const {
118
238
  sourceRoot
119
239
  } = opts.compilerOption;
120
- const relativeSrc = _path.default.relative(_path.default.join(projectPath, sourceRoot || ''), filePath);
240
+ const relativeSrc = _path.default.relative(_path.default.join(projectPath, sourceRoot || ''), sourceFile);
121
241
  const parsed = _path.default.parse(relativeSrc);
122
- return _path.default.join(projectPath, 'test', parsed.dir, `${parsed.name}.js`);
242
+ return _path.default.join(e2eConfig.testDir, parsed.dir, `${parsed.name}.js`);
123
243
  }
124
244
 
125
245
  /**
@@ -149,4 +269,35 @@ function generateTestFn() {
149
269
  }, global.CASE_TEST_START);
150
270
  }`;
151
271
  return parser.parseExpression(fn).body;
272
+ }
273
+ function generateAutoRouterFn() {
274
+ let sleep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 3000;
275
+ let routerList = arguments.length > 1 ? arguments[1] : undefined;
276
+ const fn = `function(){
277
+ const routerList = ${JSON.stringify(routerList)}
278
+ if (!routerList) {
279
+ console.log('missing routerList');
280
+ return;
281
+ }
282
+ if (this.autoTestPageIndex === undefined) {
283
+ this.autoTestPageIndex = -1;
284
+ }
285
+
286
+ setTimeout(() => {
287
+ if (++this.autoTestPageIndex < routerList.length) {
288
+ const uri = routerList[this.autoTestPageIndex];
289
+ console.log(\`auto test page=\${uri}, index=\${this.autoTestPageIndex}\`);
290
+ router.push({ uri });
291
+ } else {
292
+ console.log('auto test finish');
293
+ }
294
+ }, ${sleep});
295
+ }`;
296
+ return parser.parseExpression(fn).body;
297
+ }
298
+ function generateAutoRouterInOnShow() {
299
+ const fn = `function(){
300
+ this.$generateAutoRunTest();
301
+ }`;
302
+ return parser.parseExpression(fn).body;
152
303
  }
@@ -63,18 +63,13 @@ const ATTRIBUTE_CONFIG = {
63
63
  if (nameLocation) {
64
64
  const {
65
65
  startLine,
66
- startColumn,
67
- pos
66
+ startColumn
68
67
  } = nameLocation;
69
68
  logs.forEach(log => {
70
69
  const position = log.position;
71
70
  if (position) {
72
71
  position.startLine += startLine - 1;
73
72
  position.startColumn += startColumn;
74
- position.pos += pos;
75
- position.endLine += startLine - 1;
76
- position.endColumn += startColumn;
77
- position.end += pos;
78
73
  }
79
74
  option.onLog(log);
80
75
  });
@@ -755,7 +755,7 @@ class StyleUtil {
755
755
  return value[0];
756
756
  } else {
757
757
  // 例如animation-name属性值
758
- return value.join(' ');
758
+ return value.map(item => item === ',' ? `${item} ` : item).join('').trim();
759
759
  }
760
760
  } else {
761
761
  // 例如transform属性值,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/parser",
3
- "version": "2.0.5-beta.1",
3
+ "version": "2.0.5-beta.3",
4
4
  "description": "Parse the source code of aiot and convert it to the AST (Abstract Syntax Tree) of the target code.",
5
5
  "keywords": [
6
6
  "aiot",
@@ -20,8 +20,8 @@
20
20
  "test": "node ./__tests__/parser.test.js"
21
21
  },
22
22
  "dependencies": {
23
- "@aiot-toolkit/generator": "2.0.5-beta.1",
24
- "@aiot-toolkit/shared-utils": "2.0.5-beta.1",
23
+ "@aiot-toolkit/generator": "2.0.5-beta.3",
24
+ "@aiot-toolkit/shared-utils": "2.0.5-beta.3",
25
25
  "@babel/core": "^7.23.6",
26
26
  "@babel/generator": "^7.24.10",
27
27
  "@babel/parser": "^7.24.8",
@@ -32,7 +32,8 @@
32
32
  "colord": "^2.9.3",
33
33
  "css-tree": "npm:aiot-css-tree@^2.3.1",
34
34
  "csstree-validator": "^3.0.0",
35
- "file-lane": "2.0.5-beta.1",
35
+ "eslint": "^8.46.0",
36
+ "file-lane": "2.0.5-beta.3",
36
37
  "fs-extra": "^11.2.0",
37
38
  "google-protobuf": "^3.21.2",
38
39
  "less": "^4.2.0",
@@ -51,6 +52,7 @@
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/css-tree": "^2.3.3",
55
+ "@types/eslint": "^8.56.12",
54
56
  "@types/fs-extra": "^11.0.4",
55
57
  "@types/less": "^3.0.5",
56
58
  "@types/postcss-import": "^14.0.3",
@@ -58,5 +60,5 @@
58
60
  "@types/reserved-words": "^0.1.4",
59
61
  "babel-plugin-tester": "^11.0.4"
60
62
  },
61
- "gitHead": "44f8c6ee763bc01bd53c655b93e09c698057fc3e"
63
+ "gitHead": "273b9c3c85c4e199d642fd7aced0f140b7eac496"
62
64
  }