Files
bun.sh/test/cli/run/tsconfig-override.test.ts
2025-07-15 22:00:17 -07:00

306 lines
8.3 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
import path from "node:path";
describe("bun run --tsconfig-override", () => {
test("should use custom tsconfig for path resolution", async () => {
const dir = tempDirWithFiles("run-tsconfig-override", {
"index.ts": `
import { helper } from '@helpers/math';
console.log(helper());
`,
"src/math.ts": `
export function helper() {
return "success from custom tsconfig";
}
`,
"tsconfig.json": `
{
"compilerOptions": {
"paths": {
"@helpers/*": ["./wrong/*"]
}
}
}
`,
"custom-tsconfig.json": `
{
"compilerOptions": {
"paths": {
"@helpers/*": ["./src/*"]
}
}
}
`,
});
await using failProc = Bun.spawn({
cmd: [bunExe(), "run", path.join(dir, "index.ts")],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [failStderr, failExitCode] = await Promise.all([failProc.stderr.text(), failProc.exited]);
expect(failStderr).toContain("Cannot find module");
expect(failExitCode).not.toBe(0);
await using successProc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", path.join(dir, "custom-tsconfig.json"), path.join(dir, "index.ts")],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [successStdout, successStderr, successExitCode] = await Promise.all([
successProc.stdout.text(),
successProc.stderr.text(),
successProc.exited,
]);
expect(successStdout).toContain("success from custom tsconfig");
if (!successStderr.includes("Internal error: directory mismatch")) {
expect(successStderr).toBe("");
}
expect(successExitCode).toBe(0);
});
test("should work with relative tsconfig path", async () => {
const dir = tempDirWithFiles("run-tsconfig-relative", {
"src/main.ts": `
import { lib } from '@lib/util';
console.log(lib());
`,
"lib/util.ts": `
export function lib() {
return 42;
}
`,
"config/custom.json": `
{
"compilerOptions": {
"baseUrl": "../",
"paths": {
"@lib/*": ["lib/*"]
}
}
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", "./config/custom.json", "./src/main.ts"],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toContain("42");
if (!stderr.includes("Internal error: directory mismatch")) {
expect(stderr).toBe("");
}
expect(exitCode).toBe(0);
});
test("should work with monorepo-style paths", async () => {
const dir = tempDirWithFiles("run-tsconfig-monorepo", {
"apps/web/src/index.ts": `
import { Button } from '@ui/components';
import { config } from '@shared/config';
console.log('App loaded with', Button(), config);
`,
"packages/ui/components/index.ts": `
export function Button() {
return 'Button component';
}
`,
"packages/shared/config.ts": `
export const config = { name: 'monorepo-app' };
`,
"apps/web/tsconfig.json": `
{
"compilerOptions": {
"baseUrl": "../../",
"paths": {
"@ui/*": ["packages/ui/*"],
"@shared/*": ["packages/shared/*"]
}
}
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", "./apps/web/tsconfig.json", "./apps/web/src/index.ts"],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toContain("Button component");
expect(stdout).toContain("monorepo-app");
if (!stderr.includes("Internal error: directory mismatch")) {
expect(stderr).toBe("");
}
expect(exitCode).toBe(0);
});
test("should work with nested directories and complex paths", async () => {
const dir = tempDirWithFiles("run-tsconfig-nested", {
"frontend/src/pages/home.ts": `
import { api } from '~/api/client';
import { utils } from '#/utils/helpers';
console.log(api.getHome(), utils.format('test'));
`,
"frontend/src/api/client.ts": `
export const api = {
getHome: () => 'home-data'
};
`,
"frontend/src/utils/helpers.ts": `
export const utils = {
format: (str: string) => \`formatted-\${str}\`
};
`,
"frontend/tsconfig.json": `
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"~/*": ["./*"],
"#/*": ["./*"]
}
}
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", "./frontend/tsconfig.json", "./frontend/src/pages/home.ts"],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toContain("home-data");
expect(stdout).toContain("formatted-test");
if (!stderr.includes("Internal error: directory mismatch")) {
expect(stderr).toBe("");
}
expect(exitCode).toBe(0);
});
test("should handle extending tsconfig with overrides", async () => {
const dir = tempDirWithFiles("run-tsconfig-extends", {
"src/app.ts": `
import { core } from '@core/main';
import { feature } from '@features/auth';
console.log('Loaded:', core, feature);
`,
"packages/core/main.ts": `
export const core = 'core-module';
`,
"features/auth/index.ts": `
export const feature = 'auth-feature';
`,
"tsconfig.base.json": `
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@core/*": ["packages/core/*"]
}
}
}
`,
"tsconfig.dev.json": `
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@core/*": ["packages/core/*"],
"@features/*": ["features/*"]
}
}
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", "./tsconfig.dev.json", "./src/app.ts"],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toContain("core-module");
expect(stdout).toContain("auth-feature");
if (!stderr.includes("Internal error: directory mismatch")) {
expect(stderr).toBe("");
}
expect(exitCode).toBe(0);
});
test("should work from different working directories", async () => {
const dir = tempDirWithFiles("run-tsconfig-cwd", {
"project/src/main.ts": `
import { helper } from '@utils/math';
console.log('Result:', helper(5, 3));
`,
"project/utils/math.ts": `
export function helper(a: number, b: number) {
return a + b;
}
`,
"project/tsconfig.json": `
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*": ["utils/*"]
}
}
}
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--tsconfig-override", "project/tsconfig.json", "project/src/main.ts"],
env: bunEnv,
cwd: dir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toContain("Result: 8");
if (!stderr.includes("Internal error: directory mismatch")) {
expect(stderr).toBe("");
}
expect(exitCode).toBe(0);
});
});