@fluixi/ts-plugin 0.1.0-alpha.1 → 0.1.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -14
- package/dist/index.js +1032 -635
- 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,53 +451,214 @@ function importedNames(ts, decl) {
|
|
|
411
451
|
}
|
|
412
452
|
|
|
413
453
|
// src/pieces.ts
|
|
454
|
+
function rawContent(ts, node, sf) {
|
|
455
|
+
const text = node.getText(sf);
|
|
456
|
+
const closesWithSubstitution = ts.isTemplateHead(node) || ts.isTemplateMiddle(node);
|
|
457
|
+
return text.slice(1, closesWithSubstitution ? -2 : -1);
|
|
458
|
+
}
|
|
414
459
|
function templatePieces(ts, tpl, sf) {
|
|
415
460
|
if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
|
|
416
|
-
return [{ kind: "static", text: tpl
|
|
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
|
}
|
|
426
|
-
|
|
427
|
-
|
|
477
|
+
|
|
478
|
+
// src/cache.ts
|
|
479
|
+
var parsed = /* @__PURE__ */ new WeakMap();
|
|
480
|
+
function parseCached(ts, sf, tpl) {
|
|
481
|
+
let byTemplate = parsed.get(sf);
|
|
482
|
+
if (!byTemplate) {
|
|
483
|
+
byTemplate = /* @__PURE__ */ new Map();
|
|
484
|
+
parsed.set(sf, byTemplate);
|
|
485
|
+
}
|
|
486
|
+
const key = tpl.getStart(sf);
|
|
487
|
+
const hit = byTemplate.get(key);
|
|
488
|
+
if (hit) return hit;
|
|
489
|
+
const result = b(templatePieces(ts, tpl, sf));
|
|
490
|
+
byTemplate.set(key, result);
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
var templates = /* @__PURE__ */ new WeakMap();
|
|
494
|
+
function templatesIn(ts, sf) {
|
|
495
|
+
const hit = templates.get(sf);
|
|
496
|
+
if (hit) return hit;
|
|
497
|
+
const found = [];
|
|
428
498
|
const visit = (node) => {
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
found = node;
|
|
432
|
-
return;
|
|
499
|
+
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
|
|
500
|
+
found.push(node);
|
|
433
501
|
}
|
|
434
502
|
ts.forEachChild(node, visit);
|
|
435
503
|
};
|
|
436
504
|
visit(sf);
|
|
505
|
+
found.sort((a2, b2) => a2.getStart(sf) - b2.getStart(sf));
|
|
506
|
+
templates.set(sf, found);
|
|
437
507
|
return found;
|
|
438
508
|
}
|
|
509
|
+
function templateAtCached(ts, sf, pos) {
|
|
510
|
+
let match;
|
|
511
|
+
for (const tpl of templatesIn(ts, sf)) {
|
|
512
|
+
const start = tpl.template.getStart(sf);
|
|
513
|
+
if (start > pos) break;
|
|
514
|
+
if (pos <= tpl.template.getEnd()) match = tpl;
|
|
515
|
+
}
|
|
516
|
+
return match;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ../compiler/dist/resolve/builtins.mjs
|
|
520
|
+
var s = ["Show", "For", "Index", "Switch", "Match", "Portal", "Dynamic", "ErrorBoundary"];
|
|
521
|
+
var i = ["Suspense", "SuspenseList", "Await"];
|
|
522
|
+
var u2 = ["Router", "Outlet", "Redirect", "Link"];
|
|
523
|
+
var p2 = [...s, ...i, ...u2];
|
|
524
|
+
var a = /* @__PURE__ */ new Set([...s, ...i]);
|
|
525
|
+
function l(o = {}) {
|
|
526
|
+
let { controlFlowModule: e = "@fluixi/dom", coreModule: r = "@fluixi/core", routerModule: c = "@fluixi/core/router-next" } = o, n = {};
|
|
527
|
+
for (let t of s) n[t] = e;
|
|
528
|
+
for (let t of i) n[t] = r;
|
|
529
|
+
for (let t of u2) n[t] = c;
|
|
530
|
+
return n;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/auto-import.ts
|
|
534
|
+
var AUTO_IMPORTED = l();
|
|
535
|
+
var exportsByProgram = /* @__PURE__ */ new WeakMap();
|
|
536
|
+
function moduleExports(ts, program, host, from, moduleName) {
|
|
537
|
+
let cache2 = exportsByProgram.get(program);
|
|
538
|
+
if (!cache2) {
|
|
539
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
540
|
+
exportsByProgram.set(program, cache2);
|
|
541
|
+
}
|
|
542
|
+
if (cache2.has(moduleName)) return cache2.get(moduleName);
|
|
543
|
+
let symbols;
|
|
544
|
+
try {
|
|
545
|
+
const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
|
|
546
|
+
const file = resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName);
|
|
547
|
+
const checker = program.getTypeChecker();
|
|
548
|
+
const moduleSymbol = file && checker.getSymbolAtLocation(file);
|
|
549
|
+
if (moduleSymbol) symbols = checker.getExportsOfModule(moduleSymbol);
|
|
550
|
+
} catch {
|
|
551
|
+
symbols = void 0;
|
|
552
|
+
}
|
|
553
|
+
cache2.set(moduleName, symbols);
|
|
554
|
+
return symbols;
|
|
555
|
+
}
|
|
556
|
+
var sitesByProgram = /* @__PURE__ */ new WeakMap();
|
|
557
|
+
function declarationSite(ts, program, host, from, name) {
|
|
558
|
+
const moduleName = AUTO_IMPORTED[name];
|
|
559
|
+
if (!moduleName) return void 0;
|
|
560
|
+
let cache2 = sitesByProgram.get(program);
|
|
561
|
+
if (!cache2) {
|
|
562
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
563
|
+
sitesByProgram.set(program, cache2);
|
|
564
|
+
}
|
|
565
|
+
const key = `${moduleName}:${name}`;
|
|
566
|
+
if (cache2.has(key)) return cache2.get(key);
|
|
567
|
+
let site;
|
|
568
|
+
try {
|
|
569
|
+
const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
|
|
570
|
+
const entry = resolved.resolvedModule?.resolvedFileName;
|
|
571
|
+
if (entry) site = findExport(ts, host, program.getCompilerOptions(), entry, name, 0);
|
|
572
|
+
} catch {
|
|
573
|
+
site = void 0;
|
|
574
|
+
}
|
|
575
|
+
cache2.set(key, site);
|
|
576
|
+
return site;
|
|
577
|
+
}
|
|
578
|
+
function findExport(ts, host, options, fileName, name, depth) {
|
|
579
|
+
if (depth > 4) return void 0;
|
|
580
|
+
const text = host.readFile?.(fileName);
|
|
581
|
+
if (text === void 0) return void 0;
|
|
582
|
+
const sf = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true);
|
|
583
|
+
const reExports = [];
|
|
584
|
+
for (const statement of sf.statements) {
|
|
585
|
+
const declared = declaredName(ts, statement, name);
|
|
586
|
+
if (declared) {
|
|
587
|
+
return { fileName, start: declared.getStart(sf), length: declared.getWidth(sf) };
|
|
588
|
+
}
|
|
589
|
+
if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
590
|
+
const specifier = statement.moduleSpecifier.text;
|
|
591
|
+
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
592
|
+
const named = statement.exportClause.elements.find((e) => e.name.text === name);
|
|
593
|
+
if (named) reExports.unshift(specifier);
|
|
594
|
+
} else if (!statement.exportClause) {
|
|
595
|
+
reExports.push(specifier);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
for (const specifier of reExports) {
|
|
600
|
+
const target = resolveRelative(ts, host, options, fileName, specifier);
|
|
601
|
+
const found = target && findExport(ts, host, options, target, name, depth + 1);
|
|
602
|
+
if (found) return found;
|
|
603
|
+
}
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
function declaredName(ts, statement, name) {
|
|
607
|
+
const exported = ts.canHaveModifiers(statement) ? ts.getModifiers(statement)?.some((m2) => m2.kind === ts.SyntaxKind.ExportKeyword) : false;
|
|
608
|
+
if (!exported) return void 0;
|
|
609
|
+
if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name?.text === name) {
|
|
610
|
+
return statement.name;
|
|
611
|
+
}
|
|
612
|
+
if (ts.isVariableStatement(statement)) {
|
|
613
|
+
for (const d of statement.declarationList.declarations) {
|
|
614
|
+
if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
function resolveRelative(ts, host, options, from, specifier) {
|
|
620
|
+
if (!specifier.startsWith(".")) return void 0;
|
|
621
|
+
try {
|
|
622
|
+
return ts.resolveModuleName(specifier, from, options, host).resolvedModule?.resolvedFileName;
|
|
623
|
+
} catch {
|
|
624
|
+
return void 0;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function autoImportedSymbol(ts, program, host, from, name) {
|
|
628
|
+
const moduleName = AUTO_IMPORTED[name];
|
|
629
|
+
if (!moduleName) return void 0;
|
|
630
|
+
const symbol = moduleExports(ts, program, host, from, moduleName)?.find((s2) => s2.name === name);
|
|
631
|
+
if (!symbol) return void 0;
|
|
632
|
+
if (!(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
|
|
633
|
+
try {
|
|
634
|
+
return program.getTypeChecker().getAliasedSymbol(symbol);
|
|
635
|
+
} catch {
|
|
636
|
+
return symbol;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
439
639
|
|
|
440
640
|
// src/hover.ts
|
|
441
|
-
function componentQuickInfo(ts, ls, fileName, position) {
|
|
641
|
+
function componentQuickInfo(ts, ls, host, fileName, position) {
|
|
442
642
|
const program = ls.getProgram();
|
|
443
643
|
const sf = program?.getSourceFile(fileName);
|
|
444
644
|
if (!program || !sf) return void 0;
|
|
445
|
-
const template =
|
|
645
|
+
const template = templateAtCached(ts, sf, position);
|
|
446
646
|
if (!template) return void 0;
|
|
447
647
|
const hit = componentTagAt(template, sf, position, ts);
|
|
448
648
|
if (!hit) return void 0;
|
|
449
649
|
const checker = program.getTypeChecker();
|
|
450
650
|
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
451
|
-
const symbol = checker.getSymbolsInScope(template, flags).find((
|
|
651
|
+
const symbol = checker.getSymbolsInScope(template, flags).find((s2) => s2.name === hit.name) ?? autoImportedSymbol(ts, program, host, sf, hit.name);
|
|
452
652
|
const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
453
653
|
if (!decl) return void 0;
|
|
454
|
-
const
|
|
455
|
-
const
|
|
654
|
+
const declFile = decl.getSourceFile();
|
|
655
|
+
const namePos = decl.name?.getStart(declFile) ?? decl.getStart(declFile);
|
|
656
|
+
const info = ls.getQuickInfoAtPosition(declFile.fileName, namePos);
|
|
456
657
|
if (!info) return void 0;
|
|
457
658
|
return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
|
|
458
659
|
}
|
|
459
660
|
function componentTagAt(template, sf, position, ts) {
|
|
460
|
-
const { root } =
|
|
661
|
+
const { root } = parseCached(ts, sf, template.template);
|
|
461
662
|
let hit;
|
|
462
663
|
const walk = (nodes) => {
|
|
463
664
|
for (const node of nodes) {
|
|
@@ -584,21 +785,21 @@ function holeRoles(nodes, out) {
|
|
|
584
785
|
};
|
|
585
786
|
for (const node of nodes) {
|
|
586
787
|
if (node.kind === "Element" || node.kind === "Component") {
|
|
587
|
-
for (const
|
|
588
|
-
if (
|
|
589
|
-
if (node.kind === "Element" &&
|
|
590
|
-
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 });
|
|
591
792
|
}
|
|
592
|
-
const eachDir = node.attributes.find((
|
|
793
|
+
const eachDir = node.attributes.find((a2) => a2.kind === "EachDirective");
|
|
593
794
|
if (eachDir && eachDir.kind === "EachDirective") {
|
|
594
795
|
renderChildren(node, { kind: "each", eachHole: eachDir.hole });
|
|
595
796
|
}
|
|
596
797
|
if (node.kind === "Component") {
|
|
597
798
|
const propHole = (name) => {
|
|
598
|
-
const
|
|
799
|
+
const a2 = node.attributes.find(
|
|
599
800
|
(x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
|
|
600
801
|
);
|
|
601
|
-
return
|
|
802
|
+
return a2 && a2.kind === "Attribute" && a2.value && a2.value.kind === "hole" ? a2.value.hole : void 0;
|
|
602
803
|
};
|
|
603
804
|
const eachHole = propHole("each");
|
|
604
805
|
if (node.tag === "For" && eachHole !== void 0) renderChildren(node, { kind: "each", eachHole });
|
|
@@ -611,7 +812,7 @@ function holeRoles(nodes, out) {
|
|
|
611
812
|
}
|
|
612
813
|
}
|
|
613
814
|
function resolveGlobalType(ts, checker, location, name) {
|
|
614
|
-
const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((
|
|
815
|
+
const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((s2) => s2.getName() === name);
|
|
615
816
|
return sym ? checker.getDeclaredTypeOfSymbol(sym) : void 0;
|
|
616
817
|
}
|
|
617
818
|
function resolveElementType(ts, checker, location, tag) {
|
|
@@ -621,14 +822,14 @@ function resolveElementType(ts, checker, location, tag) {
|
|
|
621
822
|
return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
|
|
622
823
|
}
|
|
623
824
|
function inferParamAt(ts, program, sf, position) {
|
|
624
|
-
const template =
|
|
825
|
+
const template = templateAtCached(ts, sf, position);
|
|
625
826
|
if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
|
|
626
827
|
const spans = template.template.templateSpans;
|
|
627
|
-
const holeExprs = spans.map((
|
|
828
|
+
const holeExprs = spans.map((s2) => s2.expression);
|
|
628
829
|
const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
|
|
629
830
|
if (holeIndex === -1) return void 0;
|
|
630
831
|
const roles = /* @__PURE__ */ new Map();
|
|
631
|
-
holeRoles(
|
|
832
|
+
holeRoles(parseCached(ts, sf, template.template).root.children, roles);
|
|
632
833
|
const role = roles.get(holeIndex);
|
|
633
834
|
if (!role) return void 0;
|
|
634
835
|
const fn = holeExprs[holeIndex];
|
|
@@ -639,6 +840,12 @@ function inferParamAt(ts, program, sf, position) {
|
|
|
639
840
|
const id = identifierAt(ts, sf, position);
|
|
640
841
|
if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
|
|
641
842
|
const checker = program.getTypeChecker();
|
|
843
|
+
const resolved = typeForRole(ts, checker, fn, role, holeExprs);
|
|
844
|
+
const { typeText, type } = resolved;
|
|
845
|
+
if (!typeText) return void 0;
|
|
846
|
+
return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
|
|
847
|
+
}
|
|
848
|
+
function typeForRole(ts, checker, fn, role, holeExprs) {
|
|
642
849
|
let typeText;
|
|
643
850
|
let type;
|
|
644
851
|
if (role.kind === "event") {
|
|
@@ -659,8 +866,27 @@ function inferParamAt(ts, program, sf, position) {
|
|
|
659
866
|
type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
|
|
660
867
|
if (type) typeText = checker.typeToString(type);
|
|
661
868
|
}
|
|
662
|
-
|
|
663
|
-
|
|
869
|
+
return { typeText, type };
|
|
870
|
+
}
|
|
871
|
+
function inferParamsIn(ts, program, sf) {
|
|
872
|
+
const checker = program.getTypeChecker();
|
|
873
|
+
const out = [];
|
|
874
|
+
for (const template of templatesIn(ts, sf)) {
|
|
875
|
+
const tpl = template.template;
|
|
876
|
+
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
877
|
+
const holeExprs = tpl.templateSpans.map((s2) => s2.expression);
|
|
878
|
+
const roles = /* @__PURE__ */ new Map();
|
|
879
|
+
holeRoles(parseCached(ts, sf, tpl).root.children, roles);
|
|
880
|
+
for (const [holeIndex, role] of roles) {
|
|
881
|
+
const fn = holeExprs[holeIndex];
|
|
882
|
+
if (!fn || !ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue;
|
|
883
|
+
const param = fn.parameters[0];
|
|
884
|
+
if (!param || !ts.isIdentifier(param.name) || param.type) continue;
|
|
885
|
+
const { type } = typeForRole(ts, checker, fn, role, holeExprs);
|
|
886
|
+
if (type) out.push({ fn, paramName: param.name.text, type });
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return out;
|
|
664
890
|
}
|
|
665
891
|
function paramQuickInfo(ts, ls, fileName, position) {
|
|
666
892
|
const program = ls.getProgram();
|
|
@@ -702,40 +928,39 @@ function contains(outer, inner) {
|
|
|
702
928
|
}
|
|
703
929
|
|
|
704
930
|
// src/props.ts
|
|
705
|
-
function componentPropCompletions(ts, ls, fileName, position) {
|
|
931
|
+
function componentPropCompletions(ts, ls, host, fileName, position) {
|
|
706
932
|
const program = ls.getProgram();
|
|
707
933
|
const sf = program?.getSourceFile(fileName);
|
|
708
934
|
if (!program || !sf) return void 0;
|
|
709
|
-
const template =
|
|
935
|
+
const template = templateAtCached(ts, sf, position);
|
|
710
936
|
if (!template) return void 0;
|
|
711
937
|
const comp = componentTagAtAttrArea(ts, sf, template.template, position);
|
|
712
938
|
if (!comp || !comp.tag) return void 0;
|
|
939
|
+
const span = attrNameSpan(sf.text, position);
|
|
713
940
|
const cf = CONTROL_FLOW_PROPS[comp.tag];
|
|
714
941
|
if (cf) {
|
|
715
942
|
const already2 = new Set(comp.attributeNames);
|
|
716
|
-
return cf.filter((n) => !already2.has(n)).map((name) => propEntry(ts, name));
|
|
943
|
+
return cf.filter((n) => !already2.has(n)).map((name) => propEntry(ts, name, span));
|
|
717
944
|
}
|
|
718
945
|
const checker = program.getTypeChecker();
|
|
719
946
|
const rootName = comp.tag.split(".")[0];
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
const compType = checker.getTypeAtLocation(decl);
|
|
723
|
-
const propsType = firstParamType(ts, checker, compType);
|
|
947
|
+
const compType = componentTypeOf(ts, program, checker, host, sf, template.template, rootName);
|
|
948
|
+
const propsType = compType && firstParamType(ts, checker, compType);
|
|
724
949
|
if (!propsType) return void 0;
|
|
725
950
|
const already = new Set(comp.attributeNames);
|
|
726
951
|
const entries = [];
|
|
727
|
-
for (const
|
|
728
|
-
const name =
|
|
952
|
+
for (const p3 of propsType.getProperties()) {
|
|
953
|
+
const name = p3.getName();
|
|
729
954
|
if (name === "children" || already.has(name)) continue;
|
|
730
|
-
entries.push(propEntry(ts, name));
|
|
955
|
+
entries.push(propEntry(ts, name, span));
|
|
731
956
|
}
|
|
732
957
|
return entries.length ? entries : void 0;
|
|
733
958
|
}
|
|
734
|
-
function componentPropHover(ts, ls, fileName, position) {
|
|
959
|
+
function componentPropHover(ts, ls, host, fileName, position) {
|
|
735
960
|
const program = ls.getProgram();
|
|
736
961
|
const sf = program?.getSourceFile(fileName);
|
|
737
962
|
if (!program || !sf) return void 0;
|
|
738
|
-
const template =
|
|
963
|
+
const template = templateAtCached(ts, sf, position);
|
|
739
964
|
if (!template) return void 0;
|
|
740
965
|
const hit = propNameAt(ts, sf, template.template, position);
|
|
741
966
|
if (!hit) return void 0;
|
|
@@ -746,11 +971,13 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
746
971
|
if (!typeText) return void 0;
|
|
747
972
|
} else if (hit.tag) {
|
|
748
973
|
const checker = program.getTypeChecker();
|
|
749
|
-
const
|
|
750
|
-
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);
|
|
751
977
|
const sym = propsType && propsType.getProperty(hit.prop);
|
|
752
978
|
if (!sym) return void 0;
|
|
753
|
-
|
|
979
|
+
const at = sym.valueDeclaration ?? sym.declarations?.[0] ?? template.template;
|
|
980
|
+
typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, at));
|
|
754
981
|
}
|
|
755
982
|
if (!typeText) return void 0;
|
|
756
983
|
return {
|
|
@@ -771,16 +998,16 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
771
998
|
};
|
|
772
999
|
}
|
|
773
1000
|
function propNameAt(ts, sf, tpl, position) {
|
|
774
|
-
const { root } =
|
|
1001
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
775
1002
|
let hit;
|
|
776
1003
|
const walk = (nodes) => {
|
|
777
1004
|
for (const node of nodes) {
|
|
778
1005
|
if (node.kind === "Component") {
|
|
779
1006
|
const c = node;
|
|
780
|
-
for (const
|
|
781
|
-
if ((
|
|
782
|
-
const start =
|
|
783
|
-
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 };
|
|
784
1011
|
}
|
|
785
1012
|
}
|
|
786
1013
|
}
|
|
@@ -817,36 +1044,45 @@ var CONTROL_FLOW_PROPS = {
|
|
|
817
1044
|
Dynamic: ["component"],
|
|
818
1045
|
Await: ["value", "fallback"]
|
|
819
1046
|
};
|
|
820
|
-
function propEntry(ts, name) {
|
|
821
|
-
return {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1047
|
+
function propEntry(ts, name, replacementSpan) {
|
|
1048
|
+
return {
|
|
1049
|
+
name,
|
|
1050
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1051
|
+
kindModifiers: "",
|
|
1052
|
+
sortText: "0_" + name,
|
|
1053
|
+
insertText: name,
|
|
1054
|
+
// Without a span the editor filters these against whatever word *it* computes at the
|
|
1055
|
+
// cursor, and inside a template literal that is not the attribute name — so the list
|
|
1056
|
+
// showed in full until the first keystroke and then emptied. Saying exactly which
|
|
1057
|
+
// characters are being replaced makes filtering match what is typed.
|
|
1058
|
+
replacementSpan
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function attrNameSpan(text, position) {
|
|
1062
|
+
const isNameChar = (c) => !!c && /[A-Za-z0-9_$:@?.\-]/.test(c);
|
|
1063
|
+
let start = position;
|
|
1064
|
+
while (start > 0 && isNameChar(text[start - 1])) start--;
|
|
1065
|
+
let end = position;
|
|
1066
|
+
while (end < text.length && isNameChar(text[end])) end++;
|
|
1067
|
+
return { start, length: end - start };
|
|
1068
|
+
}
|
|
1069
|
+
function componentTypeOf(ts, program, checker, host, sf, template, name) {
|
|
1070
|
+
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
1071
|
+
const symbol = checker.getSymbolsInScope(template, flags).find((s2) => s2.name === name) ?? autoImportedSymbol(ts, program, host, sf, name);
|
|
1072
|
+
if (!symbol) return void 0;
|
|
1073
|
+
const resolved = symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
1074
|
+
const decl = resolved.valueDeclaration ?? resolved.declarations?.[0];
|
|
1075
|
+
return decl ? checker.getTypeOfSymbolAtLocation(resolved, decl) : void 0;
|
|
840
1076
|
}
|
|
841
1077
|
function firstParamType(ts, checker, type) {
|
|
842
1078
|
for (const sig of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
|
|
843
|
-
const
|
|
844
|
-
if (
|
|
1079
|
+
const p3 = sig.getParameters()[0];
|
|
1080
|
+
if (p3) return checker.getTypeOfSymbolAtLocation(p3, p3.valueDeclaration ?? sig.getDeclaration());
|
|
845
1081
|
}
|
|
846
1082
|
return void 0;
|
|
847
1083
|
}
|
|
848
1084
|
function componentTagAtAttrArea(ts, sf, tpl, position) {
|
|
849
|
-
const { root } =
|
|
1085
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
850
1086
|
let hit;
|
|
851
1087
|
const walk = (nodes) => {
|
|
852
1088
|
for (const node of nodes) {
|
|
@@ -854,7 +1090,7 @@ function componentTagAtAttrArea(ts, sf, tpl, position) {
|
|
|
854
1090
|
const c = node;
|
|
855
1091
|
const openStart = c.loc.start + 1 + (c.tag ? c.tag.length : 0);
|
|
856
1092
|
const openEnd = firstChildOrCloseStart(c);
|
|
857
|
-
if (position > openStart && position
|
|
1093
|
+
if (position > openStart && position < openEnd) {
|
|
858
1094
|
hit = { tag: c.tag, attributeNames: attributeNames(c) };
|
|
859
1095
|
}
|
|
860
1096
|
}
|
|
@@ -872,8 +1108,8 @@ function firstChildOrCloseStart(c) {
|
|
|
872
1108
|
}
|
|
873
1109
|
function attributeNames(c) {
|
|
874
1110
|
const out = [];
|
|
875
|
-
for (const
|
|
876
|
-
if (
|
|
1111
|
+
for (const a2 of c.attributes) {
|
|
1112
|
+
if (a2.kind === "Attribute" || a2.kind === "PropertyBinding" || a2.kind === "BindDirective") out.push(a2.name);
|
|
877
1113
|
}
|
|
878
1114
|
return out;
|
|
879
1115
|
}
|
|
@@ -890,13 +1126,13 @@ function paramMemberCompletions(ts, ls, fileName, position) {
|
|
|
890
1126
|
const checker = program.getTypeChecker();
|
|
891
1127
|
const already = /* @__PURE__ */ new Set();
|
|
892
1128
|
const entries = [];
|
|
893
|
-
for (const
|
|
894
|
-
const name =
|
|
1129
|
+
for (const p3 of checker.getApparentType(info.type).getProperties()) {
|
|
1130
|
+
const name = p3.getName();
|
|
895
1131
|
if (already.has(name) || name.startsWith("__")) continue;
|
|
896
1132
|
already.add(name);
|
|
897
1133
|
entries.push({
|
|
898
1134
|
name,
|
|
899
|
-
kind: memberKind(ts,
|
|
1135
|
+
kind: memberKind(ts, p3),
|
|
900
1136
|
kindModifiers: "",
|
|
901
1137
|
sortText: "0_" + name,
|
|
902
1138
|
insertText: name
|
|
@@ -921,366 +1157,450 @@ function propertyAccessAt(ts, sf, position) {
|
|
|
921
1157
|
return found;
|
|
922
1158
|
}
|
|
923
1159
|
|
|
924
|
-
// src/
|
|
925
|
-
var
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
);
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
|
|
997
|
-
}
|
|
998
|
-
return -1;
|
|
999
|
-
}
|
|
1000
|
-
function buildVirtual(ts, sf) {
|
|
1001
|
-
const templates = collectTemplates(ts, sf);
|
|
1002
|
-
if (templates.length === 0) return void 0;
|
|
1003
|
-
const srcText = sf.text;
|
|
1004
|
-
const segments = [];
|
|
1005
|
-
let gen = "";
|
|
1006
|
-
let src = 0;
|
|
1007
|
-
const copy = (to) => {
|
|
1008
|
-
if (to <= src) return;
|
|
1009
|
-
const genStart = gen.length;
|
|
1010
|
-
gen += srcText.slice(src, to);
|
|
1011
|
-
segments.push({ srcStart: src, srcEnd: to, genStart, genEnd: gen.length });
|
|
1012
|
-
src = to;
|
|
1160
|
+
// src/elements.ts
|
|
1161
|
+
var ATTRIBUTES_MODULE = "@fluixi/dom/attributes";
|
|
1162
|
+
var perProgram = /* @__PURE__ */ new WeakMap();
|
|
1163
|
+
function intrinsics(ts, program, host, sf) {
|
|
1164
|
+
const hit = perProgram.get(program);
|
|
1165
|
+
if (hit) return hit;
|
|
1166
|
+
let type;
|
|
1167
|
+
try {
|
|
1168
|
+
const checker = program.getTypeChecker();
|
|
1169
|
+
const declared = (file) => {
|
|
1170
|
+
if (!file) return void 0;
|
|
1171
|
+
const moduleSymbol = checker.getSymbolAtLocation(file);
|
|
1172
|
+
const symbol = moduleSymbol?.exports?.get(ts.escapeLeadingUnderscores("IntrinsicElements"));
|
|
1173
|
+
return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : void 0;
|
|
1174
|
+
};
|
|
1175
|
+
const resolved = ts.resolveModuleName(
|
|
1176
|
+
ATTRIBUTES_MODULE,
|
|
1177
|
+
sf.fileName,
|
|
1178
|
+
program.getCompilerOptions(),
|
|
1179
|
+
host
|
|
1180
|
+
);
|
|
1181
|
+
type = declared(
|
|
1182
|
+
resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName)
|
|
1183
|
+
);
|
|
1184
|
+
if (!type) {
|
|
1185
|
+
for (const file of program.getSourceFiles()) {
|
|
1186
|
+
if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
|
|
1187
|
+
type = declared(file);
|
|
1188
|
+
if (type) break;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
} catch {
|
|
1192
|
+
type = void 0;
|
|
1193
|
+
}
|
|
1194
|
+
const entry = { type, byTag: /* @__PURE__ */ new Map() };
|
|
1195
|
+
perProgram.set(program, entry);
|
|
1196
|
+
return entry;
|
|
1197
|
+
}
|
|
1198
|
+
function attributesFor(ts, program, host, sf, tag) {
|
|
1199
|
+
const state = intrinsics(ts, program, host, sf);
|
|
1200
|
+
if (!state.type) return void 0;
|
|
1201
|
+
if (state.byTag.has(tag)) return state.byTag.get(tag);
|
|
1202
|
+
let props;
|
|
1203
|
+
const checker = program.getTypeChecker();
|
|
1204
|
+
const tagSymbol = state.type.getProperty(tag);
|
|
1205
|
+
if (tagSymbol) {
|
|
1206
|
+
const decl = tagSymbol.valueDeclaration ?? tagSymbol.declarations?.[0];
|
|
1207
|
+
if (decl) props = checker.getTypeOfSymbolAtLocation(tagSymbol, decl).getProperties();
|
|
1208
|
+
}
|
|
1209
|
+
state.byTag.set(tag, props);
|
|
1210
|
+
return props;
|
|
1211
|
+
}
|
|
1212
|
+
function elementAtAttributeArea(ts, sf, tpl, position) {
|
|
1213
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1214
|
+
let hit;
|
|
1215
|
+
const walk = (nodes) => {
|
|
1216
|
+
for (const node of nodes) {
|
|
1217
|
+
if (node.kind === "Element") {
|
|
1218
|
+
const el = node;
|
|
1219
|
+
const openStart = el.loc.start + 1 + el.tag.length;
|
|
1220
|
+
const openEnd = firstAttributeEnd(el);
|
|
1221
|
+
if (position > openStart && position <= openEnd) {
|
|
1222
|
+
const present = /* @__PURE__ */ new Set();
|
|
1223
|
+
for (const a2 of el.attributes) {
|
|
1224
|
+
if ("loc" in a2 && a2.loc) present.add(writtenName(sf.text, a2.loc.start));
|
|
1225
|
+
}
|
|
1226
|
+
hit = { tag: el.tag, present };
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1230
|
+
if (hit) return;
|
|
1231
|
+
}
|
|
1013
1232
|
};
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1233
|
+
walk(root.children);
|
|
1234
|
+
return hit;
|
|
1235
|
+
}
|
|
1236
|
+
function writtenName(text, start) {
|
|
1237
|
+
let end = start;
|
|
1238
|
+
while (end < text.length && !/[\s=/>]/.test(text[end])) end++;
|
|
1239
|
+
return text.slice(start, end);
|
|
1240
|
+
}
|
|
1241
|
+
function firstAttributeEnd(el) {
|
|
1242
|
+
let end = el.loc.start + 1 + el.tag.length;
|
|
1243
|
+
for (const a2 of el.attributes) {
|
|
1244
|
+
if ("loc" in a2 && a2.loc) end = Math.max(end, a2.loc.end);
|
|
1019
1245
|
}
|
|
1020
|
-
|
|
1021
|
-
return { text: gen, segments };
|
|
1246
|
+
return end + 1;
|
|
1022
1247
|
}
|
|
1023
|
-
function
|
|
1024
|
-
|
|
1248
|
+
function elementAttributeCompletions(ts, ls, host, fileName, position) {
|
|
1249
|
+
const program = ls.getProgram();
|
|
1250
|
+
const sf = program?.getSourceFile(fileName);
|
|
1251
|
+
if (!program || !sf) return void 0;
|
|
1252
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1253
|
+
if (!tpl) return void 0;
|
|
1254
|
+
const hit = elementAtAttributeArea(ts, sf, tpl.template, position);
|
|
1255
|
+
if (!hit) return void 0;
|
|
1256
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1257
|
+
if (!props) return void 0;
|
|
1258
|
+
const entries = [];
|
|
1259
|
+
for (const p3 of props) {
|
|
1260
|
+
const name = p3.getName();
|
|
1261
|
+
if (name === "children" || hit.present.has(name)) continue;
|
|
1262
|
+
entries.push({
|
|
1263
|
+
name,
|
|
1264
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1265
|
+
kindModifiers: "",
|
|
1266
|
+
sortText: name.startsWith("on") ? "1" : "0"
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
return entries.length ? entries : void 0;
|
|
1025
1270
|
}
|
|
1026
|
-
function
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1271
|
+
function attributeNameAt(ts, sf, tpl, position) {
|
|
1272
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1273
|
+
let hit;
|
|
1274
|
+
const walk = (nodes) => {
|
|
1275
|
+
for (const node of nodes) {
|
|
1276
|
+
if (node.kind === "Element") {
|
|
1277
|
+
const el = node;
|
|
1278
|
+
for (const a2 of el.attributes) {
|
|
1279
|
+
if (!("loc" in a2) || !a2.loc) continue;
|
|
1280
|
+
const start = a2.loc.start;
|
|
1281
|
+
const name = writtenName(sf.text, start);
|
|
1282
|
+
if (name && position >= start && position <= start + name.length) {
|
|
1283
|
+
hit = { tag: el.tag, name, start };
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1288
|
+
if (hit) return;
|
|
1033
1289
|
}
|
|
1034
|
-
ts.forEachChild(node, visit);
|
|
1035
1290
|
};
|
|
1036
|
-
|
|
1037
|
-
return
|
|
1291
|
+
walk(root.children);
|
|
1292
|
+
return hit;
|
|
1038
1293
|
}
|
|
1039
|
-
function
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1294
|
+
function elementAttributeHover(ts, ls, host, fileName, position) {
|
|
1295
|
+
const program = ls.getProgram();
|
|
1296
|
+
const sf = program?.getSourceFile(fileName);
|
|
1297
|
+
if (!program || !sf) return void 0;
|
|
1298
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1299
|
+
if (!tpl) return void 0;
|
|
1300
|
+
const hit = attributeNameAt(ts, sf, tpl.template, position);
|
|
1301
|
+
if (!hit) return void 0;
|
|
1302
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1303
|
+
const symbol = props?.find((p3) => p3.getName() === hit.name);
|
|
1304
|
+
if (!symbol) return void 0;
|
|
1305
|
+
const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
1306
|
+
if (!decl) return void 0;
|
|
1307
|
+
const checker = program.getTypeChecker();
|
|
1308
|
+
const typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, decl));
|
|
1309
|
+
return {
|
|
1310
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1311
|
+
kindModifiers: "",
|
|
1312
|
+
textSpan: { start: hit.start, length: hit.name.length },
|
|
1313
|
+
displayParts: [
|
|
1314
|
+
{ text: "(", kind: "punctuation" },
|
|
1315
|
+
{ text: "attribute", kind: "text" },
|
|
1316
|
+
{ text: ")", kind: "punctuation" },
|
|
1317
|
+
{ text: " ", kind: "space" },
|
|
1318
|
+
{ text: `${hit.tag}.`, kind: "text" },
|
|
1319
|
+
{ text: hit.name, kind: "propertyName" },
|
|
1320
|
+
{ text: ":", kind: "punctuation" },
|
|
1321
|
+
{ text: " ", kind: "space" },
|
|
1322
|
+
{ text: typeText, kind: "text" }
|
|
1323
|
+
],
|
|
1324
|
+
documentation: symbol.getDocumentationComment(checker)
|
|
1044
1325
|
};
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/diagnostics.ts
|
|
1329
|
+
var ACCESSOR_NOT_CALLED = 9001;
|
|
1330
|
+
function acceptsAnyProperty(ts, checker, type) {
|
|
1331
|
+
const open = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter | ts.TypeFlags.Never;
|
|
1332
|
+
if (type.flags & open) return true;
|
|
1333
|
+
if (checker.getIndexInfoOfType?.(type, ts.IndexKind.String)) return true;
|
|
1334
|
+
if (type.isUnion()) return type.types.some((t) => acceptsAnyProperty(ts, checker, t));
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
function accessorDiagnostics(ts, program, sf) {
|
|
1338
|
+
const checker = program.getTypeChecker();
|
|
1339
|
+
const out = [];
|
|
1340
|
+
for (const template of templatesIn(ts, sf)) {
|
|
1341
|
+
const tpl = template.template;
|
|
1342
|
+
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
1343
|
+
const holes = tpl.templateSpans.map((s2) => s2.expression);
|
|
1344
|
+
const visit = (nodes, parent) => {
|
|
1345
|
+
for (const node of nodes) {
|
|
1346
|
+
if (node.kind === "Expression" && parent?.kind === "Element") {
|
|
1347
|
+
const expression = holes[node.hole];
|
|
1348
|
+
if (expression && isAccessorRead(ts, expression) && isAccessor(ts, checker, expression)) {
|
|
1349
|
+
const text = expression.getText(sf);
|
|
1350
|
+
out.push({
|
|
1351
|
+
file: sf,
|
|
1352
|
+
start: expression.getStart(sf),
|
|
1353
|
+
length: expression.getWidth(sf),
|
|
1354
|
+
category: ts.DiagnosticCategory.Warning,
|
|
1355
|
+
code: ACCESSOR_NOT_CALLED,
|
|
1356
|
+
messageText: `'${text}' is an accessor \u2014 call it (\`${text}()\`). Left bare it renders the function and never updates.`,
|
|
1357
|
+
source: "fluixi"
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1079
1360
|
}
|
|
1361
|
+
const children = "children" in node ? node.children : void 0;
|
|
1362
|
+
if (children) visit(children, node);
|
|
1080
1363
|
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1364
|
+
};
|
|
1365
|
+
visit(parseCached(ts, sf, tpl).root.children, null);
|
|
1083
1366
|
}
|
|
1367
|
+
return out;
|
|
1084
1368
|
}
|
|
1085
|
-
function
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
putKey(keyStart, keyText);
|
|
1126
|
-
gen += ": ";
|
|
1127
|
-
emitValue();
|
|
1128
|
-
};
|
|
1129
|
-
for (const a of node.attributes) {
|
|
1130
|
-
if (a.kind === "Attribute") {
|
|
1131
|
-
const keyStart = a.boolean ? a.loc.start + 1 : a.loc.start;
|
|
1132
|
-
const v2 = a.value;
|
|
1133
|
-
entry(keyStart, a.name, () => {
|
|
1134
|
-
if (!v2) {
|
|
1135
|
-
gen += "true";
|
|
1136
|
-
} else if (v2.kind === "static") {
|
|
1137
|
-
gen += JSON.stringify(v2.value);
|
|
1138
|
-
} else if (v2.kind === "hole") {
|
|
1139
|
-
putHole(v2.hole);
|
|
1140
|
-
} else {
|
|
1141
|
-
gen += "undefined as any";
|
|
1142
|
-
}
|
|
1369
|
+
function isAccessorRead(ts, node) {
|
|
1370
|
+
return ts.isIdentifier(node) || ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
|
|
1371
|
+
}
|
|
1372
|
+
function isAccessor(ts, checker, node) {
|
|
1373
|
+
const signatures = checker.getTypeAtLocation(node).getCallSignatures();
|
|
1374
|
+
if (signatures.length !== 1) return false;
|
|
1375
|
+
const signature = signatures[0];
|
|
1376
|
+
if (signature.getParameters().some((p3) => !isOptional(ts, p3))) return false;
|
|
1377
|
+
const returned = signature.getReturnType();
|
|
1378
|
+
return !(returned.flags & (ts.TypeFlags.Void | ts.TypeFlags.Never));
|
|
1379
|
+
}
|
|
1380
|
+
function isOptional(ts, symbol) {
|
|
1381
|
+
const declaration = symbol.valueDeclaration;
|
|
1382
|
+
return !!declaration && ts.isParameter(declaration) && (!!declaration.questionToken || !!declaration.initializer || !!declaration.dotDotDotToken);
|
|
1383
|
+
}
|
|
1384
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
1385
|
+
function templateDiagnostics(ts, ls, fileName) {
|
|
1386
|
+
const program = ls.getProgram();
|
|
1387
|
+
const sf = program?.getSourceFile(fileName);
|
|
1388
|
+
if (!program || !sf) return [];
|
|
1389
|
+
const hit = cache.get(sf);
|
|
1390
|
+
if (hit) return hit;
|
|
1391
|
+
const checker = program.getTypeChecker();
|
|
1392
|
+
const out = [];
|
|
1393
|
+
out.push(...accessorDiagnostics(ts, program, sf));
|
|
1394
|
+
for (const { fn, paramName, type } of inferParamsIn(ts, program, sf)) {
|
|
1395
|
+
if (acceptsAnyProperty(ts, checker, type)) continue;
|
|
1396
|
+
const visit = (node) => {
|
|
1397
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === paramName && ts.isIdentifier(node.name)) {
|
|
1398
|
+
const symbol = checker.getSymbolAtLocation(node.expression);
|
|
1399
|
+
const declaredHere = symbol?.declarations?.some((d) => d.parent === fn);
|
|
1400
|
+
if (declaredHere && !checker.getPropertyOfType(type, node.name.text)) {
|
|
1401
|
+
out.push({
|
|
1402
|
+
file: sf,
|
|
1403
|
+
start: node.name.getStart(sf),
|
|
1404
|
+
length: node.name.getWidth(sf),
|
|
1405
|
+
category: ts.DiagnosticCategory.Error,
|
|
1406
|
+
code: 2339,
|
|
1407
|
+
messageText: `Property '${node.name.text}' does not exist on type '${checker.typeToString(type)}'.`,
|
|
1408
|
+
source: "fluixi"
|
|
1143
1409
|
});
|
|
1144
|
-
} else if (a.kind === "PropertyBinding") {
|
|
1145
|
-
entry(a.loc.start + 1, a.name, () => putHole(a.hole));
|
|
1146
1410
|
}
|
|
1147
1411
|
}
|
|
1148
|
-
|
|
1412
|
+
ts.forEachChild(node, visit);
|
|
1149
1413
|
};
|
|
1414
|
+
if (fn.body) visit(fn.body);
|
|
1150
1415
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
if (consumed.has(i)) continue;
|
|
1154
|
-
const role = roles.get(i);
|
|
1155
|
-
if (role && role.kind === "eachRender") {
|
|
1156
|
-
calls.push(() => {
|
|
1157
|
-
gen += "__fxEach(";
|
|
1158
|
-
putHole(role.arrayHole);
|
|
1159
|
-
gen += ", ";
|
|
1160
|
-
putHole(i);
|
|
1161
|
-
gen += ")";
|
|
1162
|
-
});
|
|
1163
|
-
} else if (role && role.kind === "indexRender") {
|
|
1164
|
-
calls.push(() => {
|
|
1165
|
-
gen += "__fxIndex(";
|
|
1166
|
-
putHole(role.arrayHole);
|
|
1167
|
-
gen += ", ";
|
|
1168
|
-
putHole(i);
|
|
1169
|
-
gen += ")";
|
|
1170
|
-
});
|
|
1171
|
-
} else if (role && role.kind === "showRender") {
|
|
1172
|
-
calls.push(() => {
|
|
1173
|
-
gen += "__fxShow(";
|
|
1174
|
-
putHole(role.whenHole);
|
|
1175
|
-
gen += ", ";
|
|
1176
|
-
putHole(i);
|
|
1177
|
-
gen += ")";
|
|
1178
|
-
});
|
|
1179
|
-
} else if (role && role.kind === "event") {
|
|
1180
|
-
const key = EVENT_MAP_KEYS.has(role.event) ? role.event : role.event;
|
|
1181
|
-
calls.push(() => {
|
|
1182
|
-
gen += `__fxOn(${JSON.stringify(key)}, `;
|
|
1183
|
-
putHole(i);
|
|
1184
|
-
gen += ")";
|
|
1185
|
-
});
|
|
1186
|
-
} else if (role && role.kind === "ref" && isFn(ts, tpl.holes[i])) {
|
|
1187
|
-
const tag = role.tag;
|
|
1188
|
-
calls.push(() => {
|
|
1189
|
-
gen += `__fxRef(${JSON.stringify(tag)}, `;
|
|
1190
|
-
putHole(i);
|
|
1191
|
-
gen += ")";
|
|
1192
|
-
});
|
|
1193
|
-
} else {
|
|
1194
|
-
calls.push(() => {
|
|
1195
|
-
gen += "__fxExpr(";
|
|
1196
|
-
putHole(i);
|
|
1197
|
-
gen += ")";
|
|
1198
|
-
});
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
gen += "(";
|
|
1202
|
-
calls.forEach((emit, idx) => {
|
|
1203
|
-
if (idx > 0) gen += ", ";
|
|
1204
|
-
emit();
|
|
1205
|
-
});
|
|
1206
|
-
gen += calls.length > 0 ? ", undefined as any)" : "undefined as any)";
|
|
1207
|
-
return gen;
|
|
1416
|
+
cache.set(sf, out);
|
|
1417
|
+
return out;
|
|
1208
1418
|
}
|
|
1209
1419
|
|
|
1210
|
-
// src/
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1420
|
+
// src/fixes.ts
|
|
1421
|
+
function templateCodeFixes(ts, ls, fileName, start, end, errorCodes) {
|
|
1422
|
+
if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return [];
|
|
1423
|
+
const out = [];
|
|
1424
|
+
for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
|
|
1425
|
+
if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
|
|
1426
|
+
if (diagnostic.start > end || diagnostic.start + (diagnostic.length ?? 0) < start) continue;
|
|
1427
|
+
const at = diagnostic.start + (diagnostic.length ?? 0);
|
|
1428
|
+
const name = diagnostic.file?.text.slice(diagnostic.start, at) ?? "value";
|
|
1429
|
+
out.push({
|
|
1430
|
+
fixName: "fluixiCallAccessor",
|
|
1431
|
+
description: `Call '${name}'`,
|
|
1432
|
+
changes: [
|
|
1433
|
+
{
|
|
1434
|
+
fileName,
|
|
1435
|
+
textChanges: [{ span: { start: at, length: 0 }, newText: "()" }]
|
|
1223
1436
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1437
|
+
],
|
|
1438
|
+
// Every occurrence in the file is the same edit, so offer to do them at once.
|
|
1439
|
+
fixAllDescription: "Call every uncalled accessor in this file"
|
|
1227
1440
|
});
|
|
1228
|
-
this.ls = ts.createLanguageService(virtualHost, ts.createDocumentRegistry());
|
|
1229
|
-
}
|
|
1230
|
-
ls;
|
|
1231
|
-
/** Cached per file: the virtual text + segments, keyed by the file's version. */
|
|
1232
|
-
cache = /* @__PURE__ */ new Map();
|
|
1233
|
-
/** The virtual doc for a file (built + cached per source version), if it has templates. */
|
|
1234
|
-
virtual(fileName) {
|
|
1235
|
-
const version = this.host.getScriptVersion(fileName);
|
|
1236
|
-
const hit = this.cache.get(fileName);
|
|
1237
|
-
if (hit && hit.version === version) return hit.text ? hit : void 0;
|
|
1238
|
-
const snap = this.host.getScriptSnapshot(fileName);
|
|
1239
|
-
if (!snap) return void 0;
|
|
1240
|
-
const src = snap.getText(0, snap.getLength());
|
|
1241
|
-
const sf = this.ts.createSourceFile(fileName, src, this.ts.ScriptTarget.Latest, true, scriptKind(this.ts, fileName));
|
|
1242
|
-
const v2 = buildVirtual(this.ts, sf);
|
|
1243
|
-
this.cache.set(fileName, { version, text: v2?.text ?? "", segments: v2?.segments ?? [] });
|
|
1244
|
-
return v2;
|
|
1245
|
-
}
|
|
1246
|
-
segmentsFor(fileName) {
|
|
1247
|
-
return this.virtual(fileName)?.segments;
|
|
1248
|
-
}
|
|
1249
|
-
service() {
|
|
1250
|
-
return this.ls;
|
|
1251
|
-
}
|
|
1252
|
-
toGen(fileName, pos) {
|
|
1253
|
-
const segs = this.virtual(fileName)?.segments;
|
|
1254
|
-
return segs ? toGenerated(segs, pos) : -1;
|
|
1255
|
-
}
|
|
1256
|
-
toSrc(fileName, pos) {
|
|
1257
|
-
const segs = this.virtual(fileName)?.segments;
|
|
1258
|
-
return segs ? toSource(segs, pos) : -1;
|
|
1259
|
-
}
|
|
1260
|
-
/** True when a SOURCE position lies inside an embedded `${…}` hole. */
|
|
1261
|
-
inHole(fileName, pos) {
|
|
1262
|
-
const segs = this.virtual(fileName)?.segments;
|
|
1263
|
-
return segs ? inHole(segs, pos) : false;
|
|
1264
|
-
}
|
|
1265
|
-
/** True when a SOURCE position lies on a mapped component prop-name key. */
|
|
1266
|
-
inKey(fileName, pos) {
|
|
1267
|
-
const segs = this.virtual(fileName)?.segments;
|
|
1268
|
-
return segs ? inKey(segs, pos) : false;
|
|
1269
1441
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (
|
|
1274
|
-
|
|
1275
|
-
|
|
1442
|
+
return out;
|
|
1443
|
+
}
|
|
1444
|
+
function templateFixAll(ts, ls, fileName, errorCodes) {
|
|
1445
|
+
if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return void 0;
|
|
1446
|
+
const textChanges = [];
|
|
1447
|
+
for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
|
|
1448
|
+
if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
|
|
1449
|
+
const at = diagnostic.start + (diagnostic.length ?? 0);
|
|
1450
|
+
textChanges.push({ span: { start: at, length: 0 }, newText: "()" });
|
|
1451
|
+
}
|
|
1452
|
+
if (textChanges.length === 0) return void 0;
|
|
1453
|
+
textChanges.sort((a2, b2) => b2.span.start - a2.span.start);
|
|
1454
|
+
return { changes: [{ fileName, textChanges }], commands: void 0 };
|
|
1276
1455
|
}
|
|
1277
1456
|
|
|
1278
|
-
// src/
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
const
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1457
|
+
// src/navigation.ts
|
|
1458
|
+
var spansByFile = /* @__PURE__ */ new WeakMap();
|
|
1459
|
+
function tagSpans(ts, sf) {
|
|
1460
|
+
const hit = spansByFile.get(sf);
|
|
1461
|
+
if (hit) return hit;
|
|
1462
|
+
const out = [];
|
|
1463
|
+
for (const template of templatesIn(ts, sf)) {
|
|
1464
|
+
const { root } = parseCached(ts, sf, template.template);
|
|
1465
|
+
const walk = (nodes) => {
|
|
1466
|
+
for (const node of nodes) {
|
|
1467
|
+
if (node.kind === "Component" && node.tag) {
|
|
1468
|
+
const component = node;
|
|
1469
|
+
const name = component.tag.split(".")[0];
|
|
1470
|
+
out.push({ name, start: component.loc.start + 1, length: name.length });
|
|
1471
|
+
const closing = closingTagName(sf.text, component.loc.end, component.tag);
|
|
1472
|
+
if (closing !== void 0) out.push({ name, start: closing, length: name.length });
|
|
1473
|
+
}
|
|
1474
|
+
if ("children" in node && node.children) walk(node.children);
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
walk(root.children);
|
|
1478
|
+
}
|
|
1479
|
+
spansByFile.set(sf, out);
|
|
1480
|
+
return out;
|
|
1481
|
+
}
|
|
1482
|
+
function closingTagName(text, end, tag) {
|
|
1483
|
+
const open = text.lastIndexOf("</", end);
|
|
1484
|
+
if (open === -1) return void 0;
|
|
1485
|
+
const close = text.indexOf(">", open);
|
|
1486
|
+
if (close === -1 || close >= end) return void 0;
|
|
1487
|
+
const index = text.indexOf(tag, open);
|
|
1488
|
+
return index !== -1 && index < close ? index : void 0;
|
|
1489
|
+
}
|
|
1490
|
+
function tagAt(ts, sf, position) {
|
|
1491
|
+
if (!templateAtCached(ts, sf, position)) return void 0;
|
|
1492
|
+
return tagSpans(ts, sf).find((s2) => position >= s2.start && position <= s2.start + s2.length);
|
|
1493
|
+
}
|
|
1494
|
+
function symbolFor(ts, program, host, location, name) {
|
|
1495
|
+
const checker = program.getTypeChecker();
|
|
1496
|
+
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
1497
|
+
const symbol = checker.getSymbolsInScope(location, flags).find((s2) => s2.name === name);
|
|
1498
|
+
if (!symbol) return autoImportedSymbol(ts, program, host, location.getSourceFile(), name);
|
|
1499
|
+
return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
1500
|
+
}
|
|
1501
|
+
function declarationFor(ts, program, host, sf, tag) {
|
|
1502
|
+
const template = templateAtCached(ts, sf, tag.start);
|
|
1503
|
+
if (!template) return void 0;
|
|
1504
|
+
const symbol = symbolFor(ts, program, host, template, tag.name);
|
|
1505
|
+
return symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
1506
|
+
}
|
|
1507
|
+
function templateDefinition(ts, ls, host, fileName, position) {
|
|
1508
|
+
const program = ls.getProgram();
|
|
1509
|
+
const sf = program?.getSourceFile(fileName);
|
|
1510
|
+
if (!program || !sf) return void 0;
|
|
1511
|
+
const tag = tagAt(ts, sf, position);
|
|
1512
|
+
if (!tag) return void 0;
|
|
1513
|
+
const decl = declarationFor(ts, program, host, sf, tag);
|
|
1514
|
+
if (!decl) {
|
|
1515
|
+
const site = declarationSite(ts, program, host, sf, tag.name);
|
|
1516
|
+
if (!site) return void 0;
|
|
1517
|
+
return [
|
|
1518
|
+
{
|
|
1519
|
+
fileName: site.fileName,
|
|
1520
|
+
textSpan: { start: site.start, length: site.length },
|
|
1521
|
+
kind: ts.ScriptElementKind.unknown,
|
|
1522
|
+
name: tag.name,
|
|
1523
|
+
containerKind: ts.ScriptElementKind.unknown,
|
|
1524
|
+
containerName: ""
|
|
1525
|
+
}
|
|
1526
|
+
];
|
|
1527
|
+
}
|
|
1528
|
+
const target = decl.name ?? decl;
|
|
1529
|
+
const declFile = decl.getSourceFile();
|
|
1530
|
+
return [
|
|
1531
|
+
{
|
|
1532
|
+
fileName: declFile.fileName,
|
|
1533
|
+
textSpan: { start: target.getStart(declFile), length: target.getWidth(declFile) },
|
|
1534
|
+
kind: ts.ScriptElementKind.unknown,
|
|
1535
|
+
name: tag.name,
|
|
1536
|
+
containerKind: ts.ScriptElementKind.unknown,
|
|
1537
|
+
containerName: ""
|
|
1538
|
+
}
|
|
1539
|
+
];
|
|
1540
|
+
}
|
|
1541
|
+
function tagOccurrences(ts, program, host, symbol, name) {
|
|
1542
|
+
const out = [];
|
|
1543
|
+
for (const sf of program.getSourceFiles()) {
|
|
1544
|
+
if (sf.isDeclarationFile) continue;
|
|
1545
|
+
const text = sf.text;
|
|
1546
|
+
if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) continue;
|
|
1547
|
+
if (text.indexOf(name) === -1) continue;
|
|
1548
|
+
for (const span of tagSpans(ts, sf)) {
|
|
1549
|
+
if (span.name !== name) continue;
|
|
1550
|
+
const template = templateAtCached(ts, sf, span.start);
|
|
1551
|
+
if (!template || symbolFor(ts, program, host, template, name) !== symbol) continue;
|
|
1552
|
+
out.push({ fileName: sf.fileName, span: { start: span.start, length: span.length } });
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
return out;
|
|
1556
|
+
}
|
|
1557
|
+
function symbolAt(ts, program, host, sf, position) {
|
|
1558
|
+
const checker = program.getTypeChecker();
|
|
1559
|
+
const tag = tagAt(ts, sf, position);
|
|
1560
|
+
if (tag) {
|
|
1561
|
+
const template = templateAtCached(ts, sf, tag.start);
|
|
1562
|
+
const symbol2 = template && symbolFor(ts, program, host, template, tag.name);
|
|
1563
|
+
return symbol2 ? { symbol: symbol2, name: tag.name } : void 0;
|
|
1564
|
+
}
|
|
1565
|
+
const token = tokenAt(ts, sf, position);
|
|
1566
|
+
if (!token || !ts.isIdentifier(token)) return void 0;
|
|
1567
|
+
let symbol = checker.getSymbolAtLocation(token);
|
|
1568
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
|
|
1569
|
+
return symbol ? { symbol, name: token.text } : void 0;
|
|
1570
|
+
}
|
|
1571
|
+
function tokenAt(ts, sf, position) {
|
|
1572
|
+
let found;
|
|
1573
|
+
const visit = (node) => {
|
|
1574
|
+
if (position < node.getStart(sf) || position > node.getEnd()) return;
|
|
1575
|
+
if (node.getChildCount(sf) === 0) found = node;
|
|
1576
|
+
ts.forEachChild(node, visit);
|
|
1577
|
+
};
|
|
1578
|
+
visit(sf);
|
|
1579
|
+
return found;
|
|
1580
|
+
}
|
|
1581
|
+
function templateReferences(ts, ls, host, fileName, position) {
|
|
1582
|
+
const program = ls.getProgram();
|
|
1583
|
+
const sf = program?.getSourceFile(fileName);
|
|
1584
|
+
if (!program || !sf) return [];
|
|
1585
|
+
const hit = symbolAt(ts, program, host, sf, position);
|
|
1586
|
+
if (!hit) return [];
|
|
1587
|
+
return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
|
|
1588
|
+
fileName: f2,
|
|
1589
|
+
textSpan: span,
|
|
1590
|
+
isWriteAccess: false,
|
|
1591
|
+
isDefinition: false
|
|
1592
|
+
}));
|
|
1593
|
+
}
|
|
1594
|
+
function templateRenameLocations(ts, ls, host, fileName, position) {
|
|
1595
|
+
const program = ls.getProgram();
|
|
1596
|
+
const sf = program?.getSourceFile(fileName);
|
|
1597
|
+
if (!program || !sf) return [];
|
|
1598
|
+
const hit = symbolAt(ts, program, host, sf, position);
|
|
1599
|
+
if (!hit) return [];
|
|
1600
|
+
return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
|
|
1601
|
+
fileName: f2,
|
|
1602
|
+
textSpan: span
|
|
1603
|
+
}));
|
|
1284
1604
|
}
|
|
1285
1605
|
|
|
1286
1606
|
// src/index.ts
|
|
@@ -1289,12 +1609,7 @@ function init(modules) {
|
|
|
1289
1609
|
return {
|
|
1290
1610
|
create(info) {
|
|
1291
1611
|
const ls = info.languageService;
|
|
1292
|
-
|
|
1293
|
-
try {
|
|
1294
|
-
vs = new VirtualService(ts, info.languageServiceHost);
|
|
1295
|
-
} catch {
|
|
1296
|
-
vs = void 0;
|
|
1297
|
-
}
|
|
1612
|
+
const host = info.languageServiceHost;
|
|
1298
1613
|
const proxy = /* @__PURE__ */ Object.create(null);
|
|
1299
1614
|
for (const key of Object.keys(ls)) {
|
|
1300
1615
|
const member = ls[key];
|
|
@@ -1304,9 +1619,24 @@ function init(modules) {
|
|
|
1304
1619
|
isGlobalCompletion: false,
|
|
1305
1620
|
isMemberCompletion: false,
|
|
1306
1621
|
isNewIdentifierLocation: true,
|
|
1622
|
+
// Re-ask on every keystroke instead of letting the editor filter what it already
|
|
1623
|
+
// has. Inside a template the valid set changes with the cursor — a tag name, then
|
|
1624
|
+
// that tag's props — and a list cached from one position is wrong at the next.
|
|
1625
|
+
// Without this the props appear only after enough typing to force a fresh
|
|
1626
|
+
// request, which reads as completion working intermittently.
|
|
1627
|
+
isIncomplete: true,
|
|
1307
1628
|
entries
|
|
1308
1629
|
});
|
|
1309
1630
|
const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
|
|
1631
|
+
const dedupe = (items) => {
|
|
1632
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1633
|
+
return items.filter((i2) => {
|
|
1634
|
+
const key = `${i2.fileName}:${i2.textSpan.start}:${i2.textSpan.length}`;
|
|
1635
|
+
if (seen.has(key)) return false;
|
|
1636
|
+
seen.add(key);
|
|
1637
|
+
return true;
|
|
1638
|
+
});
|
|
1639
|
+
};
|
|
1310
1640
|
const reliableFilter = (fileName, diagnostics) => {
|
|
1311
1641
|
const program = ls.getProgram();
|
|
1312
1642
|
const sourceFile = program?.getSourceFile(fileName);
|
|
@@ -1318,47 +1648,49 @@ function init(modules) {
|
|
|
1318
1648
|
return true;
|
|
1319
1649
|
});
|
|
1320
1650
|
};
|
|
1321
|
-
|
|
1322
|
-
|
|
1651
|
+
proxy.getSemanticDiagnostics = (fileName) => {
|
|
1652
|
+
const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
|
|
1323
1653
|
try {
|
|
1324
|
-
|
|
1325
|
-
const out = [];
|
|
1326
|
-
for (const d of get(fileName)) {
|
|
1327
|
-
if (d.start === void 0) continue;
|
|
1328
|
-
const m2 = mapDiagnostic(vs, fileName, d);
|
|
1329
|
-
if (m2 && m2.start !== void 0 && (vs.inHole(fileName, m2.start) || vs.inKey(fileName, m2.start))) {
|
|
1330
|
-
out.push({ ...m2, file: sourceFile });
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
return out;
|
|
1654
|
+
return base.concat(templateDiagnostics(ts, ls, fileName));
|
|
1334
1655
|
} catch {
|
|
1335
|
-
return
|
|
1656
|
+
return base;
|
|
1336
1657
|
}
|
|
1337
1658
|
};
|
|
1338
|
-
proxy.getSemanticDiagnostics = (fileName) => {
|
|
1339
|
-
const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
|
|
1340
|
-
return base.concat(holeDiagnostics(fileName, (f) => vs.service().getSemanticDiagnostics(f)));
|
|
1341
|
-
};
|
|
1342
1659
|
proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
|
|
1343
1660
|
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
1344
1661
|
try {
|
|
1345
|
-
const prop = componentPropHover(ts, ls, fileName, position);
|
|
1662
|
+
const prop = componentPropHover(ts, ls, host, fileName, position);
|
|
1346
1663
|
if (prop) return prop;
|
|
1347
1664
|
} catch {
|
|
1348
1665
|
}
|
|
1666
|
+
try {
|
|
1667
|
+
const attr = elementAttributeHover(ts, ls, host, fileName, position);
|
|
1668
|
+
if (attr) return attr;
|
|
1669
|
+
} catch {
|
|
1670
|
+
}
|
|
1349
1671
|
try {
|
|
1350
1672
|
const inferred = paramQuickInfo(ts, ls, fileName, position);
|
|
1351
1673
|
if (inferred) return inferred;
|
|
1352
1674
|
} catch {
|
|
1353
1675
|
}
|
|
1354
|
-
|
|
1676
|
+
try {
|
|
1677
|
+
const tag = componentQuickInfo(ts, ls, host, fileName, position);
|
|
1678
|
+
if (tag) return tag;
|
|
1679
|
+
} catch {
|
|
1680
|
+
}
|
|
1681
|
+
return ls.getQuickInfoAtPosition(fileName, position);
|
|
1355
1682
|
};
|
|
1356
1683
|
proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
|
|
1357
1684
|
try {
|
|
1358
|
-
const props = componentPropCompletions(ts, ls, fileName, position);
|
|
1685
|
+
const props = componentPropCompletions(ts, ls, host, fileName, position);
|
|
1359
1686
|
if (props) return completionList(props);
|
|
1360
1687
|
} catch {
|
|
1361
1688
|
}
|
|
1689
|
+
try {
|
|
1690
|
+
const attrs = elementAttributeCompletions(ts, ls, host, fileName, position);
|
|
1691
|
+
if (attrs) return completionList(attrs);
|
|
1692
|
+
} catch {
|
|
1693
|
+
}
|
|
1362
1694
|
try {
|
|
1363
1695
|
const members = paramMemberCompletions(ts, ls, fileName, position);
|
|
1364
1696
|
if (members) return completionList(members);
|
|
@@ -1366,6 +1698,71 @@ function init(modules) {
|
|
|
1366
1698
|
}
|
|
1367
1699
|
return ls.getCompletionsAtPosition(fileName, position, options, formatting);
|
|
1368
1700
|
};
|
|
1701
|
+
proxy.getDefinitionAtPosition = (fileName, position) => {
|
|
1702
|
+
try {
|
|
1703
|
+
const tag = templateDefinition(ts, ls, host, fileName, position);
|
|
1704
|
+
if (tag) return tag;
|
|
1705
|
+
} catch {
|
|
1706
|
+
}
|
|
1707
|
+
return ls.getDefinitionAtPosition(fileName, position);
|
|
1708
|
+
};
|
|
1709
|
+
proxy.getDefinitionAndBoundSpan = (fileName, position) => {
|
|
1710
|
+
try {
|
|
1711
|
+
const definitions = templateDefinition(ts, ls, host, fileName, position);
|
|
1712
|
+
if (definitions) {
|
|
1713
|
+
const sf = ls.getProgram()?.getSourceFile(fileName);
|
|
1714
|
+
const tag = sf && tagAt(ts, sf, position);
|
|
1715
|
+
if (tag) {
|
|
1716
|
+
return { definitions, textSpan: { start: tag.start, length: tag.length } };
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
} catch {
|
|
1720
|
+
}
|
|
1721
|
+
return ls.getDefinitionAndBoundSpan(fileName, position);
|
|
1722
|
+
};
|
|
1723
|
+
proxy.getReferencesAtPosition = (fileName, position) => {
|
|
1724
|
+
const base = ls.getReferencesAtPosition(fileName, position) ?? [];
|
|
1725
|
+
try {
|
|
1726
|
+
const extra = templateReferences(ts, ls, host, fileName, position);
|
|
1727
|
+
return extra.length ? dedupe([...base, ...extra]) : base;
|
|
1728
|
+
} catch {
|
|
1729
|
+
return base;
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => {
|
|
1733
|
+
const base = ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs) ?? [];
|
|
1734
|
+
try {
|
|
1735
|
+
const extra = templateRenameLocations(ts, ls, host, fileName, position);
|
|
1736
|
+
if (!base.length || !extra.length) return base.length ? base : void 0;
|
|
1737
|
+
return dedupe([...base, ...extra]);
|
|
1738
|
+
} catch {
|
|
1739
|
+
return base;
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
|
|
1743
|
+
const base = ls.getCodeFixesAtPosition(
|
|
1744
|
+
fileName,
|
|
1745
|
+
start,
|
|
1746
|
+
end,
|
|
1747
|
+
errorCodes,
|
|
1748
|
+
formatOptions,
|
|
1749
|
+
preferences
|
|
1750
|
+
);
|
|
1751
|
+
try {
|
|
1752
|
+
const own = templateCodeFixes(ts, ls, fileName, start, end, errorCodes);
|
|
1753
|
+
return own.length ? [...base, ...own] : base;
|
|
1754
|
+
} catch {
|
|
1755
|
+
return base;
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
proxy.getCombinedCodeFix = (scope, fixId, formatOptions, preferences) => {
|
|
1759
|
+
if (fixId === "fluixiCallAccessor") {
|
|
1760
|
+
const fileName = scope.fileName;
|
|
1761
|
+
const combined = templateFixAll(ts, ls, fileName, [ACCESSOR_NOT_CALLED]);
|
|
1762
|
+
if (combined) return combined;
|
|
1763
|
+
}
|
|
1764
|
+
return ls.getCombinedCodeFix(scope, fixId, formatOptions, preferences);
|
|
1765
|
+
};
|
|
1369
1766
|
return proxy;
|
|
1370
1767
|
}
|
|
1371
1768
|
};
|