@llui/vite-plugin 0.0.28 → 0.0.30
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 +6 -0
- package/dist/binding-descriptors.d.ts +23 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +82 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/compiler-cache.d.ts +20 -0
- package/dist/compiler-cache.d.ts.map +1 -0
- package/dist/compiler-cache.js +20 -0
- package/dist/compiler-cache.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -2
- package/dist/index.js.map +1 -1
- package/dist/msg-annotations.d.ts +23 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +91 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/schema-hash.d.ts +16 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +31 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/transform.d.ts +29 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +210 -9
- package/dist/transform.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
const DEFAULT = {
|
|
3
|
+
intent: null,
|
|
4
|
+
alwaysAffordable: false,
|
|
5
|
+
requiresConfirm: false,
|
|
6
|
+
humanOnly: false,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Walk a Msg-like discriminated-union type alias and extract JSDoc
|
|
10
|
+
* annotations attached to each union member. Returns null if no
|
|
11
|
+
* recognizable union is found so callers can skip emission cleanly.
|
|
12
|
+
*
|
|
13
|
+
* Expected JSDoc grammar (order-independent):
|
|
14
|
+
* @intent("human readable")
|
|
15
|
+
* @alwaysAffordable
|
|
16
|
+
* @requiresConfirm
|
|
17
|
+
* @humanOnly
|
|
18
|
+
*
|
|
19
|
+
* Unknown tags are ignored; malformed @intent (no quoted string) is
|
|
20
|
+
* treated as "no intent". The four flags are booleans; any occurrence
|
|
21
|
+
* of the tag sets it true.
|
|
22
|
+
*/
|
|
23
|
+
export function extractMsgAnnotations(source) {
|
|
24
|
+
const sf = ts.createSourceFile('msg.ts', source, ts.ScriptTarget.Latest, true);
|
|
25
|
+
const aliases = [];
|
|
26
|
+
sf.forEachChild((n) => {
|
|
27
|
+
if (ts.isTypeAliasDeclaration(n))
|
|
28
|
+
aliases.push(n);
|
|
29
|
+
});
|
|
30
|
+
const named = aliases.find((a) => a.name.text === 'Msg');
|
|
31
|
+
const alias = named ?? aliases.find((a) => ts.isUnionTypeNode(a.type));
|
|
32
|
+
if (!alias || !ts.isUnionTypeNode(alias.type))
|
|
33
|
+
return null;
|
|
34
|
+
const result = {};
|
|
35
|
+
const types = alias.type.types;
|
|
36
|
+
for (let i = 0; i < types.length; i++) {
|
|
37
|
+
const member = types[i];
|
|
38
|
+
if (member === undefined || !ts.isTypeLiteralNode(member))
|
|
39
|
+
continue;
|
|
40
|
+
const variant = readDiscriminantLiteral(member);
|
|
41
|
+
if (!variant)
|
|
42
|
+
continue;
|
|
43
|
+
// Leading JSDoc for union member i is scanned from the end of the
|
|
44
|
+
// previous element (or union.pos for the first member), because
|
|
45
|
+
// TypeScript's parser places comment ranges relative to the token
|
|
46
|
+
// that follows them — and the | bar is not part of the TypeLiteralNode.
|
|
47
|
+
const prev = types[i - 1];
|
|
48
|
+
const scanPos = i === 0 || prev === undefined ? alias.type.pos : prev.end;
|
|
49
|
+
const comment = readLeadingJSDoc(source, scanPos);
|
|
50
|
+
result[variant] = parseAnnotations(comment);
|
|
51
|
+
}
|
|
52
|
+
return Object.keys(result).length === 0 ? null : result;
|
|
53
|
+
}
|
|
54
|
+
function readDiscriminantLiteral(lit) {
|
|
55
|
+
for (const m of lit.members) {
|
|
56
|
+
if (!ts.isPropertySignature(m))
|
|
57
|
+
continue;
|
|
58
|
+
if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type')
|
|
59
|
+
continue;
|
|
60
|
+
if (!m.type || !ts.isLiteralTypeNode(m.type))
|
|
61
|
+
continue;
|
|
62
|
+
const literal = m.type.literal;
|
|
63
|
+
if (ts.isStringLiteral(literal))
|
|
64
|
+
return literal.text;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function readLeadingJSDoc(source, scanPos) {
|
|
69
|
+
const ranges = ts.getLeadingCommentRanges(source, scanPos) ?? [];
|
|
70
|
+
const docs = ranges
|
|
71
|
+
.filter((r) => r.kind === ts.SyntaxKind.MultiLineCommentTrivia)
|
|
72
|
+
.map((r) => source.slice(r.pos, r.end))
|
|
73
|
+
.filter((txt) => txt.startsWith('/**'));
|
|
74
|
+
return docs.join('\n');
|
|
75
|
+
}
|
|
76
|
+
function parseAnnotations(comment) {
|
|
77
|
+
if (!comment)
|
|
78
|
+
return { ...DEFAULT };
|
|
79
|
+
const intent = readIntent(comment);
|
|
80
|
+
return {
|
|
81
|
+
intent,
|
|
82
|
+
alwaysAffordable: /@alwaysAffordable\b/.test(comment),
|
|
83
|
+
requiresConfirm: /@requiresConfirm\b/.test(comment),
|
|
84
|
+
humanOnly: /@humanOnly\b/.test(comment),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function readIntent(comment) {
|
|
88
|
+
const match = comment.match(/@intent\s*\(\s*["\u201c]([^"\u201d]*)["\u201d]\s*\)/);
|
|
89
|
+
return match?.[1] ?? null;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=msg-annotations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.js","sourceRoot":"","sources":["../src/msg-annotations.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript'\n\nexport type MessageAnnotations = {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n humanOnly: boolean\n}\n\nconst DEFAULT: MessageAnnotations = {\n intent: null,\n alwaysAffordable: false,\n requiresConfirm: false,\n humanOnly: false,\n}\n\n/**\n * Walk a Msg-like discriminated-union type alias and extract JSDoc\n * annotations attached to each union member. Returns null if no\n * recognizable union is found so callers can skip emission cleanly.\n *\n * Expected JSDoc grammar (order-independent):\n * @intent(\"human readable\")\n * @alwaysAffordable\n * @requiresConfirm\n * @humanOnly\n *\n * Unknown tags are ignored; malformed @intent (no quoted string) is\n * treated as \"no intent\". The four flags are booleans; any occurrence\n * of the tag sets it true.\n */\nexport function extractMsgAnnotations(source: string): Record<string, MessageAnnotations> | null {\n const sf = ts.createSourceFile('msg.ts', source, ts.ScriptTarget.Latest, true)\n const aliases: ts.TypeAliasDeclaration[] = []\n sf.forEachChild((n) => {\n if (ts.isTypeAliasDeclaration(n)) aliases.push(n)\n })\n const named = aliases.find((a) => a.name.text === 'Msg')\n const alias = named ?? aliases.find((a) => ts.isUnionTypeNode(a.type))\n if (!alias || !ts.isUnionTypeNode(alias.type)) return null\n\n const result: Record<string, MessageAnnotations> = {}\n const types = alias.type.types\n for (let i = 0; i < types.length; i++) {\n const member = types[i]\n if (member === undefined || !ts.isTypeLiteralNode(member)) continue\n const variant = readDiscriminantLiteral(member)\n if (!variant) continue\n // Leading JSDoc for union member i is scanned from the end of the\n // previous element (or union.pos for the first member), because\n // TypeScript's parser places comment ranges relative to the token\n // that follows them — and the | bar is not part of the TypeLiteralNode.\n const prev = types[i - 1]\n const scanPos = i === 0 || prev === undefined ? alias.type.pos : prev.end\n const comment = readLeadingJSDoc(source, scanPos)\n result[variant] = parseAnnotations(comment)\n }\n return Object.keys(result).length === 0 ? null : result\n}\n\nfunction readDiscriminantLiteral(lit: ts.TypeLiteralNode): string | null {\n for (const m of lit.members) {\n if (!ts.isPropertySignature(m)) continue\n if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type') continue\n if (!m.type || !ts.isLiteralTypeNode(m.type)) continue\n const literal = m.type.literal\n if (ts.isStringLiteral(literal)) return literal.text\n }\n return null\n}\n\nfunction readLeadingJSDoc(source: string, scanPos: number): string {\n const ranges = ts.getLeadingCommentRanges(source, scanPos) ?? []\n const docs = ranges\n .filter((r) => r.kind === ts.SyntaxKind.MultiLineCommentTrivia)\n .map((r) => source.slice(r.pos, r.end))\n .filter((txt) => txt.startsWith('/**'))\n return docs.join('\\n')\n}\n\nfunction parseAnnotations(comment: string): MessageAnnotations {\n if (!comment) return { ...DEFAULT }\n const intent = readIntent(comment)\n return {\n intent,\n alwaysAffordable: /@alwaysAffordable\\b/.test(comment),\n requiresConfirm: /@requiresConfirm\\b/.test(comment),\n humanOnly: /@humanOnly\\b/.test(comment),\n }\n}\n\nfunction readIntent(comment: string): string | null {\n const match = comment.match(/@intent\\s*\\(\\s*[\"\\u201c]([^\"\\u201d]*)[\"\\u201d]\\s*\\)/)\n return match?.[1] ?? null\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MessageAnnotations } from './msg-annotations.js';
|
|
2
|
+
export type SchemaHashInput = {
|
|
3
|
+
msgSchema: unknown;
|
|
4
|
+
stateSchema: unknown;
|
|
5
|
+
msgAnnotations: Record<string, MessageAnnotations> | null | undefined;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Stable hex SHA-256 (first 32 chars) over a normalized JSON serialization
|
|
9
|
+
* of msgSchema + stateSchema + msgAnnotations. Object key order is
|
|
10
|
+
* normalized so equivalent inputs always produce equal hashes.
|
|
11
|
+
*
|
|
12
|
+
* Used by the runtime to detect when the browser-to-server `hello` frame
|
|
13
|
+
* needs to re-send its schema payload (dev hot-reload).
|
|
14
|
+
*/
|
|
15
|
+
export declare function computeSchemaHash(input: SchemaHashInput): string;
|
|
16
|
+
//# sourceMappingURL=schema-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-hash.d.ts","sourceRoot":"","sources":["../src/schema-hash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;CACtE,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAQhE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Stable hex SHA-256 (first 32 chars) over a normalized JSON serialization
|
|
4
|
+
* of msgSchema + stateSchema + msgAnnotations. Object key order is
|
|
5
|
+
* normalized so equivalent inputs always produce equal hashes.
|
|
6
|
+
*
|
|
7
|
+
* Used by the runtime to detect when the browser-to-server `hello` frame
|
|
8
|
+
* needs to re-send its schema payload (dev hot-reload).
|
|
9
|
+
*/
|
|
10
|
+
export function computeSchemaHash(input) {
|
|
11
|
+
const normalized = {
|
|
12
|
+
msgSchema: sortDeep(input.msgSchema),
|
|
13
|
+
stateSchema: sortDeep(input.stateSchema),
|
|
14
|
+
msgAnnotations: sortDeep(input.msgAnnotations ?? null),
|
|
15
|
+
};
|
|
16
|
+
const json = JSON.stringify(normalized);
|
|
17
|
+
return createHash('sha256').update(json).digest('hex').slice(0, 32);
|
|
18
|
+
}
|
|
19
|
+
function sortDeep(value) {
|
|
20
|
+
if (value === null || typeof value !== 'object')
|
|
21
|
+
return value;
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return value.map(sortDeep);
|
|
24
|
+
const obj = value;
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const k of Object.keys(obj).sort()) {
|
|
27
|
+
out[k] = sortDeep(obj[k]);
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=schema-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-hash.js","sourceRoot":"","sources":["../src/schema-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AASxC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,MAAM,UAAU,GAAG;QACjB,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QACpC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC;QACxC,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC;KACvD,CAAA;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACrE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpD,MAAM,GAAG,GAAG,KAAgC,CAAA;IAC5C,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACxC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import { createHash } from 'node:crypto'\nimport type { MessageAnnotations } from './msg-annotations.js'\n\nexport type SchemaHashInput = {\n msgSchema: unknown\n stateSchema: unknown\n msgAnnotations: Record<string, MessageAnnotations> | null | undefined\n}\n\n/**\n * Stable hex SHA-256 (first 32 chars) over a normalized JSON serialization\n * of msgSchema + stateSchema + msgAnnotations. Object key order is\n * normalized so equivalent inputs always produce equal hashes.\n *\n * Used by the runtime to detect when the browser-to-server `hello` frame\n * needs to re-send its schema payload (dev hot-reload).\n */\nexport function computeSchemaHash(input: SchemaHashInput): string {\n const normalized = {\n msgSchema: sortDeep(input.msgSchema),\n stateSchema: sortDeep(input.stateSchema),\n msgAnnotations: sortDeep(input.msgAnnotations ?? null),\n }\n const json = JSON.stringify(normalized)\n return createHash('sha256').update(json).digest('hex').slice(0, 32)\n}\n\nfunction sortDeep(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(sortDeep)\n const obj = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const k of Object.keys(obj).sort()) {\n out[k] = sortDeep(obj[k])\n }\n return out\n}\n"]}
|
package/dist/transform.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
1
2
|
/**
|
|
2
3
|
* Transform a source file containing @llui/dom imports.
|
|
3
4
|
* Returns the transformed source or null if no transformation needed.
|
|
@@ -7,7 +8,7 @@ export interface TransformEdit {
|
|
|
7
8
|
end: number;
|
|
8
9
|
replacement: string;
|
|
9
10
|
}
|
|
10
|
-
export declare function transformLlui(source: string, _filename: string, devMode?: boolean, mcpPort?: number | null, verbose?: boolean): {
|
|
11
|
+
export declare function transformLlui(source: string, _filename: string, devMode?: boolean, emitAgentMetadata?: boolean, mcpPort?: number | null, verbose?: boolean): {
|
|
11
12
|
output: string;
|
|
12
13
|
edits: TransformEdit[];
|
|
13
14
|
} | null;
|
|
@@ -47,4 +48,31 @@ export declare function transformUseClientSsr(source: string, _filename: string)
|
|
|
47
48
|
* without parsing the whole file twice.
|
|
48
49
|
*/
|
|
49
50
|
export declare function hasUseClientDirective(source: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Extract the view function body (the value of the `view:` property) from
|
|
53
|
+
* a component() config object literal. Uses a regex heuristic — good enough
|
|
54
|
+
* for round-tripping source for dev/agent tools.
|
|
55
|
+
*/
|
|
56
|
+
export declare function extractViewBody(code: string): string | null;
|
|
57
|
+
/**
|
|
58
|
+
* Extract the component `name:` string literal from a component() call's
|
|
59
|
+
* first argument object literal in the source text.
|
|
60
|
+
*/
|
|
61
|
+
export declare function extractComponentNameFromConfig(node: ts.CallExpression): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Generate Object.defineProperty calls for __preSource, __postSource,
|
|
64
|
+
* __msgMaskMap, and __bindingSources on a component variable. These are
|
|
65
|
+
* non-enumerable so they don't appear in JSON.stringify(componentDef) but are
|
|
66
|
+
* visible to devtools.
|
|
67
|
+
*/
|
|
68
|
+
export declare function generateCompilerCacheProps(varName: string, componentName: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* After the full output string is assembled, update each cached component's
|
|
71
|
+
* postSource (extract view body from the transformed output), then append
|
|
72
|
+
* Object.defineProperty calls for all four compiler-cache properties.
|
|
73
|
+
*/
|
|
74
|
+
export declare function appendCompilerCacheProps(output: string, componentDecls: Array<{
|
|
75
|
+
varName: string;
|
|
76
|
+
componentName: string;
|
|
77
|
+
}>): string;
|
|
50
78
|
//# sourceMappingURL=transform.d.ts.map
|
package/dist/transform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAiL3B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,UAAQ,EACf,iBAAiB,UAAQ,EACzB,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,OAAO,UAAQ,GACd;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,GAAG,IAAI,CA8TnD;AAwoID,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,wBAAwB,GAAG,IAAI,CAoGjC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA4B7D;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,GAAG,IAAI,CAcrF;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CASzF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,GAChE,MAAM,CAYR"}
|
package/dist/transform.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { collectDeps } from './collect-deps.js';
|
|
3
3
|
import { extractMsgSchema, extractEffectSchema } from './msg-schema.js';
|
|
4
|
+
import { extractMsgAnnotations } from './msg-annotations.js';
|
|
4
5
|
import { extractStateSchema } from './state-schema.js';
|
|
6
|
+
import { computeSchemaHash } from './schema-hash.js';
|
|
7
|
+
import { extractBindingDescriptors } from './binding-descriptors.js';
|
|
8
|
+
import { compilerCache } from './compiler-cache.js';
|
|
5
9
|
function createMaskLiteral(f, mask) {
|
|
6
10
|
if (mask >= 0)
|
|
7
11
|
return f.createNumericLiteral(mask);
|
|
@@ -172,7 +176,7 @@ function resolveKey(key, kind) {
|
|
|
172
176
|
return 'class';
|
|
173
177
|
return key;
|
|
174
178
|
}
|
|
175
|
-
export function transformLlui(source, _filename, devMode = false, mcpPort = 5200, verbose = false) {
|
|
179
|
+
export function transformLlui(source, _filename, devMode = false, emitAgentMetadata = false, mcpPort = 5200, verbose = false) {
|
|
176
180
|
const sourceFile = ts.createSourceFile('input.ts', source, ts.ScriptTarget.Latest, true);
|
|
177
181
|
// Find the @llui/dom import
|
|
178
182
|
const imp = findLluiImport(sourceFile);
|
|
@@ -296,12 +300,20 @@ export function transformLlui(source, _filename, devMode = false, mcpPort = 5200
|
|
|
296
300
|
let result = tryInjectDirty(node, fieldBits, f);
|
|
297
301
|
if (result)
|
|
298
302
|
usesApplyBinding = true;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
+
// Extract schema data once — used both for devMode injections and the
|
|
304
|
+
// unconditional __schemaHash (spec §7.4: hash ships in prod too).
|
|
305
|
+
const msgSchema = extractMsgSchema(source);
|
|
306
|
+
const msgAnnotations = extractMsgAnnotations(source);
|
|
307
|
+
const stateSchema = extractStateSchema(source);
|
|
308
|
+
const bindingDescriptors = extractBindingDescriptors(source);
|
|
309
|
+
const shouldEmitAgentMetadata = devMode || emitAgentMetadata;
|
|
310
|
+
if (shouldEmitAgentMetadata) {
|
|
311
|
+
if (msgSchema) {
|
|
312
|
+
result = injectMsgSchema(result ?? node, msgSchema, f);
|
|
313
|
+
}
|
|
314
|
+
if (msgAnnotations && hasNonDefaultAnnotation(msgAnnotations)) {
|
|
315
|
+
result = injectMsgAnnotations(result ?? node, msgAnnotations, f);
|
|
303
316
|
}
|
|
304
|
-
const stateSchema = extractStateSchema(source);
|
|
305
317
|
if (stateSchema) {
|
|
306
318
|
result = injectStateSchema(result ?? node, stateSchema.fields, f);
|
|
307
319
|
}
|
|
@@ -309,8 +321,37 @@ export function transformLlui(source, _filename, devMode = false, mcpPort = 5200
|
|
|
309
321
|
if (effectSchema) {
|
|
310
322
|
result = injectEffectSchema(result ?? node, effectSchema, f);
|
|
311
323
|
}
|
|
324
|
+
if (bindingDescriptors.length > 0) {
|
|
325
|
+
result = injectBindingDescriptors(result ?? node, bindingDescriptors, f);
|
|
326
|
+
}
|
|
327
|
+
// Populate compiler cache — preSource and msgMaskMap are known now;
|
|
328
|
+
// postSource is filled in after the full output is assembled.
|
|
329
|
+
const cachedComponentName = extractComponentNameFromConfig(node);
|
|
330
|
+
if (cachedComponentName) {
|
|
331
|
+
const preSource = extractViewBody(source) ?? '';
|
|
332
|
+
const msgMaskMap = {};
|
|
333
|
+
for (const [path, bit] of fieldBits) {
|
|
334
|
+
msgMaskMap[path] = bit;
|
|
335
|
+
}
|
|
336
|
+
compilerCache.set(cachedComponentName, {
|
|
337
|
+
preSource,
|
|
338
|
+
postSource: '',
|
|
339
|
+
msgMaskMap,
|
|
340
|
+
bindingSources: [],
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (devMode) {
|
|
312
345
|
result = injectComponentMeta(result ?? node, node, sourceFile, _filename, f);
|
|
313
346
|
}
|
|
347
|
+
// __schemaHash is always emitted (not dev-gated) — runtime uses it to
|
|
348
|
+
// gate hello-frame re-sends during hot-reload in prod builds too.
|
|
349
|
+
const schemaHash = computeSchemaHash({
|
|
350
|
+
msgSchema: msgSchema ?? null,
|
|
351
|
+
stateSchema: stateSchema ?? null,
|
|
352
|
+
msgAnnotations,
|
|
353
|
+
});
|
|
354
|
+
result = injectSchemaHash(result ?? node, schemaHash, f);
|
|
314
355
|
if (result) {
|
|
315
356
|
if (hasPos)
|
|
316
357
|
edits.push({ start: origStart, end: origEnd, replacement: '' });
|
|
@@ -326,8 +367,8 @@ export function transformLlui(source, _filename, devMode = false, mcpPort = 5200
|
|
|
326
367
|
transformed = cleanupImports(transformed, lluiImport, importedHelpers, safeToRemove, usesElSplit, usesElTemplate, usesMemo, usesApplyBinding, usesCloneStaticTemplate, f);
|
|
327
368
|
if (edits.length === 0)
|
|
328
369
|
return null;
|
|
329
|
-
// Find component declarations for HMR
|
|
330
|
-
const componentDecls = devMode ? findComponentDeclarations(sourceFile, lluiImport) : [];
|
|
370
|
+
// Find component declarations for HMR and agent metadata
|
|
371
|
+
const componentDecls = devMode || emitAgentMetadata ? findComponentDeclarations(sourceFile, lluiImport) : [];
|
|
331
372
|
// Build per-statement edits by comparing original vs transformed.
|
|
332
373
|
// Only emit edits for statements that actually changed.
|
|
333
374
|
// Untouched code keeps its original positions → accurate source maps.
|
|
@@ -347,7 +388,10 @@ export function transformLlui(source, _filename, devMode = false, mcpPort = 5200
|
|
|
347
388
|
const { top: _top, bottom: _bottom } = devMode
|
|
348
389
|
? generateDevCode(componentDecls, mcpPort)
|
|
349
390
|
: { top: '', bottom: '' };
|
|
350
|
-
|
|
391
|
+
let output = (_top ? _top + '\n' : '') + printer.printFile(transformed) + (_bottom ? '\n' + _bottom : '');
|
|
392
|
+
if (devMode || emitAgentMetadata) {
|
|
393
|
+
output = appendCompilerCacheProps(output, componentDecls);
|
|
394
|
+
}
|
|
351
395
|
return { output, edits: [{ start: 0, end: source.length, replacement: output }] };
|
|
352
396
|
}
|
|
353
397
|
// Compare ignoring trailing semicolons and whitespace (printer adds them)
|
|
@@ -379,6 +423,13 @@ export function transformLlui(source, _filename, devMode = false, mcpPort = 5200
|
|
|
379
423
|
for (const edit of sorted) {
|
|
380
424
|
output = output.slice(0, edit.start) + edit.replacement + output.slice(edit.end);
|
|
381
425
|
}
|
|
426
|
+
// After output is assembled, update postSource in cache and emit non-enumerable props
|
|
427
|
+
if ((devMode || emitAgentMetadata) && componentDecls.length > 0) {
|
|
428
|
+
const cacheProps = appendCompilerCacheProps(output, componentDecls);
|
|
429
|
+
if (cacheProps !== output) {
|
|
430
|
+
output = cacheProps;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
382
433
|
return { output, edits: finalEdits };
|
|
383
434
|
}
|
|
384
435
|
// ── HMR ──────────────────────────────────────────────────────────
|
|
@@ -2124,6 +2175,71 @@ function injectMsgSchema(node, schema, f) {
|
|
|
2124
2175
|
...node.arguments.slice(1),
|
|
2125
2176
|
]);
|
|
2126
2177
|
}
|
|
2178
|
+
function hasNonDefaultAnnotation(a) {
|
|
2179
|
+
for (const v of Object.values(a)) {
|
|
2180
|
+
if (v.intent !== null)
|
|
2181
|
+
return true;
|
|
2182
|
+
if (v.alwaysAffordable)
|
|
2183
|
+
return true;
|
|
2184
|
+
if (v.requiresConfirm)
|
|
2185
|
+
return true;
|
|
2186
|
+
if (v.humanOnly)
|
|
2187
|
+
return true;
|
|
2188
|
+
}
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2191
|
+
function annotationsToObjectLiteral(a) {
|
|
2192
|
+
const props = [];
|
|
2193
|
+
for (const [variant, ann] of Object.entries(a)) {
|
|
2194
|
+
props.push(ts.factory.createPropertyAssignment(variant, ts.factory.createObjectLiteralExpression([
|
|
2195
|
+
ts.factory.createPropertyAssignment('intent', ann.intent === null
|
|
2196
|
+
? ts.factory.createNull()
|
|
2197
|
+
: ts.factory.createStringLiteral(ann.intent)),
|
|
2198
|
+
ts.factory.createPropertyAssignment('alwaysAffordable', ann.alwaysAffordable ? ts.factory.createTrue() : ts.factory.createFalse()),
|
|
2199
|
+
ts.factory.createPropertyAssignment('requiresConfirm', ann.requiresConfirm ? ts.factory.createTrue() : ts.factory.createFalse()),
|
|
2200
|
+
ts.factory.createPropertyAssignment('humanOnly', ann.humanOnly ? ts.factory.createTrue() : ts.factory.createFalse()),
|
|
2201
|
+
], true)));
|
|
2202
|
+
}
|
|
2203
|
+
return ts.factory.createObjectLiteralExpression(props, true);
|
|
2204
|
+
}
|
|
2205
|
+
function injectMsgAnnotations(node, annotations, f) {
|
|
2206
|
+
const configArg = node.arguments[0];
|
|
2207
|
+
if (!configArg || !ts.isObjectLiteralExpression(configArg))
|
|
2208
|
+
return node;
|
|
2209
|
+
// Don't inject if already present
|
|
2210
|
+
for (const prop of configArg.properties) {
|
|
2211
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
2212
|
+
ts.isIdentifier(prop.name) &&
|
|
2213
|
+
prop.name.text === '__msgAnnotations') {
|
|
2214
|
+
return node;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
const annotationsProp = f.createPropertyAssignment('__msgAnnotations', annotationsToObjectLiteral(annotations));
|
|
2218
|
+
const newConfig = f.createObjectLiteralExpression([...configArg.properties, annotationsProp], true);
|
|
2219
|
+
return f.createCallExpression(node.expression, node.typeArguments, [
|
|
2220
|
+
newConfig,
|
|
2221
|
+
...node.arguments.slice(1),
|
|
2222
|
+
]);
|
|
2223
|
+
}
|
|
2224
|
+
function injectSchemaHash(node, hash, f) {
|
|
2225
|
+
const configArg = node.arguments[0];
|
|
2226
|
+
if (!configArg || !ts.isObjectLiteralExpression(configArg))
|
|
2227
|
+
return node;
|
|
2228
|
+
// Don't inject if already present
|
|
2229
|
+
for (const prop of configArg.properties) {
|
|
2230
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
2231
|
+
ts.isIdentifier(prop.name) &&
|
|
2232
|
+
prop.name.text === '__schemaHash') {
|
|
2233
|
+
return node;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
const hashProp = f.createPropertyAssignment('__schemaHash', f.createStringLiteral(hash));
|
|
2237
|
+
const newConfig = f.createObjectLiteralExpression([...configArg.properties, hashProp], true);
|
|
2238
|
+
return f.createCallExpression(node.expression, node.typeArguments, [
|
|
2239
|
+
newConfig,
|
|
2240
|
+
...node.arguments.slice(1),
|
|
2241
|
+
]);
|
|
2242
|
+
}
|
|
2127
2243
|
function injectEffectSchema(node, schema, f) {
|
|
2128
2244
|
const configArg = node.arguments[0];
|
|
2129
2245
|
if (!configArg || !ts.isObjectLiteralExpression(configArg))
|
|
@@ -2161,6 +2277,29 @@ function injectEffectSchema(node, schema, f) {
|
|
|
2161
2277
|
...node.arguments.slice(1),
|
|
2162
2278
|
]);
|
|
2163
2279
|
}
|
|
2280
|
+
function injectBindingDescriptors(node, descs, f) {
|
|
2281
|
+
const configArg = node.arguments[0];
|
|
2282
|
+
if (!configArg || !ts.isObjectLiteralExpression(configArg))
|
|
2283
|
+
return node;
|
|
2284
|
+
// Don't inject if already present
|
|
2285
|
+
for (const prop of configArg.properties) {
|
|
2286
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
2287
|
+
ts.isIdentifier(prop.name) &&
|
|
2288
|
+
prop.name.text === '__bindingDescriptors') {
|
|
2289
|
+
return node;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
const descsProp = f.createPropertyAssignment('__bindingDescriptors', bindingDescriptorsToArrayLiteral(descs));
|
|
2293
|
+
const newConfig = f.createObjectLiteralExpression([...configArg.properties, descsProp], true);
|
|
2294
|
+
return f.createCallExpression(node.expression, node.typeArguments, [
|
|
2295
|
+
newConfig,
|
|
2296
|
+
...node.arguments.slice(1),
|
|
2297
|
+
]);
|
|
2298
|
+
}
|
|
2299
|
+
function bindingDescriptorsToArrayLiteral(descs) {
|
|
2300
|
+
const entries = descs.map((d) => ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment('variant', ts.factory.createStringLiteral(d.variant))], false));
|
|
2301
|
+
return ts.factory.createArrayLiteralExpression(entries, true);
|
|
2302
|
+
}
|
|
2164
2303
|
// ── Per-item accessor detection ──────────────────────────────────
|
|
2165
2304
|
// ── Item selector deduplication ──────────────────────────────────
|
|
2166
2305
|
/**
|
|
@@ -3293,4 +3432,66 @@ export function hasUseClientDirective(source) {
|
|
|
3293
3432
|
}
|
|
3294
3433
|
return source.startsWith("'use client'", i) || source.startsWith('"use client"', i);
|
|
3295
3434
|
}
|
|
3435
|
+
// ── Compiler cache helpers ────────────────────────────────────────
|
|
3436
|
+
/**
|
|
3437
|
+
* Extract the view function body (the value of the `view:` property) from
|
|
3438
|
+
* a component() config object literal. Uses a regex heuristic — good enough
|
|
3439
|
+
* for round-tripping source for dev/agent tools.
|
|
3440
|
+
*/
|
|
3441
|
+
export function extractViewBody(code) {
|
|
3442
|
+
const match = /\bview\s*:\s*([\s\S]*?)(?=,\s*(?:onEffect|update|init|name|onMsg)\s*:|}\s*\))/m.exec(code);
|
|
3443
|
+
return match?.[1]?.trim() ?? null;
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Extract the component `name:` string literal from a component() call's
|
|
3447
|
+
* first argument object literal in the source text.
|
|
3448
|
+
*/
|
|
3449
|
+
export function extractComponentNameFromConfig(node) {
|
|
3450
|
+
const configArg = node.arguments[0];
|
|
3451
|
+
if (!configArg || !ts.isObjectLiteralExpression(configArg))
|
|
3452
|
+
return null;
|
|
3453
|
+
for (const prop of configArg.properties) {
|
|
3454
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
3455
|
+
ts.isIdentifier(prop.name) &&
|
|
3456
|
+
prop.name.text === 'name' &&
|
|
3457
|
+
ts.isStringLiteral(prop.initializer)) {
|
|
3458
|
+
return prop.initializer.text;
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
return null;
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Generate Object.defineProperty calls for __preSource, __postSource,
|
|
3465
|
+
* __msgMaskMap, and __bindingSources on a component variable. These are
|
|
3466
|
+
* non-enumerable so they don't appear in JSON.stringify(componentDef) but are
|
|
3467
|
+
* visible to devtools.
|
|
3468
|
+
*/
|
|
3469
|
+
export function generateCompilerCacheProps(varName, componentName) {
|
|
3470
|
+
const entry = compilerCache.get(componentName);
|
|
3471
|
+
if (!entry)
|
|
3472
|
+
return '';
|
|
3473
|
+
return (`\nObject.defineProperty(${varName}, '__preSource', { value: ${JSON.stringify(entry.preSource)}, enumerable: false, configurable: true })` +
|
|
3474
|
+
`\nObject.defineProperty(${varName}, '__postSource', { value: ${JSON.stringify(entry.postSource)}, enumerable: false, configurable: true })` +
|
|
3475
|
+
`\nObject.defineProperty(${varName}, '__msgMaskMap', { value: ${JSON.stringify(entry.msgMaskMap)}, enumerable: false, configurable: true })` +
|
|
3476
|
+
`\nObject.defineProperty(${varName}, '__bindingSources', { value: ${JSON.stringify(entry.bindingSources)}, enumerable: false, configurable: true })`);
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* After the full output string is assembled, update each cached component's
|
|
3480
|
+
* postSource (extract view body from the transformed output), then append
|
|
3481
|
+
* Object.defineProperty calls for all four compiler-cache properties.
|
|
3482
|
+
*/
|
|
3483
|
+
export function appendCompilerCacheProps(output, componentDecls) {
|
|
3484
|
+
let result = output;
|
|
3485
|
+
for (const { varName, componentName } of componentDecls) {
|
|
3486
|
+
const existing = compilerCache.get(componentName);
|
|
3487
|
+
if (!existing)
|
|
3488
|
+
continue;
|
|
3489
|
+
// Update the cache entry with the post-transform view body
|
|
3490
|
+
const postSource = extractViewBody(output) ?? '';
|
|
3491
|
+
compilerCache.set(componentName, { ...existing, postSource });
|
|
3492
|
+
// Append non-enumerable property definitions
|
|
3493
|
+
result += generateCompilerCacheProps(varName, componentName);
|
|
3494
|
+
}
|
|
3495
|
+
return result;
|
|
3496
|
+
}
|
|
3296
3497
|
//# sourceMappingURL=transform.js.map
|