Skip to content
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
24 changes: 24 additions & 0 deletions extensions/ext-parser-babel/src/parser-only.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ describe("BabelParseOnlyParser", () => {
assertEquals(decorated.type, "File");
});

it("parses compiled JSX under a Markdown or MDX path", async () => {
// Markdown and MDX reach the parser as compiled JSX. Choosing the Babel
// plugins from the authored extension would leave JSX off and the markup
// would parse as a regular expression.
const compiled = "export default function MDXContent() { return <h1>Title</h1>; }";

const parsed = await Promise.all(
["page.mdx", "page.md", "page.MDX"].map((filePath) =>
parser.parse({ code: compiled, filePath })
),
);

assertEquals(parsed.map((ast) => ast.type), ["File", "File", "File"]);
});

it("keeps `<T>x` a type assertion for a `.ts` path", async () => {
const asserted = await parser.parse({
code: "const value = <string> input;",
filePath: "module.ts",
});

assertEquals(asserted.type, "File");
});

it("preserves Babel syntax-error identity and location metadata", async () => {
let thrown: unknown;
try {
Expand Down
17 changes: 16 additions & 1 deletion extensions/ext-parser-babel/src/parser-only.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ export interface BabelParseOnlyParserContract {
parse(options: ParseOptions): Promise<ASTNode>;
}

/**
* The path the plugin choice reasons about.
*
* Markdown and MDX can reach this parser as compiled JSX, and the authored
* `.md` or `.mdx` extension would switch JSX off, so the emitted markup parses
* as a regular expression and throws "Unterminated regular expression". Map
* them onto a `.tsx` path for the plugin choice only. Nothing else reads this
* value, and `.ts` keeps `<T>x` a type assertion because only Markdown paths
* are rewritten.
*/
function parseablePath(filePath?: string): string | undefined {
if (filePath === undefined) return undefined;
return filePath.replace(/\.mdx?$/i, ".tsx");
}

function pickPlugins(filePath?: string): parser.ParserPlugin[] {
const normalizedPath = filePath?.toLowerCase() ?? "";
const supportsJsx = !filePath ||
Expand Down Expand Up @@ -53,7 +68,7 @@ export class BabelParseOnlyParser implements BabelParseOnlyParserContract {
sourceType: "unambiguous",
allowReturnOutsideFunction: options.allowReturnOutsideFunction === true ||
/\.(?:cjs|js)$/.test(filePath),
plugins: pickPlugins(options.filePath),
plugins: pickPlugins(parseablePath(options.filePath)),
});
const node: { type: string } = ast;
return Promise.resolve(node as ASTNode);
Expand Down
Loading