Skip to content

Commit

Permalink
feat: improve props type (#435)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Nov 21, 2023
1 parent 0ef067b commit 7508680
Show file tree
Hide file tree
Showing 42 changed files with 13,584 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-cooks-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": minor
---

feat: improve props type
95 changes: 81 additions & 14 deletions src/parser/converts/attr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import { ParseError } from "../../errors";
import type { ScriptLetCallback } from "../../context/script-let";
import type { AttributeToken } from "../html";
import { svelteVersion } from "../svelte-version";

/** Convert for Attributes */
export function* convertAttributes(
Expand Down Expand Up @@ -200,6 +201,7 @@ function convertAttribute(
processAttributeValue(
node.value as (SvAST.Text | SvAST.MustacheTag)[],
attribute,
parent,
ctx,
);

Expand All @@ -213,27 +215,51 @@ function convertAttribute(
function processAttributeValue(
nodeValue: (SvAST.Text | SvAST.MustacheTag)[],
attribute: SvelteAttribute | SvelteStyleDirectiveLongform,
attributeParent: (SvelteAttribute | SvelteStyleDirectiveLongform)["parent"],
ctx: Context,
) {
for (let index = 0; index < nodeValue.length; index++) {
const v = nodeValue[index];
if (v.type === "Text") {
if (v.start === v.end) {
// Empty
const nodes = nodeValue
.filter(
(v) =>
v.type !== "Text" ||
// ignore empty
// https://github.com/sveltejs/svelte/pull/6539
continue;
}
const next = nodeValue[index + 1];
if (next && next.start < v.end) {
// Maybe bug in Svelte can cause the completion index to shift.
// console.log(ctx.getText(v), v.data)
v.end = next.start;
v.start < v.end,
)
.map((v, index, array) => {
if (v.type === "Text") {
const next = array[index + 1];
if (next && next.start < v.end) {
// Maybe bug in Svelte can cause the completion index to shift.
return {
...v,
end: next.start,
};
}
}
return v;
});
if (
nodes.length === 1 &&
nodes[0].type === "MustacheTag" &&
attribute.type === "SvelteAttribute"
) {
const typing = buildAttributeType(
attributeParent.parent,
attribute.key.name,
ctx,
);
const mustache = convertMustacheTag(nodes[0], attribute, typing, ctx);
attribute.value.push(mustache);
return;
}
for (const v of nodes) {
if (v.type === "Text") {
attribute.value.push(convertTextToLiteral(v, attribute, ctx));
continue;
}
if (v.type === "MustacheTag") {
const mustache = convertMustacheTag(v, attribute, ctx);
const mustache = convertMustacheTag(v, attribute, null, ctx);
attribute.value.push(mustache);
continue;
}
Expand All @@ -246,6 +272,47 @@ function processAttributeValue(
}
}

/** Build attribute type */
function buildAttributeType(
element: SvelteElement | SvelteScriptElement | SvelteStyleElement,
attrName: string,
ctx: Context,
) {
if (
svelteVersion.gte(5) &&
attrName.startsWith("on") &&
(element.type !== "SvelteElement" || element.kind === "html")
) {
return buildEventHandlerType(element, attrName.slice(2), ctx);
}
if (element.type !== "SvelteElement" || element.kind !== "component") {
return null;
}
const elementName = ctx.elements.get(element)!.name;
const componentPropsType = `import('svelte').ComponentProps<${elementName}>`;
return conditional({
check: `'${attrName}'`,
extends: `infer PROP`,
true: conditional({
check: `PROP`,
extends: `keyof ${componentPropsType}`,
true: `${componentPropsType}[PROP]`,
false: `never`,
}),
false: `never`,
});

/** Generate `C extends E ? T : F` type. */
function conditional(types: {
check: string;
extends: string;
true: string;
false: string;
}) {
return `${types.check} extends ${types.extends}?(${types.true}):(${types.false})`;
}
}

/** Convert for Spread */
function convertSpreadAttribute(
node: SvAST.Spread,
Expand Down Expand Up @@ -491,7 +558,7 @@ function convertStyleDirective(
end: keyName.range[1],
});

processAttributeValue(node.value, directive, ctx);
processAttributeValue(node.value, directive, parent, ctx);

return directive;
}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/converts/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export function* convertChildren(
continue;
}
if (child.type === "MustacheTag") {
yield convertMustacheTag(child, parent, ctx);
yield convertMustacheTag(child, parent, null, ctx);
continue;
}
if (child.type === "RawMustacheTag") {
Expand Down
7 changes: 5 additions & 2 deletions src/parser/converts/mustache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import type * as SvAST from "../svelte-ast-types";
export function convertMustacheTag(
node: SvAST.MustacheTag,
parent: SvelteMustacheTag["parent"],
typing: string | null,
ctx: Context,
): SvelteMustacheTagText {
return convertMustacheTag0(node, "text", parent, ctx);
return convertMustacheTag0(node, "text", parent, typing, ctx);
}
/** Convert for MustacheTag */
export function convertRawMustacheTag(
Expand All @@ -24,6 +25,7 @@ export function convertRawMustacheTag(
node,
"raw",
parent,
null,
ctx,
);
const atHtmlStart = ctx.code.indexOf("@html", mustache.range[0]);
Expand Down Expand Up @@ -64,6 +66,7 @@ function convertMustacheTag0<T extends SvelteMustacheTag>(
node: SvAST.MustacheTag | SvAST.RawMustacheTag,
kind: T["kind"],
parent: T["parent"],
typing: string | null,
ctx: Context,
): T {
const mustache = {
Expand All @@ -73,7 +76,7 @@ function convertMustacheTag0<T extends SvelteMustacheTag>(
parent,
...ctx.getConvertLocation(node),
} as T;
ctx.scriptLet.addExpression(node.expression, mustache, null, (es) => {
ctx.scriptLet.addExpression(node.expression, mustache, typing, (es) => {
mustache.expression = es;
});
return mustache;
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/parser/ast/svelte5/ts-event01-input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import Component from 'foo.svelte'
</script>
<button onclick="{e=>{}}"></button>
<Component onclick="{e=>{}}"></Component>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"ruleId": "no-unused-vars",
"code": "e",
"line": 4,
"column": 19
},
{
"ruleId": "no-unused-vars",
"code": "e",
"line": 5,
"column": 22
}
]
Loading

0 comments on commit 7508680

Please sign in to comment.