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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +714 -573
  2. package/package.json +2 -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 = (i, n) => {
6
+ i.parent = n;
7
+ let r = B(i);
8
+ for (let s of r) t(s, i);
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 B(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
  }
@@ -45,129 +45,169 @@ var u = class {
45
45
  let i = this.pieces[this.pieces.length - 1];
46
46
  return i ? i.kind === "static" ? i.start + i.text.length : i.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;
56
+ let t = this.pieces[this.pi];
57
+ if (t.kind !== "static" || this.oi + 1 < t.text.length) return false;
58
58
  let i = this.pieces[this.pi + 1];
59
59
  return i !== void 0 && i.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
68
  let i = this.pieces[this.pi];
69
- return i.kind !== "static" ? "" : i.text[this.oi + e] ?? "";
69
+ return i.kind !== "static" ? "" : i.text[this.oi + t] ?? "";
70
70
  }
71
- startsWith(e) {
71
+ startsWith(t) {
72
72
  if (this.eof()) return false;
73
73
  let i = this.pieces[this.pi];
74
- return i.kind !== "static" ? false : i.text.startsWith(e, this.oi);
74
+ return i.kind !== "static" ? false : i.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++] ?? "";
78
+ let t = this.pieces[this.pi];
79
+ if (t.kind !== "static") return "";
80
+ let i = t.text[this.oi++] ?? "";
81
81
  return this.normalize(), i;
82
82
  }
83
- readWhile(e) {
83
+ readWhile(t) {
84
84
  let i = "";
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
+ i += n, this.oi++;
89
89
  }
90
90
  return this.normalize(), i;
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 E = (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, i;
106
+ e.name[0] === "@" ? (t = "at", i = e.name.slice(1)) : e.name.startsWith("on:") ? (t = "colon", i = e.name.slice(3)) : (t = "on", i = e.name.slice(2));
107
+ let [n, ...r] = i.split("."), s = 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: s, 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: "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) => E(e, true) }];
111
+ function k(e, t = f, i = { component: false }) {
112
+ for (let n of t) if (!(n.elementOnly && i.component) && n.match(e.name)) return n.build(e);
113
+ return E(e, false);
114
+ }
115
+ var D = /* @__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((i) => "name" in i && i.name === t);
125
+ }
126
+ function w(e) {
127
+ return e.attributes.some((t) => t.kind === "Spread");
128
+ }
129
+ function v(e, t) {
130
+ let i = u(e, t);
131
+ if (!i || i.kind !== "Attribute" || !i.value) return;
132
+ let n = i.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((i) => i.kind === "EventBinding" && t(i.name));
141
+ }
142
+ function R(e) {
143
+ let t = [], i = (r, s, o) => {
144
+ t.push({ code: s, message: o, severity: "warning", loc: r.loc });
145
+ }, n = (r) => {
146
+ for (let s of r) {
147
+ s.kind === "Element" && V(s, i);
148
+ let o = "children" in s ? s.children : void 0;
149
+ o && n(o);
150
+ }
151
+ };
152
+ return n(e.children), t;
153
+ }
154
+ function V(e, t) {
155
+ if (w(e)) return;
156
+ let i = e.tag.toLowerCase();
157
+ i === "img" && !u(e, "alt") && !H(e) && t(e, "a11y-img-alt", 'An `img` needs an `alt`. Use `alt=""` when the image is decorative.'), i === "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."), i === "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(i) && A(e, (r) => r === "click") && !u(e, "role") && !A(e, (r) => W.has(r)) && t(e, "a11y-click-without-keyboard", `A click handler on \`${i}\` is unreachable by keyboard. Use a \`button\`, or add a \`role\` and a key handler.`);
160
+ }
161
+ var I = /[A-Za-z0-9\-_:@.?]/;
162
+ var b = class {
163
+ constructor(t, i) {
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 = i.svg ?? false, this.directives = i.directives ?? f, this.accessibility = i.accessibility ?? true;
126
166
  }
127
167
  parse() {
128
- let e = this.r.pos(), i = [];
168
+ let t = this.r.pos(), i = [];
129
169
  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));
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: i, loc: this.r.span(t) };
174
+ return x(n), this.validateTree(n), this.accessibility && this.diagnostics.push(...R(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 i = (n) => {
178
+ let r = null;
179
+ for (let s of n) s.kind === "Text" && /^\s*$/.test(s.value) || (s.kind === "Element" || s.kind === "Component" ? (s.attributes.some((l) => l.kind === "ElseDirective") && (r != null && (r.kind === "Element" || r.kind === "Component") && r.attributes.some((a) => a.kind === "IfDirective") || this.report("else-without-if", "error", "`else` must immediately follow an element with `if`", s.loc)), r = s) : r = null, "children" in s && s.children && i(s.children));
140
180
  };
141
- i(e.children);
181
+ i(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
207
  let i = this.readText();
168
- i && e.push(i);
208
+ i && t.push(i);
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,20 +219,20 @@ 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 i = 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, s = [];
227
+ return this.r.startsWith("/>") ? (this.r.advance(), this.r.advance(), r = 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(t))), { kind: "Component", tag: "", tagHole: i, attributes: n, children: s, selfClosing: r, loc: this.r.span(t) };
188
228
  }
189
229
  readText() {
190
- let e = this.r.pos(), i = "";
230
+ let t = this.r.pos(), i = "";
191
231
  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;
232
+ return i ? { kind: "Text", value: i, 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
237
  let i = "";
198
238
  for (; !this.r.eof() && !this.r.startsWith("-->"); ) {
@@ -202,148 +242,148 @@ var g = class {
202
242
  }
203
243
  i += 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: i, 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
250
  let i = 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: i, 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 i = this.readName(), n = C(i), r = this.parseAttributes(n);
261
+ this.foldEachKey(r, t), this.r.skipWhitespace();
262
+ let s = i.toLowerCase(), o = !n && S.has(s), l = !n && T.has(s), a = false, c = [];
263
+ if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(), a = true;
264
+ else if (this.r.peek() === ">" && this.r.advance(), !l) if (c = o ? this.parseRawText(i) : this.parseChildren(), this.r.startsWith("</")) {
265
+ let g = this.consumeCloseTag();
266
+ g && g !== i && this.report("mismatched-close-tag", "error", `Expected </${i}>, got </${g}>`, this.r.span(t));
267
+ } else this.report("unclosed-tag", "error", `Unclosed <${i}>`, this.r.span(t));
268
+ let h = this.r.span(t);
269
+ if (n) return { kind: "Component", tag: i, tagHole: null, attributes: r, children: c, selfClosing: a, loc: h };
270
+ let d = this.svg || D.has(i) ? "svg" : "html";
271
+ return { kind: "Element", tag: i, namespace: d, attributes: r, children: c, selfClosing: a, rawText: o, loc: h };
272
+ }
273
+ parseRawText(t) {
274
+ let i = [], n = "</" + t.toLowerCase(), r = "", s = this.r.pos(), o = () => {
275
+ r && i.push({ kind: "Text", value: r, raw: true, loc: this.r.span(s) }), 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();
279
+ o();
240
280
  let l = this.r.takeHole();
241
281
  i.push({ kind: "Expression", hole: l.index, loc: l.loc }), s = this.r.pos();
242
282
  continue;
243
283
  }
244
- n || (s = this.r.pos()), n += this.r.advance();
284
+ r || (s = this.r.pos()), r += this.r.advance();
245
285
  }
246
- return a(), i;
286
+ return o(), i;
247
287
  }
248
- startsWithCloseFor(e) {
288
+ startsWithCloseFor(t) {
249
289
  let i = 0;
250
- for (; i < e.length; ) {
251
- let r = this.r.peek(i);
252
- if (r === "" || r.toLowerCase() !== e[i]) return false;
290
+ for (; i < t.length; ) {
291
+ let n = this.r.peek(i);
292
+ if (n === "" || n.toLowerCase() !== t[i]) return false;
253
293
  i++;
254
294
  }
255
295
  return true;
256
296
  }
257
- parseAttributes(e) {
297
+ parseAttributes(t) {
258
298
  let i = [];
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 a = this.r.takeHole();
302
+ i.push({ kind: "Spread", hole: a.index, loc: a.loc });
263
303
  continue;
264
304
  }
265
305
  if (this.r.startsWith("...")) {
266
- let o = this.r.pos();
306
+ let a = 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
+ i.push({ kind: "Spread", hole: c.index, loc: this.r.span(a) });
310
+ } else this.report("invalid-binding", "error", "Spread `...` must be followed by ${\u2026}", this.r.span(a));
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 s = 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 l = { name: r, nameLoc: s, value: o, loc: this.r.span(n), report: (a, c) => this.report(a, "error", c, this.r.span(n)) };
323
+ i.push(k(l, this.directives, { component: t }));
284
324
  }
285
325
  return i;
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 i = [], 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 && (i.push({ text: n }), n = ""), i.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 && i.push({ text: n }), this.r.peek() === t && this.r.advance(), i;
345
+ }
346
+ classifyParts(t) {
347
+ let i = t.filter((n) => "hole" in n);
348
+ return i.length === 0 ? { kind: "static", value: t.map((n) => "text" in n ? n.text : "").join("") } : t.length === 1 ? { kind: "hole", hole: i[0].hole } : { kind: "mixed", parts: t };
349
+ }
350
+ foldEachKey(t, i) {
351
+ let n = null, r = 0, s = 0, o = 0, l = 0;
352
+ for (let d of t) d.kind === "EachDirective" ? (n = d, s++) : d.kind === "IfDirective" ? r++ : d.kind === "ElseDirective" ? o++ : d.kind === "RefBinding" && l++;
353
+ let a = this.r.span(i);
354
+ s > 1 && this.report("duplicate-directive", "error", "Multiple `each` directives on one element", a), r > 1 && this.report("duplicate-directive", "error", "Multiple `if` directives on one element", a), l > 1 && this.report("duplicate-directive", "error", "Multiple `ref` bindings on one element", a), r > 0 && s > 0 && this.report("unsupported-combination", "error", "`if` and `each` cannot both apply to one element \u2014 wrap one in another element", a), r > 0 && o > 0 && this.report("invalid-directive", "error", "`if` and `else` cannot be on the same element", a);
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", a);
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) => I.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, i, n, r) {
373
+ this.diagnostics.push({ code: t, severity: i, message: n, loc: r });
334
374
  }
335
375
  };
336
- function k(t, e = {}) {
337
- return new g(t, e).parse();
376
+ function N(e, t = {}) {
377
+ return new b(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 L(e) {
380
+ let t = [], i = 0;
381
+ return e.forEach((n, r) => {
382
+ t.push({ kind: "static", text: n, start: i }), i += n.length, r < e.length - 1 && t.push({ kind: "hole", index: r, start: i, end: i });
383
+ }), t;
344
384
  }
345
- function F(t, e = {}) {
346
- return k(A(t), e);
385
+ function Q(e, t = {}) {
386
+ return N(L(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
  };
@@ -435,7 +475,7 @@ function parseCached(ts, sf, tpl) {
435
475
  const key = tpl.getStart(sf);
436
476
  const hit = byTemplate.get(key);
437
477
  if (hit) return hit;
438
- const result = k(templatePieces(ts, tpl, sf));
478
+ const result = N(templatePieces(ts, tpl, sf));
439
479
  byTemplate.set(key, result);
440
480
  return result;
441
481
  }
@@ -465,8 +505,136 @@ function templateAtCached(ts, sf, pos) {
465
505
  return match;
466
506
  }
467
507
 
508
+ // src/auto-import.ts
509
+ var DOM = "@fluixi/dom";
510
+ var CORE = "@fluixi/core";
511
+ var ROUTER = "@fluixi/core/router-next";
512
+ var AUTO_IMPORTED = {
513
+ // Control flow. Suspense and Await live in core; dom doesn't export them.
514
+ Show: DOM,
515
+ For: DOM,
516
+ Index: DOM,
517
+ Switch: DOM,
518
+ Match: DOM,
519
+ Portal: DOM,
520
+ Dynamic: DOM,
521
+ ErrorBoundary: DOM,
522
+ Suspense: CORE,
523
+ SuspenseList: CORE,
524
+ Await: CORE,
525
+ // Router. `Routes` isn't a component and `lazy` is a call, so neither appears.
526
+ Router: ROUTER,
527
+ Outlet: ROUTER,
528
+ Redirect: ROUTER,
529
+ Link: ROUTER
530
+ };
531
+ var exportsByProgram = /* @__PURE__ */ new WeakMap();
532
+ function moduleExports(ts, program, host, from, moduleName) {
533
+ let cache2 = exportsByProgram.get(program);
534
+ if (!cache2) {
535
+ cache2 = /* @__PURE__ */ new Map();
536
+ exportsByProgram.set(program, cache2);
537
+ }
538
+ if (cache2.has(moduleName)) return cache2.get(moduleName);
539
+ let symbols;
540
+ try {
541
+ const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
542
+ const file = resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName);
543
+ const checker = program.getTypeChecker();
544
+ const moduleSymbol = file && checker.getSymbolAtLocation(file);
545
+ if (moduleSymbol) symbols = checker.getExportsOfModule(moduleSymbol);
546
+ } catch {
547
+ symbols = void 0;
548
+ }
549
+ cache2.set(moduleName, symbols);
550
+ return symbols;
551
+ }
552
+ var sitesByProgram = /* @__PURE__ */ new WeakMap();
553
+ function declarationSite(ts, program, host, from, name) {
554
+ const moduleName = AUTO_IMPORTED[name];
555
+ if (!moduleName) return void 0;
556
+ let cache2 = sitesByProgram.get(program);
557
+ if (!cache2) {
558
+ cache2 = /* @__PURE__ */ new Map();
559
+ sitesByProgram.set(program, cache2);
560
+ }
561
+ const key = `${moduleName}:${name}`;
562
+ if (cache2.has(key)) return cache2.get(key);
563
+ let site;
564
+ try {
565
+ const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
566
+ const entry = resolved.resolvedModule?.resolvedFileName;
567
+ if (entry) site = findExport(ts, host, program.getCompilerOptions(), entry, name, 0);
568
+ } catch {
569
+ site = void 0;
570
+ }
571
+ cache2.set(key, site);
572
+ return site;
573
+ }
574
+ function findExport(ts, host, options, fileName, name, depth) {
575
+ if (depth > 4) return void 0;
576
+ const text = host.readFile?.(fileName);
577
+ if (text === void 0) return void 0;
578
+ const sf = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true);
579
+ const reExports = [];
580
+ for (const statement of sf.statements) {
581
+ const declared = declaredName(ts, statement, name);
582
+ if (declared) {
583
+ return { fileName, start: declared.getStart(sf), length: declared.getWidth(sf) };
584
+ }
585
+ if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
586
+ const specifier = statement.moduleSpecifier.text;
587
+ if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
588
+ const named = statement.exportClause.elements.find((e) => e.name.text === name);
589
+ if (named) reExports.unshift(specifier);
590
+ } else if (!statement.exportClause) {
591
+ reExports.push(specifier);
592
+ }
593
+ }
594
+ }
595
+ for (const specifier of reExports) {
596
+ const target = resolveRelative(ts, host, options, fileName, specifier);
597
+ const found = target && findExport(ts, host, options, target, name, depth + 1);
598
+ if (found) return found;
599
+ }
600
+ return void 0;
601
+ }
602
+ function declaredName(ts, statement, name) {
603
+ const exported = ts.canHaveModifiers(statement) ? ts.getModifiers(statement)?.some((m2) => m2.kind === ts.SyntaxKind.ExportKeyword) : false;
604
+ if (!exported) return void 0;
605
+ if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name?.text === name) {
606
+ return statement.name;
607
+ }
608
+ if (ts.isVariableStatement(statement)) {
609
+ for (const d of statement.declarationList.declarations) {
610
+ if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
611
+ }
612
+ }
613
+ return void 0;
614
+ }
615
+ function resolveRelative(ts, host, options, from, specifier) {
616
+ if (!specifier.startsWith(".")) return void 0;
617
+ try {
618
+ return ts.resolveModuleName(specifier, from, options, host).resolvedModule?.resolvedFileName;
619
+ } catch {
620
+ return void 0;
621
+ }
622
+ }
623
+ function autoImportedSymbol(ts, program, host, from, name) {
624
+ const moduleName = AUTO_IMPORTED[name];
625
+ if (!moduleName) return void 0;
626
+ const symbol = moduleExports(ts, program, host, from, moduleName)?.find((s) => s.name === name);
627
+ if (!symbol) return void 0;
628
+ if (!(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
629
+ try {
630
+ return program.getTypeChecker().getAliasedSymbol(symbol);
631
+ } catch {
632
+ return symbol;
633
+ }
634
+ }
635
+
468
636
  // src/hover.ts
469
- function componentQuickInfo(ts, ls, fileName, position) {
637
+ function componentQuickInfo(ts, ls, host, fileName, position) {
470
638
  const program = ls.getProgram();
471
639
  const sf = program?.getSourceFile(fileName);
472
640
  if (!program || !sf) return void 0;
@@ -476,11 +644,12 @@ function componentQuickInfo(ts, ls, fileName, position) {
476
644
  if (!hit) return void 0;
477
645
  const checker = program.getTypeChecker();
478
646
  const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
479
- const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name);
647
+ const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name) ?? autoImportedSymbol(ts, program, host, sf, hit.name);
480
648
  const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
481
649
  if (!decl) return void 0;
482
- const namePos = decl.name?.getStart(sf) ?? decl.getStart(sf);
483
- const info = ls.getQuickInfoAtPosition(fileName, namePos);
650
+ const declFile = decl.getSourceFile();
651
+ const namePos = decl.name?.getStart(declFile) ?? decl.getStart(declFile);
652
+ const info = ls.getQuickInfoAtPosition(declFile.fileName, namePos);
484
653
  if (!info) return void 0;
485
654
  return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
486
655
  }
@@ -667,6 +836,12 @@ function inferParamAt(ts, program, sf, position) {
667
836
  const id = identifierAt(ts, sf, position);
668
837
  if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
669
838
  const checker = program.getTypeChecker();
839
+ const resolved = typeForRole(ts, checker, fn, role, holeExprs);
840
+ const { typeText, type } = resolved;
841
+ if (!typeText) return void 0;
842
+ return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
843
+ }
844
+ function typeForRole(ts, checker, fn, role, holeExprs) {
670
845
  let typeText;
671
846
  let type;
672
847
  if (role.kind === "event") {
@@ -687,8 +862,27 @@ function inferParamAt(ts, program, sf, position) {
687
862
  type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
688
863
  if (type) typeText = checker.typeToString(type);
689
864
  }
690
- if (!typeText) return void 0;
691
- return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
865
+ return { typeText, type };
866
+ }
867
+ function inferParamsIn(ts, program, sf) {
868
+ const checker = program.getTypeChecker();
869
+ const out = [];
870
+ for (const template of templatesIn(ts, sf)) {
871
+ const tpl = template.template;
872
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
873
+ const holeExprs = tpl.templateSpans.map((s) => s.expression);
874
+ const roles = /* @__PURE__ */ new Map();
875
+ holeRoles(parseCached(ts, sf, tpl).root.children, roles);
876
+ for (const [holeIndex, role] of roles) {
877
+ const fn = holeExprs[holeIndex];
878
+ if (!fn || !ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue;
879
+ const param = fn.parameters[0];
880
+ if (!param || !ts.isIdentifier(param.name) || param.type) continue;
881
+ const { type } = typeForRole(ts, checker, fn, role, holeExprs);
882
+ if (type) out.push({ fn, paramName: param.name.text, type });
883
+ }
884
+ }
885
+ return out;
692
886
  }
693
887
  function paramQuickInfo(ts, ls, fileName, position) {
694
888
  const program = ls.getProgram();
@@ -975,7 +1169,6 @@ function intrinsics(ts, program, host, sf) {
975
1169
  );
976
1170
  if (!type) {
977
1171
  for (const file of program.getSourceFiles()) {
978
- if (!file.isDeclarationFile && file.fileName.indexOf("@fluixi/dom") === -1) continue;
979
1172
  if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
980
1173
  type = declared(file);
981
1174
  if (type) break;
@@ -1118,385 +1311,283 @@ function elementAttributeHover(ts, ls, host, fileName, position) {
1118
1311
  };
1119
1312
  }
1120
1313
 
1121
- // src/virtual.ts
1122
- var EVENT_MAP_KEYS = new Set(Object.keys({
1123
- click: 0,
1124
- dblclick: 0,
1125
- mousedown: 0,
1126
- mouseup: 0,
1127
- mousemove: 0,
1128
- mouseenter: 0,
1129
- mouseleave: 0,
1130
- mouseover: 0,
1131
- mouseout: 0,
1132
- contextmenu: 0,
1133
- keydown: 0,
1134
- keyup: 0,
1135
- keypress: 0,
1136
- input: 0,
1137
- beforeinput: 0,
1138
- change: 0,
1139
- submit: 0,
1140
- focus: 0,
1141
- blur: 0,
1142
- focusin: 0,
1143
- focusout: 0,
1144
- pointerdown: 0,
1145
- pointerup: 0,
1146
- pointermove: 0,
1147
- pointerenter: 0,
1148
- pointerleave: 0,
1149
- wheel: 0,
1150
- scroll: 0,
1151
- drag: 0,
1152
- drop: 0,
1153
- touchstart: 0,
1154
- touchend: 0,
1155
- touchmove: 0,
1156
- animationend: 0,
1157
- transitionend: 0
1158
- }));
1159
- var PRELUDE = (
1160
- // Control-flow component prop types, declared inline so they ALWAYS resolve
1161
- // (no dependency on @fluixi/core being importable from the virtual document).
1162
- // Prop shapes drive hover + value checking on when/each/fallback/mount/…;
1163
- // render-prop children are typed separately by __fxEach/__fxShow.
1164
- 'declare const __fxCF: {\n Show: <T>(props: { when: T; fallback?: unknown; children?: unknown }) => any;\n For: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; by?: (item: T) => unknown; children?: unknown }) => any;\n Index: <T>(props: { each: readonly T[] | undefined | null; fallback?: unknown; children?: unknown }) => any;\n Switch: (props: { fallback?: unknown; children?: unknown }) => any;\n Match: <T>(props: { when: T; children?: unknown }) => any;\n Suspense: (props: { fallback?: unknown; children?: unknown }) => any;\n SuspenseList: (props: { revealOrder: "forwards" | "backwards" | "together"; children?: unknown }) => any;\n Portal: (props: { mount?: Node; useShadow?: boolean; children?: unknown }) => any;\n ErrorBoundary: (props: { fallback?: unknown; children?: unknown }) => any;\n Dynamic: (props: { component: unknown; [k: string]: unknown }) => any;\n Await: <T>(props: { value: Promise<T> | (() => Promise<T>); fallback?: unknown; children?: unknown }) => any;\n};\ndeclare const __fxEach: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: T, index: () => number) => any) => any;\ndeclare const __fxIndex: <T>(items: ArrayLike<T> | Iterable<T> | null | undefined, render: (item: () => T, index: number) => any) => any;\ndeclare const __fxShow: <T>(when: T, render: (value: NonNullable<T>) => any) => any;\ndeclare const __fxOn: <K extends string>(name: K, handler: (event: K extends keyof HTMLElementEventMap ? HTMLElementEventMap[K] : Event) => any) => any;\ndeclare const __fxRef: <K extends string>(tag: K, ref: (el: K extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[K] : HTMLElement) => any) => any;\ndeclare const __fxExpr: (value: any) => any;\ndeclare function __fxProps<P>(comp: (props: P, ...rest: any[]) => any, props: NoInfer<P>): any;\n'
1165
- );
1166
- var CONTROL_FLOW = /* @__PURE__ */ new Set([
1167
- "Show",
1168
- "For",
1169
- "Index",
1170
- "Switch",
1171
- "Match",
1172
- "Dynamic",
1173
- "ErrorBoundary",
1174
- "Suspense",
1175
- "SuspenseList",
1176
- "Await",
1177
- "Portal"
1178
- ]);
1179
- function inHole(segments, pos) {
1180
- return segments.some((s) => s.hole && pos >= s.srcStart && pos <= s.srcEnd);
1181
- }
1182
- function inKey(segments, pos) {
1183
- return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
1184
- }
1185
- function toSource(segments, pos) {
1186
- for (const s of segments) {
1187
- if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
1188
- }
1189
- return -1;
1190
- }
1191
- function buildVirtual(ts, sf) {
1192
- const templates2 = collectTemplates(ts, sf);
1193
- if (templates2.length === 0) return void 0;
1194
- const srcText = sf.text;
1195
- const segments = [];
1196
- let gen = "";
1197
- let src = 0;
1198
- const copy = (to) => {
1199
- if (to <= src) return;
1200
- const genStart = gen.length;
1201
- gen += srcText.slice(src, to);
1202
- segments.push({ srcStart: src, srcEnd: to, genStart, genEnd: gen.length });
1203
- src = to;
1204
- };
1205
- gen += PRELUDE;
1206
- for (const tpl of templates2) {
1207
- copy(tpl.node.getStart(sf));
1208
- gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
1209
- src = tpl.node.getEnd();
1210
- }
1211
- copy(srcText.length);
1212
- return { text: gen, segments };
1314
+ // src/diagnostics.ts
1315
+ var ACCESSOR_NOT_CALLED = 9001;
1316
+ function acceptsAnyProperty(ts, checker, type) {
1317
+ const open = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter | ts.TypeFlags.Never;
1318
+ if (type.flags & open) return true;
1319
+ if (checker.getIndexInfoOfType?.(type, ts.IndexKind.String)) return true;
1320
+ if (type.isUnion()) return type.types.some((t) => acceptsAnyProperty(ts, checker, t));
1321
+ return false;
1213
1322
  }
1214
- function isFn(ts, node) {
1215
- return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
1216
- }
1217
- function collectTemplates(ts, sf) {
1323
+ function accessorDiagnostics(ts, program, sf) {
1324
+ const checker = program.getTypeChecker();
1218
1325
  const out = [];
1219
- const visit = (node) => {
1220
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
1221
- const tpl = node.template;
1222
- const holes = ts.isTemplateExpression(tpl) ? tpl.templateSpans.map((s) => s.expression) : [];
1223
- out.push({ node, holes });
1224
- }
1225
- ts.forEachChild(node, visit);
1226
- };
1227
- visit(sf);
1228
- return out;
1229
- }
1230
- function analyze(nodes, roles, consumed) {
1231
- const renderChild = (node) => {
1232
- if (!("children" in node) || !node.children) return void 0;
1233
- const expr = node.children.find((c) => c.kind === "Expression");
1234
- return expr && expr.kind === "Expression" ? expr.hole : void 0;
1235
- };
1236
- for (const node of nodes) {
1237
- if (node.kind === "Element" || node.kind === "Component") {
1238
- for (const a of node.attributes) {
1239
- if (a.kind === "EventBinding") roles.set(a.hole, { kind: "event", event: a.name.toLowerCase() });
1240
- if (a.kind === "RefBinding" && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1241
- if (a.kind === "UseDirective" && a.hole != null && node.kind === "Element") roles.set(a.hole, { kind: "ref", tag: node.tag });
1242
- }
1243
- const eachDir = node.attributes.find((a) => a.kind === "EachDirective");
1244
- const propHole = (name) => {
1245
- const a = node.attributes.find(
1246
- (x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
1247
- );
1248
- return a && a.kind === "Attribute" && a.value && a.value.kind === "hole" ? a.value.hole : void 0;
1249
- };
1250
- const wire = (arrayHole, kind) => {
1251
- const r = renderChild(node);
1252
- if (r !== void 0) {
1253
- roles.set(r, { kind, arrayHole });
1254
- consumed.add(arrayHole);
1255
- }
1256
- };
1257
- if (eachDir && eachDir.kind === "EachDirective") wire(eachDir.hole, "eachRender");
1258
- else if (node.kind === "Component" && node.tag === "For") {
1259
- const h = propHole("each");
1260
- if (h !== void 0) wire(h, "eachRender");
1261
- } else if (node.kind === "Component" && node.tag === "Index") {
1262
- const h = propHole("each");
1263
- if (h !== void 0) wire(h, "indexRender");
1264
- } else if (node.kind === "Component" && node.tag === "Show") {
1265
- const w = propHole("when");
1266
- const r = renderChild(node);
1267
- if (w !== void 0 && r !== void 0) {
1268
- roles.set(r, { kind: "showRender", whenHole: w });
1269
- consumed.add(w);
1326
+ for (const template of templatesIn(ts, sf)) {
1327
+ const tpl = template.template;
1328
+ if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
1329
+ const holes = tpl.templateSpans.map((s) => s.expression);
1330
+ const visit = (nodes, parent) => {
1331
+ for (const node of nodes) {
1332
+ if (node.kind === "Expression" && parent?.kind === "Element") {
1333
+ const expression = holes[node.hole];
1334
+ if (expression && isAccessorRead(ts, expression) && isAccessor(ts, checker, expression)) {
1335
+ const text = expression.getText(sf);
1336
+ out.push({
1337
+ file: sf,
1338
+ start: expression.getStart(sf),
1339
+ length: expression.getWidth(sf),
1340
+ category: ts.DiagnosticCategory.Warning,
1341
+ code: ACCESSOR_NOT_CALLED,
1342
+ messageText: `'${text}' is an accessor \u2014 call it (\`${text}()\`). Left bare it renders the function and never updates.`,
1343
+ source: "fluixi"
1344
+ });
1345
+ }
1270
1346
  }
1347
+ const children = "children" in node ? node.children : void 0;
1348
+ if (children) visit(children, node);
1271
1349
  }
1272
- }
1273
- if ("children" in node && node.children) analyze(node.children, roles, consumed);
1350
+ };
1351
+ visit(parseCached(ts, sf, tpl).root.children, null);
1274
1352
  }
1353
+ return out;
1275
1354
  }
1276
- function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
1277
- const roles = /* @__PURE__ */ new Map();
1278
- const consumed = /* @__PURE__ */ new Set();
1279
- const { root } = k(templatePieces(ts, tpl.node.template, sf));
1280
- analyze(root.children, roles, consumed);
1281
- const holeSrc = (i) => [tpl.holes[i].getStart(sf), tpl.holes[i].getEnd()];
1282
- const putHole = (i) => {
1283
- const [s, e] = holeSrc(i);
1284
- const genStart = gen.length;
1285
- gen += srcText.slice(s, e);
1286
- segments.push({ srcStart: s, srcEnd: e, genStart, genEnd: gen.length, hole: true });
1287
- };
1288
- const isIdent = (t) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t);
1289
- const putKey = (start, text) => {
1290
- const genStart = gen.length;
1291
- if (isIdent(text)) {
1292
- gen += text;
1293
- segments.push({ srcStart: start, srcEnd: start + text.length, genStart, genEnd: gen.length, key: true });
1294
- } else {
1295
- gen += JSON.stringify(text);
1296
- }
1297
- };
1298
- const calls = [];
1299
- const walkComponents = (nodes) => {
1300
- for (const node of nodes) {
1301
- if (node.kind === "Component") calls.push(componentPropsEmitter(node));
1302
- if ("children" in node && node.children) walkComponents(node.children);
1303
- }
1304
- };
1305
- function componentPropsEmitter(node) {
1306
- return () => {
1307
- gen += "__fxProps(";
1308
- if (node.tagHole != null) putHole(node.tagHole);
1309
- else if (CONTROL_FLOW.has(node.tag)) gen += `__fxCF.${node.tag}`;
1310
- else gen += node.tag;
1311
- gen += ", { ";
1312
- let first = true;
1313
- const entry = (keyStart, keyText, emitValue) => {
1314
- if (!first) gen += ", ";
1315
- first = false;
1316
- putKey(keyStart, keyText);
1317
- gen += ": ";
1318
- emitValue();
1319
- };
1320
- for (const a of node.attributes) {
1321
- if (a.kind === "Attribute") {
1322
- const keyStart = a.boolean ? a.loc.start + 1 : a.loc.start;
1323
- const v2 = a.value;
1324
- entry(keyStart, a.name, () => {
1325
- if (!v2) {
1326
- gen += "true";
1327
- } else if (v2.kind === "static") {
1328
- gen += JSON.stringify(v2.value);
1329
- } else if (v2.kind === "hole") {
1330
- putHole(v2.hole);
1331
- } else {
1332
- gen += "undefined as any";
1333
- }
1355
+ function isAccessorRead(ts, node) {
1356
+ return ts.isIdentifier(node) || ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
1357
+ }
1358
+ function isAccessor(ts, checker, node) {
1359
+ const signatures = checker.getTypeAtLocation(node).getCallSignatures();
1360
+ if (signatures.length !== 1) return false;
1361
+ const signature = signatures[0];
1362
+ if (signature.getParameters().some((p2) => !isOptional(ts, p2))) return false;
1363
+ const returned = signature.getReturnType();
1364
+ return !(returned.flags & (ts.TypeFlags.Void | ts.TypeFlags.Never));
1365
+ }
1366
+ function isOptional(ts, symbol) {
1367
+ const declaration = symbol.valueDeclaration;
1368
+ return !!declaration && ts.isParameter(declaration) && (!!declaration.questionToken || !!declaration.initializer || !!declaration.dotDotDotToken);
1369
+ }
1370
+ var cache = /* @__PURE__ */ new WeakMap();
1371
+ function templateDiagnostics(ts, ls, fileName) {
1372
+ const program = ls.getProgram();
1373
+ const sf = program?.getSourceFile(fileName);
1374
+ if (!program || !sf) return [];
1375
+ const hit = cache.get(sf);
1376
+ if (hit) return hit;
1377
+ const checker = program.getTypeChecker();
1378
+ const out = [];
1379
+ out.push(...accessorDiagnostics(ts, program, sf));
1380
+ for (const { fn, paramName, type } of inferParamsIn(ts, program, sf)) {
1381
+ if (acceptsAnyProperty(ts, checker, type)) continue;
1382
+ const visit = (node) => {
1383
+ if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === paramName && ts.isIdentifier(node.name)) {
1384
+ const symbol = checker.getSymbolAtLocation(node.expression);
1385
+ const declaredHere = symbol?.declarations?.some((d) => d.parent === fn);
1386
+ if (declaredHere && !checker.getPropertyOfType(type, node.name.text)) {
1387
+ out.push({
1388
+ file: sf,
1389
+ start: node.name.getStart(sf),
1390
+ length: node.name.getWidth(sf),
1391
+ category: ts.DiagnosticCategory.Error,
1392
+ code: 2339,
1393
+ messageText: `Property '${node.name.text}' does not exist on type '${checker.typeToString(type)}'.`,
1394
+ source: "fluixi"
1334
1395
  });
1335
- } else if (a.kind === "PropertyBinding") {
1336
- entry(a.loc.start + 1, a.name, () => putHole(a.hole));
1337
1396
  }
1338
1397
  }
1339
- gen += " })";
1398
+ ts.forEachChild(node, visit);
1340
1399
  };
1400
+ if (fn.body) visit(fn.body);
1341
1401
  }
1342
- walkComponents(root.children);
1343
- for (let i = 0; i < tpl.holes.length; i++) {
1344
- if (consumed.has(i)) continue;
1345
- const role = roles.get(i);
1346
- if (role && role.kind === "eachRender") {
1347
- calls.push(() => {
1348
- gen += "__fxEach(";
1349
- putHole(role.arrayHole);
1350
- gen += ", ";
1351
- putHole(i);
1352
- gen += ")";
1353
- });
1354
- } else if (role && role.kind === "indexRender") {
1355
- calls.push(() => {
1356
- gen += "__fxIndex(";
1357
- putHole(role.arrayHole);
1358
- gen += ", ";
1359
- putHole(i);
1360
- gen += ")";
1361
- });
1362
- } else if (role && role.kind === "showRender") {
1363
- calls.push(() => {
1364
- gen += "__fxShow(";
1365
- putHole(role.whenHole);
1366
- gen += ", ";
1367
- putHole(i);
1368
- gen += ")";
1369
- });
1370
- } else if (role && role.kind === "event") {
1371
- const key = EVENT_MAP_KEYS.has(role.event) ? role.event : role.event;
1372
- calls.push(() => {
1373
- gen += `__fxOn(${JSON.stringify(key)}, `;
1374
- putHole(i);
1375
- gen += ")";
1376
- });
1377
- } else if (role && role.kind === "ref" && isFn(ts, tpl.holes[i])) {
1378
- const tag = role.tag;
1379
- calls.push(() => {
1380
- gen += `__fxRef(${JSON.stringify(tag)}, `;
1381
- putHole(i);
1382
- gen += ")";
1383
- });
1384
- } else {
1385
- calls.push(() => {
1386
- gen += "__fxExpr(";
1387
- putHole(i);
1388
- gen += ")";
1389
- });
1390
- }
1402
+ cache.set(sf, out);
1403
+ return out;
1404
+ }
1405
+
1406
+ // src/fixes.ts
1407
+ function templateCodeFixes(ts, ls, fileName, start, end, errorCodes) {
1408
+ if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return [];
1409
+ const out = [];
1410
+ for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
1411
+ if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
1412
+ if (diagnostic.start > end || diagnostic.start + (diagnostic.length ?? 0) < start) continue;
1413
+ const at = diagnostic.start + (diagnostic.length ?? 0);
1414
+ const name = diagnostic.file?.text.slice(diagnostic.start, at) ?? "value";
1415
+ out.push({
1416
+ fixName: "fluixiCallAccessor",
1417
+ description: `Call '${name}'`,
1418
+ changes: [
1419
+ {
1420
+ fileName,
1421
+ textChanges: [{ span: { start: at, length: 0 }, newText: "()" }]
1422
+ }
1423
+ ],
1424
+ // Every occurrence in the file is the same edit, so offer to do them at once.
1425
+ fixAllDescription: "Call every uncalled accessor in this file"
1426
+ });
1391
1427
  }
1392
- gen += "(";
1393
- calls.forEach((emit, idx) => {
1394
- if (idx > 0) gen += ", ";
1395
- emit();
1396
- });
1397
- gen += calls.length > 0 ? ", undefined as any)" : "undefined as any)";
1398
- return gen;
1428
+ return out;
1429
+ }
1430
+ function templateFixAll(ts, ls, fileName, errorCodes) {
1431
+ if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return void 0;
1432
+ const textChanges = [];
1433
+ for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
1434
+ if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
1435
+ const at = diagnostic.start + (diagnostic.length ?? 0);
1436
+ textChanges.push({ span: { start: at, length: 0 }, newText: "()" });
1437
+ }
1438
+ if (textChanges.length === 0) return void 0;
1439
+ textChanges.sort((a, b2) => b2.span.start - a.span.start);
1440
+ return { changes: [{ fileName, textChanges }], commands: void 0 };
1399
1441
  }
1400
1442
 
1401
- // src/shadow.ts
1402
- var SUFFIX = ".__fx.ts";
1403
- function isShadowPath(fileName) {
1404
- return fileName.endsWith(SUFFIX);
1405
- }
1406
- function shadowPathFor(fileName) {
1407
- return fileName + SUFFIX;
1408
- }
1409
- function sourcePathFor(shadow) {
1410
- return shadow.slice(0, -SUFFIX.length);
1411
- }
1412
- var ShadowFiles = class {
1413
- constructor(ts, host) {
1414
- this.ts = ts;
1415
- this.host = host;
1416
- this.patchHost();
1417
- }
1418
- built = /* @__PURE__ */ new Map();
1419
- /** Source files known to contain no templates, so we skip building a shadow. */
1420
- barren = /* @__PURE__ */ new Map();
1421
- /**
1422
- * Teach the existing host about shadow paths. The language service holds this
1423
- * host object, so overriding its methods in place is enough — no second service.
1424
- */
1425
- patchHost() {
1426
- const ts = this.ts;
1427
- const host = this.host;
1428
- const self = this;
1429
- const originalNames = host.getScriptFileNames.bind(host);
1430
- host.getScriptFileNames = () => {
1431
- const names = originalNames();
1432
- const out = names.slice();
1433
- for (const name of names) {
1434
- if (!isShadowPath(name) && self.build(name)) out.push(shadowPathFor(name));
1443
+ // src/navigation.ts
1444
+ var spansByFile = /* @__PURE__ */ new WeakMap();
1445
+ function tagSpans(ts, sf) {
1446
+ const hit = spansByFile.get(sf);
1447
+ if (hit) return hit;
1448
+ const out = [];
1449
+ for (const template of templatesIn(ts, sf)) {
1450
+ const { root } = parseCached(ts, sf, template.template);
1451
+ const walk = (nodes) => {
1452
+ for (const node of nodes) {
1453
+ if (node.kind === "Component" && node.tag) {
1454
+ const component = node;
1455
+ const name = component.tag.split(".")[0];
1456
+ out.push({ name, start: component.loc.start + 1, length: name.length });
1457
+ const closing = closingTagName(sf.text, component.loc.end, component.tag);
1458
+ if (closing !== void 0) out.push({ name, start: closing, length: name.length });
1459
+ }
1460
+ if ("children" in node && node.children) walk(node.children);
1435
1461
  }
1436
- return out;
1437
1462
  };
1438
- const originalSnapshot = host.getScriptSnapshot.bind(host);
1439
- host.getScriptSnapshot = (fileName) => {
1440
- if (!isShadowPath(fileName)) return originalSnapshot(fileName);
1441
- const built = self.build(sourcePathFor(fileName));
1442
- return built ? ts.ScriptSnapshot.fromString(built.text) : void 0;
1443
- };
1444
- const originalVersion = host.getScriptVersion.bind(host);
1445
- host.getScriptVersion = (fileName) => isShadowPath(fileName) ? originalVersion(sourcePathFor(fileName)) : originalVersion(fileName);
1446
- if (typeof host.fileExists === "function") {
1447
- const originalExists = host.fileExists.bind(host);
1448
- host.fileExists = (fileName) => isShadowPath(fileName) ? !!self.build(sourcePathFor(fileName)) : originalExists(fileName);
1449
- }
1463
+ walk(root.children);
1450
1464
  }
1451
- /** Build (and cache per source version) the shadow text for a file. */
1452
- build(fileName) {
1453
- const version = this.host.getScriptVersion(fileName);
1454
- if (this.barren.get(fileName) === version) return void 0;
1455
- const hit = this.built.get(fileName);
1456
- if (hit && hit.version === version) return hit;
1457
- const snapshot = this.host.getScriptSnapshot(fileName);
1458
- if (!snapshot) return void 0;
1459
- const text = snapshot.getText(0, snapshot.getLength());
1460
- if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) {
1461
- this.barren.set(fileName, version);
1462
- this.built.delete(fileName);
1463
- return void 0;
1465
+ spansByFile.set(sf, out);
1466
+ return out;
1467
+ }
1468
+ function closingTagName(text, end, tag) {
1469
+ const open = text.lastIndexOf("</", end);
1470
+ if (open === -1) return void 0;
1471
+ const close = text.indexOf(">", open);
1472
+ if (close === -1 || close >= end) return void 0;
1473
+ const index = text.indexOf(tag, open);
1474
+ return index !== -1 && index < close ? index : void 0;
1475
+ }
1476
+ function tagAt(ts, sf, position) {
1477
+ if (!templateAtCached(ts, sf, position)) return void 0;
1478
+ return tagSpans(ts, sf).find((s) => position >= s.start && position <= s.start + s.length);
1479
+ }
1480
+ function symbolFor(ts, program, host, location, name) {
1481
+ const checker = program.getTypeChecker();
1482
+ const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
1483
+ const symbol = checker.getSymbolsInScope(location, flags).find((s) => s.name === name);
1484
+ if (!symbol) return autoImportedSymbol(ts, program, host, location.getSourceFile(), name);
1485
+ return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
1486
+ }
1487
+ function declarationFor(ts, program, host, sf, tag) {
1488
+ const template = templateAtCached(ts, sf, tag.start);
1489
+ if (!template) return void 0;
1490
+ const symbol = symbolFor(ts, program, host, template, tag.name);
1491
+ return symbol?.valueDeclaration ?? symbol?.declarations?.[0];
1492
+ }
1493
+ function templateDefinition(ts, ls, host, fileName, position) {
1494
+ const program = ls.getProgram();
1495
+ const sf = program?.getSourceFile(fileName);
1496
+ if (!program || !sf) return void 0;
1497
+ const tag = tagAt(ts, sf, position);
1498
+ if (!tag) return void 0;
1499
+ const decl = declarationFor(ts, program, host, sf, tag);
1500
+ if (!decl) {
1501
+ const site = declarationSite(ts, program, host, sf, tag.name);
1502
+ if (!site) return void 0;
1503
+ return [
1504
+ {
1505
+ fileName: site.fileName,
1506
+ textSpan: { start: site.start, length: site.length },
1507
+ kind: ts.ScriptElementKind.unknown,
1508
+ name: tag.name,
1509
+ containerKind: ts.ScriptElementKind.unknown,
1510
+ containerName: ""
1511
+ }
1512
+ ];
1513
+ }
1514
+ const target = decl.name ?? decl;
1515
+ const declFile = decl.getSourceFile();
1516
+ return [
1517
+ {
1518
+ fileName: declFile.fileName,
1519
+ textSpan: { start: target.getStart(declFile), length: target.getWidth(declFile) },
1520
+ kind: ts.ScriptElementKind.unknown,
1521
+ name: tag.name,
1522
+ containerKind: ts.ScriptElementKind.unknown,
1523
+ containerName: ""
1464
1524
  }
1465
- const sf = this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.Latest, true);
1466
- const virtual = buildVirtual(this.ts, sf);
1467
- if (!virtual) {
1468
- this.barren.set(fileName, version);
1469
- this.built.delete(fileName);
1470
- return void 0;
1525
+ ];
1526
+ }
1527
+ function tagOccurrences(ts, program, host, symbol, name) {
1528
+ const out = [];
1529
+ for (const sf of program.getSourceFiles()) {
1530
+ if (sf.isDeclarationFile) continue;
1531
+ const text = sf.text;
1532
+ if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) continue;
1533
+ if (text.indexOf(name) === -1) continue;
1534
+ for (const span of tagSpans(ts, sf)) {
1535
+ if (span.name !== name) continue;
1536
+ const template = templateAtCached(ts, sf, span.start);
1537
+ if (!template || symbolFor(ts, program, host, template, name) !== symbol) continue;
1538
+ out.push({ fileName: sf.fileName, span: { start: span.start, length: span.length } });
1471
1539
  }
1472
- const entry = {
1473
- version,
1474
- text: virtual.text + "\nexport {};\n",
1475
- segments: virtual.segments
1476
- };
1477
- this.built.set(fileName, entry);
1478
- return entry;
1479
1540
  }
1480
- /** The shadow path for a source file, when it has one. */
1481
- shadowFor(fileName) {
1482
- return this.build(fileName) ? shadowPathFor(fileName) : void 0;
1483
- }
1484
- segmentsFor(fileName) {
1485
- return this.build(fileName)?.segments;
1486
- }
1487
- toSrc(fileName, position) {
1488
- const segments = this.segmentsFor(fileName);
1489
- return segments ? toSource(segments, position) : -1;
1490
- }
1491
- inHole(fileName, position) {
1492
- const segments = this.segmentsFor(fileName);
1493
- return !!segments && inHole(segments, position);
1494
- }
1495
- inKey(fileName, position) {
1496
- const segments = this.segmentsFor(fileName);
1497
- return !!segments && inKey(segments, position);
1498
- }
1499
- };
1541
+ return out;
1542
+ }
1543
+ function symbolAt(ts, program, host, sf, position) {
1544
+ const checker = program.getTypeChecker();
1545
+ const tag = tagAt(ts, sf, position);
1546
+ if (tag) {
1547
+ const template = templateAtCached(ts, sf, tag.start);
1548
+ const symbol2 = template && symbolFor(ts, program, host, template, tag.name);
1549
+ return symbol2 ? { symbol: symbol2, name: tag.name } : void 0;
1550
+ }
1551
+ const token = tokenAt(ts, sf, position);
1552
+ if (!token || !ts.isIdentifier(token)) return void 0;
1553
+ let symbol = checker.getSymbolAtLocation(token);
1554
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
1555
+ return symbol ? { symbol, name: token.text } : void 0;
1556
+ }
1557
+ function tokenAt(ts, sf, position) {
1558
+ let found;
1559
+ const visit = (node) => {
1560
+ if (position < node.getStart(sf) || position > node.getEnd()) return;
1561
+ if (node.getChildCount(sf) === 0) found = node;
1562
+ ts.forEachChild(node, visit);
1563
+ };
1564
+ visit(sf);
1565
+ return found;
1566
+ }
1567
+ function templateReferences(ts, ls, host, fileName, position) {
1568
+ const program = ls.getProgram();
1569
+ const sf = program?.getSourceFile(fileName);
1570
+ if (!program || !sf) return [];
1571
+ const hit = symbolAt(ts, program, host, sf, position);
1572
+ if (!hit) return [];
1573
+ return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
1574
+ fileName: f2,
1575
+ textSpan: span,
1576
+ isWriteAccess: false,
1577
+ isDefinition: false
1578
+ }));
1579
+ }
1580
+ function templateRenameLocations(ts, ls, host, fileName, position) {
1581
+ const program = ls.getProgram();
1582
+ const sf = program?.getSourceFile(fileName);
1583
+ if (!program || !sf) return [];
1584
+ const hit = symbolAt(ts, program, host, sf, position);
1585
+ if (!hit) return [];
1586
+ return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
1587
+ fileName: f2,
1588
+ textSpan: span
1589
+ }));
1590
+ }
1500
1591
 
1501
1592
  // src/index.ts
1502
1593
  function init(modules) {
@@ -1505,12 +1596,6 @@ function init(modules) {
1505
1596
  create(info) {
1506
1597
  const ls = info.languageService;
1507
1598
  const host = info.languageServiceHost;
1508
- let shadows;
1509
- try {
1510
- shadows = new ShadowFiles(ts, info.languageServiceHost);
1511
- } catch {
1512
- shadows = void 0;
1513
- }
1514
1599
  const proxy = /* @__PURE__ */ Object.create(null);
1515
1600
  for (const key of Object.keys(ls)) {
1516
1601
  const member = ls[key];
@@ -1523,6 +1608,15 @@ function init(modules) {
1523
1608
  entries
1524
1609
  });
1525
1610
  const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
1611
+ const dedupe = (items) => {
1612
+ const seen = /* @__PURE__ */ new Set();
1613
+ return items.filter((i) => {
1614
+ const key = `${i.fileName}:${i.textSpan.start}:${i.textSpan.length}`;
1615
+ if (seen.has(key)) return false;
1616
+ seen.add(key);
1617
+ return true;
1618
+ });
1619
+ };
1526
1620
  const reliableFilter = (fileName, diagnostics) => {
1527
1621
  const program = ls.getProgram();
1528
1622
  const sourceFile = program?.getSourceFile(fileName);
@@ -1534,38 +1628,15 @@ function init(modules) {
1534
1628
  return true;
1535
1629
  });
1536
1630
  };
1537
- const holeDiagnostics = (fileName) => {
1538
- const shadow = shadows?.shadowFor(fileName);
1539
- if (!shadow) return [];
1631
+ proxy.getSemanticDiagnostics = (fileName) => {
1632
+ const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1540
1633
  try {
1541
- const sourceFile = ls.getProgram()?.getSourceFile(fileName);
1542
- const out = [];
1543
- for (const d of ls.getSemanticDiagnostics(shadow)) {
1544
- if (d.start === void 0) continue;
1545
- const start = shadows.toSrc(fileName, d.start);
1546
- if (start === -1) continue;
1547
- if (!shadows.inHole(fileName, start) && !shadows.inKey(fileName, start)) continue;
1548
- out.push({ ...d, start, file: sourceFile });
1549
- }
1550
- return out;
1634
+ return base.concat(templateDiagnostics(ts, ls, fileName));
1551
1635
  } catch {
1552
- return [];
1636
+ return base;
1553
1637
  }
1554
1638
  };
1555
- proxy.getSemanticDiagnostics = (fileName) => {
1556
- if (isShadowPath(fileName)) return [];
1557
- const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1558
- return base.concat(holeDiagnostics(fileName));
1559
- };
1560
- proxy.getSuggestionDiagnostics = (fileName) => isShadowPath(fileName) ? [] : reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1561
- proxy.getSyntacticDiagnostics = (fileName) => isShadowPath(fileName) ? [] : ls.getSyntacticDiagnostics(fileName);
1562
- const notShadow = (items) => items?.filter((i) => !isShadowPath(i.fileName));
1563
- proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => notShadow(ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs));
1564
- proxy.getReferencesAtPosition = (fileName, position) => notShadow(ls.getReferencesAtPosition(fileName, position));
1565
- proxy.getDefinitionAtPosition = (fileName, position) => notShadow(ls.getDefinitionAtPosition(fileName, position));
1566
- proxy.getImplementationAtPosition = (fileName, position) => notShadow(ls.getImplementationAtPosition(fileName, position));
1567
- proxy.getDocumentHighlights = (fileName, position, filesToSearch) => ls.getDocumentHighlights(fileName, position, filesToSearch)?.filter((h) => !isShadowPath(h.fileName));
1568
- proxy.findReferences = (fileName, position) => ls.findReferences(fileName, position)?.filter((r) => !isShadowPath(r.definition.fileName)).map((r) => ({ ...r, references: r.references.filter((x2) => !isShadowPath(x2.fileName)) }));
1639
+ proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1569
1640
  proxy.getQuickInfoAtPosition = (fileName, position) => {
1570
1641
  try {
1571
1642
  const prop = componentPropHover(ts, ls, fileName, position);
@@ -1582,7 +1653,12 @@ function init(modules) {
1582
1653
  if (inferred) return inferred;
1583
1654
  } catch {
1584
1655
  }
1585
- return ls.getQuickInfoAtPosition(fileName, position) ?? componentQuickInfo(ts, ls, fileName, position);
1656
+ try {
1657
+ const tag = componentQuickInfo(ts, ls, host, fileName, position);
1658
+ if (tag) return tag;
1659
+ } catch {
1660
+ }
1661
+ return ls.getQuickInfoAtPosition(fileName, position);
1586
1662
  };
1587
1663
  proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
1588
1664
  try {
@@ -1602,6 +1678,71 @@ function init(modules) {
1602
1678
  }
1603
1679
  return ls.getCompletionsAtPosition(fileName, position, options, formatting);
1604
1680
  };
1681
+ proxy.getDefinitionAtPosition = (fileName, position) => {
1682
+ try {
1683
+ const tag = templateDefinition(ts, ls, host, fileName, position);
1684
+ if (tag) return tag;
1685
+ } catch {
1686
+ }
1687
+ return ls.getDefinitionAtPosition(fileName, position);
1688
+ };
1689
+ proxy.getDefinitionAndBoundSpan = (fileName, position) => {
1690
+ try {
1691
+ const definitions = templateDefinition(ts, ls, host, fileName, position);
1692
+ if (definitions) {
1693
+ const sf = ls.getProgram()?.getSourceFile(fileName);
1694
+ const tag = sf && tagAt(ts, sf, position);
1695
+ if (tag) {
1696
+ return { definitions, textSpan: { start: tag.start, length: tag.length } };
1697
+ }
1698
+ }
1699
+ } catch {
1700
+ }
1701
+ return ls.getDefinitionAndBoundSpan(fileName, position);
1702
+ };
1703
+ proxy.getReferencesAtPosition = (fileName, position) => {
1704
+ const base = ls.getReferencesAtPosition(fileName, position) ?? [];
1705
+ try {
1706
+ const extra = templateReferences(ts, ls, host, fileName, position);
1707
+ return extra.length ? dedupe([...base, ...extra]) : base;
1708
+ } catch {
1709
+ return base;
1710
+ }
1711
+ };
1712
+ proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => {
1713
+ const base = ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs) ?? [];
1714
+ try {
1715
+ const extra = templateRenameLocations(ts, ls, host, fileName, position);
1716
+ if (!base.length || !extra.length) return base.length ? base : void 0;
1717
+ return dedupe([...base, ...extra]);
1718
+ } catch {
1719
+ return base;
1720
+ }
1721
+ };
1722
+ proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
1723
+ const base = ls.getCodeFixesAtPosition(
1724
+ fileName,
1725
+ start,
1726
+ end,
1727
+ errorCodes,
1728
+ formatOptions,
1729
+ preferences
1730
+ );
1731
+ try {
1732
+ const own = templateCodeFixes(ts, ls, fileName, start, end, errorCodes);
1733
+ return own.length ? [...base, ...own] : base;
1734
+ } catch {
1735
+ return base;
1736
+ }
1737
+ };
1738
+ proxy.getCombinedCodeFix = (scope, fixId, formatOptions, preferences) => {
1739
+ if (fixId === "fluixiCallAccessor") {
1740
+ const fileName = scope.fileName;
1741
+ const combined = templateFixAll(ts, ls, fileName, [ACCESSOR_NOT_CALLED]);
1742
+ if (combined) return combined;
1743
+ }
1744
+ return ls.getCombinedCodeFix(scope, fixId, formatOptions, preferences);
1745
+ };
1605
1746
  return proxy;
1606
1747
  }
1607
1748
  };