Skip to content

Commit 53ddfcc

Browse files
committed
refactor: remove the need of having adapters for parser (yagni)
1 parent a37f9d6 commit 53ddfcc

File tree

6 files changed

+160
-203
lines changed

6 files changed

+160
-203
lines changed

packages/core/src/index.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
import { readFileSync } from "fs";
22

33
import { scan } from "./scanner";
4+
import { createItem } from "./entities/item";
45
import type { Item } from "./entities/item";
56
import { parse } from "./parser";
7+
import { createLocation } from "./entities/location";
68

79
export const esonar = async () => {
810
const projects = scan();
911
const items: Item[] = [];
1012

1113
for (const project of projects) {
1214
for (const file of project.files) {
13-
const content = readFileSync(file, "utf-8");
15+
const code = readFileSync(file, "utf-8");
1416
const module = project.metadata.name;
1517

16-
const projectItems = await parse(content, {
17-
file,
18-
module,
18+
await parse(code, (item) => {
19+
items.push(
20+
createItem({
21+
...item,
22+
location: createLocation({
23+
code,
24+
file,
25+
module,
26+
offset: item.offset,
27+
}),
28+
}),
29+
);
1930
});
20-
21-
items.push(...projectItems);
2231
}
2332
}
2433

packages/core/src/parser/adapters/swc.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.

packages/core/src/parser/helpers.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

packages/core/src/parser/index.ts

Lines changed: 143 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,156 @@
1+
import type {
2+
ImportDeclaration,
3+
JSXAttrValue,
4+
JSXOpeningElement,
5+
Module,
6+
TsType,
7+
} from "@swc/core";
8+
import { parse as swcParse } from "@swc/core";
9+
import { walk } from "astray";
10+
111
import type { Import } from "../entities/import";
212
import type { Item } from "../entities/item";
3-
import { createItem } from "../entities/item";
4-
import type { Location } from "../entities/location";
5-
import { createLocation } from "../entities/location";
13+
import type { Primitive } from "../types";
14+
15+
export const parse = async (
16+
code: string,
17+
addCallback: (
18+
item: Pick<Item, "args" | "module" | "name" | "type"> & {
19+
offset: number;
20+
},
21+
) => void,
22+
) => {
23+
const imports = new Map<Import["alias"], Import>();
624

7-
import { parser } from "./adapters/swc";
25+
const ast = await swcParse(code, {
26+
syntax: "typescript",
27+
tsx: true,
28+
});
829

9-
type Context = Pick<Location, "file" | "module">;
30+
/**
31+
* Traverse method using the visitor design pattern.
32+
* SWC traverser (`import { Visitor } from "@swc/core/Visitor"`) is not used since it doesn't traverse recursively nodes
33+
* (at least, recursive `JSXOpeningElement`s are not retrieved)
34+
*/
35+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
36+
walk<Module, void, AST>(ast, {
37+
ImportDeclaration(node) {
38+
const module = node.source.value;
1039

11-
export const parse = async (code: string, context: Context) => {
12-
const items: Item[] = [];
13-
const imports = new Map<Import["alias"], Import>();
40+
node.specifiers.forEach((specifier) => {
41+
const specifierValue = specifier.local.value;
1442

15-
await parser.execute(code, {
16-
onAddingItem: ({ name, args, module, offset, type }) => {
17-
items.push(
18-
createItem({
19-
name,
20-
args,
21-
location: createLocation({
22-
code,
23-
file: context.file,
24-
module: context.module,
25-
offset,
26-
}),
43+
imports.set(specifierValue, {
44+
name:
45+
// @ts-expect-error `imported` field is exposed by `ImportSpecifier` node (@todo: fix the typing issue in @swc/core)
46+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
47+
(specifier.imported?.value || specifierValue) as string,
48+
alias: specifierValue,
2749
module,
28-
type,
29-
}),
30-
);
50+
});
51+
});
3152
},
32-
onGettingImport(name) {
33-
return imports.get(name);
53+
JSXOpeningElement(node) {
54+
if (node.name.type !== "Identifier") return;
55+
56+
const name = node.name.value;
57+
const importMetadata = imports.get(name);
58+
59+
if (!importMetadata) return;
60+
61+
addCallback({
62+
name: importMetadata.name,
63+
args: {
64+
data: node.attributes.reduce<Record<string, unknown>>(
65+
(props, prop) => {
66+
if (
67+
prop.type !== "JSXAttribute" ||
68+
prop.name.type !== "Identifier"
69+
)
70+
return props;
71+
72+
props[prop.name.value] = getLiteralValue(
73+
prop.value,
74+
);
75+
76+
return props;
77+
},
78+
{},
79+
),
80+
isSpread: false,
81+
},
82+
module: importMetadata.module,
83+
offset: node.span.start,
84+
type: "component",
85+
});
3486
},
35-
onSettingImport(data) {
36-
imports.set(data.alias, data);
87+
TsType(node) {
88+
let typeValue = "";
89+
90+
if (
91+
node.type === "TsTypeReference" &&
92+
node.typeName.type === "Identifier"
93+
) {
94+
typeValue = node.typeName.value;
95+
}
96+
97+
if (
98+
node.type === "TsIndexedAccessType" &&
99+
node.objectType.type === "TsTypeReference" &&
100+
node.objectType.typeName.type === "Identifier"
101+
) {
102+
typeValue = node.objectType.typeName.value;
103+
}
104+
105+
const importMetadata = imports.get(typeValue);
106+
107+
if (!importMetadata) return;
108+
109+
addCallback({
110+
name: importMetadata.name,
111+
module: importMetadata.module,
112+
offset: node.span.start,
113+
type: "type",
114+
});
37115
},
38116
});
117+
};
118+
119+
type AST = {
120+
ImportDeclaration: ImportDeclaration;
121+
JSXOpeningElement: JSXOpeningElement;
122+
TsType: TsType;
123+
};
124+
125+
/**
126+
* Helper to unify the way unknown AST token are persisted
127+
* @param token AST token value
128+
* @returns Formatted AST token
129+
*/
130+
const getUnknownValue = (token: string) => `#${token}`;
131+
132+
const getLiteralValue = (node: JSXAttrValue | undefined): Primitive => {
133+
if (!node) {
134+
return true;
135+
}
136+
137+
if (node.type === "NullLiteral") {
138+
return null;
139+
}
140+
141+
if (
142+
node.type === "StringLiteral" ||
143+
node.type === "NumericLiteral" ||
144+
node.type === "BigIntLiteral" ||
145+
node.type === "BooleanLiteral" ||
146+
node.type === "JSXText"
147+
) {
148+
return node.value;
149+
}
150+
151+
if (node.type === "JSXExpressionContainer") {
152+
return getLiteralValue(node.expression as JSXAttrValue);
153+
}
39154

40-
return items;
155+
return getUnknownValue(node.type);
41156
};

packages/core/src/parser/types.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)