mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
### What does this PR do? A bug in our typescript parser was causing `module.foo = foo` to parse as a typescript namespace. If it didn't end with a semicolon and there's a statement on the next line it would cause a syntax error. Example: ```ts module.foo = foo foo.foo = foo ``` fixes #22929 fixes #22883 ### How did you verify your code works? Added a regression test
116 lines
3.0 KiB
TypeScript
116 lines
3.0 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { mkdtempSync, writeFileSync } from "fs";
|
|
import { bunEnv, bunExe } from "harness";
|
|
import { tmpdir } from "os";
|
|
import { join } from "path";
|
|
|
|
test("Module._extensions should not break ASI (automatic semicolon insertion)", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "bun-module-extensions-asi-"));
|
|
|
|
// Create a module without semicolons that relies on ASI
|
|
const moduleWithoutSemi = join(dir, "module-no-semi.js");
|
|
writeFileSync(
|
|
moduleWithoutSemi,
|
|
`function f() {}
|
|
module.exports = f
|
|
f.f = f`,
|
|
);
|
|
|
|
// Create a test file that hooks Module._extensions
|
|
const testFile = join(dir, "test.js");
|
|
writeFileSync(
|
|
testFile,
|
|
`
|
|
const Module = require("module");
|
|
const orig = Module._extensions[".js"];
|
|
|
|
// Hook Module._extensions[".js"] - commonly done by transpiler libraries
|
|
Module._extensions[".js"] = (m, f) => {
|
|
return orig(m, f);
|
|
};
|
|
|
|
// This should work without parse errors
|
|
const result = require("./module-no-semi.js");
|
|
if (typeof result !== 'function') {
|
|
throw new Error('Expected function but got ' + typeof result);
|
|
}
|
|
if (result.f !== result) {
|
|
throw new Error('Expected result.f === result');
|
|
}
|
|
console.log('SUCCESS');
|
|
`,
|
|
);
|
|
|
|
// Run the test
|
|
const proc = Bun.spawn({
|
|
cmd: [bunExe(), testFile],
|
|
cwd: dir,
|
|
env: bunEnv,
|
|
stderr: "pipe",
|
|
stdout: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Should not have parse errors
|
|
expect(stderr).not.toContain("Expected '{'");
|
|
expect(stderr).not.toContain("Unexpected end of file");
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout.trim()).toBe("SUCCESS");
|
|
});
|
|
|
|
test("Module._extensions works with modules that have semicolons", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "bun-module-extensions-semi-"));
|
|
|
|
// Create a module with semicolons
|
|
const moduleWithSemi = join(dir, "module-with-semi.js");
|
|
writeFileSync(
|
|
moduleWithSemi,
|
|
`function g() { return 42; }
|
|
module.exports = g;
|
|
g.g = g;`,
|
|
);
|
|
|
|
// Create a test file that hooks Module._extensions
|
|
const testFile = join(dir, "test.js");
|
|
writeFileSync(
|
|
testFile,
|
|
`
|
|
const Module = require("module");
|
|
const orig = Module._extensions[".js"];
|
|
|
|
Module._extensions[".js"] = (m, f) => {
|
|
return orig(m, f);
|
|
};
|
|
|
|
// This should also work with semicolons
|
|
const result = require("./module-with-semi.js");
|
|
if (typeof result !== 'function') {
|
|
throw new Error('Expected function but got ' + typeof result);
|
|
}
|
|
if (result() !== 42) {
|
|
throw new Error('Expected result() === 42');
|
|
}
|
|
if (result.g !== result) {
|
|
throw new Error('Expected result.g === result');
|
|
}
|
|
console.log('SUCCESS');
|
|
`,
|
|
);
|
|
|
|
// Run the test
|
|
const proc = Bun.spawn({
|
|
cmd: [bunExe(), testFile],
|
|
cwd: dir,
|
|
env: bunEnv,
|
|
stderr: "pipe",
|
|
stdout: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Should work correctly
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout.trim()).toBe("SUCCESS");
|
|
});
|