@vitarx/plugin-vite 0.0.1-beta.8 → 1.0.0-beta.1

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/README.md CHANGED
@@ -222,6 +222,30 @@ import.meta.hot.accept(mod => {
222
222
  2. **被导出** - 使用 `export` 导出
223
223
  3. **包含 JSX** - 函数体内包含 JSX 语法或返回编译宏组件
224
224
 
225
+ ### HMR 代码分离
226
+
227
+ HMR 在检测到组件更新时,会将组件代码分离为 **UI 代码** 和 **逻辑代码** 两部分:
228
+
229
+ - **UI 代码**:`createView`、`branch`、`expr`、`accessor`、`dynamic`、`withDirectives` 等调用
230
+ - **逻辑代码**:其余所有代码
231
+
232
+ 仅逻辑代码变化时,组件完全重新挂载;仅 UI 代码变化时,仅重建视图树。
233
+
234
+ ### 开发模式位置信息
235
+
236
+ 开发模式下,`createView` 调用会注入位置信息参数(用于调试),`branch`、`expr`、`accessor` 不注入:
237
+
238
+ ```javascript
239
+ // dev 模式
240
+ createView("div", { children: "hello" }, { fileName: "App.tsx", lineNumber: 5, columnNumber: 10 })
241
+ expr(() => a && b) // 无位置信息
242
+ branch(() => unref(show) ? 0 : 1) // 无位置信息
243
+ accessor(props, "value") // 无位置信息
244
+
245
+ // 生产模式
246
+ createView("div", { children: "hello" }) // 无位置信息
247
+ ```
248
+
225
249
  ## 子元素处理
226
250
 
227
251
  ### 响应式子元素
@@ -230,19 +254,55 @@ import.meta.hot.accept(mod => {
230
254
  // 标识符保持原样
231
255
  <div>{value}</div>
232
256
 
233
- // 成员表达式使用 access
257
+ // 成员表达式使用 accessor
234
258
  <div>{props.value}</div>
235
- // 编译为: access(props, 'value')
259
+ // 编译为: accessor(props, 'value')
236
260
 
237
261
  // 条件表达式使用 branch
238
262
  <div>{show ? 'yes' : 'no'}</div>
239
263
  // 编译为: branch(() => unref(show) ? 0 : 1, [...])
240
264
 
241
- // 逻辑表达式使用 dynamic
265
+ // 逻辑表达式使用 expr
242
266
  <div>{a && b}</div>
243
- // 编译为: dynamic(() => a && b)
267
+ // 编译为: expr(() => a && b)
268
+
269
+ // 二元表达式使用 expr
270
+ <div>{count + 1}</div>
271
+ // 编译为: expr(() => count + 1)
272
+
273
+ // 函数调用使用 expr
274
+ <div>{render()}</div>
275
+ // 编译为: expr(() => render())
244
276
  ```
245
277
 
278
+ ### expr 与数组返回值
279
+
280
+ `expr` 底层由 `DynamicView` 渲染。当 `expr` 中的表达式返回数组时(如 `arr.map()`),运行时会自动检测并原样返回数组(仅渲染一次,不跟踪响应式)。推荐使用 `For` 组件实现动态数组的响应式渲染。
281
+
282
+ ```jsx
283
+ // arr.map() 返回数组,expr 运行时原样返回(仅渲染一次)
284
+ <div>{items.map(item => <span>{item}</span>)}</div>
285
+ // 编译为: expr(() => items.map(item => createView("span", { children: item })))
286
+
287
+ // 推荐:使用 For 组件实现响应式数组渲染
288
+ <div>
289
+ <For each={items} key="id">{item => <span>{item}</span>}</For>
290
+ </div>
291
+ ```
292
+
293
+ ### 运行时 API 说明
294
+
295
+ | API | 用途 | 编译器注入 | HMR 识别为 UI 代码 |
296
+ |------------------|---------|-----------|---------------|
297
+ | `createView` | 创建视图节点 | ✅ | ✅ |
298
+ | `branch` | 条件分支渲染 | ✅ | ✅ |
299
+ | `expr` | 动态表达式包装 | ✅ | ✅ |
300
+ | `accessor` | 属性访问追踪 | ✅ | ✅ |
301
+ | `dynamic` | 动态值包装 | ❌(用户手动使用) | ✅ |
302
+ | `withDirectives` | 指令绑定 | ✅ | ✅ |
303
+
304
+ > **注意:** `expr` 是编译器插桩注入的辅助函数,用于包装逻辑/二元/调用表达式;`dynamic` 是运行时独立 API,供用户手动使用。两者在 HMR 代码分离时均被视为 UI 描述代码。
305
+
246
306
  ## API 参考
247
307
 
248
308
  ### 插件选项
@@ -2,21 +2,11 @@
2
2
  * Vitarx 运行时 API 名称
3
3
  * 这些 API 需要从 vitarx 包导入
4
4
  */
5
- export declare const VITARX_APIS: {
6
- readonly CREATE_VIEW: "createView";
7
- readonly FRAGMENT: "Fragment";
8
- readonly BRANCH: "branch";
9
- readonly DYNAMIC: "dynamic";
10
- readonly ACCESS: "access";
11
- readonly WITH_DIRECTIVES: "withDirectives";
12
- readonly UNREF: "unref";
13
- readonly IS_REF: "isRef";
14
- };
15
5
  /**
16
6
  * UI 相关的 API 名称列表
17
7
  * 这些 API 用于创建视图,在 HMR 时需要识别
18
8
  */
19
- export declare const UI_API_NAMES: readonly ["createView", "branch", "dynamic", "access", "withDirectives"];
9
+ export declare const UI_API_NAMES: readonly ["createView", "branch", "dynamic", "expr", "accessor", "withDirectives"];
20
10
  /**
21
11
  * Ref 相关 API 名称
22
12
  * 用于识别 ref 变量
@@ -2,21 +2,11 @@
2
2
  * Vitarx 运行时 API 名称
3
3
  * 这些 API 需要从 vitarx 包导入
4
4
  */
5
- export const VITARX_APIS = {
6
- CREATE_VIEW: 'createView',
7
- FRAGMENT: 'Fragment',
8
- BRANCH: 'branch',
9
- DYNAMIC: 'dynamic',
10
- ACCESS: 'access',
11
- WITH_DIRECTIVES: 'withDirectives',
12
- UNREF: 'unref',
13
- IS_REF: 'isRef'
14
- };
15
5
  /**
16
6
  * UI 相关的 API 名称列表
17
7
  * 这些 API 用于创建视图,在 HMR 时需要识别
18
8
  */
19
- export const UI_API_NAMES = ['createView', 'branch', 'dynamic', 'access', 'withDirectives'];
9
+ export const UI_API_NAMES = ['createView', 'branch', 'dynamic', 'expr', 'accessor', 'withDirectives'];
20
10
  /**
21
11
  * Ref 相关 API 名称
22
12
  * 用于识别 ref 变量
package/dist/context.d.ts CHANGED
@@ -17,10 +17,10 @@ export interface ImportInfo {
17
17
  Fragment: boolean;
18
18
  /** branch - 条件分支函数 */
19
19
  branch: boolean;
20
- /** dynamic - 动态值包装函数 */
21
- dynamic: boolean;
22
- /** access - 属性访问函数 */
23
- access: boolean;
20
+ /** expr - 动态表达式包装函数 */
21
+ expr: boolean;
22
+ /** accessor - 属性访问函数 */
23
+ accessor: boolean;
24
24
  /** withDirectives - 指令包装函数 */
25
25
  withDirectives: boolean;
26
26
  /** unref - ref 解包函数 */
@@ -36,8 +36,8 @@ export interface VitarxImportAliases {
36
36
  createView: string | null;
37
37
  Fragment: string | null;
38
38
  branch: string | null;
39
- dynamic: string | null;
40
- access: string | null;
39
+ expr: string | null;
40
+ accessor: string | null;
41
41
  withDirectives: string | null;
42
42
  unref: string | null;
43
43
  isRef: string | null;
@@ -78,8 +78,6 @@ export interface TransformContext {
78
78
  refApiAliases: RefApiAliases;
79
79
  /** ref 变量名集合 */
80
80
  refVariables: Set<string>;
81
- /** UI API 别名集合(用于 HMR 代码分离识别) */
82
- uiApiAliases: Set<string>;
83
81
  /** builder 函数的本地别名(用于识别纯构建组件) */
84
82
  builderAlias: string | null;
85
83
  /** 已处理的 AST 节点追踪集合 */
package/dist/context.js CHANGED
@@ -18,8 +18,8 @@ export function createContext(code, id, options, ast) {
18
18
  createView: false,
19
19
  Fragment: false,
20
20
  branch: false,
21
- dynamic: false,
22
- access: false,
21
+ expr: false,
22
+ accessor: false,
23
23
  withDirectives: false,
24
24
  unref: false,
25
25
  isRef: false
@@ -29,8 +29,8 @@ export function createContext(code, id, options, ast) {
29
29
  createView: null,
30
30
  Fragment: null,
31
31
  branch: null,
32
- dynamic: null,
33
- access: null,
32
+ expr: null,
33
+ accessor: null,
34
34
  withDirectives: null,
35
35
  unref: null,
36
36
  isRef: null
@@ -43,7 +43,6 @@ export function createContext(code, id, options, ast) {
43
43
  computed: null
44
44
  },
45
45
  refVariables: new Set(),
46
- uiApiAliases: new Set(),
47
46
  builderAlias: null,
48
47
  processedNodes: new WeakSet(),
49
48
  pureCommentedNodes: new WeakSet(),
@@ -11,7 +11,7 @@ export interface ChangeCode {
11
11
  * 判断两个函数组件的代码差异
12
12
  *
13
13
  * 分析策略:
14
- * 1. 解析函数代码,提取所有 createView/branch/dynamic 等 UI API 调用
14
+ * 1. 解析函数代码,提取所有 createView/branch/expr 等 UI API 调用
15
15
  * 2. 将这些调用识别为 UI 描述代码
16
16
  * 3. 其余代码识别为非 UI 代码(逻辑代码)
17
17
  * 4. 分别比较 UI 代码和非 UI 代码是否变化
@@ -13,13 +13,16 @@ import { UI_API_NAMES } from '../constants/index.js';
13
13
  const UI_APIS = new Set([...UI_API_NAMES, 'jsxDEV', 'h']);
14
14
  /**
15
15
  * 判断标识符名称是否为 UI API
16
- * 支持原始名称和带后缀的名称(如 jsxDEV$1)
16
+ * 支持原始名称和带后缀的名称(如 jsxDEV$1、branch$2、expr$1)
17
17
  */
18
18
  function isUIApi(name) {
19
19
  if (UI_APIS.has(name))
20
20
  return true;
21
- // 支持带数字后缀的 createView
22
- return /^jsxDEV\$\d+$/.test(name) || /^createView\$\d+$/.test(name);
21
+ for (const api of UI_APIS) {
22
+ if (name.startsWith(api + '$'))
23
+ return true;
24
+ }
25
+ return false;
23
26
  }
24
27
  /**
25
28
  * 从函数代码中分离 UI 代码和非 UI 代码
@@ -100,7 +103,7 @@ function normalizeCode(code) {
100
103
  * 判断两个函数组件的代码差异
101
104
  *
102
105
  * 分析策略:
103
- * 1. 解析函数代码,提取所有 createView/branch/dynamic 等 UI API 调用
106
+ * 1. 解析函数代码,提取所有 createView/branch/expr 等 UI API 调用
104
107
  * 2. 将这些调用识别为 UI 描述代码
105
108
  * 3. 其余代码识别为非 UI 代码(逻辑代码)
106
109
  * 4. 分别比较 UI 代码和非 UI 代码是否变化
package/dist/index.js CHANGED
@@ -32,10 +32,10 @@ export default function vitarx(options) {
32
32
  let viteConfig;
33
33
  return {
34
34
  name: 'vite-plugin-vitarx',
35
+ enforce: 'pre',
35
36
  config(config, env) {
36
37
  isDEV = env.command === 'serve' && !env.isPreview;
37
- const configSSR = !!config.build?.ssr;
38
- isSSR = env.isSsrBuild === true || configSSR;
38
+ isSSR = env.isSsrBuild === true;
39
39
  return {
40
40
  define: {
41
41
  __VITARX_DEV__: JSON.stringify(isDEV),
@@ -52,7 +52,11 @@ export default function vitarx(options) {
52
52
  };
53
53
  },
54
54
  configResolved(config) {
55
- viteConfig = config;
55
+ // 根据最终的配置结果,确定是否是 SSR 模式
56
+ if (!isSSR) {
57
+ isSSR = !!config.build?.ssr;
58
+ config.define['__VITARX_SSR__'] = JSON.stringify(isSSR);
59
+ }
56
60
  compileOptions = {
57
61
  dev: isDEV,
58
62
  ssr: isSSR,
@@ -1,6 +1,6 @@
1
1
  import { isJSXElement } from '@babel/types';
2
2
  import { createError } from '../../error.js';
3
- import { collectVIfChainInfo, createArrowFunction, createBranch, filterWhitespaceChildren, getDevLocInfo, hasDirective, removeVIfChainDirectives, validateVIfChain } from '../../utils/index.js';
3
+ import { collectVIfChainInfo, createArrowFunction, createBranch, filterWhitespaceChildren, hasDirective, removeVIfChainDirectives, validateVIfChain } from '../../utils/index.js';
4
4
  /**
5
5
  * 处理 IfBlock 组件
6
6
  */
@@ -15,9 +15,7 @@ export function processIfBlock(path, ctx) {
15
15
  const { nodes, conditions } = collectVIfChainInfo(jsxChildren);
16
16
  nodes.forEach(node => removeVIfChainDirectives(node));
17
17
  const branches = nodes.map(node => createArrowFunction(node));
18
- // 获取位置信息(仅开发环境)
19
- const locInfo = getDevLocInfo(ctx, path.node);
20
- const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
18
+ const branchCall = createBranch({ conditions, branches }, ctx);
21
19
  if (path.node.loc) {
22
20
  branchCall.loc = path.node.loc;
23
21
  }
@@ -1,7 +1,7 @@
1
1
  import * as t from '@babel/types';
2
2
  import { isJSXElement, isJSXText } from '@babel/types';
3
3
  import { createError } from '../../error.js';
4
- import { createArrowFunction, createBranch, getDevLocInfo, getJSXAttributeByName, getJSXElementName } from '../../utils/index.js';
4
+ import { createArrowFunction, createBranch, getJSXAttributeByName, getJSXElementName } from '../../utils/index.js';
5
5
  /**
6
6
  * 处理 Switch 组件
7
7
  * @param path - JSX 元素路径
@@ -20,11 +20,9 @@ export function processSwitch(path, ctx) {
20
20
  conditions.push(t.booleanLiteral(true));
21
21
  branches.push(createArrowFunction(fallbackValue));
22
22
  }
23
- // 获取位置信息(仅开发环境)
24
- const locInfo = getDevLocInfo(ctx, path.node);
25
23
  // 生成 branch 调用
26
24
  // Switch 的 when 条件如果是标识符,需要 unref
27
- const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
25
+ const branchCall = createBranch({ conditions, branches }, ctx);
28
26
  if (path.node.loc) {
29
27
  branchCall.loc = path.node.loc;
30
28
  }
@@ -2,5 +2,5 @@
2
2
  * 指令处理模块
3
3
  * @module passes/directives
4
4
  */
5
- export { processVIfChain } from './vIf.js';
5
+ export { processVIfChain, transformChildrenVIfChains } from './vIf.js';
6
6
  export { processDirectives } from './processDirectives.js';
@@ -2,5 +2,5 @@
2
2
  * 指令处理模块
3
3
  * @module passes/directives
4
4
  */
5
- export { processVIfChain } from './vIf.js';
5
+ export { processVIfChain, transformChildrenVIfChains } from './vIf.js';
6
6
  export { processDirectives } from './processDirectives.js';
@@ -1,20 +1,27 @@
1
1
  /**
2
2
  * v-if 链处理器
3
- * 处理 Fragment 中的 v-if/v-else-if/v-else 链
3
+ * 处理 Fragment/Element 中的 v-if/v-else-if/v-else 链
4
4
  * @module passes/directives/vIf
5
5
  */
6
6
  import type { NodePath } from '@babel/traverse';
7
7
  import * as t from '@babel/types';
8
8
  import { TransformContext } from '../../context.js';
9
+ type TransformJSXElementFn = (node: t.JSXElement, ctx: TransformContext, handleVIf: boolean) => t.Expression | null;
10
+ type JSXContainerPath = NodePath<t.JSXFragment | t.JSXElement>;
9
11
  /**
10
- * JSX 元素转换函数类型
12
+ * 处理子元素数组中的 v-if 链
13
+ * 收集并转换 v-if/v-else-if/v-else 链为 branch 调用
14
+ * @param children - 子元素数组(会被原地修改)
15
+ * @param ctx - 转换上下文
16
+ * @param transformJSXElement - JSX 元素转换函数
17
+ * @returns 处理后的子元素数组(链节点已替换为 branch 调用,nullLiteral 已移除)
11
18
  */
12
- type TransformJSXElementFn = (node: t.JSXElement, ctx: TransformContext, handleVIf: boolean) => t.Expression | null;
19
+ export declare function transformChildrenVIfChains(children: t.Node[], ctx: TransformContext, transformJSXElement: TransformJSXElementFn): t.Node[];
13
20
  /**
14
- * 处理 Fragment 中的 v-if 链
15
- * @param path - JSX Fragment 路径
21
+ * 处理 Fragment/Element 中的 v-if 链
22
+ * @param path - JSX Fragment 或 Element 路径
16
23
  * @param ctx - 转换上下文
17
24
  * @param transformJSXElement - JSX 元素转换函数
18
25
  */
19
- export declare function processVIfChain(path: NodePath<t.JSXFragment>, ctx: TransformContext, transformJSXElement: TransformJSXElementFn): void;
26
+ export declare function processVIfChain(path: JSXContainerPath, ctx: TransformContext, transformJSXElement: TransformJSXElementFn): void;
20
27
  export {};
@@ -1,30 +1,45 @@
1
1
  import * as t from '@babel/types';
2
2
  import { isJSXElement, isJSXText } from '@babel/types';
3
- import { collectFragmentVIfChains, createArrowFunction, createBranch, getDevLocInfo, isWhitespaceJSXText, removeVIfChainDirectives } from '../../utils/index.js';
3
+ import { collectFragmentVIfChains, createArrowFunction, createBranch, isWhitespaceJSXText, removeVIfChainDirectives } from '../../utils/index.js';
4
4
  /**
5
- * 处理 Fragment 中的 v-if 链
6
- * @param path - JSX Fragment 路径
5
+ * 处理子元素数组中的 v-if 链
6
+ * 收集并转换 v-if/v-else-if/v-else 链为 branch 调用
7
+ * @param children - 子元素数组(会被原地修改)
7
8
  * @param ctx - 转换上下文
8
9
  * @param transformJSXElement - JSX 元素转换函数
10
+ * @returns 处理后的子元素数组(链节点已替换为 branch 调用,nullLiteral 已移除)
9
11
  */
10
- export function processVIfChain(path, ctx, transformJSXElement) {
11
- const children = path.node.children;
12
+ export function transformChildrenVIfChains(children, ctx, transformJSXElement) {
12
13
  const chains = collectFragmentVIfChains(children);
13
14
  if (chains.length === 0)
14
- return;
15
- // 从后向前处理链,避免索引偏移问题
15
+ return children;
16
16
  for (let c = chains.length - 1; c >= 0; c--) {
17
17
  processVIfChainItem(chains[c], children, ctx, transformJSXElement);
18
18
  }
19
- // 清理已处理的节点
20
- cleanupProcessedChildren(path, children);
19
+ return children.filter(child => {
20
+ if (t.isNullLiteral(child))
21
+ return false;
22
+ return !(isJSXText(child) && isWhitespaceJSXText(child));
23
+ });
24
+ }
25
+ /**
26
+ * 处理 Fragment/Element 中的 v-if 链
27
+ * @param path - JSX Fragment 或 Element 路径
28
+ * @param ctx - 转换上下文
29
+ * @param transformJSXElement - JSX 元素转换函数
30
+ */
31
+ export function processVIfChain(path, ctx, transformJSXElement) {
32
+ const children = path.node.children;
33
+ const processed = transformChildrenVIfChains(children, ctx, transformJSXElement);
34
+ if (processed !== children) {
35
+ path.node.children = processed;
36
+ }
21
37
  }
22
38
  /**
23
39
  * 处理单个 v-if 链
24
40
  */
25
41
  function processVIfChainItem(chain, children, ctx, transformJSXElement) {
26
42
  const { nodes, conditions, endIndex } = chain;
27
- // 计算链在 children 中的起始位置
28
43
  let start = -1;
29
44
  for (let i = 0; i < children.length; i++) {
30
45
  const child = children[i];
@@ -33,7 +48,6 @@ function processVIfChainItem(chain, children, ctx, transformJSXElement) {
33
48
  break;
34
49
  }
35
50
  }
36
- // 移除指令并转换节点
37
51
  const branches = [];
38
52
  for (const node of nodes) {
39
53
  removeVIfChainDirectives(node);
@@ -42,12 +56,7 @@ function processVIfChainItem(chain, children, ctx, transformJSXElement) {
42
56
  branches.push(createArrowFunction(transformedNode));
43
57
  }
44
58
  }
45
- // 获取第一个节点的位置信息(仅开发环境)
46
- const firstNode = nodes[0];
47
- const locInfo = getDevLocInfo(ctx, firstNode);
48
- // 生成 branch 调用
49
- const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
50
- // 替换链的第一个节点,其他节点标记为 null
59
+ const branchCall = createBranch({ conditions, branches }, ctx);
51
60
  if (start >= 0) {
52
61
  children[start] = branchCall;
53
62
  for (let k = start + 1; k <= endIndex; k++) {
@@ -55,13 +64,3 @@ function processVIfChainItem(chain, children, ctx, transformJSXElement) {
55
64
  }
56
65
  }
57
66
  }
58
- /**
59
- * 清理已处理的子节点
60
- */
61
- function cleanupProcessedChildren(path, children) {
62
- path.node.children = children.filter(child => {
63
- if (t.isNullLiteral(child))
64
- return false;
65
- return !(isJSXText(child) && isWhitespaceJSXText(child));
66
- });
67
- }
@@ -15,7 +15,7 @@ import { GET_COMPONENT_VIEW_ALIAS } from './inject-imports.js';
15
15
  export function createHMRRegistrationStatements(variableNames) {
16
16
  const statements = [];
17
17
  statements.push(t.variableDeclaration('const', [
18
- t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), []))
18
+ t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
19
19
  ]));
20
20
  statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view)])));
21
21
  const stateProperties = variableNames.map(name => t.objectMethod('get', t.identifier(name), [], t.blockStatement([t.returnStatement(t.identifier(name))])));
@@ -35,7 +35,7 @@ export function createHMRRegistrationStatements(variableNames) {
35
35
  export function createWrappedComponentHMRStatements(componentName) {
36
36
  const statements = [];
37
37
  statements.push(t.variableDeclaration('const', [
38
- t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), []))
38
+ t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
39
39
  ]));
40
40
  statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view), t.identifier(componentName)])));
41
41
  return statements;
@@ -13,8 +13,8 @@ const API_IMPORT_CONFIG = [
13
13
  { name: 'createView', importKey: 'createView' },
14
14
  { name: 'Fragment', importKey: 'Fragment' },
15
15
  { name: 'branch', importKey: 'branch' },
16
- { name: 'dynamic', importKey: 'dynamic' },
17
- { name: 'access', importKey: 'access' },
16
+ { name: 'expr', importKey: 'expr' },
17
+ { name: 'accessor', importKey: 'accessor' },
18
18
  { name: 'withDirectives', importKey: 'withDirectives' },
19
19
  { name: 'unref', importKey: 'unref' },
20
20
  { name: 'isRef', importKey: 'isRef' }
@@ -4,25 +4,28 @@
4
4
  * @module passes/jsx/processChildren
5
5
  */
6
6
  import * as t from '@babel/types';
7
- import { isBooleanLiteral, isConditionalExpression, isIdentifier, isJSXElement, isJSXExpressionContainer, isJSXFragment, isJSXText, isLogicalExpression, isMemberExpression, isNumericLiteral, isStringLiteral } from '@babel/types';
7
+ import { isBinaryExpression, isConditionalExpression, isIdentifier, isJSXElement, isJSXExpressionContainer, isJSXFragment, isJSXText, isLogicalExpression, isMemberExpression } from '@babel/types';
8
8
  import { markImport } from '../../context.js';
9
- import { addPureComment, createAccessCall, createBinaryBranch, createDynamicCall, getAlias, getDevLocInfo } from '../../utils/index.js';
9
+ import { addPureComment, createAccessorCall, createBinaryBranch, createExprCall, getAlias } from '../../utils/index.js';
10
10
  /**
11
- * 处理成员表达式
11
+ * 处理成员表达式,生成 accessor 调用
12
12
  */
13
13
  function handleMemberExpression(expr, ctx) {
14
- markImport(ctx, 'access');
15
- const accessAlias = getAlias(ctx.vitarxAliases, 'access');
16
- return createAccessCall(expr.object, expr.property, accessAlias);
14
+ markImport(ctx, 'accessor');
15
+ const accessorAlias = getAlias(ctx.vitarxAliases, 'accessor');
16
+ return createAccessorCall(expr.object, expr.property, accessorAlias);
17
17
  }
18
18
  /**
19
- * 处理逻辑表达式
19
+ * 使用 expr 包装表达式
20
+ * 用于逻辑表达式、二元表达式和调用表达式的统一包装
21
+ * 运行时根据返回值类型决定是否跟踪响应式:
22
+ * - 数组返回值:原样返回(仅渲染一次)
23
+ * - 其他返回值:创建 DynamicView 追踪
20
24
  */
21
- function handleLogicalExpression(expr, ctx, loc) {
22
- markImport(ctx, 'dynamic');
23
- const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
24
- const locInfo = loc ? getDevLocInfo(ctx, { loc }) : null;
25
- return addPureComment(createDynamicCall(expr, dynamicAlias, locInfo), ctx);
25
+ function wrapWithExpr(expr, ctx) {
26
+ markImport(ctx, 'expr');
27
+ const exprAlias = getAlias(ctx.vitarxAliases, 'expr');
28
+ return addPureComment(createExprCall(expr, exprAlias), ctx);
26
29
  }
27
30
  /**
28
31
  * 处理子节点数组
@@ -42,59 +45,40 @@ export function processChildren(children, ctx) {
42
45
  }
43
46
  /**
44
47
  * 处理单个子节点
48
+ * JSX 容器节点在此拆包后委托给 processChildExpression 处理
49
+ * 已转换的 API 调用(createView/branch 等 CallExpression)直接透传
45
50
  */
46
51
  function processChildNode(node, ctx) {
47
- // JSX 文本
48
52
  if (isJSXText(node)) {
49
53
  const trimmed = node.value.trim();
50
54
  if (!trimmed)
51
55
  return null;
52
56
  return t.stringLiteral(trimmed);
53
57
  }
54
- // JSX 表达式容器
55
58
  if (isJSXExpressionContainer(node)) {
56
59
  if (node.expression.type === 'JSXEmptyExpression')
57
60
  return null;
58
- return processChildExpression(node.expression, ctx, node.loc);
61
+ return processChildExpression(node.expression, ctx);
59
62
  }
60
- // JSX 展开子元素
61
63
  if (node.type === 'JSXSpreadChild') {
62
- return processChildExpression(node.expression, ctx, node.loc);
64
+ return processChildExpression(node.expression, ctx);
63
65
  }
64
- // JSX 元素或片段
65
66
  if (isJSXElement(node) || isJSXFragment(node)) {
66
67
  return node;
67
68
  }
68
- // 字面量
69
- if (isStringLiteral(node) || isNumericLiteral(node) || isBooleanLiteral(node)) {
70
- return node;
71
- }
72
- // 标识符
73
- if (isIdentifier(node)) {
74
- return node;
75
- }
76
- // 成员表达式
77
- if (isMemberExpression(node)) {
78
- return handleMemberExpression(node, ctx);
79
- }
80
- // 条件表达式
81
- if (isConditionalExpression(node)) {
82
- return processConditionalExpression(node, ctx);
83
- }
84
- // 逻辑表达式
85
- if (isLogicalExpression(node)) {
86
- return handleLogicalExpression(node, ctx);
87
- }
88
- // 调用表达式
69
+ // 已转换的 API 调用(createView/branch 等)直接透传,不包装 expr
89
70
  if (node.type === 'CallExpression') {
90
71
  return node;
91
72
  }
92
- return node;
73
+ // 其他表达式类型委托给 processChildExpression
74
+ return processChildExpression(node, ctx);
93
75
  }
94
76
  /**
95
- * 处理子表达式
77
+ * 处理子表达式(来自 JSX 表达式容器或条件分支)
78
+ * 根据表达式类型生成对应的运行时 API 调用
79
+ * CallExpression 在此用 expr 包装(用户函数调用需要响应式追踪)
96
80
  */
97
- function processChildExpression(expr, ctx, loc) {
81
+ function processChildExpression(expr, ctx) {
98
82
  if (isIdentifier(expr)) {
99
83
  return expr;
100
84
  }
@@ -104,22 +88,17 @@ function processChildExpression(expr, ctx, loc) {
104
88
  if (isConditionalExpression(expr)) {
105
89
  return processConditionalExpression(expr, ctx);
106
90
  }
107
- if (isLogicalExpression(expr)) {
108
- return handleLogicalExpression(expr, ctx, loc);
91
+ if (isLogicalExpression(expr) || isBinaryExpression(expr) || expr.type === 'CallExpression') {
92
+ return wrapWithExpr(expr, ctx);
109
93
  }
110
94
  return expr;
111
95
  }
112
96
  /**
113
- * 处理条件表达式
114
- * 转换为 branch 调用
97
+ * 处理条件表达式,转换为 branch 调用
115
98
  */
116
99
  function processConditionalExpression(node, ctx) {
117
100
  const { test, consequent, alternate } = node;
118
- // 处理分支
119
101
  const processedConsequent = processChildNode(consequent, ctx) || t.nullLiteral();
120
102
  const processedAlternate = processChildNode(alternate, ctx) || t.nullLiteral();
121
- // 获取位置信息(仅开发环境)
122
- const locInfo = getDevLocInfo(ctx, node);
123
- // 使用公共的 createBinaryBranch
124
- return createBinaryBranch(test, processedConsequent, processedAlternate, ctx, locInfo);
103
+ return createBinaryBranch(test, processedConsequent, processedAlternate, ctx);
125
104
  }
@@ -2,8 +2,8 @@ import * as t from '@babel/types';
2
2
  import { isJSXElement } from '@babel/types';
3
3
  import { addWarning, markImport } from '../../context.js';
4
4
  import { createError, createWarning } from '../../error.js';
5
- import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren, filterEffectiveChildren, isNativeElement, isVElse, isVIf, isVIfChain, removeVIfChainDirectives } from '../../utils/index.js';
6
- import { processDirectives } from '../directives/index.js';
5
+ import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, filterEffectiveChildren, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren, isNativeElement, isVElse, isVIf, isVIfChain, removeVIfChainDirectives } from '../../utils/index.js';
6
+ import { processDirectives, transformChildrenVIfChains } from '../directives/index.js';
7
7
  import { processProps } from '../props/index.js';
8
8
  import { processChildren } from './processChildren.js';
9
9
  /**
@@ -123,8 +123,10 @@ function processElementChildren(node, props, ctx) {
123
123
  }
124
124
  }
125
125
  }
126
+ // 处理子元素中的 v-if 链(确保嵌套链在 transformJSXElement 递归调用时被正确处理)
127
+ const chainProcessed = transformChildrenVIfChains(children, ctx, transformJSXElement);
126
128
  // 处理子元素
127
- const processedChildren = processChildren(children, ctx);
129
+ const processedChildren = processChildren(chainProcessed, ctx);
128
130
  const childrenValue = processedChildren.length === 1 ? processedChildren[0] : t.arrayExpression(processedChildren);
129
131
  // 添加 children 属性
130
132
  if (props) {
@@ -146,10 +148,7 @@ function transformSingleVIf(node, ctx) {
146
148
  const transformedNode = transformJSXElement(node, ctx, false);
147
149
  if (!transformedNode)
148
150
  return null;
149
- // 获取位置信息(仅开发环境)
150
- const locInfo = getDevLocInfo(ctx, node);
151
- // 生成 branch 调用
152
- const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)], locInfo }, ctx);
151
+ const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)] }, ctx);
153
152
  if (node.loc) {
154
153
  branchCall.loc = node.loc;
155
154
  }
package/dist/transform.js CHANGED
@@ -40,8 +40,8 @@ function setupAliases(ctx, program) {
40
40
  'createView',
41
41
  'Fragment',
42
42
  'branch',
43
- 'dynamic',
44
- 'access',
43
+ 'expr',
44
+ 'accessor',
45
45
  'withDirectives',
46
46
  'unref',
47
47
  'isRef'
@@ -84,6 +84,9 @@ function transformAST(ast, ctx) {
84
84
  ctx.processedNodes.add(path.node);
85
85
  processPureCompileComponent(path, ctx);
86
86
  }
87
+ else {
88
+ processVIfChain(path, ctx, transformJSXElement);
89
+ }
87
90
  },
88
91
  exit(path) {
89
92
  if (ctx.processedNodes.has(path.node))
@@ -3,7 +3,8 @@
3
3
  * @module utils/ast-builders
4
4
  */
5
5
  import * as t from '@babel/types';
6
- import { type ArrowFunctionExpression, type CallExpression, type Expression, type ObjectExpression, type SourceLocation } from '@babel/types';
6
+ import { type ArrowFunctionExpression, type CallExpression, type Expression, type ObjectExpression } from '@babel/types';
7
+ import type { SourceLocation } from 'acorn';
7
8
  import type { TransformContext, VitarxImportAliases } from '../context.js';
8
9
  /**
9
10
  * 创建 unref 调用
@@ -13,30 +14,28 @@ import type { TransformContext, VitarxImportAliases } from '../context.js';
13
14
  */
14
15
  export declare function createUnrefCall(argument: Expression, alias?: string): CallExpression;
15
16
  /**
16
- * 创建 access 调用
17
+ * 创建 accessor 调用
17
18
  * @param object - 对象表达式
18
19
  * @param key - 键表达式
19
- * @param alias - access 别名
20
+ * @param alias - accessor 别名
20
21
  * @returns CallExpression
21
22
  */
22
- export declare function createAccessCall(object: Expression, key: Expression, alias?: string): CallExpression;
23
+ export declare function createAccessorCall(object: Expression, key: Expression, alias?: string): CallExpression;
23
24
  /**
24
- * 创建 dynamic 调用
25
+ * 创建 expr 调用
25
26
  * @param argument - 参数表达式
26
- * @param alias - dynamic 别名
27
- * @param locInfo - 位置信息对象
27
+ * @param alias - expr 别名
28
28
  * @returns CallExpression
29
29
  */
30
- export declare function createDynamicCall(argument: Expression, alias?: string, locInfo?: ObjectExpression | null): CallExpression;
30
+ export declare function createExprCall(argument: Expression, alias?: string): CallExpression;
31
31
  /**
32
32
  * 创建 branch 调用
33
33
  * @param condition - 条件函数
34
34
  * @param branches - 分支函数数组
35
35
  * @param alias - branch 别名
36
- * @param locInfo - 位置信息对象
37
36
  * @returns CallExpression
38
37
  */
39
- export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string, locInfo?: ObjectExpression | null): CallExpression;
38
+ export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string): CallExpression;
40
39
  /**
41
40
  * 创建 createView 调用
42
41
  * @param type - 元素类型
@@ -60,13 +59,6 @@ export declare function createWithDirectivesCall(view: Expression, directives: A
60
59
  * @returns ArrowFunctionExpression
61
60
  */
62
61
  export declare function createArrowFunction(body: Expression): ArrowFunctionExpression;
63
- /**
64
- * 创建位置信息对象
65
- * @param filename - 文件名
66
- * @param loc - 源码位置
67
- * @returns ObjectExpression
68
- */
69
- export declare function createLocationObject(filename: string, loc: SourceLocation): ObjectExpression;
70
62
  /**
71
63
  * 为调用表达式添加 @__PURE__ 注释
72
64
  * @param node - 调用表达式节点
@@ -81,6 +73,13 @@ export declare function addPureComment<T extends CallExpression>(node: T, ctx: T
81
73
  * @returns 别名或原名
82
74
  */
83
75
  export declare function getAlias(aliases: VitarxImportAliases, name: keyof VitarxImportAliases): string;
76
+ /**
77
+ * 创建位置信息对象
78
+ * @param filename - 文件名
79
+ * @param loc - 源码位置
80
+ * @returns ObjectExpression
81
+ */
82
+ export declare function createLocationObject(filename: string, loc: SourceLocation): ObjectExpression;
84
83
  /**
85
84
  * 获取开发环境下的位置信息对象
86
85
  * 仅在开发环境下返回位置信息,生产环境返回 null
@@ -15,13 +15,13 @@ export function createUnrefCall(argument, alias) {
15
15
  return t.callExpression(t.identifier(alias || 'unref'), [argument]);
16
16
  }
17
17
  /**
18
- * 创建 access 调用
18
+ * 创建 accessor 调用
19
19
  * @param object - 对象表达式
20
20
  * @param key - 键表达式
21
- * @param alias - access 别名
21
+ * @param alias - accessor 别名
22
22
  * @returns CallExpression
23
23
  */
24
- export function createAccessCall(object, key, alias) {
24
+ export function createAccessorCall(object, key, alias) {
25
25
  let keyArg;
26
26
  if (isIdentifier(key)) {
27
27
  keyArg = t.stringLiteral(key.name);
@@ -32,36 +32,26 @@ export function createAccessCall(object, key, alias) {
32
32
  else {
33
33
  keyArg = key;
34
34
  }
35
- return t.callExpression(t.identifier(alias || 'access'), [object, keyArg]);
35
+ return t.callExpression(t.identifier(alias || 'accessor'), [object, keyArg]);
36
36
  }
37
37
  /**
38
- * 创建 dynamic 调用
38
+ * 创建 expr 调用
39
39
  * @param argument - 参数表达式
40
- * @param alias - dynamic 别名
41
- * @param locInfo - 位置信息对象
40
+ * @param alias - expr 别名
42
41
  * @returns CallExpression
43
42
  */
44
- export function createDynamicCall(argument, alias, locInfo) {
45
- const args = [t.arrowFunctionExpression([], argument)];
46
- if (locInfo) {
47
- args.push(locInfo);
48
- }
49
- return t.callExpression(t.identifier(alias || 'dynamic'), args);
43
+ export function createExprCall(argument, alias) {
44
+ return t.callExpression(t.identifier(alias || 'expr'), [t.arrowFunctionExpression([], argument)]);
50
45
  }
51
46
  /**
52
47
  * 创建 branch 调用
53
48
  * @param condition - 条件函数
54
49
  * @param branches - 分支函数数组
55
50
  * @param alias - branch 别名
56
- * @param locInfo - 位置信息对象
57
51
  * @returns CallExpression
58
52
  */
59
- export function createBranchCall(condition, branches, alias, locInfo) {
60
- const args = [condition, t.arrayExpression(branches)];
61
- if (locInfo) {
62
- args.push(locInfo);
63
- }
64
- return t.callExpression(t.identifier(alias || 'branch'), args);
53
+ export function createBranchCall(condition, branches, alias) {
54
+ return t.callExpression(t.identifier(alias || 'branch'), [condition, t.arrayExpression(branches)]);
65
55
  }
66
56
  /**
67
57
  * 创建 createView 调用
@@ -73,15 +63,12 @@ export function createBranchCall(condition, branches, alias, locInfo) {
73
63
  */
74
64
  export function createCreateViewCall(type, props, locInfo, alias) {
75
65
  const args = [type];
76
- // props 参数
77
66
  if (props) {
78
67
  args.push(props);
79
68
  }
80
69
  else if (locInfo) {
81
- // 如果有 locInfo 但没有 props,需要传 null
82
70
  args.push(t.nullLiteral());
83
71
  }
84
- // locInfo 参数
85
72
  if (locInfo) {
86
73
  args.push(locInfo);
87
74
  }
@@ -111,19 +98,6 @@ export function createWithDirectivesCall(view, directives, alias) {
111
98
  export function createArrowFunction(body) {
112
99
  return t.arrowFunctionExpression([], body);
113
100
  }
114
- /**
115
- * 创建位置信息对象
116
- * @param filename - 文件名
117
- * @param loc - 源码位置
118
- * @returns ObjectExpression
119
- */
120
- export function createLocationObject(filename, loc) {
121
- return t.objectExpression([
122
- t.objectProperty(t.identifier('fileName'), t.stringLiteral(filename)),
123
- t.objectProperty(t.identifier('lineNumber'), t.numericLiteral(loc.start.line)),
124
- t.objectProperty(t.identifier('columnNumber'), t.numericLiteral(loc.start.column + 1))
125
- ]);
126
- }
127
101
  /**
128
102
  * 为调用表达式添加 @__PURE__ 注释
129
103
  * @param node - 调用表达式节点
@@ -147,6 +121,19 @@ export function addPureComment(node, ctx) {
147
121
  export function getAlias(aliases, name) {
148
122
  return aliases[name] || name;
149
123
  }
124
+ /**
125
+ * 创建位置信息对象
126
+ * @param filename - 文件名
127
+ * @param loc - 源码位置
128
+ * @returns ObjectExpression
129
+ */
130
+ export function createLocationObject(filename, loc) {
131
+ return t.objectExpression([
132
+ t.objectProperty(t.identifier('fileName'), t.stringLiteral(filename)),
133
+ t.objectProperty(t.identifier('lineNumber'), t.numericLiteral(loc.start.line)),
134
+ t.objectProperty(t.identifier('columnNumber'), t.numericLiteral(loc.start.column + 1))
135
+ ]);
136
+ }
150
137
  /**
151
138
  * 获取开发环境下的位置信息对象
152
139
  * 仅在开发环境下返回位置信息,生产环境返回 null
@@ -15,8 +15,6 @@ export interface BranchConfig {
15
15
  branches: t.ArrowFunctionExpression[];
16
16
  /** 是否使用 unref 包装标识符条件 */
17
17
  useRef?: boolean;
18
- /** 位置信息对象 */
19
- locInfo?: t.ObjectExpression | null;
20
18
  }
21
19
  /**
22
20
  * 创建 branch 调用
@@ -36,7 +34,6 @@ export declare function buildNestedCondition(conditions: t.Expression[], ctx: Tr
36
34
  * @param consequent - 真值分支
37
35
  * @param alternate - 假值分支
38
36
  * @param ctx - 转换上下文
39
- * @param locInfo - 位置信息对象
40
37
  * @returns branch 调用表达式
41
38
  */
42
- export declare function createBinaryBranch(condition: t.Expression, consequent: t.Expression, alternate: t.Expression, ctx: TransformContext, locInfo?: t.ObjectExpression | null): t.CallExpression;
39
+ export declare function createBinaryBranch(condition: t.Expression, consequent: t.Expression, alternate: t.Expression, ctx: TransformContext): t.CallExpression;
@@ -14,15 +14,14 @@ import { addPureComment, createArrowFunction, createBranchCall, createUnrefCall,
14
14
  * @returns branch 调用表达式
15
15
  */
16
16
  export function createBranch(config, ctx) {
17
- const { conditions, branches, useRef = true, locInfo } = config;
17
+ const { conditions, branches, useRef = true } = config;
18
18
  markImport(ctx, 'branch');
19
- // 只有在需要 unref 且条件中有标识符时才标记导入
20
19
  if (useRef && conditions.some(c => isIdentifier(c))) {
21
20
  markImport(ctx, 'unref');
22
21
  }
23
22
  const conditionExpr = buildNestedCondition(conditions, ctx, useRef);
24
23
  const branchAlias = getAlias(ctx.vitarxAliases, 'branch');
25
- return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias, locInfo), ctx);
24
+ return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias), ctx);
26
25
  }
27
26
  /**
28
27
  * 判断表达式是否需要括号包裹
@@ -85,13 +84,11 @@ export function buildNestedCondition(conditions, ctx, useRef = true) {
85
84
  * @param consequent - 真值分支
86
85
  * @param alternate - 假值分支
87
86
  * @param ctx - 转换上下文
88
- * @param locInfo - 位置信息对象
89
87
  * @returns branch 调用表达式
90
88
  */
91
- export function createBinaryBranch(condition, consequent, alternate, ctx, locInfo) {
89
+ export function createBinaryBranch(condition, consequent, alternate, ctx) {
92
90
  const conditions = [condition, t.booleanLiteral(true)];
93
91
  const branches = [createArrowFunction(consequent), createArrowFunction(alternate)];
94
- // 只有条件是 Identifier 时才需要 unref
95
92
  const useRef = isIdentifier(condition);
96
- return createBranch({ conditions, branches, useRef, locInfo }, ctx);
93
+ return createBranch({ conditions, branches, useRef }, ctx);
97
94
  }
@@ -1,6 +1,6 @@
1
1
  export { isWhitespaceJSXText } from './ast-guards.js';
2
2
  export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
3
- export { createUnrefCall, createAccessCall, createDynamicCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
3
+ export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
4
4
  export { collectPatternBindings, collectObjectPatternBindings } from './pattern-helpers.js';
5
5
  export { createBranch, createBinaryBranch, buildNestedCondition, type BranchConfig } from './branch-factory.js';
6
6
  export { validateVIfChain, collectVIfChainInfo, collectFragmentVIfChains, type VIfChainInfo } from './vif-helpers.js';
@@ -3,7 +3,7 @@ export { isWhitespaceJSXText } from './ast-guards.js';
3
3
  // JSX 辅助函数
4
4
  export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
5
5
  // AST 构建函数
6
- export { createUnrefCall, createAccessCall, createDynamicCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
6
+ export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
7
7
  // 模式处理辅助函数
8
8
  export { collectPatternBindings, collectObjectPatternBindings } from './pattern-helpers.js';
9
9
  // Branch 工厂
@@ -26,10 +26,13 @@ export declare function isNativeElement(name: string): boolean;
26
26
  export declare function getJSXAttributeByName(node: JSXElement, name: string): JSXAttribute | undefined;
27
27
  /**
28
28
  * 检查元素是否具有指定指令
29
+ * v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
30
+ * 其他指令(如 v-show)支持 v-xxx:arg 命名空间格式
29
31
  */
30
32
  export declare function hasDirective(node: JSXElement, directiveName: string): boolean;
31
33
  /**
32
34
  * 获取指令的值
35
+ * v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
33
36
  */
34
37
  export declare function getDirectiveValue(node: JSXElement, directiveName: string): Expression | null;
35
38
  /**
@@ -54,6 +57,7 @@ export declare function isVElse(node: JSXElement): boolean;
54
57
  export declare function removeVDirectives(node: JSXElement): void;
55
58
  /**
56
59
  * 移除元素上的 v-if 链指令(v-if、v-else-if、v-else)
60
+ * 仅处理 JSXIdentifier 格式,不支持命名空间格式(v:if)
57
61
  */
58
62
  export declare function removeVIfChainDirectives(node: JSXElement): void;
59
63
  /**
@@ -5,8 +5,8 @@
5
5
  import * as t from '@babel/types';
6
6
  import { isJSXElement, isJSXText } from '@babel/types';
7
7
  import { DIRECTIVE_PREFIX, PURE_COMPILE_COMPONENTS } from '../constants/index.js';
8
- import { isWhitespaceJSXText } from './ast-guards.js';
9
8
  import { createError } from '../error.js';
9
+ import { isWhitespaceJSXText } from './ast-guards.js';
10
10
  /**
11
11
  * 获取 JSX 元素的名称
12
12
  */
@@ -45,14 +45,19 @@ export function getJSXAttributeByName(node, name) {
45
45
  }
46
46
  return undefined;
47
47
  }
48
+ const V_IF_CHAIN_DIRECTIVES = new Set(['v-if', 'v-else-if', 'v-else']);
48
49
  /**
49
50
  * 检查元素是否具有指定指令
51
+ * v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
52
+ * 其他指令(如 v-show)支持 v-xxx:arg 命名空间格式
50
53
  */
51
54
  export function hasDirective(node, directiveName) {
52
55
  for (const attr of node.openingElement.attributes) {
53
56
  if (attr.type === 'JSXAttribute') {
54
57
  const attrName = attr.name;
55
58
  if (attrName.type === 'JSXNamespacedName') {
59
+ if (V_IF_CHAIN_DIRECTIVES.has(directiveName))
60
+ continue;
56
61
  if (attrName.namespace.name === 'v' && attrName.name.name === directiveName.slice(2)) {
57
62
  return true;
58
63
  }
@@ -66,6 +71,7 @@ export function hasDirective(node, directiveName) {
66
71
  }
67
72
  /**
68
73
  * 获取指令的值
74
+ * v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
69
75
  */
70
76
  export function getDirectiveValue(node, directiveName) {
71
77
  for (const attr of node.openingElement.attributes) {
@@ -73,6 +79,8 @@ export function getDirectiveValue(node, directiveName) {
73
79
  const attrName = attr.name;
74
80
  let matches = false;
75
81
  if (attrName.type === 'JSXNamespacedName') {
82
+ if (V_IF_CHAIN_DIRECTIVES.has(directiveName))
83
+ continue;
76
84
  matches = attrName.namespace.name === 'v' && attrName.name.name === directiveName.slice(2);
77
85
  }
78
86
  else if (attrName.type === 'JSXIdentifier') {
@@ -129,19 +137,14 @@ export function removeVDirectives(node) {
129
137
  }
130
138
  /**
131
139
  * 移除元素上的 v-if 链指令(v-if、v-else-if、v-else)
140
+ * 仅处理 JSXIdentifier 格式,不支持命名空间格式(v:if)
132
141
  */
133
142
  export function removeVIfChainDirectives(node) {
134
- const vIfChainDirectives = ['v-if', 'v-else-if', 'v-else'];
135
143
  node.openingElement.attributes = node.openingElement.attributes.filter(attr => {
136
144
  if (attr.type === 'JSXAttribute') {
137
145
  const attrName = attr.name;
138
- if (attrName.type === 'JSXNamespacedName') {
139
- if (attrName.namespace.name === 'v') {
140
- return !vIfChainDirectives.includes(`v-${attrName.name.name}`);
141
- }
142
- }
143
- else if (attrName.type === 'JSXIdentifier') {
144
- return !vIfChainDirectives.includes(attrName.name);
146
+ if (attrName.type === 'JSXIdentifier') {
147
+ return !V_IF_CHAIN_DIRECTIVES.has(attrName.name);
145
148
  }
146
149
  }
147
150
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitarx/plugin-vite",
3
- "version": "0.0.1-beta.8",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "The official plugin for Vitarx support in Vite.",
5
5
  "author": "ZhuChongLin <8210856@qq.com>",
6
6
  "license": "MIT",
@@ -60,7 +60,7 @@
60
60
  "rimraf": "^6.1.3",
61
61
  "vitest": "^4.0.18",
62
62
  "vite": "^8.0.5",
63
- "vitarx": "^4.0.0-beta.8"
63
+ "vitarx": "^4.0.0-beta.16"
64
64
  },
65
65
  "scripts": {
66
66
  "build": "vitest run && rimraf dist && tsc -p tsconfig.build.json",