@vobs/compiler 1.0.0 → 1.2.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.
@@ -24,10 +24,10 @@ describe('compiler', () => {
24
24
  })
25
25
 
26
26
  it('编译 JSX 为 DOM 渲染调用', () => {
27
- const code = `const el = <div>hello</div>`
28
-
27
+ const code = `const el = <div>hello {name.value}</div>`
28
+
29
29
  const result = compile(code)
30
-
30
+
31
31
  expect(result).toContain('createElement')
32
32
  expect(result).toContain('createText')
33
33
  expect(result).toContain('insertBefore')
@@ -37,12 +37,12 @@ describe('compiler', () => {
37
37
  const code = `
38
38
  function Counter() {
39
39
  const count = state(0)
40
- return <div>hello</div>
40
+ return <div>hello {count.value}</div>
41
41
  }
42
42
  `
43
-
43
+
44
44
  const result = compile(code)
45
-
45
+
46
46
  expect(result).toContain('function Counter')
47
47
  expect(result).toContain('createElement')
48
48
  })
@@ -51,13 +51,50 @@ describe('compiler', () => {
51
51
  const code = `
52
52
  const el = <button onClick={() => { console.log('clicked') }}>点击</button>
53
53
  `
54
-
54
+
55
55
  const result = compile(code)
56
-
56
+
57
57
  expect(result).toContain('addEventListener')
58
58
  expect(result).toContain('click')
59
59
  })
60
60
 
61
+ it('重入安全:插件在编译过程中调用 compile() 不污染外层编译状态', () => {
62
+ // 外层源码的变量名会命中内层片段用到的 helper 名,验证 takenNames/helperAliases
63
+ // 等状态在外层编译全程保持独立(此前为模块级变量,嵌套编译会互相覆盖)。
64
+ const nestedPlugin: CompilerPlugin = {
65
+ name: 'nested-compile',
66
+ analyze() {
67
+ compile(`const el = <span>inner</span>`, { filename: 'inner.tsx' })
68
+ }
69
+ }
70
+ const code = `
71
+ const createElement = () => null
72
+ const el = <div>outer {name.value}</div>
73
+ `
74
+
75
+ const result = compileWithSourceMap(code, {
76
+ filename: 'outer.tsx',
77
+ plugins: [nestedPlugin]
78
+ })
79
+
80
+ // 外层的局部绑定 createElement 仍在,运行时 helper 注入为别名导入
81
+ expect(result.code).toContain('const createElement = () => null')
82
+ expect(result.code).toContain('_vobs_createElement')
83
+ expect(result.code).toContain('outer')
84
+ expect(result.diagnostics).toEqual([])
85
+ })
86
+
87
+ it('重入安全:连续多次编译各自独立,计数器与别名不跨编译泄漏', () => {
88
+ const first = compileWithSourceMap(`const a = <div className="x">{a.value}</div>`, { filename: 'a.tsx' })
89
+ const second = compileWithSourceMap(`const b = <div className="y">{b.value}</div>`, { filename: 'b.tsx' })
90
+
91
+ // 临时变量计数器每次编译从 0 开始
92
+ expect(first.code).toContain('_el0')
93
+ expect(second.code).toContain('_el0')
94
+ // 第二次编译不带第一次残留的诊断
95
+ expect(second.diagnostics).toEqual([])
96
+ })
97
+
61
98
  it('将静态属性合并为一次 setStaticProps 调用', () => {
62
99
  const result = compile(`const el = <input className="field" disabled id="name" />`)
63
100
  expect(result).toContain('setStaticProps')
@@ -85,6 +122,38 @@ describe('compiler', () => {
85
122
  expect(result).toContain('state(0)')
86
123
  })
87
124
 
125
+ it('自动推断 state() 的 debugName(未显式传参时使用变量名)', () => {
126
+ const result = compile(`import { state } from '@vobs/reactivity'\nconst username = state('')`)
127
+
128
+ expect(result).toContain(`state('', "username")`)
129
+ })
130
+
131
+ it('别名导入的 state 同样推断 debugName', () => {
132
+ const result = compile(`import { state as signal } from '@vobs/reactivity'\nconst username = signal('')`)
133
+
134
+ expect(result).toContain(`signal('', "username")`)
135
+ })
136
+
137
+ it('显式传入 debugName 时不覆盖', () => {
138
+ const result = compile(`import { state } from '@vobs/reactivity'\nconst username = state('', 'auth.name')`)
139
+
140
+ expect(result).toContain(`state('', 'auth.name')`)
141
+ expect(result).not.toContain('"username"')
142
+ })
143
+
144
+ it('state 被本地声明遮蔽时不推断 debugName', () => {
145
+ const result = compile(`import { state } from '@vobs/reactivity'\nfunction state(value: unknown) { return value }\nconst username = state('')`)
146
+
147
+ expect(result).toContain(`state('')`)
148
+ expect(result).not.toContain('"username"')
149
+ })
150
+
151
+ it('非 state 调用不推断 debugName', () => {
152
+ const result = compile(`const items = useState([])`)
153
+
154
+ expect(result).toContain('useState([])')
155
+ })
156
+
88
157
  it('编译条件 JSX 为动态块', () => {
89
158
  const result = compile(`const el = <div>{show.value && <span>visible</span>}</div>`)
90
159
 
@@ -168,9 +237,11 @@ describe('compiler', () => {
168
237
  it('只导入生成代码实际使用的运行时 helper', () => {
169
238
  const result = compile(`const el = <div>hello</div>`)
170
239
 
171
- expect(result).toContain('createElement')
172
- expect(result).toContain('createText')
173
- expect(result).toContain('insertBefore')
240
+ // 完全静态的子树提升为模板:只需 createTemplate + cloneTemplate
241
+ expect(result).toContain('createTemplate')
242
+ expect(result).toContain('cloneTemplate')
243
+ expect(result).not.toContain('createElement')
244
+ expect(result).not.toContain('insertBefore')
174
245
  expect(result).not.toContain('bindAttribute')
175
246
  expect(result).not.toContain('bindText')
176
247
  expect(result).not.toContain('insertDynamic')
@@ -240,7 +311,7 @@ describe('compiler', () => {
240
311
  const result = compile(`
241
312
  import { createElement } from './shim'
242
313
  export const tag = createElement('span')
243
- const el = <div>hello</div>
314
+ const el = <div>hello {name.value}</div>
244
315
  `)
245
316
 
246
317
  // 用户导入与调用保持不变,编译器 helper 走别名,不再产生重复声明
@@ -255,12 +326,12 @@ describe('compiler', () => {
255
326
  it('源文件本地声明与 helper 同名时注入别名 import', () => {
256
327
  const result = compile(`
257
328
  const createText = (value: string) => value
258
- const el = <div>hello</div>
329
+ const el = <div>hello {name.value}</div>
259
330
  `)
260
331
 
261
332
  expect(result).toContain('createText as _vobs_createText')
262
333
  expect(result).toContain('from "@vobs/vobs"')
263
- expect(result).toContain('insertBefore(_el0, _vobs_createText("hello")')
334
+ expect(result).toContain('insertBefore(_el0, _vobs_createText("hello ")')
264
335
  // 用户本地声明不受影响
265
336
  expect(result).toContain('const createText = (value: string) => value')
266
337
  })
@@ -283,7 +354,7 @@ describe('compiler', () => {
283
354
  it('同名绑定触发别名时保留别名一致性(同一 helper 只注入一次)', () => {
284
355
  const result = compile(`
285
356
  const createElement = String
286
- const el = <div><span>one</span></div>
357
+ const el = <div><span>one {name.value}</span></div>
287
358
  `)
288
359
 
289
360
  expect(result.match(/_vobs_createElement/g)?.length).toBeGreaterThanOrEqual(2)
@@ -294,7 +365,7 @@ describe('compiler', () => {
294
365
  it('生成的临时变量避开用户已声明的名称', () => {
295
366
  const result = compile(`
296
367
  const _el0 = 'reserved'
297
- const el = <div>hello</div>
368
+ const el = <div>hello {name.value}</div>
298
369
  `)
299
370
 
300
371
  // _el0 被用户占用,编译产物必须改用下一个可用名称
@@ -335,6 +406,17 @@ describe('compiler', () => {
335
406
  expect(result).toContain('line: 2')
336
407
  })
337
408
 
409
+ it('sourceLocation: false 剔除组件源码位置', () => {
410
+ const result = compile(`
411
+ const el = <Editor><input disabled={locked.value} value={text.value} /></Editor>
412
+ `, { filename: 'src/editor.tsx', sourceLocation: false })
413
+
414
+ expect(result).toContain('createComponent')
415
+ expect(result).not.toContain('file:')
416
+ expect(result).not.toContain('line:')
417
+ expect(result).not.toContain('column:')
418
+ })
419
+
338
420
  it('生成包含原始内容的 Source Map', () => {
339
421
  const result = compileWithSourceMap(`const el = <div>hello</div>`, { filename: 'src/App.tsx' })
340
422