@vobs/compiler 1.1.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.
- package/dist/compile.cjs +1149 -0
- package/dist/compile.cjs.map +1 -0
- package/dist/compile.d.cts +8 -0
- package/dist/compile.d.ts +8 -0
- package/dist/compile.js +1141 -0
- package/dist/compile.js.map +1 -0
- package/dist/i18n-extractor.cjs +51 -0
- package/dist/i18n-extractor.cjs.map +1 -0
- package/dist/i18n-extractor.d.cts +16 -0
- package/dist/i18n-extractor.d.ts +16 -0
- package/dist/i18n-extractor.js +45 -0
- package/dist/i18n-extractor.js.map +1 -0
- package/dist/index.cjs +1187 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1178 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.cjs +4 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +70 -0
- package/dist/plugin.d.ts +70 -0
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +26 -6
- package/src/compile.test.ts +32 -0
- package/src/compile.ts +81 -6
package/src/compile.ts
CHANGED
|
@@ -33,6 +33,10 @@ interface CompileState {
|
|
|
33
33
|
templates: Map<string, ts.Identifier>
|
|
34
34
|
/** 是否为组件调用生成源码位置(生产构建传 false 剔除,减小产物体积)。 */
|
|
35
35
|
sourceLocation: boolean
|
|
36
|
+
/** 从 @vobs/reactivity / @vobs/vobs 导入的 `state` 别名(含 as 别名),用于 debugName 自动推断。 */
|
|
37
|
+
stateAliases: ReadonlySet<string>
|
|
38
|
+
/** 非 import 的本地声明绑定名:`state` 被本地声明遮蔽时禁用 debugName 推断。 */
|
|
39
|
+
localBindings: ReadonlySet<string>
|
|
36
40
|
/** 编译器自身产出的诊断(如不支持的 JSX 形态),与 TypeScript 解析诊断合并返回。 */
|
|
37
41
|
diagnostics: CompilerDiagnostic[]
|
|
38
42
|
}
|
|
@@ -95,6 +99,8 @@ export function compileWithSourceMap(code: string, options: CompileOptions = {})
|
|
|
95
99
|
helperAliases: new Map(),
|
|
96
100
|
templates: new Map(),
|
|
97
101
|
sourceLocation: options.sourceLocation ?? true,
|
|
102
|
+
stateAliases: collectStateAliases(sourceFile),
|
|
103
|
+
localBindings: collectLocallyDeclaredNames(sourceFile),
|
|
98
104
|
diagnostics: []
|
|
99
105
|
}
|
|
100
106
|
const cleanFilename = filename.split(/[?#]/u, 1)[0] || filename
|
|
@@ -126,9 +132,12 @@ export function compileWithSourceMap(code: string, options: CompileOptions = {})
|
|
|
126
132
|
const statements = sourceFile.statements.map(statement =>
|
|
127
133
|
ts.isImportDeclaration(statement) ? rebuildImport(state, statement) : transformStatement(state, statement)
|
|
128
134
|
)
|
|
135
|
+
// 模板声明必须先于 runtime import 生成:声明里的 createTemplate 依赖
|
|
136
|
+
// helperRef 注册别名,import 需要在别名全部就绪后再构建。
|
|
137
|
+
const templateDeclarations = createTemplateDeclarations(state)
|
|
129
138
|
const resultFile = ts.factory.updateSourceFile(sourceFile, [
|
|
130
139
|
...createRuntimeImports(state),
|
|
131
|
-
...
|
|
140
|
+
...templateDeclarations,
|
|
132
141
|
...statements
|
|
133
142
|
])
|
|
134
143
|
|
|
@@ -384,6 +393,36 @@ function collectDeclaredNames(sourceFile: ts.SourceFile): Set<string> {
|
|
|
384
393
|
return names
|
|
385
394
|
}
|
|
386
395
|
|
|
396
|
+
/** 收集从 @vobs/reactivity / @vobs/vobs 导入的 `state` 绑定名(含 `as` 别名)。 */
|
|
397
|
+
function collectStateAliases(sourceFile: ts.SourceFile): Set<string> {
|
|
398
|
+
const aliases = new Set<string>()
|
|
399
|
+
for (const statement of sourceFile.statements) {
|
|
400
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue
|
|
401
|
+
const module = statement.moduleSpecifier.text
|
|
402
|
+
if (module !== '@vobs/reactivity' && module !== '@vobs/vobs') continue
|
|
403
|
+
const clause = statement.importClause
|
|
404
|
+
if (!clause?.namedBindings || !ts.isNamedImports(clause.namedBindings)) continue
|
|
405
|
+
for (const element of clause.namedBindings.elements) {
|
|
406
|
+
if (element.propertyName ? element.propertyName.text === 'state' : element.name.text === 'state') {
|
|
407
|
+
aliases.add(element.name.text)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return aliases
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** 与 collectDeclaredNames 相同,但跳过 import 声明:用于判断 helper 名是否被本地声明遮蔽。 */
|
|
415
|
+
function collectLocallyDeclaredNames(sourceFile: ts.SourceFile): Set<string> {
|
|
416
|
+
const names = new Set<string>()
|
|
417
|
+
const visit = (node: ts.Node): void => {
|
|
418
|
+
if (ts.isImportDeclaration(node)) return
|
|
419
|
+
if (ts.isIdentifier(node) && isBindingName(node)) names.add(node.text)
|
|
420
|
+
ts.forEachChild(node, visit)
|
|
421
|
+
}
|
|
422
|
+
visit(sourceFile)
|
|
423
|
+
return names
|
|
424
|
+
}
|
|
425
|
+
|
|
387
426
|
/** 标识符是否为某个声明的绑定名(import、变量、函数、参数、类成员等)。 */
|
|
388
427
|
function isBindingName(node: ts.Identifier): boolean {
|
|
389
428
|
const parent = node.parent
|
|
@@ -479,22 +518,28 @@ function transformStatement(state: CompileState, node: ts.Statement): ts.Stateme
|
|
|
479
518
|
}
|
|
480
519
|
|
|
481
520
|
function transformVariableStatement(state: CompileState, node: ts.VariableStatement): ts.VariableStatement {
|
|
521
|
+
let changed = false
|
|
482
522
|
const declarations = node.declarationList.declarations.map(declaration => {
|
|
483
523
|
const initializer = declaration.initializer
|
|
484
|
-
if (!initializer
|
|
524
|
+
if (!initializer) return declaration
|
|
525
|
+
|
|
526
|
+
let nextInitializer = inferStateDebugName(state, declaration, initializer) ?? initializer
|
|
527
|
+
if (containsJsx(nextInitializer)) {
|
|
528
|
+
nextInitializer = transformEmbeddedExpression(state, nextInitializer)
|
|
529
|
+
}
|
|
530
|
+
if (nextInitializer === initializer) return declaration
|
|
485
531
|
|
|
532
|
+
changed = true
|
|
486
533
|
return ts.factory.updateVariableDeclaration(
|
|
487
534
|
declaration,
|
|
488
535
|
declaration.name,
|
|
489
536
|
declaration.exclamationToken,
|
|
490
537
|
declaration.type,
|
|
491
|
-
|
|
538
|
+
nextInitializer
|
|
492
539
|
)
|
|
493
540
|
})
|
|
494
541
|
|
|
495
|
-
if (
|
|
496
|
-
return node
|
|
497
|
-
}
|
|
542
|
+
if (!changed) return node
|
|
498
543
|
|
|
499
544
|
return tagStatement(state, ts.factory.updateVariableStatement(
|
|
500
545
|
node,
|
|
@@ -503,6 +548,36 @@ function transformVariableStatement(state: CompileState, node: ts.VariableStatem
|
|
|
503
548
|
), node)
|
|
504
549
|
}
|
|
505
550
|
|
|
551
|
+
/**
|
|
552
|
+
* `const name = state(initial)` 在未显式传入 debugName 时从变量名推断:
|
|
553
|
+
* `const username = state('')` → `state('', 'username')`,使 DevTools 信号名称与源码命名一致。
|
|
554
|
+
* 仅当 `state` 确认来自 @vobs/reactivity / @vobs/vobs、未被本地声明遮蔽、
|
|
555
|
+
* 且调用只带一个参数时启用;其余形态保持原样。
|
|
556
|
+
*/
|
|
557
|
+
function inferStateDebugName(
|
|
558
|
+
state: CompileState,
|
|
559
|
+
declaration: ts.VariableDeclaration,
|
|
560
|
+
initializer: ts.Expression
|
|
561
|
+
): ts.Expression | undefined {
|
|
562
|
+
if (state.stateAliases.size === 0) return undefined
|
|
563
|
+
if (!ts.isIdentifier(declaration.name)) return undefined
|
|
564
|
+
|
|
565
|
+
let call = initializer
|
|
566
|
+
while (ts.isParenthesizedExpression(call) || ts.isAsExpression(call) || ts.isTypeAssertionExpression(call) || ts.isSatisfiesExpression(call)) {
|
|
567
|
+
call = call.expression
|
|
568
|
+
}
|
|
569
|
+
if (!ts.isCallExpression(call)) return undefined
|
|
570
|
+
const callee = call.expression
|
|
571
|
+
if (!ts.isIdentifier(callee) || !state.stateAliases.has(callee.text)) return undefined
|
|
572
|
+
if (state.localBindings.has(callee.text)) return undefined
|
|
573
|
+
if (call.arguments.length !== 1) return undefined
|
|
574
|
+
|
|
575
|
+
return ts.factory.createCallExpression(callee, call.typeArguments, [
|
|
576
|
+
...call.arguments,
|
|
577
|
+
ts.factory.createStringLiteral(declaration.name.text)
|
|
578
|
+
])
|
|
579
|
+
}
|
|
580
|
+
|
|
506
581
|
function containsJsx(expression: ts.Expression): boolean {
|
|
507
582
|
let found = false
|
|
508
583
|
const visit = (node: ts.Node): void => {
|