aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-11 19:01:00 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-11 19:01:00 -0800
commitd67c95d8ebb366d14976ce74e9448a23c3d6886a (patch)
treecc43f1bfb7d26a5e9c55a7090e470dc7e210dc18
parentbfd7f3398cbbdce3da97158835fd311af3f73f3a (diff)
downloadbun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.tar.gz
bun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.tar.zst
bun-d67c95d8ebb366d14976ce74e9448a23c3d6886a.zip
[bun install] Implement bunfig.toml config
-rwxr-xr-xbun.lockbbin7340 -> 7340 bytes
-rwxr-xr-xintegration/scripts/bun.lockbbin17219 -> 17219 bytes
-rwxr-xr-xpackages/bun-error/bun.lockbbin11916 -> 11916 bytes
-rw-r--r--src/api/schema.d.ts48
-rw-r--r--src/api/schema.js271
-rw-r--r--src/api/schema.peechy33
-rw-r--r--src/api/schema.zig235
-rw-r--r--src/bunfig.zig253
-rw-r--r--src/cli.zig179
-rw-r--r--src/env_loader.zig15
-rw-r--r--src/http/header_builder.zig18
-rw-r--r--src/string_builder.zig11
12 files changed, 977 insertions, 86 deletions
diff --git a/bun.lockb b/bun.lockb
index 0be659f47..88730849d 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/integration/scripts/bun.lockb b/integration/scripts/bun.lockb
index cea4eb8c4..a7b9d31b2 100755
--- a/integration/scripts/bun.lockb
+++ b/integration/scripts/bun.lockb
Binary files differ
diff --git a/packages/bun-error/bun.lockb b/packages/bun-error/bun.lockb
index e61a4dd71..825941f44 100755
--- a/packages/bun-error/bun.lockb
+++ b/packages/bun-error/bun.lockb
Binary files differ
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, &params), 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;
+}