Skip to content

Commit

Permalink
refactor: use typescript as parser instead of babel parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ssssota committed Sep 16, 2023
1 parent 7639415 commit eb3db6e
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 56 deletions.
3 changes: 1 addition & 2 deletions packages/vite-plugin-doctest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,13 @@
"devDependencies": {
"remark-parse": "^10.0.2",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"unified": "^10.1.2",
"vite": "^4.4.9",
"vitest": "^0.34.0"
},
"dependencies": {
"@babel/parser": "^7.22.16",
"magic-string": "^0.30.3",
"typescript": "^4.0.0 || ^5.0.0",
"vite": "^4.0.0 || ^5.0.0-0"
}
}
52 changes: 41 additions & 11 deletions packages/vite-plugin-doctest/src/transformers/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { parse } from "@babel/parser";
import MagicString from "magic-string";
import { extractCode, extractTestComment, vitestExports } from "./utils";
import typescript from "typescript";
import { extractCode, vitestExports } from "./utils";

export function transform(code: string, id: string) {
const comments =
parse(code, {
plugins: id.match(/\.[cm]?tsx?$/) ? ["typescript"] : [],
sourceType: "module",
}).comments ?? [];
const tests = comments
.filter((c) => c.type === "CommentBlock" && c.value.startsWith("*"))
.flatMap((c) => extractTestComment(c.value))
.flatMap(extractCode);
const node = typescript.createSourceFile(
id,
code,
typescript.ScriptTarget.ESNext,
);
const jsDocs = findJSDoc(node);
const tests = jsDocs.flatMap(extractTestComment).flatMap(extractCode);
if (tests.length === 0) return code;

const s = new MagicString(code);
Expand All @@ -36,3 +34,35 @@ export function transform(code: string, id: string) {
map: s.generateMap({ hires: true, source: id }),
};
}

function findJSDoc(node: typescript.Node): typescript.JSDoc[] {
const nodes: typescript.JSDoc[] = [];
// HACK: typescript.Node has no jsDoc property
if ("jsDoc" in node && Array.isArray(node.jsDoc) && node.jsDoc.length > 0) {
nodes.push(
...node.jsDoc.flatMap((doc) => (typescript.isJSDoc(doc) ? [doc] : [])),
);
}
node.forEachChild((child) => nodes.push(...findJSDoc(child)));
return nodes;
}

function extractTestComment(jsDoc: typescript.JSDoc): string[] {
return (
jsDoc.tags?.flatMap((tag) => {
if (tag.comment == null) return [];
const commentText =
typeof tag.comment === "string"
? tag.comment
: tag.comment.map((c) => c.text).join("\n");
// tagName should be `import.meta.vitest`. But it splits into `import` and `.meta.vitest`.
if (
tag.tagName.text === "import" &&
commentText.startsWith(".meta.vitest")
) {
return [commentText];
}
return [];
}) ?? []
);
}
8 changes: 0 additions & 8 deletions packages/vite-plugin-doctest/src/transformers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ export const vitestExports = [
"vitest",
];

export function extractTestComment(jsDoc: string): string[] {
const testTagMatcher = /^import\.meta\.vitest\b/;
if (!jsDoc.startsWith("*")) return [];
const tags = jsDoc.replace(/^[ \t]*\*[ \t]*/gm, "").split(/\n@/g).slice(1);
const testTags = tags.filter((tag) => tag.match(testTagMatcher));
return testTags.map((tagComment) => tagComment.replace(testTagMatcher, ""));
}

type TestCode = {
name?: string;
code: string;
Expand Down
105 changes: 70 additions & 35 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit eb3db6e

Please sign in to comment.