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