diff options
author | 2022-02-11 19:01:00 -0800 | |
---|---|---|
committer | 2022-02-11 19:01:00 -0800 | |
commit | d67c95d8ebb366d14976ce74e9448a23c3d6886a (patch) | |
tree | cc43f1bfb7d26a5e9c55a7090e470dc7e210dc18 | |
parent | bfd7f3398cbbdce3da97158835fd311af3f73f3a (diff) | |
download | bun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.tar.gz bun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.tar.zst bun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.zip |
[bun install] Implement bunfig.toml config
-rwxr-xr-x | bun.lockb | bin | 7340 -> 7340 bytes | |||
-rwxr-xr-x | integration/scripts/bun.lockb | bin | 17219 -> 17219 bytes | |||
-rwxr-xr-x | packages/bun-error/bun.lockb | bin | 11916 -> 11916 bytes | |||
-rw-r--r-- | src/api/schema.d.ts | 48 | ||||
-rw-r--r-- | src/api/schema.js | 271 | ||||
-rw-r--r-- | src/api/schema.peechy | 33 | ||||
-rw-r--r-- | src/api/schema.zig | 235 | ||||
-rw-r--r-- | src/bunfig.zig | 253 | ||||
-rw-r--r-- | src/cli.zig | 179 | ||||
-rw-r--r-- | src/env_loader.zig | 15 | ||||
-rw-r--r-- | src/http/header_builder.zig | 18 | ||||
-rw-r--r-- | src/string_builder.zig | 11 |
12 files changed, 977 insertions, 86 deletions
Binary files differ diff --git a/integration/scripts/bun.lockb b/integration/scripts/bun.lockb Binary files differindex cea4eb8c4..a7b9d31b2 100755 --- a/integration/scripts/bun.lockb +++ b/integration/scripts/bun.lockb diff --git a/packages/bun-error/bun.lockb b/packages/bun-error/bun.lockb Binary files differindex e61a4dd71..825941f44 100755 --- a/packages/bun-error/bun.lockb +++ b/packages/bun-error/bun.lockb diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts index 015f6af40..d896bc160 100644 --- a/src/api/schema.d.ts +++ b/src/api/schema.d.ts @@ -603,6 +603,37 @@ export interface WebsocketMessageResolveID { id: uint32; } +export interface NPMRegistry { + url: string; + username: string; + password: string; + token: string; +} + +export interface NPMRegistryMap { + scopes: string[]; + registries: NPMRegistry[]; +} + +export interface BunInstall { + default_registry?: NPMRegistry; + scoped?: NPMRegistryMap; + lockfile_path?: string; + save_lockfile_path?: string; + cache_directory?: string; + dry_run?: boolean; + force?: boolean; + save_dev?: boolean; + save_optional?: boolean; + save_peer?: boolean; + save_lockfile?: boolean; + production?: boolean; + save_yarn_lockfile?: boolean; + native_bin_links?: string[]; + disable_cache?: boolean; + disable_manifest_cache?: boolean; +} + export declare function encodeStackFrame( message: StackFrame, bb: ByteBuffer @@ -869,3 +900,20 @@ export declare function encodeWebsocketMessageResolveID( export declare function decodeWebsocketMessageResolveID( buffer: ByteBuffer ): WebsocketMessageResolveID; +export declare function encodeNPMRegistry( + message: NPMRegistry, + bb: ByteBuffer +): void; +export declare function decodeNPMRegistry(buffer: ByteBuffer): NPMRegistry; +export declare function encodeNPMRegistryMap( + message: NPMRegistryMap, + bb: ByteBuffer +): void; +export declare function decodeNPMRegistryMap( + buffer: ByteBuffer +): NPMRegistryMap; +export declare function encodeBunInstall( + message: BunInstall, + bb: ByteBuffer +): void; +export declare function decodeBunInstall(buffer: ByteBuffer): BunInstall; diff --git a/src/api/schema.js b/src/api/schema.js index da91af252..67852d027 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -2748,6 +2748,271 @@ function encodeWebsocketMessageResolveID(message, bb) { } } +function decodeNPMRegistry(bb) { + var result = {}; + + result["url"] = bb.readString(); + result["username"] = bb.readString(); + result["password"] = bb.readString(); + result["token"] = bb.readString(); + return result; +} + +function encodeNPMRegistry(message, bb) { + var value = message["url"]; + if (value != null) { + bb.writeString(value); + } else { + throw new Error('Missing required field "url"'); + } + + var value = message["username"]; + if (value != null) { + bb.writeString(value); + } else { + throw new Error('Missing required field "username"'); + } + + var value = message["password"]; + if (value != null) { + bb.writeString(value); + } else { + throw new Error('Missing required field "password"'); + } + + var value = message["token"]; + if (value != null) { + bb.writeString(value); + } else { + throw new Error('Missing required field "token"'); + } +} + +function decodeNPMRegistryMap(bb) { + var result = {}; + + var length = bb.readVarUint(); + var values = (result["scopes"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = bb.readString(); + var length = bb.readVarUint(); + var values = (result["registries"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = decodeNPMRegistry(bb); + return result; +} + +function encodeNPMRegistryMap(message, bb) { + var value = message["scopes"]; + if (value != null) { + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + bb.writeString(value); + } + } else { + throw new Error('Missing required field "scopes"'); + } + + var value = message["registries"]; + if (value != null) { + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + encodeNPMRegistry(value, bb); + } + } else { + throw new Error('Missing required field "registries"'); + } +} + +function decodeBunInstall(bb) { + var result = {}; + + while (true) { + switch (bb.readByte()) { + case 0: + return result; + + case 1: + result["default_registry"] = decodeNPMRegistry(bb); + break; + + case 2: + result["scoped"] = decodeNPMRegistryMap(bb); + break; + + case 3: + result["lockfile_path"] = bb.readString(); + break; + + case 4: + result["save_lockfile_path"] = bb.readString(); + break; + + case 5: + result["cache_directory"] = bb.readString(); + break; + + case 6: + result["dry_run"] = !!bb.readByte(); + break; + + case 7: + result["force"] = !!bb.readByte(); + break; + + case 8: + result["save_dev"] = !!bb.readByte(); + break; + + case 9: + result["save_optional"] = !!bb.readByte(); + break; + + case 10: + result["save_peer"] = !!bb.readByte(); + break; + + case 11: + result["save_lockfile"] = !!bb.readByte(); + break; + + case 12: + result["production"] = !!bb.readByte(); + break; + + case 13: + result["save_yarn_lockfile"] = !!bb.readByte(); + break; + + case 14: + var length = bb.readVarUint(); + var values = (result["native_bin_links"] = Array(length)); + for (var i = 0; i < length; i++) values[i] = bb.readString(); + break; + + case 15: + result["disable_cache"] = !!bb.readByte(); + break; + + case 16: + result["disable_manifest_cache"] = !!bb.readByte(); + break; + + default: + throw new Error("Attempted to parse invalid message"); + } + } +} + +function encodeBunInstall(message, bb) { + var value = message["default_registry"]; + if (value != null) { + bb.writeByte(1); + encodeNPMRegistry(value, bb); + } + + var value = message["scoped"]; + if (value != null) { + bb.writeByte(2); + encodeNPMRegistryMap(value, bb); + } + + var value = message["lockfile_path"]; + if (value != null) { + bb.writeByte(3); + bb.writeString(value); + } + + var value = message["save_lockfile_path"]; + if (value != null) { + bb.writeByte(4); + bb.writeString(value); + } + + var value = message["cache_directory"]; + if (value != null) { + bb.writeByte(5); + bb.writeString(value); + } + + var value = message["dry_run"]; + if (value != null) { + bb.writeByte(6); + bb.writeByte(value); + } + + var value = message["force"]; + if (value != null) { + bb.writeByte(7); + bb.writeByte(value); + } + + var value = message["save_dev"]; + if (value != null) { + bb.writeByte(8); + bb.writeByte(value); + } + + var value = message["save_optional"]; + if (value != null) { + bb.writeByte(9); + bb.writeByte(value); + } + + var value = message["save_peer"]; + if (value != null) { + bb.writeByte(10); + bb.writeByte(value); + } + + var value = message["save_lockfile"]; + if (value != null) { + bb.writeByte(11); + bb.writeByte(value); + } + + var value = message["production"]; + if (value != null) { + bb.writeByte(12); + bb.writeByte(value); + } + + var value = message["save_yarn_lockfile"]; + if (value != null) { + bb.writeByte(13); + bb.writeByte(value); + } + + var value = message["native_bin_links"]; + if (value != null) { + bb.writeByte(14); + var values = value, + n = values.length; + bb.writeVarUint(n); + for (var i = 0; i < n; i++) { + value = values[i]; + bb.writeString(value); + } + } + + var value = message["disable_cache"]; + if (value != null) { + bb.writeByte(15); + bb.writeByte(value); + } + + var value = message["disable_manifest_cache"]; + if (value != null) { + bb.writeByte(16); + bb.writeByte(value); + } + bb.writeByte(0); +} + export { Loader }; export { LoaderKeys }; export { FrameworkEntryPointType }; @@ -2874,3 +3139,9 @@ export { decodeWebsocketCommandBuildWithFilePath }; export { encodeWebsocketCommandBuildWithFilePath }; export { decodeWebsocketMessageResolveID }; export { encodeWebsocketMessageResolveID }; +export { decodeNPMRegistry }; +export { encodeNPMRegistry }; +export { decodeNPMRegistryMap }; +export { encodeNPMRegistryMap }; +export { decodeBunInstall }; +export { encodeBunInstall }; diff --git a/src/api/schema.peechy b/src/api/schema.peechy index 58ca3f013..a7284f713 100644 --- a/src/api/schema.peechy +++ b/src/api/schema.peechy @@ -344,7 +344,6 @@ message TransformOptions { uint16 port = 25; MessageLevel logLevel = 26; - } struct FileHandle { @@ -516,4 +515,36 @@ struct WebsocketCommandBuildWithFilePath { struct WebsocketMessageResolveID { uint32 id; +} + +struct NPMRegistry { + string url; + string username; + string password; + string token; +} + +struct NPMRegistryMap { + string[] scopes; + NPMRegistry[] registries; +} + +message BunInstall { + NPMRegistry default_registry = 1; + NPMRegistryMap scoped = 2; + string lockfile_path = 3; + string save_lockfile_path = 4; + string cache_directory = 5; + bool dry_run = 6; + bool force = 7; + bool save_dev = 8; + bool save_optional = 9; + bool save_peer = 10; + bool save_lockfile = 11; + bool production = 12; + bool save_yarn_lockfile = 13; + string[] native_bin_links = 14; + + bool disable_cache = 15; + bool disable_manifest_cache = 16; }
\ No newline at end of file diff --git a/src/api/schema.zig b/src/api/schema.zig index 8f21c350c..8879b34cb 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -2617,4 +2617,239 @@ pub const Api = struct { try writer.writeInt(this.id); } }; + + pub const NpmRegistry = struct { + /// url + url: []const u8, + + /// username + username: []const u8, + + /// password + password: []const u8, + + /// token + token: []const u8, + + pub fn decode(reader: anytype) anyerror!NpmRegistry { + var this = std.mem.zeroes(NpmRegistry); + + this.url = try reader.readValue([]const u8); + this.username = try reader.readValue([]const u8); + this.password = try reader.readValue([]const u8); + this.token = try reader.readValue([]const u8); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(@TypeOf(this.url), this.url); + try writer.writeValue(@TypeOf(this.username), this.username); + try writer.writeValue(@TypeOf(this.password), this.password); + try writer.writeValue(@TypeOf(this.token), this.token); + } + }; + + pub const NpmRegistryMap = struct { + /// scopes + scopes: []const []const u8, + + /// registries + registries: []const NpmRegistry, + + pub fn decode(reader: anytype) anyerror!NpmRegistryMap { + var this = std.mem.zeroes(NpmRegistryMap); + + this.scopes = try reader.readArray([]const u8); + this.registries = try reader.readArray(NpmRegistry); + return this; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray([]const u8, this.scopes); + try writer.writeArray(NpmRegistry, this.registries); + } + }; + + pub const BunInstall = struct { + /// default_registry + default_registry: ?NpmRegistry = null, + + /// scoped + scoped: ?NpmRegistryMap = null, + + /// lockfile_path + lockfile_path: ?[]const u8 = null, + + /// save_lockfile_path + save_lockfile_path: ?[]const u8 = null, + + /// cache_directory + cache_directory: ?[]const u8 = null, + + /// dry_run + dry_run: ?bool = null, + + /// force + force: ?bool = null, + + /// save_dev + save_dev: ?bool = null, + + /// save_optional + save_optional: ?bool = null, + + /// save_peer + save_peer: ?bool = null, + + /// save_lockfile + save_lockfile: ?bool = null, + + /// production + production: ?bool = null, + + /// save_yarn_lockfile + save_yarn_lockfile: ?bool = null, + + /// native_bin_links + native_bin_links: []const []const u8, + + /// disable_cache + disable_cache: ?bool = null, + + /// disable_manifest_cache + disable_manifest_cache: ?bool = null, + + pub fn decode(reader: anytype) anyerror!BunInstall { + var this = std.mem.zeroes(BunInstall); + + while (true) { + switch (try reader.readByte()) { + 0 => { + return this; + }, + + 1 => { + this.default_registry = try reader.readValue(NpmRegistry); + }, + 2 => { + this.scoped = try reader.readValue(NpmRegistryMap); + }, + 3 => { + this.lockfile_path = try reader.readValue([]const u8); + }, + 4 => { + this.save_lockfile_path = try reader.readValue([]const u8); + }, + 5 => { + this.cache_directory = try reader.readValue([]const u8); + }, + 6 => { + this.dry_run = try reader.readValue(bool); + }, + 7 => { + this.force = try reader.readValue(bool); + }, + 8 => { + this.save_dev = try reader.readValue(bool); + }, + 9 => { + this.save_optional = try reader.readValue(bool); + }, + 10 => { + this.save_peer = try reader.readValue(bool); + }, + 11 => { + this.save_lockfile = try reader.readValue(bool); + }, + 12 => { + this.production = try reader.readValue(bool); + }, + 13 => { + this.save_yarn_lockfile = try reader.readValue(bool); + }, + 14 => { + this.native_bin_links = try reader.readArray([]const u8); + }, + 15 => { + this.disable_cache = try reader.readValue(bool); + }, + 16 => { + this.disable_manifest_cache = try reader.readValue(bool); + }, + else => { + return error.InvalidMessage; + }, + } + } + unreachable; + } + + pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + if (this.default_registry) |default_registry| { + try writer.writeFieldID(1); + try writer.writeValue(@TypeOf(default_registry), default_registry); + } + if (this.scoped) |scoped| { + try writer.writeFieldID(2); + try writer.writeValue(@TypeOf(scoped), scoped); + } + if (this.lockfile_path) |lockfile_path| { + try writer.writeFieldID(3); + try writer.writeValue(@TypeOf(lockfile_path), lockfile_path); + } + if (this.save_lockfile_path) |save_lockfile_path| { + try writer.writeFieldID(4); + try writer.writeValue(@TypeOf(save_lockfile_path), save_lockfile_path); + } + if (this.cache_directory) |cache_directory| { + try writer.writeFieldID(5); + try writer.writeValue(@TypeOf(cache_directory), cache_directory); + } + if (this.dry_run) |dry_run| { + try writer.writeFieldID(6); + try writer.writeInt(@as(u8, @boolToInt(dry_run))); + } + if (this.force) |force| { + try writer.writeFieldID(7); + try writer.writeInt(@as(u8, @boolToInt(force))); + } + if (this.save_dev) |save_dev| { + try writer.writeFieldID(8); + try writer.writeInt(@as(u8, @boolToInt(save_dev))); + } + if (this.save_optional) |save_optional| { + try writer.writeFieldID(9); + try writer.writeInt(@as(u8, @boolToInt(save_optional))); + } + if (this.save_peer) |save_peer| { + try writer.writeFieldID(10); + try writer.writeInt(@as(u8, @boolToInt(save_peer))); + } + if (this.save_lockfile) |save_lockfile| { + try writer.writeFieldID(11); + try writer.writeInt(@as(u8, @boolToInt(save_lockfile))); + } + if (this.production) |production| { + try writer.writeFieldID(12); + try writer.writeInt(@as(u8, @boolToInt(production))); + } + if (this.save_yarn_lockfile) |save_yarn_lockfile| { + try writer.writeFieldID(13); + try writer.writeInt(@as(u8, @boolToInt(save_yarn_lockfile))); + } + if (this.native_bin_links) |native_bin_links| { + try writer.writeFieldID(14); + try writer.writeArray([]const u8, native_bin_links); + } + if (this.disable_cache) |disable_cache| { + try writer.writeFieldID(15); + try writer.writeInt(@as(u8, @boolToInt(disable_cache))); + } + if (this.disable_manifest_cache) |disable_manifest_cache| { + try writer.writeFieldID(16); + try writer.writeInt(@as(u8, @boolToInt(disable_manifest_cache))); + } + try writer.endMessage(); + } + }; }; diff --git a/src/bunfig.zig b/src/bunfig.zig index 46d14ff0f..65e200098 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -12,7 +12,6 @@ const URL = @import("./query_string_map.zig").URL; const C = _global.C; const options = @import("./options.zig"); const logger = @import("./logger.zig"); -const cache = @import("./cache.zig"); const js_ast = @import("./js_ast.zig"); const js_lexer = @import("./js_lexer.zig"); const Defines = @import("./defines.zig"); @@ -45,6 +44,68 @@ pub const Bunfig = struct { return error.@"Invalid Bunfig"; } + fn parseRegistry(this: *Parser, expr: js_ast.Expr) !Api.NpmRegistry { + var registry = std.mem.zeroes(Api.NpmRegistry); + + switch (expr.data) { + .e_string => |str| { + const url = URL.parse(str.utf8); + // Token + if (url.username.len == 0 and url.password.len > 0) { + registry.token = url.password; + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{s}/{s}", .{ url.displayProtocol(), url.displayHostname(), std.mem.trimLeft(u8, url.pathname, "/") }); + } else if (url.username.len > 0 and url.password.len > 0) { + registry.username = url.username; + registry.password = url.password; + registry.url = try std.fmt.allocPrint(this.allocator, "{s}://{s}/{s}", .{ url.displayProtocol(), url.displayHostname(), std.mem.trimLeft(u8, url.pathname, "/") }); + } else { + registry.url = url.href; + } + }, + .e_object => |obj| { + if (obj.get("url")) |url| { + try this.expect(url, .e_string); + registry.url = url.data.e_string.utf8; + } + + if (obj.get("username")) |username| { + try this.expect(username, .e_string); + registry.username = username.data.e_string.utf8; + } + + if (obj.get("password")) |password| { + try this.expect(password, .e_string); + registry.password = password.data.e_string.utf8; + } + + if (obj.get("token")) |token| { + try this.expect(token, .e_string); + registry.token = token.data.e_string.utf8; + } + }, + else => { + try this.addError(expr.loc, "Expected registry to be a URL string or an object"); + }, + } + + return registry; + } + + fn loadLogLevel(this: *Parser, expr: js_ast.Expr) !void { + try this.expect(expr, .e_string); + const Matcher = strings.ExactSizeMatcher(8); + + this.bunfig.log_level = switch (Matcher.match(expr.asString(this.allocator).?)) { + Matcher.case("debug") => Api.MessageLevel.debug, + Matcher.case("error") => Api.MessageLevel.err, + Matcher.case("warn") => Api.MessageLevel.warn, + else => { + try this.addError(expr.loc, "Invalid log level, must be one of debug, error, or warn"); + unreachable; + }, + }; + } + pub fn parse(this: *Parser, comptime cmd: Command.Tag) !void { const json = this.json; var allocator = this.allocator; @@ -53,6 +114,10 @@ pub const Bunfig = struct { try this.addError(json.loc, "bunfig expects an object { } at the root"); } + if (json.get("logLevel")) |expr| { + try this.loadLogLevel(expr); + } + if (json.get("define")) |expr| { try this.expect(expr, .e_object); var valid_count: usize = 0; @@ -89,6 +154,10 @@ pub const Bunfig = struct { this.ctx.debug.fallback_only = disable.asBool() orelse false; } + if (expr.get("logLevel")) |expr2| { + try this.loadLogLevel(expr2); + } + if (expr.get("port")) |port| { try this.expect(port, .e_number); this.bunfig.port = port.data.e_number.toU16(); @@ -99,6 +168,169 @@ pub const Bunfig = struct { } } + if (comptime cmd.isNPMRelated()) { + if (json.get("install")) |bun| { + var install: *Api.BunInstall = this.ctx.install orelse brk: { + var install_ = try this.allocator.create(Api.BunInstall); + install_.* = std.mem.zeroes(Api.BunInstall); + this.ctx.install = install_; + break :brk install_; + }; + + if (bun.get("registry")) |registry| { + install.default_registry = try this.parseRegistry(registry); + } + + if (bun.get("scopes")) |scopes| { + var registry_map = install.scoped orelse std.mem.zeroes(Api.NpmRegistryMap); + try this.expect(scopes, .e_object); + const count = scopes.data.e_object.properties.len + registry_map.registries.len; + var registries = std.ArrayListUnmanaged(Api.NpmRegistry){ + .items = try this.allocator.alloc(Api.NpmRegistry, count), + .capacity = count, + }; + registries.appendSliceAssumeCapacity(registry_map.registries); + + var names = std.ArrayListUnmanaged(string){ + .items = try this.allocator.alloc(string, count), + .capacity = count, + }; + + names.appendSliceAssumeCapacity(registry_map.names); + + for (scopes.data.e_object.properties.slice()) |prop| { + const name_ = prop.key.?.data.e_string.string(this.allocator) orelse continue; + const value = prop.value orelse continue; + if (name_.len == 0) continue; + const name = if (name_[0] == '@') name_[1..] else name_; + var index = names.items.len; + for (names.items) |comparator, i| { + if (strings.eql(name, comparator)) { + index = i; + break; + } + } + + if (index == names.items.len) { + names.items.len += 1; + registries.items.len += 1; + } + names.items[index] = name; + registries.items[index] = try this.parseRegistry(value); + } + + registry_map.registries = registries.items; + registry_map.names = names.items; + install.scoped = registry_map; + } + + if (bun.get("dryRun")) |dry_run| { + if (dry_run.asBool()) |value| { + install.dry_run = value; + } + } + + if (bun.get("production")) |production| { + if (production.asBool()) |value| { + install.production = value; + } + } + + if (bun.get("lockfile")) |lockfile_expr| { + if (lockfile_expr.get("outputFormat")) |lockfile| { + try this.expect(lockfile, .e_string); + if (lockfile.asString(this.allocator)) |value| { + if (!(strings.eqlComptime(value, "bun"))) { + if (!strings.eqlComptime(value, "yarn")) { + try this.addError(lockfile.loc, "Invalid lockfile format, only 'yarn' output is implemented"); + } + + install.save_yarn_lockfile = true; + } + } + } + + if (lockfile_expr.get("save")) |lockfile| { + if (lockfile.asString()) |value| { + install.save_lockfile = value; + } + } + + if (lockfile_expr.get("path")) |lockfile| { + if (lockfile.asString()) |value| { + install.lockfile_path = value; + } + } + + if (lockfile_expr.get("savePath")) |lockfile| { + if (lockfile.asString()) |value| { + install.save_lockfile_path = value; + } + } + } + + if (bun.get("optional")) |optional| { + if (optional.asBool()) |value| { + install.save_optional = value; + } + } + + if (bun.get("peer")) |optional| { + if (optional.asBool()) |value| { + install.save_peer = value; + } + } + + if (bun.get("dev")) |optional| { + if (optional.asBool()) |value| { + install.save_dev = value; + } + } + + if (bun.get("logLevel")) |expr| { + try this.loadLogLevel(expr); + } + + if (bun.get("cache")) |cache| { + load: { + if (cache.asBool()) |value| { + if (!value) { + install.disable_cache = true; + install.disable_manifest_cache = true; + } + + break :load; + } + + if (cache.asString(allocator)) |value| { + install.cache_directory = value; + break :load; + } + + if (cache.data == .e_object) { + if (cache.get("disable")) |disable| { + if (disable.asBool()) |value| { + install.disable_cache = value; + } + } + + if (cache.get("disableManifest")) |disable| { + if (disable.asBool()) |value| { + install.disable_manifest_cache = value; + } + } + + if (cache.get("directory")) |directory| { + if (directory.asString(allocator)) |value| { + install.cache_directory = value; + } + } + } + } + } + } + } + if (json.get("bundle")) |bun| { if (comptime cmd == .DevCommand or cmd == .BuildCommand or cmd == .RunCommand or cmd == .AutoCommand or cmd == .BunCommand) { if (bun.get("saveTo")) |file| { @@ -108,6 +340,10 @@ pub const Bunfig = struct { } if (comptime cmd == .BunCommand) { + if (bun.get("logLevel")) |expr2| { + try this.loadLogLevel(expr2); + } + if (bun.get("entryPoints")) |entryPoints| { try this.expect(entryPoints, .e_array); const items = entryPoints.data.e_array.items.slice(); @@ -224,21 +460,6 @@ pub const Bunfig = struct { }; } - if (json.get("logLevel")) |expr| { - try this.expect(expr, .e_string); - const Matcher = strings.ExactSizeMatcher(8); - - this.bunfig.log_level = switch (Matcher.match(expr.asString(allocator).?)) { - Matcher.case("debug") => Api.MessageLevel.debug, - Matcher.case("error") => Api.MessageLevel.err, - Matcher.case("warn") => Api.MessageLevel.warn, - else => { - try this.addError(expr.loc, "Invalid log level, must be one of debug, error, or warn"); - unreachable; - }, - }; - } - Analytics.Features.bunfig = true; } diff --git a/src/cli.zig b/src/cli.zig index 38128f66e..ed588a2cd 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -205,6 +205,97 @@ pub const Arguments = struct { Global.exit(0); } + fn loadConfigPath(allocator: std.mem.Allocator, auto_loaded: bool, config_path: [:0]const u8, ctx: *Command.Context, comptime cmd: Command.Tag) !void { + var config_file = std.fs.openFileAbsoluteZ(config_path, .{ .read = true }) catch |err| { + if (auto_loaded) return; + Output.prettyErrorln("<r><red>error<r>: {s} opening config \"{s}\"", .{ + @errorName(err), + std.mem.span(config_path), + }); + Output.flush(); + Global.exit(1); + }; + defer config_file.close(); + var contents = config_file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| { + if (auto_loaded) return; + Output.prettyErrorln("<r><red>error<r>: {s} reading config \"{s}\"", .{ + @errorName(err), + std.mem.span(config_path), + }); + Output.flush(); + Global.exit(1); + }; + + js_ast.Stmt.Data.Store.create(allocator); + js_ast.Expr.Data.Store.create(allocator); + defer { + js_ast.Stmt.Data.Store.reset(); + js_ast.Expr.Data.Store.reset(); + } + var original_level = ctx.log.level; + defer { + ctx.log.level = original_level; + } + ctx.log.level = logger.Log.Level.warn; + try Bunfig.parse(allocator, logger.Source.initPathString(std.mem.span(config_path), contents), ctx, cmd); + } + + fn getHomeConfigPath(cwd: string, buf: *[std.fs.MAX_PATH_BYTES]u8) ?[:0]const u8 { + if (std.os.getenvZ("XDG_CONFIG_HOME")) |data_dir| { + var paths = [_]string{ data_dir, ".bunfig.toml" }; + return resolve_path.joinAbsStringBufZ(cwd, buf, &paths, .auto); + } + + if (std.os.getenvZ("HOME")) |data_dir| { + var paths = [_]string{ data_dir, ".bunfig.toml" }; + return resolve_path.joinAbsStringBufZ(cwd, buf, &paths, .auto); + } + + return null; + } + + pub fn loadConfig(allocator: std.mem.Allocator, args: clap.Args(clap.Help, ¶ms), ctx: *Command.Context, comptime cmd: Command.Tag) !void { + var config_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + if (comptime cmd.readGlobalConfig()) { + if (getHomeConfigPath(ctx.args.absolute_working_dir, &config_buf)) |path| { + try loadConfigPath(allocator, true, path, ctx, comptime cmd); + } + } + + var config_path_: []const u8 = ""; + if (args.option("--config")) |config_path__| { + config_path_ = config_path__; + } + var auto_loaded: bool = false; + if (config_path_.len == 0 and (args.option("--config") != null or Command.Tag.always_loads_config.get(cmd))) { + config_path_ = "bunfig.toml"; + auto_loaded = true; + } + + if (config_path_.len == 0) { + return; + } + + var config_path: [:0]u8 = undefined; + if (config_path_[0] == '/') { + @memcpy(&config_buf, config_path_.ptr, config_path_.len); + config_buf[config_path_.len] = 0; + config_path = config_buf[0..config_path_.len :0]; + } else { + var parts = [_]string{ ctx.args.absolute_working_dir.?, config_path_ }; + config_path_ = resolve_path.joinAbsStringBuf( + ctx.args.absolute_working_dir.?, + &config_buf, + &parts, + .auto, + ); + config_buf[config_path_.len] = 0; + config_path = config_buf[0..config_path_.len :0]; + } + + try loadConfigPath(allocator, auto_loaded, config_path, ctx, comptime cmd); + } + pub fn parse(allocator: std.mem.Allocator, ctx: *Command.Context, comptime cmd: Command.Tag) !Api.TransformOptions { var diag = clap.Diagnostic{}; @@ -229,79 +320,14 @@ pub const Arguments = struct { cwd = try std.process.getCwdAlloc(allocator); } - var opts: Api.TransformOptions = ctx.args; - opts.absolute_working_dir = cwd; + ctx.args.absolute_working_dir = cwd; if (comptime Command.Tag.loads_config.get(cmd)) { - load_config: { - var config_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - - var config_path_: []const u8 = ""; - if (args.option("--config")) |config_path__| { - config_path_ = config_path__; - } - var auto_loaded: bool = false; - if (config_path_.len == 0 and (args.option("--config") != null or Command.Tag.always_loads_config.get(cmd))) { - config_path_ = "bunfig.toml"; - auto_loaded = true; - } - - if (config_path_.len == 0) { - break :load_config; - } - - var config_path: [:0]u8 = undefined; - if (config_path_[0] == '/') { - @memcpy(&config_buf, config_path_.ptr, config_path_.len); - config_buf[config_path_.len] = 0; - config_path = config_buf[0..config_path_.len :0]; - } else { - var parts = [_]string{ cwd, config_path_ }; - config_path_ = resolve_path.joinAbsStringBuf( - cwd, - &config_buf, - &parts, - .auto, - ); - config_buf[config_path_.len] = 0; - config_path = config_buf[0..config_path_.len :0]; - } - - var config_file = std.fs.openFileAbsoluteZ(config_path, .{ .read = true }) catch |err| { - if (auto_loaded) break :load_config; - Output.prettyErrorln("<r><red>error<r>: {s} opening config \"{s}\"", .{ - @errorName(err), - std.mem.span(config_path), - }); - Output.flush(); - Global.exit(1); - }; - var contents = config_file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| { - if (auto_loaded) break :load_config; - Output.prettyErrorln("<r><red>error<r>: {s} reading config \"{s}\"", .{ - @errorName(err), - std.mem.span(config_path), - }); - Output.flush(); - Global.exit(1); - }; - - js_ast.Stmt.Data.Store.create(allocator); - js_ast.Expr.Data.Store.create(allocator); - defer { - js_ast.Stmt.Data.Store.reset(); - js_ast.Expr.Data.Store.reset(); - } - var original_level = ctx.log.level; - defer { - ctx.log.level = original_level; - } - ctx.log.level = logger.Log.Level.warn; - try Bunfig.parse(allocator, logger.Source.initPathString(std.mem.span(config_path), contents), ctx, cmd); - opts = ctx.args; - } + try loadConfig(allocator, args, ctx, cmd); } + var opts: Api.TransformOptions = ctx.args; + var defines_tuple = try DefineColonList.resolve(allocator, args.options("--define")); if (defines_tuple.keys.len > 0) { @@ -699,6 +725,7 @@ pub const Command = struct { log: *logger.Log, allocator: std.mem.Allocator, positionals: []const string = &[_]string{}, + install: ?*Api.BunInstall = null, debug: DebugOptions = DebugOptions{}, @@ -1126,6 +1153,20 @@ pub const Command = struct { PackageManagerCommand, TestCommand, + pub fn readGlobalConfig(this: Tag) bool { + return switch (this) { + .PackageManagerCommand, .InstallCommand, .AddCommand, .RemoveCommand => true, + else => false, + }; + } + + pub fn isNPMRelated(this: Tag) bool { + return switch (this) { + .PackageManagerCommand, .InstallCommand, .AddCommand, .RemoveCommand => true, + else => false, + }; + } + pub const cares_about_bun_file: std.EnumArray(Tag, bool) = std.EnumArray(Tag, bool).initDefault(false, .{ .AutoCommand = true, .BuildCommand = true, diff --git a/src/env_loader.zig b/src/env_loader.zig index a36956424..f680ea371 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -396,6 +396,21 @@ pub const Loader = struct { const empty_string_value: string = "\"\""; + pub fn get(this: *const Loader, key: string) ?string { + var _key = key; + if (_key.len > 0 and _key[0] == '$') { + _key = key[1..]; + } + + if (_key.len == 0) return null; + + return this.map.get(_key); + } + + pub fn getAuto(this: *const Loader, key: string) string { + return this.get(key) orelse key; + } + /// Load values from the environment into Define. /// /// If there is a framework, values from the framework are inserted with a diff --git a/src/http/header_builder.zig b/src/http/header_builder.zig index 0f33b9d4b..18dcbe1e7 100644 --- a/src/http/header_builder.zig +++ b/src/http/header_builder.zig @@ -36,6 +36,24 @@ pub fn append(this: *HeaderBuilder, name: string, value: string) void { this.entries.appendAssumeCapacity(Headers.Kv{ .name = name_ptr, .value = value_ptr }); } +pub fn appendFmt(this: *HeaderBuilder, name: string, comptime fmt: string, args: anytype) void { + const name_ptr = Api.StringPointer{ + .offset = @truncate(u32, this.content.len), + .length = @truncate(u32, name.len), + }; + + _ = this.content.append(name); + + const value = this.content.fmt(fmt, args); + + const value_ptr = Api.StringPointer{ + .offset = @truncate(u32, this.content.len), + .length = @truncate(u32, value.len), + }; + + this.entries.appendAssumeCapacity(Headers.Kv{ .name = name_ptr, .value = value_ptr }); +} + pub fn apply(this: *HeaderBuilder, client: *HTTPClient) void { client.header_entries = this.entries; client.header_buf = this.content.ptr.?[0..this.content.len]; diff --git a/src/string_builder.zig b/src/string_builder.zig index 577fa1548..610ddd00f 100644 --- a/src/string_builder.zig +++ b/src/string_builder.zig @@ -30,3 +30,14 @@ pub fn append(this: *StringBuilder, slice: string) string { assert(this.len <= this.cap); return result; } + +const std = @import("std"); +pub fn fmt(this: *StringBuilder, comptime str: string, args: anytype) string { + assert(this.len <= this.cap); // didn't count everything + assert(this.ptr != null); // must call allocate first + + var buf = this.ptr.?[this.len..this.cap]; + const out = std.fmt.bufPrint(buf, str, args) catch unreachable; + this.len += out.len; + return out; +} |