Skip to content
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

Refactor machine extractor so it exposes path, as well as node, when traversing #174

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 2 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
{
"editor.insertSpaces": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
}
}
"typescript.tsdk": "node_modules/typescript/lib"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"extractor": "yarn workspace @xstate/machine-extractor",
"server": "yarn workspace @xstate/vscode-server",
"client": "yarn workspace stately-vscode",
"🍿": "yarn workspace @xstate/recast-experiment",
"shared": "yarn workspace @xstate/tools-shared",
"prepare": "husky install",
"postinstall": "preconstruct dev && manypkg check",
Expand Down
98 changes: 55 additions & 43 deletions packages/machine-extractor/src/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { types as t } from "@babel/core";
import { NodePath, types as t } from "@babel/core";
import { Action, ChooseCondition } from "xstate";
import { assign, choose, forwardTo, send } from "xstate/lib/actions";
import { Cond, CondNode } from "./conds";
Expand Down Expand Up @@ -33,6 +33,7 @@ import { wrapParserResult } from "./wrapParserResult";

export interface ActionNode {
node: t.Node;
path: NodePath<t.Node>;
action: Action<any, any>;
name: string;
chooseConditions?: ParsedChooseCondition[];
Expand All @@ -49,63 +50,67 @@ export interface ParsedChooseCondition {
export const ActionAsIdentifier = maybeTsAsExpression(
createParser({
babelMatcher: t.isIdentifier,
parseNode: (node, context): ActionNode => {
parsePath: (path, context): ActionNode => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is being touched anyway - I'd rename this here. Perhaps collect? extract? I ain't sure how to name what we return here since it's pretty generic hence I propose a verb-only name.

return {
action: node.name,
node,
name: node.name,
action: path.node.name,
node: path.node,
path,
name: path.node.name,
declarationType: "identifier",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}),
})
);

export const ActionAsFunctionExpression = maybeTsAsExpression(
maybeIdentifierTo(
createParser({
babelMatcher: isFunctionOrArrowFunctionExpression,
parseNode: (node, context): ActionNode => {
parsePath: (path, context): ActionNode => {
const action = function actions() {};
const id = context.getNodeHash(node);
const id = context.getNodeHash(path.node);

action.toJSON = () => id;
return {
node,
node: path.node,
path,
action,
name: "",
declarationType: "inline",
inlineDeclarationId: id,
};
},
}),
),
})
)
);

export const ActionAsString = maybeTsAsExpression(
maybeIdentifierTo(
createParser({
babelMatcher: t.isStringLiteral,
parseNode: (node, context): ActionNode => {
parsePath: (path, context): ActionNode => {
return {
action: node.value,
node,
name: node.value,
path,
action: path.node.value,
node: path.node,
name: path.node.value,
declarationType: "named",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}),
),
})
)
);

export const ActionAsNode = createParser({
babelMatcher: t.isNode,
parseNode: (node, context): ActionNode => {
const id = context.getNodeHash(node);
parsePath: (path, context): ActionNode => {
const id = context.getNodeHash(path.node);
return {
path,
action: id,
node,
node: path.node,
name: "",
declarationType: "unknown",
inlineDeclarationId: id,
Expand All @@ -120,12 +125,12 @@ const ChooseFirstArg = arrayOf(
// too recursive
// TODO - fix
actions: maybeArrayOf(ActionAsString),
}),
})
);

export const ChooseAction = wrapParserResult(
namedFunctionCall("choose", ChooseFirstArg),
(result, node, context): ActionNode => {
(result, path, context): ActionNode => {
const conditions: ParsedChooseCondition[] = [];

result.argument1Result?.forEach((arg1Result) => {
Expand Down Expand Up @@ -153,34 +158,37 @@ export const ChooseAction = wrapParserResult(
});

return {
node: node,
path,
node: path.node,
action: choose(conditions.map((condition) => condition.condition)),
chooseConditions: conditions,
name: "",
declarationType: "inline",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}
);

interface AssignFirstArg {
path: NodePath<t.Node>;
node: t.Node;
value: {} | (() => {});
}

const AssignFirstArgObject = createParser({
babelMatcher: t.isObjectExpression,
parseNode: (node, context) => {
parsePath: (path, context) => {
return {
node,
path,
node: path.node,
value: {},
};
},
});

const AssignFirstArgFunction = createParser({
babelMatcher: isFunctionOrArrowFunctionExpression,
parseNode: (node, context) => {
parsePath: (path, context) => {
const value = function anonymous() {
return {};
};
Expand All @@ -189,7 +197,8 @@ const AssignFirstArgFunction = createParser({
};

return {
node,
path,
node: path.node,
value,
};
},
Expand All @@ -202,7 +211,7 @@ const AssignFirstArg = unionType<AssignFirstArg>([

export const AssignAction = wrapParserResult(
namedFunctionCall("assign", AssignFirstArg),
(result, node, context): ActionNode => {
(result, path, context): ActionNode => {
const defaultAction = function anonymous() {
return {};
};
Expand All @@ -211,13 +220,14 @@ export const AssignAction = wrapParserResult(
};

return {
path,
node: result.node,
action: assign(result.argument1Result?.value || defaultAction),
name: "",
declarationType: "inline",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}
);

export const SendActionSecondArg = objectTypeWithKnownKeys({
Expand All @@ -233,10 +243,11 @@ export const SendAction = wrapParserResult(
namedFunctionCall(
"send",
unionType<{ node: t.Node; value?: string }>([StringLiteral, AnyNode]),
SendActionSecondArg,
SendActionSecondArg
),
(result, node, context): ActionNode => {
(result, path, context): ActionNode => {
return {
path,
node: result.node,
name: "",
action: send(
Expand All @@ -250,12 +261,12 @@ export const SendAction = wrapParserResult(
id: result.argument2Result?.id?.value,
to: result.argument2Result?.to?.value,
delay: result.argument2Result?.delay?.value,
},
}
),
declarationType: "inline",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}
);

export const ForwardToActionSecondArg = objectTypeWithKnownKeys({
Expand All @@ -264,17 +275,18 @@ export const ForwardToActionSecondArg = objectTypeWithKnownKeys({

export const ForwardToAction = wrapParserResult(
namedFunctionCall("forwardTo", StringLiteral, ForwardToActionSecondArg),
(result, node, context): ActionNode => {
(result, path, context): ActionNode => {
return {
node: result.node,
action: forwardTo(result.argument1Result?.value || "", {
to: result.argument2Result?.to?.value,
}),
path,
name: "",
declarationType: "inline",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
}
);

const NamedAction = unionType([
Expand Down Expand Up @@ -306,5 +318,5 @@ const BasicAction = unionType([
export const ArrayOfBasicActions = maybeArrayOf(BasicAction);

export const MaybeArrayOfActions = maybeArrayOf(
unionType([NamedAction, BasicAction]),
unionType([NamedAction, BasicAction])
);
35 changes: 20 additions & 15 deletions packages/machine-extractor/src/conds.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { types as t } from "@babel/core";
import { NodePath, types as t } from "@babel/core";
import { Condition } from "xstate";
import { DeclarationType } from ".";
import { createParser } from "./createParser";
Expand All @@ -7,6 +7,7 @@ import { isFunctionOrArrowFunctionExpression } from "./utils";

export interface CondNode {
node: t.Node;
path: NodePath<t.Node>;
name: string;
cond: Condition<any, any>;
declarationType: DeclarationType;
Expand All @@ -15,38 +16,41 @@ export interface CondNode {

const CondAsFunctionExpression = createParser({
babelMatcher: isFunctionOrArrowFunctionExpression,
parseNode: (node, context): CondNode => {
parsePath: (path, context): CondNode => {
return {
node,
path,
node: path.node,
name: "",
cond: () => {
return false;
},
declarationType: "inline",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
});

const CondAsStringLiteral = createParser({
babelMatcher: t.isStringLiteral,
parseNode: (node, context): CondNode => {
parsePath: (path, context): CondNode => {
return {
node,
name: node.value,
cond: node.value,
path,
node: path.node,
name: path.node.value,
cond: path.node.value,
declarationType: "named",
inlineDeclarationId: context.getNodeHash(node),
inlineDeclarationId: context.getNodeHash(path.node),
};
},
});

const CondAsNode = createParser({
babelMatcher: t.isNode,
parseNode: (node, context): CondNode => {
const id = context.getNodeHash(node);
parsePath: (path, context): CondNode => {
const id = context.getNodeHash(path.node);
return {
node,
path,
node: path.node,
name: "",
cond: id,
declarationType: "unknown",
Expand All @@ -57,10 +61,11 @@ const CondAsNode = createParser({

const CondAsIdentifier = createParser({
babelMatcher: t.isIdentifier,
parseNode: (node, context): CondNode => {
const id = context.getNodeHash(node);
parsePath: (path, context): CondNode => {
const id = context.getNodeHash(path.node);
return {
node,
path,
node: path.node,
name: "",
cond: id,
declarationType: "identifier",
Expand Down
12 changes: 6 additions & 6 deletions packages/machine-extractor/src/createParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { types as t } from "@babel/core";
import { NodePath, types as t } from "@babel/core";
import { Parser, ParserContext } from "./types";

/**
Expand All @@ -7,14 +7,14 @@ import { Parser, ParserContext } from "./types";
*/
export const createParser = <T extends t.Node, Result>(params: {
babelMatcher: (node: any) => node is T;
parseNode: (node: T, context: ParserContext) => Result;
parsePath: (path: NodePath<T>, context: ParserContext) => Result;
}): Parser<T, Result> => {
const matches = (node: T) => {
const matches = (node: t.Node) => {
return params.babelMatcher(node);
};
const parse = (node: any, context: ParserContext): Result | undefined => {
if (!matches(node)) return undefined;
return params.parseNode(node, context);
const parse = (path: any, context: ParserContext): Result | undefined => {
if (!matches(path?.node)) return undefined;
return params.parsePath(path, context);
};
return {
parse,
Expand Down
Loading