fix(javascript): parse JavaScript sources with JavaScript grammar (#3782)

parserPluginsForPath enabled Babel's typescript plugin for .js, .jsx,
.mjs and .cjs sources. TypeScript reads some valid JavaScript
differently: a conditional whose consequent is an arrow with a
parenthesized body (c?x=>(a):y=>(b)) throws as a return-type
annotation, and a<b>(c) becomes a generic call. VS Code's 20 MB
workbench bundles therefore returned no AST at all.

Select JavaScript grammar (JSX plus legacy decorators) for explicit
JavaScript extensions. TypeScript dialects and unnamed sources keep the
existing TypeScript-capable contract.

Fixes #3780
Refs #3670

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822
2026-10-14 18:12:30 -07:00
committed by GitHub
parent 59f528f0e5
commit 44b197f924
3 changed files with 53 additions and 4 deletions
@@ -0,0 +1,42 @@
import { expect, it } from "vitest";
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
import { analyzeParsedJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
const analyze = (source: string, path: string) => {
const parsed = parseJavaScriptSource(source, path);
return parsed === null
? null
: {
errors: parsed.errors.length,
facts: analyzeParsedJavaScriptStaticSource(source, parsed),
};
};
it("parses conditional arrow bodies in JavaScript sources with JavaScript grammar", () => {
// Bundlers emit this shape; TypeScript reads `):H=>` as a return type.
const source = `const {ipcMain}=require("electron");
const pick=n===0?H=>({start:H.startY}):H=>({start:H.startX});
ipcMain.handle("layout:ready",()=>pick);`;
for (const path of ["main.js", "main.cjs", "main.mjs", "main.jsx"]) {
const result = analyze(source, path);
expect(result?.errors).toBe(0);
expect(result?.facts.electron.ipc).toContainEqual(
expect.objectContaining({ channel: "layout:ready", operation: "handle" }),
);
}
});
it("keeps JavaScript comparison meaning for angle brackets in JavaScript sources", () => {
const source = `const a=fetch<limit>("/api/compare");`;
expect(analyze(source, "main.js")?.facts.endpoints).toEqual([]);
expect(analyze(source, "main.ts")?.facts.endpoints).toContainEqual(
expect.objectContaining({ value: "/api/compare" }),
);
});
it("keeps TypeScript syntax available for TypeScript and unnamed sources", () => {
const source = "let count: number = 1; export type Id = string;";
expect(parseJavaScriptSource(source, "value.ts")?.errors).toEqual([]);
expect(parseJavaScriptSource(source)?.errors).toEqual([]);
});