@fluixi/ts-plugin 0.1.0-alpha.1 → 0.1.0-alpha.11

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 (3) hide show
  1. package/README.md +45 -14
  2. package/dist/index.js +1032 -635
  3. 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,53 +451,214 @@ 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
  }
426
- function templateAt(ts, sf, pos) {
427
- let found;
477
+
478
+ // src/cache.ts
479
+ var parsed = /* @__PURE__ */ new WeakMap();
480
+ function parseCached(ts, sf, tpl) {
481
+ let byTemplate = parsed.get(sf);
482
+ if (!byTemplate) {
483
+ byTemplate = /* @__PURE__ */ new Map();
484
+ parsed.set(sf, byTemplate);
485
+ }
486
+ const key = tpl.getStart(sf);
487
+ const hit = byTemplate.get(key);
488
+ if (hit) return hit;
489
+ const result = b(templatePieces(ts, tpl, sf));
490
+ byTemplate.set(key, result);
491
+ return result;
492
+ }
493
+ var templates = /* @__PURE__ */ new WeakMap();
494
+ function templatesIn(ts, sf) {
495
+ const hit = templates.get(sf);
496
+ if (hit) return hit;
497
+ const found = [];
428
498
  const visit = (node) => {
429
- if (found) return;
430
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg") && pos >= node.template.getStart(sf) && pos <= node.template.getEnd()) {
431
- found = node;
432
- return;
499
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
500
+ found.push(node);
433
501
  }
434
502
  ts.forEachChild(node, visit);
435
503
  };
436
504
  visit(sf);
505
+ found.sort((a2, b2) => a2.getStart(sf) - b2.getStart(sf));
506
+ templates.set(sf, found);
437
507
  return found;
438
508
  }
509
+ function templateAtCached(ts, sf, pos) {
510
+ let match;
511
+ for (const tpl of templatesIn(ts, sf)) {
512
+ const start = tpl.template.getStart(sf);
513
+ if (start > pos) break;
514
+ if (pos <= tpl.template.getEnd()) match = tpl;
515
+ }
516
+ return match;
517
+ }
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
+ }
439
639
 
440
640
  // src/hover.ts
441
- function componentQuickInfo(ts, ls, fileName, position) {
641
+ function componentQuickInfo(ts, ls, host, fileName, position) {
442
642
  const program = ls.getProgram();
443
643
  const sf = program?.getSourceFile(fileName);
444
644
  if (!program || !sf) return void 0;
445
- const template = templateAt(ts, sf, position);
645
+ const template = templateAtCached(ts, sf, position);
446
646
  if (!template) return void 0;
447
647
  const hit = componentTagAt(template, sf, position, ts);
448
648
  if (!hit) return void 0;
449
649
  const checker = program.getTypeChecker();
450
650
  const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
451
- const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name);
651
+ const symbol = checker.getSymbolsInScope(template, flags).find((s2) => s2.name === hit.name) ?? autoImportedSymbol(ts, program, host, sf, hit.name);
452
652
  const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
453
653
  if (!decl) return void 0;
454
- const namePos = decl.name?.getStart(sf) ?? decl.getStart(sf);
455
- 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);
456
657
  if (!info) return void 0;
457
658
  return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
458
659
  }
459
660
  function componentTagAt(template, sf, position, ts) {
460
- const { root } = k(templatePieces(ts, template.template, sf));
661
+ const { root } = parseCached(ts, sf, template.template);
461
662
  let hit;
462
663
  const walk = (nodes) => {
463
664
  for (const node of nodes) {
@@ -584,21 +785,21 @@ function holeRoles(nodes, out) {
584
785
  };
585
786
  for (const node of nodes) {
586
787
  if (node.kind === "Element" || node.kind === "Component") {
587
- for (const a of node.attributes) {
588
- if (a.kind === "EventBinding") out.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
589
- if (node.kind === "Element" && a.kind === "RefBinding") out.set(a.hole, { kind: "refEl", tag: node.tag });
590
- if (node.kind === "Element" && a.kind === "UseDirective" && a.hole != null) out.set(a.hole, { kind: "refEl", tag: node.tag });
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 });
591
792
  }
592
- const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
793
+ const eachDir = node.attributes.find((a2) => a2.kind === "EachDirective");
593
794
  if (eachDir && eachDir.kind === "EachDirective") {
594
795
  renderChildren(node, { kind: "each", eachHole: eachDir.hole });
595
796
  }
596
797
  if (node.kind === "Component") {
597
798
  const propHole = (name) => {
598
- const a = node.attributes.find(
799
+ const a2 = node.attributes.find(
599
800
  (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
600
801
  );
601
- 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;
602
803
  };
603
804
  const eachHole = propHole("each");
604
805
  if (node.tag === "For" && eachHole !== void 0) renderChildren(node, { kind: "each", eachHole });
@@ -611,7 +812,7 @@ function holeRoles(nodes, out) {
611
812
  }
612
813
  }
613
814
  function resolveGlobalType(ts, checker, location, name) {
614
- 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);
615
816
  return sym ? checker.getDeclaredTypeOfSymbol(sym) : void 0;
616
817
  }
617
818
  function resolveElementType(ts, checker, location, tag) {
@@ -621,14 +822,14 @@ function resolveElementType(ts, checker, location, tag) {
621
822
  return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
622
823
  }
623
824
  function inferParamAt(ts, program, sf, position) {
624
- const template = templateAt(ts, sf, position);
825
+ const template = templateAtCached(ts, sf, position);
625
826
  if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
626
827
  const spans = template.template.templateSpans;
627
- const holeExprs = spans.map((s) => s.expression);
828
+ const holeExprs = spans.map((s2) => s2.expression);
628
829
  const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
629
830
  if (holeIndex === -1) return void 0;
630
831
  const roles = /* @__PURE__ */ new Map();
631
- holeRoles(k(templatePieces(ts, template.template, sf)).root.children, roles);
832
+ holeRoles(parseCached(ts, sf, template.template).root.children, roles);
632
833
  const role = roles.get(holeIndex);
633
834
  if (!role) return void 0;
634
835
  const fn = holeExprs[holeIndex];
@@ -639,6 +840,12 @@ function inferParamAt(ts, program, sf, position) {
639
840
  const id = identifierAt(ts, sf, position);
640
841
  if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
641
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) {
642
849
  let typeText;
643
850
  let type;
644
851
  if (role.kind === "event") {
@@ -659,8 +866,27 @@ function inferParamAt(ts, program, sf, position) {
659
866
  type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
660
867
  if (type) typeText = checker.typeToString(type);
661
868
  }
662
- if (!typeText) return void 0;
663
- 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;
664
890
  }
665
891
  function paramQuickInfo(ts, ls, fileName, position) {
666
892
  const program = ls.getProgram();
@@ -702,40 +928,39 @@ function contains(outer, inner) {
702
928
  }
703
929
 
704
930
  // src/props.ts
705
- function componentPropCompletions(ts, ls, fileName, position) {
931
+ function componentPropCompletions(ts, ls, host, fileName, position) {
706
932
  const program = ls.getProgram();
707
933
  const sf = program?.getSourceFile(fileName);
708
934
  if (!program || !sf) return void 0;
709
- const template = templateAt(ts, sf, position);
935
+ const template = templateAtCached(ts, sf, position);
710
936
  if (!template) return void 0;
711
937
  const comp = componentTagAtAttrArea(ts, sf, template.template, position);
712
938
  if (!comp || !comp.tag) return void 0;
939
+ const span = attrNameSpan(sf.text, position);
713
940
  const cf = CONTROL_FLOW_PROPS[comp.tag];
714
941
  if (cf) {
715
942
  const already2 = new Set(comp.attributeNames);
716
- 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));
717
944
  }
718
945
  const checker = program.getTypeChecker();
719
946
  const rootName = comp.tag.split(".")[0];
720
- const decl = findDeclaration(ts, sf, rootName);
721
- if (!decl) return void 0;
722
- const compType = checker.getTypeAtLocation(decl);
723
- const propsType = firstParamType(ts, checker, compType);
947
+ const compType = componentTypeOf(ts, program, checker, host, sf, template.template, rootName);
948
+ const propsType = compType && firstParamType(ts, checker, compType);
724
949
  if (!propsType) return void 0;
725
950
  const already = new Set(comp.attributeNames);
726
951
  const entries = [];
727
- for (const p2 of propsType.getProperties()) {
728
- const name = p2.getName();
952
+ for (const p3 of propsType.getProperties()) {
953
+ const name = p3.getName();
729
954
  if (name === "children" || already.has(name)) continue;
730
- entries.push(propEntry(ts, name));
955
+ entries.push(propEntry(ts, name, span));
731
956
  }
732
957
  return entries.length ? entries : void 0;
733
958
  }
734
- function componentPropHover(ts, ls, fileName, position) {
959
+ function componentPropHover(ts, ls, host, fileName, position) {
735
960
  const program = ls.getProgram();
736
961
  const sf = program?.getSourceFile(fileName);
737
962
  if (!program || !sf) return void 0;
738
- const template = templateAt(ts, sf, position);
963
+ const template = templateAtCached(ts, sf, position);
739
964
  if (!template) return void 0;
740
965
  const hit = propNameAt(ts, sf, template.template, position);
741
966
  if (!hit) return void 0;
@@ -746,11 +971,13 @@ function componentPropHover(ts, ls, fileName, position) {
746
971
  if (!typeText) return void 0;
747
972
  } else if (hit.tag) {
748
973
  const checker = program.getTypeChecker();
749
- const decl = findDeclaration(ts, sf, hit.tag.split(".")[0]);
750
- 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);
751
977
  const sym = propsType && propsType.getProperty(hit.prop);
752
978
  if (!sym) return void 0;
753
- 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));
754
981
  }
755
982
  if (!typeText) return void 0;
756
983
  return {
@@ -771,16 +998,16 @@ function componentPropHover(ts, ls, fileName, position) {
771
998
  };
772
999
  }
773
1000
  function propNameAt(ts, sf, tpl, position) {
774
- const { root } = k(templatePieces(ts, tpl, sf));
1001
+ const { root } = parseCached(ts, sf, tpl);
775
1002
  let hit;
776
1003
  const walk = (nodes) => {
777
1004
  for (const node of nodes) {
778
1005
  if (node.kind === "Component") {
779
1006
  const c = node;
780
- for (const a of c.attributes) {
781
- if ((a.kind === "Attribute" || a.kind === "PropertyBinding" || a.kind === "BindDirective") && "loc" in a) {
782
- const start = a.loc.start;
783
- if (position >= start && position <= start + a.name.length) hit = { tag: c.tag, prop: a.name, start };
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 };
784
1011
  }
785
1012
  }
786
1013
  }
@@ -817,36 +1044,45 @@ var CONTROL_FLOW_PROPS = {
817
1044
  Dynamic: ["component"],
818
1045
  Await: ["value", "fallback"]
819
1046
  };
820
- function propEntry(ts, name) {
821
- return { name, kind: ts.ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: "0_" + name, insertText: name };
822
- }
823
- function findDeclaration(ts, sf, name) {
824
- for (const stmt of sf.statements) {
825
- if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && stmt.name?.text === name) return stmt.name;
826
- if (ts.isVariableStatement(stmt)) {
827
- for (const d of stmt.declarationList.declarations) {
828
- if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
829
- }
830
- }
831
- if (ts.isImportDeclaration(stmt)) {
832
- const b2 = stmt.importClause?.namedBindings;
833
- if (b2 && ts.isNamedImports(b2)) {
834
- for (const el of b2.elements) if (el.name.text === name) return el.name;
835
- }
836
- if (stmt.importClause?.name?.text === name) return stmt.importClause.name;
837
- }
838
- }
839
- return void 0;
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;
840
1076
  }
841
1077
  function firstParamType(ts, checker, type) {
842
1078
  for (const sig of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
843
- const p2 = sig.getParameters()[0];
844
- if (p2) return checker.getTypeOfSymbolAtLocation(p2, p2.valueDeclaration ?? sig.getDeclaration());
1079
+ const p3 = sig.getParameters()[0];
1080
+ if (p3) return checker.getTypeOfSymbolAtLocation(p3, p3.valueDeclaration ?? sig.getDeclaration());
845
1081
  }
846
1082
  return void 0;
847
1083
  }
848
1084
  function componentTagAtAttrArea(ts, sf, tpl, position) {
849
- const { root } = k(templatePieces(ts, tpl, sf));
1085
+ const { root } = parseCached(ts, sf, tpl);
850
1086
  let hit;
851
1087
  const walk = (nodes) => {
852
1088
  for (const node of nodes) {
@@ -854,7 +1090,7 @@ function componentTagAtAttrArea(ts, sf, tpl, position) {
854
1090
  const c = node;
855
1091
  const openStart = c.loc.start + 1 + (c.tag ? c.tag.length : 0);
856
1092
  const openEnd = firstChildOrCloseStart(c);
857
- if (position > openStart && position <= openEnd) {
1093
+ if (position > openStart && position < openEnd) {
858
1094
  hit = { tag: c.tag, attributeNames: attributeNames(c) };
859
1095
  }
860
1096
  }
@@ -872,8 +1108,8 @@ function firstChildOrCloseStart(c) {
872
1108
  }
873
1109
  function attributeNames(c) {
874
1110
  const out = [];
875
- for (const a of c.attributes) {
876
- 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);
877
1113
  }
878
1114
  return out;
879
1115
  }
@@ -890,13 +1126,13 @@ function paramMemberCompletions(ts, ls, fileName, position) {
890
1126
  const checker = program.getTypeChecker();
891
1127
  const already = /* @__PURE__ */ new Set();
892
1128
  const entries = [];
893
- for (const p2 of checker.getApparentType(info.type).getProperties()) {
894
- const name = p2.getName();
1129
+ for (const p3 of checker.getApparentType(info.type).getProperties()) {
1130
+ const name = p3.getName();
895
1131
  if (already.has(name) || name.startsWith("__")) continue;
896
1132
  already.add(name);
897
1133
  entries.push({
898
1134
  name,
899
- kind: memberKind(ts, p2),
1135
+ kind: memberKind(ts, p3),
900
1136
  kindModifiers: "",
901
1137
  sortText: "0_" + name,
902
1138
  insertText: name
@@ -921,366 +1157,450 @@ function propertyAccessAt(ts, sf, position) {
921
1157
  return found;
922
1158
  }
923
1159
 
924
- // src/virtual.ts
925
- var EVENT_MAP_KEYS = new Set(Object.keys({
926
- click: 0,
927
- dblclick: 0,
928
- mousedown: 0,
929
- mouseup: 0,
930
- mousemove: 0,
931
- mouseenter: 0,
932
- mouseleave: 0,
933
- mouseover: 0,
934
- mouseout: 0,
935
- contextmenu: 0,
936
- keydown: 0,
937
- keyup: 0,
938
- keypress: 0,
939
- input: 0,
940
- beforeinput: 0,
941
- change: 0,
942
- submit: 0,
943
- focus: 0,
944
- blur: 0,
945
- focusin: 0,
946
- focusout: 0,
947
- pointerdown: 0,
948
- pointerup: 0,
949
- pointermove: 0,
950
- pointerenter: 0,
951
- pointerleave: 0,
952
- wheel: 0,
953
- scroll: 0,
954
- drag: 0,
955
- drop: 0,
956
- touchstart: 0,
957
- touchend: 0,
958
- touchmove: 0,
959
- animationend: 0,
960
- transitionend: 0
961
- }));
962
- var PRELUDE = (
963
- // Control-flow component prop types, declared inline so they ALWAYS resolve
964
- // (no dependency on @fluixi/core being importable from the virtual document).
965
- // Prop shapes drive hover + value checking on when/each/fallback/mount/…;
966
- // render-prop children are typed separately by __fxEach/__fxShow.
967
- 'declare const __fxCF: {\n Show: <T>(props: { when: T; fallback?: unknown; children?: unknown }) => any;\n For: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; by?: (item: T) => unknown; children?: unknown }) => any;\n Index: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; children?: unknown }) => any;\n Switch: (props: { fallback?: unknown; children?: unknown }) => any;\n Match: <T>(props: { when: T; children?: unknown }) => any;\n Suspense: (props: { fallback?: unknown; children?: unknown }) => any;\n SuspenseList: (props: { revealOrder: "forwards" | "backwards" | "together"; children?: unknown }) => any;\n Portal: (props: { mount?: Node; useShadow?: boolean; children?: unknown }) => any;\n ErrorBoundary: (props: { fallback?: unknown; children?: unknown }) => any;\n Dynamic: (props: { component: unknown; [k: string]: unknown }) => any;\n Await: <T>(props: { value: Promise<T> | (() => Promise<T>); fallback?: unknown; children?: unknown }) => any;\n};\ndeclare const __fxEach: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: T, index: () => number) => any) => any;\ndeclare const __fxIndex: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: () => T, index: number) => any) => any;\ndeclare const __fxShow: <T>(when: T, render: (value: NonNullable<T>) => any) => any;\ndeclare const __fxOn: <K extends string>(name: K, handler: (event: K extends keyof HTMLElementEventMap ? HTMLElementEventMap[K] : Event) => any) => any;\ndeclare const __fxRef: <K extends string>(tag: K, ref: (el: K extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[K] : HTMLElement) => any) => any;\ndeclare const __fxExpr: (value: any) => any;\ndeclare function __fxProps<P>(comp: (props: P, ...rest: any[]) => any, props: NoInfer<P>): any;\n'
968
- );
969
- var CONTROL_FLOW = /* @__PURE__ */ new Set([
970
- "Show",
971
- "For",
972
- "Index",
973
- "Switch",
974
- "Match",
975
- "Dynamic",
976
- "ErrorBoundary",
977
- "Suspense",
978
- "SuspenseList",
979
- "Await",
980
- "Portal"
981
- ]);
982
- function inHole(segments, pos) {
983
- return segments.some((s) => s.hole && pos >= s.srcStart && pos <= s.srcEnd);
984
- }
985
- function inKey(segments, pos) {
986
- return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
987
- }
988
- function toGenerated(segments, pos) {
989
- for (const s of segments) {
990
- if (pos >= s.srcStart && pos <= s.srcEnd) return s.genStart + (pos - s.srcStart);
991
- }
992
- return -1;
993
- }
994
- function toSource(segments, pos) {
995
- for (const s of segments) {
996
- if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
997
- }
998
- return -1;
999
- }
1000
- function buildVirtual(ts, sf) {
1001
- const templates = collectTemplates(ts, sf);
1002
- if (templates.length === 0) return void 0;
1003
- const srcText = sf.text;
1004
- const segments = [];
1005
- let gen = "";
1006
- let src = 0;
1007
- const copy = (to) => {
1008
- if (to <= src) return;
1009
- const genStart = gen.length;
1010
- gen += srcText.slice(src, to);
1011
- segments.push({ srcStart: src, srcEnd: to, genStart, genEnd: gen.length });
1012
- src = to;
1160
+ // src/elements.ts
1161
+ var ATTRIBUTES_MODULE = "@fluixi/dom/attributes";
1162
+ var perProgram = /* @__PURE__ */ new WeakMap();
1163
+ function intrinsics(ts, program, host, sf) {
1164
+ const hit = perProgram.get(program);
1165
+ if (hit) return hit;
1166
+ let type;
1167
+ try {
1168
+ const checker = program.getTypeChecker();
1169
+ const declared = (file) => {
1170
+ if (!file) return void 0;
1171
+ const moduleSymbol = checker.getSymbolAtLocation(file);
1172
+ const symbol = moduleSymbol?.exports?.get(ts.escapeLeadingUnderscores("IntrinsicElements"));
1173
+ return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : void 0;
1174
+ };
1175
+ const resolved = ts.resolveModuleName(
1176
+ ATTRIBUTES_MODULE,
1177
+ sf.fileName,
1178
+ program.getCompilerOptions(),
1179
+ host
1180
+ );
1181
+ type = declared(
1182
+ resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName)
1183
+ );
1184
+ if (!type) {
1185
+ for (const file of program.getSourceFiles()) {
1186
+ if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
1187
+ type = declared(file);
1188
+ if (type) break;
1189
+ }
1190
+ }
1191
+ } catch {
1192
+ type = void 0;
1193
+ }
1194
+ const entry = { type, byTag: /* @__PURE__ */ new Map() };
1195
+ perProgram.set(program, entry);
1196
+ return entry;
1197
+ }
1198
+ function attributesFor(ts, program, host, sf, tag) {
1199
+ const state = intrinsics(ts, program, host, sf);
1200
+ if (!state.type) return void 0;
1201
+ if (state.byTag.has(tag)) return state.byTag.get(tag);
1202
+ let props;
1203
+ const checker = program.getTypeChecker();
1204
+ const tagSymbol = state.type.getProperty(tag);
1205
+ if (tagSymbol) {
1206
+ const decl = tagSymbol.valueDeclaration ?? tagSymbol.declarations?.[0];
1207
+ if (decl) props = checker.getTypeOfSymbolAtLocation(tagSymbol, decl).getProperties();
1208
+ }
1209
+ state.byTag.set(tag, props);
1210
+ return props;
1211
+ }
1212
+ function elementAtAttributeArea(ts, sf, tpl, position) {
1213
+ const { root } = parseCached(ts, sf, tpl);
1214
+ let hit;
1215
+ const walk = (nodes) => {
1216
+ for (const node of nodes) {
1217
+ if (node.kind === "Element") {
1218
+ const el = node;
1219
+ const openStart = el.loc.start + 1 + el.tag.length;
1220
+ const openEnd = firstAttributeEnd(el);
1221
+ if (position > openStart && position <= openEnd) {
1222
+ const present = /* @__PURE__ */ new Set();
1223
+ for (const a2 of el.attributes) {
1224
+ if ("loc" in a2 && a2.loc) present.add(writtenName(sf.text, a2.loc.start));
1225
+ }
1226
+ hit = { tag: el.tag, present };
1227
+ }
1228
+ }
1229
+ if (!hit && "children" in node && node.children) walk(node.children);
1230
+ if (hit) return;
1231
+ }
1013
1232
  };
1014
- gen += PRELUDE;
1015
- for (const tpl of templates) {
1016
- copy(tpl.node.getStart(sf));
1017
- gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
1018
- src = tpl.node.getEnd();
1233
+ walk(root.children);
1234
+ return hit;
1235
+ }
1236
+ function writtenName(text, start) {
1237
+ let end = start;
1238
+ while (end < text.length && !/[\s=/>]/.test(text[end])) end++;
1239
+ return text.slice(start, end);
1240
+ }
1241
+ function firstAttributeEnd(el) {
1242
+ let end = el.loc.start + 1 + el.tag.length;
1243
+ for (const a2 of el.attributes) {
1244
+ if ("loc" in a2 && a2.loc) end = Math.max(end, a2.loc.end);
1019
1245
  }
1020
- copy(srcText.length);
1021
- return { text: gen, segments };
1246
+ return end + 1;
1022
1247
  }
1023
- function isFn(ts, node) {
1024
- return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
1248
+ function elementAttributeCompletions(ts, ls, host, fileName, position) {
1249
+ const program = ls.getProgram();
1250
+ const sf = program?.getSourceFile(fileName);
1251
+ if (!program || !sf) return void 0;
1252
+ const tpl = templateAtCached(ts, sf, position);
1253
+ if (!tpl) return void 0;
1254
+ const hit = elementAtAttributeArea(ts, sf, tpl.template, position);
1255
+ if (!hit) return void 0;
1256
+ const props = attributesFor(ts, program, host, sf, hit.tag);
1257
+ if (!props) return void 0;
1258
+ const entries = [];
1259
+ for (const p3 of props) {
1260
+ const name = p3.getName();
1261
+ if (name === "children" || hit.present.has(name)) continue;
1262
+ entries.push({
1263
+ name,
1264
+ kind: ts.ScriptElementKind.memberVariableElement,
1265
+ kindModifiers: "",
1266
+ sortText: name.startsWith("on") ? "1" : "0"
1267
+ });
1268
+ }
1269
+ return entries.length ? entries : void 0;
1025
1270
  }
1026
- function collectTemplates(ts, sf) {
1027
- const out = [];
1028
- const visit = (node) => {
1029
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
1030
- const tpl = node.template;
1031
- const holes = ts.isTemplateExpression(tpl) ? tpl.templateSpans.map((s) => s.expression) : [];
1032
- out.push({ node, holes });
1271
+ function attributeNameAt(ts, sf, tpl, position) {
1272
+ const { root } = parseCached(ts, sf, tpl);
1273
+ let hit;
1274
+ const walk = (nodes) => {
1275
+ for (const node of nodes) {
1276
+ if (node.kind === "Element") {
1277
+ const el = node;
1278
+ for (const a2 of el.attributes) {
1279
+ if (!("loc" in a2) || !a2.loc) continue;
1280
+ const start = a2.loc.start;
1281
+ const name = writtenName(sf.text, start);
1282
+ if (name && position >= start && position <= start + name.length) {
1283
+ hit = { tag: el.tag, name, start };
1284
+ }
1285
+ }
1286
+ }
1287
+ if (!hit && "children" in node && node.children) walk(node.children);
1288
+ if (hit) return;
1033
1289
  }
1034
- ts.forEachChild(node, visit);
1035
1290
  };
1036
- visit(sf);
1037
- return out;
1291
+ walk(root.children);
1292
+ return hit;
1038
1293
  }
1039
- function analyze(nodes, roles, consumed) {
1040
- const renderChild = (node) => {
1041
- if (!("children" in node) || !node.children) return void 0;
1042
- const expr = node.children.find((c) => c.kind === "Expression");
1043
- return expr && expr.kind === "Expression" ? expr.hole : void 0;
1294
+ function elementAttributeHover(ts, ls, host, fileName, position) {
1295
+ const program = ls.getProgram();
1296
+ const sf = program?.getSourceFile(fileName);
1297
+ if (!program || !sf) return void 0;
1298
+ const tpl = templateAtCached(ts, sf, position);
1299
+ if (!tpl) return void 0;
1300
+ const hit = attributeNameAt(ts, sf, tpl.template, position);
1301
+ if (!hit) return void 0;
1302
+ const props = attributesFor(ts, program, host, sf, hit.tag);
1303
+ const symbol = props?.find((p3) => p3.getName() === hit.name);
1304
+ if (!symbol) return void 0;
1305
+ const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
1306
+ if (!decl) return void 0;
1307
+ const checker = program.getTypeChecker();
1308
+ const typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, decl));
1309
+ return {
1310
+ kind: ts.ScriptElementKind.memberVariableElement,
1311
+ kindModifiers: "",
1312
+ textSpan: { start: hit.start, length: hit.name.length },
1313
+ displayParts: [
1314
+ { text: "(", kind: "punctuation" },
1315
+ { text: "attribute", kind: "text" },
1316
+ { text: ")", kind: "punctuation" },
1317
+ { text: " ", kind: "space" },
1318
+ { text: `${hit.tag}.`, kind: "text" },
1319
+ { text: hit.name, kind: "propertyName" },
1320
+ { text: ":", kind: "punctuation" },
1321
+ { text: " ", kind: "space" },
1322
+ { text: typeText, kind: "text" }
1323
+ ],
1324
+ documentation: symbol.getDocumentationComment(checker)
1044
1325
  };
1045
- for (const node of nodes) {
1046
- if (node.kind === "Element" || node.kind === "Component") {
1047
- for (const a of node.attributes) {
1048
- if (a.kind === "EventBinding") roles.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
1049
- if (a.kind === "RefBinding" && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1050
- if (a.kind === "UseDirective" && a.hole != null && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1051
- }
1052
- const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
1053
- const propHole = (name) => {
1054
- const a = node.attributes.find(
1055
- (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
1056
- );
1057
- return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
1058
- };
1059
- const wire = (arrayHole, kind) => {
1060
- const r = renderChild(node);
1061
- if (r !== void 0) {
1062
- roles.set(r, { kind, arrayHole });
1063
- consumed.add(arrayHole);
1064
- }
1065
- };
1066
- if (eachDir && eachDir.kind === "EachDirective") wire(eachDir.hole, "eachRender");
1067
- else if (node.kind === "Component" && node.tag === "For") {
1068
- const h = propHole("each");
1069
- if (h !== void 0) wire(h, "eachRender");
1070
- } else if (node.kind === "Component" && node.tag === "Index") {
1071
- const h = propHole("each");
1072
- if (h !== void 0) wire(h, "indexRender");
1073
- } else if (node.kind === "Component" && node.tag === "Show") {
1074
- const w = propHole("when");
1075
- const r = renderChild(node);
1076
- if (w !== void 0 && r !== void 0) {
1077
- roles.set(r, { kind: "showRender", whenHole: w });
1078
- consumed.add(w);
1326
+ }
1327
+
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;
1336
+ }
1337
+ function accessorDiagnostics(ts, program, sf) {
1338
+ const checker = program.getTypeChecker();
1339
+ const out = [];
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
+ }
1079
1360
  }
1361
+ const children = "children" in node ? node.children : void 0;
1362
+ if (children) visit(children, node);
1080
1363
  }
1081
- }
1082
- if ("children" in node && node.children) analyze(node.children, roles, consumed);
1364
+ };
1365
+ visit(parseCached(ts, sf, tpl).root.children, null);
1083
1366
  }
1367
+ return out;
1084
1368
  }
1085
- function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
1086
- const roles = /* @__PURE__ */ new Map();
1087
- const consumed = /* @__PURE__ */ new Set();
1088
- const { root } = k(templatePieces(ts, tpl.node.template, sf));
1089
- analyze(root.children, roles, consumed);
1090
- const holeSrc = (i) => [tpl.holes[i].getStart(sf), tpl.holes[i].getEnd()];
1091
- const putHole = (i) => {
1092
- const [s, e] = holeSrc(i);
1093
- const genStart = gen.length;
1094
- gen += srcText.slice(s, e);
1095
- segments.push({ srcStart: s, srcEnd: e, genStart, genEnd: gen.length, hole: true });
1096
- };
1097
- const isIdent = (t) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t);
1098
- const putKey = (start, text) => {
1099
- const genStart = gen.length;
1100
- if (isIdent(text)) {
1101
- gen += text;
1102
- segments.push({ srcStart: start, srcEnd: start + text.length, genStart, genEnd: gen.length, key: true });
1103
- } else {
1104
- gen += JSON.stringify(text);
1105
- }
1106
- };
1107
- const calls = [];
1108
- const walkComponents = (nodes) => {
1109
- for (const node of nodes) {
1110
- if (node.kind === "Component") calls.push(componentPropsEmitter(node));
1111
- if ("children" in node && node.children) walkComponents(node.children);
1112
- }
1113
- };
1114
- function componentPropsEmitter(node) {
1115
- return () => {
1116
- gen += "__fxProps(";
1117
- if (node.tagHole != null) putHole(node.tagHole);
1118
- else if (CONTROL_FLOW.has(node.tag)) gen += `__fxCF.${node.tag}`;
1119
- else gen += node.tag;
1120
- gen += ", { ";
1121
- let first = true;
1122
- const entry = (keyStart, keyText, emitValue) => {
1123
- if (!first) gen += ", ";
1124
- first = false;
1125
- putKey(keyStart, keyText);
1126
- gen += ": ";
1127
- emitValue();
1128
- };
1129
- for (const a of node.attributes) {
1130
- if (a.kind === "Attribute") {
1131
- const keyStart = a.boolean ? a.loc.start + 1 : a.loc.start;
1132
- const v2 = a.value;
1133
- entry(keyStart, a.name, () => {
1134
- if (!v2) {
1135
- gen += "true";
1136
- } else if (v2.kind === "static") {
1137
- gen += JSON.stringify(v2.value);
1138
- } else if (v2.kind === "hole") {
1139
- putHole(v2.hole);
1140
- } else {
1141
- gen += "undefined as any";
1142
- }
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"
1143
1409
  });
1144
- } else if (a.kind === "PropertyBinding") {
1145
- entry(a.loc.start + 1, a.name, () => putHole(a.hole));
1146
1410
  }
1147
1411
  }
1148
- gen += " })";
1412
+ ts.forEachChild(node, visit);
1149
1413
  };
1414
+ if (fn.body) visit(fn.body);
1150
1415
  }
1151
- walkComponents(root.children);
1152
- for (let i = 0; i < tpl.holes.length; i++) {
1153
- if (consumed.has(i)) continue;
1154
- const role = roles.get(i);
1155
- if (role && role.kind === "eachRender") {
1156
- calls.push(() => {
1157
- gen += "__fxEach(";
1158
- putHole(role.arrayHole);
1159
- gen += ", ";
1160
- putHole(i);
1161
- gen += ")";
1162
- });
1163
- } else if (role && role.kind === "indexRender") {
1164
- calls.push(() => {
1165
- gen += "__fxIndex(";
1166
- putHole(role.arrayHole);
1167
- gen += ", ";
1168
- putHole(i);
1169
- gen += ")";
1170
- });
1171
- } else if (role && role.kind === "showRender") {
1172
- calls.push(() => {
1173
- gen += "__fxShow(";
1174
- putHole(role.whenHole);
1175
- gen += ", ";
1176
- putHole(i);
1177
- gen += ")";
1178
- });
1179
- } else if (role && role.kind === "event") {
1180
- const key = EVENT_MAP_KEYS.has(role.event) ? role.event : role.event;
1181
- calls.push(() => {
1182
- gen += `__fxOn(${JSON.stringify(key)}, `;
1183
- putHole(i);
1184
- gen += ")";
1185
- });
1186
- } else if (role && role.kind === "ref" && isFn(ts, tpl.holes[i])) {
1187
- const tag = role.tag;
1188
- calls.push(() => {
1189
- gen += `__fxRef(${JSON.stringify(tag)}, `;
1190
- putHole(i);
1191
- gen += ")";
1192
- });
1193
- } else {
1194
- calls.push(() => {
1195
- gen += "__fxExpr(";
1196
- putHole(i);
1197
- gen += ")";
1198
- });
1199
- }
1200
- }
1201
- gen += "(";
1202
- calls.forEach((emit, idx) => {
1203
- if (idx > 0) gen += ", ";
1204
- emit();
1205
- });
1206
- gen += calls.length > 0 ? ", undefined as any)" : "undefined as any)";
1207
- return gen;
1416
+ cache.set(sf, out);
1417
+ return out;
1208
1418
  }
1209
1419
 
1210
- // src/virtual-service.ts
1211
- var VirtualService = class {
1212
- constructor(ts, host) {
1213
- this.ts = ts;
1214
- this.host = host;
1215
- const self = this;
1216
- const virtualHost = new Proxy(host, {
1217
- get(target, prop, receiver) {
1218
- if (prop === "getScriptSnapshot") {
1219
- return (fileName) => {
1220
- const v2 = self.virtual(fileName);
1221
- return v2 ? self.ts.ScriptSnapshot.fromString(v2.text) : target.getScriptSnapshot(fileName);
1222
- };
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: "()" }]
1223
1436
  }
1224
- const value = Reflect.get(target, prop, receiver);
1225
- return typeof value === "function" ? value.bind(target) : value;
1226
- }
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"
1227
1440
  });
1228
- this.ls = ts.createLanguageService(virtualHost, ts.createDocumentRegistry());
1229
- }
1230
- ls;
1231
- /** Cached per file: the virtual text + segments, keyed by the file's version. */
1232
- cache = /* @__PURE__ */ new Map();
1233
- /** The virtual doc for a file (built + cached per source version), if it has templates. */
1234
- virtual(fileName) {
1235
- const version = this.host.getScriptVersion(fileName);
1236
- const hit = this.cache.get(fileName);
1237
- if (hit && hit.version === version) return hit.text ? hit : void 0;
1238
- const snap = this.host.getScriptSnapshot(fileName);
1239
- if (!snap) return void 0;
1240
- const src = snap.getText(0, snap.getLength());
1241
- const sf = this.ts.createSourceFile(fileName, src, this.ts.ScriptTarget.Latest, true, scriptKind(this.ts, fileName));
1242
- const v2 = buildVirtual(this.ts, sf);
1243
- this.cache.set(fileName, { version, text: v2?.text ?? "", segments: v2?.segments ?? [] });
1244
- return v2;
1245
- }
1246
- segmentsFor(fileName) {
1247
- return this.virtual(fileName)?.segments;
1248
- }
1249
- service() {
1250
- return this.ls;
1251
- }
1252
- toGen(fileName, pos) {
1253
- const segs = this.virtual(fileName)?.segments;
1254
- return segs ? toGenerated(segs, pos) : -1;
1255
- }
1256
- toSrc(fileName, pos) {
1257
- const segs = this.virtual(fileName)?.segments;
1258
- return segs ? toSource(segs, pos) : -1;
1259
- }
1260
- /** True when a SOURCE position lies inside an embedded `${…}` hole. */
1261
- inHole(fileName, pos) {
1262
- const segs = this.virtual(fileName)?.segments;
1263
- return segs ? inHole(segs, pos) : false;
1264
- }
1265
- /** True when a SOURCE position lies on a mapped component prop-name key. */
1266
- inKey(fileName, pos) {
1267
- const segs = this.virtual(fileName)?.segments;
1268
- return segs ? inKey(segs, pos) : false;
1269
1441
  }
1270
- };
1271
- function scriptKind(ts, fileName) {
1272
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
1273
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
1274
- if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) return ts.ScriptKind.JS;
1275
- return ts.ScriptKind.TS;
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 };
1276
1455
  }
1277
1456
 
1278
- // src/bridge.ts
1279
- function mapDiagnostic(vs, fileName, d) {
1280
- if (d.start === void 0) return d;
1281
- const start = vs.toSrc(fileName, d.start);
1282
- if (start === -1) return void 0;
1283
- return { ...d, start };
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);
1475
+ }
1476
+ };
1477
+ walk(root.children);
1478
+ }
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: ""
1538
+ }
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 } });
1553
+ }
1554
+ }
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
+ }));
1284
1604
  }
1285
1605
 
1286
1606
  // src/index.ts
@@ -1289,12 +1609,7 @@ function init(modules) {
1289
1609
  return {
1290
1610
  create(info) {
1291
1611
  const ls = info.languageService;
1292
- let vs;
1293
- try {
1294
- vs = new VirtualService(ts, info.languageServiceHost);
1295
- } catch {
1296
- vs = void 0;
1297
- }
1612
+ const host = info.languageServiceHost;
1298
1613
  const proxy = /* @__PURE__ */ Object.create(null);
1299
1614
  for (const key of Object.keys(ls)) {
1300
1615
  const member = ls[key];
@@ -1304,9 +1619,24 @@ function init(modules) {
1304
1619
  isGlobalCompletion: false,
1305
1620
  isMemberCompletion: false,
1306
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,
1307
1628
  entries
1308
1629
  });
1309
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
+ };
1310
1640
  const reliableFilter = (fileName, diagnostics) => {
1311
1641
  const program = ls.getProgram();
1312
1642
  const sourceFile = program?.getSourceFile(fileName);
@@ -1318,47 +1648,49 @@ function init(modules) {
1318
1648
  return true;
1319
1649
  });
1320
1650
  };
1321
- const holeDiagnostics = (fileName, get) => {
1322
- if (!vs || !vs.segmentsFor(fileName)) return [];
1651
+ proxy.getSemanticDiagnostics = (fileName) => {
1652
+ const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1323
1653
  try {
1324
- const sourceFile = ls.getProgram()?.getSourceFile(fileName);
1325
- const out = [];
1326
- for (const d of get(fileName)) {
1327
- if (d.start === void 0) continue;
1328
- const m2 = mapDiagnostic(vs, fileName, d);
1329
- if (m2 && m2.start !== void 0 && (vs.inHole(fileName, m2.start) || vs.inKey(fileName, m2.start))) {
1330
- out.push({ ...m2, file: sourceFile });
1331
- }
1332
- }
1333
- return out;
1654
+ return base.concat(templateDiagnostics(ts, ls, fileName));
1334
1655
  } catch {
1335
- return [];
1656
+ return base;
1336
1657
  }
1337
1658
  };
1338
- proxy.getSemanticDiagnostics = (fileName) => {
1339
- const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1340
- return base.concat(holeDiagnostics(fileName, (f) => vs.service().getSemanticDiagnostics(f)));
1341
- };
1342
1659
  proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1343
1660
  proxy.getQuickInfoAtPosition = (fileName, position) => {
1344
1661
  try {
1345
- const prop = componentPropHover(ts, ls, fileName, position);
1662
+ const prop = componentPropHover(ts, ls, host, fileName, position);
1346
1663
  if (prop) return prop;
1347
1664
  } catch {
1348
1665
  }
1666
+ try {
1667
+ const attr = elementAttributeHover(ts, ls, host, fileName, position);
1668
+ if (attr) return attr;
1669
+ } catch {
1670
+ }
1349
1671
  try {
1350
1672
  const inferred = paramQuickInfo(ts, ls, fileName, position);
1351
1673
  if (inferred) return inferred;
1352
1674
  } catch {
1353
1675
  }
1354
- 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);
1355
1682
  };
1356
1683
  proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
1357
1684
  try {
1358
- const props = componentPropCompletions(ts, ls, fileName, position);
1685
+ const props = componentPropCompletions(ts, ls, host, fileName, position);
1359
1686
  if (props) return completionList(props);
1360
1687
  } catch {
1361
1688
  }
1689
+ try {
1690
+ const attrs = elementAttributeCompletions(ts, ls, host, fileName, position);
1691
+ if (attrs) return completionList(attrs);
1692
+ } catch {
1693
+ }
1362
1694
  try {
1363
1695
  const members = paramMemberCompletions(ts, ls, fileName, position);
1364
1696
  if (members) return completionList(members);
@@ -1366,6 +1698,71 @@ function init(modules) {
1366
1698
  }
1367
1699
  return ls.getCompletionsAtPosition(fileName, position, options, formatting);
1368
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
+ };
1369
1766
  return proxy;
1370
1767
  }
1371
1768
  };