@vitarx/plugin-vite 0.0.1-alpha.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.
Files changed (42) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +298 -0
  3. package/dist/components.js +1 -0
  4. package/dist/constants/index.js +59 -0
  5. package/dist/context.js +78 -0
  6. package/dist/error.js +156 -0
  7. package/dist/hmr-client/index.js +143 -0
  8. package/dist/hmr-client/update.js +53 -0
  9. package/dist/hmr-client/utils.js +125 -0
  10. package/dist/index.js +65 -0
  11. package/dist/passes/components/ifBlock.js +42 -0
  12. package/dist/passes/components/index.js +24 -0
  13. package/dist/passes/components/switch.js +102 -0
  14. package/dist/passes/directives/index.js +6 -0
  15. package/dist/passes/directives/processDirectives.js +46 -0
  16. package/dist/passes/directives/vIf.js +64 -0
  17. package/dist/passes/hmr/index.js +5 -0
  18. package/dist/passes/hmr/inject.js +189 -0
  19. package/dist/passes/imports/collectImports.js +56 -0
  20. package/dist/passes/imports/collectRefVariables.js +95 -0
  21. package/dist/passes/imports/index.js +7 -0
  22. package/dist/passes/imports/injectImports.js +96 -0
  23. package/dist/passes/index.js +16 -0
  24. package/dist/passes/jsx/index.js +7 -0
  25. package/dist/passes/jsx/processChildren.js +114 -0
  26. package/dist/passes/jsx/processJSXElement.js +173 -0
  27. package/dist/passes/jsx/processJSXFragment.js +37 -0
  28. package/dist/passes/props/attribute.js +165 -0
  29. package/dist/passes/props/index.js +94 -0
  30. package/dist/passes/props/types.js +1 -0
  31. package/dist/passes/props/vmodel.js +115 -0
  32. package/dist/transform.js +144 -0
  33. package/dist/utils/ast-builders.js +142 -0
  34. package/dist/utils/ast-guards.js +16 -0
  35. package/dist/utils/branch-factory.js +107 -0
  36. package/dist/utils/component-collect.js +233 -0
  37. package/dist/utils/generate.js +11 -0
  38. package/dist/utils/index.js +16 -0
  39. package/dist/utils/jsx-helpers.js +168 -0
  40. package/dist/utils/pattern-helpers.js +47 -0
  41. package/dist/utils/vif-helpers.js +127 -0
  42. package/package.json +63 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * JSX 相关工具函数
3
+ * @module utils/jsx-helpers
4
+ */
5
+ import * as t from '@babel/types';
6
+ import { isJSXElement, isJSXText } from '@babel/types';
7
+ import { DIRECTIVE_PREFIX, PURE_COMPILE_COMPONENTS } from '../constants/index.js';
8
+ import { isWhitespaceJSXText } from './ast-guards.js';
9
+ import { createError } from '../error.js';
10
+ /**
11
+ * 获取 JSX 元素的名称
12
+ */
13
+ export function getJSXElementName(node) {
14
+ const nameNode = node.openingElement.name;
15
+ return nameNode.type === 'JSXIdentifier' ? nameNode.name : null;
16
+ }
17
+ /**
18
+ * 判断名称是否为纯编译组件
19
+ */
20
+ export function isPureCompileComponent(name) {
21
+ return PURE_COMPILE_COMPONENTS.includes(name);
22
+ }
23
+ /**
24
+ * 判断名称是否为组件(首字母大写)
25
+ */
26
+ export function isComponent(name) {
27
+ return name[0] === name[0]?.toUpperCase();
28
+ }
29
+ /**
30
+ * 判断名称是否为原生元素
31
+ */
32
+ export function isNativeElement(name) {
33
+ return !isComponent(name);
34
+ }
35
+ /**
36
+ * 根据名称获取 JSX 属性
37
+ */
38
+ export function getJSXAttributeByName(node, name) {
39
+ for (const attr of node.openingElement.attributes) {
40
+ if (attr.type === 'JSXAttribute' &&
41
+ attr.name.type === 'JSXIdentifier' &&
42
+ attr.name.name === name) {
43
+ return attr;
44
+ }
45
+ }
46
+ return undefined;
47
+ }
48
+ /**
49
+ * 检查元素是否具有指定指令
50
+ */
51
+ export function hasDirective(node, directiveName) {
52
+ for (const attr of node.openingElement.attributes) {
53
+ if (attr.type === 'JSXAttribute') {
54
+ const attrName = attr.name;
55
+ if (attrName.type === 'JSXNamespacedName') {
56
+ if (attrName.namespace.name === 'v' && attrName.name.name === directiveName.slice(2)) {
57
+ return true;
58
+ }
59
+ }
60
+ else if (attrName.type === 'JSXIdentifier' && attrName.name === directiveName) {
61
+ return true;
62
+ }
63
+ }
64
+ }
65
+ return false;
66
+ }
67
+ /**
68
+ * 获取指令的值
69
+ */
70
+ export function getDirectiveValue(node, directiveName) {
71
+ for (const attr of node.openingElement.attributes) {
72
+ if (attr.type === 'JSXAttribute') {
73
+ const attrName = attr.name;
74
+ let matches = false;
75
+ if (attrName.type === 'JSXNamespacedName') {
76
+ matches = attrName.namespace.name === 'v' && attrName.name.name === directiveName.slice(2);
77
+ }
78
+ else if (attrName.type === 'JSXIdentifier') {
79
+ matches = attrName.name === directiveName;
80
+ }
81
+ if (matches) {
82
+ const value = attr.value;
83
+ if (value?.type === 'JSXExpressionContainer') {
84
+ return value.expression;
85
+ }
86
+ if (value?.type === 'StringLiteral') {
87
+ return value;
88
+ }
89
+ return t.booleanLiteral(true);
90
+ }
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * 检查元素是否为 v-if 链的一部分
97
+ */
98
+ export function isVIfChain(node) {
99
+ return (hasDirective(node, 'v-if') || hasDirective(node, 'v-else-if') || hasDirective(node, 'v-else'));
100
+ }
101
+ /**
102
+ * 检查元素是否有 v-if 指令
103
+ */
104
+ export function isVIf(node) {
105
+ return hasDirective(node, 'v-if');
106
+ }
107
+ /**
108
+ * 检查元素是否有 v-else-if 指令
109
+ */
110
+ export function isVElseIf(node) {
111
+ return hasDirective(node, 'v-else-if');
112
+ }
113
+ /**
114
+ * 检查元素是否有 v-else 指令
115
+ */
116
+ export function isVElse(node) {
117
+ return hasDirective(node, 'v-else');
118
+ }
119
+ /**
120
+ * 移除元素上所有 v- 开头的指令属性
121
+ */
122
+ export function removeVDirectives(node) {
123
+ node.openingElement.attributes = node.openingElement.attributes.filter(attr => {
124
+ if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier') {
125
+ return !attr.name.name.startsWith(DIRECTIVE_PREFIX);
126
+ }
127
+ return true;
128
+ });
129
+ }
130
+ /**
131
+ * 移除元素上指定名称的属性
132
+ */
133
+ export function removeAttribute(node, attrName) {
134
+ const index = node.openingElement.attributes.findIndex(attr => {
135
+ if (attr.type === 'JSXAttribute') {
136
+ const name = attr.name;
137
+ if (name.type === 'JSXIdentifier')
138
+ return name.name === attrName;
139
+ if (name.type === 'JSXNamespacedName') {
140
+ return `${name.namespace.name}:${name.name.name}` === attrName;
141
+ }
142
+ }
143
+ return false;
144
+ });
145
+ if (index !== -1) {
146
+ node.openingElement.attributes.splice(index, 1);
147
+ }
148
+ }
149
+ /**
150
+ * 过滤掉空白文本子节点
151
+ */
152
+ export function filterWhitespaceChildren(children) {
153
+ return children.filter(child => !isJSXText(child) || !isWhitespaceJSXText(child));
154
+ }
155
+ /**
156
+ * 校验 Match 组件必须在 Switch 内使用
157
+ * @param children - 子节点数组
158
+ */
159
+ export function validateMatchInSwitch(children) {
160
+ for (const child of children) {
161
+ if (isJSXElement(child)) {
162
+ const childName = getJSXElementName(child);
163
+ if (childName === 'Match') {
164
+ throw createError('E012', child);
165
+ }
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 从绑定模式中收集变量名
3
+ * 支持标识符、对象模式、数组模式
4
+ * @param pattern - 绑定模式
5
+ * @param variables - 变量名集合
6
+ */
7
+ export function collectPatternBindings(pattern, variables) {
8
+ if (pattern.type === 'Identifier') {
9
+ variables.add(pattern.name);
10
+ }
11
+ else if (pattern.type === 'ObjectPattern') {
12
+ for (const prop of pattern.properties) {
13
+ if (prop.type === 'RestElement') {
14
+ collectPatternBindings(prop.argument, variables);
15
+ }
16
+ else {
17
+ collectPatternBindings(prop.value, variables);
18
+ }
19
+ }
20
+ }
21
+ else if (pattern.type === 'ArrayPattern') {
22
+ for (const elem of pattern.elements) {
23
+ if (elem) {
24
+ collectPatternBindings(elem, variables);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ /**
30
+ * 从对象解构模式中收集变量名
31
+ * 专门用于 toRefs 解构场景
32
+ * @param pattern - 对象模式
33
+ * @param variables - 变量名集合
34
+ */
35
+ export function collectObjectPatternBindings(pattern, variables) {
36
+ for (const prop of pattern.properties) {
37
+ if (prop.type === 'RestElement') {
38
+ collectPatternBindings(prop.argument, variables);
39
+ }
40
+ else if (prop.value.type === 'Identifier') {
41
+ variables.add(prop.value.name);
42
+ }
43
+ else if (prop.value.type === 'ObjectPattern') {
44
+ collectObjectPatternBindings(prop.value, variables);
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * v-if 链处理工具模块
3
+ * 统一处理 v-if/v-else-if/v-else 链的验证和收集
4
+ * @module utils/vif-helpers
5
+ */
6
+ import * as t from '@babel/types';
7
+ import { isJSXElement, isJSXText } from '@babel/types';
8
+ import { createError } from '../error.js';
9
+ import { isWhitespaceJSXText } from './ast-guards.js';
10
+ import { getDirectiveValue, hasDirective, isVElse, isVElseIf, isVIf, isVIfChain } from './jsx-helpers.js';
11
+ /**
12
+ * 验证 v-if 链的合法性
13
+ */
14
+ export function validateVIfChain(children) {
15
+ if (children.length === 0)
16
+ return;
17
+ if (!hasDirective(children[0], 'v-if')) {
18
+ throw createError('E008', children[0], 'First element must have v-if directive');
19
+ }
20
+ for (let i = 1; i < children.length; i++) {
21
+ const child = children[i];
22
+ const prevChild = children[i - 1];
23
+ const hasVElseIf = hasDirective(child, 'v-else-if');
24
+ const hasVElse = hasDirective(child, 'v-else');
25
+ if (!hasVElseIf && !hasVElse) {
26
+ throw createError('E008', child, 'Elements after v-if must have v-else-if or v-else directive');
27
+ }
28
+ if (hasVElse && i !== children.length - 1) {
29
+ throw createError('E008', child, 'v-else must be the last element');
30
+ }
31
+ if (hasVElseIf && hasDirective(prevChild, 'v-else')) {
32
+ throw createError('E008', child, 'v-else-if cannot follow v-else');
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * 从 JSX 元素数组收集 v-if 链信息
38
+ */
39
+ export function collectVIfChainInfo(nodes) {
40
+ const conditions = [];
41
+ for (const node of nodes) {
42
+ const vIfValue = getDirectiveValue(node, 'v-if');
43
+ if (vIfValue) {
44
+ conditions.push(vIfValue);
45
+ continue;
46
+ }
47
+ const vElseIfValue = getDirectiveValue(node, 'v-else-if');
48
+ if (vElseIfValue) {
49
+ conditions.push(vElseIfValue);
50
+ continue;
51
+ }
52
+ if (hasDirective(node, 'v-else')) {
53
+ conditions.push(t.booleanLiteral(true));
54
+ continue;
55
+ }
56
+ throw createError('E008', node, 'Element missing v-if/v-else-if/v-else directive');
57
+ }
58
+ return { nodes, conditions, endIndex: nodes.length - 1 };
59
+ }
60
+ /**
61
+ * 收集 Fragment 中的 v-if 链
62
+ */
63
+ export function collectFragmentVIfChains(children) {
64
+ const chains = [];
65
+ let i = 0;
66
+ while (i < children.length) {
67
+ const child = children[i];
68
+ if (isJSXText(child)) {
69
+ i++;
70
+ continue;
71
+ }
72
+ if (!isJSXElement(child)) {
73
+ i++;
74
+ continue;
75
+ }
76
+ if (!isVIfChain(child)) {
77
+ i++;
78
+ continue;
79
+ }
80
+ if (isVIf(child)) {
81
+ const chain = collectSingleChainFromFragment(children, i);
82
+ chains.push(chain);
83
+ i = chain.endIndex + 1;
84
+ }
85
+ else if (isVElseIf(child) || isVElse(child)) {
86
+ throw createError(isVElse(child) ? 'E003' : 'E004', child);
87
+ }
88
+ else {
89
+ i++;
90
+ }
91
+ }
92
+ return chains;
93
+ }
94
+ /**
95
+ * 从 Fragment 收集单个 v-if 链
96
+ */
97
+ function collectSingleChainFromFragment(children, startIndex) {
98
+ const nodes = [children[startIndex]];
99
+ const conditions = [getDirectiveValue(nodes[0], 'v-if')];
100
+ let j = startIndex + 1;
101
+ while (j < children.length) {
102
+ const nextChild = children[j];
103
+ if (isJSXText(nextChild) && isWhitespaceJSXText(nextChild)) {
104
+ j++;
105
+ continue;
106
+ }
107
+ if (!isJSXElement(nextChild))
108
+ break;
109
+ if (!isVIfChain(nextChild))
110
+ break;
111
+ if (isVElseIf(nextChild)) {
112
+ nodes.push(nextChild);
113
+ conditions.push(getDirectiveValue(nextChild, 'v-else-if'));
114
+ j++;
115
+ }
116
+ else if (isVElse(nextChild)) {
117
+ nodes.push(nextChild);
118
+ conditions.push(t.booleanLiteral(true));
119
+ j++;
120
+ break;
121
+ }
122
+ else {
123
+ break;
124
+ }
125
+ }
126
+ return { nodes, conditions, endIndex: j - 1 };
127
+ }
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@vitarx/plugin-vite",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "The official plugin for Vitarx support in Vite.",
5
+ "author": "ZhuChongLin <8210856@qq.com>",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "vitarx",
9
+ "vite-plugin"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://gitee.com/vitarx/plugin-vite.git"
14
+ },
15
+ "type": "module",
16
+ "module": "dist/index.js",
17
+ "types": "dist/index.d.ts",
18
+ "files": [
19
+ "dist",
20
+ "LICENSE",
21
+ "README.md"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ }
30
+ },
31
+ "peerDependencies": {
32
+ "vitarx": "^4.0.0-0",
33
+ "vite": "^8.0.0-0"
34
+ },
35
+ "dependencies": {
36
+ "@babel/generator": "^7.29.1",
37
+ "@babel/parser": "^7.29.0",
38
+ "@babel/traverse": "^7.29.0",
39
+ "@babel/types": "^7.29.0",
40
+ "acorn": "^8.16.0",
41
+ "acorn-walk": "^8.3.5"
42
+ },
43
+ "devDependencies": {
44
+ "@commitlint/cli": "^20.4.2",
45
+ "@commitlint/config-conventional": "^20.4.2",
46
+ "@commitlint/types": "^20.4.0",
47
+ "@types/babel__core": "^7.20.5",
48
+ "@types/babel__generator": "^7.27.0",
49
+ "@types/babel__traverse": "^7.28.0",
50
+ "@types/node": "^25.3.2",
51
+ "husky": "^9.1.7",
52
+ "node": "^25.7.0",
53
+ "rimraf": "^6.1.3",
54
+ "vitarx": "^4.0.0-beta.0",
55
+ "vite": "^8.0.0-beta.16",
56
+ "vitest": "^4.0.18"
57
+ },
58
+ "scripts": {
59
+ "build": "rimraf dist && tsc",
60
+ "test": "vitest run",
61
+ "release": "release-cli"
62
+ }
63
+ }