@vitarx/plugin-vite 0.0.1-alpha.1 → 0.0.1-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.
@@ -12,6 +12,12 @@ export declare const VITARX_APIS: {
12
12
  readonly UNREF: "unref";
13
13
  readonly IS_REF: "isRef";
14
14
  };
15
+ /**
16
+ * UI 相关的 API 名称列表
17
+ * 这些 API 用于创建视图,在 HMR 时需要识别
18
+ */
19
+ export declare const UI_API_NAMES: readonly ["createView", "branch", "dynamic", "access", "withDirectives"];
20
+ export type UIApiName = (typeof UI_API_NAMES)[number];
15
21
  /**
16
22
  * Ref 相关 API 名称
17
23
  * 用于识别 ref 变量
@@ -12,6 +12,11 @@ export const VITARX_APIS = {
12
12
  UNREF: 'unref',
13
13
  IS_REF: 'isRef'
14
14
  };
15
+ /**
16
+ * UI 相关的 API 名称列表
17
+ * 这些 API 用于创建视图,在 HMR 时需要识别
18
+ */
19
+ export const UI_API_NAMES = ['createView', 'branch', 'dynamic', 'access', 'withDirectives'];
15
20
  /**
16
21
  * Ref 相关 API 名称
17
22
  * 用于识别 ref 变量
package/dist/context.d.ts CHANGED
@@ -6,11 +6,12 @@
6
6
  import type { File } from '@babel/types';
7
7
  import type { CompileOptions } from './transform.js';
8
8
  import type { CompilerWarning } from './error.js';
9
+ import { UI_API_NAMES } from './constants/index.js';
9
10
  /**
10
11
  * UI 相关的 API 名称列表
11
12
  * 这些 API 用于创建视图,在 HMR 时需要识别
12
13
  */
13
- export declare const UI_API_NAMES: readonly ["createView", "branch", "dynamic", "access", "withDirectives"];
14
+ export declare const UI_API_NAMES_CONST: readonly ["createView", "branch", "dynamic", "access", "withDirectives"];
14
15
  export type UIApiName = (typeof UI_API_NAMES)[number];
15
16
  /**
16
17
  * 导入信息
package/dist/context.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { UI_API_NAMES } from './constants/index.js';
1
2
  /**
2
3
  * UI 相关的 API 名称列表
3
4
  * 这些 API 用于创建视图,在 HMR 时需要识别
4
5
  */
5
- export const UI_API_NAMES = ['createView', 'branch', 'dynamic', 'access', 'withDirectives'];
6
+ export const UI_API_NAMES_CONST = UI_API_NAMES;
6
7
  /**
7
8
  * 创建转换上下文
8
9
  * @param code - 源代码
@@ -5,11 +5,12 @@
5
5
  */
6
6
  import { parse } from 'acorn';
7
7
  import { simple as walkSimple } from 'acorn-walk';
8
+ import { UI_API_NAMES } from '../constants/index.js';
8
9
  /**
9
10
  * UI 相关的运行时 API 名称
10
11
  * 这些 API 调用代表 UI 描述代码
11
12
  */
12
- const UI_APIS = new Set(['createView', 'branch', 'dynamic', 'access', 'withDirectives', 'jsxDEV']);
13
+ const UI_APIS = new Set([...UI_API_NAMES, 'jsxDEV']);
13
14
  /**
14
15
  * 判断标识符名称是否为 UI API
15
16
  * 支持原始名称和带后缀的名称(如 jsxDEV$1)
package/dist/index.js CHANGED
@@ -43,13 +43,12 @@ export default function vitarx(_options) {
43
43
  },
44
44
  configResolved(config) {
45
45
  viteConfig = config;
46
- const sourcemap = config.build.sourcemap;
47
46
  compileOptions = {
48
47
  dev: isDEV,
49
48
  ssr: isSSR,
50
49
  hmr: isDEV && !isSSR,
51
50
  runtimeModule: 'vitarx',
52
- sourceMap: sourcemap === 'inline' ? 'inline' : sourcemap === true ? 'both' : false
51
+ sourceMap: false
53
52
  };
54
53
  },
55
54
  async transform(code, id) {
@@ -57,9 +56,9 @@ export default function vitarx(_options) {
57
56
  if (result) {
58
57
  return await transformWithOxc(result.code, id, {
59
58
  jsx: 'preserve'
60
- }, result.map, viteConfig);
59
+ }, this.getCombinedSourcemap(), viteConfig);
61
60
  }
62
- return result;
61
+ return null;
63
62
  }
64
63
  };
65
64
  }
@@ -1,6 +1,6 @@
1
1
  import { isJSXElement } from '@babel/types';
2
2
  import { createError } from '../../error.js';
3
- import { collectVIfChainInfo, createArrowFunction, createBranch, filterWhitespaceChildren, hasDirective, removeVDirectives, validateVIfChain } from '../../utils/index.js';
3
+ import { collectVIfChainInfo, createArrowFunction, createBranch, filterWhitespaceChildren, getDevLocInfo, hasDirective, removeVIfChainDirectives, validateVIfChain } from '../../utils/index.js';
4
4
  /**
5
5
  * 处理 IfBlock 组件
6
6
  */
@@ -13,9 +13,11 @@ export function processIfBlock(path, ctx) {
13
13
  const jsxChildren = children;
14
14
  validateVIfChain(jsxChildren);
15
15
  const { nodes, conditions } = collectVIfChainInfo(jsxChildren);
16
- nodes.forEach(node => removeVDirectives(node));
16
+ nodes.forEach(node => removeVIfChainDirectives(node));
17
17
  const branches = nodes.map(node => createArrowFunction(node));
18
- const branchCall = createBranch({ conditions, branches }, ctx);
18
+ // 获取位置信息(仅开发环境)
19
+ const locInfo = getDevLocInfo(ctx, path.node);
20
+ const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
19
21
  if (path.node.loc) {
20
22
  branchCall.loc = path.node.loc;
21
23
  }
@@ -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, getJSXAttributeByName, getJSXElementName } from '../../utils/index.js';
4
+ import { createArrowFunction, createBranch, getDevLocInfo, getJSXAttributeByName, getJSXElementName } from '../../utils/index.js';
5
5
  /**
6
6
  * 处理 Switch 组件
7
7
  * @param path - JSX 元素路径
@@ -20,9 +20,11 @@ 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);
23
25
  // 生成 branch 调用
24
26
  // Switch 的 when 条件如果是标识符,需要 unref
25
- const branchCall = createBranch({ conditions, branches, useRef: true }, ctx);
27
+ const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
26
28
  if (path.node.loc) {
27
29
  branchCall.loc = path.node.loc;
28
30
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import * as t from '@babel/types';
7
7
  import { TransformContext } from '../../context.js';
8
+ import type { DirectiveInfo } from '../../passes/props/types.js';
8
9
  /**
9
10
  * 处理指令
10
11
  * 将指令转换为 withDirectives 调用
@@ -13,4 +14,4 @@ import { TransformContext } from '../../context.js';
13
14
  * @param ctx - 转换上下文
14
15
  * @returns 处理后的调用表达式
15
16
  */
16
- export declare function processDirectives(viewCall: t.CallExpression, directives: Map<string, t.Expression>, ctx: TransformContext): t.CallExpression;
17
+ export declare function processDirectives(viewCall: t.CallExpression, directives: Map<string, DirectiveInfo>, ctx: TransformContext): t.CallExpression;
@@ -20,9 +20,9 @@ export function processDirectives(viewCall, directives, ctx) {
20
20
  return viewCall;
21
21
  }
22
22
  const directiveArray = [];
23
- for (const [name, value] of directives) {
23
+ for (const [name, info] of directives) {
24
24
  const directiveName = name.slice(2); // 移除 'v-' 前缀
25
- const directiveValue = buildDirectiveValue(value, ctx);
25
+ const directiveValue = buildDirectiveValue(info.value, info.arg, ctx);
26
26
  directiveArray.push([directiveName, directiveValue]);
27
27
  }
28
28
  markImport(ctx, 'withDirectives');
@@ -32,15 +32,20 @@ export function processDirectives(viewCall, directives, ctx) {
32
32
  /**
33
33
  * 构建指令值对象
34
34
  */
35
- function buildDirectiveValue(value, ctx) {
35
+ function buildDirectiveValue(value, arg, ctx) {
36
+ const properties = [];
37
+ // 添加 value 属性
36
38
  if (isIdentifier(value)) {
37
39
  markImport(ctx, 'unref');
38
40
  const unrefAlias = getAlias(ctx.vitarxAliases, 'unref');
39
- return t.objectExpression([
40
- t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))]))
41
- ]);
41
+ properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))])));
42
42
  }
43
- return t.objectExpression([
44
- t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(value)]))
45
- ]);
43
+ else {
44
+ properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(value)])));
45
+ }
46
+ // 添加 arg 属性
47
+ if (arg) {
48
+ properties.push(t.objectProperty(t.identifier('arg'), t.stringLiteral(arg)));
49
+ }
50
+ return t.objectExpression(properties);
46
51
  }
@@ -1,6 +1,6 @@
1
1
  import * as t from '@babel/types';
2
2
  import { isJSXElement, isJSXText } from '@babel/types';
3
- import { collectFragmentVIfChains, createArrowFunction, createBranch, isWhitespaceJSXText, removeVDirectives } from '../../utils/index.js';
3
+ import { collectFragmentVIfChains, createArrowFunction, createBranch, getDevLocInfo, isWhitespaceJSXText, removeVIfChainDirectives } from '../../utils/index.js';
4
4
  /**
5
5
  * 处理 Fragment 中的 v-if 链
6
6
  * @param path - JSX Fragment 路径
@@ -36,14 +36,17 @@ function processVIfChainItem(chain, children, ctx, transformJSXElement) {
36
36
  // 移除指令并转换节点
37
37
  const branches = [];
38
38
  for (const node of nodes) {
39
- removeVDirectives(node);
39
+ removeVIfChainDirectives(node);
40
40
  const transformedNode = transformJSXElement(node, ctx, false);
41
41
  if (transformedNode) {
42
42
  branches.push(createArrowFunction(transformedNode));
43
43
  }
44
44
  }
45
+ // 获取第一个节点的位置信息(仅开发环境)
46
+ const firstNode = nodes[0];
47
+ const locInfo = getDevLocInfo(ctx, firstNode);
45
48
  // 生成 branch 调用
46
- const branchCall = createBranch({ conditions, branches }, ctx);
49
+ const branchCall = createBranch({ conditions, branches, locInfo }, ctx);
47
50
  // 替换链的第一个节点,其他节点标记为 null
48
51
  if (start >= 0) {
49
52
  children[start] = branchCall;
@@ -6,7 +6,24 @@
6
6
  import * as t from '@babel/types';
7
7
  import { isBooleanLiteral, isConditionalExpression, isIdentifier, isJSXElement, isJSXExpressionContainer, isJSXFragment, isJSXText, isLogicalExpression, isMemberExpression, isNumericLiteral, isStringLiteral } from '@babel/types';
8
8
  import { markImport } from '../../context.js';
9
- import { addPureComment, createAccessCall, createBinaryBranch, createDynamicCall, getAlias } from '../../utils/index.js';
9
+ import { addPureComment, createAccessCall, createBinaryBranch, createDynamicCall, getAlias, getDevLocInfo } from '../../utils/index.js';
10
+ /**
11
+ * 处理成员表达式
12
+ */
13
+ function handleMemberExpression(expr, ctx) {
14
+ markImport(ctx, 'access');
15
+ const accessAlias = getAlias(ctx.vitarxAliases, 'access');
16
+ return createAccessCall(expr.object, expr.property, accessAlias);
17
+ }
18
+ /**
19
+ * 处理逻辑表达式
20
+ */
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));
26
+ }
10
27
  /**
11
28
  * 处理子节点数组
12
29
  * @param children - 子节点数组
@@ -38,11 +55,11 @@ function processChildNode(node, ctx) {
38
55
  if (isJSXExpressionContainer(node)) {
39
56
  if (node.expression.type === 'JSXEmptyExpression')
40
57
  return null;
41
- return processChildExpression(node.expression, ctx);
58
+ return processChildExpression(node.expression, ctx, node.loc);
42
59
  }
43
60
  // JSX 展开子元素
44
61
  if (node.type === 'JSXSpreadChild') {
45
- return processChildExpression(node.expression, ctx);
62
+ return processChildExpression(node.expression, ctx, node.loc);
46
63
  }
47
64
  // JSX 元素或片段
48
65
  if (isJSXElement(node) || isJSXFragment(node)) {
@@ -58,9 +75,7 @@ function processChildNode(node, ctx) {
58
75
  }
59
76
  // 成员表达式
60
77
  if (isMemberExpression(node)) {
61
- markImport(ctx, 'access');
62
- const accessAlias = getAlias(ctx.vitarxAliases, 'access');
63
- return createAccessCall(node.object, node.property, accessAlias);
78
+ return handleMemberExpression(node, ctx);
64
79
  }
65
80
  // 条件表达式
66
81
  if (isConditionalExpression(node)) {
@@ -68,9 +83,7 @@ function processChildNode(node, ctx) {
68
83
  }
69
84
  // 逻辑表达式
70
85
  if (isLogicalExpression(node)) {
71
- markImport(ctx, 'dynamic');
72
- const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
73
- return addPureComment(createDynamicCall(node, dynamicAlias));
86
+ return handleLogicalExpression(node, ctx);
74
87
  }
75
88
  // 调用表达式
76
89
  if (node.type === 'CallExpression') {
@@ -81,22 +94,18 @@ function processChildNode(node, ctx) {
81
94
  /**
82
95
  * 处理子表达式
83
96
  */
84
- function processChildExpression(expr, ctx) {
97
+ function processChildExpression(expr, ctx, loc) {
85
98
  if (isIdentifier(expr)) {
86
99
  return expr;
87
100
  }
88
101
  if (isMemberExpression(expr)) {
89
- markImport(ctx, 'access');
90
- const accessAlias = getAlias(ctx.vitarxAliases, 'access');
91
- return createAccessCall(expr.object, expr.property, accessAlias);
102
+ return handleMemberExpression(expr, ctx);
92
103
  }
93
104
  if (isConditionalExpression(expr)) {
94
105
  return processConditionalExpression(expr, ctx);
95
106
  }
96
107
  if (isLogicalExpression(expr)) {
97
- markImport(ctx, 'dynamic');
98
- const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
99
- return addPureComment(createDynamicCall(expr, dynamicAlias));
108
+ return handleLogicalExpression(expr, ctx, loc);
100
109
  }
101
110
  return expr;
102
111
  }
@@ -109,6 +118,8 @@ function processConditionalExpression(node, ctx) {
109
118
  // 处理分支
110
119
  const processedConsequent = processChildNode(consequent, ctx) || t.nullLiteral();
111
120
  const processedAlternate = processChildNode(alternate, ctx) || t.nullLiteral();
121
+ // 获取位置信息(仅开发环境)
122
+ const locInfo = getDevLocInfo(ctx, node);
112
123
  // 使用公共的 createBinaryBranch
113
- return createBinaryBranch(test, processedConsequent, processedAlternate, ctx);
124
+ return createBinaryBranch(test, processedConsequent, processedAlternate, ctx, locInfo);
114
125
  }
@@ -1,8 +1,8 @@
1
1
  import * as t from '@babel/types';
2
- import { isJSXElement, isJSXExpressionContainer, isJSXText } from '@babel/types';
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, createLocationObject, getAlias, getDirectiveValue, getJSXAttributeByName, getJSXElementName, isNativeElement, isVElse, isVIf, isVIfChain, removeVDirectives } from '../../utils/index.js';
5
+ import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren, filterEffectiveChildren, isNativeElement, isVElse, isVIf, isVIfChain, removeVIfChainDirectives } from '../../utils/index.js';
6
6
  import { processDirectives } from '../directives/index.js';
7
7
  import { processProps } from '../props/index.js';
8
8
  import { processChildren } from './processChildren.js';
@@ -72,15 +72,7 @@ export function transformJSXElement(node, ctx, handleVIf = false) {
72
72
  // 确定元素类型
73
73
  const type = isNativeElement(name) ? t.stringLiteral(name) : t.identifier(name);
74
74
  // 检测是否有有效子元素
75
- const hasChildren = node.children.some(child => {
76
- if (isJSXText(child)) {
77
- return child.value.trim().length > 0;
78
- }
79
- if (isJSXExpressionContainer(child)) {
80
- return child.expression.type !== 'JSXEmptyExpression';
81
- }
82
- return true;
83
- });
75
+ const hasChildren = hasEffectiveChildren(node);
84
76
  // 处理属性(有子元素时跳过 children 属性)
85
77
  const { props, directives } = processProps(node, ctx, hasChildren);
86
78
  // 处理子元素
@@ -88,7 +80,7 @@ export function transformJSXElement(node, ctx, handleVIf = false) {
88
80
  // 生成 createView 调用
89
81
  markImport(ctx, 'createView');
90
82
  const createViewAlias = getAlias(ctx.vitarxAliases, 'createView');
91
- const locInfo = ctx.options.dev && node.loc ? createLocationObject(ctx.filename, node.loc) : null;
83
+ const locInfo = getDevLocInfo(ctx, node);
92
84
  let viewCall = createCreateViewCall(type, finalProps, locInfo, createViewAlias);
93
85
  // 处理指令
94
86
  if (directives.size > 0) {
@@ -108,15 +100,7 @@ export function transformJSXElement(node, ctx, handleVIf = false) {
108
100
  */
109
101
  function processElementChildren(node, props, ctx) {
110
102
  // 过滤有效子元素
111
- const children = node.children.filter(child => {
112
- if (isJSXText(child)) {
113
- return child.value.trim().length > 0;
114
- }
115
- if (isJSXExpressionContainer(child)) {
116
- return child.expression.type !== 'JSXEmptyExpression';
117
- }
118
- return true;
119
- });
103
+ const children = filterEffectiveChildren(node);
120
104
  if (children.length === 0) {
121
105
  return props;
122
106
  }
@@ -158,14 +142,14 @@ function transformSingleVIf(node, ctx) {
158
142
  const condition = getDirectiveValue(node, 'v-if');
159
143
  if (!condition)
160
144
  return null;
161
- // 移除 v- 指令
162
- removeVDirectives(node);
163
- // 转换元素
145
+ removeVIfChainDirectives(node);
164
146
  const transformedNode = transformJSXElement(node, ctx, false);
165
147
  if (!transformedNode)
166
148
  return null;
149
+ // 获取位置信息(仅开发环境)
150
+ const locInfo = getDevLocInfo(ctx, node);
167
151
  // 生成 branch 调用
168
- const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)] }, ctx);
152
+ const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)], locInfo }, ctx);
169
153
  if (node.loc) {
170
154
  branchCall.loc = node.loc;
171
155
  }
@@ -1,5 +1,5 @@
1
1
  import * as t from '@babel/types';
2
- import { addPureComment, createCreateViewCall, createLocationObject, filterWhitespaceChildren, getAlias, validateMatchInSwitch } from '../../utils/index.js';
2
+ import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo, validateMatchInSwitch } from '../../utils/index.js';
3
3
  import { processChildren } from './processChildren.js';
4
4
  /**
5
5
  * 处理 JSX Fragment
@@ -17,7 +17,7 @@ export function processJSXFragment(path, ctx) {
17
17
  ctx.imports.createView = true;
18
18
  const fragmentAlias = getAlias(ctx.vitarxAliases, 'Fragment');
19
19
  const createViewAlias = getAlias(ctx.vitarxAliases, 'createView');
20
- const locInfo = ctx.options.dev && node.loc ? createLocationObject(ctx.filename, node.loc) : null;
20
+ const locInfo = getDevLocInfo(ctx, node);
21
21
  // 无子元素
22
22
  if (children.length === 0) {
23
23
  const viewCall = addPureComment(createCreateViewCall(t.identifier(fragmentAlias), null, locInfo, createViewAlias));
@@ -71,7 +71,7 @@ export function processAttribute(attr, existingPropNames, ctx) {
71
71
  }
72
72
  /**
73
73
  * 处理命名空间属性
74
- * 支持 v:xxx 格式的指令语法
74
+ *
75
75
  * @param attr - JSX 属性节点
76
76
  * @param attrName - 命名空间名称
77
77
  * @param existingPropNames - 已存在的属性名集合
@@ -82,22 +82,24 @@ function processNamespacedAttribute(attr, attrName, existingPropNames, ctx) {
82
82
  const namespace = attrName.namespace.name;
83
83
  const name = attrName.name.name;
84
84
  const fullName = `${namespace}:${name}`;
85
- // v:xxx 指令
86
- if (namespace === 'v') {
87
- const directiveName = `v-${name}`;
88
- const value = getAttributeValue(attr.value);
89
- // v:bind 等同于 v-bind
90
- if (directiveName === 'v-bind') {
91
- return { type: 'directive', name: 'v-bind', value, isVBind: true, isVModel: false };
92
- }
93
- // 其他指令(排除 v-if 系列和 v-model)
94
- if (!['v-if', 'v-else-if', 'v-else', 'v-model'].includes(directiveName)) {
95
- return { type: 'directive', name: directiveName, value, isVBind: false, isVModel: false };
85
+ const value = getAttributeValue(attr.value);
86
+ // v-xxx:arg 格式的带参数指令 (如 v-test:t)
87
+ if (namespace.startsWith('v-')) {
88
+ const directiveName = namespace;
89
+ // 排除 v-if 系列和 v-model
90
+ if (!['v-if', 'v-else-if', 'v-else', 'v-model', 'v-bind'].includes(directiveName)) {
91
+ return {
92
+ type: 'directive',
93
+ name: directiveName,
94
+ value,
95
+ isVBind: false,
96
+ isVModel: false,
97
+ arg: name
98
+ };
96
99
  }
97
100
  }
98
101
  // 其他命名空间属性作为普通属性处理
99
102
  existingPropNames.add(fullName);
100
- const value = getAttributeValue(attr.value);
101
103
  const property = createProperty(fullName, value, ctx);
102
104
  return { type: 'property', property };
103
105
  }
@@ -140,26 +142,20 @@ function getAttributeValue(value) {
140
142
  * @returns 对象属性或方法节点
141
143
  */
142
144
  function createProperty(key, value, ctx) {
143
- // 静态值:直接赋值
145
+ const keyNode = t.stringLiteral(key);
144
146
  if (isStringLiteral(value) || isNumericLiteral(value) || isBooleanLiteral(value)) {
145
- return t.objectProperty(t.identifier(key), value);
147
+ return t.objectProperty(keyNode, value);
146
148
  }
147
- // Identifier: 检查是否为已知的 ref 变量
148
149
  if (isIdentifier(value)) {
149
- // 已知 ref 变量:直接访问 .value
150
150
  if (ctx.refVariables.has(value.name)) {
151
- return t.objectMethod('get', t.identifier(key), [], t.blockStatement([t.returnStatement(t.memberExpression(value, t.identifier('value')))]));
151
+ return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(t.memberExpression(value, t.identifier('value')))]));
152
152
  }
153
- // children 属性:直接引用,不使用 unref
154
- // 原因:children 中的 ref 是合法的可变渲染源
155
153
  if (key === 'children') {
156
- return t.objectProperty(t.identifier(key), value);
154
+ return t.objectProperty(keyNode, value);
157
155
  }
158
- // 其他未知变量:使用 unref 进行解包
159
156
  markImport(ctx, 'unref');
160
157
  const unrefAlias = getAlias(ctx.vitarxAliases, 'unref');
161
- return t.objectMethod('get', t.identifier(key), [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))]));
158
+ return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))]));
162
159
  }
163
- // 其他表达式:直接返回(保持响应性)
164
- return t.objectMethod('get', t.identifier(key), [], t.blockStatement([t.returnStatement(value)]));
160
+ return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(value)]));
165
161
  }
@@ -75,7 +75,7 @@ export function processProps(node, ctx, hasChildren = false) {
75
75
  }
76
76
  // 其他指令:添加到指令映射
77
77
  else {
78
- directives.set(result.name, result.value);
78
+ directives.set(result.name, { value: result.value, arg: result.arg });
79
79
  }
80
80
  }
81
81
  else if (result.type === 'property') {
@@ -3,14 +3,23 @@
3
3
  * @module passes/props/types
4
4
  */
5
5
  import * as t from '@babel/types';
6
+ /**
7
+ * 指令信息
8
+ */
9
+ export interface DirectiveInfo {
10
+ /** 指令值表达式 */
11
+ value: t.Expression;
12
+ /** 指令参数 (如 v-test:t 中的 t) */
13
+ arg?: string;
14
+ }
6
15
  /**
7
16
  * Props 处理结果
8
17
  */
9
18
  export interface PropsResult {
10
19
  /** 处理后的 props 对象表达式,无属性时为 null */
11
20
  props: t.ObjectExpression | null;
12
- /** 指令映射表,key 为指令名,value 为指令值表达式 */
13
- directives: Map<string, t.Expression>;
21
+ /** 指令映射表,key 为指令名,value 为指令信息 */
22
+ directives: Map<string, DirectiveInfo>;
14
23
  /** 是否包含 v-bind 展开属性 */
15
24
  hasVBind: boolean;
16
25
  }
@@ -36,6 +45,7 @@ export type AttributeResult = {
36
45
  value: t.Expression;
37
46
  isVBind: boolean;
38
47
  isVModel: boolean;
48
+ arg?: string;
39
49
  } | {
40
50
  type: 'property';
41
51
  property: t.ObjectProperty | t.ObjectMethod;
@@ -1,5 +1,10 @@
1
+ /**
2
+ * AST 节点构建函数
3
+ * @module utils/ast-builders
4
+ */
5
+ import * as t from '@babel/types';
1
6
  import { type ArrowFunctionExpression, type CallExpression, type Expression, type ObjectExpression, type SourceLocation } from '@babel/types';
2
- import type { VitarxImportAliases } from '../context.js';
7
+ import type { TransformContext, VitarxImportAliases } from '../context.js';
3
8
  /**
4
9
  * 创建 unref 调用
5
10
  * @param argument - 参数表达式
@@ -19,17 +24,19 @@ export declare function createAccessCall(object: Expression, key: Expression, al
19
24
  * 创建 dynamic 调用
20
25
  * @param argument - 参数表达式
21
26
  * @param alias - dynamic 别名
27
+ * @param locInfo - 位置信息对象
22
28
  * @returns CallExpression
23
29
  */
24
- export declare function createDynamicCall(argument: Expression, alias?: string): CallExpression;
30
+ export declare function createDynamicCall(argument: Expression, alias?: string, locInfo?: ObjectExpression | null): CallExpression;
25
31
  /**
26
32
  * 创建 branch 调用
27
33
  * @param condition - 条件函数
28
34
  * @param branches - 分支函数数组
29
35
  * @param alias - branch 别名
36
+ * @param locInfo - 位置信息对象
30
37
  * @returns CallExpression
31
38
  */
32
- export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string): CallExpression;
39
+ export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string, locInfo?: ObjectExpression | null): CallExpression;
33
40
  /**
34
41
  * 创建 createView 调用
35
42
  * @param type - 元素类型
@@ -73,3 +80,13 @@ export declare function addPureComment<T extends CallExpression>(node: T): T;
73
80
  * @returns 别名或原名
74
81
  */
75
82
  export declare function getAlias(aliases: VitarxImportAliases, name: keyof VitarxImportAliases): string;
83
+ /**
84
+ * 获取开发环境下的位置信息对象
85
+ * 仅在开发环境下返回位置信息,生产环境返回 null
86
+ * @param ctx - 转换上下文
87
+ * @param node - AST 节点或位置信息
88
+ * @returns 位置信息对象或 null
89
+ */
90
+ export declare function getDevLocInfo(ctx: TransformContext, node: t.Node | {
91
+ loc: t.SourceLocation | null;
92
+ }): ObjectExpression | null;
@@ -40,22 +40,30 @@ export function createAccessCall(object, key, alias) {
40
40
  * 创建 dynamic 调用
41
41
  * @param argument - 参数表达式
42
42
  * @param alias - dynamic 别名
43
+ * @param locInfo - 位置信息对象
43
44
  * @returns CallExpression
44
45
  */
45
- export function createDynamicCall(argument, alias) {
46
- return t.callExpression(t.identifier(alias || 'dynamic'), [
47
- t.arrowFunctionExpression([], argument)
48
- ]);
46
+ export function createDynamicCall(argument, alias, locInfo) {
47
+ const args = [t.arrowFunctionExpression([], argument)];
48
+ if (locInfo) {
49
+ args.push(locInfo);
50
+ }
51
+ return t.callExpression(t.identifier(alias || 'dynamic'), args);
49
52
  }
50
53
  /**
51
54
  * 创建 branch 调用
52
55
  * @param condition - 条件函数
53
56
  * @param branches - 分支函数数组
54
57
  * @param alias - branch 别名
58
+ * @param locInfo - 位置信息对象
55
59
  * @returns CallExpression
56
60
  */
57
- export function createBranchCall(condition, branches, alias) {
58
- return t.callExpression(t.identifier(alias || 'branch'), [condition, t.arrayExpression(branches)]);
61
+ export function createBranchCall(condition, branches, alias, locInfo) {
62
+ const args = [condition, t.arrayExpression(branches)];
63
+ if (locInfo) {
64
+ args.push(locInfo);
65
+ }
66
+ return t.callExpression(t.identifier(alias || 'branch'), args);
59
67
  }
60
68
  /**
61
69
  * 创建 createView 调用
@@ -140,3 +148,13 @@ export function addPureComment(node) {
140
148
  export function getAlias(aliases, name) {
141
149
  return aliases[name] || name;
142
150
  }
151
+ /**
152
+ * 获取开发环境下的位置信息对象
153
+ * 仅在开发环境下返回位置信息,生产环境返回 null
154
+ * @param ctx - 转换上下文
155
+ * @param node - AST 节点或位置信息
156
+ * @returns 位置信息对象或 null
157
+ */
158
+ export function getDevLocInfo(ctx, node) {
159
+ return ctx.options.dev && node.loc ? createLocationObject(ctx.filename, node.loc) : null;
160
+ }
@@ -15,6 +15,8 @@ export interface BranchConfig {
15
15
  branches: t.ArrowFunctionExpression[];
16
16
  /** 是否使用 unref 包装标识符条件 */
17
17
  useRef?: boolean;
18
+ /** 位置信息对象 */
19
+ locInfo?: t.ObjectExpression | null;
18
20
  }
19
21
  /**
20
22
  * 创建 branch 调用
@@ -28,20 +30,13 @@ export declare function createBranch(config: BranchConfig, ctx: TransformContext
28
30
  * 从后向前构建三元表达式链
29
31
  */
30
32
  export declare function buildNestedCondition(conditions: t.Expression[], ctx: TransformContext, useRef?: boolean): t.Expression;
31
- /**
32
- * 从条件数组和分支节点创建完整的 branch 调用
33
- * @param conditions - 条件表达式数组
34
- * @param branchNodes - 分支节点数组(会被包装为箭头函数)
35
- * @param ctx - 转换上下文
36
- * @returns branch 调用表达式
37
- */
38
- export declare function createBranchFromNodes(conditions: t.Expression[], branchNodes: t.Expression[], ctx: TransformContext): t.CallExpression;
39
33
  /**
40
34
  * 创建简单的二元条件 branch(用于三元表达式)
41
35
  * @param condition - 条件表达式
42
36
  * @param consequent - 真值分支
43
37
  * @param alternate - 假值分支
44
38
  * @param ctx - 转换上下文
39
+ * @param locInfo - 位置信息对象
45
40
  * @returns branch 调用表达式
46
41
  */
47
- export declare function createBinaryBranch(condition: t.Expression, consequent: t.Expression, alternate: t.Expression, ctx: TransformContext): t.CallExpression;
42
+ export declare function createBinaryBranch(condition: t.Expression, consequent: t.Expression, alternate: t.Expression, ctx: TransformContext, locInfo?: t.ObjectExpression | null): t.CallExpression;
@@ -14,7 +14,7 @@ import { addPureComment, createArrowFunction, createBranchCall, createUnrefCall,
14
14
  * @returns branch 调用表达式
15
15
  */
16
16
  export function createBranch(config, ctx) {
17
- const { conditions, branches, useRef = true } = config;
17
+ const { conditions, branches, useRef = true, locInfo } = config;
18
18
  markImport(ctx, 'branch');
19
19
  // 只有在需要 unref 且条件中有标识符时才标记导入
20
20
  if (useRef && conditions.some(c => isIdentifier(c))) {
@@ -22,7 +22,7 @@ export function createBranch(config, ctx) {
22
22
  }
23
23
  const conditionExpr = buildNestedCondition(conditions, ctx, useRef);
24
24
  const branchAlias = getAlias(ctx.vitarxAliases, 'branch');
25
- return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias));
25
+ return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias, locInfo));
26
26
  }
27
27
  /**
28
28
  * 判断表达式是否需要括号包裹
@@ -79,29 +79,19 @@ export function buildNestedCondition(conditions, ctx, useRef = true) {
79
79
  }
80
80
  return result || t.nullLiteral();
81
81
  }
82
- /**
83
- * 从条件数组和分支节点创建完整的 branch 调用
84
- * @param conditions - 条件表达式数组
85
- * @param branchNodes - 分支节点数组(会被包装为箭头函数)
86
- * @param ctx - 转换上下文
87
- * @returns branch 调用表达式
88
- */
89
- export function createBranchFromNodes(conditions, branchNodes, ctx) {
90
- const branches = branchNodes.map(node => createArrowFunction(node));
91
- return createBranch({ conditions, branches, useRef: true }, ctx);
92
- }
93
82
  /**
94
83
  * 创建简单的二元条件 branch(用于三元表达式)
95
84
  * @param condition - 条件表达式
96
85
  * @param consequent - 真值分支
97
86
  * @param alternate - 假值分支
98
87
  * @param ctx - 转换上下文
88
+ * @param locInfo - 位置信息对象
99
89
  * @returns branch 调用表达式
100
90
  */
101
- export function createBinaryBranch(condition, consequent, alternate, ctx) {
91
+ export function createBinaryBranch(condition, consequent, alternate, ctx, locInfo) {
102
92
  const conditions = [condition, t.booleanLiteral(true)];
103
93
  const branches = [createArrowFunction(consequent), createArrowFunction(alternate)];
104
94
  // 只有条件是 Identifier 时才需要 unref
105
95
  const useRef = isIdentifier(condition);
106
- return createBranch({ conditions, branches, useRef }, ctx);
96
+ return createBranch({ conditions, branches, useRef, locInfo }, ctx);
107
97
  }
@@ -1,6 +1,6 @@
1
1
  export { isWhitespaceJSXText } from './ast-guards.js';
2
- export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeAttribute, filterWhitespaceChildren, validateMatchInSwitch } from './jsx-helpers.js';
3
- export { createUnrefCall, createAccessCall, createDynamicCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias } from './ast-builders.js';
2
+ export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateMatchInSwitch } from './jsx-helpers.js';
3
+ export { createUnrefCall, createAccessCall, createDynamicCall, 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';
@@ -1,9 +1,9 @@
1
1
  // 类型守卫
2
2
  export { isWhitespaceJSXText } from './ast-guards.js';
3
3
  // JSX 辅助函数
4
- export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeAttribute, filterWhitespaceChildren, validateMatchInSwitch } from './jsx-helpers.js';
4
+ export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateMatchInSwitch } from './jsx-helpers.js';
5
5
  // AST 构建函数
6
- export { createUnrefCall, createAccessCall, createDynamicCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias } from './ast-builders.js';
6
+ export { createUnrefCall, createAccessCall, createDynamicCall, 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 工厂
@@ -52,6 +52,10 @@ export declare function isVElse(node: JSXElement): boolean;
52
52
  * 移除元素上所有 v- 开头的指令属性
53
53
  */
54
54
  export declare function removeVDirectives(node: JSXElement): void;
55
+ /**
56
+ * 移除元素上的 v-if 链指令(v-if、v-else-if、v-else)
57
+ */
58
+ export declare function removeVIfChainDirectives(node: JSXElement): void;
55
59
  /**
56
60
  * 移除元素上指定名称的属性
57
61
  */
@@ -60,6 +64,18 @@ export declare function removeAttribute(node: JSXElement, attrName: string): voi
60
64
  * 过滤掉空白文本子节点
61
65
  */
62
66
  export declare function filterWhitespaceChildren(children: t.Node[]): t.Node[];
67
+ /**
68
+ * 检查 JSX 元素是否有有效的子元素
69
+ * @param node - JSX 元素节点
70
+ * @returns 是否有有效子元素
71
+ */
72
+ export declare function hasEffectiveChildren(node: JSXElement): boolean;
73
+ /**
74
+ * 过滤并返回有效的子元素
75
+ * @param node - JSX 元素节点
76
+ * @returns 有效子元素数组
77
+ */
78
+ export declare function filterEffectiveChildren(node: JSXElement): t.Node[];
63
79
  /**
64
80
  * 校验 Match 组件必须在 Switch 内使用
65
81
  * @param children - 子节点数组
@@ -127,6 +127,26 @@ export function removeVDirectives(node) {
127
127
  return true;
128
128
  });
129
129
  }
130
+ /**
131
+ * 移除元素上的 v-if 链指令(v-if、v-else-if、v-else)
132
+ */
133
+ export function removeVIfChainDirectives(node) {
134
+ const vIfChainDirectives = ['v-if', 'v-else-if', 'v-else'];
135
+ node.openingElement.attributes = node.openingElement.attributes.filter(attr => {
136
+ if (attr.type === 'JSXAttribute') {
137
+ 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);
145
+ }
146
+ }
147
+ return true;
148
+ });
149
+ }
130
150
  /**
131
151
  * 移除元素上指定名称的属性
132
152
  */
@@ -152,6 +172,38 @@ export function removeAttribute(node, attrName) {
152
172
  export function filterWhitespaceChildren(children) {
153
173
  return children.filter(child => !isJSXText(child) || !isWhitespaceJSXText(child));
154
174
  }
175
+ /**
176
+ * 检查 JSX 元素是否有有效的子元素
177
+ * @param node - JSX 元素节点
178
+ * @returns 是否有有效子元素
179
+ */
180
+ export function hasEffectiveChildren(node) {
181
+ return node.children.some(child => {
182
+ if (isJSXText(child)) {
183
+ return child.value.trim().length > 0;
184
+ }
185
+ if (child.type === 'JSXExpressionContainer') {
186
+ return child.expression.type !== 'JSXEmptyExpression';
187
+ }
188
+ return true;
189
+ });
190
+ }
191
+ /**
192
+ * 过滤并返回有效的子元素
193
+ * @param node - JSX 元素节点
194
+ * @returns 有效子元素数组
195
+ */
196
+ export function filterEffectiveChildren(node) {
197
+ return node.children.filter(child => {
198
+ if (isJSXText(child)) {
199
+ return child.value.trim().length > 0;
200
+ }
201
+ if (child.type === 'JSXExpressionContainer') {
202
+ return child.expression.type !== 'JSXEmptyExpression';
203
+ }
204
+ return true;
205
+ });
206
+ }
155
207
  /**
156
208
  * 校验 Match 组件必须在 Switch 内使用
157
209
  * @param children - 子节点数组
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitarx/plugin-vite",
3
- "version": "0.0.1-alpha.1",
3
+ "version": "0.0.1-beta.1",
4
4
  "description": "The official plugin for Vitarx support in Vite.",
5
5
  "author": "ZhuChongLin <8210856@qq.com>",
6
6
  "license": "MIT",