@fluixi/ts-plugin 0.1.0-alpha.1 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +928 -551
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
// ../template-parser/dist/index.mjs
|
|
4
|
-
function
|
|
5
|
-
let
|
|
6
|
-
i.parent =
|
|
7
|
-
let
|
|
8
|
-
for (let s of
|
|
4
|
+
function x(e) {
|
|
5
|
+
let t = (i, n) => {
|
|
6
|
+
i.parent = n;
|
|
7
|
+
let r = B(i);
|
|
8
|
+
for (let s of r) t(s, i);
|
|
9
9
|
};
|
|
10
|
-
return e
|
|
10
|
+
return t(e, null), e;
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
switch (
|
|
12
|
+
function B(e) {
|
|
13
|
+
switch (e.kind) {
|
|
14
14
|
case "Root":
|
|
15
15
|
case "Element":
|
|
16
16
|
case "Component":
|
|
17
17
|
case "Fragment":
|
|
18
|
-
return
|
|
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
|
}
|
|
@@ -45,129 +45,169 @@ var u = class {
|
|
|
45
45
|
let i = this.pieces[this.pieces.length - 1];
|
|
46
46
|
return i ? i.kind === "static" ? i.start + i.text.length : i.end : 0;
|
|
47
47
|
}
|
|
48
|
-
let
|
|
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 (
|
|
56
|
+
let t = this.pieces[this.pi];
|
|
57
|
+
if (t.kind !== "static" || this.oi + 1 < t.text.length) return false;
|
|
58
58
|
let i = this.pieces[this.pi + 1];
|
|
59
59
|
return i !== void 0 && i.kind === "hole";
|
|
60
60
|
}
|
|
61
61
|
takeHole() {
|
|
62
|
-
let
|
|
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
68
|
let i = this.pieces[this.pi];
|
|
69
|
-
return i.kind !== "static" ? "" : i.text[this.oi +
|
|
69
|
+
return i.kind !== "static" ? "" : i.text[this.oi + t] ?? "";
|
|
70
70
|
}
|
|
71
|
-
startsWith(
|
|
71
|
+
startsWith(t) {
|
|
72
72
|
if (this.eof()) return false;
|
|
73
73
|
let i = this.pieces[this.pi];
|
|
74
|
-
return i.kind !== "static" ? false : i.text.startsWith(
|
|
74
|
+
return i.kind !== "static" ? false : i.text.startsWith(t, this.oi);
|
|
75
75
|
}
|
|
76
76
|
advance() {
|
|
77
77
|
if (this.eof()) return "";
|
|
78
|
-
let
|
|
79
|
-
if (
|
|
80
|
-
let i =
|
|
78
|
+
let t = this.pieces[this.pi];
|
|
79
|
+
if (t.kind !== "static") return "";
|
|
80
|
+
let i = t.text[this.oi++] ?? "";
|
|
81
81
|
return this.normalize(), i;
|
|
82
82
|
}
|
|
83
|
-
readWhile(
|
|
83
|
+
readWhile(t) {
|
|
84
84
|
let i = "";
|
|
85
85
|
for (; !this.eof() && !this.atHole(); ) {
|
|
86
|
-
let
|
|
87
|
-
if (
|
|
88
|
-
i +=
|
|
86
|
+
let n = this.peek();
|
|
87
|
+
if (n === "" || !t(n)) break;
|
|
88
|
+
i += n, this.oi++;
|
|
89
89
|
}
|
|
90
90
|
return this.normalize(), i;
|
|
91
91
|
}
|
|
92
92
|
skipWhitespace() {
|
|
93
|
-
this.readWhile((
|
|
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 E = (e, t) => ({ kind: "Attribute", name: t ? e.name.slice(1) : e.name, value: e.value, boolean: t, loc: e.loc });
|
|
104
|
+
var f = [{ id: "event", match: (e) => e[0] === "@" || e.startsWith("on:") || /^on[A-Z]/.test(e), build: (e) => {
|
|
105
|
+
let t, i;
|
|
106
|
+
e.name[0] === "@" ? (t = "at", i = e.name.slice(1)) : e.name.startsWith("on:") ? (t = "colon", i = e.name.slice(3)) : (t = "on", i = e.name.slice(2));
|
|
107
|
+
let [n, ...r] = i.split("."), s = n.toLowerCase();
|
|
108
|
+
for (let o of r) y.has(o) || e.report("invalid-directive", `Unknown event modifier ".${o}" \u2014 expected one of ${[...y].join(", ")}`);
|
|
109
|
+
return { kind: "EventBinding", name: s, syntax: t, modifiers: r, hole: p(e, "event binding"), loc: e.loc };
|
|
110
|
+
} }, { id: "bind", elementOnly: true, match: (e) => e.startsWith("bind:"), build: (e) => ({ kind: "BindDirective", name: e.name.slice(5), hole: p(e, "two-way binding"), loc: e.loc }) }, { id: "class-directive", elementOnly: true, match: (e) => e.startsWith("class:"), build: (e) => ({ kind: "ClassDirective", name: e.name.slice(6), hole: p(e, "class directive"), loc: e.loc }) }, { id: "style-directive", elementOnly: true, match: (e) => e.startsWith("style:"), build: (e) => ({ kind: "StyleDirective", name: e.name.slice(6), hole: p(e, "style directive"), loc: e.loc }) }, { id: "use", match: (e) => e === "use" || e.startsWith("use:"), build: (e) => ({ kind: "UseDirective", name: e.name.startsWith("use:") ? e.name.slice(4) : null, hole: e.value && e.value.kind === "hole" ? e.value.hole : null, loc: e.loc }) }, { id: "if", elementOnly: true, match: (e) => e === "if", build: (e) => ({ kind: "IfDirective", hole: p(e, "if directive"), loc: e.loc }) }, { id: "each", elementOnly: true, match: (e) => e === "each", build: (e) => ({ kind: "EachDirective", hole: p(e, "each directive"), key: null, loc: e.loc }) }, { id: "else", elementOnly: true, match: (e) => e === "else", build: (e) => (e.value !== null && e.report("invalid-directive", "`else` takes no value"), { kind: "ElseDirective", loc: e.loc }) }, { id: "ref", match: (e) => e === "ref", build: (e) => ({ kind: "RefBinding", hole: p(e, "ref binding"), loc: e.loc }) }, { id: "property", match: (e) => e[0] === ".", build: (e) => ({ kind: "PropertyBinding", name: e.name.slice(1), hole: p(e, "property binding"), loc: e.loc }) }, { id: "boolean", match: (e) => e[0] === "?", build: (e) => E(e, true) }];
|
|
111
|
+
function k(e, t = f, i = { component: false }) {
|
|
112
|
+
for (let n of t) if (!(n.elementOnly && i.component) && n.match(e.name)) return n.build(e);
|
|
113
|
+
return E(e, false);
|
|
114
|
+
}
|
|
115
|
+
var D = /* @__PURE__ */ new Set(["svg", "path", "circle", "rect", "line", "polygon", "polyline", "ellipse", "g", "defs", "clipPath", "text"]);
|
|
116
|
+
var S = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
|
|
117
|
+
var T = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
118
|
+
function C(e) {
|
|
119
|
+
return e.length > 0 && (e[0] !== e[0].toLowerCase() || e.includes("."));
|
|
120
|
+
}
|
|
121
|
+
var P = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea", "summary", "details", "option", "label"]);
|
|
122
|
+
var W = /* @__PURE__ */ new Set(["keydown", "keyup", "keypress"]);
|
|
123
|
+
function u(e, t) {
|
|
124
|
+
return e.attributes.find((i) => "name" in i && i.name === t);
|
|
125
|
+
}
|
|
126
|
+
function w(e) {
|
|
127
|
+
return e.attributes.some((t) => t.kind === "Spread");
|
|
128
|
+
}
|
|
129
|
+
function v(e, t) {
|
|
130
|
+
let i = u(e, t);
|
|
131
|
+
if (!i || i.kind !== "Attribute" || !i.value) return;
|
|
132
|
+
let n = i.value;
|
|
133
|
+
return n.kind === "static" ? n.value : void 0;
|
|
134
|
+
}
|
|
135
|
+
function H(e) {
|
|
136
|
+
let t = v(e, "role");
|
|
137
|
+
return t === "presentation" || t === "none" ? true : v(e, "aria-hidden") === "true";
|
|
138
|
+
}
|
|
139
|
+
function A(e, t) {
|
|
140
|
+
return e.attributes.some((i) => i.kind === "EventBinding" && t(i.name));
|
|
141
|
+
}
|
|
142
|
+
function R(e) {
|
|
143
|
+
let t = [], i = (r, s, o) => {
|
|
144
|
+
t.push({ code: s, message: o, severity: "warning", loc: r.loc });
|
|
145
|
+
}, n = (r) => {
|
|
146
|
+
for (let s of r) {
|
|
147
|
+
s.kind === "Element" && V(s, i);
|
|
148
|
+
let o = "children" in s ? s.children : void 0;
|
|
149
|
+
o && n(o);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return n(e.children), t;
|
|
153
|
+
}
|
|
154
|
+
function V(e, t) {
|
|
155
|
+
if (w(e)) return;
|
|
156
|
+
let i = e.tag.toLowerCase();
|
|
157
|
+
i === "img" && !u(e, "alt") && !H(e) && t(e, "a11y-img-alt", 'An `img` needs an `alt`. Use `alt=""` when the image is decorative.'), i === "a" && !u(e, "href") && !u(e, "role") && t(e, "a11y-anchor-href", "An `a` without `href` is not a link. Use a `button` for an action."), i === "iframe" && !u(e, "title") && t(e, "a11y-iframe-title", "An `iframe` needs a `title` describing its content.");
|
|
158
|
+
let n = v(e, "tabindex") ?? v(e, "tabIndex");
|
|
159
|
+
n !== void 0 && Number(n) > 0 && t(e, "a11y-positive-tabindex", "A positive `tabindex` reorders tab navigation for the entire page. Use `0`, or restructure the markup."), !P.has(i) && A(e, (r) => r === "click") && !u(e, "role") && !A(e, (r) => W.has(r)) && t(e, "a11y-click-without-keyboard", `A click handler on \`${i}\` is unreachable by keyboard. Use a \`button\`, or add a \`role\` and a key handler.`);
|
|
160
|
+
}
|
|
161
|
+
var I = /[A-Za-z0-9\-_:@.?]/;
|
|
162
|
+
var b = class {
|
|
163
|
+
constructor(t, i) {
|
|
124
164
|
this.diagnostics = [];
|
|
125
|
-
this.r = new
|
|
165
|
+
this.r = new m(t), this.svg = i.svg ?? false, this.directives = i.directives ?? f, this.accessibility = i.accessibility ?? true;
|
|
126
166
|
}
|
|
127
167
|
parse() {
|
|
128
|
-
let
|
|
168
|
+
let t = this.r.pos(), i = [];
|
|
129
169
|
for (; !this.r.eof() && (i.push(...this.parseChildren()), !this.r.eof()); ) {
|
|
130
|
-
let
|
|
131
|
-
this.consumeCloseTag(), this.report("stray-close-tag", "error", "Close tag without a matching open tag", this.r.span(
|
|
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: i, loc: this.r.span(t) };
|
|
174
|
+
return x(n), this.validateTree(n), this.accessibility && this.diagnostics.push(...R(n)), { root: n, diagnostics: this.diagnostics };
|
|
135
175
|
}
|
|
136
|
-
validateTree(
|
|
137
|
-
let i = (
|
|
138
|
-
let
|
|
139
|
-
for (let s of
|
|
176
|
+
validateTree(t) {
|
|
177
|
+
let i = (n) => {
|
|
178
|
+
let r = null;
|
|
179
|
+
for (let s of n) s.kind === "Text" && /^\s*$/.test(s.value) || (s.kind === "Element" || s.kind === "Component" ? (s.attributes.some((l) => l.kind === "ElseDirective") && (r != null && (r.kind === "Element" || r.kind === "Component") && r.attributes.some((a) => a.kind === "IfDirective") || this.report("else-without-if", "error", "`else` must immediately follow an element with `if`", s.loc)), r = s) : r = null, "children" in s && s.children && i(s.children));
|
|
140
180
|
};
|
|
141
|
-
i(
|
|
181
|
+
i(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
207
|
let i = this.readText();
|
|
168
|
-
i &&
|
|
208
|
+
i && t.push(i);
|
|
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,20 +219,20 @@ 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 i = this.r.takeHole().index,
|
|
224
|
+
let i = 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, s = [];
|
|
227
|
+
return this.r.startsWith("/>") ? (this.r.advance(), this.r.advance(), r = true) : (this.r.peek() === ">" && this.r.advance(), s = this.parseChildren(), this.r.startsWith("</") ? (this.r.advance(), this.r.advance(), this.r.atHole() ? this.r.takeHole() : this.readName(), this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance()) : this.report("unclosed-tag", "error", "Unclosed dynamic component", this.r.span(t))), { kind: "Component", tag: "", tagHole: i, attributes: n, children: s, selfClosing: r, loc: this.r.span(t) };
|
|
188
228
|
}
|
|
189
229
|
readText() {
|
|
190
|
-
let
|
|
230
|
+
let t = this.r.pos(), i = "";
|
|
191
231
|
for (; !this.r.eof() && !this.r.atHole() && !(this.r.startsWith("</") || this.r.startsWith("<!--") || this.isTagStart() || this.isFragmentStart() || this.isDynamicTagStart()); ) i += this.r.advance();
|
|
192
|
-
return i ? { kind: "Text", value: i, raw: false, loc: this.r.span(
|
|
232
|
+
return i ? { kind: "Text", value: i, 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
237
|
let i = "";
|
|
198
238
|
for (; !this.r.eof() && !this.r.startsWith("-->"); ) {
|
|
@@ -202,148 +242,148 @@ var g = class {
|
|
|
202
242
|
}
|
|
203
243
|
i += this.r.advance();
|
|
204
244
|
}
|
|
205
|
-
return this.r.startsWith("-->") ? (this.r.advance(), this.r.advance(), this.r.advance()) : this.report("unclosed-comment", "warning", "Unterminated comment", this.r.span(
|
|
245
|
+
return this.r.startsWith("-->") ? (this.r.advance(), this.r.advance(), this.r.advance()) : this.report("unclosed-comment", "warning", "Unterminated comment", this.r.span(t)), { kind: "Comment", value: i, loc: this.r.span(t) };
|
|
206
246
|
}
|
|
207
247
|
parseFragment() {
|
|
208
|
-
let
|
|
248
|
+
let t = this.r.pos();
|
|
209
249
|
this.r.advance(), this.r.advance();
|
|
210
250
|
let i = this.parseChildren();
|
|
211
251
|
if (this.r.startsWith("</")) {
|
|
212
|
-
let
|
|
213
|
-
|
|
214
|
-
} else this.report("unclosed-tag", "error", "Unterminated fragment", this.r.span(
|
|
215
|
-
return { kind: "Fragment", children: i, loc: this.r.span(
|
|
252
|
+
let n = this.consumeCloseTag();
|
|
253
|
+
n && this.report("mismatched-close-tag", "error", `Expected </> to close fragment, got </${n}>`, this.r.span(t));
|
|
254
|
+
} else this.report("unclosed-tag", "error", "Unterminated fragment", this.r.span(t));
|
|
255
|
+
return { kind: "Fragment", children: i, loc: this.r.span(t) };
|
|
216
256
|
}
|
|
217
257
|
parseElement() {
|
|
218
|
-
let
|
|
258
|
+
let t = this.r.pos();
|
|
219
259
|
this.r.advance();
|
|
220
|
-
let i = this.readName(),
|
|
221
|
-
this.foldEachKey(
|
|
222
|
-
let s = i.toLowerCase(),
|
|
223
|
-
if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(),
|
|
224
|
-
else if (this.r.peek() === ">" && this.r.advance(), !l) if (c =
|
|
225
|
-
let
|
|
226
|
-
|
|
227
|
-
} else this.report("unclosed-tag", "error", `Unclosed <${i}>`, this.r.span(
|
|
228
|
-
let h = this.r.span(
|
|
229
|
-
if (
|
|
230
|
-
let d = this.svg ||
|
|
231
|
-
return { kind: "Element", tag: i, namespace: d, attributes:
|
|
232
|
-
}
|
|
233
|
-
parseRawText(
|
|
234
|
-
let i = [],
|
|
235
|
-
|
|
260
|
+
let i = this.readName(), n = C(i), r = this.parseAttributes(n);
|
|
261
|
+
this.foldEachKey(r, t), this.r.skipWhitespace();
|
|
262
|
+
let s = i.toLowerCase(), o = !n && S.has(s), l = !n && T.has(s), a = false, c = [];
|
|
263
|
+
if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(), a = true;
|
|
264
|
+
else if (this.r.peek() === ">" && this.r.advance(), !l) if (c = o ? this.parseRawText(i) : this.parseChildren(), this.r.startsWith("</")) {
|
|
265
|
+
let g = this.consumeCloseTag();
|
|
266
|
+
g && g !== i && this.report("mismatched-close-tag", "error", `Expected </${i}>, got </${g}>`, this.r.span(t));
|
|
267
|
+
} else this.report("unclosed-tag", "error", `Unclosed <${i}>`, this.r.span(t));
|
|
268
|
+
let h = this.r.span(t);
|
|
269
|
+
if (n) return { kind: "Component", tag: i, tagHole: null, attributes: r, children: c, selfClosing: a, loc: h };
|
|
270
|
+
let d = this.svg || D.has(i) ? "svg" : "html";
|
|
271
|
+
return { kind: "Element", tag: i, namespace: d, attributes: r, children: c, selfClosing: a, rawText: o, loc: h };
|
|
272
|
+
}
|
|
273
|
+
parseRawText(t) {
|
|
274
|
+
let i = [], n = "</" + t.toLowerCase(), r = "", s = this.r.pos(), o = () => {
|
|
275
|
+
r && i.push({ kind: "Text", value: r, raw: true, loc: this.r.span(s) }), r = "";
|
|
236
276
|
};
|
|
237
|
-
for (; !this.r.eof() && !this.startsWithCloseFor(
|
|
277
|
+
for (; !this.r.eof() && !this.startsWithCloseFor(n); ) {
|
|
238
278
|
if (this.r.atHole()) {
|
|
239
|
-
|
|
279
|
+
o();
|
|
240
280
|
let l = this.r.takeHole();
|
|
241
281
|
i.push({ kind: "Expression", hole: l.index, loc: l.loc }), s = this.r.pos();
|
|
242
282
|
continue;
|
|
243
283
|
}
|
|
244
|
-
|
|
284
|
+
r || (s = this.r.pos()), r += this.r.advance();
|
|
245
285
|
}
|
|
246
|
-
return
|
|
286
|
+
return o(), i;
|
|
247
287
|
}
|
|
248
|
-
startsWithCloseFor(
|
|
288
|
+
startsWithCloseFor(t) {
|
|
249
289
|
let i = 0;
|
|
250
|
-
for (; i <
|
|
251
|
-
let
|
|
252
|
-
if (
|
|
290
|
+
for (; i < t.length; ) {
|
|
291
|
+
let n = this.r.peek(i);
|
|
292
|
+
if (n === "" || n.toLowerCase() !== t[i]) return false;
|
|
253
293
|
i++;
|
|
254
294
|
}
|
|
255
295
|
return true;
|
|
256
296
|
}
|
|
257
|
-
parseAttributes(
|
|
297
|
+
parseAttributes(t) {
|
|
258
298
|
let i = [];
|
|
259
299
|
for (; !this.r.eof() && (this.r.skipWhitespace(), !(this.r.peek() === ">" || this.r.startsWith("/>"))); ) {
|
|
260
300
|
if (this.r.atHole()) {
|
|
261
|
-
let
|
|
262
|
-
i.push({ kind: "Spread", hole:
|
|
301
|
+
let a = this.r.takeHole();
|
|
302
|
+
i.push({ kind: "Spread", hole: a.index, loc: a.loc });
|
|
263
303
|
continue;
|
|
264
304
|
}
|
|
265
305
|
if (this.r.startsWith("...")) {
|
|
266
|
-
let
|
|
306
|
+
let a = this.r.pos();
|
|
267
307
|
if (this.r.advance(), this.r.advance(), this.r.advance(), this.r.atHole()) {
|
|
268
308
|
let c = this.r.takeHole();
|
|
269
|
-
i.push({ kind: "Spread", hole: c.index, loc: this.r.span(
|
|
270
|
-
} else this.report("invalid-binding", "error", "Spread `...` must be followed by ${\u2026}", this.r.span(
|
|
309
|
+
i.push({ kind: "Spread", hole: c.index, loc: this.r.span(a) });
|
|
310
|
+
} else this.report("invalid-binding", "error", "Spread `...` must be followed by ${\u2026}", this.r.span(a));
|
|
271
311
|
continue;
|
|
272
312
|
}
|
|
273
|
-
let
|
|
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 s = this.r.span(
|
|
318
|
+
let s = this.r.span(n);
|
|
279
319
|
this.r.skipWhitespace();
|
|
280
|
-
let
|
|
281
|
-
this.r.peek() === "=" && (this.r.advance(), this.r.skipWhitespace(),
|
|
282
|
-
let l = { name:
|
|
283
|
-
i.push(
|
|
320
|
+
let o = null;
|
|
321
|
+
this.r.peek() === "=" && (this.r.advance(), this.r.skipWhitespace(), o = this.parseAttrValue());
|
|
322
|
+
let l = { name: r, nameLoc: s, value: o, loc: this.r.span(n), report: (a, c) => this.report(a, "error", c, this.r.span(n)) };
|
|
323
|
+
i.push(k(l, this.directives, { component: t }));
|
|
284
324
|
}
|
|
285
325
|
return i;
|
|
286
326
|
}
|
|
287
327
|
parseAttrValue() {
|
|
288
328
|
if (this.r.atHole()) return { kind: "hole", hole: this.r.takeHole().index };
|
|
289
|
-
let
|
|
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 i = [],
|
|
295
|
-
for (; !this.r.eof() && this.r.peek() !==
|
|
334
|
+
let i = [], n = "";
|
|
335
|
+
for (; !this.r.eof() && this.r.peek() !== t; ) {
|
|
296
336
|
if (this.r.atHole()) {
|
|
297
|
-
|
|
337
|
+
n && (i.push({ text: n }), n = ""), i.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 i =
|
|
308
|
-
return i.length === 0 ? { kind: "static", value:
|
|
309
|
-
}
|
|
310
|
-
foldEachKey(
|
|
311
|
-
let
|
|
312
|
-
for (let d of
|
|
313
|
-
let
|
|
314
|
-
s > 1 && this.report("duplicate-directive", "error", "Multiple `each` directives on one element",
|
|
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 && i.push({ text: n }), this.r.peek() === t && this.r.advance(), i;
|
|
345
|
+
}
|
|
346
|
+
classifyParts(t) {
|
|
347
|
+
let i = t.filter((n) => "hole" in n);
|
|
348
|
+
return i.length === 0 ? { kind: "static", value: t.map((n) => "text" in n ? n.text : "").join("") } : t.length === 1 ? { kind: "hole", hole: i[0].hole } : { kind: "mixed", parts: t };
|
|
349
|
+
}
|
|
350
|
+
foldEachKey(t, i) {
|
|
351
|
+
let n = null, r = 0, s = 0, o = 0, l = 0;
|
|
352
|
+
for (let d of t) d.kind === "EachDirective" ? (n = d, s++) : d.kind === "IfDirective" ? r++ : d.kind === "ElseDirective" ? o++ : d.kind === "RefBinding" && l++;
|
|
353
|
+
let a = this.r.span(i);
|
|
354
|
+
s > 1 && this.report("duplicate-directive", "error", "Multiple `each` directives on one element", a), r > 1 && this.report("duplicate-directive", "error", "Multiple `if` directives on one element", a), l > 1 && this.report("duplicate-directive", "error", "Multiple `ref` bindings on one element", a), r > 0 && s > 0 && this.report("unsupported-combination", "error", "`if` and `each` cannot both apply to one element \u2014 wrap one in another element", a), r > 0 && o > 0 && this.report("invalid-directive", "error", "`if` and `else` cannot be on the same element", a);
|
|
355
|
+
let c = t.findIndex((d) => d.kind === "Attribute" && d.name === "key");
|
|
356
|
+
if (!n) {
|
|
357
|
+
c !== -1 && this.report("invalid-directive", "error", "`key` requires an `each` directive on the same element", a);
|
|
318
358
|
return;
|
|
319
359
|
}
|
|
320
360
|
if (c === -1) return;
|
|
321
|
-
let h =
|
|
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) => I.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, i, n, r) {
|
|
373
|
+
this.diagnostics.push({ code: t, severity: i, message: n, loc: r });
|
|
334
374
|
}
|
|
335
375
|
};
|
|
336
|
-
function
|
|
337
|
-
return new
|
|
376
|
+
function N(e, t = {}) {
|
|
377
|
+
return new b(e, t).parse();
|
|
338
378
|
}
|
|
339
|
-
function
|
|
340
|
-
let
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
}),
|
|
379
|
+
function L(e) {
|
|
380
|
+
let t = [], i = 0;
|
|
381
|
+
return e.forEach((n, r) => {
|
|
382
|
+
t.push({ kind: "static", text: n, start: i }), i += n.length, r < e.length - 1 && t.push({ kind: "hole", index: r, start: i, end: i });
|
|
383
|
+
}), t;
|
|
344
384
|
}
|
|
345
|
-
function
|
|
346
|
-
return
|
|
385
|
+
function Q(e, t = {}) {
|
|
386
|
+
return N(L(e), t);
|
|
347
387
|
}
|
|
348
388
|
|
|
349
389
|
// src/collect.ts
|
|
@@ -355,7 +395,7 @@ function usedComponentTags(ts, sourceFile) {
|
|
|
355
395
|
const tags = /* @__PURE__ */ new Set();
|
|
356
396
|
const visit = (node) => {
|
|
357
397
|
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
|
|
358
|
-
collectTags(
|
|
398
|
+
collectTags(Q(templateStatics(ts, node.template)).root.children, tags);
|
|
359
399
|
}
|
|
360
400
|
ts.forEachChild(node, visit);
|
|
361
401
|
};
|
|
@@ -423,41 +463,198 @@ function templatePieces(ts, tpl, sf) {
|
|
|
423
463
|
});
|
|
424
464
|
return pieces;
|
|
425
465
|
}
|
|
426
|
-
|
|
427
|
-
|
|
466
|
+
|
|
467
|
+
// src/cache.ts
|
|
468
|
+
var parsed = /* @__PURE__ */ new WeakMap();
|
|
469
|
+
function parseCached(ts, sf, tpl) {
|
|
470
|
+
let byTemplate = parsed.get(sf);
|
|
471
|
+
if (!byTemplate) {
|
|
472
|
+
byTemplate = /* @__PURE__ */ new Map();
|
|
473
|
+
parsed.set(sf, byTemplate);
|
|
474
|
+
}
|
|
475
|
+
const key = tpl.getStart(sf);
|
|
476
|
+
const hit = byTemplate.get(key);
|
|
477
|
+
if (hit) return hit;
|
|
478
|
+
const result = N(templatePieces(ts, tpl, sf));
|
|
479
|
+
byTemplate.set(key, result);
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
482
|
+
var templates = /* @__PURE__ */ new WeakMap();
|
|
483
|
+
function templatesIn(ts, sf) {
|
|
484
|
+
const hit = templates.get(sf);
|
|
485
|
+
if (hit) return hit;
|
|
486
|
+
const found = [];
|
|
428
487
|
const visit = (node) => {
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
found = node;
|
|
432
|
-
return;
|
|
488
|
+
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
|
|
489
|
+
found.push(node);
|
|
433
490
|
}
|
|
434
491
|
ts.forEachChild(node, visit);
|
|
435
492
|
};
|
|
436
493
|
visit(sf);
|
|
494
|
+
found.sort((a, b2) => a.getStart(sf) - b2.getStart(sf));
|
|
495
|
+
templates.set(sf, found);
|
|
437
496
|
return found;
|
|
438
497
|
}
|
|
498
|
+
function templateAtCached(ts, sf, pos) {
|
|
499
|
+
let match;
|
|
500
|
+
for (const tpl of templatesIn(ts, sf)) {
|
|
501
|
+
const start = tpl.template.getStart(sf);
|
|
502
|
+
if (start > pos) break;
|
|
503
|
+
if (pos <= tpl.template.getEnd()) match = tpl;
|
|
504
|
+
}
|
|
505
|
+
return match;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/auto-import.ts
|
|
509
|
+
var DOM = "@fluixi/dom";
|
|
510
|
+
var CORE = "@fluixi/core";
|
|
511
|
+
var ROUTER = "@fluixi/core/router-next";
|
|
512
|
+
var AUTO_IMPORTED = {
|
|
513
|
+
// Control flow. Suspense and Await live in core; dom doesn't export them.
|
|
514
|
+
Show: DOM,
|
|
515
|
+
For: DOM,
|
|
516
|
+
Index: DOM,
|
|
517
|
+
Switch: DOM,
|
|
518
|
+
Match: DOM,
|
|
519
|
+
Portal: DOM,
|
|
520
|
+
Dynamic: DOM,
|
|
521
|
+
ErrorBoundary: DOM,
|
|
522
|
+
Suspense: CORE,
|
|
523
|
+
SuspenseList: CORE,
|
|
524
|
+
Await: CORE,
|
|
525
|
+
// Router. `Routes` isn't a component and `lazy` is a call, so neither appears.
|
|
526
|
+
Router: ROUTER,
|
|
527
|
+
Outlet: ROUTER,
|
|
528
|
+
Redirect: ROUTER,
|
|
529
|
+
Link: ROUTER
|
|
530
|
+
};
|
|
531
|
+
var exportsByProgram = /* @__PURE__ */ new WeakMap();
|
|
532
|
+
function moduleExports(ts, program, host, from, moduleName) {
|
|
533
|
+
let cache2 = exportsByProgram.get(program);
|
|
534
|
+
if (!cache2) {
|
|
535
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
536
|
+
exportsByProgram.set(program, cache2);
|
|
537
|
+
}
|
|
538
|
+
if (cache2.has(moduleName)) return cache2.get(moduleName);
|
|
539
|
+
let symbols;
|
|
540
|
+
try {
|
|
541
|
+
const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
|
|
542
|
+
const file = resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName);
|
|
543
|
+
const checker = program.getTypeChecker();
|
|
544
|
+
const moduleSymbol = file && checker.getSymbolAtLocation(file);
|
|
545
|
+
if (moduleSymbol) symbols = checker.getExportsOfModule(moduleSymbol);
|
|
546
|
+
} catch {
|
|
547
|
+
symbols = void 0;
|
|
548
|
+
}
|
|
549
|
+
cache2.set(moduleName, symbols);
|
|
550
|
+
return symbols;
|
|
551
|
+
}
|
|
552
|
+
var sitesByProgram = /* @__PURE__ */ new WeakMap();
|
|
553
|
+
function declarationSite(ts, program, host, from, name) {
|
|
554
|
+
const moduleName = AUTO_IMPORTED[name];
|
|
555
|
+
if (!moduleName) return void 0;
|
|
556
|
+
let cache2 = sitesByProgram.get(program);
|
|
557
|
+
if (!cache2) {
|
|
558
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
559
|
+
sitesByProgram.set(program, cache2);
|
|
560
|
+
}
|
|
561
|
+
const key = `${moduleName}:${name}`;
|
|
562
|
+
if (cache2.has(key)) return cache2.get(key);
|
|
563
|
+
let site;
|
|
564
|
+
try {
|
|
565
|
+
const resolved = ts.resolveModuleName(moduleName, from.fileName, program.getCompilerOptions(), host);
|
|
566
|
+
const entry = resolved.resolvedModule?.resolvedFileName;
|
|
567
|
+
if (entry) site = findExport(ts, host, program.getCompilerOptions(), entry, name, 0);
|
|
568
|
+
} catch {
|
|
569
|
+
site = void 0;
|
|
570
|
+
}
|
|
571
|
+
cache2.set(key, site);
|
|
572
|
+
return site;
|
|
573
|
+
}
|
|
574
|
+
function findExport(ts, host, options, fileName, name, depth) {
|
|
575
|
+
if (depth > 4) return void 0;
|
|
576
|
+
const text = host.readFile?.(fileName);
|
|
577
|
+
if (text === void 0) return void 0;
|
|
578
|
+
const sf = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true);
|
|
579
|
+
const reExports = [];
|
|
580
|
+
for (const statement of sf.statements) {
|
|
581
|
+
const declared = declaredName(ts, statement, name);
|
|
582
|
+
if (declared) {
|
|
583
|
+
return { fileName, start: declared.getStart(sf), length: declared.getWidth(sf) };
|
|
584
|
+
}
|
|
585
|
+
if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
586
|
+
const specifier = statement.moduleSpecifier.text;
|
|
587
|
+
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
588
|
+
const named = statement.exportClause.elements.find((e) => e.name.text === name);
|
|
589
|
+
if (named) reExports.unshift(specifier);
|
|
590
|
+
} else if (!statement.exportClause) {
|
|
591
|
+
reExports.push(specifier);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
for (const specifier of reExports) {
|
|
596
|
+
const target = resolveRelative(ts, host, options, fileName, specifier);
|
|
597
|
+
const found = target && findExport(ts, host, options, target, name, depth + 1);
|
|
598
|
+
if (found) return found;
|
|
599
|
+
}
|
|
600
|
+
return void 0;
|
|
601
|
+
}
|
|
602
|
+
function declaredName(ts, statement, name) {
|
|
603
|
+
const exported = ts.canHaveModifiers(statement) ? ts.getModifiers(statement)?.some((m2) => m2.kind === ts.SyntaxKind.ExportKeyword) : false;
|
|
604
|
+
if (!exported) return void 0;
|
|
605
|
+
if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name?.text === name) {
|
|
606
|
+
return statement.name;
|
|
607
|
+
}
|
|
608
|
+
if (ts.isVariableStatement(statement)) {
|
|
609
|
+
for (const d of statement.declarationList.declarations) {
|
|
610
|
+
if (ts.isIdentifier(d.name) && d.name.text === name) return d.name;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return void 0;
|
|
614
|
+
}
|
|
615
|
+
function resolveRelative(ts, host, options, from, specifier) {
|
|
616
|
+
if (!specifier.startsWith(".")) return void 0;
|
|
617
|
+
try {
|
|
618
|
+
return ts.resolveModuleName(specifier, from, options, host).resolvedModule?.resolvedFileName;
|
|
619
|
+
} catch {
|
|
620
|
+
return void 0;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function autoImportedSymbol(ts, program, host, from, name) {
|
|
624
|
+
const moduleName = AUTO_IMPORTED[name];
|
|
625
|
+
if (!moduleName) return void 0;
|
|
626
|
+
const symbol = moduleExports(ts, program, host, from, moduleName)?.find((s) => s.name === name);
|
|
627
|
+
if (!symbol) return void 0;
|
|
628
|
+
if (!(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
|
|
629
|
+
try {
|
|
630
|
+
return program.getTypeChecker().getAliasedSymbol(symbol);
|
|
631
|
+
} catch {
|
|
632
|
+
return symbol;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
439
635
|
|
|
440
636
|
// src/hover.ts
|
|
441
|
-
function componentQuickInfo(ts, ls, fileName, position) {
|
|
637
|
+
function componentQuickInfo(ts, ls, host, fileName, position) {
|
|
442
638
|
const program = ls.getProgram();
|
|
443
639
|
const sf = program?.getSourceFile(fileName);
|
|
444
640
|
if (!program || !sf) return void 0;
|
|
445
|
-
const template =
|
|
641
|
+
const template = templateAtCached(ts, sf, position);
|
|
446
642
|
if (!template) return void 0;
|
|
447
643
|
const hit = componentTagAt(template, sf, position, ts);
|
|
448
644
|
if (!hit) return void 0;
|
|
449
645
|
const checker = program.getTypeChecker();
|
|
450
646
|
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
451
|
-
const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name);
|
|
647
|
+
const symbol = checker.getSymbolsInScope(template, flags).find((s) => s.name === hit.name) ?? autoImportedSymbol(ts, program, host, sf, hit.name);
|
|
452
648
|
const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
453
649
|
if (!decl) return void 0;
|
|
454
|
-
const
|
|
455
|
-
const
|
|
650
|
+
const declFile = decl.getSourceFile();
|
|
651
|
+
const namePos = decl.name?.getStart(declFile) ?? decl.getStart(declFile);
|
|
652
|
+
const info = ls.getQuickInfoAtPosition(declFile.fileName, namePos);
|
|
456
653
|
if (!info) return void 0;
|
|
457
654
|
return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
|
|
458
655
|
}
|
|
459
656
|
function componentTagAt(template, sf, position, ts) {
|
|
460
|
-
const { root } =
|
|
657
|
+
const { root } = parseCached(ts, sf, template.template);
|
|
461
658
|
let hit;
|
|
462
659
|
const walk = (nodes) => {
|
|
463
660
|
for (const node of nodes) {
|
|
@@ -621,14 +818,14 @@ function resolveElementType(ts, checker, location, tag) {
|
|
|
621
818
|
return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
|
|
622
819
|
}
|
|
623
820
|
function inferParamAt(ts, program, sf, position) {
|
|
624
|
-
const template =
|
|
821
|
+
const template = templateAtCached(ts, sf, position);
|
|
625
822
|
if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
|
|
626
823
|
const spans = template.template.templateSpans;
|
|
627
824
|
const holeExprs = spans.map((s) => s.expression);
|
|
628
825
|
const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
|
|
629
826
|
if (holeIndex === -1) return void 0;
|
|
630
827
|
const roles = /* @__PURE__ */ new Map();
|
|
631
|
-
holeRoles(
|
|
828
|
+
holeRoles(parseCached(ts, sf, template.template).root.children, roles);
|
|
632
829
|
const role = roles.get(holeIndex);
|
|
633
830
|
if (!role) return void 0;
|
|
634
831
|
const fn = holeExprs[holeIndex];
|
|
@@ -639,6 +836,12 @@ function inferParamAt(ts, program, sf, position) {
|
|
|
639
836
|
const id = identifierAt(ts, sf, position);
|
|
640
837
|
if (!id || id.text !== paramName || !contains(fn, id)) return void 0;
|
|
641
838
|
const checker = program.getTypeChecker();
|
|
839
|
+
const resolved = typeForRole(ts, checker, fn, role, holeExprs);
|
|
840
|
+
const { typeText, type } = resolved;
|
|
841
|
+
if (!typeText) return void 0;
|
|
842
|
+
return { paramName, typeText, type, start: id.getStart(sf), length: id.getWidth(sf) };
|
|
843
|
+
}
|
|
844
|
+
function typeForRole(ts, checker, fn, role, holeExprs) {
|
|
642
845
|
let typeText;
|
|
643
846
|
let type;
|
|
644
847
|
if (role.kind === "event") {
|
|
@@ -659,8 +862,27 @@ function inferParamAt(ts, program, sf, position) {
|
|
|
659
862
|
type = checker.getTypeAtLocation(holeExprs[role.eachHole]).getNumberIndexType();
|
|
660
863
|
if (type) typeText = checker.typeToString(type);
|
|
661
864
|
}
|
|
662
|
-
|
|
663
|
-
|
|
865
|
+
return { typeText, type };
|
|
866
|
+
}
|
|
867
|
+
function inferParamsIn(ts, program, sf) {
|
|
868
|
+
const checker = program.getTypeChecker();
|
|
869
|
+
const out = [];
|
|
870
|
+
for (const template of templatesIn(ts, sf)) {
|
|
871
|
+
const tpl = template.template;
|
|
872
|
+
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
873
|
+
const holeExprs = tpl.templateSpans.map((s) => s.expression);
|
|
874
|
+
const roles = /* @__PURE__ */ new Map();
|
|
875
|
+
holeRoles(parseCached(ts, sf, tpl).root.children, roles);
|
|
876
|
+
for (const [holeIndex, role] of roles) {
|
|
877
|
+
const fn = holeExprs[holeIndex];
|
|
878
|
+
if (!fn || !ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue;
|
|
879
|
+
const param = fn.parameters[0];
|
|
880
|
+
if (!param || !ts.isIdentifier(param.name) || param.type) continue;
|
|
881
|
+
const { type } = typeForRole(ts, checker, fn, role, holeExprs);
|
|
882
|
+
if (type) out.push({ fn, paramName: param.name.text, type });
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return out;
|
|
664
886
|
}
|
|
665
887
|
function paramQuickInfo(ts, ls, fileName, position) {
|
|
666
888
|
const program = ls.getProgram();
|
|
@@ -706,7 +928,7 @@ function componentPropCompletions(ts, ls, fileName, position) {
|
|
|
706
928
|
const program = ls.getProgram();
|
|
707
929
|
const sf = program?.getSourceFile(fileName);
|
|
708
930
|
if (!program || !sf) return void 0;
|
|
709
|
-
const template =
|
|
931
|
+
const template = templateAtCached(ts, sf, position);
|
|
710
932
|
if (!template) return void 0;
|
|
711
933
|
const comp = componentTagAtAttrArea(ts, sf, template.template, position);
|
|
712
934
|
if (!comp || !comp.tag) return void 0;
|
|
@@ -735,7 +957,7 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
735
957
|
const program = ls.getProgram();
|
|
736
958
|
const sf = program?.getSourceFile(fileName);
|
|
737
959
|
if (!program || !sf) return void 0;
|
|
738
|
-
const template =
|
|
960
|
+
const template = templateAtCached(ts, sf, position);
|
|
739
961
|
if (!template) return void 0;
|
|
740
962
|
const hit = propNameAt(ts, sf, template.template, position);
|
|
741
963
|
if (!hit) return void 0;
|
|
@@ -771,7 +993,7 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
771
993
|
};
|
|
772
994
|
}
|
|
773
995
|
function propNameAt(ts, sf, tpl, position) {
|
|
774
|
-
const { root } =
|
|
996
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
775
997
|
let hit;
|
|
776
998
|
const walk = (nodes) => {
|
|
777
999
|
for (const node of nodes) {
|
|
@@ -846,7 +1068,7 @@ function firstParamType(ts, checker, type) {
|
|
|
846
1068
|
return void 0;
|
|
847
1069
|
}
|
|
848
1070
|
function componentTagAtAttrArea(ts, sf, tpl, position) {
|
|
849
|
-
const { root } =
|
|
1071
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
850
1072
|
let hit;
|
|
851
1073
|
const walk = (nodes) => {
|
|
852
1074
|
for (const node of nodes) {
|
|
@@ -921,366 +1143,450 @@ function propertyAccessAt(ts, sf, position) {
|
|
|
921
1143
|
return found;
|
|
922
1144
|
}
|
|
923
1145
|
|
|
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;
|
|
1146
|
+
// src/elements.ts
|
|
1147
|
+
var ATTRIBUTES_MODULE = "@fluixi/dom/attributes";
|
|
1148
|
+
var perProgram = /* @__PURE__ */ new WeakMap();
|
|
1149
|
+
function intrinsics(ts, program, host, sf) {
|
|
1150
|
+
const hit = perProgram.get(program);
|
|
1151
|
+
if (hit) return hit;
|
|
1152
|
+
let type;
|
|
1153
|
+
try {
|
|
1154
|
+
const checker = program.getTypeChecker();
|
|
1155
|
+
const declared = (file) => {
|
|
1156
|
+
if (!file) return void 0;
|
|
1157
|
+
const moduleSymbol = checker.getSymbolAtLocation(file);
|
|
1158
|
+
const symbol = moduleSymbol?.exports?.get(ts.escapeLeadingUnderscores("IntrinsicElements"));
|
|
1159
|
+
return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : void 0;
|
|
1160
|
+
};
|
|
1161
|
+
const resolved = ts.resolveModuleName(
|
|
1162
|
+
ATTRIBUTES_MODULE,
|
|
1163
|
+
sf.fileName,
|
|
1164
|
+
program.getCompilerOptions(),
|
|
1165
|
+
host
|
|
1166
|
+
);
|
|
1167
|
+
type = declared(
|
|
1168
|
+
resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName)
|
|
1169
|
+
);
|
|
1170
|
+
if (!type) {
|
|
1171
|
+
for (const file of program.getSourceFiles()) {
|
|
1172
|
+
if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
|
|
1173
|
+
type = declared(file);
|
|
1174
|
+
if (type) break;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
} catch {
|
|
1178
|
+
type = void 0;
|
|
1179
|
+
}
|
|
1180
|
+
const entry = { type, byTag: /* @__PURE__ */ new Map() };
|
|
1181
|
+
perProgram.set(program, entry);
|
|
1182
|
+
return entry;
|
|
1183
|
+
}
|
|
1184
|
+
function attributesFor(ts, program, host, sf, tag) {
|
|
1185
|
+
const state = intrinsics(ts, program, host, sf);
|
|
1186
|
+
if (!state.type) return void 0;
|
|
1187
|
+
if (state.byTag.has(tag)) return state.byTag.get(tag);
|
|
1188
|
+
let props;
|
|
1189
|
+
const checker = program.getTypeChecker();
|
|
1190
|
+
const tagSymbol = state.type.getProperty(tag);
|
|
1191
|
+
if (tagSymbol) {
|
|
1192
|
+
const decl = tagSymbol.valueDeclaration ?? tagSymbol.declarations?.[0];
|
|
1193
|
+
if (decl) props = checker.getTypeOfSymbolAtLocation(tagSymbol, decl).getProperties();
|
|
1194
|
+
}
|
|
1195
|
+
state.byTag.set(tag, props);
|
|
1196
|
+
return props;
|
|
1197
|
+
}
|
|
1198
|
+
function elementAtAttributeArea(ts, sf, tpl, position) {
|
|
1199
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1200
|
+
let hit;
|
|
1201
|
+
const walk = (nodes) => {
|
|
1202
|
+
for (const node of nodes) {
|
|
1203
|
+
if (node.kind === "Element") {
|
|
1204
|
+
const el = node;
|
|
1205
|
+
const openStart = el.loc.start + 1 + el.tag.length;
|
|
1206
|
+
const openEnd = firstAttributeEnd(el);
|
|
1207
|
+
if (position > openStart && position <= openEnd) {
|
|
1208
|
+
const present = /* @__PURE__ */ new Set();
|
|
1209
|
+
for (const a of el.attributes) {
|
|
1210
|
+
if ("loc" in a && a.loc) present.add(writtenName(sf.text, a.loc.start));
|
|
1211
|
+
}
|
|
1212
|
+
hit = { tag: el.tag, present };
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1216
|
+
if (hit) return;
|
|
1217
|
+
}
|
|
1013
1218
|
};
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1219
|
+
walk(root.children);
|
|
1220
|
+
return hit;
|
|
1221
|
+
}
|
|
1222
|
+
function writtenName(text, start) {
|
|
1223
|
+
let end = start;
|
|
1224
|
+
while (end < text.length && !/[\s=/>]/.test(text[end])) end++;
|
|
1225
|
+
return text.slice(start, end);
|
|
1226
|
+
}
|
|
1227
|
+
function firstAttributeEnd(el) {
|
|
1228
|
+
let end = el.loc.start + 1 + el.tag.length;
|
|
1229
|
+
for (const a of el.attributes) {
|
|
1230
|
+
if ("loc" in a && a.loc) end = Math.max(end, a.loc.end);
|
|
1019
1231
|
}
|
|
1020
|
-
|
|
1021
|
-
return { text: gen, segments };
|
|
1232
|
+
return end + 1;
|
|
1022
1233
|
}
|
|
1023
|
-
function
|
|
1024
|
-
|
|
1234
|
+
function elementAttributeCompletions(ts, ls, host, fileName, position) {
|
|
1235
|
+
const program = ls.getProgram();
|
|
1236
|
+
const sf = program?.getSourceFile(fileName);
|
|
1237
|
+
if (!program || !sf) return void 0;
|
|
1238
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1239
|
+
if (!tpl) return void 0;
|
|
1240
|
+
const hit = elementAtAttributeArea(ts, sf, tpl.template, position);
|
|
1241
|
+
if (!hit) return void 0;
|
|
1242
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1243
|
+
if (!props) return void 0;
|
|
1244
|
+
const entries = [];
|
|
1245
|
+
for (const p2 of props) {
|
|
1246
|
+
const name = p2.getName();
|
|
1247
|
+
if (name === "children" || hit.present.has(name)) continue;
|
|
1248
|
+
entries.push({
|
|
1249
|
+
name,
|
|
1250
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1251
|
+
kindModifiers: "",
|
|
1252
|
+
sortText: name.startsWith("on") ? "1" : "0"
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
return entries.length ? entries : void 0;
|
|
1025
1256
|
}
|
|
1026
|
-
function
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1257
|
+
function attributeNameAt(ts, sf, tpl, position) {
|
|
1258
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1259
|
+
let hit;
|
|
1260
|
+
const walk = (nodes) => {
|
|
1261
|
+
for (const node of nodes) {
|
|
1262
|
+
if (node.kind === "Element") {
|
|
1263
|
+
const el = node;
|
|
1264
|
+
for (const a of el.attributes) {
|
|
1265
|
+
if (!("loc" in a) || !a.loc) continue;
|
|
1266
|
+
const start = a.loc.start;
|
|
1267
|
+
const name = writtenName(sf.text, start);
|
|
1268
|
+
if (name && position >= start && position <= start + name.length) {
|
|
1269
|
+
hit = { tag: el.tag, name, start };
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1274
|
+
if (hit) return;
|
|
1033
1275
|
}
|
|
1034
|
-
ts.forEachChild(node, visit);
|
|
1035
1276
|
};
|
|
1036
|
-
|
|
1037
|
-
return
|
|
1277
|
+
walk(root.children);
|
|
1278
|
+
return hit;
|
|
1038
1279
|
}
|
|
1039
|
-
function
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1280
|
+
function elementAttributeHover(ts, ls, host, fileName, position) {
|
|
1281
|
+
const program = ls.getProgram();
|
|
1282
|
+
const sf = program?.getSourceFile(fileName);
|
|
1283
|
+
if (!program || !sf) return void 0;
|
|
1284
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1285
|
+
if (!tpl) return void 0;
|
|
1286
|
+
const hit = attributeNameAt(ts, sf, tpl.template, position);
|
|
1287
|
+
if (!hit) return void 0;
|
|
1288
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1289
|
+
const symbol = props?.find((p2) => p2.getName() === hit.name);
|
|
1290
|
+
if (!symbol) return void 0;
|
|
1291
|
+
const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
1292
|
+
if (!decl) return void 0;
|
|
1293
|
+
const checker = program.getTypeChecker();
|
|
1294
|
+
const typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, decl));
|
|
1295
|
+
return {
|
|
1296
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1297
|
+
kindModifiers: "",
|
|
1298
|
+
textSpan: { start: hit.start, length: hit.name.length },
|
|
1299
|
+
displayParts: [
|
|
1300
|
+
{ text: "(", kind: "punctuation" },
|
|
1301
|
+
{ text: "attribute", kind: "text" },
|
|
1302
|
+
{ text: ")", kind: "punctuation" },
|
|
1303
|
+
{ text: " ", kind: "space" },
|
|
1304
|
+
{ text: `${hit.tag}.`, kind: "text" },
|
|
1305
|
+
{ text: hit.name, kind: "propertyName" },
|
|
1306
|
+
{ text: ":", kind: "punctuation" },
|
|
1307
|
+
{ text: " ", kind: "space" },
|
|
1308
|
+
{ text: typeText, kind: "text" }
|
|
1309
|
+
],
|
|
1310
|
+
documentation: symbol.getDocumentationComment(checker)
|
|
1044
1311
|
};
|
|
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
|
-
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// src/diagnostics.ts
|
|
1315
|
+
var ACCESSOR_NOT_CALLED = 9001;
|
|
1316
|
+
function acceptsAnyProperty(ts, checker, type) {
|
|
1317
|
+
const open = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter | ts.TypeFlags.Never;
|
|
1318
|
+
if (type.flags & open) return true;
|
|
1319
|
+
if (checker.getIndexInfoOfType?.(type, ts.IndexKind.String)) return true;
|
|
1320
|
+
if (type.isUnion()) return type.types.some((t) => acceptsAnyProperty(ts, checker, t));
|
|
1321
|
+
return false;
|
|
1322
|
+
}
|
|
1323
|
+
function accessorDiagnostics(ts, program, sf) {
|
|
1324
|
+
const checker = program.getTypeChecker();
|
|
1325
|
+
const out = [];
|
|
1326
|
+
for (const template of templatesIn(ts, sf)) {
|
|
1327
|
+
const tpl = template.template;
|
|
1328
|
+
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
1329
|
+
const holes = tpl.templateSpans.map((s) => s.expression);
|
|
1330
|
+
const visit = (nodes, parent) => {
|
|
1331
|
+
for (const node of nodes) {
|
|
1332
|
+
if (node.kind === "Expression" && parent?.kind === "Element") {
|
|
1333
|
+
const expression = holes[node.hole];
|
|
1334
|
+
if (expression && isAccessorRead(ts, expression) && isAccessor(ts, checker, expression)) {
|
|
1335
|
+
const text = expression.getText(sf);
|
|
1336
|
+
out.push({
|
|
1337
|
+
file: sf,
|
|
1338
|
+
start: expression.getStart(sf),
|
|
1339
|
+
length: expression.getWidth(sf),
|
|
1340
|
+
category: ts.DiagnosticCategory.Warning,
|
|
1341
|
+
code: ACCESSOR_NOT_CALLED,
|
|
1342
|
+
messageText: `'${text}' is an accessor \u2014 call it (\`${text}()\`). Left bare it renders the function and never updates.`,
|
|
1343
|
+
source: "fluixi"
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1079
1346
|
}
|
|
1347
|
+
const children = "children" in node ? node.children : void 0;
|
|
1348
|
+
if (children) visit(children, node);
|
|
1080
1349
|
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1350
|
+
};
|
|
1351
|
+
visit(parseCached(ts, sf, tpl).root.children, null);
|
|
1083
1352
|
}
|
|
1353
|
+
return out;
|
|
1084
1354
|
}
|
|
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
|
-
}
|
|
1355
|
+
function isAccessorRead(ts, node) {
|
|
1356
|
+
return ts.isIdentifier(node) || ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
|
|
1357
|
+
}
|
|
1358
|
+
function isAccessor(ts, checker, node) {
|
|
1359
|
+
const signatures = checker.getTypeAtLocation(node).getCallSignatures();
|
|
1360
|
+
if (signatures.length !== 1) return false;
|
|
1361
|
+
const signature = signatures[0];
|
|
1362
|
+
if (signature.getParameters().some((p2) => !isOptional(ts, p2))) return false;
|
|
1363
|
+
const returned = signature.getReturnType();
|
|
1364
|
+
return !(returned.flags & (ts.TypeFlags.Void | ts.TypeFlags.Never));
|
|
1365
|
+
}
|
|
1366
|
+
function isOptional(ts, symbol) {
|
|
1367
|
+
const declaration = symbol.valueDeclaration;
|
|
1368
|
+
return !!declaration && ts.isParameter(declaration) && (!!declaration.questionToken || !!declaration.initializer || !!declaration.dotDotDotToken);
|
|
1369
|
+
}
|
|
1370
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
1371
|
+
function templateDiagnostics(ts, ls, fileName) {
|
|
1372
|
+
const program = ls.getProgram();
|
|
1373
|
+
const sf = program?.getSourceFile(fileName);
|
|
1374
|
+
if (!program || !sf) return [];
|
|
1375
|
+
const hit = cache.get(sf);
|
|
1376
|
+
if (hit) return hit;
|
|
1377
|
+
const checker = program.getTypeChecker();
|
|
1378
|
+
const out = [];
|
|
1379
|
+
out.push(...accessorDiagnostics(ts, program, sf));
|
|
1380
|
+
for (const { fn, paramName, type } of inferParamsIn(ts, program, sf)) {
|
|
1381
|
+
if (acceptsAnyProperty(ts, checker, type)) continue;
|
|
1382
|
+
const visit = (node) => {
|
|
1383
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === paramName && ts.isIdentifier(node.name)) {
|
|
1384
|
+
const symbol = checker.getSymbolAtLocation(node.expression);
|
|
1385
|
+
const declaredHere = symbol?.declarations?.some((d) => d.parent === fn);
|
|
1386
|
+
if (declaredHere && !checker.getPropertyOfType(type, node.name.text)) {
|
|
1387
|
+
out.push({
|
|
1388
|
+
file: sf,
|
|
1389
|
+
start: node.name.getStart(sf),
|
|
1390
|
+
length: node.name.getWidth(sf),
|
|
1391
|
+
category: ts.DiagnosticCategory.Error,
|
|
1392
|
+
code: 2339,
|
|
1393
|
+
messageText: `Property '${node.name.text}' does not exist on type '${checker.typeToString(type)}'.`,
|
|
1394
|
+
source: "fluixi"
|
|
1143
1395
|
});
|
|
1144
|
-
} else if (a.kind === "PropertyBinding") {
|
|
1145
|
-
entry(a.loc.start + 1, a.name, () => putHole(a.hole));
|
|
1146
1396
|
}
|
|
1147
1397
|
}
|
|
1148
|
-
|
|
1398
|
+
ts.forEachChild(node, visit);
|
|
1149
1399
|
};
|
|
1400
|
+
if (fn.body) visit(fn.body);
|
|
1150
1401
|
}
|
|
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;
|
|
1402
|
+
cache.set(sf, out);
|
|
1403
|
+
return out;
|
|
1208
1404
|
}
|
|
1209
1405
|
|
|
1210
|
-
// src/
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1406
|
+
// src/fixes.ts
|
|
1407
|
+
function templateCodeFixes(ts, ls, fileName, start, end, errorCodes) {
|
|
1408
|
+
if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return [];
|
|
1409
|
+
const out = [];
|
|
1410
|
+
for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
|
|
1411
|
+
if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
|
|
1412
|
+
if (diagnostic.start > end || diagnostic.start + (diagnostic.length ?? 0) < start) continue;
|
|
1413
|
+
const at = diagnostic.start + (diagnostic.length ?? 0);
|
|
1414
|
+
const name = diagnostic.file?.text.slice(diagnostic.start, at) ?? "value";
|
|
1415
|
+
out.push({
|
|
1416
|
+
fixName: "fluixiCallAccessor",
|
|
1417
|
+
description: `Call '${name}'`,
|
|
1418
|
+
changes: [
|
|
1419
|
+
{
|
|
1420
|
+
fileName,
|
|
1421
|
+
textChanges: [{ span: { start: at, length: 0 }, newText: "()" }]
|
|
1223
1422
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1423
|
+
],
|
|
1424
|
+
// Every occurrence in the file is the same edit, so offer to do them at once.
|
|
1425
|
+
fixAllDescription: "Call every uncalled accessor in this file"
|
|
1227
1426
|
});
|
|
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
1427
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (
|
|
1274
|
-
|
|
1275
|
-
|
|
1428
|
+
return out;
|
|
1429
|
+
}
|
|
1430
|
+
function templateFixAll(ts, ls, fileName, errorCodes) {
|
|
1431
|
+
if (!errorCodes.includes(ACCESSOR_NOT_CALLED)) return void 0;
|
|
1432
|
+
const textChanges = [];
|
|
1433
|
+
for (const diagnostic of templateDiagnostics(ts, ls, fileName)) {
|
|
1434
|
+
if (diagnostic.code !== ACCESSOR_NOT_CALLED || diagnostic.start === void 0) continue;
|
|
1435
|
+
const at = diagnostic.start + (diagnostic.length ?? 0);
|
|
1436
|
+
textChanges.push({ span: { start: at, length: 0 }, newText: "()" });
|
|
1437
|
+
}
|
|
1438
|
+
if (textChanges.length === 0) return void 0;
|
|
1439
|
+
textChanges.sort((a, b2) => b2.span.start - a.span.start);
|
|
1440
|
+
return { changes: [{ fileName, textChanges }], commands: void 0 };
|
|
1276
1441
|
}
|
|
1277
1442
|
|
|
1278
|
-
// src/
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
const
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1443
|
+
// src/navigation.ts
|
|
1444
|
+
var spansByFile = /* @__PURE__ */ new WeakMap();
|
|
1445
|
+
function tagSpans(ts, sf) {
|
|
1446
|
+
const hit = spansByFile.get(sf);
|
|
1447
|
+
if (hit) return hit;
|
|
1448
|
+
const out = [];
|
|
1449
|
+
for (const template of templatesIn(ts, sf)) {
|
|
1450
|
+
const { root } = parseCached(ts, sf, template.template);
|
|
1451
|
+
const walk = (nodes) => {
|
|
1452
|
+
for (const node of nodes) {
|
|
1453
|
+
if (node.kind === "Component" && node.tag) {
|
|
1454
|
+
const component = node;
|
|
1455
|
+
const name = component.tag.split(".")[0];
|
|
1456
|
+
out.push({ name, start: component.loc.start + 1, length: name.length });
|
|
1457
|
+
const closing = closingTagName(sf.text, component.loc.end, component.tag);
|
|
1458
|
+
if (closing !== void 0) out.push({ name, start: closing, length: name.length });
|
|
1459
|
+
}
|
|
1460
|
+
if ("children" in node && node.children) walk(node.children);
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
walk(root.children);
|
|
1464
|
+
}
|
|
1465
|
+
spansByFile.set(sf, out);
|
|
1466
|
+
return out;
|
|
1467
|
+
}
|
|
1468
|
+
function closingTagName(text, end, tag) {
|
|
1469
|
+
const open = text.lastIndexOf("</", end);
|
|
1470
|
+
if (open === -1) return void 0;
|
|
1471
|
+
const close = text.indexOf(">", open);
|
|
1472
|
+
if (close === -1 || close >= end) return void 0;
|
|
1473
|
+
const index = text.indexOf(tag, open);
|
|
1474
|
+
return index !== -1 && index < close ? index : void 0;
|
|
1475
|
+
}
|
|
1476
|
+
function tagAt(ts, sf, position) {
|
|
1477
|
+
if (!templateAtCached(ts, sf, position)) return void 0;
|
|
1478
|
+
return tagSpans(ts, sf).find((s) => position >= s.start && position <= s.start + s.length);
|
|
1479
|
+
}
|
|
1480
|
+
function symbolFor(ts, program, host, location, name) {
|
|
1481
|
+
const checker = program.getTypeChecker();
|
|
1482
|
+
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
1483
|
+
const symbol = checker.getSymbolsInScope(location, flags).find((s) => s.name === name);
|
|
1484
|
+
if (!symbol) return autoImportedSymbol(ts, program, host, location.getSourceFile(), name);
|
|
1485
|
+
return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
1486
|
+
}
|
|
1487
|
+
function declarationFor(ts, program, host, sf, tag) {
|
|
1488
|
+
const template = templateAtCached(ts, sf, tag.start);
|
|
1489
|
+
if (!template) return void 0;
|
|
1490
|
+
const symbol = symbolFor(ts, program, host, template, tag.name);
|
|
1491
|
+
return symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
1492
|
+
}
|
|
1493
|
+
function templateDefinition(ts, ls, host, fileName, position) {
|
|
1494
|
+
const program = ls.getProgram();
|
|
1495
|
+
const sf = program?.getSourceFile(fileName);
|
|
1496
|
+
if (!program || !sf) return void 0;
|
|
1497
|
+
const tag = tagAt(ts, sf, position);
|
|
1498
|
+
if (!tag) return void 0;
|
|
1499
|
+
const decl = declarationFor(ts, program, host, sf, tag);
|
|
1500
|
+
if (!decl) {
|
|
1501
|
+
const site = declarationSite(ts, program, host, sf, tag.name);
|
|
1502
|
+
if (!site) return void 0;
|
|
1503
|
+
return [
|
|
1504
|
+
{
|
|
1505
|
+
fileName: site.fileName,
|
|
1506
|
+
textSpan: { start: site.start, length: site.length },
|
|
1507
|
+
kind: ts.ScriptElementKind.unknown,
|
|
1508
|
+
name: tag.name,
|
|
1509
|
+
containerKind: ts.ScriptElementKind.unknown,
|
|
1510
|
+
containerName: ""
|
|
1511
|
+
}
|
|
1512
|
+
];
|
|
1513
|
+
}
|
|
1514
|
+
const target = decl.name ?? decl;
|
|
1515
|
+
const declFile = decl.getSourceFile();
|
|
1516
|
+
return [
|
|
1517
|
+
{
|
|
1518
|
+
fileName: declFile.fileName,
|
|
1519
|
+
textSpan: { start: target.getStart(declFile), length: target.getWidth(declFile) },
|
|
1520
|
+
kind: ts.ScriptElementKind.unknown,
|
|
1521
|
+
name: tag.name,
|
|
1522
|
+
containerKind: ts.ScriptElementKind.unknown,
|
|
1523
|
+
containerName: ""
|
|
1524
|
+
}
|
|
1525
|
+
];
|
|
1526
|
+
}
|
|
1527
|
+
function tagOccurrences(ts, program, host, symbol, name) {
|
|
1528
|
+
const out = [];
|
|
1529
|
+
for (const sf of program.getSourceFiles()) {
|
|
1530
|
+
if (sf.isDeclarationFile) continue;
|
|
1531
|
+
const text = sf.text;
|
|
1532
|
+
if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) continue;
|
|
1533
|
+
if (text.indexOf(name) === -1) continue;
|
|
1534
|
+
for (const span of tagSpans(ts, sf)) {
|
|
1535
|
+
if (span.name !== name) continue;
|
|
1536
|
+
const template = templateAtCached(ts, sf, span.start);
|
|
1537
|
+
if (!template || symbolFor(ts, program, host, template, name) !== symbol) continue;
|
|
1538
|
+
out.push({ fileName: sf.fileName, span: { start: span.start, length: span.length } });
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return out;
|
|
1542
|
+
}
|
|
1543
|
+
function symbolAt(ts, program, host, sf, position) {
|
|
1544
|
+
const checker = program.getTypeChecker();
|
|
1545
|
+
const tag = tagAt(ts, sf, position);
|
|
1546
|
+
if (tag) {
|
|
1547
|
+
const template = templateAtCached(ts, sf, tag.start);
|
|
1548
|
+
const symbol2 = template && symbolFor(ts, program, host, template, tag.name);
|
|
1549
|
+
return symbol2 ? { symbol: symbol2, name: tag.name } : void 0;
|
|
1550
|
+
}
|
|
1551
|
+
const token = tokenAt(ts, sf, position);
|
|
1552
|
+
if (!token || !ts.isIdentifier(token)) return void 0;
|
|
1553
|
+
let symbol = checker.getSymbolAtLocation(token);
|
|
1554
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
|
|
1555
|
+
return symbol ? { symbol, name: token.text } : void 0;
|
|
1556
|
+
}
|
|
1557
|
+
function tokenAt(ts, sf, position) {
|
|
1558
|
+
let found;
|
|
1559
|
+
const visit = (node) => {
|
|
1560
|
+
if (position < node.getStart(sf) || position > node.getEnd()) return;
|
|
1561
|
+
if (node.getChildCount(sf) === 0) found = node;
|
|
1562
|
+
ts.forEachChild(node, visit);
|
|
1563
|
+
};
|
|
1564
|
+
visit(sf);
|
|
1565
|
+
return found;
|
|
1566
|
+
}
|
|
1567
|
+
function templateReferences(ts, ls, host, fileName, position) {
|
|
1568
|
+
const program = ls.getProgram();
|
|
1569
|
+
const sf = program?.getSourceFile(fileName);
|
|
1570
|
+
if (!program || !sf) return [];
|
|
1571
|
+
const hit = symbolAt(ts, program, host, sf, position);
|
|
1572
|
+
if (!hit) return [];
|
|
1573
|
+
return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
|
|
1574
|
+
fileName: f2,
|
|
1575
|
+
textSpan: span,
|
|
1576
|
+
isWriteAccess: false,
|
|
1577
|
+
isDefinition: false
|
|
1578
|
+
}));
|
|
1579
|
+
}
|
|
1580
|
+
function templateRenameLocations(ts, ls, host, fileName, position) {
|
|
1581
|
+
const program = ls.getProgram();
|
|
1582
|
+
const sf = program?.getSourceFile(fileName);
|
|
1583
|
+
if (!program || !sf) return [];
|
|
1584
|
+
const hit = symbolAt(ts, program, host, sf, position);
|
|
1585
|
+
if (!hit) return [];
|
|
1586
|
+
return tagOccurrences(ts, program, host, hit.symbol, hit.name).map(({ fileName: f2, span }) => ({
|
|
1587
|
+
fileName: f2,
|
|
1588
|
+
textSpan: span
|
|
1589
|
+
}));
|
|
1284
1590
|
}
|
|
1285
1591
|
|
|
1286
1592
|
// src/index.ts
|
|
@@ -1289,12 +1595,7 @@ function init(modules) {
|
|
|
1289
1595
|
return {
|
|
1290
1596
|
create(info) {
|
|
1291
1597
|
const ls = info.languageService;
|
|
1292
|
-
|
|
1293
|
-
try {
|
|
1294
|
-
vs = new VirtualService(ts, info.languageServiceHost);
|
|
1295
|
-
} catch {
|
|
1296
|
-
vs = void 0;
|
|
1297
|
-
}
|
|
1598
|
+
const host = info.languageServiceHost;
|
|
1298
1599
|
const proxy = /* @__PURE__ */ Object.create(null);
|
|
1299
1600
|
for (const key of Object.keys(ls)) {
|
|
1300
1601
|
const member = ls[key];
|
|
@@ -1307,6 +1608,15 @@ function init(modules) {
|
|
|
1307
1608
|
entries
|
|
1308
1609
|
});
|
|
1309
1610
|
const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
|
|
1611
|
+
const dedupe = (items) => {
|
|
1612
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1613
|
+
return items.filter((i) => {
|
|
1614
|
+
const key = `${i.fileName}:${i.textSpan.start}:${i.textSpan.length}`;
|
|
1615
|
+
if (seen.has(key)) return false;
|
|
1616
|
+
seen.add(key);
|
|
1617
|
+
return true;
|
|
1618
|
+
});
|
|
1619
|
+
};
|
|
1310
1620
|
const reliableFilter = (fileName, diagnostics) => {
|
|
1311
1621
|
const program = ls.getProgram();
|
|
1312
1622
|
const sourceFile = program?.getSourceFile(fileName);
|
|
@@ -1318,27 +1628,14 @@ function init(modules) {
|
|
|
1318
1628
|
return true;
|
|
1319
1629
|
});
|
|
1320
1630
|
};
|
|
1321
|
-
|
|
1322
|
-
|
|
1631
|
+
proxy.getSemanticDiagnostics = (fileName) => {
|
|
1632
|
+
const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
|
|
1323
1633
|
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;
|
|
1634
|
+
return base.concat(templateDiagnostics(ts, ls, fileName));
|
|
1334
1635
|
} catch {
|
|
1335
|
-
return
|
|
1636
|
+
return base;
|
|
1336
1637
|
}
|
|
1337
1638
|
};
|
|
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
1639
|
proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
|
|
1343
1640
|
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
1344
1641
|
try {
|
|
@@ -1346,12 +1643,22 @@ function init(modules) {
|
|
|
1346
1643
|
if (prop) return prop;
|
|
1347
1644
|
} catch {
|
|
1348
1645
|
}
|
|
1646
|
+
try {
|
|
1647
|
+
const attr = elementAttributeHover(ts, ls, host, fileName, position);
|
|
1648
|
+
if (attr) return attr;
|
|
1649
|
+
} catch {
|
|
1650
|
+
}
|
|
1349
1651
|
try {
|
|
1350
1652
|
const inferred = paramQuickInfo(ts, ls, fileName, position);
|
|
1351
1653
|
if (inferred) return inferred;
|
|
1352
1654
|
} catch {
|
|
1353
1655
|
}
|
|
1354
|
-
|
|
1656
|
+
try {
|
|
1657
|
+
const tag = componentQuickInfo(ts, ls, host, fileName, position);
|
|
1658
|
+
if (tag) return tag;
|
|
1659
|
+
} catch {
|
|
1660
|
+
}
|
|
1661
|
+
return ls.getQuickInfoAtPosition(fileName, position);
|
|
1355
1662
|
};
|
|
1356
1663
|
proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
|
|
1357
1664
|
try {
|
|
@@ -1359,6 +1666,11 @@ function init(modules) {
|
|
|
1359
1666
|
if (props) return completionList(props);
|
|
1360
1667
|
} catch {
|
|
1361
1668
|
}
|
|
1669
|
+
try {
|
|
1670
|
+
const attrs = elementAttributeCompletions(ts, ls, host, fileName, position);
|
|
1671
|
+
if (attrs) return completionList(attrs);
|
|
1672
|
+
} catch {
|
|
1673
|
+
}
|
|
1362
1674
|
try {
|
|
1363
1675
|
const members = paramMemberCompletions(ts, ls, fileName, position);
|
|
1364
1676
|
if (members) return completionList(members);
|
|
@@ -1366,6 +1678,71 @@ function init(modules) {
|
|
|
1366
1678
|
}
|
|
1367
1679
|
return ls.getCompletionsAtPosition(fileName, position, options, formatting);
|
|
1368
1680
|
};
|
|
1681
|
+
proxy.getDefinitionAtPosition = (fileName, position) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const tag = templateDefinition(ts, ls, host, fileName, position);
|
|
1684
|
+
if (tag) return tag;
|
|
1685
|
+
} catch {
|
|
1686
|
+
}
|
|
1687
|
+
return ls.getDefinitionAtPosition(fileName, position);
|
|
1688
|
+
};
|
|
1689
|
+
proxy.getDefinitionAndBoundSpan = (fileName, position) => {
|
|
1690
|
+
try {
|
|
1691
|
+
const definitions = templateDefinition(ts, ls, host, fileName, position);
|
|
1692
|
+
if (definitions) {
|
|
1693
|
+
const sf = ls.getProgram()?.getSourceFile(fileName);
|
|
1694
|
+
const tag = sf && tagAt(ts, sf, position);
|
|
1695
|
+
if (tag) {
|
|
1696
|
+
return { definitions, textSpan: { start: tag.start, length: tag.length } };
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
} catch {
|
|
1700
|
+
}
|
|
1701
|
+
return ls.getDefinitionAndBoundSpan(fileName, position);
|
|
1702
|
+
};
|
|
1703
|
+
proxy.getReferencesAtPosition = (fileName, position) => {
|
|
1704
|
+
const base = ls.getReferencesAtPosition(fileName, position) ?? [];
|
|
1705
|
+
try {
|
|
1706
|
+
const extra = templateReferences(ts, ls, host, fileName, position);
|
|
1707
|
+
return extra.length ? dedupe([...base, ...extra]) : base;
|
|
1708
|
+
} catch {
|
|
1709
|
+
return base;
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => {
|
|
1713
|
+
const base = ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs) ?? [];
|
|
1714
|
+
try {
|
|
1715
|
+
const extra = templateRenameLocations(ts, ls, host, fileName, position);
|
|
1716
|
+
if (!base.length || !extra.length) return base.length ? base : void 0;
|
|
1717
|
+
return dedupe([...base, ...extra]);
|
|
1718
|
+
} catch {
|
|
1719
|
+
return base;
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
proxy.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
|
|
1723
|
+
const base = ls.getCodeFixesAtPosition(
|
|
1724
|
+
fileName,
|
|
1725
|
+
start,
|
|
1726
|
+
end,
|
|
1727
|
+
errorCodes,
|
|
1728
|
+
formatOptions,
|
|
1729
|
+
preferences
|
|
1730
|
+
);
|
|
1731
|
+
try {
|
|
1732
|
+
const own = templateCodeFixes(ts, ls, fileName, start, end, errorCodes);
|
|
1733
|
+
return own.length ? [...base, ...own] : base;
|
|
1734
|
+
} catch {
|
|
1735
|
+
return base;
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
proxy.getCombinedCodeFix = (scope, fixId, formatOptions, preferences) => {
|
|
1739
|
+
if (fixId === "fluixiCallAccessor") {
|
|
1740
|
+
const fileName = scope.fileName;
|
|
1741
|
+
const combined = templateFixAll(ts, ls, fileName, [ACCESSOR_NOT_CALLED]);
|
|
1742
|
+
if (combined) return combined;
|
|
1743
|
+
}
|
|
1744
|
+
return ls.getCombinedCodeFix(scope, fixId, formatOptions, preferences);
|
|
1745
|
+
};
|
|
1369
1746
|
return proxy;
|
|
1370
1747
|
}
|
|
1371
1748
|
};
|