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
Show file tree
Hide file tree
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
Expand Up @@ -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",
Expand Down
34 changes: 31 additions & 3 deletions src/ast/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand All @@ -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)[];
}
Expand Down
80 changes: 59 additions & 21 deletions src/parser/converts/attr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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],
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -529,7 +554,7 @@ type DirectiveProcessors<
processPattern?: undefined;
processName?: (
expression: SvelteName
) => ScriptLetCallback<ESTree.Identifier>[];
) => ScriptLetCallback<Exclude<S["key"]["name"], SvelteName>>[];
}
| {
processExpression?: undefined;
Expand All @@ -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 */
Expand Down Expand Up @@ -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 {
Expand All @@ -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