@fluixi/ts-plugin 0.1.0-alpha.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/dist/index.js +1373 -0
  4. package/package.json +35 -0
package/dist/index.js ADDED
@@ -0,0 +1,1373 @@
1
+ "use strict";
2
+
3
+ // ../template-parser/dist/index.mjs
4
+ function N(t) {
5
+ let e = (i, r) => {
6
+ i.parent = r;
7
+ let n = C(i);
8
+ for (let s of n) e(s, i);
9
+ };
10
+ return e(t, null), t;
11
+ }
12
+ function C(t) {
13
+ switch (t.kind) {
14
+ case "Root":
15
+ case "Element":
16
+ case "Component":
17
+ case "Fragment":
18
+ return t.children;
19
+ default:
20
+ return [];
21
+ }
22
+ }
23
+ var u = class {
24
+ constructor(e) {
25
+ this.pieces = e;
26
+ this.pi = 0;
27
+ this.oi = 0;
28
+ this.normalize();
29
+ }
30
+ normalize() {
31
+ for (; this.pi < this.pieces.length; ) {
32
+ let e = this.pieces[this.pi];
33
+ if (e.kind === "static" && this.oi >= e.text.length) {
34
+ this.pi++, this.oi = 0;
35
+ continue;
36
+ }
37
+ break;
38
+ }
39
+ }
40
+ eof() {
41
+ return this.pi >= this.pieces.length;
42
+ }
43
+ pos() {
44
+ if (this.eof()) {
45
+ let i = this.pieces[this.pieces.length - 1];
46
+ return i ? i.kind === "static" ? i.start + i.text.length : i.end : 0;
47
+ }
48
+ let e = this.pieces[this.pi];
49
+ return e.kind === "static" ? e.start + this.oi : e.start;
50
+ }
51
+ atHole() {
52
+ return !this.eof() && this.pieces[this.pi].kind === "hole";
53
+ }
54
+ holeFollows() {
55
+ if (this.eof()) return false;
56
+ let e = this.pieces[this.pi];
57
+ if (e.kind !== "static" || this.oi + 1 < e.text.length) return false;
58
+ let i = this.pieces[this.pi + 1];
59
+ return i !== void 0 && i.kind === "hole";
60
+ }
61
+ takeHole() {
62
+ let e = this.pieces[this.pi];
63
+ if (e.kind !== "hole") throw new Error("takeHole called off a hole");
64
+ return this.pi++, this.oi = 0, this.normalize(), { index: e.index, loc: { start: e.start, end: e.end } };
65
+ }
66
+ peek(e = 0) {
67
+ if (this.eof()) return "";
68
+ let i = this.pieces[this.pi];
69
+ return i.kind !== "static" ? "" : i.text[this.oi + e] ?? "";
70
+ }
71
+ startsWith(e) {
72
+ if (this.eof()) return false;
73
+ let i = this.pieces[this.pi];
74
+ return i.kind !== "static" ? false : i.text.startsWith(e, this.oi);
75
+ }
76
+ advance() {
77
+ if (this.eof()) return "";
78
+ let e = this.pieces[this.pi];
79
+ if (e.kind !== "static") return "";
80
+ let i = e.text[this.oi++] ?? "";
81
+ return this.normalize(), i;
82
+ }
83
+ readWhile(e) {
84
+ let i = "";
85
+ for (; !this.eof() && !this.atHole(); ) {
86
+ let r = this.peek();
87
+ if (r === "" || !e(r)) break;
88
+ i += r, this.oi++;
89
+ }
90
+ return this.normalize(), i;
91
+ }
92
+ skipWhitespace() {
93
+ this.readWhile((e) => /\s/.test(e));
94
+ }
95
+ span(e) {
96
+ return { start: e, end: this.pos() };
97
+ }
98
+ };
99
+ var x = /* @__PURE__ */ new Set(["capture", "once", "passive", "prevent", "stop", "self"]);
100
+ function p(t, e) {
101
+ return t.value && t.value.kind === "hole" ? t.value.hole : (t.report("invalid-binding", `${e} requires an expression value: ${t.name}=\${\u2026}`), -1);
102
+ }
103
+ var b = (t, e) => ({ kind: "Attribute", name: e ? t.name.slice(1) : t.name, value: t.value, boolean: e, loc: t.loc });
104
+ var m = [{ id: "event", match: (t) => t[0] === "@" || t.startsWith("on:") || /^on[A-Z]/.test(t), build: (t) => {
105
+ let e, i;
106
+ t.name[0] === "@" ? (e = "at", i = t.name.slice(1)) : t.name.startsWith("on:") ? (e = "colon", i = t.name.slice(3)) : (e = "on", i = t.name.slice(2));
107
+ let [r, ...n] = i.split(".");
108
+ for (let s of n) x.has(s) || t.report("invalid-directive", `Unknown event modifier ".${s}" \u2014 expected one of ${[...x].join(", ")}`);
109
+ return { kind: "EventBinding", name: r, syntax: e, modifiers: n, hole: p(t, "event binding"), loc: t.loc };
110
+ } }, { id: "bind", elementOnly: true, match: (t) => t.startsWith("bind:"), build: (t) => ({ kind: "BindDirective", name: t.name.slice(5), hole: p(t, "two-way binding"), loc: t.loc }) }, { id: "class-directive", elementOnly: true, match: (t) => t.startsWith("class:"), build: (t) => ({ kind: "ClassDirective", name: t.name.slice(6), hole: p(t, "class directive"), loc: t.loc }) }, { id: "style-directive", elementOnly: true, match: (t) => t.startsWith("style:"), build: (t) => ({ kind: "StyleDirective", name: t.name.slice(6), hole: p(t, "style directive"), loc: t.loc }) }, { id: "use", match: (t) => t === "use" || t.startsWith("use:"), build: (t) => ({ kind: "UseDirective", name: t.name.startsWith("use:") ? t.name.slice(4) : null, hole: t.value && t.value.kind === "hole" ? t.value.hole : null, loc: t.loc }) }, { id: "if", elementOnly: true, match: (t) => t === "if", build: (t) => ({ kind: "IfDirective", hole: p(t, "if directive"), loc: t.loc }) }, { id: "each", elementOnly: true, match: (t) => t === "each", build: (t) => ({ kind: "EachDirective", hole: p(t, "each directive"), key: null, loc: t.loc }) }, { id: "else", elementOnly: true, match: (t) => t === "else", build: (t) => (t.value !== null && t.report("invalid-directive", "`else` takes no value"), { kind: "ElseDirective", loc: t.loc }) }, { id: "ref", match: (t) => t === "ref", build: (t) => ({ kind: "RefBinding", hole: p(t, "ref binding"), loc: t.loc }) }, { id: "property", match: (t) => t[0] === ".", build: (t) => ({ kind: "PropertyBinding", name: t.name.slice(1), hole: p(t, "property binding"), loc: t.loc }) }, { id: "boolean", match: (t) => t[0] === "?", build: (t) => b(t, true) }];
111
+ function v(t, e = m, i = { component: false }) {
112
+ for (let r of e) if (!(r.elementOnly && i.component) && r.match(t.name)) return r.build(t);
113
+ return b(t, false);
114
+ }
115
+ var y = /* @__PURE__ */ new Set(["svg", "path", "circle", "rect", "line", "polygon", "polyline", "ellipse", "g", "defs", "clipPath", "text"]);
116
+ var D = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
117
+ var E = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
118
+ function S(t) {
119
+ return t.length > 0 && (t[0] !== t[0].toLowerCase() || t.includes("."));
120
+ }
121
+ var T = /[A-Za-z0-9\-_:@.?]/;
122
+ var g = class {
123
+ constructor(e, i) {
124
+ this.diagnostics = [];
125
+ this.r = new u(e), this.svg = i.svg ?? false, this.directives = i.directives ?? m;
126
+ }
127
+ parse() {
128
+ let e = this.r.pos(), i = [];
129
+ for (; !this.r.eof() && (i.push(...this.parseChildren()), !this.r.eof()); ) {
130
+ let n = this.r.pos();
131
+ this.consumeCloseTag(), this.report("stray-close-tag", "error", "Close tag without a matching open tag", this.r.span(n));
132
+ }
133
+ let r = { kind: "Root", children: i, loc: this.r.span(e) };
134
+ return N(r), this.validateTree(r), { root: r, diagnostics: this.diagnostics };
135
+ }
136
+ validateTree(e) {
137
+ let i = (r) => {
138
+ let n = null;
139
+ for (let s of r) s.kind === "Text" && /^\s*$/.test(s.value) || (s.kind === "Element" || s.kind === "Component" ? (s.attributes.some((l) => l.kind === "ElseDirective") && (n != null && (n.kind === "Element" || n.kind === "Component") && n.attributes.some((o) => o.kind === "IfDirective") || this.report("else-without-if", "error", "`else` must immediately follow an element with `if`", s.loc)), n = s) : n = null, "children" in s && s.children && i(s.children));
140
+ };
141
+ i(e.children);
142
+ }
143
+ parseChildren() {
144
+ let e = [];
145
+ for (; !this.r.eof() && !this.r.startsWith("</"); ) {
146
+ if (this.r.startsWith("<!--")) {
147
+ e.push(this.parseComment());
148
+ continue;
149
+ }
150
+ if (this.isFragmentStart()) {
151
+ e.push(this.parseFragment());
152
+ continue;
153
+ }
154
+ if (this.isDynamicTagStart()) {
155
+ e.push(this.parseDynamicComponent());
156
+ continue;
157
+ }
158
+ if (this.isTagStart()) {
159
+ e.push(this.parseElement());
160
+ continue;
161
+ }
162
+ if (this.r.atHole()) {
163
+ let r = this.r.takeHole(), n = { kind: "Expression", hole: r.index, loc: r.loc };
164
+ e.push(n);
165
+ continue;
166
+ }
167
+ let i = this.readText();
168
+ i && e.push(i);
169
+ }
170
+ return e;
171
+ }
172
+ isTagStart() {
173
+ return this.r.peek() === "<" && /[A-Za-z]/.test(this.r.peek(1));
174
+ }
175
+ isFragmentStart() {
176
+ return this.r.peek() === "<" && this.r.peek(1) === ">";
177
+ }
178
+ isDynamicTagStart() {
179
+ return this.r.peek() === "<" && this.r.holeFollows();
180
+ }
181
+ parseDynamicComponent() {
182
+ let e = this.r.pos();
183
+ this.r.advance();
184
+ let i = this.r.takeHole().index, r = this.parseAttributes(true);
185
+ this.r.skipWhitespace();
186
+ let n = false, s = [];
187
+ return this.r.startsWith("/>") ? (this.r.advance(), this.r.advance(), n = true) : (this.r.peek() === ">" && this.r.advance(), s = this.parseChildren(), this.r.startsWith("</") ? (this.r.advance(), this.r.advance(), this.r.atHole() ? this.r.takeHole() : this.readName(), this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance()) : this.report("unclosed-tag", "error", "Unclosed dynamic component", this.r.span(e))), { kind: "Component", tag: "", tagHole: i, attributes: r, children: s, selfClosing: n, loc: this.r.span(e) };
188
+ }
189
+ readText() {
190
+ let e = this.r.pos(), i = "";
191
+ for (; !this.r.eof() && !this.r.atHole() && !(this.r.startsWith("</") || this.r.startsWith("<!--") || this.isTagStart() || this.isFragmentStart() || this.isDynamicTagStart()); ) i += this.r.advance();
192
+ return i ? { kind: "Text", value: i, raw: false, loc: this.r.span(e) } : null;
193
+ }
194
+ parseComment() {
195
+ let e = this.r.pos();
196
+ this.r.advance(), this.r.advance(), this.r.advance(), this.r.advance();
197
+ let i = "";
198
+ for (; !this.r.eof() && !this.r.startsWith("-->"); ) {
199
+ if (this.r.atHole()) {
200
+ this.r.takeHole(), i += "\0";
201
+ continue;
202
+ }
203
+ i += this.r.advance();
204
+ }
205
+ return this.r.startsWith("-->") ? (this.r.advance(), this.r.advance(), this.r.advance()) : this.report("unclosed-comment", "warning", "Unterminated comment", this.r.span(e)), { kind: "Comment", value: i, loc: this.r.span(e) };
206
+ }
207
+ parseFragment() {
208
+ let e = this.r.pos();
209
+ this.r.advance(), this.r.advance();
210
+ let i = this.parseChildren();
211
+ if (this.r.startsWith("</")) {
212
+ let r = this.consumeCloseTag();
213
+ r && this.report("mismatched-close-tag", "error", `Expected </> to close fragment, got </${r}>`, this.r.span(e));
214
+ } else this.report("unclosed-tag", "error", "Unterminated fragment", this.r.span(e));
215
+ return { kind: "Fragment", children: i, loc: this.r.span(e) };
216
+ }
217
+ parseElement() {
218
+ let e = this.r.pos();
219
+ this.r.advance();
220
+ let i = this.readName(), r = S(i), n = this.parseAttributes(r);
221
+ this.foldEachKey(n, e), this.r.skipWhitespace();
222
+ let s = i.toLowerCase(), a = !r && D.has(s), l = !r && E.has(s), o = false, c = [];
223
+ if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(), o = true;
224
+ else if (this.r.peek() === ">" && this.r.advance(), !l) if (c = a ? this.parseRawText(i) : this.parseChildren(), this.r.startsWith("</")) {
225
+ let f = this.consumeCloseTag();
226
+ f && f !== i && this.report("mismatched-close-tag", "error", `Expected </${i}>, got </${f}>`, this.r.span(e));
227
+ } else this.report("unclosed-tag", "error", `Unclosed <${i}>`, this.r.span(e));
228
+ let h = this.r.span(e);
229
+ if (r) return { kind: "Component", tag: i, tagHole: null, attributes: n, children: c, selfClosing: o, loc: h };
230
+ let d = this.svg || y.has(i) ? "svg" : "html";
231
+ return { kind: "Element", tag: i, namespace: d, attributes: n, children: c, selfClosing: o, rawText: a, loc: h };
232
+ }
233
+ parseRawText(e) {
234
+ let i = [], r = "</" + e.toLowerCase(), n = "", s = this.r.pos(), a = () => {
235
+ n && i.push({ kind: "Text", value: n, raw: true, loc: this.r.span(s) }), n = "";
236
+ };
237
+ for (; !this.r.eof() && !this.startsWithCloseFor(r); ) {
238
+ if (this.r.atHole()) {
239
+ a();
240
+ let l = this.r.takeHole();
241
+ i.push({ kind: "Expression", hole: l.index, loc: l.loc }), s = this.r.pos();
242
+ continue;
243
+ }
244
+ n || (s = this.r.pos()), n += this.r.advance();
245
+ }
246
+ return a(), i;
247
+ }
248
+ startsWithCloseFor(e) {
249
+ let i = 0;
250
+ for (; i < e.length; ) {
251
+ let r = this.r.peek(i);
252
+ if (r === "" || r.toLowerCase() !== e[i]) return false;
253
+ i++;
254
+ }
255
+ return true;
256
+ }
257
+ parseAttributes(e) {
258
+ let i = [];
259
+ for (; !this.r.eof() && (this.r.skipWhitespace(), !(this.r.peek() === ">" || this.r.startsWith("/>"))); ) {
260
+ if (this.r.atHole()) {
261
+ let o = this.r.takeHole();
262
+ i.push({ kind: "Spread", hole: o.index, loc: o.loc });
263
+ continue;
264
+ }
265
+ if (this.r.startsWith("...")) {
266
+ let o = this.r.pos();
267
+ if (this.r.advance(), this.r.advance(), this.r.advance(), this.r.atHole()) {
268
+ let c = this.r.takeHole();
269
+ i.push({ kind: "Spread", hole: c.index, loc: this.r.span(o) });
270
+ } else this.report("invalid-binding", "error", "Spread `...` must be followed by ${\u2026}", this.r.span(o));
271
+ continue;
272
+ }
273
+ let r = this.r.pos(), n = this.readName();
274
+ if (!n) {
275
+ this.r.advance();
276
+ continue;
277
+ }
278
+ let s = this.r.span(r);
279
+ this.r.skipWhitespace();
280
+ let a = null;
281
+ this.r.peek() === "=" && (this.r.advance(), this.r.skipWhitespace(), a = this.parseAttrValue());
282
+ let l = { name: n, nameLoc: s, value: a, loc: this.r.span(r), report: (o, c) => this.report(o, "error", c, this.r.span(r)) };
283
+ i.push(v(l, this.directives, { component: e }));
284
+ }
285
+ return i;
286
+ }
287
+ parseAttrValue() {
288
+ if (this.r.atHole()) return { kind: "hole", hole: this.r.takeHole().index };
289
+ let e = this.r.peek();
290
+ return e === '"' || e === "'" ? this.classifyParts(this.readQuotedParts(e)) : { kind: "static", value: this.r.readWhile((r) => !/[\s>]/.test(r)) };
291
+ }
292
+ readQuotedParts(e) {
293
+ this.r.advance();
294
+ let i = [], r = "";
295
+ for (; !this.r.eof() && this.r.peek() !== e; ) {
296
+ if (this.r.atHole()) {
297
+ r && (i.push({ text: r }), r = ""), i.push({ hole: this.r.takeHole().index });
298
+ continue;
299
+ }
300
+ let n = this.r.advance();
301
+ if (n === "") break;
302
+ r += n;
303
+ }
304
+ return r && i.push({ text: r }), this.r.peek() === e && this.r.advance(), i;
305
+ }
306
+ classifyParts(e) {
307
+ let i = e.filter((r) => "hole" in r);
308
+ return i.length === 0 ? { kind: "static", value: e.map((r) => "text" in r ? r.text : "").join("") } : e.length === 1 ? { kind: "hole", hole: i[0].hole } : { kind: "mixed", parts: e };
309
+ }
310
+ foldEachKey(e, i) {
311
+ let r = null, n = 0, s = 0, a = 0, l = 0;
312
+ for (let d of e) d.kind === "EachDirective" ? (r = d, s++) : d.kind === "IfDirective" ? n++ : d.kind === "ElseDirective" ? a++ : d.kind === "RefBinding" && l++;
313
+ let o = this.r.span(i);
314
+ s > 1 && this.report("duplicate-directive", "error", "Multiple `each` directives on one element", o), n > 1 && this.report("duplicate-directive", "error", "Multiple `if` directives on one element", o), l > 1 && this.report("duplicate-directive", "error", "Multiple `ref` bindings on one element", o), n > 0 && s > 0 && this.report("unsupported-combination", "error", "`if` and `each` cannot both apply to one element \u2014 wrap one in another element", o), n > 0 && a > 0 && this.report("invalid-directive", "error", "`if` and `else` cannot be on the same element", o);
315
+ let c = e.findIndex((d) => d.kind === "Attribute" && d.name === "key");
316
+ if (!r) {
317
+ c !== -1 && this.report("invalid-directive", "error", "`key` requires an `each` directive on the same element", o);
318
+ return;
319
+ }
320
+ if (c === -1) return;
321
+ let h = e[c];
322
+ h.kind === "Attribute" && h.value && (h.value.kind === "static" ? r.key = { static: h.value.value } : h.value.kind === "hole" && (r.key = { hole: h.value.hole })), e.splice(c, 1);
323
+ }
324
+ readName() {
325
+ return this.r.readWhile((e) => T.test(e));
326
+ }
327
+ consumeCloseTag() {
328
+ this.r.advance(), this.r.advance();
329
+ let e = this.readName();
330
+ return this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance(), e;
331
+ }
332
+ report(e, i, r, n) {
333
+ this.diagnostics.push({ code: e, severity: i, message: r, loc: n });
334
+ }
335
+ };
336
+ function k(t, e = {}) {
337
+ return new g(t, e).parse();
338
+ }
339
+ function A(t) {
340
+ let e = [], i = 0;
341
+ return t.forEach((r, n) => {
342
+ e.push({ kind: "static", text: r, start: i }), i += r.length, n < t.length - 1 && e.push({ kind: "hole", index: n, start: i, end: i });
343
+ }), e;
344
+ }
345
+ function F(t, e = {}) {
346
+ return k(A(t), e);
347
+ }
348
+
349
+ // src/collect.ts
350
+ var UNUSED_CODES = /* @__PURE__ */ new Set([6133, 6196, 6198, 6199, 6205, 6138, 6192]);
351
+ function isUnusedCode(code) {
352
+ return UNUSED_CODES.has(code);
353
+ }
354
+ function usedComponentTags(ts, sourceFile) {
355
+ const tags = /* @__PURE__ */ new Set();
356
+ const visit = (node) => {
357
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
358
+ collectTags(F(templateStatics(ts, node.template)).root.children, tags);
359
+ }
360
+ ts.forEachChild(node, visit);
361
+ };
362
+ visit(sourceFile);
363
+ return tags;
364
+ }
365
+ function templateStatics(ts, tpl) {
366
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) return [tpl.text];
367
+ const out = [tpl.head.text];
368
+ for (const span of tpl.templateSpans) out.push(span.literal.text);
369
+ return out;
370
+ }
371
+ function collectTags(nodes, out) {
372
+ for (const node of nodes) {
373
+ if (node.kind === "Component" && node.tag) out.add(node.tag.split(".")[0]);
374
+ if ("children" in node && node.children) collectTags(node.children, out);
375
+ }
376
+ }
377
+ function shouldSuppress(ts, sourceFile, diagnostic, used) {
378
+ if (diagnostic.start === void 0 || !isUnusedCode(diagnostic.code)) return false;
379
+ const spanText = sourceFile.text.substr(diagnostic.start, diagnostic.length ?? 0);
380
+ const decl = importDeclarationAt(ts, sourceFile, diagnostic.start);
381
+ if (decl) {
382
+ const names = importedNames(ts, decl);
383
+ if (names.includes(spanText)) return used.has(spanText);
384
+ return names.some((n) => used.has(n));
385
+ }
386
+ return used.has(spanText);
387
+ }
388
+ function importDeclarationAt(ts, sourceFile, pos) {
389
+ let found;
390
+ const visit = (node) => {
391
+ if (found) return;
392
+ if (ts.isImportDeclaration(node) && node.getStart(sourceFile) <= pos && node.getEnd() >= pos) {
393
+ found = node;
394
+ return;
395
+ }
396
+ ts.forEachChild(node, visit);
397
+ };
398
+ visit(sourceFile);
399
+ return found;
400
+ }
401
+ function importedNames(ts, decl) {
402
+ const bindings = decl.importClause?.namedBindings;
403
+ const names = [];
404
+ if (decl.importClause?.name) names.push(decl.importClause.name.text);
405
+ if (bindings && ts.isNamedImports(bindings)) {
406
+ for (const el of bindings.elements) names.push(el.name.text);
407
+ } else if (bindings && ts.isNamespaceImport(bindings)) {
408
+ names.push(bindings.name.text);
409
+ }
410
+ return names;
411
+ }
412
+
413
+ // src/pieces.ts
414
+ function templatePieces(ts, tpl, sf) {
415
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
416
+ return [{ kind: "static", text: tpl.text, start: tpl.getStart(sf) + 1 }];
417
+ }
418
+ const pieces = [{ kind: "static", text: tpl.head.text, start: tpl.head.getStart(sf) + 1 }];
419
+ tpl.templateSpans.forEach((span, i) => {
420
+ const e = span.expression;
421
+ pieces.push({ kind: "hole", index: i, start: e.getStart(sf), end: e.getEnd() });
422
+ pieces.push({ kind: "static", text: span.literal.text, start: span.literal.getStart(sf) + 1 });
423
+ });
424
+ return pieces;
425
+ }
426
+ function templateAt(ts, sf, pos) {
427
+ let found;
428
+ const visit = (node) => {
429
+ if (found) return;
430
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg") && pos >= node.template.getStart(sf) && pos <= node.template.getEnd()) {
431
+ found = node;
432
+ return;
433
+ }
434
+ ts.forEachChild(node, visit);
435
+ };
436
+ visit(sf);
437
+ return found;
438
+ }
439
+
440
+ // src/hover.ts
441
+ function componentQuickInfo(ts, ls, fileName, position) {
442
+ const program = ls.getProgram();
443
+ const sf = program?.getSourceFile(fileName);
444
+ if (!program || !sf) return void 0;
445
+ const template = templateAt(ts, sf, position);
446
+ if (!template) return void 0;
447
+ const hit = componentTagAt(template, sf, position, ts);
448
+ if (!hit) return void 0;
449
+ const checker = program.getTypeChecker();
450
+ const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
451
+ const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name);
452
+ const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
453
+ if (!decl) return void 0;
454
+ const namePos = decl.name?.getStart(sf) ?? decl.getStart(sf);
455
+ const info = ls.getQuickInfoAtPosition(fileName, namePos);
456
+ if (!info) return void 0;
457
+ return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
458
+ }
459
+ function componentTagAt(template, sf, position, ts) {
460
+ const { root } = k(templatePieces(ts, template.template, sf));
461
+ let hit;
462
+ const walk = (nodes) => {
463
+ for (const node of nodes) {
464
+ if (node.kind === "Component" && node.tag) {
465
+ const openStart = node.loc.start + 1;
466
+ if (within(position, openStart, node.tag.length)) {
467
+ hit = { name: node.tag.split(".")[0], start: openStart };
468
+ }
469
+ }
470
+ if (!hit && "children" in node && node.children) walk(node.children);
471
+ if (hit) return;
472
+ }
473
+ };
474
+ walk(root.children);
475
+ return hit;
476
+ }
477
+ function within(pos, start, length) {
478
+ return pos >= start && pos <= start + length;
479
+ }
480
+
481
+ // src/infer.ts
482
+ var EVENT_TYPES = {
483
+ click: "MouseEvent",
484
+ dblclick: "MouseEvent",
485
+ mousedown: "MouseEvent",
486
+ mouseup: "MouseEvent",
487
+ mousemove: "MouseEvent",
488
+ mouseenter: "MouseEvent",
489
+ mouseleave: "MouseEvent",
490
+ mouseover: "MouseEvent",
491
+ mouseout: "MouseEvent",
492
+ contextmenu: "MouseEvent",
493
+ keydown: "KeyboardEvent",
494
+ keyup: "KeyboardEvent",
495
+ keypress: "KeyboardEvent",
496
+ input: "InputEvent",
497
+ beforeinput: "InputEvent",
498
+ change: "Event",
499
+ submit: "SubmitEvent",
500
+ focus: "FocusEvent",
501
+ blur: "FocusEvent",
502
+ focusin: "FocusEvent",
503
+ focusout: "FocusEvent",
504
+ pointerdown: "PointerEvent",
505
+ pointerup: "PointerEvent",
506
+ pointermove: "PointerEvent",
507
+ pointerenter: "PointerEvent",
508
+ pointerleave: "PointerEvent",
509
+ wheel: "WheelEvent",
510
+ scroll: "Event",
511
+ drag: "DragEvent",
512
+ drop: "DragEvent",
513
+ touchstart: "TouchEvent",
514
+ touchend: "TouchEvent",
515
+ touchmove: "TouchEvent",
516
+ animationend: "AnimationEvent",
517
+ transitionend: "TransitionEvent"
518
+ };
519
+ var TAG_ELEMENTS = {
520
+ a: "HTMLAnchorElement",
521
+ area: "HTMLAreaElement",
522
+ audio: "HTMLAudioElement",
523
+ br: "HTMLBRElement",
524
+ button: "HTMLButtonElement",
525
+ canvas: "HTMLCanvasElement",
526
+ data: "HTMLDataElement",
527
+ datalist: "HTMLDataListElement",
528
+ dialog: "HTMLDialogElement",
529
+ div: "HTMLDivElement",
530
+ dl: "HTMLDListElement",
531
+ embed: "HTMLEmbedElement",
532
+ fieldset: "HTMLFieldSetElement",
533
+ form: "HTMLFormElement",
534
+ h1: "HTMLHeadingElement",
535
+ h2: "HTMLHeadingElement",
536
+ h3: "HTMLHeadingElement",
537
+ h4: "HTMLHeadingElement",
538
+ h5: "HTMLHeadingElement",
539
+ h6: "HTMLHeadingElement",
540
+ hr: "HTMLHRElement",
541
+ iframe: "HTMLIFrameElement",
542
+ img: "HTMLImageElement",
543
+ input: "HTMLInputElement",
544
+ label: "HTMLLabelElement",
545
+ legend: "HTMLLegendElement",
546
+ li: "HTMLLIElement",
547
+ link: "HTMLLinkElement",
548
+ map: "HTMLMapElement",
549
+ menu: "HTMLMenuElement",
550
+ meter: "HTMLMeterElement",
551
+ object: "HTMLObjectElement",
552
+ ol: "HTMLOListElement",
553
+ optgroup: "HTMLOptGroupElement",
554
+ option: "HTMLOptionElement",
555
+ output: "HTMLOutputElement",
556
+ p: "HTMLParagraphElement",
557
+ picture: "HTMLPictureElement",
558
+ pre: "HTMLPreElement",
559
+ progress: "HTMLProgressElement",
560
+ q: "HTMLQuoteElement",
561
+ select: "HTMLSelectElement",
562
+ slot: "HTMLSlotElement",
563
+ source: "HTMLSourceElement",
564
+ span: "HTMLSpanElement",
565
+ table: "HTMLTableElement",
566
+ tbody: "HTMLTableSectionElement",
567
+ td: "HTMLTableCellElement",
568
+ template: "HTMLTemplateElement",
569
+ textarea: "HTMLTextAreaElement",
570
+ tfoot: "HTMLTableSectionElement",
571
+ th: "HTMLTableCellElement",
572
+ thead: "HTMLTableSectionElement",
573
+ time: "HTMLTimeElement",
574
+ tr: "HTMLTableRowElement",
575
+ track: "HTMLTrackElement",
576
+ ul: "HTMLUListElement",
577
+ video: "HTMLVideoElement"
578
+ };
579
+ function holeRoles(nodes, out) {
580
+ const renderChildren = (node, role) => {
581
+ if ("children" in node && node.children) {
582
+ for (const child of node.children) if (child.kind === "Expression") out.set(child.hole, role);
583
+ }
584
+ };
585
+ for (const node of nodes) {
586
+ if (node.kind === "Element" || node.kind === "Component") {
587
+ for (const a of node.attributes) {
588
+ if (a.kind === "EventBinding") out.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
589
+ if (node.kind === "Element" && a.kind === "RefBinding") out.set(a.hole, { kind: "refEl", tag: node.tag });
590
+ if (node.kind === "Element" && a.kind === "UseDirective" && a.hole != null) out.set(a.hole, { kind: "refEl", tag: node.tag });
591
+ }
592
+ const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
593
+ if (eachDir && eachDir.kind === "EachDirective") {
594
+ renderChildren(node, { kind: "each", eachHole: eachDir.hole });
595
+ }
596
+ if (node.kind === "Component") {
597
+ const propHole = (name) => {
598
+ const a = node.attributes.find(
599
+ (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
600
+ );
601
+ return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
602
+ };
603
+ const eachHole = propHole("each");
604
+ if (node.tag === "For" && eachHole !== void 0) renderChildren(node, { kind: "each", eachHole });
605
+ if (node.tag === "Index" && eachHole !== void 0) renderChildren(node, { kind: "indexEach", eachHole });
606
+ const whenHole = propHole("when");
607
+ if (node.tag === "Show" && whenHole !== void 0) renderChildren(node, { kind: "show", whenHole });
608
+ }
609
+ }
610
+ if ("children" in node && node.children) holeRoles(node.children, out);
611
+ }
612
+ }
613
+ function resolveGlobalType(ts, checker, location, name) {
614
+ const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((s) => s.getName() === name);
615
+ return sym ? checker.getDeclaredTypeOfSymbol(sym) : void 0;
616
+ }
617
+ function resolveElementType(ts, checker, location, tag) {
618
+ const map = resolveGlobalType(ts, checker, location, "HTMLElementTagNameMap");
619
+ const prop = map?.getProperty(tag.toLowerCase());
620
+ const t = prop ? checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration ?? location) : void 0;
621
+ return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
622
+ }
623
+ function inferParamAt(ts, program, sf, position) {
624
+ const template = templateAt(ts, sf, position);
625
+ if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
626
+ const spans = template.template.templateSpans;
627
+ const holeExprs = spans.map((s) => s.expression);
628
+ const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
629
+ if (holeIndex === -1) return void 0;
630
+ const roles = /* @__PURE__ */ new Map();
631
+ holeRoles(k(templatePieces(ts, template.template, sf)).root.children, roles);
632
+ const role = roles.get(holeIndex);
633
+ if (!role) return void 0;
634
+ const fn = holeExprs[holeIndex];
635
+ if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) return void 0;
636
+ const param = fn.parameters[0];
637
+ if (!param || !ts.isIdentifier(param.name) || param.type) return void 0;
638
+ const paramName = param.name.text;
639
+ const id = identifierAt(ts, sf, position);
640
+ if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
641
+ const checker = program.getTypeChecker();
642
+ let typeText;
643
+ let type;
644
+ if (role.kind === "event") {
645
+ const name = EVENT_TYPES[role.event] ?? "Event";
646
+ typeText = name;
647
+ type = resolveGlobalType(ts, checker, fn, name);
648
+ } else if (role.kind === "refEl") {
649
+ type = resolveElementType(ts, checker, fn, role.tag);
650
+ typeText = type ? checker.typeToString(type) : TAG_ELEMENTS[role.tag.toLowerCase()] ?? "HTMLElement";
651
+ if (!type) type = resolveGlobalType(ts, checker, fn, typeText);
652
+ } else if (role.kind === "show") {
653
+ type = checker.getNonNullableType(checker.getTypeAtLocation(holeExprs[role.whenHole]));
654
+ typeText = checker.typeToString(type);
655
+ } else if (role.kind === "indexEach") {
656
+ const elem = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
657
+ if (elem) typeText = `() => ${checker.typeToString(elem)}`;
658
+ } else {
659
+ type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
660
+ if (type) typeText = checker.typeToString(type);
661
+ }
662
+ if (!typeText) return void 0;
663
+ return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
664
+ }
665
+ function paramQuickInfo(ts, ls, fileName, position) {
666
+ const program = ls.getProgram();
667
+ const sf = program?.getSourceFile(fileName);
668
+ if (!program || !sf) return void 0;
669
+ const info = inferParamAt(ts, program, sf, position);
670
+ if (!info) return void 0;
671
+ return {
672
+ kind: ts.ScriptElementKind.parameterElement,
673
+ kindModifiers: "",
674
+ textSpan: { start: info.start, length: info.length },
675
+ displayParts: [
676
+ { text: "(", kind: "punctuation" },
677
+ { text: "parameter", kind: "text" },
678
+ { text: ")", kind: "punctuation" },
679
+ { text: " ", kind: "space" },
680
+ { text: info.paramName, kind: "parameterName" },
681
+ { text: ":", kind: "punctuation" },
682
+ { text: " ", kind: "space" },
683
+ { text: info.typeText, kind: "text" }
684
+ ],
685
+ documentation: [{ text: "Inferred by Fluixi from the html`` template.", kind: "text" }]
686
+ };
687
+ }
688
+ function identifierAt(ts, sf, pos) {
689
+ let found;
690
+ const visit = (node) => {
691
+ if (found) return;
692
+ if (pos >= node.getStart(sf) && pos <= node.getEnd()) {
693
+ if (ts.isIdentifier(node)) found = node;
694
+ node.forEachChild(visit);
695
+ }
696
+ };
697
+ sf.forEachChild(visit);
698
+ return found;
699
+ }
700
+ function contains(outer, inner) {
701
+ return inner.getStart() >= outer.getStart() && inner.getEnd() <= outer.getEnd();
702
+ }
703
+
704
+ // src/props.ts
705
+ function componentPropCompletions(ts, ls, fileName, position) {
706
+ const program = ls.getProgram();
707
+ const sf = program?.getSourceFile(fileName);
708
+ if (!program || !sf) return void 0;
709
+ const template = templateAt(ts, sf, position);
710
+ if (!template) return void 0;
711
+ const comp = componentTagAtAttrArea(ts, sf, template.template, position);
712
+ if (!comp || !comp.tag) return void 0;
713
+ const cf = CONTROL_FLOW_PROPS[comp.tag];
714
+ if (cf) {
715
+ const already2 = new Set(comp.attributeNames);
716
+ return cf.filter((n) => !already2.has(n)).map((name) => propEntry(ts, name));
717
+ }
718
+ const checker = program.getTypeChecker();
719
+ const rootName = comp.tag.split(".")[0];
720
+ const decl = findDeclaration(ts, sf, rootName);
721
+ if (!decl) return void 0;
722
+ const compType = checker.getTypeAtLocation(decl);
723
+ const propsType = firstParamType(ts, checker, compType);
724
+ if (!propsType) return void 0;
725
+ const already = new Set(comp.attributeNames);
726
+ const entries = [];
727
+ for (const p2 of propsType.getProperties()) {
728
+ const name = p2.getName();
729
+ if (name === "children" || already.has(name)) continue;
730
+ entries.push(propEntry(ts, name));
731
+ }
732
+ return entries.length ? entries : void 0;
733
+ }
734
+ function componentPropHover(ts, ls, fileName, position) {
735
+ const program = ls.getProgram();
736
+ const sf = program?.getSourceFile(fileName);
737
+ if (!program || !sf) return void 0;
738
+ const template = templateAt(ts, sf, position);
739
+ if (!template) return void 0;
740
+ const hit = propNameAt(ts, sf, template.template, position);
741
+ if (!hit) return void 0;
742
+ let typeText;
743
+ const cf = CONTROL_FLOW_PROP_TYPES[hit.tag];
744
+ if (cf) {
745
+ typeText = cf[hit.prop];
746
+ if (!typeText) return void 0;
747
+ } else if (hit.tag) {
748
+ const checker = program.getTypeChecker();
749
+ const decl = findDeclaration(ts, sf, hit.tag.split(".")[0]);
750
+ const propsType = decl && firstParamType(ts, checker, checker.getTypeAtLocation(decl));
751
+ const sym = propsType && propsType.getProperty(hit.prop);
752
+ if (!sym) return void 0;
753
+ typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? decl));
754
+ }
755
+ if (!typeText) return void 0;
756
+ return {
757
+ kind: ts.ScriptElementKind.memberVariableElement,
758
+ kindModifiers: "",
759
+ textSpan: { start: hit.start, length: hit.prop.length },
760
+ displayParts: [
761
+ { text: "(", kind: "punctuation" },
762
+ { text: "property", kind: "text" },
763
+ { text: ")", kind: "punctuation" },
764
+ { text: " ", kind: "space" },
765
+ { text: hit.prop, kind: "propertyName" },
766
+ { text: ":", kind: "punctuation" },
767
+ { text: " ", kind: "space" },
768
+ { text: typeText, kind: "text" }
769
+ ],
770
+ documentation: []
771
+ };
772
+ }
773
+ function propNameAt(ts, sf, tpl, position) {
774
+ const { root } = k(templatePieces(ts, tpl, sf));
775
+ let hit;
776
+ const walk = (nodes) => {
777
+ for (const node of nodes) {
778
+ if (node.kind === "Component") {
779
+ const c = node;
780
+ for (const a of c.attributes) {
781
+ if ((a.kind === "Attribute" || a.kind === "PropertyBinding" || a.kind === "BindDirective") && "loc" in a) {
782
+ const start = a.loc.start;
783
+ if (position >= start && position <= start + a.name.length) hit = { tag: c.tag, prop: a.name, start };
784
+ }
785
+ }
786
+ }
787
+ if (!hit && "children" in node && node.children) walk(node.children);
788
+ if (hit) return;
789
+ }
790
+ };
791
+ walk(root.children);
792
+ return hit;
793
+ }
794
+ var CONTROL_FLOW_PROP_TYPES = {
795
+ Show: { when: "T", fallback: "JSX.Element", keyed: "boolean" },
796
+ For: { each: "readonly T[] | undefined | null", fallback: "JSX.Element", by: "(item: T) => unknown" },
797
+ Index: { each: "readonly T[] | undefined | null", fallback: "JSX.Element" },
798
+ Switch: { fallback: "JSX.Element" },
799
+ Match: { when: "T" },
800
+ Suspense: { fallback: "JSX.Element" },
801
+ SuspenseList: { revealOrder: '"forwards" | "backwards" | "together"', tail: '"collapsed" | "hidden"' },
802
+ Portal: { mount: "Node", useShadow: "boolean", isSVG: "boolean" },
803
+ ErrorBoundary: { fallback: "JSX.Element | ((err: unknown, reset: () => void) => JSX.Element)" },
804
+ Dynamic: { component: "Component | string" },
805
+ Await: { value: "Promise<T> | (() => Promise<T>)", fallback: "JSX.Element" }
806
+ };
807
+ var CONTROL_FLOW_PROPS = {
808
+ Show: ["when", "fallback", "keyed"],
809
+ For: ["each", "fallback", "by"],
810
+ Index: ["each", "fallback"],
811
+ Switch: ["fallback"],
812
+ Match: ["when"],
813
+ Suspense: ["fallback"],
814
+ SuspenseList: ["revealOrder", "tail"],
815
+ Portal: ["mount", "useShadow", "isSVG"],
816
+ ErrorBoundary: ["fallback"],
817
+ Dynamic: ["component"],
818
+ Await: ["value", "fallback"]
819
+ };
820
+ function propEntry(ts, name) {
821
+ return { name, kind: ts.ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: "0_" + name, insertText: name };
822
+ }
823
+ function findDeclaration(ts, sf, name) {
824
+ for (const stmt of sf.statements) {
825
+ if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && stmt.name?.text === name) return stmt.name;
826
+ if (ts.isVariableStatement(stmt)) {
827
+ for (const d of stmt.declarationList.declarations) {
828
+ if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
829
+ }
830
+ }
831
+ if (ts.isImportDeclaration(stmt)) {
832
+ const b2 = stmt.importClause?.namedBindings;
833
+ if (b2 && ts.isNamedImports(b2)) {
834
+ for (const el of b2.elements) if (el.name.text === name) return el.name;
835
+ }
836
+ if (stmt.importClause?.name?.text === name) return stmt.importClause.name;
837
+ }
838
+ }
839
+ return void 0;
840
+ }
841
+ function firstParamType(ts, checker, type) {
842
+ for (const sig of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
843
+ const p2 = sig.getParameters()[0];
844
+ if (p2) return checker.getTypeOfSymbolAtLocation(p2, p2.valueDeclaration ?? sig.getDeclaration());
845
+ }
846
+ return void 0;
847
+ }
848
+ function componentTagAtAttrArea(ts, sf, tpl, position) {
849
+ const { root } = k(templatePieces(ts, tpl, sf));
850
+ let hit;
851
+ const walk = (nodes) => {
852
+ for (const node of nodes) {
853
+ if (node.kind === "Component") {
854
+ const c = node;
855
+ const openStart = c.loc.start + 1 + (c.tag ? c.tag.length : 0);
856
+ const openEnd = firstChildOrCloseStart(c);
857
+ if (position > openStart && position <= openEnd) {
858
+ hit = { tag: c.tag, attributeNames: attributeNames(c) };
859
+ }
860
+ }
861
+ if (!hit && "children" in node && node.children) walk(node.children);
862
+ if (hit) return;
863
+ }
864
+ };
865
+ walk(root.children);
866
+ return hit;
867
+ }
868
+ function firstChildOrCloseStart(c) {
869
+ if (c.selfClosing) return c.loc.end;
870
+ const first = c.children[0];
871
+ return first ? first.loc.start : c.loc.end;
872
+ }
873
+ function attributeNames(c) {
874
+ const out = [];
875
+ for (const a of c.attributes) {
876
+ if (a.kind === "Attribute" || a.kind === "PropertyBinding" || a.kind === "BindDirective") out.push(a.name);
877
+ }
878
+ return out;
879
+ }
880
+
881
+ // src/completion.ts
882
+ function paramMemberCompletions(ts, ls, fileName, position) {
883
+ const program = ls.getProgram();
884
+ const sf = program?.getSourceFile(fileName);
885
+ if (!program || !sf) return void 0;
886
+ const access = propertyAccessAt(ts, sf, position);
887
+ if (!access || !ts.isIdentifier(access.expression)) return void 0;
888
+ const info = inferParamAt(ts, program, sf, access.expression.getStart(sf));
889
+ if (!info || info.paramName !== access.expression.text || !info.type) return void 0;
890
+ const checker = program.getTypeChecker();
891
+ const already = /* @__PURE__ */ new Set();
892
+ const entries = [];
893
+ for (const p2 of checker.getApparentType(info.type).getProperties()) {
894
+ const name = p2.getName();
895
+ if (already.has(name) || name.startsWith("__")) continue;
896
+ already.add(name);
897
+ entries.push({
898
+ name,
899
+ kind: memberKind(ts, p2),
900
+ kindModifiers: "",
901
+ sortText: "0_" + name,
902
+ insertText: name
903
+ });
904
+ }
905
+ return entries.length ? entries : void 0;
906
+ }
907
+ function memberKind(ts, sym) {
908
+ if (sym.flags & ts.SymbolFlags.Method) return ts.ScriptElementKind.memberFunctionElement;
909
+ return ts.ScriptElementKind.memberVariableElement;
910
+ }
911
+ function propertyAccessAt(ts, sf, position) {
912
+ let found;
913
+ const visit = (node) => {
914
+ if (position < node.getFullStart() || position > node.getEnd()) return;
915
+ if (ts.isPropertyAccessExpression(node) && position > node.expression.getEnd() && position <= node.getEnd()) {
916
+ found = node;
917
+ }
918
+ node.forEachChild(visit);
919
+ };
920
+ sf.forEachChild(visit);
921
+ return found;
922
+ }
923
+
924
+ // src/virtual.ts
925
+ var EVENT_MAP_KEYS = new Set(Object.keys({
926
+ click: 0,
927
+ dblclick: 0,
928
+ mousedown: 0,
929
+ mouseup: 0,
930
+ mousemove: 0,
931
+ mouseenter: 0,
932
+ mouseleave: 0,
933
+ mouseover: 0,
934
+ mouseout: 0,
935
+ contextmenu: 0,
936
+ keydown: 0,
937
+ keyup: 0,
938
+ keypress: 0,
939
+ input: 0,
940
+ beforeinput: 0,
941
+ change: 0,
942
+ submit: 0,
943
+ focus: 0,
944
+ blur: 0,
945
+ focusin: 0,
946
+ focusout: 0,
947
+ pointerdown: 0,
948
+ pointerup: 0,
949
+ pointermove: 0,
950
+ pointerenter: 0,
951
+ pointerleave: 0,
952
+ wheel: 0,
953
+ scroll: 0,
954
+ drag: 0,
955
+ drop: 0,
956
+ touchstart: 0,
957
+ touchend: 0,
958
+ touchmove: 0,
959
+ animationend: 0,
960
+ transitionend: 0
961
+ }));
962
+ var PRELUDE = (
963
+ // Control-flow component prop types, declared inline so they ALWAYS resolve
964
+ // (no dependency on @fluixi/core being importable from the virtual document).
965
+ // Prop shapes drive hover + value checking on when/each/fallback/mount/…;
966
+ // render-prop children are typed separately by __fxEach/__fxShow.
967
+ 'declare const __fxCF: {\n Show: <T>(props: { when: T; fallback?: unknown; children?: unknown }) => any;\n For: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; by?: (item: T) => unknown; children?: unknown }) => any;\n Index: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; children?: unknown }) => any;\n Switch: (props: { fallback?: unknown; children?: unknown }) => any;\n Match: <T>(props: { when: T; children?: unknown }) => any;\n Suspense: (props: { fallback?: unknown; children?: unknown }) => any;\n SuspenseList: (props: { revealOrder: "forwards" | "backwards" | "together"; children?: unknown }) => any;\n Portal: (props: { mount?: Node; useShadow?: boolean; children?: unknown }) => any;\n ErrorBoundary: (props: { fallback?: unknown; children?: unknown }) => any;\n Dynamic: (props: { component: unknown; [k: string]: unknown }) => any;\n Await: <T>(props: { value: Promise<T> | (() => Promise<T>); fallback?: unknown; children?: unknown }) => any;\n};\ndeclare const __fxEach: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: T, index: () => number) => any) => any;\ndeclare const __fxIndex: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: () => T, index: number) => any) => any;\ndeclare const __fxShow: <T>(when: T, render: (value: NonNullable<T>) => any) => any;\ndeclare const __fxOn: <K extends string>(name: K, handler: (event: K extends keyof HTMLElementEventMap ? HTMLElementEventMap[K] : Event) => any) => any;\ndeclare const __fxRef: <K extends string>(tag: K, ref: (el: K extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[K] : HTMLElement) => any) => any;\ndeclare const __fxExpr: (value: any) => any;\ndeclare function __fxProps<P>(comp: (props: P, ...rest: any[]) => any, props: NoInfer<P>): any;\n'
968
+ );
969
+ var CONTROL_FLOW = /* @__PURE__ */ new Set([
970
+ "Show",
971
+ "For",
972
+ "Index",
973
+ "Switch",
974
+ "Match",
975
+ "Dynamic",
976
+ "ErrorBoundary",
977
+ "Suspense",
978
+ "SuspenseList",
979
+ "Await",
980
+ "Portal"
981
+ ]);
982
+ function inHole(segments, pos) {
983
+ return segments.some((s) => s.hole && pos >= s.srcStart && pos <= s.srcEnd);
984
+ }
985
+ function inKey(segments, pos) {
986
+ return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
987
+ }
988
+ function toGenerated(segments, pos) {
989
+ for (const s of segments) {
990
+ if (pos >= s.srcStart && pos <= s.srcEnd) return s.genStart + (pos - s.srcStart);
991
+ }
992
+ return -1;
993
+ }
994
+ function toSource(segments, pos) {
995
+ for (const s of segments) {
996
+ if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
997
+ }
998
+ return -1;
999
+ }
1000
+ function buildVirtual(ts, sf) {
1001
+ const templates = collectTemplates(ts, sf);
1002
+ if (templates.length === 0) return void 0;
1003
+ const srcText = sf.text;
1004
+ const segments = [];
1005
+ let gen = "";
1006
+ let src = 0;
1007
+ const copy = (to) => {
1008
+ if (to <= src) return;
1009
+ const genStart = gen.length;
1010
+ gen += srcText.slice(src, to);
1011
+ segments.push({ srcStart: src, srcEnd: to, genStart, genEnd: gen.length });
1012
+ src = to;
1013
+ };
1014
+ gen += PRELUDE;
1015
+ for (const tpl of templates) {
1016
+ copy(tpl.node.getStart(sf));
1017
+ gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
1018
+ src = tpl.node.getEnd();
1019
+ }
1020
+ copy(srcText.length);
1021
+ return { text: gen, segments };
1022
+ }
1023
+ function isFn(ts, node) {
1024
+ return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
1025
+ }
1026
+ function collectTemplates(ts, sf) {
1027
+ const out = [];
1028
+ const visit = (node) => {
1029
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
1030
+ const tpl = node.template;
1031
+ const holes = ts.isTemplateExpression(tpl) ? tpl.templateSpans.map((s) => s.expression) : [];
1032
+ out.push({ node, holes });
1033
+ }
1034
+ ts.forEachChild(node, visit);
1035
+ };
1036
+ visit(sf);
1037
+ return out;
1038
+ }
1039
+ function analyze(nodes, roles, consumed) {
1040
+ const renderChild = (node) => {
1041
+ if (!("children" in node) || !node.children) return void 0;
1042
+ const expr = node.children.find((c) => c.kind === "Expression");
1043
+ return expr && expr.kind === "Expression" ? expr.hole : void 0;
1044
+ };
1045
+ for (const node of nodes) {
1046
+ if (node.kind === "Element" || node.kind === "Component") {
1047
+ for (const a of node.attributes) {
1048
+ if (a.kind === "EventBinding") roles.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
1049
+ if (a.kind === "RefBinding" && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1050
+ if (a.kind === "UseDirective" && a.hole != null && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1051
+ }
1052
+ const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
1053
+ const propHole = (name) => {
1054
+ const a = node.attributes.find(
1055
+ (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
1056
+ );
1057
+ return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
1058
+ };
1059
+ const wire = (arrayHole, kind) => {
1060
+ const r = renderChild(node);
1061
+ if (r !== void 0) {
1062
+ roles.set(r, { kind, arrayHole });
1063
+ consumed.add(arrayHole);
1064
+ }
1065
+ };
1066
+ if (eachDir && eachDir.kind === "EachDirective") wire(eachDir.hole, "eachRender");
1067
+ else if (node.kind === "Component" && node.tag === "For") {
1068
+ const h = propHole("each");
1069
+ if (h !== void 0) wire(h, "eachRender");
1070
+ } else if (node.kind === "Component" && node.tag === "Index") {
1071
+ const h = propHole("each");
1072
+ if (h !== void 0) wire(h, "indexRender");
1073
+ } else if (node.kind === "Component" && node.tag === "Show") {
1074
+ const w = propHole("when");
1075
+ const r = renderChild(node);
1076
+ if (w !== void 0 && r !== void 0) {
1077
+ roles.set(r, { kind: "showRender", whenHole: w });
1078
+ consumed.add(w);
1079
+ }
1080
+ }
1081
+ }
1082
+ if ("children" in node && node.children) analyze(node.children, roles, consumed);
1083
+ }
1084
+ }
1085
+ function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
1086
+ const roles = /* @__PURE__ */ new Map();
1087
+ const consumed = /* @__PURE__ */ new Set();
1088
+ const { root } = k(templatePieces(ts, tpl.node.template, sf));
1089
+ analyze(root.children, roles, consumed);
1090
+ const holeSrc = (i) => [tpl.holes[i].getStart(sf), tpl.holes[i].getEnd()];
1091
+ const putHole = (i) => {
1092
+ const [s, e] = holeSrc(i);
1093
+ const genStart = gen.length;
1094
+ gen += srcText.slice(s, e);
1095
+ segments.push({ srcStart: s, srcEnd: e, genStart, genEnd: gen.length, hole: true });
1096
+ };
1097
+ const isIdent = (t) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t);
1098
+ const putKey = (start, text) => {
1099
+ const genStart = gen.length;
1100
+ if (isIdent(text)) {
1101
+ gen += text;
1102
+ segments.push({ srcStart: start, srcEnd: start + text.length, genStart, genEnd: gen.length, key: true });
1103
+ } else {
1104
+ gen += JSON.stringify(text);
1105
+ }
1106
+ };
1107
+ const calls = [];
1108
+ const walkComponents = (nodes) => {
1109
+ for (const node of nodes) {
1110
+ if (node.kind === "Component") calls.push(componentPropsEmitter(node));
1111
+ if ("children" in node && node.children) walkComponents(node.children);
1112
+ }
1113
+ };
1114
+ function componentPropsEmitter(node) {
1115
+ return () => {
1116
+ gen += "__fxProps(";
1117
+ if (node.tagHole != null) putHole(node.tagHole);
1118
+ else if (CONTROL_FLOW.has(node.tag)) gen += `__fxCF.${node.tag}`;
1119
+ else gen += node.tag;
1120
+ gen += ", { ";
1121
+ let first = true;
1122
+ const entry = (keyStart, keyText, emitValue) => {
1123
+ if (!first) gen += ", ";
1124
+ first = false;
1125
+ putKey(keyStart, keyText);
1126
+ gen += ": ";
1127
+ emitValue();
1128
+ };
1129
+ for (const a of node.attributes) {
1130
+ if (a.kind === "Attribute") {
1131
+ const keyStart = a.boolean ? a.loc.start + 1 : a.loc.start;
1132
+ const v2 = a.value;
1133
+ entry(keyStart, a.name, () => {
1134
+ if (!v2) {
1135
+ gen += "true";
1136
+ } else if (v2.kind === "static") {
1137
+ gen += JSON.stringify(v2.value);
1138
+ } else if (v2.kind === "hole") {
1139
+ putHole(v2.hole);
1140
+ } else {
1141
+ gen += "undefined as any";
1142
+ }
1143
+ });
1144
+ } else if (a.kind === "PropertyBinding") {
1145
+ entry(a.loc.start + 1, a.name, () => putHole(a.hole));
1146
+ }
1147
+ }
1148
+ gen += " })";
1149
+ };
1150
+ }
1151
+ walkComponents(root.children);
1152
+ for (let i = 0; i < tpl.holes.length; i++) {
1153
+ if (consumed.has(i)) continue;
1154
+ const role = roles.get(i);
1155
+ if (role && role.kind === "eachRender") {
1156
+ calls.push(() => {
1157
+ gen += "__fxEach(";
1158
+ putHole(role.arrayHole);
1159
+ gen += ", ";
1160
+ putHole(i);
1161
+ gen += ")";
1162
+ });
1163
+ } else if (role && role.kind === "indexRender") {
1164
+ calls.push(() => {
1165
+ gen += "__fxIndex(";
1166
+ putHole(role.arrayHole);
1167
+ gen += ", ";
1168
+ putHole(i);
1169
+ gen += ")";
1170
+ });
1171
+ } else if (role && role.kind === "showRender") {
1172
+ calls.push(() => {
1173
+ gen += "__fxShow(";
1174
+ putHole(role.whenHole);
1175
+ gen += ", ";
1176
+ putHole(i);
1177
+ gen += ")";
1178
+ });
1179
+ } else if (role && role.kind === "event") {
1180
+ const key = EVENT_MAP_KEYS.has(role.event) ? role.event : role.event;
1181
+ calls.push(() => {
1182
+ gen += `__fxOn(${JSON.stringify(key)}, `;
1183
+ putHole(i);
1184
+ gen += ")";
1185
+ });
1186
+ } else if (role && role.kind === "ref" && isFn(ts, tpl.holes[i])) {
1187
+ const tag = role.tag;
1188
+ calls.push(() => {
1189
+ gen += `__fxRef(${JSON.stringify(tag)}, `;
1190
+ putHole(i);
1191
+ gen += ")";
1192
+ });
1193
+ } else {
1194
+ calls.push(() => {
1195
+ gen += "__fxExpr(";
1196
+ putHole(i);
1197
+ gen += ")";
1198
+ });
1199
+ }
1200
+ }
1201
+ gen += "(";
1202
+ calls.forEach((emit, idx) => {
1203
+ if (idx > 0) gen += ", ";
1204
+ emit();
1205
+ });
1206
+ gen += calls.length > 0 ? ", undefined as any)" : "undefined as any)";
1207
+ return gen;
1208
+ }
1209
+
1210
+ // src/virtual-service.ts
1211
+ var VirtualService = class {
1212
+ constructor(ts, host) {
1213
+ this.ts = ts;
1214
+ this.host = host;
1215
+ const self = this;
1216
+ const virtualHost = new Proxy(host, {
1217
+ get(target, prop, receiver) {
1218
+ if (prop === "getScriptSnapshot") {
1219
+ return (fileName) => {
1220
+ const v2 = self.virtual(fileName);
1221
+ return v2 ? self.ts.ScriptSnapshot.fromString(v2.text) : target.getScriptSnapshot(fileName);
1222
+ };
1223
+ }
1224
+ const value = Reflect.get(target, prop, receiver);
1225
+ return typeof value === "function" ? value.bind(target) : value;
1226
+ }
1227
+ });
1228
+ this.ls = ts.createLanguageService(virtualHost, ts.createDocumentRegistry());
1229
+ }
1230
+ ls;
1231
+ /** Cached per file: the virtual text + segments, keyed by the file's version. */
1232
+ cache = /* @__PURE__ */ new Map();
1233
+ /** The virtual doc for a file (built + cached per source version), if it has templates. */
1234
+ virtual(fileName) {
1235
+ const version = this.host.getScriptVersion(fileName);
1236
+ const hit = this.cache.get(fileName);
1237
+ if (hit && hit.version === version) return hit.text ? hit : void 0;
1238
+ const snap = this.host.getScriptSnapshot(fileName);
1239
+ if (!snap) return void 0;
1240
+ const src = snap.getText(0, snap.getLength());
1241
+ const sf = this.ts.createSourceFile(fileName, src, this.ts.ScriptTarget.Latest, true, scriptKind(this.ts, fileName));
1242
+ const v2 = buildVirtual(this.ts, sf);
1243
+ this.cache.set(fileName, { version, text: v2?.text ?? "", segments: v2?.segments ?? [] });
1244
+ return v2;
1245
+ }
1246
+ segmentsFor(fileName) {
1247
+ return this.virtual(fileName)?.segments;
1248
+ }
1249
+ service() {
1250
+ return this.ls;
1251
+ }
1252
+ toGen(fileName, pos) {
1253
+ const segs = this.virtual(fileName)?.segments;
1254
+ return segs ? toGenerated(segs, pos) : -1;
1255
+ }
1256
+ toSrc(fileName, pos) {
1257
+ const segs = this.virtual(fileName)?.segments;
1258
+ return segs ? toSource(segs, pos) : -1;
1259
+ }
1260
+ /** True when a SOURCE position lies inside an embedded `${…}` hole. */
1261
+ inHole(fileName, pos) {
1262
+ const segs = this.virtual(fileName)?.segments;
1263
+ return segs ? inHole(segs, pos) : false;
1264
+ }
1265
+ /** True when a SOURCE position lies on a mapped component prop-name key. */
1266
+ inKey(fileName, pos) {
1267
+ const segs = this.virtual(fileName)?.segments;
1268
+ return segs ? inKey(segs, pos) : false;
1269
+ }
1270
+ };
1271
+ function scriptKind(ts, fileName) {
1272
+ if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
1273
+ if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
1274
+ if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) return ts.ScriptKind.JS;
1275
+ return ts.ScriptKind.TS;
1276
+ }
1277
+
1278
+ // src/bridge.ts
1279
+ function mapDiagnostic(vs, fileName, d) {
1280
+ if (d.start === void 0) return d;
1281
+ const start = vs.toSrc(fileName, d.start);
1282
+ if (start === -1) return void 0;
1283
+ return { ...d, start };
1284
+ }
1285
+
1286
+ // src/index.ts
1287
+ function init(modules) {
1288
+ const ts = modules.typescript;
1289
+ return {
1290
+ create(info) {
1291
+ const ls = info.languageService;
1292
+ let vs;
1293
+ try {
1294
+ vs = new VirtualService(ts, info.languageServiceHost);
1295
+ } catch {
1296
+ vs = void 0;
1297
+ }
1298
+ const proxy = /* @__PURE__ */ Object.create(null);
1299
+ for (const key of Object.keys(ls)) {
1300
+ const member = ls[key];
1301
+ proxy[key] = typeof member === "function" ? member.bind(ls) : member;
1302
+ }
1303
+ const completionList = (entries) => ({
1304
+ isGlobalCompletion: false,
1305
+ isMemberCompletion: false,
1306
+ isNewIdentifierLocation: true,
1307
+ entries
1308
+ });
1309
+ const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
1310
+ const reliableFilter = (fileName, diagnostics) => {
1311
+ const program = ls.getProgram();
1312
+ const sourceFile = program?.getSourceFile(fileName);
1313
+ if (!program || !sourceFile) return diagnostics;
1314
+ const used = usedComponentTags(ts, sourceFile);
1315
+ return diagnostics.filter((d) => {
1316
+ if (used.size > 0 && shouldSuppress(ts, sourceFile, d, used)) return false;
1317
+ if (IMPLICIT_ANY.has(d.code) && d.start !== void 0 && inferParamAt(ts, program, sourceFile, d.start)) return false;
1318
+ return true;
1319
+ });
1320
+ };
1321
+ const holeDiagnostics = (fileName, get) => {
1322
+ if (!vs || !vs.segmentsFor(fileName)) return [];
1323
+ try {
1324
+ const sourceFile = ls.getProgram()?.getSourceFile(fileName);
1325
+ const out = [];
1326
+ for (const d of get(fileName)) {
1327
+ if (d.start === void 0) continue;
1328
+ const m2 = mapDiagnostic(vs, fileName, d);
1329
+ if (m2 && m2.start !== void 0 && (vs.inHole(fileName, m2.start) || vs.inKey(fileName, m2.start))) {
1330
+ out.push({ ...m2, file: sourceFile });
1331
+ }
1332
+ }
1333
+ return out;
1334
+ } catch {
1335
+ return [];
1336
+ }
1337
+ };
1338
+ proxy.getSemanticDiagnostics = (fileName) => {
1339
+ const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1340
+ return base.concat(holeDiagnostics(fileName, (f) => vs.service().getSemanticDiagnostics(f)));
1341
+ };
1342
+ proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1343
+ proxy.getQuickInfoAtPosition = (fileName, position) => {
1344
+ try {
1345
+ const prop = componentPropHover(ts, ls, fileName, position);
1346
+ if (prop) return prop;
1347
+ } catch {
1348
+ }
1349
+ try {
1350
+ const inferred = paramQuickInfo(ts, ls, fileName, position);
1351
+ if (inferred) return inferred;
1352
+ } catch {
1353
+ }
1354
+ return ls.getQuickInfoAtPosition(fileName, position) ?? componentQuickInfo(ts, ls, fileName, position);
1355
+ };
1356
+ proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
1357
+ try {
1358
+ const props = componentPropCompletions(ts, ls, fileName, position);
1359
+ if (props) return completionList(props);
1360
+ } catch {
1361
+ }
1362
+ try {
1363
+ const members = paramMemberCompletions(ts, ls, fileName, position);
1364
+ if (members) return completionList(members);
1365
+ } catch {
1366
+ }
1367
+ return ls.getCompletionsAtPosition(fileName, position, options, formatting);
1368
+ };
1369
+ return proxy;
1370
+ }
1371
+ };
1372
+ }
1373
+ module.exports = init;