Skip to content

fix: parsing error when use with member expr #249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 24, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/swift-dolphins-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": minor
---

fix: parsing error when use with member expr
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@
"@types/node": "^18.11.0",
"@types/semver": "^7.3.9",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"@typescript-eslint/parser": "~5.43.0",
"benchmark": "^2.1.4",
"chai": "^4.3.4",
"code-red": "^0.2.3",
34 changes: 31 additions & 3 deletions src/ast/html.ts
Original file line number Diff line number Diff line change
@@ -491,12 +491,30 @@ export type SvelteDirective =
| SvelteLetDirective
| SvelteRefDirective
| SvelteTransitionDirective;
export interface SvelteDirectiveKey extends BaseNode {

export type SvelteDirectiveKey =
| SvelteDirectiveKeyTextName
| SvelteDirectiveKeyFunctionName
| SvelteDirectiveKeyForEventHandler
| SvelteDirectiveKeyForAction
| SvelteDirectiveKeyForStyleShorthand;
interface BaseSvelteDirectiveKey<N extends ESTree.Expression | SvelteName>
extends BaseNode {
type: "SvelteDirectiveKey";
name: ESTree.Identifier | SvelteName;
name: N;
modifiers: string[];
parent: SvelteDirective | SvelteStyleDirective;
}
export type SvelteDirectiveKeyTextName = BaseSvelteDirectiveKey<SvelteName>;
export type SvelteDirectiveKeyFunctionName =
BaseSvelteDirectiveKey<ESTree.Identifier>;
export type SvelteDirectiveKeyForEventHandler =
BaseSvelteDirectiveKey<SvelteName>;
export type SvelteDirectiveKeyForAction = BaseSvelteDirectiveKey<
ESTree.Identifier | ESTree.MemberExpression | SvelteName
>;
export type SvelteDirectiveKeyForStyleShorthand =
BaseSvelteDirectiveKey<ESTree.Identifier>;

interface BaseSvelteDirective extends BaseNode {
type: "SvelteDirective";
@@ -506,36 +524,44 @@ interface BaseSvelteDirective extends BaseNode {

export interface SvelteActionDirective extends BaseSvelteDirective {
kind: "Action";
key: SvelteDirectiveKeyForAction;
expression: null | ESTree.Expression;
}
export interface SvelteAnimationDirective extends BaseSvelteDirective {
kind: "Animation";
key: SvelteDirectiveKeyFunctionName;
expression: null | ESTree.Expression;
}
export interface SvelteBindingDirective extends BaseSvelteDirective {
kind: "Binding";
key: SvelteDirectiveKeyTextName;
shorthand: boolean;
expression: null | ESTree.Expression;
}
export interface SvelteClassDirective extends BaseSvelteDirective {
kind: "Class";
key: SvelteDirectiveKeyTextName;
shorthand: boolean;
expression: null | ESTree.Expression;
}
export interface SvelteEventHandlerDirective extends BaseSvelteDirective {
kind: "EventHandler";
key: SvelteDirectiveKeyForEventHandler;
expression: null | ESTree.Expression;
}
export interface SvelteLetDirective extends BaseSvelteDirective {
kind: "Let";
key: SvelteDirectiveKeyTextName;
expression: null | ESTree.Pattern;
}
export interface SvelteRefDirective extends BaseSvelteDirective {
kind: "Ref";
key: SvelteDirectiveKeyTextName;
expression: null | ESTree.Expression;
}
export interface SvelteTransitionDirective extends BaseSvelteDirective {
kind: "Transition";
key: SvelteDirectiveKeyFunctionName;
intro: boolean;
outro: boolean;
expression: null | ESTree.Expression;
@@ -547,16 +573,18 @@ export type SvelteStyleDirective =
| SvelteStyleDirectiveLongform;
interface BaseSvelteStyleDirective extends BaseNode {
type: "SvelteStyleDirective";
key: SvelteDirectiveKey;
key: SvelteDirectiveKeyTextName | SvelteDirectiveKeyForStyleShorthand;
value: (SvelteLiteral | SvelteMustacheTagText)[];
parent: SvelteStartTag;
}
export interface SvelteStyleDirectiveShorthand
extends BaseSvelteStyleDirective {
key: SvelteDirectiveKeyForStyleShorthand;
shorthand: true;
value: [];
}
export interface SvelteStyleDirectiveLongform extends BaseSvelteStyleDirective {
key: SvelteDirectiveKeyTextName;
shorthand: false;
value: (SvelteLiteral | SvelteMustacheTagText)[];
}
80 changes: 59 additions & 21 deletions src/parser/converts/attr.ts
Original file line number Diff line number Diff line change
@@ -369,7 +369,7 @@ function convertStyleDirective(
parent: SvelteStyleDirective["parent"],
ctx: Context
): SvelteStyleDirective {
const directive: SvelteStyleDirective = {
const directive: SvelteStyleDirectiveLongform = {
type: "SvelteStyleDirective",
key: null as any,
shorthand: false,
@@ -379,20 +379,27 @@ function convertStyleDirective(
};
processDirectiveKey(node, directive, ctx);

const keyName = directive.key.name as SvelteName;
const keyName = directive.key.name;
if (node.value === true) {
(directive as unknown as SvelteStyleDirectiveShorthand).shorthand = true;
ctx.scriptLet.addExpression(keyName, directive.key, null, (expression) => {
if (expression.type !== "Identifier") {
throw new ParseError(
`Expected JS identifier or attribute value.`,
expression.range![0],
ctx
);
const shorthandDirective =
directive as unknown as SvelteStyleDirectiveShorthand;
shorthandDirective.shorthand = true;
ctx.scriptLet.addExpression(
keyName,
shorthandDirective.key,
null,
(expression) => {
if (expression.type !== "Identifier") {
throw new ParseError(
`Expected JS identifier or attribute value.`,
expression.range![0],
ctx
);
}
shorthandDirective.key.name = expression;
}
directive.key.name = expression;
});
return directive;
);
return shorthandDirective;
}
ctx.addToken("HTMLIdentifier", {
start: keyName.range[0],
@@ -426,7 +433,13 @@ function convertTransitionDirective(
ctx,
null
),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key),
processName: (name) =>
ctx.scriptLet.addExpression(
name,
directive.key,
null,
buildExpressionTypeChecker(["Identifier"], ctx)
),
});
return directive;
}
@@ -451,7 +464,13 @@ function convertAnimationDirective(
ctx,
null
),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key),
processName: (name) =>
ctx.scriptLet.addExpression(
name,
directive.key,
null,
buildExpressionTypeChecker(["Identifier"], ctx)
),
});
return directive;
}
@@ -476,7 +495,13 @@ function convertActionDirective(
ctx,
null
),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key),
processName: (name) =>
ctx.scriptLet.addExpression(
name,
directive.key,
null,
buildExpressionTypeChecker(["Identifier", "MemberExpression"], ctx)
),
});
return directive;
}
@@ -529,7 +554,7 @@ type DirectiveProcessors<
processPattern?: undefined;
processName?: (
expression: SvelteName
) => ScriptLetCallback<ESTree.Identifier>[];
) => ScriptLetCallback<Exclude<S["key"]["name"], SvelteName>>[];
}
| {
processExpression?: undefined;
@@ -539,7 +564,7 @@ type DirectiveProcessors<
) => ScriptLetCallback<NonNullable<E>>[];
processName?: (
expression: SvelteName
) => ScriptLetCallback<ESTree.Identifier>[];
) => ScriptLetCallback<Exclude<S["key"]["name"], SvelteName>>[];
};

/** Common process for directive */
@@ -658,9 +683,6 @@ function processDirectiveExpression<
if (!shorthand) {
if (processors.processName) {
processors.processName(keyName).push((es) => {
if (es.type !== "Identifier") {
throw new ParseError(`Expected JS identifier.`, es.range![0], ctx);
}
key.name = es;
});
} else {
@@ -682,3 +704,19 @@ function buildProcessExpressionForExpression(
return ctx.scriptLet.addExpression(expression, directive, typing);
};
}

/** Build expression type checker to script let callbacks */
function buildExpressionTypeChecker<T extends ESTree.Expression>(
expected: T["type"][],
ctx: Context
): ScriptLetCallback<T> {
return (node) => {
if (!expected.includes(node.type)) {
throw new ParseError(
`Expected JS ${expected.join(", or ")}, but ${node.type} found.`,
node.range![0],
ctx
);
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
import Inner from './Inner.svelte'
const foo = { bar: () => alert('foo.bar') }
</script>

<Inner on:foo.bar/> <!-- bubble (not member) -->

<!-- https://svelte.dev/repl/d6f31e9c5b784f8bb6bc3abd2c7153a7?version=3.52.0 -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"ruleId": "no-unused-vars",
"code": "foo",
"line": 3,
"column": 8
}
]
Loading