@fluixi/ts-plugin 0.1.0-alpha.3 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +244 -224
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// ../template-parser/dist/index.mjs
|
|
4
4
|
function x(e) {
|
|
5
|
-
let t = (
|
|
6
|
-
|
|
7
|
-
let r =
|
|
8
|
-
for (let
|
|
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
10
|
return t(e, null), e;
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function R(e) {
|
|
13
13
|
switch (e.kind) {
|
|
14
14
|
case "Root":
|
|
15
15
|
case "Element":
|
|
@@ -42,8 +42,8 @@ var m = 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
48
|
let t = this.pieces[this.pi];
|
|
49
49
|
return t.kind === "static" ? t.start + this.oi : t.start;
|
|
@@ -55,8 +55,8 @@ var m = class {
|
|
|
55
55
|
if (this.eof()) return false;
|
|
56
56
|
let t = this.pieces[this.pi];
|
|
57
57
|
if (t.kind !== "static" || this.oi + 1 < t.text.length) return false;
|
|
58
|
-
let
|
|
59
|
-
return
|
|
58
|
+
let i2 = this.pieces[this.pi + 1];
|
|
59
|
+
return i2 !== void 0 && i2.kind === "hole";
|
|
60
60
|
}
|
|
61
61
|
takeHole() {
|
|
62
62
|
let t = this.pieces[this.pi];
|
|
@@ -65,29 +65,29 @@ var m = class {
|
|
|
65
65
|
}
|
|
66
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
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
78
|
let t = this.pieces[this.pi];
|
|
79
79
|
if (t.kind !== "static") return "";
|
|
80
|
-
let
|
|
81
|
-
return this.normalize(),
|
|
80
|
+
let i2 = t.text[this.oi++] ?? "";
|
|
81
|
+
return this.normalize(), i2;
|
|
82
82
|
}
|
|
83
83
|
readWhile(t) {
|
|
84
|
-
let
|
|
84
|
+
let i2 = "";
|
|
85
85
|
for (; !this.eof() && !this.atHole(); ) {
|
|
86
86
|
let n = this.peek();
|
|
87
87
|
if (n === "" || !t(n)) break;
|
|
88
|
-
|
|
88
|
+
i2 += n, this.oi++;
|
|
89
89
|
}
|
|
90
|
-
return this.normalize(),
|
|
90
|
+
return this.normalize(), i2;
|
|
91
91
|
}
|
|
92
92
|
skipWhitespace() {
|
|
93
93
|
this.readWhile((t) => /\s/.test(t));
|
|
@@ -100,19 +100,19 @@ var y = /* @__PURE__ */ new Set(["capture", "once", "passive", "prevent", "stop"
|
|
|
100
100
|
function p(e, t) {
|
|
101
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
102
|
}
|
|
103
|
-
var
|
|
103
|
+
var D = (e, t) => ({ kind: "Attribute", name: t ? e.name.slice(1) : e.name, value: e.value, boolean: t, loc: e.loc });
|
|
104
104
|
var f = [{ id: "event", match: (e) => e[0] === "@" || e.startsWith("on:") || /^on[A-Z]/.test(e), build: (e) => {
|
|
105
|
-
let t,
|
|
106
|
-
e.name[0] === "@" ? (t = "at",
|
|
107
|
-
let [n, ...r] =
|
|
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
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:
|
|
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) =>
|
|
111
|
-
function k(e, t = f,
|
|
112
|
-
for (let n of t) if (!(n.elementOnly &&
|
|
113
|
-
return
|
|
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
114
|
}
|
|
115
|
-
var
|
|
115
|
+
var E = /* @__PURE__ */ new Set(["svg", "path", "circle", "rect", "line", "polygon", "polyline", "ellipse", "g", "defs", "clipPath", "text"]);
|
|
116
116
|
var S = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
|
|
117
117
|
var T = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
118
118
|
function C(e) {
|
|
@@ -121,15 +121,15 @@ function C(e) {
|
|
|
121
121
|
var P = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea", "summary", "details", "option", "label"]);
|
|
122
122
|
var W = /* @__PURE__ */ new Set(["keydown", "keyup", "keypress"]);
|
|
123
123
|
function u(e, t) {
|
|
124
|
-
return e.attributes.find((
|
|
124
|
+
return e.attributes.find((i2) => "name" in i2 && i2.name === t);
|
|
125
125
|
}
|
|
126
126
|
function w(e) {
|
|
127
127
|
return e.attributes.some((t) => t.kind === "Spread");
|
|
128
128
|
}
|
|
129
129
|
function v(e, t) {
|
|
130
|
-
let
|
|
131
|
-
if (!
|
|
132
|
-
let n =
|
|
130
|
+
let i2 = u(e, t);
|
|
131
|
+
if (!i2 || i2.kind !== "Attribute" || !i2.value) return;
|
|
132
|
+
let n = i2.value;
|
|
133
133
|
return n.kind === "static" ? n.value : void 0;
|
|
134
134
|
}
|
|
135
135
|
function H(e) {
|
|
@@ -137,48 +137,48 @@ function H(e) {
|
|
|
137
137
|
return t === "presentation" || t === "none" ? true : v(e, "aria-hidden") === "true";
|
|
138
138
|
}
|
|
139
139
|
function A(e, t) {
|
|
140
|
-
return e.attributes.some((
|
|
140
|
+
return e.attributes.some((i2) => i2.kind === "EventBinding" && t(i2.name));
|
|
141
141
|
}
|
|
142
|
-
function
|
|
143
|
-
let t = [],
|
|
144
|
-
t.push({ code:
|
|
142
|
+
function B(e) {
|
|
143
|
+
let t = [], i2 = (r, s2, o) => {
|
|
144
|
+
t.push({ code: s2, message: o, severity: "warning", loc: r.loc });
|
|
145
145
|
}, n = (r) => {
|
|
146
|
-
for (let
|
|
147
|
-
|
|
148
|
-
let o = "children" in
|
|
146
|
+
for (let s2 of r) {
|
|
147
|
+
s2.kind === "Element" && L(s2, i2);
|
|
148
|
+
let o = "children" in s2 ? s2.children : void 0;
|
|
149
149
|
o && n(o);
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
152
|
return n(e.children), t;
|
|
153
153
|
}
|
|
154
|
-
function
|
|
154
|
+
function L(e, t) {
|
|
155
155
|
if (w(e)) return;
|
|
156
|
-
let
|
|
157
|
-
|
|
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
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(
|
|
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
160
|
}
|
|
161
|
-
var
|
|
162
|
-
var
|
|
163
|
-
constructor(t,
|
|
161
|
+
var V = /[A-Za-z0-9\-_:@.?]/;
|
|
162
|
+
var N = class {
|
|
163
|
+
constructor(t, i2) {
|
|
164
164
|
this.diagnostics = [];
|
|
165
|
-
this.r = new m(t), this.svg =
|
|
165
|
+
this.r = new m(t), this.svg = i2.svg ?? false, this.directives = i2.directives ?? f, this.accessibility = i2.accessibility ?? true;
|
|
166
166
|
}
|
|
167
167
|
parse() {
|
|
168
|
-
let t = this.r.pos(),
|
|
169
|
-
for (; !this.r.eof() && (
|
|
168
|
+
let t = this.r.pos(), i2 = [];
|
|
169
|
+
for (; !this.r.eof() && (i2.push(...this.parseChildren()), !this.r.eof()); ) {
|
|
170
170
|
let r = this.r.pos();
|
|
171
171
|
this.consumeCloseTag(), this.report("stray-close-tag", "error", "Close tag without a matching open tag", this.r.span(r));
|
|
172
172
|
}
|
|
173
|
-
let n = { kind: "Root", children:
|
|
174
|
-
return x(n), this.validateTree(n), this.accessibility && this.diagnostics.push(...
|
|
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 };
|
|
175
175
|
}
|
|
176
176
|
validateTree(t) {
|
|
177
|
-
let
|
|
177
|
+
let i2 = (n) => {
|
|
178
178
|
let r = null;
|
|
179
|
-
for (let
|
|
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));
|
|
180
180
|
};
|
|
181
|
-
|
|
181
|
+
i2(t.children);
|
|
182
182
|
}
|
|
183
183
|
parseChildren() {
|
|
184
184
|
let t = [];
|
|
@@ -204,8 +204,8 @@ var b = class {
|
|
|
204
204
|
t.push(r);
|
|
205
205
|
continue;
|
|
206
206
|
}
|
|
207
|
-
let
|
|
208
|
-
|
|
207
|
+
let i2 = this.readText();
|
|
208
|
+
i2 && t.push(i2);
|
|
209
209
|
}
|
|
210
210
|
return t;
|
|
211
211
|
}
|
|
@@ -221,93 +221,93 @@ var b = class {
|
|
|
221
221
|
parseDynamicComponent() {
|
|
222
222
|
let t = this.r.pos();
|
|
223
223
|
this.r.advance();
|
|
224
|
-
let
|
|
224
|
+
let i2 = this.r.takeHole().index, n = this.parseAttributes(true);
|
|
225
225
|
this.r.skipWhitespace();
|
|
226
|
-
let r = false,
|
|
227
|
-
return this.r.startsWith("/>") ? (this.r.advance(), this.r.advance(), r = true) : (this.r.peek() === ">" && 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) };
|
|
228
228
|
}
|
|
229
229
|
readText() {
|
|
230
|
-
let t = this.r.pos(),
|
|
231
|
-
for (; !this.r.eof() && !this.r.atHole() && !(this.r.startsWith("</") || this.r.startsWith("<!--") || this.isTagStart() || this.isFragmentStart() || this.isDynamicTagStart()); )
|
|
232
|
-
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;
|
|
233
233
|
}
|
|
234
234
|
parseComment() {
|
|
235
235
|
let t = this.r.pos();
|
|
236
236
|
this.r.advance(), this.r.advance(), this.r.advance(), this.r.advance();
|
|
237
|
-
let
|
|
237
|
+
let i2 = "";
|
|
238
238
|
for (; !this.r.eof() && !this.r.startsWith("-->"); ) {
|
|
239
239
|
if (this.r.atHole()) {
|
|
240
|
-
this.r.takeHole(),
|
|
240
|
+
this.r.takeHole(), i2 += "\0";
|
|
241
241
|
continue;
|
|
242
242
|
}
|
|
243
|
-
|
|
243
|
+
i2 += this.r.advance();
|
|
244
244
|
}
|
|
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:
|
|
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) };
|
|
246
246
|
}
|
|
247
247
|
parseFragment() {
|
|
248
248
|
let t = this.r.pos();
|
|
249
249
|
this.r.advance(), this.r.advance();
|
|
250
|
-
let
|
|
250
|
+
let i2 = this.parseChildren();
|
|
251
251
|
if (this.r.startsWith("</")) {
|
|
252
252
|
let n = this.consumeCloseTag();
|
|
253
253
|
n && this.report("mismatched-close-tag", "error", `Expected </> to close fragment, got </${n}>`, this.r.span(t));
|
|
254
254
|
} else this.report("unclosed-tag", "error", "Unterminated fragment", this.r.span(t));
|
|
255
|
-
return { kind: "Fragment", children:
|
|
255
|
+
return { kind: "Fragment", children: i2, loc: this.r.span(t) };
|
|
256
256
|
}
|
|
257
257
|
parseElement() {
|
|
258
258
|
let t = this.r.pos();
|
|
259
259
|
this.r.advance();
|
|
260
|
-
let
|
|
260
|
+
let i2 = this.readName(), n = C(i2), r = this.parseAttributes(n);
|
|
261
261
|
this.foldEachKey(r, t), this.r.skipWhitespace();
|
|
262
|
-
let
|
|
263
|
-
if (this.r.startsWith("/>")) this.r.advance(), this.r.advance(),
|
|
264
|
-
else if (this.r.peek() === ">" && this.r.advance(), !
|
|
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
265
|
let g = this.consumeCloseTag();
|
|
266
|
-
g && g !==
|
|
267
|
-
} else this.report("unclosed-tag", "error", `Unclosed <${
|
|
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
268
|
let h = this.r.span(t);
|
|
269
|
-
if (n) return { kind: "Component", tag:
|
|
270
|
-
let d = this.svg ||
|
|
271
|
-
return { kind: "Element", tag:
|
|
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
272
|
}
|
|
273
273
|
parseRawText(t) {
|
|
274
|
-
let
|
|
275
|
-
r &&
|
|
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 = "";
|
|
276
276
|
};
|
|
277
277
|
for (; !this.r.eof() && !this.startsWithCloseFor(n); ) {
|
|
278
278
|
if (this.r.atHole()) {
|
|
279
279
|
o();
|
|
280
|
-
let
|
|
281
|
-
|
|
280
|
+
let l2 = this.r.takeHole();
|
|
281
|
+
i2.push({ kind: "Expression", hole: l2.index, loc: l2.loc }), s2 = this.r.pos();
|
|
282
282
|
continue;
|
|
283
283
|
}
|
|
284
|
-
r || (
|
|
284
|
+
r || (s2 = this.r.pos()), r += this.r.advance();
|
|
285
285
|
}
|
|
286
|
-
return o(),
|
|
286
|
+
return o(), i2;
|
|
287
287
|
}
|
|
288
288
|
startsWithCloseFor(t) {
|
|
289
|
-
let
|
|
290
|
-
for (;
|
|
291
|
-
let n = this.r.peek(
|
|
292
|
-
if (n === "" || n.toLowerCase() !== t[
|
|
293
|
-
|
|
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++;
|
|
294
294
|
}
|
|
295
295
|
return true;
|
|
296
296
|
}
|
|
297
297
|
parseAttributes(t) {
|
|
298
|
-
let
|
|
298
|
+
let i2 = [];
|
|
299
299
|
for (; !this.r.eof() && (this.r.skipWhitespace(), !(this.r.peek() === ">" || this.r.startsWith("/>"))); ) {
|
|
300
300
|
if (this.r.atHole()) {
|
|
301
|
-
let
|
|
302
|
-
|
|
301
|
+
let a2 = this.r.takeHole();
|
|
302
|
+
i2.push({ kind: "Spread", hole: a2.index, loc: a2.loc });
|
|
303
303
|
continue;
|
|
304
304
|
}
|
|
305
305
|
if (this.r.startsWith("...")) {
|
|
306
|
-
let
|
|
306
|
+
let a2 = this.r.pos();
|
|
307
307
|
if (this.r.advance(), this.r.advance(), this.r.advance(), this.r.atHole()) {
|
|
308
308
|
let c = this.r.takeHole();
|
|
309
|
-
|
|
310
|
-
} 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));
|
|
311
311
|
continue;
|
|
312
312
|
}
|
|
313
313
|
let n = this.r.pos(), r = this.readName();
|
|
@@ -315,14 +315,14 @@ var b = class {
|
|
|
315
315
|
this.r.advance();
|
|
316
316
|
continue;
|
|
317
317
|
}
|
|
318
|
-
let
|
|
318
|
+
let s2 = this.r.span(n);
|
|
319
319
|
this.r.skipWhitespace();
|
|
320
320
|
let o = null;
|
|
321
321
|
this.r.peek() === "=" && (this.r.advance(), this.r.skipWhitespace(), o = this.parseAttrValue());
|
|
322
|
-
let
|
|
323
|
-
|
|
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 }));
|
|
324
324
|
}
|
|
325
|
-
return
|
|
325
|
+
return i2;
|
|
326
326
|
}
|
|
327
327
|
parseAttrValue() {
|
|
328
328
|
if (this.r.atHole()) return { kind: "hole", hole: this.r.takeHole().index };
|
|
@@ -331,30 +331,30 @@ var b = class {
|
|
|
331
331
|
}
|
|
332
332
|
readQuotedParts(t) {
|
|
333
333
|
this.r.advance();
|
|
334
|
-
let
|
|
334
|
+
let i2 = [], n = "";
|
|
335
335
|
for (; !this.r.eof() && this.r.peek() !== t; ) {
|
|
336
336
|
if (this.r.atHole()) {
|
|
337
|
-
n && (
|
|
337
|
+
n && (i2.push({ text: n }), n = ""), i2.push({ hole: this.r.takeHole().index });
|
|
338
338
|
continue;
|
|
339
339
|
}
|
|
340
340
|
let r = this.r.advance();
|
|
341
341
|
if (r === "") break;
|
|
342
342
|
n += r;
|
|
343
343
|
}
|
|
344
|
-
return n &&
|
|
344
|
+
return n && i2.push({ text: n }), this.r.peek() === t && this.r.advance(), i2;
|
|
345
345
|
}
|
|
346
346
|
classifyParts(t) {
|
|
347
|
-
let
|
|
348
|
-
return
|
|
349
|
-
}
|
|
350
|
-
foldEachKey(t,
|
|
351
|
-
let n = null, r = 0,
|
|
352
|
-
for (let d of t) d.kind === "EachDirective" ? (n = d,
|
|
353
|
-
let
|
|
354
|
-
|
|
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
355
|
let c = t.findIndex((d) => d.kind === "Attribute" && d.name === "key");
|
|
356
356
|
if (!n) {
|
|
357
|
-
c !== -1 && this.report("invalid-directive", "error", "`key` requires an `each` directive on the same element",
|
|
357
|
+
c !== -1 && this.report("invalid-directive", "error", "`key` requires an `each` directive on the same element", a2);
|
|
358
358
|
return;
|
|
359
359
|
}
|
|
360
360
|
if (c === -1) return;
|
|
@@ -362,28 +362,28 @@ var b = class {
|
|
|
362
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);
|
|
363
363
|
}
|
|
364
364
|
readName() {
|
|
365
|
-
return this.r.readWhile((t) =>
|
|
365
|
+
return this.r.readWhile((t) => V.test(t));
|
|
366
366
|
}
|
|
367
367
|
consumeCloseTag() {
|
|
368
368
|
this.r.advance(), this.r.advance();
|
|
369
369
|
let t = this.readName();
|
|
370
370
|
return this.r.skipWhitespace(), this.r.peek() === ">" && this.r.advance(), t;
|
|
371
371
|
}
|
|
372
|
-
report(t,
|
|
373
|
-
this.diagnostics.push({ code: t, severity:
|
|
372
|
+
report(t, i2, n, r) {
|
|
373
|
+
this.diagnostics.push({ code: t, severity: i2, message: n, loc: r });
|
|
374
374
|
}
|
|
375
375
|
};
|
|
376
|
-
function
|
|
377
|
-
return new
|
|
376
|
+
function b(e, t = {}) {
|
|
377
|
+
return new N(e, t).parse();
|
|
378
378
|
}
|
|
379
|
-
function
|
|
380
|
-
let t = [],
|
|
379
|
+
function I(e) {
|
|
380
|
+
let t = [], i2 = 0;
|
|
381
381
|
return e.forEach((n, r) => {
|
|
382
|
-
t.push({ kind: "static", text: n, start:
|
|
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
383
|
}), t;
|
|
384
384
|
}
|
|
385
385
|
function Q(e, t = {}) {
|
|
386
|
-
return
|
|
386
|
+
return b(I(e), t);
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// src/collect.ts
|
|
@@ -451,15 +451,26 @@ function importedNames(ts, decl) {
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
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
|
+
}
|
|
454
459
|
function templatePieces(ts, tpl, sf) {
|
|
455
460
|
if (ts.isNoSubstitutionTemplateLiteral(tpl)) {
|
|
456
|
-
return [{ kind: "static", text: tpl
|
|
461
|
+
return [{ kind: "static", text: rawContent(ts, tpl, sf), start: tpl.getStart(sf) + 1 }];
|
|
457
462
|
}
|
|
458
|
-
const pieces = [
|
|
459
|
-
|
|
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) => {
|
|
460
467
|
const e = span.expression;
|
|
461
|
-
pieces.push({ kind: "hole", index:
|
|
462
|
-
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
|
+
});
|
|
463
474
|
});
|
|
464
475
|
return pieces;
|
|
465
476
|
}
|
|
@@ -475,7 +486,7 @@ function parseCached(ts, sf, tpl) {
|
|
|
475
486
|
const key = tpl.getStart(sf);
|
|
476
487
|
const hit = byTemplate.get(key);
|
|
477
488
|
if (hit) return hit;
|
|
478
|
-
const result =
|
|
489
|
+
const result = b(templatePieces(ts, tpl, sf));
|
|
479
490
|
byTemplate.set(key, result);
|
|
480
491
|
return result;
|
|
481
492
|
}
|
|
@@ -491,7 +502,7 @@ function templatesIn(ts, sf) {
|
|
|
491
502
|
ts.forEachChild(node, visit);
|
|
492
503
|
};
|
|
493
504
|
visit(sf);
|
|
494
|
-
found.sort((
|
|
505
|
+
found.sort((a2, b2) => a2.getStart(sf) - b2.getStart(sf));
|
|
495
506
|
templates.set(sf, found);
|
|
496
507
|
return found;
|
|
497
508
|
}
|
|
@@ -505,29 +516,22 @@ function templateAtCached(ts, sf, pos) {
|
|
|
505
516
|
return match;
|
|
506
517
|
}
|
|
507
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
|
+
|
|
508
533
|
// src/auto-import.ts
|
|
509
|
-
var
|
|
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
|
-
};
|
|
534
|
+
var AUTO_IMPORTED = l();
|
|
531
535
|
var exportsByProgram = /* @__PURE__ */ new WeakMap();
|
|
532
536
|
function moduleExports(ts, program, host, from, moduleName) {
|
|
533
537
|
let cache2 = exportsByProgram.get(program);
|
|
@@ -623,7 +627,7 @@ function resolveRelative(ts, host, options, from, specifier) {
|
|
|
623
627
|
function autoImportedSymbol(ts, program, host, from, name) {
|
|
624
628
|
const moduleName = AUTO_IMPORTED[name];
|
|
625
629
|
if (!moduleName) return void 0;
|
|
626
|
-
const symbol = moduleExports(ts, program, host, from, moduleName)?.find((
|
|
630
|
+
const symbol = moduleExports(ts, program, host, from, moduleName)?.find((s2) => s2.name === name);
|
|
627
631
|
if (!symbol) return void 0;
|
|
628
632
|
if (!(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
|
|
629
633
|
try {
|
|
@@ -644,7 +648,7 @@ function componentQuickInfo(ts, ls, host, fileName, position) {
|
|
|
644
648
|
if (!hit) return void 0;
|
|
645
649
|
const checker = program.getTypeChecker();
|
|
646
650
|
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
647
|
-
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);
|
|
648
652
|
const decl = symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
649
653
|
if (!decl) return void 0;
|
|
650
654
|
const declFile = decl.getSourceFile();
|
|
@@ -781,21 +785,21 @@ function holeRoles(nodes, out) {
|
|
|
781
785
|
};
|
|
782
786
|
for (const node of nodes) {
|
|
783
787
|
if (node.kind === "Element" || node.kind === "Component") {
|
|
784
|
-
for (const
|
|
785
|
-
if (
|
|
786
|
-
if (node.kind === "Element" &&
|
|
787
|
-
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 });
|
|
788
792
|
}
|
|
789
|
-
const eachDir = node.attributes.find((
|
|
793
|
+
const eachDir = node.attributes.find((a2) => a2.kind === "EachDirective");
|
|
790
794
|
if (eachDir && eachDir.kind === "EachDirective") {
|
|
791
795
|
renderChildren(node, { kind: "each", eachHole: eachDir.hole });
|
|
792
796
|
}
|
|
793
797
|
if (node.kind === "Component") {
|
|
794
798
|
const propHole = (name) => {
|
|
795
|
-
const
|
|
799
|
+
const a2 = node.attributes.find(
|
|
796
800
|
(x2) => x2.kind === "Attribute" && x2.name === name && x2.value != null && x2.value.kind === "hole"
|
|
797
801
|
);
|
|
798
|
-
return
|
|
802
|
+
return a2 && a2.kind === "Attribute" && a2.value && a2.value.kind === "hole" ? a2.value.hole : void 0;
|
|
799
803
|
};
|
|
800
804
|
const eachHole = propHole("each");
|
|
801
805
|
if (node.tag === "For" && eachHole !== void 0) renderChildren(node, { kind: "each", eachHole });
|
|
@@ -808,7 +812,7 @@ function holeRoles(nodes, out) {
|
|
|
808
812
|
}
|
|
809
813
|
}
|
|
810
814
|
function resolveGlobalType(ts, checker, location, name) {
|
|
811
|
-
const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((
|
|
815
|
+
const sym = checker.getSymbolsInScope(location, ts.SymbolFlags.Type).find((s2) => s2.getName() === name);
|
|
812
816
|
return sym ? checker.getDeclaredTypeOfSymbol(sym) : void 0;
|
|
813
817
|
}
|
|
814
818
|
function resolveElementType(ts, checker, location, tag) {
|
|
@@ -821,7 +825,7 @@ function inferParamAt(ts, program, sf, position) {
|
|
|
821
825
|
const template = templateAtCached(ts, sf, position);
|
|
822
826
|
if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
|
|
823
827
|
const spans = template.template.templateSpans;
|
|
824
|
-
const holeExprs = spans.map((
|
|
828
|
+
const holeExprs = spans.map((s2) => s2.expression);
|
|
825
829
|
const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
|
|
826
830
|
if (holeIndex === -1) return void 0;
|
|
827
831
|
const roles = /* @__PURE__ */ new Map();
|
|
@@ -870,7 +874,7 @@ function inferParamsIn(ts, program, sf) {
|
|
|
870
874
|
for (const template of templatesIn(ts, sf)) {
|
|
871
875
|
const tpl = template.template;
|
|
872
876
|
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
873
|
-
const holeExprs = tpl.templateSpans.map((
|
|
877
|
+
const holeExprs = tpl.templateSpans.map((s2) => s2.expression);
|
|
874
878
|
const roles = /* @__PURE__ */ new Map();
|
|
875
879
|
holeRoles(parseCached(ts, sf, tpl).root.children, roles);
|
|
876
880
|
for (const [holeIndex, role] of roles) {
|
|
@@ -924,7 +928,7 @@ function contains(outer, inner) {
|
|
|
924
928
|
}
|
|
925
929
|
|
|
926
930
|
// src/props.ts
|
|
927
|
-
function componentPropCompletions(ts, ls, fileName, position) {
|
|
931
|
+
function componentPropCompletions(ts, ls, host, fileName, position) {
|
|
928
932
|
const program = ls.getProgram();
|
|
929
933
|
const sf = program?.getSourceFile(fileName);
|
|
930
934
|
if (!program || !sf) return void 0;
|
|
@@ -932,28 +936,27 @@ function componentPropCompletions(ts, ls, fileName, position) {
|
|
|
932
936
|
if (!template) return void 0;
|
|
933
937
|
const comp = componentTagAtAttrArea(ts, sf, template.template, position);
|
|
934
938
|
if (!comp || !comp.tag) return void 0;
|
|
939
|
+
const span = attrNameSpan(sf.text, position);
|
|
935
940
|
const cf = CONTROL_FLOW_PROPS[comp.tag];
|
|
936
941
|
if (cf) {
|
|
937
942
|
const already2 = new Set(comp.attributeNames);
|
|
938
|
-
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));
|
|
939
944
|
}
|
|
940
945
|
const checker = program.getTypeChecker();
|
|
941
946
|
const rootName = comp.tag.split(".")[0];
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
const compType = checker.getTypeAtLocation(decl);
|
|
945
|
-
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);
|
|
946
949
|
if (!propsType) return void 0;
|
|
947
950
|
const already = new Set(comp.attributeNames);
|
|
948
951
|
const entries = [];
|
|
949
|
-
for (const
|
|
950
|
-
const name =
|
|
952
|
+
for (const p3 of propsType.getProperties()) {
|
|
953
|
+
const name = p3.getName();
|
|
951
954
|
if (name === "children" || already.has(name)) continue;
|
|
952
|
-
entries.push(propEntry(ts, name));
|
|
955
|
+
entries.push(propEntry(ts, name, span));
|
|
953
956
|
}
|
|
954
957
|
return entries.length ? entries : void 0;
|
|
955
958
|
}
|
|
956
|
-
function componentPropHover(ts, ls, fileName, position) {
|
|
959
|
+
function componentPropHover(ts, ls, host, fileName, position) {
|
|
957
960
|
const program = ls.getProgram();
|
|
958
961
|
const sf = program?.getSourceFile(fileName);
|
|
959
962
|
if (!program || !sf) return void 0;
|
|
@@ -968,11 +971,13 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
968
971
|
if (!typeText) return void 0;
|
|
969
972
|
} else if (hit.tag) {
|
|
970
973
|
const checker = program.getTypeChecker();
|
|
971
|
-
const
|
|
972
|
-
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);
|
|
973
977
|
const sym = propsType && propsType.getProperty(hit.prop);
|
|
974
978
|
if (!sym) return void 0;
|
|
975
|
-
|
|
979
|
+
const at = sym.valueDeclaration ?? sym.declarations?.[0] ?? template.template;
|
|
980
|
+
typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, at));
|
|
976
981
|
}
|
|
977
982
|
if (!typeText) return void 0;
|
|
978
983
|
return {
|
|
@@ -999,10 +1004,10 @@ function propNameAt(ts, sf, tpl, position) {
|
|
|
999
1004
|
for (const node of nodes) {
|
|
1000
1005
|
if (node.kind === "Component") {
|
|
1001
1006
|
const c = node;
|
|
1002
|
-
for (const
|
|
1003
|
-
if ((
|
|
1004
|
-
const start =
|
|
1005
|
-
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 };
|
|
1006
1011
|
}
|
|
1007
1012
|
}
|
|
1008
1013
|
}
|
|
@@ -1039,31 +1044,40 @@ var CONTROL_FLOW_PROPS = {
|
|
|
1039
1044
|
Dynamic: ["component"],
|
|
1040
1045
|
Await: ["value", "fallback"]
|
|
1041
1046
|
};
|
|
1042
|
-
function propEntry(ts, name) {
|
|
1043
|
-
return {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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;
|
|
1062
1076
|
}
|
|
1063
1077
|
function firstParamType(ts, checker, type) {
|
|
1064
1078
|
for (const sig of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
|
|
1065
|
-
const
|
|
1066
|
-
if (
|
|
1079
|
+
const p3 = sig.getParameters()[0];
|
|
1080
|
+
if (p3) return checker.getTypeOfSymbolAtLocation(p3, p3.valueDeclaration ?? sig.getDeclaration());
|
|
1067
1081
|
}
|
|
1068
1082
|
return void 0;
|
|
1069
1083
|
}
|
|
@@ -1076,7 +1090,7 @@ function componentTagAtAttrArea(ts, sf, tpl, position) {
|
|
|
1076
1090
|
const c = node;
|
|
1077
1091
|
const openStart = c.loc.start + 1 + (c.tag ? c.tag.length : 0);
|
|
1078
1092
|
const openEnd = firstChildOrCloseStart(c);
|
|
1079
|
-
if (position > openStart && position
|
|
1093
|
+
if (position > openStart && position < openEnd) {
|
|
1080
1094
|
hit = { tag: c.tag, attributeNames: attributeNames(c) };
|
|
1081
1095
|
}
|
|
1082
1096
|
}
|
|
@@ -1094,8 +1108,8 @@ function firstChildOrCloseStart(c) {
|
|
|
1094
1108
|
}
|
|
1095
1109
|
function attributeNames(c) {
|
|
1096
1110
|
const out = [];
|
|
1097
|
-
for (const
|
|
1098
|
-
if (
|
|
1111
|
+
for (const a2 of c.attributes) {
|
|
1112
|
+
if (a2.kind === "Attribute" || a2.kind === "PropertyBinding" || a2.kind === "BindDirective") out.push(a2.name);
|
|
1099
1113
|
}
|
|
1100
1114
|
return out;
|
|
1101
1115
|
}
|
|
@@ -1112,13 +1126,13 @@ function paramMemberCompletions(ts, ls, fileName, position) {
|
|
|
1112
1126
|
const checker = program.getTypeChecker();
|
|
1113
1127
|
const already = /* @__PURE__ */ new Set();
|
|
1114
1128
|
const entries = [];
|
|
1115
|
-
for (const
|
|
1116
|
-
const name =
|
|
1129
|
+
for (const p3 of checker.getApparentType(info.type).getProperties()) {
|
|
1130
|
+
const name = p3.getName();
|
|
1117
1131
|
if (already.has(name) || name.startsWith("__")) continue;
|
|
1118
1132
|
already.add(name);
|
|
1119
1133
|
entries.push({
|
|
1120
1134
|
name,
|
|
1121
|
-
kind: memberKind(ts,
|
|
1135
|
+
kind: memberKind(ts, p3),
|
|
1122
1136
|
kindModifiers: "",
|
|
1123
1137
|
sortText: "0_" + name,
|
|
1124
1138
|
insertText: name
|
|
@@ -1206,8 +1220,8 @@ function elementAtAttributeArea(ts, sf, tpl, position) {
|
|
|
1206
1220
|
const openEnd = firstAttributeEnd(el);
|
|
1207
1221
|
if (position > openStart && position <= openEnd) {
|
|
1208
1222
|
const present = /* @__PURE__ */ new Set();
|
|
1209
|
-
for (const
|
|
1210
|
-
if ("loc" in
|
|
1223
|
+
for (const a2 of el.attributes) {
|
|
1224
|
+
if ("loc" in a2 && a2.loc) present.add(writtenName(sf.text, a2.loc.start));
|
|
1211
1225
|
}
|
|
1212
1226
|
hit = { tag: el.tag, present };
|
|
1213
1227
|
}
|
|
@@ -1226,8 +1240,8 @@ function writtenName(text, start) {
|
|
|
1226
1240
|
}
|
|
1227
1241
|
function firstAttributeEnd(el) {
|
|
1228
1242
|
let end = el.loc.start + 1 + el.tag.length;
|
|
1229
|
-
for (const
|
|
1230
|
-
if ("loc" in
|
|
1243
|
+
for (const a2 of el.attributes) {
|
|
1244
|
+
if ("loc" in a2 && a2.loc) end = Math.max(end, a2.loc.end);
|
|
1231
1245
|
}
|
|
1232
1246
|
return end + 1;
|
|
1233
1247
|
}
|
|
@@ -1242,8 +1256,8 @@ function elementAttributeCompletions(ts, ls, host, fileName, position) {
|
|
|
1242
1256
|
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1243
1257
|
if (!props) return void 0;
|
|
1244
1258
|
const entries = [];
|
|
1245
|
-
for (const
|
|
1246
|
-
const name =
|
|
1259
|
+
for (const p3 of props) {
|
|
1260
|
+
const name = p3.getName();
|
|
1247
1261
|
if (name === "children" || hit.present.has(name)) continue;
|
|
1248
1262
|
entries.push({
|
|
1249
1263
|
name,
|
|
@@ -1261,9 +1275,9 @@ function attributeNameAt(ts, sf, tpl, position) {
|
|
|
1261
1275
|
for (const node of nodes) {
|
|
1262
1276
|
if (node.kind === "Element") {
|
|
1263
1277
|
const el = node;
|
|
1264
|
-
for (const
|
|
1265
|
-
if (!("loc" in
|
|
1266
|
-
const start =
|
|
1278
|
+
for (const a2 of el.attributes) {
|
|
1279
|
+
if (!("loc" in a2) || !a2.loc) continue;
|
|
1280
|
+
const start = a2.loc.start;
|
|
1267
1281
|
const name = writtenName(sf.text, start);
|
|
1268
1282
|
if (name && position >= start && position <= start + name.length) {
|
|
1269
1283
|
hit = { tag: el.tag, name, start };
|
|
@@ -1286,7 +1300,7 @@ function elementAttributeHover(ts, ls, host, fileName, position) {
|
|
|
1286
1300
|
const hit = attributeNameAt(ts, sf, tpl.template, position);
|
|
1287
1301
|
if (!hit) return void 0;
|
|
1288
1302
|
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1289
|
-
const symbol = props?.find((
|
|
1303
|
+
const symbol = props?.find((p3) => p3.getName() === hit.name);
|
|
1290
1304
|
if (!symbol) return void 0;
|
|
1291
1305
|
const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
1292
1306
|
if (!decl) return void 0;
|
|
@@ -1326,7 +1340,7 @@ function accessorDiagnostics(ts, program, sf) {
|
|
|
1326
1340
|
for (const template of templatesIn(ts, sf)) {
|
|
1327
1341
|
const tpl = template.template;
|
|
1328
1342
|
if (ts.isNoSubstitutionTemplateLiteral(tpl)) continue;
|
|
1329
|
-
const holes = tpl.templateSpans.map((
|
|
1343
|
+
const holes = tpl.templateSpans.map((s2) => s2.expression);
|
|
1330
1344
|
const visit = (nodes, parent) => {
|
|
1331
1345
|
for (const node of nodes) {
|
|
1332
1346
|
if (node.kind === "Expression" && parent?.kind === "Element") {
|
|
@@ -1359,7 +1373,7 @@ function isAccessor(ts, checker, node) {
|
|
|
1359
1373
|
const signatures = checker.getTypeAtLocation(node).getCallSignatures();
|
|
1360
1374
|
if (signatures.length !== 1) return false;
|
|
1361
1375
|
const signature = signatures[0];
|
|
1362
|
-
if (signature.getParameters().some((
|
|
1376
|
+
if (signature.getParameters().some((p3) => !isOptional(ts, p3))) return false;
|
|
1363
1377
|
const returned = signature.getReturnType();
|
|
1364
1378
|
return !(returned.flags & (ts.TypeFlags.Void | ts.TypeFlags.Never));
|
|
1365
1379
|
}
|
|
@@ -1436,7 +1450,7 @@ function templateFixAll(ts, ls, fileName, errorCodes) {
|
|
|
1436
1450
|
textChanges.push({ span: { start: at, length: 0 }, newText: "()" });
|
|
1437
1451
|
}
|
|
1438
1452
|
if (textChanges.length === 0) return void 0;
|
|
1439
|
-
textChanges.sort((
|
|
1453
|
+
textChanges.sort((a2, b2) => b2.span.start - a2.span.start);
|
|
1440
1454
|
return { changes: [{ fileName, textChanges }], commands: void 0 };
|
|
1441
1455
|
}
|
|
1442
1456
|
|
|
@@ -1475,12 +1489,12 @@ function closingTagName(text, end, tag) {
|
|
|
1475
1489
|
}
|
|
1476
1490
|
function tagAt(ts, sf, position) {
|
|
1477
1491
|
if (!templateAtCached(ts, sf, position)) return void 0;
|
|
1478
|
-
return tagSpans(ts, sf).find((
|
|
1492
|
+
return tagSpans(ts, sf).find((s2) => position >= s2.start && position <= s2.start + s2.length);
|
|
1479
1493
|
}
|
|
1480
1494
|
function symbolFor(ts, program, host, location, name) {
|
|
1481
1495
|
const checker = program.getTypeChecker();
|
|
1482
1496
|
const flags = ts.SymbolFlags.Value | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Alias;
|
|
1483
|
-
const symbol = checker.getSymbolsInScope(location, flags).find((
|
|
1497
|
+
const symbol = checker.getSymbolsInScope(location, flags).find((s2) => s2.name === name);
|
|
1484
1498
|
if (!symbol) return autoImportedSymbol(ts, program, host, location.getSourceFile(), name);
|
|
1485
1499
|
return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
1486
1500
|
}
|
|
@@ -1605,13 +1619,19 @@ function init(modules) {
|
|
|
1605
1619
|
isGlobalCompletion: false,
|
|
1606
1620
|
isMemberCompletion: false,
|
|
1607
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,
|
|
1608
1628
|
entries
|
|
1609
1629
|
});
|
|
1610
1630
|
const IMPLICIT_ANY = /* @__PURE__ */ new Set([7006, 7044]);
|
|
1611
1631
|
const dedupe = (items) => {
|
|
1612
1632
|
const seen = /* @__PURE__ */ new Set();
|
|
1613
|
-
return items.filter((
|
|
1614
|
-
const key = `${
|
|
1633
|
+
return items.filter((i2) => {
|
|
1634
|
+
const key = `${i2.fileName}:${i2.textSpan.start}:${i2.textSpan.length}`;
|
|
1615
1635
|
if (seen.has(key)) return false;
|
|
1616
1636
|
seen.add(key);
|
|
1617
1637
|
return true;
|
|
@@ -1639,7 +1659,7 @@ function init(modules) {
|
|
|
1639
1659
|
proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
|
|
1640
1660
|
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
1641
1661
|
try {
|
|
1642
|
-
const prop = componentPropHover(ts, ls, fileName, position);
|
|
1662
|
+
const prop = componentPropHover(ts, ls, host, fileName, position);
|
|
1643
1663
|
if (prop) return prop;
|
|
1644
1664
|
} catch {
|
|
1645
1665
|
}
|
|
@@ -1662,7 +1682,7 @@ function init(modules) {
|
|
|
1662
1682
|
};
|
|
1663
1683
|
proxy.getCompletionsAtPosition = (fileName, position, options, formatting) => {
|
|
1664
1684
|
try {
|
|
1665
|
-
const props = componentPropCompletions(ts, ls, fileName, position);
|
|
1685
|
+
const props = componentPropCompletions(ts, ls, host, fileName, position);
|
|
1666
1686
|
if (props) return completionList(props);
|
|
1667
1687
|
} catch {
|
|
1668
1688
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluixi/ts-plugin",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@fluixi/template-parser": "0.1.0-alpha.
|
|
19
|
+
"@fluixi/template-parser": "0.1.0-alpha.3",
|
|
20
|
+
"@fluixi/compiler": "1.0.0-alpha.70"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"esbuild": "^0.24.0",
|