@vureact/compiler-core 1.0.4 → 1.1.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
@@ -1,3 +1,280 @@
1
1
  # @vureact/compiler-core
2
2
 
3
- 👉 [https://vureact.top](https://vureact.top/en)
3
+ ## What is VuReact?
4
+
5
+ [VuReact](http://vureact.top) (pronounced /vjuːˈriːækt/) is an intelligent compilation toolchain for Vue 3 → React migration.
6
+
7
+ It is not a simple syntax conversion, but **semantic-level compilation**: it understands the intent of Vue code and generates code that adheres to React best practices. It consists of two parts: **compile-time transformation** + **runtime adaptation**.
8
+
9
+ The core strategy is **"convention over configuration"** — through clear compilation conventions, it ensures stable and reliable conversion, making it especially suitable for **progressive migration** scenarios.
10
+
11
+ ## Quick Start
12
+
13
+ This section will guide you through creating, compiling, and running your first VuReact project; alternatively, you can check out the [online examples first](https://codesandbox.io/p/sandbox/examples-f5rlpk).
14
+
15
+ After completion, you will clearly understand three things:
16
+
17
+ 1. Under what conventions input SFCs can be stably converted
18
+ 2. What the compiled directory structure looks like
19
+ 3. The semantic correspondence between the output TSX and the original SFC
20
+ 4. The compiler automatically analyzes and appends dependencies, eliminating the need to manually manage React hooks dependencies
21
+
22
+ ## Step 0: Prepare the Directory
23
+
24
+ First, set up a minimal project (illustration):
25
+
26
+ ```txt
27
+ my-app/
28
+ ├─ src/
29
+ │ ├─ components/
30
+ │ │ └─ Counter.vue
31
+ │ ├─ main.ts
32
+ │ └─ index.css
33
+ ├─ package.json
34
+ └─ vureact.config.js
35
+ ```
36
+
37
+ ## Step 1: Installation
38
+
39
+ Install the VuReact compiler in your Vue project:
40
+
41
+ ```bash
42
+ # Using npm
43
+ npm install -D @vureact/compiler-core
44
+
45
+ # Using yarn
46
+ yarn add -D @vureact/compiler-core
47
+
48
+ # Using pnpm
49
+ pnpm add -D @vureact/compiler-core
50
+ ```
51
+
52
+ ## Step 2: Write the Input SFC
53
+
54
+ `src/components/Counter.vue`
55
+
56
+ ```html
57
+ <template>
58
+ <section class="counter-card">
59
+ <h2>{{ props.title || title }}</h2>
60
+ <p>Count: {{ count }}</p>
61
+ <button @click="increment">+1</button>
62
+ <button @click="methods.decrease">-1</button>
63
+ </section>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ // @vr-name: Counter (Note: Tells the compiler what component name to generate)
68
+ import { computed, ref } from 'vue';
69
+
70
+ // You can also use macros to define component names
71
+ defineOptions({ name: 'Counter' });
72
+
73
+ // Define props
74
+ const props = defineProps<{ title?: string }>();
75
+
76
+ // Define emits
77
+ const emits = defineEmits<{
78
+ (e: 'change'): void;
79
+ (e: 'update', value: number): number;
80
+ }>();
81
+
82
+ const step = ref(1);
83
+ const count = ref(0);
84
+ const title = computed(() => `Counter x${step.value}`);
85
+
86
+ const increment = () => {
87
+ count.value += step.value;
88
+ emits('update', count.value);
89
+ };
90
+
91
+ const methods = {
92
+ decrease() {
93
+ count.value -= step.value;
94
+ },
95
+ };
96
+ </script>
97
+
98
+ <style lang="less" scoped>
99
+ @border-color: #ddd;
100
+ @border-radius: 8px;
101
+ @padding-base: 12px;
102
+
103
+ .counter-card {
104
+ border: 1px solid @border-color;
105
+ border-radius: @border-radius;
106
+ padding: @padding-base;
107
+ }
108
+ </style>
109
+ ```
110
+
111
+ ## Step 3: Configure the Compiler
112
+
113
+ `vureact.config.js`
114
+
115
+ ```js
116
+ import { defineConfig } from '@vureact/compiler-core';
117
+
118
+ export default defineConfig({
119
+ input: 'src',
120
+ // Key: Exclude Vue entry files to avoid entry semantic conflicts
121
+ exclude: ['src/main.ts'],
122
+ output: {
123
+ workspace: '.vureact',
124
+ outDir: 'dist',
125
+ // Disable environment initialization for tutorial scenarios to observe pure compilation output
126
+ bootstrapVite: false,
127
+ },
128
+ format: {
129
+ enabled: true, // Enable formatting (this will increase compilation time).
130
+ formatter: 'prettier',
131
+ },
132
+ });
133
+ ```
134
+
135
+ ## Step 4: Execute Compilation
136
+
137
+ ### Method 1: Use the npx command
138
+
139
+ Run in the root directory:
140
+
141
+ ```bash
142
+ npx vureact build
143
+ ```
144
+
145
+ ### Method 2: Use npm scripts
146
+
147
+ Add script commands to `package.json`:
148
+
149
+ ```json
150
+ "scripts": {
151
+ "watch": "vureact watch",
152
+ "build": "vureact build"
153
+ }
154
+ ```
155
+
156
+ ```bash
157
+ npm run build
158
+ ```
159
+
160
+ ## Step 5: View the Output Directory Tree
161
+
162
+ Compiled directory (illustration):
163
+
164
+ ```txt
165
+ my-app/
166
+ ├─ .vureact/
167
+ │ ├─ cache/
168
+ │ │ └─ _metadata.json
169
+ │ └─ dist/
170
+ │ └─ src/
171
+ │ └─ components/
172
+ │ ├─ Counter.tsx
173
+ │ └─ Counter-<hash>.css
174
+ ├─ src/
175
+ │ └─ ...
176
+ └─ vureact.config.js
177
+ ```
178
+
179
+ ## Step 6: Compare the Generated Results
180
+
181
+ Below is a typical formatted output (slightly simplified for illustration; the actual hash and property names are subject to local output):
182
+
183
+ ```ts
184
+ import { memo, useCallback, useMemo } from 'react';
185
+ import { useComputed, useVRef } from '@vureact/runtime-core';
186
+ import './Counter-a1b2c3.css';
187
+
188
+ // Derived from defineProps and defineEmits
189
+ type ICounterType = {
190
+ title?: string
191
+ onChange: () => void;
192
+ onUpdate: (value: number) => number;
193
+ }
194
+
195
+ // Component wrapped with memo
196
+ const Counter = memo((props: ICounterType) => {
197
+ // ref/computed converted to equivalent adaptation APIs
198
+ const step = useVRef(1);
199
+ const count = useVRef(0);
200
+ const title = useComputed(() => `Counter x${step.value}`);
201
+
202
+ // Automatically analyze dependencies of top-level arrow functions and append useCallback optimization
203
+ const increment = useCallback(() => {
204
+ count.value += step.value;
205
+ props.onUpdate?.(count.value); // emits conversion
206
+ }, [count.value, step.value, props.onUpdate]);
207
+
208
+ // Automatically analyze dependencies in top-level objects and append useMemo optimization
209
+ const methods = useMemo(
210
+ () => ({
211
+ decrease() {
212
+ count.value -= step.value;
213
+ },
214
+ }),
215
+ [count.value, step.value],
216
+ );
217
+
218
+ return (
219
+ <>
220
+ <section className="counter-card" data-css-a1b2c3>
221
+ <h2 data-css-a1b2c3>{props.title || title.value}</h2>
222
+ <p data-css-a1b2c3>Count: {count.value}</p>
223
+ <button onClick={increment} data-css-a1b2c3>
224
+ +1
225
+ </button>
226
+ <button onClick={methods.decrease} data-css-a1b2c3>
227
+ -1
228
+ </button>
229
+ </section>
230
+ </>
231
+ );
232
+ });
233
+
234
+ export default Counter;
235
+ ```
236
+
237
+ CSS file content:
238
+
239
+ ```css
240
+ .counter-card[data-css-a1b2c3] {
241
+ border: 1px solid #ddd;
242
+ border-radius: 8px;
243
+ padding: 12px;
244
+ }
245
+ ```
246
+
247
+ ## Key Observations
248
+
249
+ 1. The special comment `// @vr-name: Counter` defines the component name
250
+ 2. `defineProps` and `defineEmits` are converted to TS component types
251
+ 3. Non-pure UI display components are wrapped with `memo` by default
252
+ 4. `ref` / `computed` are converted to runtime adaptation APIs (`useVRef` / `useComputed`)
253
+ 5. Template event callbacks generate React-semantic `onClick`
254
+ 6. Top-level arrow functions have their dependencies automatically analyzed and `useCallback` is injected where applicable
255
+ 7. Top-level variable declarations have their dependencies automatically analyzed and `useMemo` is injected where applicable
256
+ 8. The `.value` suffix is added to original `ref` state values in JSX
257
+ 9. Less styles are compiled to CSS code
258
+ 10. Scoped styles generate hashed CSS files and add scoped attributes to elements
259
+
260
+ ## Common Failure Points
261
+
262
+ - Failure to exclude Vue entry files (e.g., `src/main.ts` or `App.vue`)
263
+ - Calling APIs that are converted to Hooks outside the top level
264
+ - Unanalyzable expressions appearing in templates (triggering warnings)
265
+ - Disabling style preprocessing while using `scoped`, leading to scoping failure
266
+
267
+ ## Ecosystem Integration
268
+
269
+ - **[Vue Core Adaptation Package](https://runtime.vureact.top/)**: Provides React versions of Vue's common built-in components, core Composition API, etc.
270
+ - **[Vue Router Adaptation Package](https://router.vureact.top/)**: Supports conversion from Vue Router 4.x to React Router DOM 7.9+.
271
+
272
+ If necessary, you can choose [☣️ Mixed Coding](https://vureact.top/guide/mind-control-readme.html) to directly use the React ecosystem.
273
+
274
+ ## 🔗 Links
275
+
276
+ - GitHub: <https://github.com/vureact-js/core>
277
+ - Gitee: <https://gitee.com/vureact-js/core>
278
+ - Documentation: [https://vureact.top](https://vureact.top/)
279
+ - npm: <https://www.npmjs.com/package/@vureact/compiler-core>
280
+ - Online Examples: <https://codesandbox.io/p/devbox/compiler-examples-n8yg68>
package/README.zh.md ADDED
@@ -0,0 +1,280 @@
1
+ # @vureact/compiler-core
2
+
3
+ ## 什么是 VuReact?
4
+
5
+ [VuReact](http://vureact.top)(发音 `/vjuːˈriːækt/`)是一个面向 Vue 3 → React 的智能编译工具链。
6
+
7
+ 它不是简单的语法转换,而是**语义级编译**:理解 Vue 代码的意图,生成符合 React 最佳实践的代码。由**编译时转换** + **运行时适配**两部分构成。
8
+
9
+ 核心策略是 **“约定优先”** ——通过明确的编译约定,确保转换稳定可靠,尤其适合**渐进式迁移**场景。
10
+
11
+ ## 快速开始
12
+
13
+ 本节将引导你完成第一个 VuReact 项目的创建、编译和运行;或者选择先查看 [在线示例。](https://codesandbox.io/p/sandbox/examples-f5rlpk)
14
+
15
+ 完成后你会明确三件事:
16
+
17
+ 1. 输入 SFC 在什么约定下可稳定转换
18
+ 2. 编译后目录会长什么样
19
+ 3. 输出 TSX 与原始 SFC 的语义对应关系
20
+ 4. 编译器会自动分析并追加依赖,无需手动管理 React hooks 依赖项
21
+
22
+ ## Step 0:准备目录
23
+
24
+ 先准备一个最小工程(示意):
25
+
26
+ ```txt
27
+ my-app/
28
+ ├─ src/
29
+ │ ├─ components/
30
+ │ │ └─ Counter.vue
31
+ │ ├─ main.ts
32
+ │ └─ index.css
33
+ ├─ package.json
34
+ └─ vureact.config.js
35
+ ```
36
+
37
+ ## Step 1:安装
38
+
39
+ 在你的 Vue 项目中安装 VuReact 编译器:
40
+
41
+ ```bash
42
+ # 使用 npm
43
+ npm install -D @vureact/compiler-core
44
+
45
+ # 使用 yarn
46
+ yarn add -D @vureact/compiler-core
47
+
48
+ # 使用 pnpm
49
+ pnpm add -D @vureact/compiler-core
50
+ ```
51
+
52
+ ## Step 2:编写输入 SFC
53
+
54
+ `src/components/Counter.vue`
55
+
56
+ ```html
57
+ <template>
58
+ <section class="counter-card">
59
+ <h2>{{ props.title || title }}</h2>
60
+ <p>Count: {{ count }}</p>
61
+ <button @click="increment">+1</button>
62
+ <button @click="methods.decrease">-1</button>
63
+ </section>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ // @vr-name: Counter (注:用于告诉编译器,该生成什么组件名)
68
+ import { computed, ref } from 'vue';
69
+
70
+ // 也可以使用宏定义组件名
71
+ defineOptions({ name: 'Counter' });
72
+
73
+ // 定义 props
74
+ const props = defineProps<{ title?: string }>();
75
+
76
+ // 定义 emits
77
+ const emits = defineEmits<{
78
+ (e: 'change'): void;
79
+ (e: 'update', value: number): number;
80
+ }>();
81
+
82
+ const step = ref(1);
83
+ const count = ref(0);
84
+ const title = computed(() => `Counter x${step.value}`);
85
+
86
+ const increment = () => {
87
+ count.value += step.value;
88
+ emits('update', count.value);
89
+ };
90
+
91
+ const methods = {
92
+ decrease() {
93
+ count.value -= step.value;
94
+ },
95
+ };
96
+ </script>
97
+
98
+ <style lang="less" scoped>
99
+ @border-color: #ddd;
100
+ @border-radius: 8px;
101
+ @padding-base: 12px;
102
+
103
+ .counter-card {
104
+ border: 1px solid @border-color;
105
+ border-radius: @border-radius;
106
+ padding: @padding-base;
107
+ }
108
+ </style>
109
+ ```
110
+
111
+ ## Step 3:配置编译器
112
+
113
+ `vureact.config.js`
114
+
115
+ ```js
116
+ import { defineConfig } from '@vureact/compiler-core';
117
+
118
+ export default defineConfig({
119
+ input: 'src',
120
+ // 关键:排除 Vue 入口文件,避免入口语义冲突
121
+ exclude: ['src/main.ts'],
122
+ output: {
123
+ workspace: '.vureact',
124
+ outDir: 'dist',
125
+ // 教程场景关闭环境初始化,便于观察纯编译产物
126
+ bootstrapVite: false,
127
+ },
128
+ format: {
129
+ enabled: true, // 开启格式化,同时这也会增加编译耗时。
130
+ formatter: 'prettier',
131
+ },
132
+ });
133
+ ```
134
+
135
+ ## Step 4:执行编译
136
+
137
+ ### 方式一:使用 npx 命令
138
+
139
+ 在根目录下运行:
140
+
141
+ ```bash
142
+ npx vureact build
143
+ ```
144
+
145
+ ### 方式二:使用 npm scripts
146
+
147
+ 在 `package.json` 里添加脚本命令:
148
+
149
+ ```json
150
+ "scripts": {
151
+ "watch": "vureact watch",
152
+ "build": "vureact build"
153
+ }
154
+ ```
155
+
156
+ ```bash
157
+ npm run build
158
+ ```
159
+
160
+ ## Step 5:查看输出目录树
161
+
162
+ 编译后目录(示意):
163
+
164
+ ```txt
165
+ my-app/
166
+ ├─ .vureact/
167
+ │ ├─ cache/
168
+ │ │ └─ _metadata.json
169
+ │ └─ dist/
170
+ │ └─ src/
171
+ │ └─ components/
172
+ │ ├─ Counter.tsx
173
+ │ └─ Counter-<hash>.css
174
+ ├─ src/
175
+ │ └─ ...
176
+ └─ vureact.config.js
177
+ ```
178
+
179
+ ## Step 6:对照生成结果
180
+
181
+ 下面是一个格式化后的典型输出(为说明做了轻微简化,实际哈希与属性名以本地产物为准):
182
+
183
+ ```ts
184
+ import { memo, useCallback, useMemo } from 'react';
185
+ import { useComputed, useVRef } from '@vureact/runtime-core';
186
+ import './Counter-a1b2c3.css';
187
+
188
+ // 根据 defineProps 和 defineEmits 推导
189
+ type ICounterType = {
190
+ title?: string
191
+ onChange: () => void;
192
+ onUpdate: (value: number) => number;
193
+ }
194
+
195
+ // memo 包裹组件
196
+ const Counter = memo((props: ICounterType) => {
197
+ // ref/computed 转换成了对等的适配 API
198
+ const step = useVRef(1);
199
+ const count = useVRef(0);
200
+ const title = useComputed(() => `Counter x${step.value}`);
201
+
202
+ // 自动分析顶层箭头函数依赖,并追加 useCallback 优化
203
+ const increment = useCallback(() => {
204
+ count.value += step.value;
205
+ props.onUpdate?.(count.value); // emits 转换
206
+ }, [count.value, step.value, props.onUpdate]);
207
+
208
+ // 自动分析顶层对象中的依赖,并追加 useMemo 优化
209
+ const methods = useMemo(
210
+ () => ({
211
+ decrease() {
212
+ count.value -= step.value;
213
+ },
214
+ }),
215
+ [count.value, step.value],
216
+ );
217
+
218
+ return (
219
+ <>
220
+ <section className="counter-card" data-css-a1b2c3>
221
+ <h2 data-css-a1b2c3>{props.title || title.value}</h2>
222
+ <p data-css-a1b2c3>Count: {count.value}</p>
223
+ <button onClick={increment} data-css-a1b2c3>
224
+ +1
225
+ </button>
226
+ <button onClick={methods.decrease} data-css-a1b2c3>
227
+ -1
228
+ </button>
229
+ </section>
230
+ </>
231
+ );
232
+ });
233
+
234
+ export default Counter;
235
+ ```
236
+
237
+ CSS 文件内容:
238
+
239
+ ```css
240
+ .counter-card[data-css-a1b2c3] {
241
+ border: 1px solid #ddd;
242
+ border-radius: 8px;
243
+ padding: 12px;
244
+ }
245
+ ```
246
+
247
+ ## 关键观察点
248
+
249
+ 1. `// @vr-name: Counter` 这段特殊注释定义了组件名
250
+ 2. `defineProps` 和 `defineEmits` 被转换成了 TS 组件类型
251
+ 3. 非纯 UI 展示组件,默认会走 `memo` 包装
252
+ 4. `ref` / `computed` 被转换为 runtime 适配 API(`useVRef` / `useComputed`)
253
+ 5. 模板事件回调会生成符合 React 语义的 `onClick`
254
+ 6. 顶层箭头函数自动分析依赖,尝试注入 `useCallback`
255
+ 7. 顶层变量声明自动分析依赖,尝试注入 `useMemo`
256
+ 8. 对 JSX 中的原 `ref` 状态值补上 `.value`
257
+ 9. `less` 样式被编译为 css 代码
258
+ 10. `scoped` 样式会生成带哈希的 css 文件,并在元素上标注作用域属性
259
+
260
+ ## 常见失败点
261
+
262
+ - 没排除 Vue 入口文件,如 `src/main.ts` 或 `App.vue`
263
+ - 在非顶层调用会被转换为 Hook 的 API
264
+ - 模板里出现不可分析表达式并被告警
265
+ - 关闭样式预处理且使用 `scoped`,导致作用域失效
266
+
267
+ ## 生态集成
268
+
269
+ - **[Vue 核心适配包](https://runtime.vureact.top/)**:提供 React 版的 Vue 常用内置组件、核心 Composition API 等
270
+ - **[Vue 路由适配包](https://router.vureact.top/)**:支持 Vue Router 4.x -> React Router DOM 7.9+ 转换
271
+
272
+ 如果确实需要,你可以选择 [☣️混合编写](https://vureact.top/guide/mind-control-readme.html),以此直接使用 React 生态。
273
+
274
+ ## 🔗 链接
275
+
276
+ - GitHub:<https://github.com/vureact-js/core>
277
+ - Gitee:<https://gitee.com/vureact-js/core>
278
+ - 文档:[https://vureact.top](https://vureact.top/)
279
+ - npm:<https://www.npmjs.com/package/@vureact/compiler-core>
280
+ - 在线示例:<https://codesandbox.io/p/devbox/compiler-examples-n8yg68>