mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 21:32:05 +00:00
* alright now just gotta try running it
* fix a gajillion compiler errors
* even more code
* okay i fixed more errors
* wip
* Update launch.json
* Update string_builder.zig
* `fast_debug_build_mode` makes debug build 2x faster
* Update bundle_v2.zig
* more code!
* It bundles!
* Rename `Bun.Transpiler` to `Bun.Bundler`
* `import()` expressions almost work
* wip attempt to get import() expr to work
* Bundle namespace imports
* Attempt to fix the issue with import() unsuccessfully
* consider current working directory when resolving relative paths (#2313)
* consider current working directory when resolving relative paths
fixes #2298
* comment test
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* support `expect().toThrow(/pattern/)` (#2314)
- fix time-zone-dependent test failure
* fix missing `Blob` error messages on Linux (#2315)
* fix & clean up tests (#2318)
- skip flaky tests when running as `root`
- use `expect().toThrow()`
- clean up temporary files after tests
* feat(tty): add some `tty.WriteStream` methods to `process.{stdout, stderr}` (#2320)
* feat(stdio): add some `tty.WriteStream` methods
* chore(builtins): add process builtin gen'd code
* Fix docker install command
* `bun test` on macOS in GitHub Actions (#2322)
* Fixes #2323
* throw invalid parameter errors in `crypto.scryptSync` (#2331)
* throw invalid parameter errors
* remove comptime, add empty buffer function
* remove error_name comptime
* Add reference documentation for bun:test (#2327)
* Reorganize tests (#2332)
* Fix html-rewriter.test.js
* fix the wrong thing being incremented in hmr example (#2334)
* Add more test harness
* Improve Benchmarking page, small fixes (#2339)
* Improve benchmarking page
* WIP
* Add typescript instructions to hot
* Document preload in Plugins. Fix loader in plugin types.
* Fix typo
* Fix links
* run prettier
* Document openInEditor
* improve `Buffer` compatibility with Node.js (#2341)
* improve `Buffer` compatibility with Node.js
* use `memmove()`
allow `encoding` to be `undefined`
* run `bun test` after macOS builds (#2343)
* "binary" is an alias of "latin1"
Fixes https://github.com/oven-sh/bun/issues/2110
* More spec compliant `Blob.prototype.type` (#2340)
* Make `Blob.prototype. type` more spec compliant
* Add a few more checks for isNumber()
* Fix `make headers`
* Safer JSValue.isString()
* More tests for blob.slice
* Make `Blob.prototype.type` more spec compliant
* Add isASCII check
* Fix types
* Fix failing type test
* Update blob.zig
* Update blob.zig
* Fix .eql check on empty values
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* Fix bug in test runner
* Support `import()` expressions
* Implement `require()`
* clean up bit_set.zig slightly
* Move some things around
* misc cleanup
* Cleanup some things
* Fix a lot of stuff
* Fix `module.exports.fn = fn;` in ESM entry point
* Fix crash due when printing file
* Fix issue with class names
* Fix issue with `export default identifier`
* Update js_parser.zig
* optimization: inline single-property object acceses and arrays
* Fix undefined memory in renamed symbols list
* Handle call target
* wip
* Inline it
* Fix undefined memory issue when reclaiming blocks in ast
* Halt linking on any parse errors
* alias
* Rename `enable_bundling` to `enable_legacy_bundling`
* Workaround anonymous struct literal zig bug
* Use slower approach (without bitset) because it doesn't break after 8 symbols
* Fix incorrectly-renaming statically defined symbols
* Handle more edgecases in our bit_set fork
* Reduce number of allocations for `define`
* Do not rename unbound symbols
* Clean up dot defines a little more
* Make the generated names prettier
* Workaround runtime symbol missing issue
* Fail the build on errors
* Support export * from
* Support `--outfile`
* partially fix renaming
* fanicer symbol renaming impl
* misc, extremely revertible cleanup
* Fix up some bugs with symbol renaming
* formatting
* Update launch.json
* Parse `__PURE__` comments
* clean up simd code for pure comments
* changes to merge
* workaround runtime issue
* Fix issue with `export * as` not propagating correctly
* Make all top-level declarations `var` when bundling
* Fix missing prefix
* Fix assigning to stack copy
* Fix missing runtime symbol
* Fix bug with namespace exports
* Dramatically reduce allocations
* Update launch.json
* Add missing flags
* Update js_parser.zig
* small cleanup
* Make the export name better
* Fix unnecessary `var foo = foo`
* Implement CommonJS -> ESM conversion
* Implement module redirects
* Port esbuild bundler tests for new bundler (#2380)
* started porting esbuild tests
* clean up test names and api before moving on
* port tests using a program i wrote
* replace todo generated comment
* fix generated tests not including some files
* work on tests
* [github web editor] add define, external, inject, minifySyntax, minifyWhitespace options.
* get most of the todo comments out of the way, but expectBundled does not handle most of the cases
* continue working on esbuild tests
* use test.skip for unsupported tests
* Fixups for test runner
* Hoist imports & exports
* Fix test
* Hoist classes
* bundler test refining, 51/835
* Fix runtime require
* bundler test refining, 81/835
* bundler test refining, 93/835
* Make the test work in any timezone
* feat(expect): update toBeInstanceOf (#2396)
* feat: update instanceof binding
* fix: according to PR comments
* Rename `expectObjectTypeCount` to `expectMaxObjectTypeCount`
* Fix socket tests with connection errors (#2403)
* release pending activity with connection error handler
* unref poll_ref
* remove trailing comma
* Organize Dockerfiles for official status
* Remove test Dockerfile
* Remove old Docker workflow
* Feat(test): add toMatch (#2404)
* Fix various fetch/response/request tests (#2416)
* fix most fetch tests, skip a few
* fastGet, toValueGC, and invalid init
* bigint unreachable, range error, log process as process
* remove extra fetch_headers
* remove js_type parameter, check isObject()
* throw invalid mime type error, use enum literal
* switch back to promise rejection
* RangeError pascal case
* Fix several bugs (#2418)
* utf16 codepoint with replacement character
* Fix test failure with `TextEncoder("ascii')`
* Add missing type
* Fix Response.prototype.bodyUsed and Request.prototype.bodyUsed
* Fix bug with scrypt error not clearing
* Update server.zig
* oopsie
* 💅
* docs: Use correct url in the 'Issues' link in README header (#2420)
* Fix crash when rendering error page and the server or network is slow
* [fetch] Make the default body value `null` when unspecified
This is better aligned with the fetch spec
* Make node-net tests less flaky
* [node:net] Fix issue with `listen` callback firing before it's listening
* Always clear timers in node test harness
* Fix out of bounds access
Repro'd in Buffer tests
* Update UWS
cc @cirospaciari
* Make this test more thorough
* Hanging abort test
* 0 length body is a null stream
* Several bug fixes (#2427)
* Fix test
* Fix segfault when unexpected type is passed in `expect().toThrow`
* Fix issues with request constructor
* Don't bother cloning headers when its empty
* woops
* more tests
* fix incorrect test
* Make the fetch error messages better
* Update response.zig
* Fix test that failed on macOS
* Fix test
* Remove extra hash table lookups
* Support running dummy registry directly
cc @alexlamsl
* Update test
* Update test
* fixup
* Workaround crash in test runner
* Fixup test
* Fixup test
* Update os.test.js
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* Remove usages of port numbers in tests
* Set -O2 and -fno-rtti
* Remove -g
* Prevent undefined memory access
* [bun test] Implement `--rerun-each` flag to run each test N times
* Reduce number of module scopes created
* add some extra abort checks into streams (#2430)
* add some checks to avoid UAF
* avoid multiple calls to finalize if endFromJS is called more than once
* fix no-op comment
* mark as requested_end on abort
* remove requested_end from abort
* remove unnecessary check (#2432)
* Fix bug with scoped aliased dependencies in bun install on macOS
* remove `addLog`, remove `--prominent-compile-errors`
* Finish the upgrade
* Optional chaining flag
* Implement same_target_becomes_destructuring optimization
* bundler test refining, 109/835
* Reset bindings
* Support multiple entry points
* Implement `--entry-names` flag
* Use a tempdir with a better name
* prettier
* Log file name
* Update js_parser.zig
* Mark all bun builtins as external
* Make resolve errors actually errors
* Update bundler_default.test.ts
* Fix `await import(foo)`
* WIP react server components
* Do more stuff at runtime
* ✂️
* Support automatic JSX imports
* Use a module cache for now
* Update tsconfig.base.json
* Fix ThisOutsideFunctionNotRenamed
* woopsie
* moar cpu
* clamp it
* fixup
* Add a bunch of assertions
* Bun uses automatic runtime by default
* Parse Import Attributes
* Add a note about Valgrind
* Update developing.md
* Fix up code splitting for React Server Components
* Implement client component manifest
* Fix crash with --react-server-components and no client components
* Backport 4d31e3c917
* Update launch.json
* Fix for latest zig
* Workaround bug with ?[]const string
Occasionally saw alignment errors in this code
Workaround https://github.com/ziglang/zig/issues/15085
related: https://github.com/ziglang/zig/pull/15089
* switch to regular slice
* Avoid initializing named_imports and named_exports as undefined
* Reduce usages of `undefined`
* Add more assertions
* --watch wip
* Update javascript.zig
* Possibly fix the race condition
* Faster `do`
* bump allocator
* Reduce the size of `Symbol` slightly
* Alphabetically sort runtime import symbols, for determinism
* Prepare for code splitting
* handle overlapping stdout
* pure
* clean up some things
* Fix bug with `$$typeof`
* Address CommonJS -> ESM hoisting bug
* Support `"use server"` in manifest
* Implement `"use server"`
* Fix importing bun builtins when bundling
* Make `commonjs_to_esm` a feature flag, fix some splitting bugs
* ✂️
* fixme remove this
* Fix crash in longestCommonPath
* Chunking! Just need to do import paths now.
* Import paths work...now trying to figure out how to make runtime symbols work
* add workaround
* Replace `bun bun` with `bun build`
* Fix crash with dual package hazard
* Fix many CommonJS <> ESM interop bugs
* Support package.json `"sideEffects"`
also skip loading unnecessary package.json data in `bun run`
* add a not good --watch implementation
* bundler test refining, 140/831
* remove accidentally committed file
* do not return status code 1 on successful bundles
* bundler test refining, 159/830
* pass exit code to exitOrWatch
* clean up help menu
-remove two spaces to line up bun build
-moved all <r> tags to the end of the text they are colorizing
-moved other colors to the start of the text they colorize
-removed unneeded <r> tags, keeping only one at the start of the block
* importstar is fully ported
* wip
* you can run code in this branch now
* Disable this transform
* organize and document bundler tests
* Fix double import
* Fix sloppy mode function declarations
* Disable our CommonJS transform for now
* add `assertNotPresent` to make splitting cases easier
* Bump!
* Update bun.d.ts
* use import.meta.require in runtime code
* Disable this again
* Fix dirname
* Fix ESM -> CJS wrapper
* 💅
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Alex Lam S.L <alexlamsl@gmail.com>
Co-authored-by: Derrick Farris <mr.dcfarris@gmail.com>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Co-authored-by: pfg <pfg@pfg.pw>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: dave caruso <me@paperdave.net>
Co-authored-by: zhiyuan <32867472+zhiyuang@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Kamil Ogórek <kamil.ogorek@gmail.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
682 lines
30 KiB
Zig
682 lines
30 KiB
Zig
const std = @import("std");
|
|
const bun = @import("bun");
|
|
const string = bun.string;
|
|
const Output = bun.Output;
|
|
const Global = bun.Global;
|
|
const Environment = bun.Environment;
|
|
const strings = bun.strings;
|
|
const MutableString = bun.MutableString;
|
|
const stringZ = bun.stringZ;
|
|
const default_allocator = bun.default_allocator;
|
|
const URL = @import("./url.zig").URL;
|
|
const C = bun.C;
|
|
const options = @import("./options.zig");
|
|
const logger = @import("bun").logger;
|
|
const js_ast = bun.JSAst;
|
|
const js_lexer = bun.js_lexer;
|
|
const Defines = @import("./defines.zig");
|
|
const ConditionsMap = @import("./resolver/package_json.zig").ESModule.ConditionsMap;
|
|
const Api = @import("./api/schema.zig").Api;
|
|
const Npm = @import("./install/npm.zig");
|
|
const PackageJSON = @import("./resolver/package_json.zig").PackageJSON;
|
|
const resolver = @import("./resolver/resolver.zig");
|
|
pub const MacroImportReplacementMap = bun.StringArrayHashMap(string);
|
|
pub const MacroMap = bun.StringArrayHashMapUnmanaged(MacroImportReplacementMap);
|
|
pub const BundlePackageOverride = bun.StringArrayHashMapUnmanaged(options.BundleOverride);
|
|
const LoaderMap = bun.StringArrayHashMapUnmanaged(options.Loader);
|
|
const Analytics = @import("./analytics.zig");
|
|
const JSONParser = bun.JSON;
|
|
const Command = @import("cli.zig").Command;
|
|
const TOML = @import("./toml/toml_parser.zig").TOML;
|
|
|
|
// TODO: replace Api.TransformOptions with Bunfig
|
|
pub const Bunfig = struct {
|
|
pub const OfflineMode = enum {
|
|
online,
|
|
latest,
|
|
offline,
|
|
};
|
|
pub const Prefer = bun.ComptimeStringMap(OfflineMode, .{
|
|
&.{ "offline", OfflineMode.offline },
|
|
&.{ "latest", OfflineMode.latest },
|
|
&.{ "online", OfflineMode.online },
|
|
});
|
|
|
|
const Parser = struct {
|
|
json: js_ast.Expr,
|
|
source: *const logger.Source,
|
|
log: *logger.Log,
|
|
allocator: std.mem.Allocator,
|
|
bunfig: *Api.TransformOptions,
|
|
ctx: *Command.Context,
|
|
|
|
fn addError(this: *Parser, loc: logger.Loc, comptime text: string) !void {
|
|
this.log.addError(this.source, loc, text) catch unreachable;
|
|
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.data);
|
|
// 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.data;
|
|
}
|
|
|
|
if (obj.get("username")) |username| {
|
|
try this.expect(username, .e_string);
|
|
registry.username = username.data.e_string.data;
|
|
}
|
|
|
|
if (obj.get("password")) |password| {
|
|
try this.expect(password, .e_string);
|
|
registry.password = password.data.e_string.data;
|
|
}
|
|
|
|
if (obj.get("token")) |token| {
|
|
try this.expect(token, .e_string);
|
|
registry.token = token.data.e_string.data;
|
|
}
|
|
},
|
|
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,
|
|
Matcher.case("info") => Api.MessageLevel.info,
|
|
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;
|
|
|
|
if (json.data != .e_object) {
|
|
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;
|
|
const properties = expr.data.e_object.properties.slice();
|
|
for (properties) |prop| {
|
|
if (prop.value.?.data != .e_string) continue;
|
|
valid_count += 1;
|
|
}
|
|
var buffer = allocator.alloc([]const u8, valid_count * 2) catch unreachable;
|
|
var keys = buffer[0..valid_count];
|
|
var values = buffer[valid_count..];
|
|
var i: usize = 0;
|
|
for (properties) |prop| {
|
|
if (prop.value.?.data != .e_string) continue;
|
|
keys[i] = prop.key.?.data.e_string.string(allocator) catch unreachable;
|
|
values[i] = prop.value.?.data.e_string.string(allocator) catch unreachable;
|
|
i += 1;
|
|
}
|
|
this.bunfig.define = Api.StringMap{
|
|
.keys = keys,
|
|
.values = values,
|
|
};
|
|
}
|
|
|
|
if (json.get("origin")) |expr| {
|
|
try this.expect(expr, .e_string);
|
|
this.bunfig.origin = try expr.data.e_string.string(allocator);
|
|
}
|
|
|
|
if (comptime cmd == .RunCommand or cmd == .AutoCommand) {
|
|
if (json.get("serve")) |expr| {
|
|
if (expr.get("port")) |port| {
|
|
try this.expect(port, .e_number);
|
|
this.bunfig.port = port.data.e_number.toU16();
|
|
if (this.bunfig.port.? == 0) {
|
|
this.bunfig.port = 3000;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (json.get("preload")) |expr| {
|
|
if (expr.asArray()) |array_| {
|
|
var array = array_;
|
|
var preloads = try std.ArrayList(string).initCapacity(allocator, array.array.items.len);
|
|
errdefer preloads.deinit();
|
|
while (array.next()) |item| {
|
|
try this.expect(item, .e_string);
|
|
if (item.data.e_string.len() > 0)
|
|
preloads.appendAssumeCapacity(try item.data.e_string.string(allocator));
|
|
}
|
|
this.ctx.preloads = preloads.items;
|
|
} else if (expr.data != .e_null) {
|
|
try this.addError(expr.loc, "Expected preload to be an array");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comptime cmd == .DevCommand or cmd == .AutoCommand) {
|
|
if (json.get("dev")) |expr| {
|
|
if (expr.get("disableBunJS")) |disable| {
|
|
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();
|
|
if (this.bunfig.port.? == 0) {
|
|
this.bunfig.port = 3000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comptime cmd == .TestCommand) {
|
|
if (json.get("test")) |test_| {
|
|
if (test_.get("root")) |root| {
|
|
this.ctx.debug.test_directory = root.asString(this.allocator) orelse "";
|
|
}
|
|
|
|
if (test_.get("preload")) |expr| {
|
|
if (expr.asArray()) |array_| {
|
|
var array = array_;
|
|
var preloads = try std.ArrayList(string).initCapacity(allocator, array.array.items.len);
|
|
errdefer preloads.deinit();
|
|
while (array.next()) |item| {
|
|
try this.expect(item, .e_string);
|
|
if (item.data.e_string.len() > 0)
|
|
preloads.appendAssumeCapacity(try item.data.e_string.string(allocator));
|
|
}
|
|
this.ctx.preloads = preloads.items;
|
|
} else if (expr.data != .e_null) {
|
|
try this.addError(expr.loc, "Expected preload to be an array");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comptime cmd.isNPMRelated() or cmd == .RunCommand or cmd == .AutoCommand) {
|
|
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 (json.get("auto")) |auto_install_expr| {
|
|
if (auto_install_expr.data == .e_string) {
|
|
this.ctx.debug.global_cache = options.GlobalCache.Map.get(auto_install_expr.asString(this.allocator) orelse "") orelse {
|
|
try this.addError(auto_install_expr.loc, "Invalid auto install setting, must be one of true, false, or \"force\" \"fallback\" \"disable\"");
|
|
return;
|
|
};
|
|
} else if (auto_install_expr.data == .e_boolean) {
|
|
this.ctx.debug.global_cache = if (auto_install_expr.asBool().?)
|
|
options.GlobalCache.allow_install
|
|
else
|
|
options.GlobalCache.disable;
|
|
} else {
|
|
try this.addError(auto_install_expr.loc, "Invalid auto install setting, must be one of true, false, or \"force\" \"fallback\" \"disable\"");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (json.get("prefer")) |prefer_expr| {
|
|
try this.expect(prefer_expr, .e_string);
|
|
|
|
if (Prefer.get(prefer_expr.asString(bun.default_allocator) orelse "")) |setting| {
|
|
this.ctx.debug.offline_mode_setting = setting;
|
|
} else {
|
|
try this.addError(prefer_expr.loc, "Invalid prefer setting, must be one of online or offline");
|
|
}
|
|
}
|
|
|
|
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 = try std.ArrayListUnmanaged(Api.NpmRegistry).initCapacity(this.allocator, count);
|
|
registries.appendSliceAssumeCapacity(registry_map.registries);
|
|
|
|
var names = try std.ArrayListUnmanaged(string).initCapacity(this.allocator, count);
|
|
names.appendSliceAssumeCapacity(registry_map.scopes);
|
|
|
|
for (scopes.data.e_object.properties.slice()) |prop| {
|
|
const name_ = prop.key.?.asString(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, 0..) |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.scopes = 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("print")) |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.asBool()) |value| {
|
|
install.save_lockfile = value;
|
|
}
|
|
}
|
|
|
|
if (lockfile_expr.get("path")) |lockfile| {
|
|
if (lockfile.asString(allocator)) |value| {
|
|
install.lockfile_path = value;
|
|
}
|
|
}
|
|
|
|
if (lockfile_expr.get("savePath")) |lockfile| {
|
|
if (lockfile.asString(allocator)) |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("globalDir")) |dir| {
|
|
if (dir.asString(allocator)) |value| {
|
|
install.global_dir = value;
|
|
}
|
|
}
|
|
|
|
if (_bun.get("globalBinDir")) |dir| {
|
|
if (dir.asString(allocator)) |value| {
|
|
install.global_bin_dir = 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("dir")) |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 == .BuildCommand) {
|
|
if (_bun.get("saveTo")) |file| {
|
|
try this.expect(file, .e_string);
|
|
this.bunfig.node_modules_bundle_path = try file.data.e_string.string(allocator);
|
|
}
|
|
|
|
if (_bun.get("outdir")) |dir| {
|
|
try this.expect(dir, .e_string);
|
|
this.bunfig.output_dir = try dir.data.e_string.string(allocator);
|
|
}
|
|
}
|
|
|
|
if (comptime cmd == .BuildCommand) {
|
|
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();
|
|
var names = try this.allocator.alloc(string, items.len);
|
|
for (items, 0..) |item, i| {
|
|
try this.expect(item, .e_string);
|
|
names[i] = try item.data.e_string.string(allocator);
|
|
}
|
|
this.bunfig.entry_points = names;
|
|
}
|
|
|
|
if (_bun.get("packages")) |expr| {
|
|
try this.expect(expr, .e_object);
|
|
var valid_count: usize = 0;
|
|
Analytics.Features.always_bundle = true;
|
|
|
|
const object = expr.data.e_object;
|
|
const properties = object.properties.slice();
|
|
for (properties) |prop| {
|
|
if (prop.value.?.data != .e_boolean) continue;
|
|
valid_count += 1;
|
|
}
|
|
|
|
try this.ctx.debug.package_bundle_map.ensureTotalCapacity(allocator, valid_count);
|
|
|
|
for (properties) |prop| {
|
|
if (prop.value.?.data != .e_boolean) continue;
|
|
|
|
const path = try prop.key.?.data.e_string.string(allocator);
|
|
|
|
if (!resolver.isPackagePath(path)) {
|
|
try this.addError(prop.key.?.loc, "Expected package name");
|
|
}
|
|
|
|
this.ctx.debug.package_bundle_map.putAssumeCapacity(path, switch (prop.value.?.asBool() orelse false) {
|
|
true => options.BundlePackage.always,
|
|
false => options.BundlePackage.never,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var jsx_factory: string = "";
|
|
var jsx_fragment: string = "";
|
|
var jsx_import_source: string = "";
|
|
var jsx_runtime = Api.JsxRuntime.automatic;
|
|
var jsx_dev = true;
|
|
|
|
if (json.get("jsx")) |expr| {
|
|
if (expr.asString(allocator)) |value| {
|
|
if (strings.eqlComptime(value, "react")) {
|
|
jsx_runtime = Api.JsxRuntime.classic;
|
|
} else if (strings.eqlComptime(value, "solid")) {
|
|
jsx_runtime = Api.JsxRuntime.solid;
|
|
} else if (strings.eqlComptime(value, "react-jsx")) {
|
|
jsx_runtime = Api.JsxRuntime.automatic;
|
|
jsx_dev = false;
|
|
} else if (strings.eqlComptime(value, "react-jsxDEV")) {
|
|
jsx_runtime = Api.JsxRuntime.automatic;
|
|
jsx_dev = true;
|
|
} else {
|
|
try this.addError(expr.loc, "Invalid jsx runtime, only 'react', 'solid', 'react-jsx', and 'react-jsxDEV' are supported");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (json.get("jsxImportSource")) |expr| {
|
|
if (expr.asString(allocator)) |value| {
|
|
jsx_import_source = try allocator.dupe(u8, value);
|
|
}
|
|
}
|
|
|
|
if (json.get("jsxFragment")) |expr| {
|
|
if (expr.asString(allocator)) |value| {
|
|
jsx_fragment = try allocator.dupe(u8, value);
|
|
}
|
|
}
|
|
|
|
if (json.get("jsxFactory")) |expr| {
|
|
if (expr.asString(allocator)) |value| {
|
|
jsx_factory = try allocator.dupe(u8, value);
|
|
}
|
|
}
|
|
|
|
if (this.bunfig.jsx == null) {
|
|
this.bunfig.jsx = Api.Jsx{
|
|
.factory = bun.constStrToU8(jsx_factory),
|
|
.fragment = bun.constStrToU8(jsx_fragment),
|
|
.import_source = bun.constStrToU8(jsx_import_source),
|
|
.runtime = jsx_runtime,
|
|
.development = jsx_dev,
|
|
.react_fast_refresh = false,
|
|
};
|
|
} else {
|
|
var jsx: *Api.Jsx = &this.bunfig.jsx.?;
|
|
if (jsx_factory.len > 0) {
|
|
jsx.factory = bun.constStrToU8(jsx_factory);
|
|
}
|
|
if (jsx_fragment.len > 0) {
|
|
jsx.fragment = bun.constStrToU8(jsx_fragment);
|
|
}
|
|
if (jsx_import_source.len > 0) {
|
|
jsx.import_source = bun.constStrToU8(jsx_import_source);
|
|
}
|
|
jsx.runtime = jsx_runtime;
|
|
jsx.development = jsx_dev;
|
|
}
|
|
|
|
switch (comptime cmd) {
|
|
.AutoCommand, .DevCommand, .BuildCommand => {
|
|
if (json.get("publicDir")) |public_dir| {
|
|
try this.expect(public_dir, .e_string);
|
|
this.bunfig.router = Api.RouteConfig{
|
|
.extensions = &.{},
|
|
.dir = &.{},
|
|
.static_dir = try public_dir.data.e_string.string(allocator),
|
|
};
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
if (json.get("debug")) |expr| {
|
|
if (expr.get("editor")) |editor| {
|
|
if (editor.asString(allocator)) |value| {
|
|
this.ctx.debug.editor = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (json.get("macros")) |expr| {
|
|
// technical debt
|
|
this.ctx.debug.macros = PackageJSON.parseMacrosJSON(allocator, expr, this.log, this.source);
|
|
Analytics.Features.macros = true;
|
|
}
|
|
|
|
if (json.get("external")) |expr| {
|
|
switch (expr.data) {
|
|
.e_string => |str| {
|
|
var externals = try allocator.alloc(string, 1);
|
|
externals[0] = try str.string(allocator);
|
|
this.bunfig.external = externals;
|
|
},
|
|
.e_array => |array| {
|
|
var externals = try allocator.alloc(string, array.items.len);
|
|
|
|
for (array.items.slice(), 0..) |item, i| {
|
|
try this.expect(item, .e_string);
|
|
externals[i] = try item.data.e_string.string(allocator);
|
|
}
|
|
|
|
this.bunfig.external = externals;
|
|
},
|
|
else => try this.addError(expr.loc, "Expected string or array"),
|
|
}
|
|
}
|
|
|
|
if (json.get("framework")) |expr| {
|
|
try this.expect(expr, .e_string);
|
|
this.bunfig.framework = Api.FrameworkConfig{
|
|
.package = expr.asString(allocator).?,
|
|
};
|
|
}
|
|
|
|
if (json.get("loader")) |expr| {
|
|
try this.expect(expr, .e_object);
|
|
const properties = expr.data.e_object.properties.slice();
|
|
var loader_names = try this.allocator.alloc(string, properties.len);
|
|
var loader_values = try this.allocator.alloc(Api.Loader, properties.len);
|
|
|
|
for (properties, 0..) |item, i| {
|
|
var key = item.key.?.asString(allocator).?;
|
|
if (key.len == 0) continue;
|
|
if (key[0] != '.') {
|
|
try this.addError(item.key.?.loc, "file extension must start with a dot");
|
|
}
|
|
var value = item.value.?;
|
|
try this.expect(value, .e_string);
|
|
|
|
const loader = options.Loader.fromString(value.asString(allocator).?) orelse {
|
|
try this.addError(value.loc, "Invalid loader");
|
|
unreachable;
|
|
};
|
|
|
|
loader_names[i] = key;
|
|
loader_values[i] = loader.toAPI();
|
|
}
|
|
this.bunfig.loaders = Api.LoaderMap{
|
|
.extensions = loader_names,
|
|
.loaders = loader_values,
|
|
};
|
|
}
|
|
|
|
Analytics.Features.bunfig = true;
|
|
}
|
|
|
|
pub fn expect(this: *Parser, expr: js_ast.Expr, token: js_ast.Expr.Tag) !void {
|
|
if (@as(js_ast.Expr.Tag, expr.data) != token) {
|
|
this.log.addErrorFmt(this.source, expr.loc, this.allocator, "expected {} but received {}", .{
|
|
token,
|
|
@as(js_ast.Expr.Tag, expr.data),
|
|
}) catch unreachable;
|
|
return error.@"Invalid Bunfig";
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn parse(allocator: std.mem.Allocator, source: logger.Source, ctx: *Command.Context, comptime cmd: Command.Tag) !void {
|
|
const log_count = ctx.log.errors + ctx.log.warnings;
|
|
|
|
var expr = if (strings.eqlComptime(source.path.name.ext[1..], "toml")) TOML.parse(&source, ctx.log, allocator) catch |err| {
|
|
if (ctx.log.errors + ctx.log.warnings == log_count) {
|
|
ctx.log.addErrorFmt(&source, logger.Loc.Empty, allocator, "Failed to parse", .{}) catch unreachable;
|
|
}
|
|
return err;
|
|
} else JSONParser.ParseTSConfig(&source, ctx.log, allocator) catch |err| {
|
|
if (ctx.log.errors + ctx.log.warnings == log_count) {
|
|
ctx.log.addErrorFmt(&source, logger.Loc.Empty, allocator, "Failed to parse", .{}) catch unreachable;
|
|
}
|
|
return err;
|
|
};
|
|
|
|
var parser = Parser{
|
|
.json = expr,
|
|
.log = ctx.log,
|
|
.allocator = allocator,
|
|
.source = &source,
|
|
.bunfig = &ctx.args,
|
|
.ctx = ctx,
|
|
};
|
|
try parser.parse(cmd);
|
|
}
|
|
};
|