@aihu/language-server 0.1.1 → 0.2.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 +11 -9
- package/dist/bin.js +20685 -250
- package/dist/chunk-DcSSxlwY.js +30 -0
- package/dist/core/index.d.ts +1630 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +30 -2
- package/dist/core/index.js.map +1 -0
- package/dist/index-ROvxbbgq.d.ts +22204 -0
- package/dist/index-ROvxbbgq.d.ts.map +1 -0
- package/dist/server.d.ts +60 -9
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19650 -172
- package/dist/server.js.map +1 -1
- package/dist/{core-BHishSOT.js → volar-plugin-cOusBZ0l.js} +952 -7
- package/dist/volar-plugin-cOusBZ0l.js.map +1 -0
- package/package.json +9 -4
- package/dist/core-BHishSOT.js.map +0 -1
|
@@ -1,7 +1,199 @@
|
|
|
1
|
+
import { t as __commonJSMin } from "./chunk-DcSSxlwY.js";
|
|
2
|
+
import { promisify } from "node:util";
|
|
1
3
|
import { execFile } from "node:child_process";
|
|
2
4
|
import { dirname, resolve } from "node:path";
|
|
3
5
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
6
|
+
//#region ../../node_modules/.bun/@volar+source-map@2.4.28/node_modules/@volar/source-map/lib/binarySearch.js
|
|
7
|
+
var require_binarySearch = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.binarySearch = binarySearch;
|
|
10
|
+
function binarySearch(values, searchValue) {
|
|
11
|
+
let low = 0;
|
|
12
|
+
let high = values.length - 1;
|
|
13
|
+
let match;
|
|
14
|
+
while (low <= high) {
|
|
15
|
+
const mid = Math.floor((low + high) / 2);
|
|
16
|
+
const midValue = values[mid];
|
|
17
|
+
if (midValue < searchValue) low = mid + 1;
|
|
18
|
+
else if (midValue > searchValue) high = mid - 1;
|
|
19
|
+
else {
|
|
20
|
+
low = mid;
|
|
21
|
+
high = mid;
|
|
22
|
+
match = mid;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
low: Math.max(Math.min(low, high, values.length - 1), 0),
|
|
28
|
+
high: Math.min(Math.max(low, high, 0), values.length - 1),
|
|
29
|
+
match
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region ../../node_modules/.bun/@volar+source-map@2.4.28/node_modules/@volar/source-map/lib/translateOffset.js
|
|
35
|
+
var require_translateOffset = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.translateOffset = translateOffset;
|
|
38
|
+
let warned = false;
|
|
39
|
+
function translateOffset(start, fromOffsets, toOffsets, fromLengths, toLengths = fromLengths, preferEnd = false) {
|
|
40
|
+
if (!fromOffsets.every((value, index) => index === 0 || fromOffsets[index - 1] <= value)) {
|
|
41
|
+
for (let i = 0; i < fromOffsets.length; i++) {
|
|
42
|
+
const fromOffset = fromOffsets[i];
|
|
43
|
+
const fromLength = fromLengths[i];
|
|
44
|
+
if (start >= fromOffset && start <= fromOffset + fromLength) {
|
|
45
|
+
const toLength = toLengths[i];
|
|
46
|
+
const toOffset = toOffsets[i];
|
|
47
|
+
let rangeOffset;
|
|
48
|
+
const relativePos = start - fromOffset;
|
|
49
|
+
if (preferEnd && toLength > fromLength && relativePos === fromLength) rangeOffset = toLength;
|
|
50
|
+
else rangeOffset = Math.min(relativePos, toLength);
|
|
51
|
+
return toOffset + rangeOffset;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!warned) {
|
|
55
|
+
warned = true;
|
|
56
|
+
console.warn("fromOffsets should be sorted in ascending order");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let low = 0;
|
|
60
|
+
let high = fromOffsets.length - 1;
|
|
61
|
+
while (low <= high) {
|
|
62
|
+
const mid = Math.floor((low + high) / 2);
|
|
63
|
+
const fromOffset = fromOffsets[mid];
|
|
64
|
+
const fromLength = fromLengths[mid];
|
|
65
|
+
if (start >= fromOffset && start <= fromOffset + fromLength) {
|
|
66
|
+
const toLength = toLengths[mid];
|
|
67
|
+
const toOffset = toOffsets[mid];
|
|
68
|
+
let rangeOffset;
|
|
69
|
+
const relativePos = start - fromOffset;
|
|
70
|
+
if (preferEnd && toLength > fromLength && relativePos === fromLength) rangeOffset = toLength;
|
|
71
|
+
else rangeOffset = Math.min(relativePos, toLength);
|
|
72
|
+
return toOffset + rangeOffset;
|
|
73
|
+
} else if (start < fromOffset) high = mid - 1;
|
|
74
|
+
else low = mid + 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region ../../node_modules/.bun/@volar+source-map@2.4.28/node_modules/@volar/source-map/lib/sourceMap.js
|
|
80
|
+
var require_sourceMap = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
81
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
82
|
+
exports.SourceMap = void 0;
|
|
83
|
+
const binarySearch_1 = require_binarySearch();
|
|
84
|
+
const translateOffset_1 = require_translateOffset();
|
|
85
|
+
var SourceMap = class {
|
|
86
|
+
constructor(mappings) {
|
|
87
|
+
this.mappings = mappings;
|
|
88
|
+
}
|
|
89
|
+
toSourceRange(generatedStart, generatedEnd, fallbackToAnyMatch, filter) {
|
|
90
|
+
return this.findMatchingStartEnd(generatedStart, generatedEnd, fallbackToAnyMatch, "generatedOffsets", filter);
|
|
91
|
+
}
|
|
92
|
+
toGeneratedRange(sourceStart, sourceEnd, fallbackToAnyMatch, filter) {
|
|
93
|
+
return this.findMatchingStartEnd(sourceStart, sourceEnd, fallbackToAnyMatch, "sourceOffsets", filter);
|
|
94
|
+
}
|
|
95
|
+
toSourceLocation(generatedOffset, filter) {
|
|
96
|
+
return this.findMatchingOffsets(generatedOffset, "generatedOffsets", filter);
|
|
97
|
+
}
|
|
98
|
+
toGeneratedLocation(sourceOffset, filter) {
|
|
99
|
+
return this.findMatchingOffsets(sourceOffset, "sourceOffsets", filter);
|
|
100
|
+
}
|
|
101
|
+
*findMatchingOffsets(offset, fromRange, filter, preferEnd = false) {
|
|
102
|
+
const memo = this.getMemoBasedOnRange(fromRange);
|
|
103
|
+
if (memo.offsets.length === 0) return;
|
|
104
|
+
const { low: start, high: end } = (0, binarySearch_1.binarySearch)(memo.offsets, offset);
|
|
105
|
+
const skip = /* @__PURE__ */ new Set();
|
|
106
|
+
const toRange = fromRange === "sourceOffsets" ? "generatedOffsets" : "sourceOffsets";
|
|
107
|
+
for (let i = start; i <= end; i++) for (const mapping of memo.mappings[i]) {
|
|
108
|
+
if (skip.has(mapping)) continue;
|
|
109
|
+
skip.add(mapping);
|
|
110
|
+
if (filter && !filter(mapping.data)) continue;
|
|
111
|
+
const mapped = (0, translateOffset_1.translateOffset)(offset, mapping[fromRange], mapping[toRange], getLengths(mapping, fromRange), getLengths(mapping, toRange), preferEnd);
|
|
112
|
+
if (mapped !== void 0) yield [mapped, mapping];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
*findMatchingStartEnd(start, end, fallbackToAnyMatch, fromRange, filter) {
|
|
116
|
+
const toRange = fromRange === "sourceOffsets" ? "generatedOffsets" : "sourceOffsets";
|
|
117
|
+
const preferEnd = toRange === "sourceOffsets";
|
|
118
|
+
const mappedStarts = [];
|
|
119
|
+
let hadMatch = false;
|
|
120
|
+
for (const [mappedStart, mapping] of this.findMatchingOffsets(start, fromRange)) {
|
|
121
|
+
if (filter && !filter(mapping.data)) continue;
|
|
122
|
+
mappedStarts.push([mappedStart, mapping]);
|
|
123
|
+
const mappedEnd = (0, translateOffset_1.translateOffset)(end, mapping[fromRange], mapping[toRange], getLengths(mapping, fromRange), getLengths(mapping, toRange), preferEnd);
|
|
124
|
+
if (mappedEnd !== void 0) {
|
|
125
|
+
hadMatch = true;
|
|
126
|
+
yield [
|
|
127
|
+
mappedStart,
|
|
128
|
+
mappedEnd,
|
|
129
|
+
mapping,
|
|
130
|
+
mapping
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!hadMatch && fallbackToAnyMatch) for (const [mappedStart, mappingStart] of mappedStarts) for (const [mappedEnd, mappingEnd] of this.findMatchingOffsets(end, fromRange, void 0, preferEnd)) {
|
|
135
|
+
if (filter && !filter(mappingEnd.data) || mappedEnd < mappedStart) continue;
|
|
136
|
+
yield [
|
|
137
|
+
mappedStart,
|
|
138
|
+
mappedEnd,
|
|
139
|
+
mappingStart,
|
|
140
|
+
mappingEnd
|
|
141
|
+
];
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
getMemoBasedOnRange(fromRange) {
|
|
146
|
+
return fromRange === "sourceOffsets" ? this.sourceCodeOffsetsMemo ??= this.createMemo("sourceOffsets") : this.generatedCodeOffsetsMemo ??= this.createMemo("generatedOffsets");
|
|
147
|
+
}
|
|
148
|
+
createMemo(key) {
|
|
149
|
+
const offsetsSet = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const mapping of this.mappings) for (let i = 0; i < mapping[key].length; i++) {
|
|
151
|
+
offsetsSet.add(mapping[key][i]);
|
|
152
|
+
offsetsSet.add(mapping[key][i] + getLengths(mapping, key)[i]);
|
|
153
|
+
}
|
|
154
|
+
const offsets = [...offsetsSet].sort((a, b) => a - b);
|
|
155
|
+
const mappings = offsets.map(() => /* @__PURE__ */ new Set());
|
|
156
|
+
for (const mapping of this.mappings) for (let i = 0; i < mapping[key].length; i++) {
|
|
157
|
+
const startIndex = (0, binarySearch_1.binarySearch)(offsets, mapping[key][i]).match;
|
|
158
|
+
const endIndex = (0, binarySearch_1.binarySearch)(offsets, mapping[key][i] + getLengths(mapping, key)[i]).match;
|
|
159
|
+
for (let i = startIndex; i <= endIndex; i++) mappings[i].add(mapping);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
offsets,
|
|
163
|
+
mappings
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
exports.SourceMap = SourceMap;
|
|
168
|
+
function getLengths(mapping, key) {
|
|
169
|
+
return key === "sourceOffsets" ? mapping.lengths : mapping.generatedLengths ?? mapping.lengths;
|
|
170
|
+
}
|
|
171
|
+
}));
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region ../../node_modules/.bun/@volar+source-map@2.4.28/node_modules/@volar/source-map/index.js
|
|
174
|
+
var require_source_map = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
175
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
176
|
+
if (k2 === void 0) k2 = k;
|
|
177
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
178
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = {
|
|
179
|
+
enumerable: true,
|
|
180
|
+
get: function() {
|
|
181
|
+
return m[k];
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
Object.defineProperty(o, k2, desc);
|
|
185
|
+
}) : (function(o, m, k, k2) {
|
|
186
|
+
if (k2 === void 0) k2 = k;
|
|
187
|
+
o[k2] = m[k];
|
|
188
|
+
}));
|
|
189
|
+
var __exportStar = exports && exports.__exportStar || function(m, exports$1) {
|
|
190
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
|
|
191
|
+
};
|
|
192
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
193
|
+
__exportStar(require_sourceMap(), exports);
|
|
194
|
+
__exportStar(require_translateOffset(), exports);
|
|
195
|
+
}));
|
|
196
|
+
//#endregion
|
|
5
197
|
//#region ../compiler/js/codemods/macro-simplification/migrate.ts
|
|
6
198
|
const INDENT = " ";
|
|
7
199
|
function migrate(source) {
|
|
@@ -1752,6 +1944,8 @@ const HOVER_TABLE = {
|
|
|
1752
1944
|
"function name(args) { return batch(() => { body }) }",
|
|
1753
1945
|
"```",
|
|
1754
1946
|
"",
|
|
1947
|
+
"",
|
|
1948
|
+
"> **Agent-side context:** `$action` is primarily for agent/MCP handler scope. In `@state` blocks, prefer `$computed` for derived values or direct function declarations. See macro-vocabulary-v2 §5.",
|
|
1755
1949
|
"[v2 spec](docs/superpowers/specs/2026-05-05-spec-macro-vocabulary-v2.md)"
|
|
1756
1950
|
].join("\n"),
|
|
1757
1951
|
$resource: [
|
|
@@ -1880,7 +2074,352 @@ const HOVER_TABLE = {
|
|
|
1880
2074
|
"",
|
|
1881
2075
|
"Usage: `$emit.name(payload)`",
|
|
1882
2076
|
"",
|
|
1883
|
-
"[v2 spec](docs/superpowers/specs/2026-05-
|
|
2077
|
+
"[Template syntax v2 spec](docs/superpowers/specs/2026-05-06-spec-template-syntax-v2.md)"
|
|
2078
|
+
].join("\n"),
|
|
2079
|
+
$watch: [
|
|
2080
|
+
"**aihu macro: `$watch`**",
|
|
2081
|
+
"",
|
|
2082
|
+
"Imperative subscription to a value with access to prev and next values.",
|
|
2083
|
+
"",
|
|
2084
|
+
"Compiles to:",
|
|
2085
|
+
"```typescript",
|
|
2086
|
+
"let _prev = source",
|
|
2087
|
+
"effect(() => { const _next = source; if (_next !== _prev) { callback(_next, _prev); _prev = _next } })",
|
|
2088
|
+
"```",
|
|
2089
|
+
"",
|
|
2090
|
+
"Does NOT run on mount. Callback runs in an untracked context.",
|
|
2091
|
+
"",
|
|
2092
|
+
"[v1 spec §2.6](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2093
|
+
].join("\n"),
|
|
2094
|
+
$expose: [
|
|
2095
|
+
"**aihu macro: `$expose`**",
|
|
2096
|
+
"",
|
|
2097
|
+
"Marks `@state` declarations as accessible from a parent via template ref",
|
|
2098
|
+
"(equivalent to Vue `defineExpose`). By default `@state` entries are private.",
|
|
2099
|
+
"",
|
|
2100
|
+
"Compiles to:",
|
|
2101
|
+
"```typescript",
|
|
2102
|
+
"defineExpose({ name1, name2, ... })",
|
|
2103
|
+
"```",
|
|
2104
|
+
"",
|
|
2105
|
+
"Usage: `$expose name1, name2` or `$expose { name1, name2 }`",
|
|
2106
|
+
"",
|
|
2107
|
+
"**Deprecated in v2 (C440)** when used INSIDE `@agent`. The agent-side",
|
|
2108
|
+
"`$expose` (and `$expose.write`) are removed; replace with per-name",
|
|
2109
|
+
"`expose: { read: true }` or `expose: { read: true, write: true }` on the",
|
|
2110
|
+
"corresponding `@state` collection entry.",
|
|
2111
|
+
"",
|
|
2112
|
+
"[v1 spec §2.9](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)",
|
|
2113
|
+
"[v2 spec §4 (C440)](docs/superpowers/specs/2026-05-05-spec-macro-vocabulary-v2.md)"
|
|
2114
|
+
].join("\n"),
|
|
2115
|
+
$shared: [
|
|
2116
|
+
"**aihu macro: `$shared`**",
|
|
2117
|
+
"",
|
|
2118
|
+
"SSR-safe global state shared across components; survives hydration.",
|
|
2119
|
+
"",
|
|
2120
|
+
"Compiles to:",
|
|
2121
|
+
"```typescript",
|
|
2122
|
+
"const name = useSharedState(key ?? 'auto-' + componentId + '-' + name, defaultValue)",
|
|
2123
|
+
"```",
|
|
2124
|
+
"",
|
|
2125
|
+
"Usage: `$shared name: type = defaultValue` or `$shared(key) name: type = default`",
|
|
2126
|
+
"",
|
|
2127
|
+
"[v1 spec §2.10](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2128
|
+
].join("\n"),
|
|
2129
|
+
$cookie: [
|
|
2130
|
+
"**aihu macro: `$cookie`**",
|
|
2131
|
+
"",
|
|
2132
|
+
"Reactive binding to a cookie value (SSR-safe).",
|
|
2133
|
+
"",
|
|
2134
|
+
"Compiles to:",
|
|
2135
|
+
"```typescript",
|
|
2136
|
+
"const name = useCookie<type>('name', { defaultValue, ...options })",
|
|
2137
|
+
"```",
|
|
2138
|
+
"",
|
|
2139
|
+
"Usage: `$cookie name: string = \"\"` or `$cookie({ maxAge: 86400 }) sessionId: string = \"\"`",
|
|
2140
|
+
"",
|
|
2141
|
+
"[v1 spec §2.11](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2142
|
+
].join("\n"),
|
|
2143
|
+
$server: [
|
|
2144
|
+
"**aihu macro: `$server`**",
|
|
2145
|
+
"",
|
|
2146
|
+
"Declares a function that runs only on the server; client calls via RPC.",
|
|
2147
|
+
"",
|
|
2148
|
+
"Compiles to:",
|
|
2149
|
+
"```typescript",
|
|
2150
|
+
"// Server: actual implementation in _aihu-server/actions/{component-id}/{name}.ts",
|
|
2151
|
+
"const name = createServerCall<Args, Return>('component-id/name')",
|
|
2152
|
+
"```",
|
|
2153
|
+
"",
|
|
2154
|
+
"Function body is never bundled to the client. Args/return must be serializable.",
|
|
2155
|
+
"",
|
|
2156
|
+
"[v1 spec §2.12](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2157
|
+
].join("\n"),
|
|
2158
|
+
$meta: [
|
|
2159
|
+
"**aihu macro: `$meta`**",
|
|
2160
|
+
"",
|
|
2161
|
+
"Declares page metadata (title, description, OG tags).",
|
|
2162
|
+
"",
|
|
2163
|
+
"Compiles to:",
|
|
2164
|
+
"```typescript",
|
|
2165
|
+
"useHead({ title, description, og })",
|
|
2166
|
+
"// or dynamic: useHead(() => ({ title: dynamicTitle }))",
|
|
2167
|
+
"```",
|
|
2168
|
+
"",
|
|
2169
|
+
"Multiple `$meta` blocks merge (later overrides earlier). SSR-emitted into `<head>`.",
|
|
2170
|
+
"",
|
|
2171
|
+
"[v1 spec §2.13](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2172
|
+
].join("\n"),
|
|
2173
|
+
$key: [
|
|
2174
|
+
"**aihu template directive: `$key`**",
|
|
2175
|
+
"",
|
|
2176
|
+
"Stable identity for list reconciliation inside `$each`.",
|
|
2177
|
+
"",
|
|
2178
|
+
"Compiles to:",
|
|
2179
|
+
"```typescript",
|
|
2180
|
+
"createEachBoundary({ key: (item) => keyExpr, ... })",
|
|
2181
|
+
"```",
|
|
2182
|
+
"",
|
|
2183
|
+
"Usage: `<li $each=\"users as u\" $key=\"u.id\">{u.name}</li>`",
|
|
2184
|
+
"",
|
|
2185
|
+
"[v1 spec §3.6](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2186
|
+
].join("\n"),
|
|
2187
|
+
$raw: [
|
|
2188
|
+
"**aihu template directive: `$raw`**",
|
|
2189
|
+
"",
|
|
2190
|
+
"Skip compilation of the element subtree. Boolean-only (no value).",
|
|
2191
|
+
"",
|
|
2192
|
+
"Interpolations like `{x}`, macros, and bindings inside are NOT processed —",
|
|
2193
|
+
"the subtree renders as static content.",
|
|
2194
|
+
"",
|
|
2195
|
+
"Usage: `<pre $raw>{this looks like interpolation but isn't}</pre>`",
|
|
2196
|
+
"",
|
|
2197
|
+
"[v1 spec §3.8](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2198
|
+
].join("\n"),
|
|
2199
|
+
$once: [
|
|
2200
|
+
"**aihu template directive: `$once`**",
|
|
2201
|
+
"",
|
|
2202
|
+
"Render subtree once at mount; never update. Boolean-only.",
|
|
2203
|
+
"",
|
|
2204
|
+
"Compiles to:",
|
|
2205
|
+
"```typescript",
|
|
2206
|
+
"createOnceBoundary({ build: () => subtree, parent: parentEl })",
|
|
2207
|
+
"```",
|
|
2208
|
+
"",
|
|
2209
|
+
"No reactive subscriptions inside. Subtree never re-renders.",
|
|
2210
|
+
"",
|
|
2211
|
+
"[v1 spec §3.9](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2212
|
+
].join("\n"),
|
|
2213
|
+
$memo: [
|
|
2214
|
+
"**aihu template directive: `$memo`**",
|
|
2215
|
+
"",
|
|
2216
|
+
"Memoize an element subtree by explicit dependencies (curly form only).",
|
|
2217
|
+
"",
|
|
2218
|
+
"Compiles to:",
|
|
2219
|
+
"```typescript",
|
|
2220
|
+
"createMemoBoundary({ deps: () => [dep1, dep2], build: () => subtree })",
|
|
2221
|
+
"```",
|
|
2222
|
+
"",
|
|
2223
|
+
"Usage: `<ExpensiveChart $memo={[data, settings]} />`",
|
|
2224
|
+
"",
|
|
2225
|
+
"[v1 spec §3.10](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2226
|
+
].join("\n"),
|
|
2227
|
+
"<$slot>": [
|
|
2228
|
+
"**aihu template element: `<$slot>`**",
|
|
2229
|
+
"",
|
|
2230
|
+
"Children passthrough or named slot.",
|
|
2231
|
+
"",
|
|
2232
|
+
"Compiles to:",
|
|
2233
|
+
"```typescript",
|
|
2234
|
+
"const slotN = useSlot('name', defaultContent)",
|
|
2235
|
+
"slotN.render({ exposed: { user, index } })",
|
|
2236
|
+
"```",
|
|
2237
|
+
"",
|
|
2238
|
+
"Usage: `<$slot />`, `<$slot name=\"header\">default</$slot>`,",
|
|
2239
|
+
"`<$slot name=\"row\" expose=\"user, index\" />`",
|
|
2240
|
+
"",
|
|
2241
|
+
"[v1 spec §3.12](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2242
|
+
].join("\n"),
|
|
2243
|
+
"<$suspense>": [
|
|
2244
|
+
"**aihu template element: `<$suspense>`**",
|
|
2245
|
+
"",
|
|
2246
|
+
"Loading boundary for async data (`$resource`).",
|
|
2247
|
+
"",
|
|
2248
|
+
"Compiles to:",
|
|
2249
|
+
"```typescript",
|
|
2250
|
+
"createSuspenseBoundary({ fallback, fallbackProps, build, parent })",
|
|
2251
|
+
"```",
|
|
2252
|
+
"",
|
|
2253
|
+
"While any tracked resource is loading, renders `fallback`. Once all ready,",
|
|
2254
|
+
"swaps to actual content. Streaming SSR-compatible.",
|
|
2255
|
+
"",
|
|
2256
|
+
"[v1 spec §3.13](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2257
|
+
].join("\n"),
|
|
2258
|
+
"<$shield>": [
|
|
2259
|
+
"**aihu template element: `<$shield>`**",
|
|
2260
|
+
"",
|
|
2261
|
+
"Error boundary.",
|
|
2262
|
+
"",
|
|
2263
|
+
"Compiles to:",
|
|
2264
|
+
"```typescript",
|
|
2265
|
+
"createShieldBoundary({ fallback, onError, build, parent })",
|
|
2266
|
+
"```",
|
|
2267
|
+
"",
|
|
2268
|
+
"Catches errors during render, in effects, and unhandled `$resource` errors.",
|
|
2269
|
+
"Slot context: `shield.error`, `shield.retry`.",
|
|
2270
|
+
"",
|
|
2271
|
+
"[v1 spec §3.14](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2272
|
+
].join("\n"),
|
|
2273
|
+
"<$guard>": [
|
|
2274
|
+
"**aihu template element: `<$guard>`**",
|
|
2275
|
+
"",
|
|
2276
|
+
"Access-control boundary (scope, permissions, rate limit).",
|
|
2277
|
+
"",
|
|
2278
|
+
"Compiles to:",
|
|
2279
|
+
"```typescript",
|
|
2280
|
+
"createGuardBoundary({ scope, permissions, rateLimit, fallback, redirect, onDeny, build })",
|
|
2281
|
+
"```",
|
|
2282
|
+
"",
|
|
2283
|
+
"Usage: `<$guard scope=\"authenticated\" fallback=\"LoginPrompt\"><Page /></$guard>`",
|
|
2284
|
+
"Slot context: `guard.user`, `guard.reason`, `guard.path`.",
|
|
2285
|
+
"",
|
|
2286
|
+
"[v1 spec §3.15](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2287
|
+
].join("\n"),
|
|
2288
|
+
"<$warp>": [
|
|
2289
|
+
"**aihu template element: `<$warp>`**",
|
|
2290
|
+
"",
|
|
2291
|
+
"Render content into a different DOM location (portal).",
|
|
2292
|
+
"",
|
|
2293
|
+
"Compiles to:",
|
|
2294
|
+
"```typescript",
|
|
2295
|
+
"createWarpBoundary({ target: () => to, condition: () => $if, build })",
|
|
2296
|
+
"```",
|
|
2297
|
+
"",
|
|
2298
|
+
"Usage: `<$warp to=\"#modal-root\"><Dialog /></$warp>`",
|
|
2299
|
+
"",
|
|
2300
|
+
"[v1 spec §3.16](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2301
|
+
].join("\n"),
|
|
2302
|
+
$reactive: [
|
|
2303
|
+
"**aihu @style macro: `$reactive`**",
|
|
2304
|
+
"",
|
|
2305
|
+
"Bind a CSS property to a reactive expression via a CSS custom property.",
|
|
2306
|
+
"",
|
|
2307
|
+
"Compiles to:",
|
|
2308
|
+
"```typescript",
|
|
2309
|
+
"effect(() => rootEl.style.setProperty('--reactive-N', String(expr)))",
|
|
2310
|
+
"/* CSS: .aihu-component { property: var(--reactive-N) } */",
|
|
2311
|
+
"```",
|
|
2312
|
+
"",
|
|
2313
|
+
"Usage: `h1 { color: $reactive(error ? \"red\" : \"black\") }`",
|
|
2314
|
+
"",
|
|
2315
|
+
"[v1 spec §4.1](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2316
|
+
].join("\n"),
|
|
2317
|
+
$tokens: [
|
|
2318
|
+
"**aihu @style macro: `$tokens`**",
|
|
2319
|
+
"",
|
|
2320
|
+
"Import design tokens from project config (`aihu.config.ts` `style.tokens`).",
|
|
2321
|
+
"",
|
|
2322
|
+
"Lowering depends on mode:",
|
|
2323
|
+
"- `tokens` mode → CSS custom properties (e.g. `--color-primary`)",
|
|
2324
|
+
"- `tailwind` mode → mapped to Tailwind class equivalents",
|
|
2325
|
+
"- `plain` mode → inlined raw values",
|
|
2326
|
+
"",
|
|
2327
|
+
"Usage: `$tokens(spacing, color, typography)`",
|
|
2328
|
+
"",
|
|
2329
|
+
"[v1 spec §4.2](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2330
|
+
].join("\n"),
|
|
2331
|
+
$global: [
|
|
2332
|
+
"**aihu @style macro: `$global`**",
|
|
2333
|
+
"",
|
|
2334
|
+
"Emit unscoped CSS rules (escape from component scoping).",
|
|
2335
|
+
"",
|
|
2336
|
+
"Usage:",
|
|
2337
|
+
"```",
|
|
2338
|
+
"@style {",
|
|
2339
|
+
" $global { body { margin: 0 } * { box-sizing: border-box } }",
|
|
2340
|
+
"}",
|
|
2341
|
+
"```",
|
|
2342
|
+
"",
|
|
2343
|
+
"`$reactive` inside `$global` targets `document.documentElement`.",
|
|
2344
|
+
"",
|
|
2345
|
+
"[v1 spec §4.3](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2346
|
+
].join("\n"),
|
|
2347
|
+
$media: [
|
|
2348
|
+
"**aihu @style macro: `$media`**",
|
|
2349
|
+
"",
|
|
2350
|
+
"CSS media-query block.",
|
|
2351
|
+
"",
|
|
2352
|
+
"Compiles to:",
|
|
2353
|
+
"```css",
|
|
2354
|
+
"@media (query) { rules }",
|
|
2355
|
+
"```",
|
|
2356
|
+
"",
|
|
2357
|
+
"Usage: `$media(min-width: 768px) { h1 { font-size: 2rem } }`",
|
|
2358
|
+
"",
|
|
2359
|
+
"[v1 spec §4.4](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2360
|
+
].join("\n"),
|
|
2361
|
+
$when: [
|
|
2362
|
+
"**aihu @style macro: `$when`**",
|
|
2363
|
+
"",
|
|
2364
|
+
"Apply styles conditionally based on a signal value.",
|
|
2365
|
+
"",
|
|
2366
|
+
"Compiles to:",
|
|
2367
|
+
"```typescript",
|
|
2368
|
+
"effect(() => { rootEl.dataset.whenN = String(condition) })",
|
|
2369
|
+
"/* CSS: .aihu-component[data-when-N=\"true\"] { rules } */",
|
|
2370
|
+
"```",
|
|
2371
|
+
"",
|
|
2372
|
+
"Usage: `$when(loading) { root { opacity: 0.5 } }`",
|
|
2373
|
+
"",
|
|
2374
|
+
"[v1 spec §4.5](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)"
|
|
2375
|
+
].join("\n"),
|
|
2376
|
+
$scope: [
|
|
2377
|
+
"**aihu @agent macro: `$scope`**",
|
|
2378
|
+
"",
|
|
2379
|
+
"Declares the auth scope required to access this component agent surface.",
|
|
2380
|
+
"",
|
|
2381
|
+
"Compiles to scope metadata attached to all MCP resources/tools registered",
|
|
2382
|
+
"for this component. Every agent request is checked against the scope.",
|
|
2383
|
+
"",
|
|
2384
|
+
"Usage: `$scope authenticated`",
|
|
2385
|
+
"",
|
|
2386
|
+
"[v2 spec §4](docs/superpowers/specs/2026-05-05-spec-macro-vocabulary-v2.md)"
|
|
2387
|
+
].join("\n"),
|
|
2388
|
+
"$rate-limit": [
|
|
2389
|
+
"**aihu @agent macro: `$rate-limit`**",
|
|
2390
|
+
"",
|
|
2391
|
+
"Override the default rate limit for this component agent surface.",
|
|
2392
|
+
"",
|
|
2393
|
+
"Compiles to rate-limit metadata attached to MCP tools. Per-component limit",
|
|
2394
|
+
"composes with scope-level limits (tighter wins). Counted per user per component.",
|
|
2395
|
+
"",
|
|
2396
|
+
"Usage: `$rate-limit \"100/min\"`, `$rate-limit \"unlimited\"`",
|
|
2397
|
+
"",
|
|
2398
|
+
"[v2 spec §4](docs/superpowers/specs/2026-05-05-spec-macro-vocabulary-v2.md)"
|
|
2399
|
+
].join("\n"),
|
|
2400
|
+
$describe: [
|
|
2401
|
+
"**aihu @agent macro: `$describe`**",
|
|
2402
|
+
"",
|
|
2403
|
+
"**Deprecated in v2 (C440)**. Replace with per-name `describe: \"<text>\"`",
|
|
2404
|
+
"on the corresponding `@state` collection entry.",
|
|
2405
|
+
"",
|
|
2406
|
+
"v1 form (for migration reference only):",
|
|
2407
|
+
"```",
|
|
2408
|
+
"$describe name \"description string\"",
|
|
2409
|
+
"$describe { name1: \"...\", name2: \"...\" }",
|
|
2410
|
+
"```",
|
|
2411
|
+
"",
|
|
2412
|
+
"v2 replacement:",
|
|
2413
|
+
"```",
|
|
2414
|
+
"@state {",
|
|
2415
|
+
" $prop: {",
|
|
2416
|
+
" name: { default: undefined, describe: 'description string' },",
|
|
2417
|
+
" }",
|
|
2418
|
+
"}",
|
|
2419
|
+
"```",
|
|
2420
|
+
"",
|
|
2421
|
+
"[v1 spec §5.6](docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)",
|
|
2422
|
+
"[v2 spec §4 (C440)](docs/superpowers/specs/2026-05-05-spec-macro-vocabulary-v2.md)"
|
|
1884
2423
|
].join("\n")
|
|
1885
2424
|
};
|
|
1886
2425
|
function getBlockContext(lines, lineIndex) {
|
|
@@ -1888,15 +2427,19 @@ function getBlockContext(lines, lineIndex) {
|
|
|
1888
2427
|
const trimmed = lines[i].trimStart();
|
|
1889
2428
|
if (/^@state\s*\{/.test(trimmed)) return "state";
|
|
1890
2429
|
if (/^@template\s*\{/.test(trimmed)) return "template";
|
|
1891
|
-
if (/^@
|
|
2430
|
+
if (/^@style\s*\{/.test(trimmed)) return "style";
|
|
2431
|
+
if (/^@agent\s*\{/.test(trimmed)) return "agent";
|
|
2432
|
+
if (/^@route\s*\{/.test(trimmed)) return "route";
|
|
1892
2433
|
}
|
|
1893
2434
|
return "unknown";
|
|
1894
2435
|
}
|
|
1895
2436
|
function getMacroAtPosition(lineText, character) {
|
|
1896
2437
|
let m;
|
|
1897
|
-
const
|
|
2438
|
+
const elementRe = /<\$(slot|suspense|shield|guard|warp)\b/g;
|
|
2439
|
+
while ((m = elementRe.exec(lineText)) !== null) if (character >= m.index && character <= m.index + m[0].length) return `<$${m[1]}>`;
|
|
2440
|
+
const namespacedRe = /\$(on|bind|effect|lifecycle|expose|emit)(?:[.:][A-Za-z_$][\w$]*)?\b/g;
|
|
1898
2441
|
while ((m = namespacedRe.exec(lineText)) !== null) if (character >= m.index && character <= m.index + m[0].length) return `$${m[1]}`;
|
|
1899
|
-
const bareRe = /\$(prop|computed|action|resource|effect|lifecycle|emit|if|each|html|show)\b/g;
|
|
2442
|
+
const bareRe = /\$(prop|computed|action|resource|effect|lifecycle|emit|if|each|html|show|watch|shared|cookie|server|meta|key|raw|once|memo|reactive|tokens|global|media|when|scope|rate-limit|describe)\b/g;
|
|
1900
2443
|
while ((m = bareRe.exec(lineText)) !== null) if (character >= m.index && character <= m.index + m[0].length) return `$${m[1]}`;
|
|
1901
2444
|
const blockRe = /\{(?:#(if|each)|@(html))\b/g;
|
|
1902
2445
|
while ((m = blockRe.exec(lineText)) !== null) if (character >= m.index && character <= m.index + m[0].length) return `$${m[1] ?? m[2]}`;
|
|
@@ -1906,6 +2449,408 @@ function getHoverContent(macro) {
|
|
|
1906
2449
|
return HOVER_TABLE[macro] ?? null;
|
|
1907
2450
|
}
|
|
1908
2451
|
//#endregion
|
|
1909
|
-
|
|
2452
|
+
//#region src/core/state-generator.ts
|
|
2453
|
+
const VIRTUAL_HEADER = [
|
|
2454
|
+
"import type { createResource as _cr } from '@aihu/runtime'",
|
|
2455
|
+
"export {}",
|
|
2456
|
+
"declare const __aihu_state__: unique symbol",
|
|
2457
|
+
""
|
|
2458
|
+
].join("\n");
|
|
2459
|
+
/**
|
|
2460
|
+
* Find the matching closing brace for an opening brace at `openPos` in `text`.
|
|
2461
|
+
* Returns the index of the `}` character, or -1 if not found.
|
|
2462
|
+
*/
|
|
2463
|
+
function findMatchingBrace(text, openPos) {
|
|
2464
|
+
let depth = 0;
|
|
2465
|
+
for (let i = openPos; i < text.length; i++) if (text[i] === "{") depth++;
|
|
2466
|
+
else if (text[i] === "}") {
|
|
2467
|
+
depth--;
|
|
2468
|
+
if (depth === 0) return i;
|
|
2469
|
+
}
|
|
2470
|
+
return -1;
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Extract the value text for a macro key starting at `colonPos` (position of
|
|
2474
|
+
* the `:` after the macro key name) using a brace-depth walk. Returns the raw
|
|
2475
|
+
* value string (may be a brace-enclosed object or a simple expression).
|
|
2476
|
+
*/
|
|
2477
|
+
function extractMacroValue(text, colonPos) {
|
|
2478
|
+
let start = colonPos + 1;
|
|
2479
|
+
while (start < text.length && (text[start] === " " || text[start] === " ")) start++;
|
|
2480
|
+
if (start >= text.length) return "";
|
|
2481
|
+
if (text[start] === "{") {
|
|
2482
|
+
const close = findMatchingBrace(text, start);
|
|
2483
|
+
if (close === -1) return text.slice(start);
|
|
2484
|
+
return text.slice(start, close + 1);
|
|
2485
|
+
}
|
|
2486
|
+
const end = text.indexOf("\n", start);
|
|
2487
|
+
return end === -1 ? text.slice(start) : text.slice(start, end);
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* Infer the TypeScript type from a `default:` value in a macro entry object.
|
|
2491
|
+
* Handles number, string, boolean literals, null/undefined, and verbatim `type:` key.
|
|
2492
|
+
*/
|
|
2493
|
+
function inferType(entryText) {
|
|
2494
|
+
const typeMatch = /\btype\s*:\s*(['"`]?)(\w[\w.<>, [\]|&?]*)\1/.exec(entryText);
|
|
2495
|
+
if (typeMatch) return typeMatch[2];
|
|
2496
|
+
const defMatch = /\bdefault\s*:\s*([^,}\n]+)/.exec(entryText);
|
|
2497
|
+
if (!defMatch) return "unknown";
|
|
2498
|
+
const val = defMatch[1].trim();
|
|
2499
|
+
if (/^-?\d+(\.\d+)?$/.test(val)) return "number";
|
|
2500
|
+
if (/^(['"`]).*\1$/.test(val)) return "string";
|
|
2501
|
+
if (val === "true" || val === "false") return "boolean";
|
|
2502
|
+
if (val === "null" || val === "undefined") return "unknown";
|
|
2503
|
+
return "unknown";
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* Extract the default value string from a macro entry for use in the virtual
|
|
2507
|
+
* TS initialiser. Numbers stay as-is; strings stay as-is; others → 0 as any.
|
|
2508
|
+
*/
|
|
2509
|
+
function inferDefault(entryText, type) {
|
|
2510
|
+
const defMatch = /\bdefault\s*:\s*([^,}\n]+)/.exec(entryText);
|
|
2511
|
+
if (!defMatch) return "0 as any";
|
|
2512
|
+
const val = defMatch[1].trim();
|
|
2513
|
+
if (type === "number") return `${val} as any`;
|
|
2514
|
+
if (type === "string") return `${val} as any`;
|
|
2515
|
+
if (type === "boolean") return `${val} as any`;
|
|
2516
|
+
return "0 as any";
|
|
2517
|
+
}
|
|
2518
|
+
const MAPPED_DATA = {
|
|
2519
|
+
verification: false,
|
|
2520
|
+
completion: true,
|
|
2521
|
+
semantic: true,
|
|
2522
|
+
navigation: true,
|
|
2523
|
+
structure: true
|
|
2524
|
+
};
|
|
2525
|
+
function lowerMacro(macroKey, valueText, macroKeyOffset, source) {
|
|
2526
|
+
const results = [];
|
|
2527
|
+
const inner = valueText.startsWith("{") ? valueText.slice(1, valueText.length - 1) : valueText;
|
|
2528
|
+
let depth = 0;
|
|
2529
|
+
let i = 0;
|
|
2530
|
+
while (i < inner.length) {
|
|
2531
|
+
const ch = inner[i];
|
|
2532
|
+
if (ch === "{") {
|
|
2533
|
+
depth++;
|
|
2534
|
+
i++;
|
|
2535
|
+
continue;
|
|
2536
|
+
}
|
|
2537
|
+
if (ch === "}") {
|
|
2538
|
+
depth--;
|
|
2539
|
+
i++;
|
|
2540
|
+
continue;
|
|
2541
|
+
}
|
|
2542
|
+
if (depth === 0 && ch !== void 0) {
|
|
2543
|
+
const identMatch = /^(\w[\w-]*)(\s*):/.exec(inner.slice(i));
|
|
2544
|
+
if (identMatch) {
|
|
2545
|
+
const name = identMatch[1];
|
|
2546
|
+
const colonPos = i + identMatch[0].length - 1;
|
|
2547
|
+
let entryValue = "";
|
|
2548
|
+
if (inner.slice(colonPos + 1).trimStart().startsWith("{")) {
|
|
2549
|
+
let s = colonPos + 1;
|
|
2550
|
+
while (s < inner.length && (inner[s] === " " || inner[s] === " " || inner[s] === "\n")) s++;
|
|
2551
|
+
if (inner[s] === "{") {
|
|
2552
|
+
const close = findMatchingBrace(inner, s);
|
|
2553
|
+
entryValue = close === -1 ? inner.slice(s) : inner.slice(s, close + 1);
|
|
2554
|
+
i = close !== -1 ? close + 1 : inner.length;
|
|
2555
|
+
} else {
|
|
2556
|
+
const eol = inner.indexOf("\n", s);
|
|
2557
|
+
entryValue = eol === -1 ? inner.slice(s) : inner.slice(s, eol);
|
|
2558
|
+
i = eol !== -1 ? eol + 1 : inner.length;
|
|
2559
|
+
}
|
|
2560
|
+
} else {
|
|
2561
|
+
const eol = inner.indexOf("\n", colonPos + 1);
|
|
2562
|
+
entryValue = eol === -1 ? inner.slice(colonPos + 1) : inner.slice(colonPos + 1, eol);
|
|
2563
|
+
i = eol !== -1 ? eol + 1 : inner.length;
|
|
2564
|
+
}
|
|
2565
|
+
const searchFrom = macroKeyOffset;
|
|
2566
|
+
const nameInSource = source.indexOf(name, searchFrom);
|
|
2567
|
+
const decl = buildDecl(macroKey, name, entryValue, nameInSource);
|
|
2568
|
+
if (decl) results.push(decl);
|
|
2569
|
+
continue;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
i++;
|
|
2573
|
+
}
|
|
2574
|
+
return results;
|
|
2575
|
+
}
|
|
2576
|
+
function buildDecl(macroKey, name, entryText, sourceNameOffset) {
|
|
2577
|
+
switch (macroKey) {
|
|
2578
|
+
case "$prop":
|
|
2579
|
+
case "$shared":
|
|
2580
|
+
case "$cookie": {
|
|
2581
|
+
const type = inferType(entryText);
|
|
2582
|
+
return {
|
|
2583
|
+
code: `const ${name}: ${type} = ${inferDefault(entryText, type)}`,
|
|
2584
|
+
sourceNameOffset,
|
|
2585
|
+
mapped: true,
|
|
2586
|
+
nameLen: name.length
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
case "$computed": return {
|
|
2590
|
+
code: `const ${name} = ${entryText.trim().replace(/^['"`]|['"`]$/g, "") || "undefined"}`,
|
|
2591
|
+
sourceNameOffset,
|
|
2592
|
+
mapped: true,
|
|
2593
|
+
nameLen: name.length
|
|
2594
|
+
};
|
|
2595
|
+
case "$action": {
|
|
2596
|
+
const bodyMatch = /=>\s*(\{[\s\S]*\}|\S+)/.exec(entryText);
|
|
2597
|
+
return {
|
|
2598
|
+
code: `function ${name}(): void ${bodyMatch ? bodyMatch[1] : "{}"}`,
|
|
2599
|
+
sourceNameOffset,
|
|
2600
|
+
mapped: true,
|
|
2601
|
+
nameLen: name.length
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
case "$resource": return {
|
|
2605
|
+
code: `const ${name} = (null as any as ReturnType<typeof _cr>)`,
|
|
2606
|
+
sourceNameOffset,
|
|
2607
|
+
mapped: true,
|
|
2608
|
+
nameLen: name.length
|
|
2609
|
+
};
|
|
2610
|
+
case "$effect": {
|
|
2611
|
+
const bodyMatch = /=>\s*(?:\{([^}]*)\}|([^,}\n]+))/.exec(entryText);
|
|
2612
|
+
return {
|
|
2613
|
+
code: `/* effect: ${name} */; ${(bodyMatch ? (bodyMatch[1] ?? bodyMatch[2] ?? "").trim() : "") || "void 0"}`,
|
|
2614
|
+
sourceNameOffset,
|
|
2615
|
+
mapped: true,
|
|
2616
|
+
nameLen: name.length
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
case "$lifecycle": return {
|
|
2620
|
+
code: `/* lifecycle.${name} */`,
|
|
2621
|
+
sourceNameOffset,
|
|
2622
|
+
mapped: false,
|
|
2623
|
+
nameLen: name.length
|
|
2624
|
+
};
|
|
2625
|
+
case "$watch": {
|
|
2626
|
+
const srcMatch = /\bsrc\s*:\s*([^,}\n]+)/.exec(entryText);
|
|
2627
|
+
return {
|
|
2628
|
+
code: `/* watch: ${name} */; ${srcMatch ? srcMatch[1].trim() : name}`,
|
|
2629
|
+
sourceNameOffset,
|
|
2630
|
+
mapped: true,
|
|
2631
|
+
nameLen: name.length
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
case "$expose": return {
|
|
2635
|
+
code: `/* expose: ${name} */`,
|
|
2636
|
+
sourceNameOffset,
|
|
2637
|
+
mapped: false,
|
|
2638
|
+
nameLen: name.length
|
|
2639
|
+
};
|
|
2640
|
+
case "$server": {
|
|
2641
|
+
const bodyMatch = /=>\s*(\{[\s\S]*?\})/.exec(entryText);
|
|
2642
|
+
return {
|
|
2643
|
+
code: `async function ${name}(): Promise<unknown> ${bodyMatch ? bodyMatch[1] : "{}"}`,
|
|
2644
|
+
sourceNameOffset,
|
|
2645
|
+
mapped: true,
|
|
2646
|
+
nameLen: name.length
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
case "$meta": return {
|
|
2650
|
+
code: `/* meta: ${name} */`,
|
|
2651
|
+
sourceNameOffset,
|
|
2652
|
+
mapped: false,
|
|
2653
|
+
nameLen: name.length
|
|
2654
|
+
};
|
|
2655
|
+
default: return null;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* Generate the virtual TypeScript file for the @state block of a .aihu source.
|
|
2660
|
+
* Returns `{ virtualCode: '', mappings: [] }` if no @state block is found.
|
|
2661
|
+
*/
|
|
2662
|
+
function generateStateVirtualCode(input) {
|
|
2663
|
+
const source = input.source;
|
|
2664
|
+
const openerMatch = /^@state\s*\{/m.exec(source);
|
|
2665
|
+
if (!openerMatch) return {
|
|
2666
|
+
virtualCode: "",
|
|
2667
|
+
mappings: []
|
|
2668
|
+
};
|
|
2669
|
+
const openBracePos = openerMatch.index + openerMatch[0].length - 1;
|
|
2670
|
+
const closeBracePos = findMatchingBrace(source, openBracePos);
|
|
2671
|
+
if (closeBracePos === -1) return {
|
|
2672
|
+
virtualCode: "",
|
|
2673
|
+
mappings: []
|
|
2674
|
+
};
|
|
2675
|
+
const blockBody = source.slice(openBracePos + 1, closeBracePos);
|
|
2676
|
+
const macroRe = /^\s*(\$(?:\w[\w-]*))\s*:/gm;
|
|
2677
|
+
const declarations = [];
|
|
2678
|
+
let macroMatch;
|
|
2679
|
+
while ((macroMatch = macroRe.exec(blockBody)) !== null) {
|
|
2680
|
+
const macroKey = macroMatch[1];
|
|
2681
|
+
const macroKeyOffsetInSource = openBracePos + 1 + macroMatch.index;
|
|
2682
|
+
const lowered = lowerMacro(macroKey, extractMacroValue(source, openBracePos + 1 + macroMatch.index + macroMatch[0].length - 1), macroKeyOffsetInSource, source);
|
|
2683
|
+
declarations.push(...lowered);
|
|
2684
|
+
}
|
|
2685
|
+
if (declarations.length === 0) return {
|
|
2686
|
+
virtualCode: "",
|
|
2687
|
+
mappings: []
|
|
2688
|
+
};
|
|
2689
|
+
let virtualCode = VIRTUAL_HEADER;
|
|
2690
|
+
const mappings = [];
|
|
2691
|
+
for (const decl of declarations) {
|
|
2692
|
+
const generatedOffset = virtualCode.length;
|
|
2693
|
+
virtualCode += `${decl.code}\n`;
|
|
2694
|
+
if (decl.mapped && decl.sourceNameOffset >= 0) {
|
|
2695
|
+
let generatedNameOffset = generatedOffset;
|
|
2696
|
+
const codeLine = decl.code;
|
|
2697
|
+
const nameStr = source.slice(decl.sourceNameOffset, decl.sourceNameOffset + decl.nameLen);
|
|
2698
|
+
const nameInGenerated = codeLine.indexOf(nameStr);
|
|
2699
|
+
if (nameInGenerated >= 0) generatedNameOffset = generatedOffset + nameInGenerated;
|
|
2700
|
+
mappings.push({
|
|
2701
|
+
sourceOffsets: [decl.sourceNameOffset],
|
|
2702
|
+
generatedOffsets: [generatedNameOffset],
|
|
2703
|
+
lengths: [decl.nameLen],
|
|
2704
|
+
data: { ...MAPPED_DATA }
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return {
|
|
2709
|
+
virtualCode,
|
|
2710
|
+
mappings
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
//#endregion
|
|
2714
|
+
//#region src/core/volar-plugin.ts
|
|
2715
|
+
/** Wrap a string in a minimal IScriptSnapshot implementation. */
|
|
2716
|
+
function createSnapshot(text) {
|
|
2717
|
+
return {
|
|
2718
|
+
getText(start, end) {
|
|
2719
|
+
return text.slice(start, end);
|
|
2720
|
+
},
|
|
2721
|
+
getLength() {
|
|
2722
|
+
return text.length;
|
|
2723
|
+
},
|
|
2724
|
+
getChangeRange() {}
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* Create the Volar LanguagePlugin for .aihu files.
|
|
2729
|
+
* Returns "aihu" language ID for .aihu URIs; generates a virtual __state__.ts
|
|
2730
|
+
* code block from the @state macro body.
|
|
2731
|
+
*/
|
|
2732
|
+
function createAihuLanguagePlugin() {
|
|
2733
|
+
return {
|
|
2734
|
+
getLanguageId(scriptId) {
|
|
2735
|
+
return (typeof scriptId === "string" ? scriptId : scriptId.fsPath ?? scriptId.path ?? "").endsWith(".aihu") ? "aihu" : void 0;
|
|
2736
|
+
},
|
|
2737
|
+
createVirtualCode(_scriptId, languageId, snapshot, _ctx) {
|
|
2738
|
+
if (languageId !== "aihu") return void 0;
|
|
2739
|
+
const output = generateStateVirtualCode({
|
|
2740
|
+
source: snapshot.getText(0, snapshot.getLength()),
|
|
2741
|
+
snapshot
|
|
2742
|
+
});
|
|
2743
|
+
if (!output.virtualCode) return void 0;
|
|
2744
|
+
return {
|
|
2745
|
+
id: "__state__",
|
|
2746
|
+
languageId: "typescript",
|
|
2747
|
+
snapshot: createSnapshot(output.virtualCode),
|
|
2748
|
+
mappings: output.mappings
|
|
2749
|
+
};
|
|
2750
|
+
},
|
|
2751
|
+
updateVirtualCode(scriptId, _virtualCode, newSnapshot, ctx) {
|
|
2752
|
+
return this.createVirtualCode(scriptId, "aihu", newSnapshot, ctx);
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Create the Volar LanguageServicePlugin for aihu.
|
|
2758
|
+
* Wraps the existing getMacroAtPosition / getHoverContent from core/hover.ts.
|
|
2759
|
+
* Per architect-brief §4.2 — "direct translation" of the existing onHover handler.
|
|
2760
|
+
*/
|
|
2761
|
+
function createAihuLanguageServicePlugin() {
|
|
2762
|
+
return {
|
|
2763
|
+
capabilities: {
|
|
2764
|
+
hoverProvider: true,
|
|
2765
|
+
completionProvider: { triggerCharacters: ["$", "@"] },
|
|
2766
|
+
diagnosticProvider: {
|
|
2767
|
+
interFileDependencies: false,
|
|
2768
|
+
workspaceDiagnostics: false
|
|
2769
|
+
},
|
|
2770
|
+
codeActionProvider: { codeActionKinds: ["quickfix"] }
|
|
2771
|
+
},
|
|
2772
|
+
create(_context) {
|
|
2773
|
+
return {
|
|
2774
|
+
provideHover(document, position, _token) {
|
|
2775
|
+
const macro = getMacroAtPosition(document.getText({
|
|
2776
|
+
start: {
|
|
2777
|
+
line: position.line,
|
|
2778
|
+
character: 0
|
|
2779
|
+
},
|
|
2780
|
+
end: {
|
|
2781
|
+
line: position.line,
|
|
2782
|
+
character: Number.MAX_SAFE_INTEGER
|
|
2783
|
+
}
|
|
2784
|
+
}), position.character);
|
|
2785
|
+
if (!macro) return null;
|
|
2786
|
+
const content = getHoverContent(macro);
|
|
2787
|
+
if (!content) return null;
|
|
2788
|
+
return { contents: {
|
|
2789
|
+
kind: "markdown",
|
|
2790
|
+
value: content
|
|
2791
|
+
} };
|
|
2792
|
+
},
|
|
2793
|
+
provideCompletionItems(document, position, _context, _token) {
|
|
2794
|
+
const ctx = getBlockContext(document.getText().split("\n"), position.line);
|
|
2795
|
+
if (ctx === "state") return {
|
|
2796
|
+
isIncomplete: false,
|
|
2797
|
+
items: STATE_MACRO_COMPLETIONS
|
|
2798
|
+
};
|
|
2799
|
+
if (ctx === "template") return {
|
|
2800
|
+
isIncomplete: false,
|
|
2801
|
+
items: []
|
|
2802
|
+
};
|
|
2803
|
+
return {
|
|
2804
|
+
isIncomplete: false,
|
|
2805
|
+
items: BLOCK_COMPLETIONS
|
|
2806
|
+
};
|
|
2807
|
+
},
|
|
2808
|
+
async provideDiagnostics(document, _token) {
|
|
2809
|
+
const filePath = document.uri.replace(/^file:\/\//, "");
|
|
2810
|
+
return (await compileWithDiagnostics(document.getText(), filePath)).diagnostics.map((diag) => ({
|
|
2811
|
+
severity: 1,
|
|
2812
|
+
range: diag.range ?? {
|
|
2813
|
+
start: {
|
|
2814
|
+
line: 0,
|
|
2815
|
+
character: 0
|
|
2816
|
+
},
|
|
2817
|
+
end: {
|
|
2818
|
+
line: 0,
|
|
2819
|
+
character: 0
|
|
2820
|
+
}
|
|
2821
|
+
},
|
|
2822
|
+
message: diag.message,
|
|
2823
|
+
code: diag.code,
|
|
2824
|
+
source: "aihu"
|
|
2825
|
+
}));
|
|
2826
|
+
},
|
|
2827
|
+
provideCodeActions(document, _range, context, _token) {
|
|
2828
|
+
if (context.diagnostics.filter((d) => typeof d.code === "string" && MIGRATE_CODES.has(d.code)).length === 0) return [];
|
|
2829
|
+
const fix = buildMigrateFix(document.getText());
|
|
2830
|
+
if (!fix) return [];
|
|
2831
|
+
return [{
|
|
2832
|
+
title: fix.title,
|
|
2833
|
+
kind: "quickfix",
|
|
2834
|
+
edit: { changes: { [document.uri]: [{
|
|
2835
|
+
range: {
|
|
2836
|
+
start: {
|
|
2837
|
+
line: 0,
|
|
2838
|
+
character: 0
|
|
2839
|
+
},
|
|
2840
|
+
end: {
|
|
2841
|
+
line: Number.MAX_SAFE_INTEGER,
|
|
2842
|
+
character: 0
|
|
2843
|
+
}
|
|
2844
|
+
},
|
|
2845
|
+
newText: fix.rewritten
|
|
2846
|
+
}] } }
|
|
2847
|
+
}];
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
//#endregion
|
|
2854
|
+
export { getHoverContent as a, parseMachineErrors as c, MIGRATE_CODES as d, buildMigrateFix as f, getBlockContext as i, BLOCK_COMPLETIONS as l, createAihuLanguageServicePlugin as n, getMacroAtPosition as o, require_source_map as p, generateStateVirtualCode as r, compileWithDiagnostics as s, createAihuLanguagePlugin as t, STATE_MACRO_COMPLETIONS as u };
|
|
1910
2855
|
|
|
1911
|
-
//# sourceMappingURL=
|
|
2856
|
+
//# sourceMappingURL=volar-plugin-cOusBZ0l.js.map
|