@weapp-vite/ast 6.8.0
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/babel.d.mts +21 -0
- package/dist/babel.mjs +64 -0
- package/dist/babelNodes.d.mts +10 -0
- package/dist/babelNodes.mjs +54 -0
- package/dist/engine-DHqNPCDA.mjs +37 -0
- package/dist/engine.d.mts +13 -0
- package/dist/engine.mjs +2 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.mjs +10 -0
- package/dist/operations/componentProps.d.mts +12 -0
- package/dist/operations/componentProps.mjs +170 -0
- package/dist/operations/featureFlags.d.mts +14 -0
- package/dist/operations/featureFlags.mjs +97 -0
- package/dist/operations/jsxAutoComponents.d.mts +36 -0
- package/dist/operations/jsxAutoComponents.mjs +305 -0
- package/dist/operations/platformApi.d.mts +13 -0
- package/dist/operations/platformApi.mjs +35 -0
- package/dist/operations/require.d.mts +24 -0
- package/dist/operations/require.mjs +54 -0
- package/dist/operations/scriptSetupImports.d.mts +17 -0
- package/dist/operations/scriptSetupImports.mjs +80 -0
- package/dist/types.d.mts +24 -0
- package/dist/types.mjs +1 -0
- package/package.json +101 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ice breaker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# @weapp-vite/ast
|
|
2
|
+
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
5
|
+
`@weapp-vite/ast` 提供 weapp-vite 体系内可复用的 AST 解析与静态分析能力,统一封装 Babel 与 Oxc 两套后端,并抽出跨包共享的分析操作,供 `weapp-vite`、`wevu`、`@wevu/compiler` 等包复用。
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- 统一的 Babel / Oxc 解析入口
|
|
10
|
+
- JS / TS / JSX / TSX 源码解析
|
|
11
|
+
- 平台 API、`require`、`script setup import` 等轻量分析
|
|
12
|
+
- 组件 `props`、特性标记、JSX 自动组件等共享分析操作
|
|
13
|
+
- 面向业务包的可参数化分析器,避免重复实现
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @weapp-vite/ast
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用
|
|
22
|
+
|
|
23
|
+
解析源码:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { parseJsLikeWithEngine } from '@weapp-vite/ast'
|
|
27
|
+
|
|
28
|
+
const babelAst = parseJsLikeWithEngine('export const value = 1')
|
|
29
|
+
const oxcAst = parseJsLikeWithEngine('export const value = 1', {
|
|
30
|
+
engine: 'oxc',
|
|
31
|
+
filename: 'inline.ts',
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
收集通用特性标记:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { collectFeatureFlagsFromCode } from '@weapp-vite/ast'
|
|
39
|
+
|
|
40
|
+
const flags = collectFeatureFlagsFromCode(code, {
|
|
41
|
+
astEngine: 'oxc',
|
|
42
|
+
moduleId: 'wevu',
|
|
43
|
+
hookToFeature: {
|
|
44
|
+
onLoad: 'enableShare',
|
|
45
|
+
onShow: 'enableShow',
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
收集 JSX 自动组件:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { collectJsxAutoComponentsFromCode } from '@weapp-vite/ast'
|
|
54
|
+
|
|
55
|
+
const result = collectJsxAutoComponentsFromCode(code, {
|
|
56
|
+
astEngine: 'babel',
|
|
57
|
+
isCollectableTag(tag) {
|
|
58
|
+
return !['view', 'text'].includes(tag)
|
|
59
|
+
},
|
|
60
|
+
isDefineComponentSource(source) {
|
|
61
|
+
return source === 'vue' || source === 'wevu'
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 导出能力
|
|
67
|
+
|
|
68
|
+
- `parseJsLikeWithEngine`
|
|
69
|
+
- `collectComponentPropsFromCode`
|
|
70
|
+
- `collectFeatureFlagsFromCode`
|
|
71
|
+
- `collectJsxAutoComponentsFromCode`
|
|
72
|
+
- `mayContainPlatformApiAccess`
|
|
73
|
+
- `collectRequireTokens`
|
|
74
|
+
- `collectScriptSetupImportsFromCode`
|
|
75
|
+
|
|
76
|
+
## 相关链接
|
|
77
|
+
|
|
78
|
+
- 文档:https://vite.icebreaker.top/
|
|
79
|
+
- 仓库:https://github.com/weapp-vite/weapp-vite
|
package/dist/babel.d.mts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as _babel_types0 from "@babel/types";
|
|
2
|
+
import * as _babel_traverse0 from "@babel/traverse";
|
|
3
|
+
import * as _babel_generator0 from "@babel/generator";
|
|
4
|
+
import * as _babel_parser0 from "@babel/parser";
|
|
5
|
+
import { ParserOptions, ParserPlugin } from "@babel/parser";
|
|
6
|
+
|
|
7
|
+
//#region src/babel.d.ts
|
|
8
|
+
declare const BABEL_TS_MODULE_PLUGINS: ParserPlugin[];
|
|
9
|
+
declare const BABEL_TS_MODULE_PARSER_OPTIONS: ParserOptions;
|
|
10
|
+
type BabelTraverse = typeof _babel_traverse0.default;
|
|
11
|
+
type BabelGenerate = typeof _babel_generator0.default;
|
|
12
|
+
type BabelParse = typeof _babel_parser0.parse;
|
|
13
|
+
type TraverseFn = (...args: Parameters<BabelTraverse>) => ReturnType<BabelTraverse>;
|
|
14
|
+
type GenerateFn = (...args: Parameters<BabelGenerate>) => ReturnType<BabelGenerate>;
|
|
15
|
+
declare const traverse: TraverseFn;
|
|
16
|
+
declare const generate: GenerateFn;
|
|
17
|
+
declare const parse: BabelParse;
|
|
18
|
+
declare function getVisitorKeys(): typeof _babel_types0.VISITOR_KEYS;
|
|
19
|
+
declare function parseJsLike(source: string): _babel_types0.File;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, generate, getVisitorKeys, parse, parseJsLike, traverse };
|
package/dist/babel.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
//#region src/babel.ts
|
|
3
|
+
const BABEL_TS_MODULE_PLUGINS = [
|
|
4
|
+
"typescript",
|
|
5
|
+
"decorators-legacy",
|
|
6
|
+
"classProperties",
|
|
7
|
+
"classPrivateProperties",
|
|
8
|
+
"classPrivateMethods",
|
|
9
|
+
"jsx"
|
|
10
|
+
];
|
|
11
|
+
const BABEL_TS_MODULE_PARSER_OPTIONS = {
|
|
12
|
+
sourceType: "module",
|
|
13
|
+
plugins: BABEL_TS_MODULE_PLUGINS
|
|
14
|
+
};
|
|
15
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
16
|
+
let cachedTraverse;
|
|
17
|
+
let cachedGenerate;
|
|
18
|
+
let cachedParse;
|
|
19
|
+
function requireCallableDefault(id) {
|
|
20
|
+
const mod = nodeRequire(id);
|
|
21
|
+
if (typeof mod === "function") return mod;
|
|
22
|
+
if (mod && (typeof mod === "object" || typeof mod === "function") && "default" in mod) {
|
|
23
|
+
const candidate = mod.default;
|
|
24
|
+
if (typeof candidate === "function") return candidate;
|
|
25
|
+
}
|
|
26
|
+
throw new TypeError(`Invalid module shape for ${id}`);
|
|
27
|
+
}
|
|
28
|
+
function getTraverse() {
|
|
29
|
+
if (!cachedTraverse) cachedTraverse = requireCallableDefault("@babel/traverse");
|
|
30
|
+
return cachedTraverse;
|
|
31
|
+
}
|
|
32
|
+
function getGenerate() {
|
|
33
|
+
if (!cachedGenerate) cachedGenerate = requireCallableDefault("@babel/generator");
|
|
34
|
+
return cachedGenerate;
|
|
35
|
+
}
|
|
36
|
+
function getParse() {
|
|
37
|
+
if (!cachedParse) cachedParse = nodeRequire("@babel/parser").parse;
|
|
38
|
+
return cachedParse;
|
|
39
|
+
}
|
|
40
|
+
const traverse = (...args) => {
|
|
41
|
+
return getTraverse()(...args);
|
|
42
|
+
};
|
|
43
|
+
const generate = (...args) => {
|
|
44
|
+
return getGenerate()(...args);
|
|
45
|
+
};
|
|
46
|
+
const parse = ((...args) => {
|
|
47
|
+
return getParse()(...args);
|
|
48
|
+
});
|
|
49
|
+
function getVisitorKeys() {
|
|
50
|
+
return nodeRequire("@babel/types").VISITOR_KEYS;
|
|
51
|
+
}
|
|
52
|
+
function parseJsLike(source) {
|
|
53
|
+
return parse(source, {
|
|
54
|
+
sourceType: "module",
|
|
55
|
+
plugins: [
|
|
56
|
+
...BABEL_TS_MODULE_PLUGINS,
|
|
57
|
+
"dynamicImport",
|
|
58
|
+
"optionalChaining",
|
|
59
|
+
"nullishCoalescingOperator"
|
|
60
|
+
]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, generate, getVisitorKeys, parse, parseJsLike, traverse };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { Expression, ObjectExpression, ObjectMethod, ObjectProperty, PrivateName } from "@babel/types";
|
|
3
|
+
|
|
4
|
+
//#region src/babelNodes.d.ts
|
|
5
|
+
declare function unwrapTypeScriptExpression(exp: Expression): Expression;
|
|
6
|
+
declare function toStaticObjectKey(key: Expression | PrivateName | t.Identifier): string | null;
|
|
7
|
+
declare function getObjectPropertyByKey(node: ObjectExpression, key: string): ObjectMethod | ObjectProperty | null;
|
|
8
|
+
declare function resolveRenderableExpression(node: ObjectMethod | ObjectProperty): Expression | null;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getObjectPropertyByKey, resolveRenderableExpression, toStaticObjectKey, unwrapTypeScriptExpression };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
//#region src/babelNodes.ts
|
|
3
|
+
function unwrapTypeScriptExpression(exp) {
|
|
4
|
+
let current = exp;
|
|
5
|
+
while (t.isTSAsExpression(current) || t.isTSTypeAssertion(current) || t.isTSNonNullExpression(current) || t.isParenthesizedExpression(current) || t.isTSInstantiationExpression(current)) {
|
|
6
|
+
if (t.isTSAsExpression(current) || t.isTSTypeAssertion(current) || t.isTSNonNullExpression(current)) {
|
|
7
|
+
current = current.expression;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (t.isParenthesizedExpression(current)) {
|
|
11
|
+
current = current.expression;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (t.isTSInstantiationExpression(current)) current = current.expression;
|
|
15
|
+
}
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
function toStaticObjectKey(key) {
|
|
19
|
+
if (t.isIdentifier(key)) return key.name;
|
|
20
|
+
if (t.isStringLiteral(key)) return key.value;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function getObjectPropertyByKey(node, key) {
|
|
24
|
+
for (const prop of node.properties) {
|
|
25
|
+
if (t.isObjectMethod(prop)) {
|
|
26
|
+
if (toStaticObjectKey(prop.key) === key) return prop;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!t.isObjectProperty(prop) || prop.computed) continue;
|
|
30
|
+
if (toStaticObjectKey(prop.key) === key) return prop;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function resolveRenderableExpression(node) {
|
|
35
|
+
if (t.isObjectMethod(node)) {
|
|
36
|
+
for (const statement of node.body.body) if (t.isReturnStatement(statement) && statement.argument) return unwrapTypeScriptExpression(statement.argument);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (!node.value) return null;
|
|
40
|
+
const value = node.value;
|
|
41
|
+
if (t.isArrowFunctionExpression(value)) {
|
|
42
|
+
if (t.isBlockStatement(value.body)) {
|
|
43
|
+
for (const statement of value.body.body) if (t.isReturnStatement(statement) && statement.argument) return unwrapTypeScriptExpression(statement.argument);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return unwrapTypeScriptExpression(value.body);
|
|
47
|
+
}
|
|
48
|
+
if (t.isFunctionExpression(value)) {
|
|
49
|
+
for (const statement of value.body.body) if (t.isReturnStatement(statement) && statement.argument) return unwrapTypeScriptExpression(statement.argument);
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { getObjectPropertyByKey, resolveRenderableExpression, toStaticObjectKey, unwrapTypeScriptExpression };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { parseJsLike } from "./babel.mjs";
|
|
2
|
+
import { parseSync } from "oxc-parser";
|
|
3
|
+
//#region src/engines/babel.ts
|
|
4
|
+
/**
|
|
5
|
+
* Babel AST 引擎。
|
|
6
|
+
*/
|
|
7
|
+
const babelAstEngine = {
|
|
8
|
+
name: "babel",
|
|
9
|
+
parseJsLike(code) {
|
|
10
|
+
return parseJsLike(code);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/engines/oxc.ts
|
|
15
|
+
/**
|
|
16
|
+
* Oxc AST 引擎。
|
|
17
|
+
*/
|
|
18
|
+
const oxcAstEngine = {
|
|
19
|
+
name: "oxc",
|
|
20
|
+
parseJsLike(code, filename = "inline.ts") {
|
|
21
|
+
return parseSync(filename, code).program;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/engine.ts
|
|
26
|
+
/**
|
|
27
|
+
* 统一解析 JS-like 源码,供 analysis 型 operation 复用。
|
|
28
|
+
*/
|
|
29
|
+
function parseJsLikeWithEngine(code, options) {
|
|
30
|
+
if ((options?.engine ?? "babel") === "oxc") {
|
|
31
|
+
if (typeof options?.parserLike?.parse === "function") return options.parserLike.parse(code);
|
|
32
|
+
return oxcAstEngine.parseJsLike(code, options?.filename);
|
|
33
|
+
}
|
|
34
|
+
return babelAstEngine.parseJsLike(code);
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { oxcAstEngine as n, babelAstEngine as r, parseJsLikeWithEngine as t };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AstEngineName, AstParserLike } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/engine.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* 统一解析 JS-like 源码,供 analysis 型 operation 复用。
|
|
6
|
+
*/
|
|
7
|
+
declare function parseJsLikeWithEngine(code: string, options?: {
|
|
8
|
+
engine?: AstEngineName;
|
|
9
|
+
filename?: string;
|
|
10
|
+
parserLike?: AstParserLike;
|
|
11
|
+
}): unknown;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { parseJsLikeWithEngine };
|
package/dist/engine.mjs
ADDED
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, generate, getVisitorKeys, parse, parseJsLike, traverse } from "./babel.mjs";
|
|
2
|
+
import { getObjectPropertyByKey, resolveRenderableExpression, toStaticObjectKey, unwrapTypeScriptExpression } from "./babelNodes.mjs";
|
|
3
|
+
import { AstEngineName, AstParserLike, WeappAstConfig } from "./types.mjs";
|
|
4
|
+
import { parseJsLikeWithEngine } from "./engine.mjs";
|
|
5
|
+
import { ComponentPropMap, collectComponentPropsFromCode } from "./operations/componentProps.mjs";
|
|
6
|
+
import { FeatureFlagOptions, collectFeatureFlagsFromCode } from "./operations/featureFlags.mjs";
|
|
7
|
+
import { JsxAutoComponentAnalysisOptions, JsxAutoComponentContext, JsxBabelModuleAnalysisOptions, JsxImportedComponent, collectJsxAutoComponentsFromCode, collectJsxImportedComponentsAndDefaultExportFromBabelAst } from "./operations/jsxAutoComponents.mjs";
|
|
8
|
+
import { mayContainPlatformApiAccess, platformApiIdentifiers } from "./operations/platformApi.mjs";
|
|
9
|
+
import { RequireToken, collectRequireTokens, mayContainStaticRequireLiteral } from "./operations/require.mjs";
|
|
10
|
+
import { ScriptSetupImport, collectScriptSetupImportsFromCode } from "./operations/scriptSetupImports.mjs";
|
|
11
|
+
import * as _babel_types0 from "@babel/types";
|
|
12
|
+
import * as oxc_parser0 from "oxc-parser";
|
|
13
|
+
|
|
14
|
+
//#region src/engines/babel.d.ts
|
|
15
|
+
/**
|
|
16
|
+
* Babel AST 引擎。
|
|
17
|
+
*/
|
|
18
|
+
declare const babelAstEngine: {
|
|
19
|
+
name: "babel";
|
|
20
|
+
parseJsLike(code: string): _babel_types0.File;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/engines/oxc.d.ts
|
|
24
|
+
/**
|
|
25
|
+
* Oxc AST 引擎。
|
|
26
|
+
*/
|
|
27
|
+
declare const oxcAstEngine: {
|
|
28
|
+
name: "oxc";
|
|
29
|
+
parseJsLike(code: string, filename?: string): oxc_parser0.Program;
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { type AstEngineName, type AstParserLike, BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, type ComponentPropMap, type FeatureFlagOptions, type JsxAutoComponentAnalysisOptions, type JsxAutoComponentContext, type JsxBabelModuleAnalysisOptions, type JsxImportedComponent, type RequireToken, type ScriptSetupImport, type WeappAstConfig, babelAstEngine, collectComponentPropsFromCode, collectFeatureFlagsFromCode, collectJsxAutoComponentsFromCode, collectJsxImportedComponentsAndDefaultExportFromBabelAst, collectRequireTokens, collectScriptSetupImportsFromCode, generate, getObjectPropertyByKey, getVisitorKeys, mayContainPlatformApiAccess, mayContainStaticRequireLiteral, oxcAstEngine, parse, parseJsLike, parseJsLikeWithEngine, platformApiIdentifiers, resolveRenderableExpression, toStaticObjectKey, traverse, unwrapTypeScriptExpression };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, generate, getVisitorKeys, parse, parseJsLike, traverse } from "./babel.mjs";
|
|
2
|
+
import { getObjectPropertyByKey, resolveRenderableExpression, toStaticObjectKey, unwrapTypeScriptExpression } from "./babelNodes.mjs";
|
|
3
|
+
import { n as oxcAstEngine, r as babelAstEngine, t as parseJsLikeWithEngine } from "./engine-DHqNPCDA.mjs";
|
|
4
|
+
import { collectComponentPropsFromCode } from "./operations/componentProps.mjs";
|
|
5
|
+
import { collectFeatureFlagsFromCode } from "./operations/featureFlags.mjs";
|
|
6
|
+
import { collectJsxAutoComponentsFromCode, collectJsxImportedComponentsAndDefaultExportFromBabelAst } from "./operations/jsxAutoComponents.mjs";
|
|
7
|
+
import { mayContainPlatformApiAccess, platformApiIdentifiers } from "./operations/platformApi.mjs";
|
|
8
|
+
import { collectRequireTokens, mayContainStaticRequireLiteral } from "./operations/require.mjs";
|
|
9
|
+
import { collectScriptSetupImportsFromCode } from "./operations/scriptSetupImports.mjs";
|
|
10
|
+
export { BABEL_TS_MODULE_PARSER_OPTIONS, BABEL_TS_MODULE_PLUGINS, babelAstEngine, collectComponentPropsFromCode, collectFeatureFlagsFromCode, collectJsxAutoComponentsFromCode, collectJsxImportedComponentsAndDefaultExportFromBabelAst, collectRequireTokens, collectScriptSetupImportsFromCode, generate, getObjectPropertyByKey, getVisitorKeys, mayContainPlatformApiAccess, mayContainStaticRequireLiteral, oxcAstEngine, parse, parseJsLike, parseJsLikeWithEngine, platformApiIdentifiers, resolveRenderableExpression, toStaticObjectKey, traverse, unwrapTypeScriptExpression };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AstEngineName } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/operations/componentProps.d.ts
|
|
4
|
+
type ComponentPropMap = Map<string, string>;
|
|
5
|
+
/**
|
|
6
|
+
* 从源码中提取组件属性类型映射。
|
|
7
|
+
*/
|
|
8
|
+
declare function collectComponentPropsFromCode(code: string, options?: {
|
|
9
|
+
astEngine?: AstEngineName;
|
|
10
|
+
}): ComponentPropMap;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { ComponentPropMap, collectComponentPropsFromCode };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { BABEL_TS_MODULE_PARSER_OPTIONS, parse, traverse } from "../babel.mjs";
|
|
2
|
+
import { t as parseJsLikeWithEngine } from "../engine-DHqNPCDA.mjs";
|
|
3
|
+
import { walk } from "oxc-walker";
|
|
4
|
+
//#region src/operations/componentProps.ts
|
|
5
|
+
const CONSTRUCTOR_TYPE_MAP = {
|
|
6
|
+
String: "string",
|
|
7
|
+
StringConstructor: "string",
|
|
8
|
+
Number: "number",
|
|
9
|
+
NumberConstructor: "number",
|
|
10
|
+
Boolean: "boolean",
|
|
11
|
+
BooleanConstructor: "boolean",
|
|
12
|
+
Object: "Record<string, any>",
|
|
13
|
+
ObjectConstructor: "Record<string, any>",
|
|
14
|
+
Array: "any[]",
|
|
15
|
+
ArrayConstructor: "any[]",
|
|
16
|
+
null: "any",
|
|
17
|
+
Null: "any",
|
|
18
|
+
NullConstructor: "any"
|
|
19
|
+
};
|
|
20
|
+
function mapConstructorName(name) {
|
|
21
|
+
if (Object.hasOwn(CONSTRUCTOR_TYPE_MAP, name)) return CONSTRUCTOR_TYPE_MAP[name];
|
|
22
|
+
return CONSTRUCTOR_TYPE_MAP[name.endsWith("Constructor") ? name.slice(0, -11) : name] ?? "any";
|
|
23
|
+
}
|
|
24
|
+
function resolveTypeFromNode(node) {
|
|
25
|
+
if (!node) return "any";
|
|
26
|
+
if (node.type === "Identifier") return mapConstructorName(node.name);
|
|
27
|
+
if (node.type === "StringLiteral" || node.type === "Literal") {
|
|
28
|
+
if (typeof node.value === "string") return mapConstructorName(node.value);
|
|
29
|
+
if (typeof node.value === "number") return "number";
|
|
30
|
+
if (typeof node.value === "boolean") return "boolean";
|
|
31
|
+
}
|
|
32
|
+
if (node.type === "NullLiteral") return "any";
|
|
33
|
+
if (node.type === "NumericLiteral") return "number";
|
|
34
|
+
if (node.type === "BooleanLiteral") return "boolean";
|
|
35
|
+
if (node.type === "MemberExpression") {
|
|
36
|
+
const property = node.property;
|
|
37
|
+
if (property?.type === "Identifier") return mapConstructorName(property.name);
|
|
38
|
+
if (property?.type === "StringLiteral" || property?.type === "Literal" && typeof property.value === "string") return mapConstructorName(property.value);
|
|
39
|
+
return "any";
|
|
40
|
+
}
|
|
41
|
+
if (node.type === "TSExpressionWithTypeArguments") return resolveTypeFromNode(node.expression);
|
|
42
|
+
if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression") return resolveTypeFromNode(node.expression);
|
|
43
|
+
if (node.type === "ArrayExpression") {
|
|
44
|
+
const types = (Array.isArray(node.elements) ? node.elements : []).map((element) => {
|
|
45
|
+
if (!element) return;
|
|
46
|
+
if (element.type === "SpreadElement") return "any";
|
|
47
|
+
return resolveTypeFromNode(element);
|
|
48
|
+
}).filter((value) => Boolean(value));
|
|
49
|
+
if (types.length === 0) return "any";
|
|
50
|
+
return [...new Set(types)].join(" | ");
|
|
51
|
+
}
|
|
52
|
+
return "any";
|
|
53
|
+
}
|
|
54
|
+
function getStaticPropertyName(node) {
|
|
55
|
+
if (!node) return;
|
|
56
|
+
if (node.type === "Identifier") return node.name;
|
|
57
|
+
if (node.type === "StringLiteral" || node.type === "Literal") {
|
|
58
|
+
if (typeof node.value === "string") return node.value;
|
|
59
|
+
}
|
|
60
|
+
if (node.type === "NumericLiteral") return String(node.value);
|
|
61
|
+
if (node.type === "Literal" && typeof node.value === "number") return String(node.value);
|
|
62
|
+
}
|
|
63
|
+
function extractPropertiesObject(node) {
|
|
64
|
+
if (!node || node.type !== "ObjectExpression") return;
|
|
65
|
+
const propMap = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const property of node.properties ?? []) {
|
|
67
|
+
if (property?.type !== "ObjectProperty" && property?.type !== "Property") continue;
|
|
68
|
+
const name = getStaticPropertyName(property.key);
|
|
69
|
+
if (!name) continue;
|
|
70
|
+
const value = property.value;
|
|
71
|
+
if (value?.type === "ObjectExpression") {
|
|
72
|
+
let primaryType;
|
|
73
|
+
const optionalTypes = [];
|
|
74
|
+
for (const option of value.properties ?? []) {
|
|
75
|
+
if (option?.type !== "ObjectProperty" && option?.type !== "Property") continue;
|
|
76
|
+
const optionName = getStaticPropertyName(option.key);
|
|
77
|
+
if (!optionName) continue;
|
|
78
|
+
if (optionName === "type") primaryType = resolveTypeFromNode(option.value);
|
|
79
|
+
else if (optionName === "optionalTypes" && option.value?.type === "ArrayExpression") for (const element of option.value.elements ?? []) {
|
|
80
|
+
if (!element) continue;
|
|
81
|
+
optionalTypes.push(resolveTypeFromNode(element));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const typeCandidates = [primaryType, ...optionalTypes].filter((candidate) => Boolean(candidate && candidate.trim().length > 0));
|
|
85
|
+
const deduped = [...new Set(typeCandidates)];
|
|
86
|
+
propMap.set(name, deduped.length > 0 ? deduped.join(" | ") : "any");
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
propMap.set(name, resolveTypeFromNode(value));
|
|
90
|
+
}
|
|
91
|
+
return propMap;
|
|
92
|
+
}
|
|
93
|
+
function resolveOptionsObjectExpression(node, bindings) {
|
|
94
|
+
if (!node) return;
|
|
95
|
+
if (node.type === "ObjectExpression") return node;
|
|
96
|
+
if (node.type === "Identifier") return bindings.get(node.name);
|
|
97
|
+
if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression") return resolveOptionsObjectExpression(node.expression, bindings);
|
|
98
|
+
}
|
|
99
|
+
function resolveOptionsObjectExpressionWithBabel(path, node) {
|
|
100
|
+
if (!node) return;
|
|
101
|
+
if (node.type === "ObjectExpression") return node;
|
|
102
|
+
if (node.type === "Identifier") {
|
|
103
|
+
const bindingPath = path.scope.getBinding(node.name)?.path;
|
|
104
|
+
if (bindingPath?.isVariableDeclarator()) {
|
|
105
|
+
const init = bindingPath.node.init;
|
|
106
|
+
if (init?.type === "ObjectExpression") return init;
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression") return resolveOptionsObjectExpressionWithBabel(path, node.expression);
|
|
111
|
+
}
|
|
112
|
+
function extractComponentProperties(optionsNode) {
|
|
113
|
+
for (const property of optionsNode?.properties ?? []) {
|
|
114
|
+
if (property?.type !== "ObjectProperty" && property?.type !== "Property") continue;
|
|
115
|
+
const name = getStaticPropertyName(property.key);
|
|
116
|
+
if (name !== "properties" && name !== "props") continue;
|
|
117
|
+
if (property.value?.type === "ObjectExpression") return extractPropertiesObject(property.value) ?? /* @__PURE__ */ new Map();
|
|
118
|
+
return /* @__PURE__ */ new Map();
|
|
119
|
+
}
|
|
120
|
+
return /* @__PURE__ */ new Map();
|
|
121
|
+
}
|
|
122
|
+
function collectComponentPropsWithBabel(code) {
|
|
123
|
+
const ast = parse(code, BABEL_TS_MODULE_PARSER_OPTIONS);
|
|
124
|
+
let props = /* @__PURE__ */ new Map();
|
|
125
|
+
traverse(ast, { CallExpression(path) {
|
|
126
|
+
if (props.size > 0) return;
|
|
127
|
+
const [options] = path.node.arguments;
|
|
128
|
+
const optionsObject = resolveOptionsObjectExpressionWithBabel(path, options);
|
|
129
|
+
if (!optionsObject) return;
|
|
130
|
+
props = extractComponentProperties(optionsObject);
|
|
131
|
+
} });
|
|
132
|
+
return props;
|
|
133
|
+
}
|
|
134
|
+
function collectComponentPropsWithOxc(code) {
|
|
135
|
+
const ast = parseJsLikeWithEngine(code, {
|
|
136
|
+
engine: "oxc",
|
|
137
|
+
filename: "inline.ts"
|
|
138
|
+
});
|
|
139
|
+
const props = /* @__PURE__ */ new Map();
|
|
140
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
141
|
+
let resolved = false;
|
|
142
|
+
walk(ast, { enter(node) {
|
|
143
|
+
if (resolved) return;
|
|
144
|
+
if (node.type === "VariableDeclarator" && node.id?.type === "Identifier") {
|
|
145
|
+
const optionsObject = resolveOptionsObjectExpression(node.init, bindings);
|
|
146
|
+
if (optionsObject) bindings.set(node.id.name, optionsObject);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (node.type !== "CallExpression") return;
|
|
150
|
+
const [options] = node.arguments ?? [];
|
|
151
|
+
const optionsObject = resolveOptionsObjectExpression(options, bindings);
|
|
152
|
+
if (!optionsObject) return;
|
|
153
|
+
resolved = true;
|
|
154
|
+
for (const [key, value] of extractComponentProperties(optionsObject)) props.set(key, value);
|
|
155
|
+
} });
|
|
156
|
+
return props;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 从源码中提取组件属性类型映射。
|
|
160
|
+
*/
|
|
161
|
+
function collectComponentPropsFromCode(code, options) {
|
|
162
|
+
const engine = options?.astEngine ?? "babel";
|
|
163
|
+
try {
|
|
164
|
+
return engine === "oxc" ? collectComponentPropsWithOxc(code) : collectComponentPropsWithBabel(code);
|
|
165
|
+
} catch {
|
|
166
|
+
return /* @__PURE__ */ new Map();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { collectComponentPropsFromCode };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AstEngineName } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/operations/featureFlags.d.ts
|
|
4
|
+
interface FeatureFlagOptions<TFeature extends string> {
|
|
5
|
+
astEngine?: AstEngineName;
|
|
6
|
+
moduleId: string;
|
|
7
|
+
hookToFeature: Record<string, TFeature>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 根据模块 ID 与 hook 映射表,从源码中收集启用的特性标识。
|
|
11
|
+
*/
|
|
12
|
+
declare function collectFeatureFlagsFromCode<TFeature extends string>(code: string, options: FeatureFlagOptions<TFeature>): Set<TFeature>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { FeatureFlagOptions, collectFeatureFlagsFromCode };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { parseJsLike, traverse } from "../babel.mjs";
|
|
2
|
+
import { t as parseJsLikeWithEngine } from "../engine-DHqNPCDA.mjs";
|
|
3
|
+
import * as t from "@babel/types";
|
|
4
|
+
import { walk } from "oxc-walker";
|
|
5
|
+
//#region src/operations/featureFlags.ts
|
|
6
|
+
function collectWithBabel(code, moduleId, hookToFeature) {
|
|
7
|
+
const ast = parseJsLike(code);
|
|
8
|
+
const namedHookLocals = /* @__PURE__ */ new Map();
|
|
9
|
+
const namespaceLocals = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const stmt of ast.program.body) {
|
|
11
|
+
if (!t.isImportDeclaration(stmt) || stmt.source.value !== moduleId) continue;
|
|
12
|
+
for (const specifier of stmt.specifiers) if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) {
|
|
13
|
+
const matched = hookToFeature[specifier.imported.name];
|
|
14
|
+
if (matched) namedHookLocals.set(specifier.local.name, matched);
|
|
15
|
+
} else if (t.isImportNamespaceSpecifier(specifier)) namespaceLocals.add(specifier.local.name);
|
|
16
|
+
}
|
|
17
|
+
if (namedHookLocals.size === 0 && namespaceLocals.size === 0) return /* @__PURE__ */ new Set();
|
|
18
|
+
const enabled = /* @__PURE__ */ new Set();
|
|
19
|
+
function consumeHookCallByName(name) {
|
|
20
|
+
const matched = namedHookLocals.get(name);
|
|
21
|
+
if (matched) enabled.add(matched);
|
|
22
|
+
}
|
|
23
|
+
function consumeNamespaceHookCall(namespace, hookName) {
|
|
24
|
+
if (!namespaceLocals.has(namespace)) return;
|
|
25
|
+
const matched = hookToFeature[hookName];
|
|
26
|
+
if (matched) enabled.add(matched);
|
|
27
|
+
}
|
|
28
|
+
traverse(ast, {
|
|
29
|
+
CallExpression(path) {
|
|
30
|
+
const callee = path.node.callee;
|
|
31
|
+
if (t.isIdentifier(callee)) {
|
|
32
|
+
consumeHookCallByName(callee.name);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.object)) {
|
|
36
|
+
const property = callee.property;
|
|
37
|
+
if (t.isIdentifier(property)) consumeNamespaceHookCall(callee.object.name, property.name);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
OptionalCallExpression(path) {
|
|
41
|
+
const callee = path.node.callee;
|
|
42
|
+
if (t.isIdentifier(callee)) {
|
|
43
|
+
consumeHookCallByName(callee.name);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.object)) {
|
|
47
|
+
const property = callee.property;
|
|
48
|
+
if (t.isIdentifier(property)) consumeNamespaceHookCall(callee.object.name, property.name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return enabled;
|
|
53
|
+
}
|
|
54
|
+
function collectWithOxc(code, moduleId, hookToFeature) {
|
|
55
|
+
const ast = parseJsLikeWithEngine(code, {
|
|
56
|
+
engine: "oxc",
|
|
57
|
+
filename: "inline.ts"
|
|
58
|
+
});
|
|
59
|
+
const namedHookLocals = /* @__PURE__ */ new Map();
|
|
60
|
+
const namespaceLocals = /* @__PURE__ */ new Set();
|
|
61
|
+
for (const statement of ast.body ?? []) {
|
|
62
|
+
if (statement?.type !== "ImportDeclaration" || statement.source?.value !== moduleId) continue;
|
|
63
|
+
for (const specifier of statement.specifiers ?? []) if (specifier.type === "ImportSpecifier" && specifier.imported?.type === "Identifier") {
|
|
64
|
+
const matched = hookToFeature[specifier.imported.name];
|
|
65
|
+
if (matched && specifier.local?.type === "Identifier") namedHookLocals.set(specifier.local.name, matched);
|
|
66
|
+
} else if (specifier.type === "ImportNamespaceSpecifier" && specifier.local?.type === "Identifier") namespaceLocals.add(specifier.local.name);
|
|
67
|
+
}
|
|
68
|
+
if (namedHookLocals.size === 0 && namespaceLocals.size === 0) return /* @__PURE__ */ new Set();
|
|
69
|
+
const enabled = /* @__PURE__ */ new Set();
|
|
70
|
+
walk(ast, { enter(node) {
|
|
71
|
+
if (node.type !== "CallExpression") return;
|
|
72
|
+
const callee = node.callee;
|
|
73
|
+
if (callee?.type === "Identifier") {
|
|
74
|
+
const matched = namedHookLocals.get(callee.name);
|
|
75
|
+
if (matched) enabled.add(matched);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (callee?.type === "MemberExpression" && callee.object?.type === "Identifier" && callee.property?.type === "Identifier" && namespaceLocals.has(callee.object.name)) {
|
|
79
|
+
const matched = hookToFeature[callee.property.name];
|
|
80
|
+
if (matched) enabled.add(matched);
|
|
81
|
+
}
|
|
82
|
+
} });
|
|
83
|
+
return enabled;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 根据模块 ID 与 hook 映射表,从源码中收集启用的特性标识。
|
|
87
|
+
*/
|
|
88
|
+
function collectFeatureFlagsFromCode(code, options) {
|
|
89
|
+
const engine = options.astEngine ?? "babel";
|
|
90
|
+
try {
|
|
91
|
+
return engine === "oxc" ? collectWithOxc(code, options.moduleId, options.hookToFeature) : collectWithBabel(code, options.moduleId, options.hookToFeature);
|
|
92
|
+
} catch {
|
|
93
|
+
return /* @__PURE__ */ new Set();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { collectFeatureFlagsFromCode };
|