diff --git a/packages/bun-types/ambient.d.ts b/packages/bun-types/ambient.d.ts index b4ac81a0a3e277..bd10bfb0d35e66 100644 --- a/packages/bun-types/ambient.d.ts +++ b/packages/bun-types/ambient.d.ts @@ -7,3 +7,13 @@ declare module "*.toml" { var contents: any; export = contents; } + +declare module "*.jsonc" { + var contents: any; + export = contents; +} + +declare module "*/bun.lock" { + var contents: import("bun").BunLockFile; + export = contents; +} diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 907883f47a3d8a..a9779ebb9b66bc 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -6431,6 +6431,69 @@ declare module "bun" { */ timestamp?: number | Date, ): Buffer; + + /** + * Types for `bun.lock` + */ + type BunLockFile = { + lockfileVersion: 0; + workspaces: { + [workspace: string]: BunLockFileWorkspacePackage; + }; + overrides?: Record; + patchedDependencies?: Record; + trustedDependencies?: string[]; + + /** + * ``` + * INFO = { prod/dev/optional/peer dependencies, os, cpu, libc (TODO), bin, binDir } + * + * npm -> [ "name@version", registry (TODO: remove if default), INFO, integrity] + * symlink -> [ "name@link:path", INFO ] + * folder -> [ "name@file:path", INFO ] + * workspace -> [ "name@workspace:path", INFO ] + * tarball -> [ "name@tarball", INFO ] + * root -> [ "name@root:", { bin, binDir } ] + * git -> [ "name@git+repo", INFO, .bun-tag string (TODO: remove this) ] + * github -> [ "name@github:user/repo", INFO, .bun-tag string (TODO: remove this) ] + * ``` + * */ + packages: { + [pkg: string]: BunLockFilePackageArray; + }; + }; + + type BunLockFileBasePackageInfo = { + dependencies?: Record; + devDependencies?: Record; + optionalDependencies?: Record; + peerDependencies?: Record; + optionalPeers?: string[]; + }; + + type BunLockFileWorkspacePackage = BunLockFileBasePackageInfo & { + name?: string; + version?: string; + }; + + type BunLockFilePackageInfo = BunLockFileBasePackageInfo & { + os?: string | string[]; + cpu?: string | string[]; + bin?: Record; + binDir?: string; + bundled?: true; + }; + + /** @see {@link BunLockFile.packages} for more info */ + type BunLockFilePackageArray = + /** npm */ + | [pkg: string, registry: string, info: BunLockFilePackageInfo, integrity: string] + /** symlink, folder, tarball, workspace */ + | [pkg: string, info: BunLockFilePackageInfo] + /** git, github */ + | [pkg: string, info: BunLockFilePackageInfo, bunTag: string] + /** root */ + | [pkg: string, info: Pick]; } // extends lib.dom.d.ts diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index fe13ad97fb73a7..b6141e518cfd56 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -2392,6 +2392,10 @@ pub const ModuleLoader = struct { } } + if (strings.eqlComptime(path.name.filename, "bun.lock")) { + loader = .json; + } + // We only run the transpiler concurrently when we can. // Today, that's: // diff --git a/src/fs.zig b/src/fs.zig index d64b8270911742..fa20e43a5272aa 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -1713,15 +1713,20 @@ pub const Path = struct { pub fn isJSONCFile(this: *const Path) bool { const str = this.name.filename; - if (strings.eqlComptime(str, "package.json")) { + + if (strings.eqlComptime(str, "package.json") or strings.eqlComptime(str, "bun.lock")) { + return true; + } + + if (strings.hasSuffixComptime(str, ".jsonc")) { return true; } - if (!(strings.hasPrefixComptime(str, "tsconfig.") or strings.hasPrefixComptime(str, "jsconfig."))) { - return false; + if (strings.hasPrefixComptime(str, "tsconfig.") or strings.hasPrefixComptime(str, "jsconfig.")) { + return strings.hasSuffixComptime(str, ".json"); } - return strings.hasSuffixComptime(str, ".json"); + return false; } pub const PackageRelative = struct { diff --git a/src/options.zig b/src/options.zig index 63e6582d1a3618..3e965a81b9d0bc 100644 --- a/src/options.zig +++ b/src/options.zig @@ -758,6 +758,7 @@ pub const Loader = enum(u8) { .{ "css", .css }, .{ "file", .file }, .{ "json", .json }, + .{ "jsonc", .json }, .{ "toml", .toml }, .{ "wasm", .wasm }, .{ "node", .napi }, @@ -783,6 +784,7 @@ pub const Loader = enum(u8) { .{ "css", .css }, .{ "file", .file }, .{ "json", .json }, + .{ "jsonc", .json }, .{ "toml", .toml }, .{ "wasm", .wasm }, .{ "node", .napi }, @@ -908,6 +910,7 @@ const default_loaders_posix = .{ .{ ".txt", .text }, .{ ".text", .text }, .{ ".html", .html }, + .{ ".jsonc", .json }, }; const default_loaders_win32 = default_loaders_posix ++ .{ .{ ".sh", .bunsh }, @@ -1286,16 +1289,18 @@ pub fn definesFromTransformOptions( const default_loader_ext_bun = [_]string{".node"}; const default_loader_ext = [_]string{ - ".jsx", ".json", - ".js", ".mjs", - ".cjs", ".css", + ".jsx", ".json", + ".js", ".mjs", + ".cjs", ".css", // https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/#new-file-extensions - ".ts", ".tsx", - ".mts", ".cts", + ".ts", ".tsx", + ".mts", ".cts", - ".toml", ".wasm", - ".txt", ".text", + ".toml", ".wasm", + ".txt", ".text", + + ".jsonc", }; // Only set it for browsers by default. @@ -1314,6 +1319,7 @@ const node_modules_default_loader_ext = [_]string{ ".toml", ".txt", ".json", + ".jsonc", ".css", ".tsx", ".cts", diff --git a/test/js/bun/resolve/bun-lock.test.ts b/test/js/bun/resolve/bun-lock.test.ts new file mode 100644 index 00000000000000..251783c00eefc4 --- /dev/null +++ b/test/js/bun/resolve/bun-lock.test.ts @@ -0,0 +1,27 @@ +import { expect, test } from "bun:test"; +import { tempDirWithFiles } from "harness"; +import { join } from "path"; + +const lockfile = `{ + "lockfileVersion": 0, + "workspaces": { + "": { + "name": "something", + "dependencies": { }, + }, + }, + "packages": { }, +}`; + +test("import bun.lock file as json", async () => { + const dir = tempDirWithFiles("bun-lock", { + "bun.lock": lockfile, + "index.ts": ` + import lockfile from './bun.lock'; + const _lockfile = ${lockfile} + if (!Bun.deepEquals(lockfile, _lockfile)) throw new Error('bun.lock wasnt imported as jsonc'); + `, + }); + + expect([join(dir, "index.ts")]).toRun(); +}); diff --git a/test/js/bun/resolve/jsonc.test.ts b/test/js/bun/resolve/jsonc.test.ts index d2571644c8d6da..bab3ba29ff3e0c 100644 --- a/test/js/bun/resolve/jsonc.test.ts +++ b/test/js/bun/resolve/jsonc.test.ts @@ -22,3 +22,19 @@ test("empty jsonc - tsconfig.json", async () => { }); expect([join(dir, "index.ts")]).toRun(); }); + +test("import anything.jsonc as json", async () => { + const jsoncFile = `{ + // comment + "trailingComma": 0, + }`; + const dir = tempDirWithFiles("jsonc", { + "anything.jsonc": jsoncFile, + "index.ts": ` + import file from './anything.jsonc'; + const _file = ${jsoncFile} + if (!Bun.deepEquals(file, _file)) throw new Error('anything.jsonc wasnt imported as jsonc'); + `, + }); + expect([join(dir, "index.ts")]).toRun(); +});