@fluixi/ts-plugin 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (2) hide show
  1. package/dist/index.js +829 -668
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -1,36 +1,36 @@
1
1
  "use strict";
2
2
 
3
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);
4
+ function x(e) {
5
+ let t = (i2, n) => {
6
+ i2.parent = n;
7
+ let r = R(i2);
8
+ for (let s2 of r) t(s2, i2);
9
9
  };
10
- return e(t, null), t;
10
+ return t(e, null), e;
11
11
  }
12
- function C(t) {
13
- switch (t.kind) {
12
+ function R(e) {
13
+ switch (e.kind) {
14
14
  case "Root":
15
15
  case "Element":
16
16
  case "Component":
17
17
  case "Fragment":
18
- return t.children;
18
+ return e.children;
19
19
  default:
20
20
  return [];
21
21
  }
22
22
  }
23
- var u = class {
24
- constructor(e) {
25
- this.pieces = e;
23
+ var m = class {
24
+ constructor(t) {
25
+ this.pieces = t;
26
26
  this.pi = 0;
27
27
  this.oi = 0;
28
28
  this.normalize();
29
29
  }
30
30
  normalize() {
31
31
  for (; this.pi < this.pieces.length; ) {
32
- let e = this.pieces[this.pi];
33
- if (e.kind === "static" && this.oi >= e.text.length) {
32
+ let t = this.pieces[this.pi];
33
+ if (t.kind === "static" && this.oi >= t.text.length) {
34
34
  this.pi++, this.oi = 0;
35
35
  continue;
36
36
  }
@@ -42,132 +42,172 @@ var u = class {
42
42
  }
43
43
  pos() {
44
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;
45
+ let i2 = this.pieces[this.pieces.length - 1];
46
+ return i2 ? i2.kind === "static" ? i2.start + i2.text.length : i2.end : 0;
47
47
  }
48
- let e = this.pieces[this.pi];
49
- return e.kind === "static" ? e.start + this.oi : e.start;
48
+ let t = this.pieces[this.pi];
49
+ return t.kind === "static" ? t.start + this.oi : t.start;
50
50
  }
51
51
  atHole() {
52
52
  return !this.eof() && this.pieces[this.pi].kind === "hole";
53
53
  }
54
54
  holeFollows() {
55
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";
56
+ let t = this.pieces[this.pi];
57
+ if (t.kind !== "static" || this.oi + 1 < t.text.length) return false;
58
+ let i2 = this.pieces[this.pi + 1];
59
+ return i2 !== void 0 && i2.kind === "hole";
60
60
  }
61
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 } };
62
+ let t = this.pieces[this.pi];
63
+ if (t.kind !== "hole") throw new Error("takeHole called off a hole");
64
+ return this.pi++, this.oi = 0, this.normalize(), { index: t.index, loc: { start: t.start, end: t.end } };
65
65
  }
66
- peek(e = 0) {
66
+ peek(t = 0) {
67
67
  if (this.eof()) return "";
68
- let i = this.pieces[this.pi];
69
- return i.kind !== "static" ? "" : i.text[this.oi + e] ?? "";
68
+ let i2 = this.pieces[this.pi];
69
+ return i2.kind !== "static" ? "" : i2.text[this.oi + t] ?? "";
70
70
  }
71
- startsWith(e) {
71
+ startsWith(t) {
72
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);
73
+ let i2 = this.pieces[this.pi];
74
+ return i2.kind !== "static" ? false : i2.text.startsWith(t, this.oi);
75
75
  }
76
76
  advance() {
77
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;
78
+ let t = this.pieces[this.pi];
79
+ if (t.kind !== "static") return "";
80
+ let i2 = t.text[this.oi++] ?? "";
81
+ return this.normalize(), i2;
82
82
  }
83
- readWhile(e) {
84
- let i = "";
83
+ readWhile(t) {
84
+ let i2 = "";
85
85
  for (; !this.eof() && !this.atHole(); ) {
86
- let r = this.peek();
87
- if (r === "" || !e(r)) break;
88
- i += r, this.oi++;
86
+ let n = this.peek();
87
+ if (n === "" || !t(n)) break;
88
+ i2 += n, this.oi++;
89
89
  }
90
- return this.normalize(), i;
90
+ return this.normalize(), i2;
91
91
  }
92
92
  skipWhitespace() {
93
- this.readWhile((e) => /\s/.test(e));
93
+ this.readWhile((t) => /\s/.test(t));
94
94
  }
95
- span(e) {
96
- return { start: e, end: this.pos() };
95
+ span(t) {
96
+ return { start: t, end: this.pos() };
97
97
  }
98
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) {
99
+ var y = /* @__PURE__ */ new Set(["capture", "once", "passive", "prevent", "stop", "self"]);
100
+ function p(e, t) {
101
+ return e.value && e.value.kind === "hole" ? e.value.hole : (e.report("invalid-binding", `${t} requires an expression value: ${e.name}=\${\u2026}`), -1);
102
+ }
103
+ var D = (e, t) => ({ kind: "Attribute", name: t ? e.name.slice(1) : e.name, value: e.value, boolean: t, loc: e.loc });
104
+ var f = [{ id: "event", match: (e) => e[0] === "@" || e.startsWith("on:") || /^on[A-Z]/.test(e), build: (e) => {
105
+ let t, i2;
106
+ e.name[0] === "@" ? (t = "at", i2 = e.name.slice(1)) : e.name.startsWith("on:") ? (t = "colon", i2 = e.name.slice(3)) : (t = "on", i2 = e.name.slice(2));
107
+ let [n, ...r] = i2.split("."), s2 = n.toLowerCase();
108
+ for (let o of r) y.has(o) || e.report("invalid-directive", `Unknown event modifier ".${o}" \u2014 expected one of ${[...y].join(", ")}`);
109
+ return { kind: "EventBinding", name: s2, syntax: t, modifiers: r, hole: p(e, "event binding"), loc: e.loc };
110
+ } }, { id: "bind", elementOnly: true, match: (e) => e.startsWith("bind:"), build: (e) => ({ kind: "BindDirective", name: e.name.slice(5), hole: p(e, "two-way binding"), loc: e.loc }) }, { id: "class-directive", elementOnly: true, match: (e) => e.startsWith("class:"), build: (e) => ({ kind: "ClassDirective", name: e.name.slice(6), hole: p(e, "class directive"), loc: e.loc }) }, { id: "style-directive", elementOnly: true, match: (e) => e.startsWith("style:"), build: (e) => ({ kind: "StyleDirective", name: e.name.slice(6), hole: p(e, "style directive"), loc: e.loc }) }, { id: "load", match: (e) => e.startsWith("load:"), build: (e) => ({ kind: "LoadDirective", strategy: e.name.slice(5), modifier: e.value && e.value.kind === "static" ? e.value.value : null, loc: e.loc }) }, { id: "use", match: (e) => e === "use" || e.startsWith("use:"), build: (e) => ({ kind: "UseDirective", name: e.name.startsWith("use:") ? e.name.slice(4) : null, hole: e.value && e.value.kind === "hole" ? e.value.hole : null, loc: e.loc }) }, { id: "if", elementOnly: true, match: (e) => e === "if", build: (e) => ({ kind: "IfDirective", hole: p(e, "if directive"), loc: e.loc }) }, { id: "each", elementOnly: true, match: (e) => e === "each", build: (e) => ({ kind: "EachDirective", hole: p(e, "each directive"), key: null, loc: e.loc }) }, { id: "else", elementOnly: true, match: (e) => e === "else", build: (e) => (e.value !== null && e.report("invalid-directive", "`else` takes no value"), { kind: "ElseDirective", loc: e.loc }) }, { id: "ref", match: (e) => e === "ref", build: (e) => ({ kind: "RefBinding", hole: p(e, "ref binding"), loc: e.loc }) }, { id: "property", match: (e) => e[0] === ".", build: (e) => ({ kind: "PropertyBinding", name: e.name.slice(1), hole: p(e, "property binding"), loc: e.loc }) }, { id: "boolean", match: (e) => e[0] === "?", build: (e) => D(e, true) }];
111
+ function k(e, t = f, i2 = { component: false }) {
112
+ for (let n of t) if (!(n.elementOnly && i2.component) && n.match(e.name)) return n.build(e);
113
+ return D(e, false);
114
+ }
115
+ var E = /* @__PURE__ */ new Set(["svg", "path", "circle", "rect", "line", "polygon", "polyline", "ellipse", "g", "defs", "clipPath", "text"]);
116
+ var S = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
117
+ var T = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
118
+ function C(e) {
119
+ return e.length > 0 && (e[0] !== e[0].toLowerCase() || e.includes("."));
120
+ }
121
+ var P = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea", "summary", "details", "option", "label"]);
122
+ var W = /* @__PURE__ */ new Set(["keydown", "keyup", "keypress"]);
123
+ function u(e, t) {
124
+ return e.attributes.find((i2) => "name" in i2 && i2.name === t);
125
+ }
126
+ function w(e) {
127
+ return e.attributes.some((t) => t.kind === "Spread");
128
+ }
129
+ function v(e, t) {
130
+ let i2 = u(e, t);
131
+ if (!i2 || i2.kind !== "Attribute" || !i2.value) return;
132
+ let n = i2.value;
133
+ return n.kind === "static" ? n.value : void 0;
134
+ }
135
+ function H(e) {
136
+ let t = v(e, "role");
137
+ return t === "presentation" || t === "none" ? true : v(e, "aria-hidden") === "true";
138
+ }
139
+ function A(e, t) {
140
+ return e.attributes.some((i2) => i2.kind === "EventBinding" && t(i2.name));
141
+ }
142
+ function B(e) {
143
+ let t = [], i2 = (r, s2, o) => {
144
+ t.push({ code: s2, message: o, severity: "warning", loc: r.loc });
145
+ }, n = (r) => {
146
+ for (let s2 of r) {
147
+ s2.kind === "Element" && L(s2, i2);
148
+ let o = "children" in s2 ? s2.children : void 0;
149
+ o && n(o);
150
+ }
151
+ };
152
+ return n(e.children), t;
153
+ }
154
+ function L(e, t) {
155
+ if (w(e)) return;
156
+ let i2 = e.tag.toLowerCase();
157
+ i2 === "img" && !u(e, "alt") && !H(e) && t(e, "a11y-img-alt", 'An `img` needs an `alt`. Use `alt=""` when the image is decorative.'), i2 === "a" && !u(e, "href") && !u(e, "role") && t(e, "a11y-anchor-href", "An `a` without `href` is not a link. Use a `button` for an action."), i2 === "iframe" && !u(e, "title") && t(e, "a11y-iframe-title", "An `iframe` needs a `title` describing its content.");
158
+ let n = v(e, "tabindex") ?? v(e, "tabIndex");
159
+ n !== void 0 && Number(n) > 0 && t(e, "a11y-positive-tabindex", "A positive `tabindex` reorders tab navigation for the entire page. Use `0`, or restructure the markup."), !P.has(i2) && A(e, (r) => r === "click") && !u(e, "role") && !A(e, (r) => W.has(r)) && t(e, "a11y-click-without-keyboard", `A click handler on \`${i2}\` is unreachable by keyboard. Use a \`button\`, or add a \`role\` and a key handler.`);
160
+ }
161
+ var V = /[A-Za-z0-9\-_:@.?]/;
162
+ var N = class {
163
+ constructor(t, i2) {
124
164
  this.diagnostics = [];
125
- this.r = new u(e), this.svg = i.svg ?? false, this.directives = i.directives ?? m;
165
+ this.r = new m(t), this.svg = i2.svg ?? false, this.directives = i2.directives ?? f, this.accessibility = i2.accessibility ?? true;
126
166
  }
127
167
  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));
168
+ let t = this.r.pos(), i2 = [];
169
+ for (; !this.r.eof() && (i2.push(...this.parseChildren()), !this.r.eof()); ) {
170
+ let r = this.r.pos();
171
+ this.consumeCloseTag(), this.report("stray-close-tag", "error", "Close tag without a matching open tag", this.r.span(r));
132
172
  }
133
- let r = { kind: "Root", children: i, loc: this.r.span(e) };
134
- return N(r), this.validateTree(r), { root: r, diagnostics: this.diagnostics };
173
+ let n = { kind: "Root", children: i2, loc: this.r.span(t) };
174
+ return x(n), this.validateTree(n), this.accessibility && this.diagnostics.push(...B(n)), { root: n, diagnostics: this.diagnostics };
135
175
  }
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));
176
+ validateTree(t) {
177
+ let i2 = (n) => {
178
+ let r = null;
179
+ for (let s2 of n) s2.kind === "Text" && /^\s*$/.test(s2.value) || (s2.kind === "Element" || s2.kind === "Component" ? (s2.attributes.some((l2) => l2.kind === "ElseDirective") && (r != null && (r.kind === "Element" || r.kind === "Component") && r.attributes.some((a2) => a2.kind === "IfDirective") || this.report("else-without-if", "error", "`else` must immediately follow an element with `if`", s2.loc)), r = s2) : r = null, "children" in s2 && s2.children && i2(s2.children));
140
180
  };
141
- i(e.children);
181
+ i2(t.children);
142
182
  }
143
183
  parseChildren() {
144
- let e = [];
184
+ let t = [];
145
185
  for (; !this.r.eof() && !this.r.startsWith("</"); ) {
146
186
  if (this.r.startsWith("<!--")) {
147
- e.push(this.parseComment());
187
+ t.push(this.parseComment());
148
188
  continue;
149
189
  }
150
190
  if (this.isFragmentStart()) {
151
- e.push(this.parseFragment());
191
+ t.push(this.parseFragment());
152
192
  continue;
153
193
  }
154
194
  if (this.isDynamicTagStart()) {
155
- e.push(this.parseDynamicComponent());
195
+ t.push(this.parseDynamicComponent());
156
196
  continue;
157
197
  }
158
198
  if (this.isTagStart()) {
159
- e.push(this.parseElement());
199
+ t.push(this.parseElement());
160
200
  continue;
161
201
  }
162
202
  if (this.r.atHole()) {
163
- let r = this.r.takeHole(), n = { kind: "Expression", hole: r.index, loc: r.loc };
164
- e.push(n);
203
+ let n = this.r.takeHole(), r = { kind: "Expression", hole: n.index, loc: n.loc };
204
+ t.push(r);
165
205
  continue;
166
206
  }
167
- let i = this.readText();
168
- i && e.push(i);
207
+ let i2 = this.readText();
208
+ i2 && t.push(i2);
169
209
  }
170
- return e;
210
+ return t;
171
211
  }
172
212
  isTagStart() {
173
213
  return this.r.peek() === "<" && /[A-Za-z]/.test(this.r.peek(1));
@@ -179,171 +219,171 @@ var g = class {
179
219
  return this.r.peek() === "<" && this.r.holeFollows();
180
220
  }
181
221
  parseDynamicComponent() {
182
- let e = this.r.pos();
222
+ let t = this.r.pos();
183
223
  this.r.advance();
184
- let i = this.r.takeHole().index, r = this.parseAttributes(true);
224
+ let i2 = this.r.takeHole().index, n = this.parseAttributes(true);
185
225
  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) };
226
+ let r = false, s2 = [];
227
+ return this.r.startsWith("/>") ? (this.r.advance(), this.r.advance(), r = true) : (this.r.peek() === ">" && this.r.advance(), s2 = 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(t))), { kind: "Component", tag: "", tagHole: i2, attributes: n, children: s2, selfClosing: r, loc: this.r.span(t) };
188
228
  }
189
229
  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;
230
+ let t = this.r.pos(), i2 = "";
231
+ for (; !this.r.eof() && !this.r.atHole() && !(this.r.startsWith("</") || this.r.startsWith("<!--") || this.isTagStart() || this.isFragmentStart() || this.isDynamicTagStart()); ) i2 += this.r.advance();
232
+ return i2 ? { kind: "Text", value: i2, raw: false, loc: this.r.span(t) } : null;
193
233
  }
194
234
  parseComment() {
195
- let e = this.r.pos();
235
+ let t = this.r.pos();
196
236
  this.r.advance(), this.r.advance(), this.r.advance(), this.r.advance();
197
- let i = "";
237
+ let i2 = "";
198
238
  for (; !this.r.eof() && !this.r.startsWith("-->"); ) {
199
239
  if (this.r.atHole()) {
200
- this.r.takeHole(), i += "\0";
240
+ this.r.takeHole(), i2 += "\0";
201
241
  continue;
202
242
  }
203
- i += this.r.advance();
243
+ i2 += this.r.advance();
204
244
  }
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) };
245
+ return this.r.startsWith("-->") ? (this.r.advance(), this.r.advance(), this.r.advance()) : this.report("unclosed-comment", "warning", "Unterminated comment", this.r.span(t)), { kind: "Comment", value: i2, loc: this.r.span(t) };
206
246
  }
207
247
  parseFragment() {
208
- let e = this.r.pos();
248
+ let t = this.r.pos();
209
249
  this.r.advance(), this.r.advance();
210
- let i = this.parseChildren();
250
+ let i2 = this.parseChildren();
211
251
  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) };
252
+ let n = this.consumeCloseTag();
253
+ n && this.report("mismatched-close-tag", "error", `Expected </> to close fragment, got </${n}>`, this.r.span(t));
254
+ } else this.report("unclosed-tag", "error", "Unterminated fragment", this.r.span(t));
255
+ return { kind: "Fragment", children: i2, loc: this.r.span(t) };
216
256
  }
217
257
  parseElement() {
218
- let e = this.r.pos();
258
+ let t = this.r.pos();
219
259
  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 = "";
260
+ let i2 = this.readName(), n = C(i2), r = this.parseAttributes(n);
261
+ this.foldEachKey(r, t), this.r.skipWhitespace();
262
+ let s2 = i2.toLowerCase(), o = !n && S.has(s2), l2 = !n && T.has(s2), a2 = false, c = [];
263
+ if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(), a2 = true;
264
+ else if (this.r.peek() === ">" && this.r.advance(), !l2) if (c = o ? this.parseRawText(i2) : this.parseChildren(), this.r.startsWith("</")) {
265
+ let g = this.consumeCloseTag();
266
+ g && g !== i2 && this.report("mismatched-close-tag", "error", `Expected </${i2}>, got </${g}>`, this.r.span(t));
267
+ } else this.report("unclosed-tag", "error", `Unclosed <${i2}>`, this.r.span(t));
268
+ let h = this.r.span(t);
269
+ if (n) return { kind: "Component", tag: i2, tagHole: null, attributes: r, children: c, selfClosing: a2, loc: h };
270
+ let d = this.svg || E.has(i2) ? "svg" : "html";
271
+ return { kind: "Element", tag: i2, namespace: d, attributes: r, children: c, selfClosing: a2, rawText: o, loc: h };
272
+ }
273
+ parseRawText(t) {
274
+ let i2 = [], n = "</" + t.toLowerCase(), r = "", s2 = this.r.pos(), o = () => {
275
+ r && i2.push({ kind: "Text", value: r, raw: true, loc: this.r.span(s2) }), r = "";
236
276
  };
237
- for (; !this.r.eof() && !this.startsWithCloseFor(r); ) {
277
+ for (; !this.r.eof() && !this.startsWithCloseFor(n); ) {
238
278
  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();
279
+ o();
280
+ let l2 = this.r.takeHole();
281
+ i2.push({ kind: "Expression", hole: l2.index, loc: l2.loc }), s2 = this.r.pos();
242
282
  continue;
243
283
  }
244
- n || (s = this.r.pos()), n += this.r.advance();
284
+ r || (s2 = this.r.pos()), r += this.r.advance();
245
285
  }
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++;
286
+ return o(), i2;
287
+ }
288
+ startsWithCloseFor(t) {
289
+ let i2 = 0;
290
+ for (; i2 < t.length; ) {
291
+ let n = this.r.peek(i2);
292
+ if (n === "" || n.toLowerCase() !== t[i2]) return false;
293
+ i2++;
254
294
  }
255
295
  return true;
256
296
  }
257
- parseAttributes(e) {
258
- let i = [];
297
+ parseAttributes(t) {
298
+ let i2 = [];
259
299
  for (; !this.r.eof() && (this.r.skipWhitespace(), !(this.r.peek() === ">" || this.r.startsWith("/>"))); ) {
260
300
  if (this.r.atHole()) {
261
- let o = this.r.takeHole();
262
- i.push({ kind: "Spread", hole: o.index, loc: o.loc });
301
+ let a2 = this.r.takeHole();
302
+ i2.push({ kind: "Spread", hole: a2.index, loc: a2.loc });
263
303
  continue;
264
304
  }
265
305
  if (this.r.startsWith("...")) {
266
- let o = this.r.pos();
306
+ let a2 = this.r.pos();
267
307
  if (this.r.advance(), this.r.advance(), this.r.advance(), this.r.atHole()) {
268
308
  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));
309
+ i2.push({ kind: "Spread", hole: c.index, loc: this.r.span(a2) });
310
+ } else this.report("invalid-binding", "error", "Spread `...` must be followed by ${\u2026}", this.r.span(a2));
271
311
  continue;
272
312
  }
273
- let r = this.r.pos(), n = this.readName();
274
- if (!n) {
313
+ let n = this.r.pos(), r = this.readName();
314
+ if (!r) {
275
315
  this.r.advance();
276
316
  continue;
277
317
  }
278
- let s = this.r.span(r);
318
+ let s2 = this.r.span(n);
279
319
  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 }));
320
+ let o = null;
321
+ this.r.peek() === "=" && (this.r.advance(), this.r.skipWhitespace(), o = this.parseAttrValue());
322
+ let l2 = { name: r, nameLoc: s2, value: o, loc: this.r.span(n), report: (a2, c) => this.report(a2, "error", c, this.r.span(n)) };
323
+ i2.push(k(l2, this.directives, { component: t }));
284
324
  }
285
- return i;
325
+ return i2;
286
326
  }
287
327
  parseAttrValue() {
288
328
  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)) };
329
+ let t = this.r.peek();
330
+ return t === '"' || t === "'" ? this.classifyParts(this.readQuotedParts(t)) : { kind: "static", value: this.r.readWhile((n) => !/[\s>]/.test(n)) };
291
331
  }
292
- readQuotedParts(e) {
332
+ readQuotedParts(t) {
293
333
  this.r.advance();
294
- let i = [], r = "";
295
- for (; !this.r.eof() && this.r.peek() !== e; ) {
334
+ let i2 = [], n = "";
335
+ for (; !this.r.eof() && this.r.peek() !== t; ) {
296
336
  if (this.r.atHole()) {
297
- r && (i.push({ text: r }), r = ""), i.push({ hole: this.r.takeHole().index });
337
+ n && (i2.push({ text: n }), n = ""), i2.push({ hole: this.r.takeHole().index });
298
338
  continue;
299
339
  }
300
- let n = this.r.advance();
301
- if (n === "") break;
302
- r += n;
340
+ let r = this.r.advance();
341
+ if (r === "") break;
342
+ n += r;
303
343
  }
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);
344
+ return n && i2.push({ text: n }), this.r.peek() === t && this.r.advance(), i2;
345
+ }
346
+ classifyParts(t) {
347
+ let i2 = t.filter((n) => "hole" in n);
348
+ return i2.length === 0 ? { kind: "static", value: t.map((n) => "text" in n ? n.text : "").join("") } : t.length === 1 ? { kind: "hole", hole: i2[0].hole } : { kind: "mixed", parts: t };
349
+ }
350
+ foldEachKey(t, i2) {
351
+ let n = null, r = 0, s2 = 0, o = 0, l2 = 0;
352
+ for (let d of t) d.kind === "EachDirective" ? (n = d, s2++) : d.kind === "IfDirective" ? r++ : d.kind === "ElseDirective" ? o++ : d.kind === "RefBinding" && l2++;
353
+ let a2 = this.r.span(i2);
354
+ s2 > 1 && this.report("duplicate-directive", "error", "Multiple `each` directives on one element", a2), r > 1 && this.report("duplicate-directive", "error", "Multiple `if` directives on one element", a2), l2 > 1 && this.report("duplicate-directive", "error", "Multiple `ref` bindings on one element", a2), r > 0 && s2 > 0 && this.report("unsupported-combination", "error", "`if` and `each` cannot both apply to one element \u2014 wrap one in another element", a2), r > 0 && o > 0 && this.report("invalid-directive", "error", "`if` and `else` cannot be on the same element", a2);
355
+ let c = t.findIndex((d) => d.kind === "Attribute" && d.name === "key");
356
+ if (!n) {
357
+ c !== -1 && this.report("invalid-directive", "error", "`key` requires an `each` directive on the same element", a2);
318
358
  return;
319
359
  }
320
360
  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);
361
+ let h = t[c];
362
+ h.kind === "Attribute" && h.value && (h.value.kind === "static" ? n.key = { static: h.value.value } : h.value.kind === "hole" && (n.key = { hole: h.value.hole })), t.splice(c, 1);
323
363
  }
324
364
  readName() {
325
- return this.r.readWhile((e) => T.test(e));
365
+ return this.r.readWhile((t) => V.test(t));
326
366
  }
327
367
  consumeCloseTag() {
328
368
  this.r.advance(), this.r.advance();
329
- let e = this.readName();
330
- return this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance(), e;
369
+ let t = this.readName();
370
+ return this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance(), t;
331
371
  }
332
- report(e, i, r, n) {
333
- this.diagnostics.push({ code: e, severity: i, message: r, loc: n });
372
+ report(t, i2, n, r) {
373
+ this.diagnostics.push({ code: t, severity: i2, message: n, loc: r });
334
374
  }
335
375
  };
336
- function k(t, e = {}) {
337
- return new g(t, e).parse();
376
+ function b(e, t = {}) {
377
+ return new N(e, t).parse();
338
378
  }
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;
379
+ function I(e) {
380
+ let t = [], i2 = 0;
381
+ return e.forEach((n, r) => {
382
+ t.push({ kind: "static", text: n, start: i2 }), i2 += n.length, r < e.length - 1 && t.push({ kind: "hole", index: r, start: i2, end: i2 });
383
+ }), t;
344
384
  }
345
- function F(t, e = {}) {
346
- return k(A(t), e);
385
+ function Q(e, t = {}) {
386
+ return b(I(e), t);
347
387
  }
348
388
 
349
389
  // src/collect.ts
@@ -355,7 +395,7 @@ function usedComponentTags(ts, sourceFile) {
355
395
  const tags = /* @__PURE__ */ new Set();
356
396
  const visit = (node) => {
357
397
  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);
398
+ collectTags(Q(templateStatics(ts, node.template)).root.children, tags);
359
399
  }
360
400
  ts.forEachChild(node, visit);
361
401
  };
@@ -411,15 +451,26 @@ function importedNames(ts, decl) {
411
451
  }
412
452
 
413
453
  // src/pieces.ts
454
+ function rawContent(ts, node, sf) {
455
+ const text = node.getText(sf);
456
+ const closesWithSubstitution = ts.isTemplateHead(node) || ts.isTemplateMiddle(node);
457
+ return text.slice(1, closesWithSubstitution ? -2 : -1);
458
+ }
414
459
  function templatePieces(ts, tpl, sf) {
415
460
  if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
416
- return [{ kind: "static", text: tpl.text, start: tpl.getStart(sf) + 1 }];
461
+ return [{ kind: "static", text: rawContent(ts, tpl, sf), start: tpl.getStart(sf) + 1 }];
417
462
  }
418
- const pieces = [{ kind: "static", text: tpl.head.text, start: tpl.head.getStart(sf) + 1 }];
419
- tpl.templateSpans.forEach((span, i) => {
463
+ const pieces = [
464
+ { kind: "static", text: rawContent(ts, tpl.head, sf), start: tpl.head.getStart(sf) + 1 }
465
+ ];
466
+ tpl.templateSpans.forEach((span, i2) => {
420
467
  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 });
468
+ pieces.push({ kind: "hole", index: i2, start: e.getStart(sf), end: e.getEnd() });
469
+ pieces.push({
470
+ kind: "static",
471
+ text: rawContent(ts, span.literal, sf),
472
+ start: span.literal.getStart(sf) + 1
473
+ });
423
474
  });
424
475
  return pieces;
425
476
  }
@@ -435,7 +486,7 @@ function parseCached(ts, sf, tpl) {
435
486
  const key = tpl.getStart(sf);
436
487
  const hit = byTemplate.get(key);
437
488
  if (hit) return hit;
438
- const result = k(templatePieces(ts, tpl, sf));
489
+ const result = b(templatePieces(ts, tpl, sf));
439
490
  byTemplate.set(key, result);
440
491
  return result;
441
492
  }
@@ -451,7 +502,7 @@ function templatesIn(ts, sf) {
451
502
  ts.forEachChild(node, visit);
452
503
  };
453
504
  visit(sf);
454
- found.sort((a, b2) => a.getStart(sf) - b2.getStart(sf));
505
+ found.sort((a2, b2) => a2.getStart(sf) - b2.getStart(sf));
455
506
  templates.set(sf, found);
456
507
  return found;
457
508
  }
@@ -465,8 +516,129 @@ function templateAtCached(ts, sf, pos) {
465
516
  return match;
466
517
  }
467
518
 
519
+ // ../compiler/dist/resolve/builtins.mjs
520
+ var s = ["Show", "For", "Index", "Switch", "Match", "Portal", "Dynamic", "ErrorBoundary"];
521
+ var i = ["Suspense", "SuspenseList", "Await"];
522
+ var u2 = ["Router", "Outlet", "Redirect", "Link"];
523
+ var p2 = [...s, ...i, ...u2];
524
+ var a = /* @__PURE__ */ new Set([...s, ...i]);
525
+ function l(o = {}) {
526
+ let { controlFlowModule: e = "@fluixi/dom", coreModule: r = "@fluixi/core", routerModule: c = "@fluixi/core/router-next" } = o, n = {};
527
+ for (let t of s) n[t] = e;
528
+ for (let t of i) n[t] = r;
529
+ for (let t of u2) n[t] = c;
530
+ return n;
531
+ }
532
+
533
+ // src/auto-import.ts
534
+ var AUTO_IMPORTED = l();
535
+ var exportsByProgram = /* @__PURE__ */ new WeakMap();
536
+ function moduleExports(ts, program, host, from, moduleName) {
537
+ let cache2 = exportsByProgram.get(program);
538
+ if (!cache2) {
539
+ cache2 = /* @__PURE__ */ new Map();
540
+ exportsByProgram.set(program, cache2);
541
+ }
542
+ if (cache2.has(moduleName)) return cache2.get(moduleName);
543
+ let symbols;
544
+ try {
545
+ const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
546
+ const file = resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName);
547
+ const checker = program.getTypeChecker();
548
+ const moduleSymbol = file && checker.getSymbolAtLocation(file);
549
+ if (moduleSymbol) symbols = checker.getExportsOfModule(moduleSymbol);
550
+ } catch {
551
+ symbols = void 0;
552
+ }
553
+ cache2.set(moduleName, symbols);
554
+ return symbols;
555
+ }
556
+ var sitesByProgram = /* @__PURE__ */ new WeakMap();
557
+ function declarationSite(ts, program, host, from, name) {
558
+ const moduleName = AUTO_IMPORTED[name];
559
+ if (!moduleName) return void 0;
560
+ let cache2 = sitesByProgram.get(program);
561
+ if (!cache2) {
562
+ cache2 = /* @__PURE__ */ new Map();
563
+ sitesByProgram.set(program, cache2);
564
+ }
565
+ const key = `${moduleName}:${name}`;
566
+ if (cache2.has(key)) return cache2.get(key);
567
+ let site;
568
+ try {
569
+ const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
570
+ const entry = resolved.resolvedModule?.resolvedFileName;
571
+ if (entry) site = findExport(ts, host, program.getCompilerOptions(), entry, name, 0);
572
+ } catch {
573
+ site = void 0;
574
+ }
575
+ cache2.set(key, site);
576
+ return site;
577
+ }
578
+ function findExport(ts, host, options, fileName, name, depth) {
579
+ if (depth > 4) return void 0;
580
+ const text = host.readFile?.(fileName);
581
+ if (text === void 0) return void 0;
582
+ const sf = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true);
583
+ const reExports = [];
584
+ for (const statement of sf.statements) {
585
+ const declared = declaredName(ts, statement, name);
586
+ if (declared) {
587
+ return { fileName, start: declared.getStart(sf), length: declared.getWidth(sf) };
588
+ }
589
+ if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
590
+ const specifier = statement.moduleSpecifier.text;
591
+ if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
592
+ const named = statement.exportClause.elements.find((e) => e.name.text === name);
593
+ if (named) reExports.unshift(specifier);
594
+ } else if (!statement.exportClause) {
595
+ reExports.push(specifier);
596
+ }
597
+ }
598
+ }
599
+ for (const specifier of reExports) {
600
+ const target = resolveRelative(ts, host, options, fileName, specifier);
601
+ const found = target && findExport(ts, host, options, target, name, depth + 1);
602
+ if (found) return found;
603
+ }
604
+ return void 0;
605
+ }
606
+ function declaredName(ts, statement, name) {
607
+ const exported = ts.canHaveModifiers(statement) ? ts.getModifiers(statement)?.some((m2) => m2.kind === ts.SyntaxKind.ExportKeyword) : false;
608
+ if (!exported) return void 0;
609
+ if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name?.text === name) {
610
+ return statement.name;
611
+ }
612
+ if (ts.isVariableStatement(statement)) {
613
+ for (const d of statement.declarationList.declarations) {
614
+ if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
615
+ }
616
+ }
617
+ return void 0;
618
+ }
619
+ function resolveRelative(ts, host, options, from, specifier) {
620
+ if (!specifier.startsWith(".")) return void 0;
621
+ try {
622
+ return ts.resolveModuleName(specifier, from, options, host).resolvedModule?.resolvedFileName;
623
+ } catch {
624
+ return void 0;
625
+ }
626
+ }
627
+ function autoImportedSymbol(ts, program, host, from, name) {
628
+ const moduleName = AUTO_IMPORTED[name];
629
+ if (!moduleName) return void 0;
630
+ const symbol = moduleExports(ts, program, host, from, moduleName)?.find((s2) => s2.name === name);
631
+ if (!symbol) return void 0;
632
+ if (!(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
633
+ try {
634
+ return program.getTypeChecker().getAliasedSymbol(symbol);
635
+ } catch {
636
+ return symbol;
637
+ }
638
+ }
639
+
468
640
  // src/hover.ts
469
- function componentQuickInfo(ts, ls, fileName, position) {
641
+ function componentQuickInfo(ts, ls, host, fileName, position) {
470
642
  const program = ls.getProgram();
471
643
  const sf = program?.getSourceFile(fileName);
472
644
  if (!program || !sf) return void 0;
@@ -476,11 +648,12 @@ function componentQuickInfo(ts, ls, fileName, position) {
476
648
  if (!hit) return void 0;
477
649
  const checker = program.getTypeChecker();
478
650
  const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
479
- const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name);
651
+ const symbol = checker.getSymbolsInScope(template, flags).find((s2) => s2.name === hit.name) ?? autoImportedSymbol(ts, program, host, sf, hit.name);
480
652
  const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
481
653
  if (!decl) return void 0;
482
- const namePos = decl.name?.getStart(sf) ?? decl.getStart(sf);
483
- const info = ls.getQuickInfoAtPosition(fileName, namePos);
654
+ const declFile = decl.getSourceFile();
655
+ const namePos = decl.name?.getStart(declFile) ?? decl.getStart(declFile);
656
+ const info = ls.getQuickInfoAtPosition(declFile.fileName, namePos);
484
657
  if (!info) return void 0;
485
658
  return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
486
659
  }
@@ -612,21 +785,21 @@ function holeRoles(nodes, out) {
612
785
  };
613
786
  for (const node of nodes) {
614
787
  if (node.kind === "Element" || node.kind === "Component") {
615
- for (const a of node.attributes) {
616
- if (a.kind === "EventBinding") out.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
617
- if (node.kind === "Element" && a.kind === "RefBinding") out.set(a.hole, { kind: "refEl", tag: node.tag });
618
- if (node.kind === "Element" && a.kind === "UseDirective" && a.hole != null) out.set(a.hole, { kind: "refEl", tag: node.tag });
788
+ for (const a2 of node.attributes) {
789
+ if (a2.kind === "EventBinding") out.set(a2.hole, { kind: "event", event: a2.name.toLowerCase() });
790
+ if (node.kind === "Element" && a2.kind === "RefBinding") out.set(a2.hole, { kind: "refEl", tag: node.tag });
791
+ if (node.kind === "Element" && a2.kind === "UseDirective" && a2.hole != null) out.set(a2.hole, { kind: "refEl", tag: node.tag });
619
792
  }
620
- const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
793
+ const eachDir = node.attributes.find((a2) => a2.kind === "EachDirective");
621
794
  if (eachDir && eachDir.kind === "EachDirective") {
622
795
  renderChildren(node, { kind: "each", eachHole: eachDir.hole });
623
796
  }
624
797
  if (node.kind === "Component") {
625
798
  const propHole = (name) => {
626
- const a = node.attributes.find(
799
+ const a2 = node.attributes.find(
627
800
  (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
628
801
  );
629
- return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
802
+ return a2 && a2.kind === "Attribute" && a2.value && a2.value.kind === "hole" ? a2.value.hole : void 0;
630
803
  };
631
804
  const eachHole = propHole("each");
632
805
  if (node.tag === "For" && eachHole !== void 0) renderChildren(node, { kind: "each", eachHole });
@@ -639,7 +812,7 @@ function holeRoles(nodes, out) {
639
812
  }
640
813
  }
641
814
  function resolveGlobalType(ts, checker, location, name) {
642
- const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((s) => s.getName() === name);
815
+ const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((s2) => s2.getName() === name);
643
816
  return sym ? checker.getDeclaredTypeOfSymbol(sym) : void 0;
644
817
  }
645
818
  function resolveElementType(ts, checker, location, tag) {
@@ -652,7 +825,7 @@ function inferParamAt(ts, program, sf, position) {
652
825
  const template = templateAtCached(ts, sf, position);
653
826
  if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
654
827
  const spans = template.template.templateSpans;
655
- const holeExprs = spans.map((s) => s.expression);
828
+ const holeExprs = spans.map((s2) => s2.expression);
656
829
  const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
657
830
  if (holeIndex === -1) return void 0;
658
831
  const roles = /* @__PURE__ */ new Map();
@@ -667,6 +840,12 @@ function inferParamAt(ts, program, sf, position) {
667
840
  const id = identifierAt(ts, sf, position);
668
841
  if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
669
842
  const checker = program.getTypeChecker();
843
+ const resolved = typeForRole(ts, checker, fn, role, holeExprs);
844
+ const { typeText, type } = resolved;
845
+ if (!typeText) return void 0;
846
+ return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
847
+ }
848
+ function typeForRole(ts, checker, fn, role, holeExprs) {
670
849
  let typeText;
671
850
  let type;
672
851
  if (role.kind === "event") {
@@ -687,8 +866,27 @@ function inferParamAt(ts, program, sf, position) {
687
866
  type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
688
867
  if (type) typeText = checker.typeToString(type);
689
868
  }
690
- if (!typeText) return void 0;
691
- return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
869
+ return { typeText, type };
870
+ }
871
+ function inferParamsIn(ts, program, sf) {
872
+ const checker = program.getTypeChecker();
873
+ const out = [];
874
+ for (const template of templatesIn(ts, sf)) {
875
+ const tpl = template.template;
876
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
877
+ const holeExprs = tpl.templateSpans.map((s2) => s2.expression);
878
+ const roles = /* @__PURE__ */ new Map();
879
+ holeRoles(parseCached(ts, sf, tpl).root.children, roles);
880
+ for (const [holeIndex, role] of roles) {
881
+ const fn = holeExprs[holeIndex];
882
+ if (!fn || !ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue;
883
+ const param = fn.parameters[0];
884
+ if (!param || !ts.isIdentifier(param.name) || param.type) continue;
885
+ const { type } = typeForRole(ts, checker, fn, role, holeExprs);
886
+ if (type) out.push({ fn, paramName: param.name.text, type });
887
+ }
888
+ }
889
+ return out;
692
890
  }
693
891
  function paramQuickInfo(ts, ls, fileName, position) {
694
892
  const program = ls.getProgram();
@@ -730,7 +928,7 @@ function contains(outer, inner) {
730
928
  }
731
929
 
732
930
  // src/props.ts
733
- function componentPropCompletions(ts, ls, fileName, position) {
931
+ function componentPropCompletions(ts, ls, host, fileName, position) {
734
932
  const program = ls.getProgram();
735
933
  const sf = program?.getSourceFile(fileName);
736
934
  if (!program || !sf) return void 0;
@@ -738,28 +936,27 @@ function componentPropCompletions(ts, ls, fileName, position) {
738
936
  if (!template) return void 0;
739
937
  const comp = componentTagAtAttrArea(ts, sf, template.template, position);
740
938
  if (!comp || !comp.tag) return void 0;
939
+ const span = attrNameSpan(sf.text, position);
741
940
  const cf = CONTROL_FLOW_PROPS[comp.tag];
742
941
  if (cf) {
743
942
  const already2 = new Set(comp.attributeNames);
744
- return cf.filter((n) => !already2.has(n)).map((name) => propEntry(ts, name));
943
+ return cf.filter((n) => !already2.has(n)).map((name) => propEntry(ts, name, span));
745
944
  }
746
945
  const checker = program.getTypeChecker();
747
946
  const rootName = comp.tag.split(".")[0];
748
- const decl = findDeclaration(ts, sf, rootName);
749
- if (!decl) return void 0;
750
- const compType = checker.getTypeAtLocation(decl);
751
- const propsType = firstParamType(ts, checker, compType);
947
+ const compType = componentTypeOf(ts, program, checker, host, sf, template.template, rootName);
948
+ const propsType = compType && firstParamType(ts, checker, compType);
752
949
  if (!propsType) return void 0;
753
950
  const already = new Set(comp.attributeNames);
754
951
  const entries = [];
755
- for (const p2 of propsType.getProperties()) {
756
- const name = p2.getName();
952
+ for (const p3 of propsType.getProperties()) {
953
+ const name = p3.getName();
757
954
  if (name === "children" || already.has(name)) continue;
758
- entries.push(propEntry(ts, name));
955
+ entries.push(propEntry(ts, name, span));
759
956
  }
760
957
  return entries.length ? entries : void 0;
761
958
  }
762
- function componentPropHover(ts, ls, fileName, position) {
959
+ function componentPropHover(ts, ls, host, fileName, position) {
763
960
  const program = ls.getProgram();
764
961
  const sf = program?.getSourceFile(fileName);
765
962
  if (!program || !sf) return void 0;
@@ -774,11 +971,13 @@ function componentPropHover(ts, ls, fileName, position) {
774
971
  if (!typeText) return void 0;
775
972
  } else if (hit.tag) {
776
973
  const checker = program.getTypeChecker();
777
- const decl = findDeclaration(ts, sf, hit.tag.split(".")[0]);
778
- const propsType = decl && firstParamType(ts, checker, checker.getTypeAtLocation(decl));
974
+ const root = hit.tag.split(".")[0];
975
+ const compType = componentTypeOf(ts, program, checker, host, sf, template.template, root);
976
+ const propsType = compType && firstParamType(ts, checker, compType);
779
977
  const sym = propsType && propsType.getProperty(hit.prop);
780
978
  if (!sym) return void 0;
781
- typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? decl));
979
+ const at = sym.valueDeclaration ?? sym.declarations?.[0] ?? template.template;
980
+ typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, at));
782
981
  }
783
982
  if (!typeText) return void 0;
784
983
  return {
@@ -805,10 +1004,10 @@ function propNameAt(ts, sf, tpl, position) {
805
1004
  for (const node of nodes) {
806
1005
  if (node.kind === "Component") {
807
1006
  const c = node;
808
- for (const a of c.attributes) {
809
- if ((a.kind === "Attribute" || a.kind === "PropertyBinding" || a.kind === "BindDirective") && "loc" in a) {
810
- const start = a.loc.start;
811
- if (position >= start && position <= start + a.name.length) hit = { tag: c.tag, prop: a.name, start };
1007
+ for (const a2 of c.attributes) {
1008
+ if ((a2.kind === "Attribute" || a2.kind === "PropertyBinding" || a2.kind === "BindDirective") && "loc" in a2) {
1009
+ const start = a2.loc.start;
1010
+ if (position >= start && position <= start + a2.name.length) hit = { tag: c.tag, prop: a2.name, start };
812
1011
  }
813
1012
  }
814
1013
  }
@@ -845,31 +1044,40 @@ var CONTROL_FLOW_PROPS = {
845
1044
  Dynamic: ["component"],
846
1045
  Await: ["value", "fallback"]
847
1046
  };
848
- function propEntry(ts, name) {
849
- return { name, kind: ts.ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: "0_" + name, insertText: name };
850
- }
851
- function findDeclaration(ts, sf, name) {
852
- for (const stmt of sf.statements) {
853
- if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && stmt.name?.text === name) return stmt.name;
854
- if (ts.isVariableStatement(stmt)) {
855
- for (const d of stmt.declarationList.declarations) {
856
- if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
857
- }
858
- }
859
- if (ts.isImportDeclaration(stmt)) {
860
- const b2 = stmt.importClause?.namedBindings;
861
- if (b2 && ts.isNamedImports(b2)) {
862
- for (const el of b2.elements) if (el.name.text === name) return el.name;
863
- }
864
- if (stmt.importClause?.name?.text === name) return stmt.importClause.name;
865
- }
866
- }
867
- return void 0;
1047
+ function propEntry(ts, name, replacementSpan) {
1048
+ return {
1049
+ name,
1050
+ kind: ts.ScriptElementKind.memberVariableElement,
1051
+ kindModifiers: "",
1052
+ sortText: "0_" + name,
1053
+ insertText: name,
1054
+ // Without a span the editor filters these against whatever word *it* computes at the
1055
+ // cursor, and inside a template literal that is not the attribute name so the list
1056
+ // showed in full until the first keystroke and then emptied. Saying exactly which
1057
+ // characters are being replaced makes filtering match what is typed.
1058
+ replacementSpan
1059
+ };
1060
+ }
1061
+ function attrNameSpan(text, position) {
1062
+ const isNameChar = (c) => !!c && /[A-Za-z0-9_$:@?.\-]/.test(c);
1063
+ let start = position;
1064
+ while (start > 0 && isNameChar(text[start - 1])) start--;
1065
+ let end = position;
1066
+ while (end < text.length && isNameChar(text[end])) end++;
1067
+ return { start, length: end - start };
1068
+ }
1069
+ function componentTypeOf(ts, program, checker, host, sf, template, name) {
1070
+ const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
1071
+ const symbol = checker.getSymbolsInScope(template, flags).find((s2) => s2.name === name) ?? autoImportedSymbol(ts, program, host, sf, name);
1072
+ if (!symbol) return void 0;
1073
+ const resolved = symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
1074
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0];
1075
+ return decl ? checker.getTypeOfSymbolAtLocation(resolved, decl) : void 0;
868
1076
  }
869
1077
  function firstParamType(ts, checker, type) {
870
1078
  for (const sig of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
871
- const p2 = sig.getParameters()[0];
872
- if (p2) return checker.getTypeOfSymbolAtLocation(p2, p2.valueDeclaration ?? sig.getDeclaration());
1079
+ const p3 = sig.getParameters()[0];
1080
+ if (p3) return checker.getTypeOfSymbolAtLocation(p3, p3.valueDeclaration ?? sig.getDeclaration());
873
1081
  }
874
1082
  return void 0;
875
1083
  }
@@ -882,7 +1090,7 @@ function componentTagAtAttrArea(ts, sf, tpl, position) {
882
1090
  const c = node;
883
1091
  const openStart = c.loc.start + 1 + (c.tag ? c.tag.length : 0);
884
1092
  const openEnd = firstChildOrCloseStart(c);
885
- if (position > openStart && position <= openEnd) {
1093
+ if (position > openStart && position < openEnd) {
886
1094
  hit = { tag: c.tag, attributeNames: attributeNames(c) };
887
1095
  }
888
1096
  }
@@ -900,8 +1108,8 @@ function firstChildOrCloseStart(c) {
900
1108
  }
901
1109
  function attributeNames(c) {
902
1110
  const out = [];
903
- for (const a of c.attributes) {
904
- if (a.kind === "Attribute" || a.kind === "PropertyBinding" || a.kind === "BindDirective") out.push(a.name);
1111
+ for (const a2 of c.attributes) {
1112
+ if (a2.kind === "Attribute" || a2.kind === "PropertyBinding" || a2.kind === "BindDirective") out.push(a2.name);
905
1113
  }
906
1114
  return out;
907
1115
  }
@@ -918,13 +1126,13 @@ function paramMemberCompletions(ts, ls, fileName, position) {
918
1126
  const checker = program.getTypeChecker();
919
1127
  const already = /* @__PURE__ */ new Set();
920
1128
  const entries = [];
921
- for (const p2 of checker.getApparentType(info.type).getProperties()) {
922
- const name = p2.getName();
1129
+ for (const p3 of checker.getApparentType(info.type).getProperties()) {
1130
+ const name = p3.getName();
923
1131
  if (already.has(name) || name.startsWith("__")) continue;
924
1132
  already.add(name);
925
1133
  entries.push({
926
1134
  name,
927
- kind: memberKind(ts, p2),
1135
+ kind: memberKind(ts, p3),
928
1136
  kindModifiers: "",
929
1137
  sortText: "0_" + name,
930
1138
  insertText: name
@@ -975,7 +1183,6 @@ function intrinsics(ts, program, host, sf) {
975
1183
  );
976
1184
  if (!type) {
977
1185
  for (const file of program.getSourceFiles()) {
978
- if (!file.isDeclarationFile && file.fileName.indexOf("@fluixi/dom") === -1) continue;
979
1186
  if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
980
1187
  type = declared(file);
981
1188
  if (type) break;
@@ -1013,8 +1220,8 @@ function elementAtAttributeArea(ts, sf, tpl, position) {
1013
1220
  const openEnd = firstAttributeEnd(el);
1014
1221
  if (position > openStart && position <= openEnd) {
1015
1222
  const present = /* @__PURE__ */ new Set();
1016
- for (const a of el.attributes) {
1017
- if ("loc" in a && a.loc) present.add(writtenName(sf.text, a.loc.start));
1223
+ for (const a2 of el.attributes) {
1224
+ if ("loc" in a2 && a2.loc) present.add(writtenName(sf.text, a2.loc.start));
1018
1225
  }
1019
1226
  hit = { tag: el.tag, present };
1020
1227
  }
@@ -1033,8 +1240,8 @@ function writtenName(text, start) {
1033
1240
  }
1034
1241
  function firstAttributeEnd(el) {
1035
1242
  let end = el.loc.start + 1 + el.tag.length;
1036
- for (const a of el.attributes) {
1037
- if ("loc" in a && a.loc) end = Math.max(end, a.loc.end);
1243
+ for (const a2 of el.attributes) {
1244
+ if ("loc" in a2 && a2.loc) end = Math.max(end, a2.loc.end);
1038
1245
  }
1039
1246
  return end + 1;
1040
1247
  }
@@ -1049,8 +1256,8 @@ function elementAttributeCompletions(ts, ls, host, fileName, position) {
1049
1256
  const props = attributesFor(ts, program, host, sf, hit.tag);
1050
1257
  if (!props) return void 0;
1051
1258
  const entries = [];
1052
- for (const p2 of props) {
1053
- const name = p2.getName();
1259
+ for (const p3 of props) {
1260
+ const name = p3.getName();
1054
1261
  if (name === "children" || hit.present.has(name)) continue;
1055
1262
  entries.push({
1056
1263
  name,
@@ -1068,9 +1275,9 @@ function attributeNameAt(ts, sf, tpl, position) {
1068
1275
  for (const node of nodes) {
1069
1276
  if (node.kind === "Element") {
1070
1277
  const el = node;
1071
- for (const a of el.attributes) {
1072
- if (!("loc" in a) || !a.loc) continue;
1073
- const start = a.loc.start;
1278
+ for (const a2 of el.attributes) {
1279
+ if (!("loc" in a2) || !a2.loc) continue;
1280
+ const start = a2.loc.start;
1074
1281
  const name = writtenName(sf.text, start);
1075
1282
  if (name && position >= start && position <= start + name.length) {
1076
1283
  hit = { tag: el.tag, name, start };
@@ -1093,7 +1300,7 @@ function elementAttributeHover(ts, ls, host, fileName, position) {
1093
1300
  const hit = attributeNameAt(ts, sf, tpl.template, position);
1094
1301
  if (!hit) return void 0;
1095
1302
  const props = attributesFor(ts, program, host, sf, hit.tag);
1096
- const symbol = props?.find((p2) => p2.getName() === hit.name);
1303
+ const symbol = props?.find((p3) => p3.getName() === hit.name);
1097
1304
  if (!symbol) return void 0;
1098
1305
  const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
1099
1306
  if (!decl) return void 0;
@@ -1118,385 +1325,283 @@ function elementAttributeHover(ts, ls, host, fileName, position) {
1118
1325
  };
1119
1326
  }
1120
1327
 
1121
- // src/virtual.ts
1122
- var EVENT_MAP_KEYS = new Set(Object.keys({
1123
- click: 0,
1124
- dblclick: 0,
1125
- mousedown: 0,
1126
- mouseup: 0,
1127
- mousemove: 0,
1128
- mouseenter: 0,
1129
- mouseleave: 0,
1130
- mouseover: 0,
1131
- mouseout: 0,
1132
- contextmenu: 0,
1133
- keydown: 0,
1134
- keyup: 0,
1135
- keypress: 0,
1136
- input: 0,
1137
- beforeinput: 0,
1138
- change: 0,
1139
- submit: 0,
1140
- focus: 0,
1141
- blur: 0,
1142
- focusin: 0,
1143
- focusout: 0,
1144
- pointerdown: 0,
1145
- pointerup: 0,
1146
- pointermove: 0,
1147
- pointerenter: 0,
1148
- pointerleave: 0,
1149
- wheel: 0,
1150
- scroll: 0,
1151
- drag: 0,
1152
- drop: 0,
1153
- touchstart: 0,
1154
- touchend: 0,
1155
- touchmove: 0,
1156
- animationend: 0,
1157
- transitionend: 0
1158
- }));
1159
- var PRELUDE = (
1160
- // Control-flow component prop types, declared inline so they ALWAYS resolve
1161
- // (no dependency on @fluixi/core being importable from the virtual document).
1162
- // Prop shapes drive hover + value checking on when/each/fallback/mount/…;
1163
- // render-prop children are typed separately by __fxEach/__fxShow.
1164
- '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'
1165
- );
1166
- var CONTROL_FLOW = /* @__PURE__ */ new Set([
1167
- "Show",
1168
- "For",
1169
- "Index",
1170
- "Switch",
1171
- "Match",
1172
- "Dynamic",
1173
- "ErrorBoundary",
1174
- "Suspense",
1175
- "SuspenseList",
1176
- "Await",
1177
- "Portal"
1178
- ]);
1179
- function inHole(segments, pos) {
1180
- return segments.some((s) => s.hole && pos >= s.srcStart && pos <= s.srcEnd);
1181
- }
1182
- function inKey(segments, pos) {
1183
- return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
1184
- }
1185
- function toSource(segments, pos) {
1186
- for (const s of segments) {
1187
- if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
1188
- }
1189
- return -1;
1190
- }
1191
- function buildVirtual(ts, sf) {
1192
- const templates2 = collectTemplates(ts, sf);
1193
- if (templates2.length === 0) return void 0;
1194
- const srcText = sf.text;
1195
- const segments = [];
1196
- let gen = "";
1197
- let src = 0;
1198
- const copy = (to) => {
1199
- if (to <= src) return;
1200
- const genStart = gen.length;
1201
- gen += srcText.slice(src, to);
1202
- segments.push({ srcStart: src, srcEnd: to, genStart, genEnd: gen.length });
1203
- src = to;
1204
- };
1205
- gen += PRELUDE;
1206
- for (const tpl of templates2) {
1207
- copy(tpl.node.getStart(sf));
1208
- gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
1209
- src = tpl.node.getEnd();
1210
- }
1211
- copy(srcText.length);
1212
- return { text: gen, segments };
1213
- }
1214
- function isFn(ts, node) {
1215
- return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
1328
+ // src/diagnostics.ts
1329
+ var ACCESSOR_NOT_CALLED = 9001;
1330
+ function acceptsAnyProperty(ts, checker, type) {
1331
+ const open = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter | ts.TypeFlags.Never;
1332
+ if (type.flags & open) return true;
1333
+ if (checker.getIndexInfoOfType?.(type, ts.IndexKind.String)) return true;
1334
+ if (type.isUnion()) return type.types.some((t) => acceptsAnyProperty(ts, checker, t));
1335
+ return false;
1216
1336
  }
1217
- function collectTemplates(ts, sf) {
1337
+ function accessorDiagnostics(ts, program, sf) {
1338
+ const checker = program.getTypeChecker();
1218
1339
  const out = [];
1219
- const visit = (node) => {
1220
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
1221
- const tpl = node.template;
1222
- const holes = ts.isTemplateExpression(tpl) ? tpl.templateSpans.map((s) => s.expression) : [];
1223
- out.push({ node, holes });
1224
- }
1225
- ts.forEachChild(node, visit);
1226
- };
1227
- visit(sf);
1228
- return out;
1229
- }
1230
- function analyze(nodes, roles, consumed) {
1231
- const renderChild = (node) => {
1232
- if (!("children" in node) || !node.children) return void 0;
1233
- const expr = node.children.find((c) => c.kind === "Expression");
1234
- return expr && expr.kind === "Expression" ? expr.hole : void 0;
1235
- };
1236
- for (const node of nodes) {
1237
- if (node.kind === "Element" || node.kind === "Component") {
1238
- for (const a of node.attributes) {
1239
- if (a.kind === "EventBinding") roles.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
1240
- if (a.kind === "RefBinding" && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1241
- if (a.kind === "UseDirective" && a.hole != null && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1242
- }
1243
- const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
1244
- const propHole = (name) => {
1245
- const a = node.attributes.find(
1246
- (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
1247
- );
1248
- return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
1249
- };
1250
- const wire = (arrayHole, kind) => {
1251
- const r = renderChild(node);
1252
- if (r !== void 0) {
1253
- roles.set(r, { kind, arrayHole });
1254
- consumed.add(arrayHole);
1255
- }
1256
- };
1257
- if (eachDir && eachDir.kind === "EachDirective") wire(eachDir.hole, "eachRender");
1258
- else if (node.kind === "Component" && node.tag === "For") {
1259
- const h = propHole("each");
1260
- if (h !== void 0) wire(h, "eachRender");
1261
- } else if (node.kind === "Component" && node.tag === "Index") {
1262
- const h = propHole("each");
1263
- if (h !== void 0) wire(h, "indexRender");
1264
- } else if (node.kind === "Component" && node.tag === "Show") {
1265
- const w = propHole("when");
1266
- const r = renderChild(node);
1267
- if (w !== void 0 && r !== void 0) {
1268
- roles.set(r, { kind: "showRender", whenHole: w });
1269
- consumed.add(w);
1340
+ for (const template of templatesIn(ts, sf)) {
1341
+ const tpl = template.template;
1342
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
1343
+ const holes = tpl.templateSpans.map((s2) => s2.expression);
1344
+ const visit = (nodes, parent) => {
1345
+ for (const node of nodes) {
1346
+ if (node.kind === "Expression" && parent?.kind === "Element") {
1347
+ const expression = holes[node.hole];
1348
+ if (expression && isAccessorRead(ts, expression) && isAccessor(ts, checker, expression)) {
1349
+ const text = expression.getText(sf);
1350
+ out.push({
1351
+ file: sf,
1352
+ start: expression.getStart(sf),
1353
+ length: expression.getWidth(sf),
1354
+ category: ts.DiagnosticCategory.Warning,
1355
+ code: ACCESSOR_NOT_CALLED,
1356
+ messageText: `'${text}' is an accessor \u2014 call it (\`${text}()\`). Left bare it renders the function and never updates.`,
1357
+ source: "fluixi"
1358
+ });
1359
+ }
1270
1360
  }
1361
+ const children = "children" in node ? node.children : void 0;
1362
+ if (children) visit(children, node);
1271
1363
  }
1272
- }
1273
- if ("children" in node && node.children) analyze(node.children, roles, consumed);
1364
+ };
1365
+ visit(parseCached(ts, sf, tpl).root.children, null);
1274
1366
  }
1367
+ return out;
1275
1368
  }
1276
- function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
1277
- const roles = /* @__PURE__ */ new Map();
1278
- const consumed = /* @__PURE__ */ new Set();
1279
- const { root } = k(templatePieces(ts, tpl.node.template, sf));
1280
- analyze(root.children, roles, consumed);
1281
- const holeSrc = (i) => [tpl.holes[i].getStart(sf), tpl.holes[i].getEnd()];
1282
- const putHole = (i) => {
1283
- const [s, e] = holeSrc(i);
1284
- const genStart = gen.length;
1285
- gen += srcText.slice(s, e);
1286
- segments.push({ srcStart: s, srcEnd: e, genStart, genEnd: gen.length, hole: true });
1287
- };
1288
- const isIdent = (t) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t);
1289
- const putKey = (start, text) => {
1290
- const genStart = gen.length;
1291
- if (isIdent(text)) {
1292
- gen += text;
1293
- segments.push({ srcStart: start, srcEnd: start + text.length, genStart, genEnd: gen.length, key: true });
1294
- } else {
1295
- gen += JSON.stringify(text);
1296
- }
1297
- };
1298
- const calls = [];
1299
- const walkComponents = (nodes) => {
1300
- for (const node of nodes) {
1301
- if (node.kind === "Component") calls.push(componentPropsEmitter(node));
1302
- if ("children" in node && node.children) walkComponents(node.children);
1303
- }
1304
- };
1305
- function componentPropsEmitter(node) {
1306
- return () => {
1307
- gen += "__fxProps(";
1308
- if (node.tagHole != null) putHole(node.tagHole);
1309
- else if (CONTROL_FLOW.has(node.tag)) gen += `__fxCF.${node.tag}`;
1310
- else gen += node.tag;
1311
- gen += ", { ";
1312
- let first = true;
1313
- const entry = (keyStart, keyText, emitValue) => {
1314
- if (!first) gen += ", ";
1315
- first = false;
1316
- putKey(keyStart, keyText);
1317
- gen += ": ";
1318
- emitValue();
1319
- };
1320
- for (const a of node.attributes) {
1321
- if (a.kind === "Attribute") {
1322
- const keyStart = a.boolean ? a.loc.start + 1 : a.loc.start;
1323
- const v2 = a.value;
1324
- entry(keyStart, a.name, () => {
1325
- if (!v2) {
1326
- gen += "true";
1327
- } else if (v2.kind === "static") {
1328
- gen += JSON.stringify(v2.value);
1329
- } else if (v2.kind === "hole") {
1330
- putHole(v2.hole);
1331
- } else {
1332
- gen += "undefined as any";
1333
- }
1369
+ function isAccessorRead(ts, node) {
1370
+ return ts.isIdentifier(node) || ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
1371
+ }
1372
+ function isAccessor(ts, checker, node) {
1373
+ const signatures = checker.getTypeAtLocation(node).getCallSignatures();
1374
+ if (signatures.length !== 1) return false;
1375
+ const signature = signatures[0];
1376
+ if (signature.getParameters().some((p3) => !isOptional(ts, p3))) return false;
1377
+ const returned = signature.getReturnType();
1378
+ return !(returned.flags & (ts.TypeFlags.Void | ts.TypeFlags.Never));
1379
+ }
1380
+ function isOptional(ts, symbol) {
1381
+ const declaration = symbol.valueDeclaration;
1382
+ return !!declaration && ts.isParameter(declaration) && (!!declaration.questionToken || !!declaration.initializer || !!declaration.dotDotDotToken);
1383
+ }
1384
+ var cache = /* @__PURE__ */ new WeakMap();
1385
+ function templateDiagnostics(ts, ls, fileName) {
1386
+ const program = ls.getProgram();
1387
+ const sf = program?.getSourceFile(fileName);
1388
+ if (!program || !sf) return [];
1389
+ const hit = cache.get(sf);
1390
+ if (hit) return hit;
1391
+ const checker = program.getTypeChecker();
1392
+ const out = [];
1393
+ out.push(...accessorDiagnostics(ts, program, sf));
1394
+ for (const { fn, paramName, type } of inferParamsIn(ts, program, sf)) {
1395
+ if (acceptsAnyProperty(ts, checker, type)) continue;
1396
+ const visit = (node) => {
1397
+ if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === paramName && ts.isIdentifier(node.name)) {
1398
+ const symbol = checker.getSymbolAtLocation(node.expression);
1399
+ const declaredHere = symbol?.declarations?.some((d) => d.parent === fn);
1400
+ if (declaredHere && !checker.getPropertyOfType(type, node.name.text)) {
1401
+ out.push({
1402
+ file: sf,
1403
+ start: node.name.getStart(sf),
1404
+ length: node.name.getWidth(sf),
1405
+ category: ts.DiagnosticCategory.Error,
1406
+ code: 2339,
1407
+ messageText: `Property '${node.name.text}' does not exist on type '${checker.typeToString(type)}'.`,
1408
+ source: "fluixi"
1334
1409
  });
1335
- } else if (a.kind === "PropertyBinding") {
1336
- entry(a.loc.start + 1, a.name, () => putHole(a.hole));
1337
1410
  }
1338
1411
  }
1339
- gen += " })";
1412
+ ts.forEachChild(node, visit);
1340
1413
  };
1414
+ if (fn.body) visit(fn.body);
1341
1415
  }
1342
- walkComponents(root.children);
1343
- for (let i = 0; i < tpl.holes.length; i++) {
1344
- if (consumed.has(i)) continue;
1345
- const role = roles.get(i);
1346
- if (role && role.kind === "eachRender") {
1347
- calls.push(() => {
1348
- gen += "__fxEach(";
1349
- putHole(role.arrayHole);
1350
- gen += ", ";
1351
- putHole(i);
1352
- gen += ")";
1353
- });
1354
- } else if (role && role.kind === "indexRender") {
1355
- calls.push(() => {
1356
- gen += "__fxIndex(";
1357
- putHole(role.arrayHole);
1358
- gen += ", ";
1359
- putHole(i);
1360
- gen += ")";
1361
- });
1362
- } else if (role && role.kind === "showRender") {
1363
- calls.push(() => {
1364
- gen += "__fxShow(";
1365
- putHole(role.whenHole);
1366
- gen += ", ";
1367
- putHole(i);
1368
- gen += ")";
1369
- });
1370
- } else if (role && role.kind === "event") {
1371
- const key = EVENT_MAP_KEYS.has(role.event) ? role.event : role.event;
1372
- calls.push(() => {
1373
- gen += `__fxOn(${JSON.stringify(key)}, `;
1374
- putHole(i);
1375
- gen += ")";
1376
- });
1377
- } else if (role && role.kind === "ref" && isFn(ts, tpl.holes[i])) {
1378
- const tag = role.tag;
1379
- calls.push(() => {
1380
- gen += `__fxRef(${JSON.stringify(tag)}, `;
1381
- putHole(i);
1382
- gen += ")";
1383
- });
1384
- } else {
1385
- calls.push(() => {
1386
- gen += "__fxExpr(";
1387
- putHole(i);
1388
- gen += ")";
1389
- });
1390
- }
1416
+ cache.set(sf, out);
1417
+ return out;
1418
+ }
1419
+
1420
+ // src/fixes.ts
1421
+ function templateCodeFixes(ts, ls, fileName, start, end, errorCodes) {
1422
+ if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return [];
1423
+ const out = [];
1424
+ for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
1425
+ if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
1426
+ if (diagnostic.start > end || diagnostic.start + (diagnostic.length ?? 0) < start) continue;
1427
+ const at = diagnostic.start + (diagnostic.length ?? 0);
1428
+ const name = diagnostic.file?.text.slice(diagnostic.start, at) ?? "value";
1429
+ out.push({
1430
+ fixName: "fluixiCallAccessor",
1431
+ description: `Call '${name}'`,
1432
+ changes: [
1433
+ {
1434
+ fileName,
1435
+ textChanges: [{ span: { start: at, length: 0 }, newText: "()" }]
1436
+ }
1437
+ ],
1438
+ // Every occurrence in the file is the same edit, so offer to do them at once.
1439
+ fixAllDescription: "Call every uncalled accessor in this file"
1440
+ });
1391
1441
  }
1392
- gen += "(";
1393
- calls.forEach((emit, idx) => {
1394
- if (idx > 0) gen += ", ";
1395
- emit();
1396
- });
1397
- gen += calls.length > 0 ? ", undefined as any)" : "undefined as any)";
1398
- return gen;
1442
+ return out;
1443
+ }
1444
+ function templateFixAll(ts, ls, fileName, errorCodes) {
1445
+ if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return void 0;
1446
+ const textChanges = [];
1447
+ for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
1448
+ if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
1449
+ const at = diagnostic.start + (diagnostic.length ?? 0);
1450
+ textChanges.push({ span: { start: at, length: 0 }, newText: "()" });
1451
+ }
1452
+ if (textChanges.length === 0) return void 0;
1453
+ textChanges.sort((a2, b2) => b2.span.start - a2.span.start);
1454
+ return { changes: [{ fileName, textChanges }], commands: void 0 };
1399
1455
  }
1400
1456
 
1401
- // src/shadow.ts
1402
- var SUFFIX = ".__fx.ts";
1403
- function isShadowPath(fileName) {
1404
- return fileName.endsWith(SUFFIX);
1405
- }
1406
- function shadowPathFor(fileName) {
1407
- return fileName + SUFFIX;
1408
- }
1409
- function sourcePathFor(shadow) {
1410
- return shadow.slice(0, -SUFFIX.length);
1411
- }
1412
- var ShadowFiles = class {
1413
- constructor(ts, host) {
1414
- this.ts = ts;
1415
- this.host = host;
1416
- this.patchHost();
1417
- }
1418
- built = /* @__PURE__ */ new Map();
1419
- /** Source files known to contain no templates, so we skip building a shadow. */
1420
- barren = /* @__PURE__ */ new Map();
1421
- /**
1422
- * Teach the existing host about shadow paths. The language service holds this
1423
- * host object, so overriding its methods in place is enough — no second service.
1424
- */
1425
- patchHost() {
1426
- const ts = this.ts;
1427
- const host = this.host;
1428
- const self = this;
1429
- const originalNames = host.getScriptFileNames.bind(host);
1430
- host.getScriptFileNames = () => {
1431
- const names = originalNames();
1432
- const out = names.slice();
1433
- for (const name of names) {
1434
- if (!isShadowPath(name) && self.build(name)) out.push(shadowPathFor(name));
1457
+ // src/navigation.ts
1458
+ var spansByFile = /* @__PURE__ */ new WeakMap();
1459
+ function tagSpans(ts, sf) {
1460
+ const hit = spansByFile.get(sf);
1461
+ if (hit) return hit;
1462
+ const out = [];
1463
+ for (const template of templatesIn(ts, sf)) {
1464
+ const { root } = parseCached(ts, sf, template.template);
1465
+ const walk = (nodes) => {
1466
+ for (const node of nodes) {
1467
+ if (node.kind === "Component" && node.tag) {
1468
+ const component = node;
1469
+ const name = component.tag.split(".")[0];
1470
+ out.push({ name, start: component.loc.start + 1, length: name.length });
1471
+ const closing = closingTagName(sf.text, component.loc.end, component.tag);
1472
+ if (closing !== void 0) out.push({ name, start: closing, length: name.length });
1473
+ }
1474
+ if ("children" in node && node.children) walk(node.children);
1435
1475
  }
1436
- return out;
1437
1476
  };
1438
- const originalSnapshot = host.getScriptSnapshot.bind(host);
1439
- host.getScriptSnapshot = (fileName) => {
1440
- if (!isShadowPath(fileName)) return originalSnapshot(fileName);
1441
- const built = self.build(sourcePathFor(fileName));
1442
- return built ? ts.ScriptSnapshot.fromString(built.text) : void 0;
1443
- };
1444
- const originalVersion = host.getScriptVersion.bind(host);
1445
- host.getScriptVersion = (fileName) => isShadowPath(fileName) ? originalVersion(sourcePathFor(fileName)) : originalVersion(fileName);
1446
- if (typeof host.fileExists === "function") {
1447
- const originalExists = host.fileExists.bind(host);
1448
- host.fileExists = (fileName) => isShadowPath(fileName) ? !!self.build(sourcePathFor(fileName)) : originalExists(fileName);
1449
- }
1477
+ walk(root.children);
1450
1478
  }
1451
- /** Build (and cache per source version) the shadow text for a file. */
1452
- build(fileName) {
1453
- const version = this.host.getScriptVersion(fileName);
1454
- if (this.barren.get(fileName) === version) return void 0;
1455
- const hit = this.built.get(fileName);
1456
- if (hit && hit.version === version) return hit;
1457
- const snapshot = this.host.getScriptSnapshot(fileName);
1458
- if (!snapshot) return void 0;
1459
- const text = snapshot.getText(0, snapshot.getLength());
1460
- if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) {
1461
- this.barren.set(fileName, version);
1462
- this.built.delete(fileName);
1463
- return void 0;
1479
+ spansByFile.set(sf, out);
1480
+ return out;
1481
+ }
1482
+ function closingTagName(text, end, tag) {
1483
+ const open = text.lastIndexOf("</", end);
1484
+ if (open === -1) return void 0;
1485
+ const close = text.indexOf(">", open);
1486
+ if (close === -1 || close >= end) return void 0;
1487
+ const index = text.indexOf(tag, open);
1488
+ return index !== -1 && index < close ? index : void 0;
1489
+ }
1490
+ function tagAt(ts, sf, position) {
1491
+ if (!templateAtCached(ts, sf, position)) return void 0;
1492
+ return tagSpans(ts, sf).find((s2) => position >= s2.start && position <= s2.start + s2.length);
1493
+ }
1494
+ function symbolFor(ts, program, host, location, name) {
1495
+ const checker = program.getTypeChecker();
1496
+ const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
1497
+ const symbol = checker.getSymbolsInScope(location, flags).find((s2) => s2.name === name);
1498
+ if (!symbol) return autoImportedSymbol(ts, program, host, location.getSourceFile(), name);
1499
+ return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
1500
+ }
1501
+ function declarationFor(ts, program, host, sf, tag) {
1502
+ const template = templateAtCached(ts, sf, tag.start);
1503
+ if (!template) return void 0;
1504
+ const symbol = symbolFor(ts, program, host, template, tag.name);
1505
+ return symbol?.valueDeclaration ?? symbol?.declarations?.[0];
1506
+ }
1507
+ function templateDefinition(ts, ls, host, fileName, position) {
1508
+ const program = ls.getProgram();
1509
+ const sf = program?.getSourceFile(fileName);
1510
+ if (!program || !sf) return void 0;
1511
+ const tag = tagAt(ts, sf, position);
1512
+ if (!tag) return void 0;
1513
+ const decl = declarationFor(ts, program, host, sf, tag);
1514
+ if (!decl) {
1515
+ const site = declarationSite(ts, program, host, sf, tag.name);
1516
+ if (!site) return void 0;
1517
+ return [
1518
+ {
1519
+ fileName: site.fileName,
1520
+ textSpan: { start: site.start, length: site.length },
1521
+ kind: ts.ScriptElementKind.unknown,
1522
+ name: tag.name,
1523
+ containerKind: ts.ScriptElementKind.unknown,
1524
+ containerName: ""
1525
+ }
1526
+ ];
1527
+ }
1528
+ const target = decl.name ?? decl;
1529
+ const declFile = decl.getSourceFile();
1530
+ return [
1531
+ {
1532
+ fileName: declFile.fileName,
1533
+ textSpan: { start: target.getStart(declFile), length: target.getWidth(declFile) },
1534
+ kind: ts.ScriptElementKind.unknown,
1535
+ name: tag.name,
1536
+ containerKind: ts.ScriptElementKind.unknown,
1537
+ containerName: ""
1464
1538
  }
1465
- const sf = this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.Latest, true);
1466
- const virtual = buildVirtual(this.ts, sf);
1467
- if (!virtual) {
1468
- this.barren.set(fileName, version);
1469
- this.built.delete(fileName);
1470
- return void 0;
1539
+ ];
1540
+ }
1541
+ function tagOccurrences(ts, program, host, symbol, name) {
1542
+ const out = [];
1543
+ for (const sf of program.getSourceFiles()) {
1544
+ if (sf.isDeclarationFile) continue;
1545
+ const text = sf.text;
1546
+ if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) continue;
1547
+ if (text.indexOf(name) === -1) continue;
1548
+ for (const span of tagSpans(ts, sf)) {
1549
+ if (span.name !== name) continue;
1550
+ const template = templateAtCached(ts, sf, span.start);
1551
+ if (!template || symbolFor(ts, program, host, template, name) !== symbol) continue;
1552
+ out.push({ fileName: sf.fileName, span: { start: span.start, length: span.length } });
1471
1553
  }
1472
- const entry = {
1473
- version,
1474
- text: virtual.text + "\nexport {};\n",
1475
- segments: virtual.segments
1476
- };
1477
- this.built.set(fileName, entry);
1478
- return entry;
1479
- }
1480
- /** The shadow path for a source file, when it has one. */
1481
- shadowFor(fileName) {
1482
- return this.build(fileName) ? shadowPathFor(fileName) : void 0;
1483
- }
1484
- segmentsFor(fileName) {
1485
- return this.build(fileName)?.segments;
1486
1554
  }
1487
- toSrc(fileName, position) {
1488
- const segments = this.segmentsFor(fileName);
1489
- return segments ? toSource(segments, position) : -1;
1490
- }
1491
- inHole(fileName, position) {
1492
- const segments = this.segmentsFor(fileName);
1493
- return !!segments && inHole(segments, position);
1494
- }
1495
- inKey(fileName, position) {
1496
- const segments = this.segmentsFor(fileName);
1497
- return !!segments && inKey(segments, position);
1498
- }
1499
- };
1555
+ return out;
1556
+ }
1557
+ function symbolAt(ts, program, host, sf, position) {
1558
+ const checker = program.getTypeChecker();
1559
+ const tag = tagAt(ts, sf, position);
1560
+ if (tag) {
1561
+ const template = templateAtCached(ts, sf, tag.start);
1562
+ const symbol2 = template && symbolFor(ts, program, host, template, tag.name);
1563
+ return symbol2 ? { symbol: symbol2, name: tag.name } : void 0;
1564
+ }
1565
+ const token = tokenAt(ts, sf, position);
1566
+ if (!token || !ts.isIdentifier(token)) return void 0;
1567
+ let symbol = checker.getSymbolAtLocation(token);
1568
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
1569
+ return symbol ? { symbol, name: token.text } : void 0;
1570
+ }
1571
+ function tokenAt(ts, sf, position) {
1572
+ let found;
1573
+ const visit = (node) => {
1574
+ if (position < node.getStart(sf) || position > node.getEnd()) return;
1575
+ if (node.getChildCount(sf) === 0) found = node;
1576
+ ts.forEachChild(node, visit);
1577
+ };
1578
+ visit(sf);
1579
+ return found;
1580
+ }
1581
+ function templateReferences(ts, ls, host, fileName, position) {
1582
+ const program = ls.getProgram();
1583
+ const sf = program?.getSourceFile(fileName);
1584
+ if (!program || !sf) return [];
1585
+ const hit = symbolAt(ts, program, host, sf, position);
1586
+ if (!hit) return [];
1587
+ return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
1588
+ fileName: f2,
1589
+ textSpan: span,
1590
+ isWriteAccess: false,
1591
+ isDefinition: false
1592
+ }));
1593
+ }
1594
+ function templateRenameLocations(ts, ls, host, fileName, position) {
1595
+ const program = ls.getProgram();
1596
+ const sf = program?.getSourceFile(fileName);
1597
+ if (!program || !sf) return [];
1598
+ const hit = symbolAt(ts, program, host, sf, position);
1599
+ if (!hit) return [];
1600
+ return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
1601
+ fileName: f2,
1602
+ textSpan: span
1603
+ }));
1604
+ }
1500
1605
 
1501
1606
  // src/index.ts
1502
1607
  function init(modules) {
@@ -1505,12 +1610,6 @@ function init(modules) {
1505
1610
  create(info) {
1506
1611
  const ls = info.languageService;
1507
1612
  const host = info.languageServiceHost;
1508
- let shadows;
1509
- try {
1510
- shadows = new ShadowFiles(ts, info.languageServiceHost);
1511
- } catch {
1512
- shadows = void 0;
1513
- }
1514
1613
  const proxy = /* @__PURE__ */ Object.create(null);
1515
1614
  for (const key of Object.keys(ls)) {
1516
1615
  const member = ls[key];
@@ -1520,9 +1619,24 @@ function init(modules) {
1520
1619
  isGlobalCompletion: false,
1521
1620
  isMemberCompletion: false,
1522
1621
  isNewIdentifierLocation: true,
1622
+ // Re-ask on every keystroke instead of letting the editor filter what it already
1623
+ // has. Inside a template the valid set changes with the cursor — a tag name, then
1624
+ // that tag's props — and a list cached from one position is wrong at the next.
1625
+ // Without this the props appear only after enough typing to force a fresh
1626
+ // request, which reads as completion working intermittently.
1627
+ isIncomplete: true,
1523
1628
  entries
1524
1629
  });
1525
1630
  const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
1631
+ const dedupe = (items) => {
1632
+ const seen = /* @__PURE__ */ new Set();
1633
+ return items.filter((i2) => {
1634
+ const key = `${i2.fileName}:${i2.textSpan.start}:${i2.textSpan.length}`;
1635
+ if (seen.has(key)) return false;
1636
+ seen.add(key);
1637
+ return true;
1638
+ });
1639
+ };
1526
1640
  const reliableFilter = (fileName, diagnostics) => {
1527
1641
  const program = ls.getProgram();
1528
1642
  const sourceFile = program?.getSourceFile(fileName);
@@ -1534,41 +1648,18 @@ function init(modules) {
1534
1648
  return true;
1535
1649
  });
1536
1650
  };
1537
- const holeDiagnostics = (fileName) => {
1538
- const shadow = shadows?.shadowFor(fileName);
1539
- if (!shadow) return [];
1651
+ proxy.getSemanticDiagnostics = (fileName) => {
1652
+ const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1540
1653
  try {
1541
- const sourceFile = ls.getProgram()?.getSourceFile(fileName);
1542
- const out = [];
1543
- for (const d of ls.getSemanticDiagnostics(shadow)) {
1544
- if (d.start === void 0) continue;
1545
- const start = shadows.toSrc(fileName, d.start);
1546
- if (start === -1) continue;
1547
- if (!shadows.inHole(fileName, start) && !shadows.inKey(fileName, start)) continue;
1548
- out.push({ ...d, start, file: sourceFile });
1549
- }
1550
- return out;
1654
+ return base.concat(templateDiagnostics(ts, ls, fileName));
1551
1655
  } catch {
1552
- return [];
1656
+ return base;
1553
1657
  }
1554
1658
  };
1555
- proxy.getSemanticDiagnostics = (fileName) => {
1556
- if (isShadowPath(fileName)) return [];
1557
- const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1558
- return base.concat(holeDiagnostics(fileName));
1559
- };
1560
- proxy.getSuggestionDiagnostics = (fileName) => isShadowPath(fileName) ? [] : reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1561
- proxy.getSyntacticDiagnostics = (fileName) => isShadowPath(fileName) ? [] : ls.getSyntacticDiagnostics(fileName);
1562
- const notShadow = (items) => items?.filter((i) => !isShadowPath(i.fileName));
1563
- proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => notShadow(ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs));
1564
- proxy.getReferencesAtPosition = (fileName, position) => notShadow(ls.getReferencesAtPosition(fileName, position));
1565
- proxy.getDefinitionAtPosition = (fileName, position) => notShadow(ls.getDefinitionAtPosition(fileName, position));
1566
- proxy.getImplementationAtPosition = (fileName, position) => notShadow(ls.getImplementationAtPosition(fileName, position));
1567
- proxy.getDocumentHighlights = (fileName, position, filesToSearch) => ls.getDocumentHighlights(fileName, position, filesToSearch)?.filter((h) => !isShadowPath(h.fileName));
1568
- proxy.findReferences = (fileName, position) => ls.findReferences(fileName, position)?.filter((r) => !isShadowPath(r.definition.fileName)).map((r) => ({ ...r, references: r.references.filter((x2) => !isShadowPath(x2.fileName)) }));
1659
+ proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1569
1660
  proxy.getQuickInfoAtPosition = (fileName, position) => {
1570
1661
  try {
1571
- const prop = componentPropHover(ts, ls, fileName, position);
1662
+ const prop = componentPropHover(ts, ls, host, fileName, position);
1572
1663
  if (prop) return prop;
1573
1664
  } catch {
1574
1665
  }
@@ -1582,11 +1673,16 @@ function init(modules) {
1582
1673
  if (inferred) return inferred;
1583
1674
  } catch {
1584
1675
  }
1585
- return ls.getQuickInfoAtPosition(fileName, position) ?? componentQuickInfo(ts, ls, fileName, position);
1676
+ try {
1677
+ const tag = componentQuickInfo(ts, ls, host, fileName, position);
1678
+ if (tag) return tag;
1679
+ } catch {
1680
+ }
1681
+ return ls.getQuickInfoAtPosition(fileName, position);
1586
1682
  };
1587
1683
  proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
1588
1684
  try {
1589
- const props = componentPropCompletions(ts, ls, fileName, position);
1685
+ const props = componentPropCompletions(ts, ls, host, fileName, position);
1590
1686
  if (props) return completionList(props);
1591
1687
  } catch {
1592
1688
  }
@@ -1602,6 +1698,71 @@ function init(modules) {
1602
1698
  }
1603
1699
  return ls.getCompletionsAtPosition(fileName, position, options, formatting);
1604
1700
  };
1701
+ proxy.getDefinitionAtPosition = (fileName, position) => {
1702
+ try {
1703
+ const tag = templateDefinition(ts, ls, host, fileName, position);
1704
+ if (tag) return tag;
1705
+ } catch {
1706
+ }
1707
+ return ls.getDefinitionAtPosition(fileName, position);
1708
+ };
1709
+ proxy.getDefinitionAndBoundSpan = (fileName, position) => {
1710
+ try {
1711
+ const definitions = templateDefinition(ts, ls, host, fileName, position);
1712
+ if (definitions) {
1713
+ const sf = ls.getProgram()?.getSourceFile(fileName);
1714
+ const tag = sf && tagAt(ts, sf, position);
1715
+ if (tag) {
1716
+ return { definitions, textSpan: { start: tag.start, length: tag.length } };
1717
+ }
1718
+ }
1719
+ } catch {
1720
+ }
1721
+ return ls.getDefinitionAndBoundSpan(fileName, position);
1722
+ };
1723
+ proxy.getReferencesAtPosition = (fileName, position) => {
1724
+ const base = ls.getReferencesAtPosition(fileName, position) ?? [];
1725
+ try {
1726
+ const extra = templateReferences(ts, ls, host, fileName, position);
1727
+ return extra.length ? dedupe([...base, ...extra]) : base;
1728
+ } catch {
1729
+ return base;
1730
+ }
1731
+ };
1732
+ proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => {
1733
+ const base = ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs) ?? [];
1734
+ try {
1735
+ const extra = templateRenameLocations(ts, ls, host, fileName, position);
1736
+ if (!base.length || !extra.length) return base.length ? base : void 0;
1737
+ return dedupe([...base, ...extra]);
1738
+ } catch {
1739
+ return base;
1740
+ }
1741
+ };
1742
+ proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
1743
+ const base = ls.getCodeFixesAtPosition(
1744
+ fileName,
1745
+ start,
1746
+ end,
1747
+ errorCodes,
1748
+ formatOptions,
1749
+ preferences
1750
+ );
1751
+ try {
1752
+ const own = templateCodeFixes(ts, ls, fileName, start, end, errorCodes);
1753
+ return own.length ? [...base, ...own] : base;
1754
+ } catch {
1755
+ return base;
1756
+ }
1757
+ };
1758
+ proxy.getCombinedCodeFix = (scope, fixId, formatOptions, preferences) => {
1759
+ if (fixId === "fluixiCallAccessor") {
1760
+ const fileName = scope.fileName;
1761
+ const combined = templateFixAll(ts, ls, fileName, [ACCESSOR_NOT_CALLED]);
1762
+ if (combined) return combined;
1763
+ }
1764
+ return ls.getCombinedCodeFix(scope, fixId, formatOptions, preferences);
1765
+ };
1605
1766
  return proxy;
1606
1767
  }
1607
1768
  };