mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
Fixes ENG-21287
Build times, from `bun run build && echo '//' >> src/main.zig && time
bun run build`
|Platform|0.14.1|0.15.2|Speedup|
|-|-|-|-|
|macos debug asan|126.90s|106.27s|1.19x|
|macos debug noasan|60.62s|50.85s|1.19x|
|linux debug asan|292.77s|241.45s|1.21x|
|linux debug noasan|146.58s|130.94s|1.12x|
|linux debug use_llvm=false|n/a|78.27s|1.87x|
|windows debug asan|177.13s|142.55s|1.24x|
Runtime performance:
- next build memory usage may have gone up by 5%. Otherwise seems the
same. Some code with writers may have gotten slower, especially one
instance of a counting writer and a few instances of unbuffered writers
that now have vtable overhead.
- File size reduced by 800kb (from 100.2mb to 99.4mb)
Improvements:
- `@export` hack is no longer needed for watch
- native x86_64 backend for linux builds faster. to use it, set use_llvm
false and no_link_obj false. also set `ASAN_OPTIONS=detect_leaks=0`
otherwise it will spam the output with tens of thousands of lines of
debug info errors. may need to use the zig lldb fork for debugging.
- zig test-obj, which we will be able to use for zig unit tests
Still an issue:
- false 'dependency loop' errors remain in watch mode
- watch mode crashes observed
Follow-up:
- [ ] search `comptime Writer: type` and `comptime W: type` and remove
- [ ] remove format_mode in our zig fork
- [ ] remove deprecated.zig autoFormatLabelFallback
- [ ] remove deprecated.zig autoFormatLabel
- [ ] remove deprecated.BufferedWriter and BufferedReader
- [ ] remove override_no_export_cpp_apis as it is no longer needed
- [ ] css Parser(W) -> Parser, and remove all the comptime writer: type
params
- [ ] remove deprecated writer fully
Files that add lines:
```
649 src/deprecated.zig
167 scripts/pack-codegen-for-zig-team.ts
54 scripts/cleartrace-impl.js
46 scripts/cleartrace.ts
43 src/windows.zig
18 src/fs.zig
17 src/bun.js/ConsoleObject.zig
16 src/output.zig
12 src/bun.js/test/debug.zig
12 src/bun.js/node/node_fs.zig
8 src/env_loader.zig
7 src/css/printer.zig
7 src/cli/init_command.zig
7 src/bun.js/node.zig
6 src/string/escapeRegExp.zig
6 src/install/PnpmMatcher.zig
5 src/bun.js/webcore/Blob.zig
4 src/crash_handler.zig
4 src/bun.zig
3 src/install/lockfile/bun.lock.zig
3 src/cli/update_interactive_command.zig
3 src/cli/pack_command.zig
3 build.zig
2 src/Progress.zig
2 src/install/lockfile/lockfile_json_stringify_for_debugging.zig
2 src/css/small_list.zig
2 src/bun.js/webcore/prompt.zig
1 test/internal/ban-words.test.ts
1 test/internal/ban-limits.json
1 src/watcher/WatcherTrace.zig
1 src/transpiler.zig
1 src/shell/builtin/cp.zig
1 src/js_printer.zig
1 src/io/PipeReader.zig
1 src/install/bin.zig
1 src/css/selectors/selector.zig
1 src/cli/run_command.zig
1 src/bun.js/RuntimeTranspilerStore.zig
1 src/bun.js/bindings/JSRef.zig
1 src/bake/DevServer.zig
```
Files that remove lines:
```
-1 src/test/recover.zig
-1 src/sql/postgres/SocketMonitor.zig
-1 src/sql/mysql/MySQLRequestQueue.zig
-1 src/sourcemap/CodeCoverage.zig
-1 src/css/values/color_js.zig
-1 src/compile_target.zig
-1 src/bundler/linker_context/convertStmtsForChunk.zig
-1 src/bundler/bundle_v2.zig
-1 src/bun.js/webcore/blob/read_file.zig
-1 src/ast/base.zig
-2 src/sql/postgres/protocol/ArrayList.zig
-2 src/shell/builtin/mkdir.zig
-2 src/install/PackageManager/patchPackage.zig
-2 src/install/PackageManager/PackageManagerDirectories.zig
-2 src/fmt.zig
-2 src/css/declaration.zig
-2 src/css/css_parser.zig
-2 src/collections/baby_list.zig
-2 src/bun.js/bindings/ZigStackFrame.zig
-2 src/ast/E.zig
-3 src/StandaloneModuleGraph.zig
-3 src/deps/picohttp.zig
-3 src/deps/libuv.zig
-3 src/btjs.zig
-4 src/threading/Futex.zig
-4 src/shell/builtin/touch.zig
-4 src/meta.zig
-4 src/install/lockfile.zig
-4 src/css/selectors/parser.zig
-5 src/shell/interpreter.zig
-5 src/css/error.zig
-5 src/bun.js/web_worker.zig
-5 src/bun.js.zig
-6 src/cli/test_command.zig
-6 src/bun.js/VirtualMachine.zig
-6 src/bun.js/uuid.zig
-6 src/bun.js/bindings/JSValue.zig
-9 src/bun.js/test/pretty_format.zig
-9 src/bun.js/api/BunObject.zig
-14 src/install/install_binding.zig
-14 src/fd.zig
-14 src/bun.js/node/path.zig
-14 scripts/pack-codegen-for-zig-team.sh
-17 src/bun.js/test/diff_format.zig
```
`git diff --numstat origin/main...HEAD | awk '{ print ($1-$2)"\t"$3 }' |
sort -rn`
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Meghan Denny <meghan@bun.com>
Co-authored-by: tayor.fish <contact@taylor.fish>
712 lines
24 KiB
Zig
712 lines
24 KiB
Zig
/// This is the index to the automatically-generated part containing code that
|
|
/// calls "__export(exports, { ... getters ... })". This is used to generate
|
|
/// getters on an exports object for ES6 export statements, and is both for
|
|
/// ES6 star imports and CommonJS-style modules. All files have one of these,
|
|
/// although it may contain no statements if there is nothing to export.
|
|
pub const namespace_export_part_index = 0;
|
|
|
|
// There are three types.
|
|
// 1. Expr (expression)
|
|
// 2. Stmt (statement)
|
|
// 3. Binding
|
|
// Q: "What's the difference between an expression and a statement?"
|
|
// A: > Expression: Something which evaluates to a value. Example: 1+2/x
|
|
// > Statement: A line of code which does something. Example: GOTO 100
|
|
// > https://stackoverflow.com/questions/19132/expression-versus-statement/19224#19224
|
|
|
|
// Expr, Binding, and Stmt each wrap a Data:
|
|
// Data is where the actual data where the node lives.
|
|
// There are four possible versions of this structure:
|
|
// [ ] 1. *Expr, *Stmt, *Binding
|
|
// [ ] 1a. *Expr, *Stmt, *Binding something something dynamic dispatch
|
|
// [ ] 2. *Data
|
|
// [x] 3. Data.(*) (The union value in Data is a pointer)
|
|
// I chose #3 mostly for code simplification -- sometimes, the data is modified in-place.
|
|
// But also it uses the least memory.
|
|
// Since Data is a union, the size in bytes of Data is the max of all types
|
|
// So with #1 or #2, if S.Function consumes 768 bits, that means Data must be >= 768 bits
|
|
// Which means "true" in code now takes up over 768 bits, probably more than what v8 spends
|
|
// Instead, this approach means Data is the size of a pointer.
|
|
// It's not really clear which approach is best without benchmarking it.
|
|
// The downside with this approach is potentially worse memory locality, since the data for the node is somewhere else.
|
|
// But it could also be better memory locality due to smaller in-memory size (more likely to hit the cache)
|
|
// only benchmarks will provide an answer!
|
|
// But we must have pointers somewhere in here because can't have types that contain themselves
|
|
|
|
/// Slice that stores capacity and length in the same space as a regular slice.
|
|
pub const ExprNodeList = BabyList(Expr);
|
|
|
|
pub const StmtNodeList = []Stmt;
|
|
pub const BindingNodeList = []Binding;
|
|
|
|
pub const ImportItemStatus = enum(u2) {
|
|
none,
|
|
/// The linker doesn't report import/export mismatch errors
|
|
generated,
|
|
/// The printer will replace this import with "undefined"
|
|
missing,
|
|
|
|
pub fn jsonStringify(self: @This(), writer: anytype) !void {
|
|
return try writer.write(@tagName(self));
|
|
}
|
|
};
|
|
|
|
pub const AssignTarget = enum(u2) {
|
|
none = 0,
|
|
replace = 1, // "a = b"
|
|
update = 2, // "a += b"
|
|
pub fn jsonStringify(self: *const @This(), writer: anytype) !void {
|
|
return try writer.write(@tagName(self));
|
|
}
|
|
};
|
|
|
|
pub const LocRef = struct {
|
|
loc: logger.Loc = logger.Loc.Empty,
|
|
|
|
// TODO: remove this optional and make Ref a function getter
|
|
// That will make this struct 128 bits instead of 192 bits and we can remove some heap allocations
|
|
ref: ?Ref = null,
|
|
};
|
|
|
|
pub const Flags = struct {
|
|
pub const JSXElement = enum {
|
|
is_key_after_spread,
|
|
has_any_dynamic,
|
|
pub const Bitset = std.enums.EnumSet(JSXElement);
|
|
};
|
|
|
|
pub const Property = enum {
|
|
is_computed,
|
|
is_method,
|
|
is_static,
|
|
was_shorthand,
|
|
is_spread,
|
|
|
|
pub inline fn init(fields: Fields) Set {
|
|
return Set.init(fields);
|
|
}
|
|
|
|
pub const None = Set{};
|
|
pub const Fields = std.enums.EnumFieldStruct(Flags.Property, bool, false);
|
|
pub const Set = std.enums.EnumSet(Flags.Property);
|
|
};
|
|
|
|
pub const Function = enum {
|
|
is_async,
|
|
is_generator,
|
|
has_rest_arg,
|
|
has_if_scope,
|
|
|
|
is_forward_declaration,
|
|
|
|
/// This is true if the function is a method
|
|
is_unique_formal_parameters,
|
|
|
|
/// Only applicable to function statements.
|
|
is_export,
|
|
|
|
pub inline fn init(fields: Fields) Set {
|
|
return Set.init(fields);
|
|
}
|
|
|
|
pub const None = Set{};
|
|
pub const Fields = std.enums.EnumFieldStruct(Function, bool, false);
|
|
pub const Set = std.enums.EnumSet(Function);
|
|
};
|
|
};
|
|
|
|
pub const ClauseItem = struct {
|
|
/// The local alias used for the imported/exported symbol in the current module.
|
|
/// For imports: `import { foo as bar }` - "bar" is the alias
|
|
/// For exports: `export { foo as bar }` - "bar" is the alias
|
|
/// For re-exports: `export { foo as bar } from 'path'` - "bar" is the alias
|
|
alias: string,
|
|
alias_loc: logger.Loc = logger.Loc.Empty,
|
|
/// Reference to the actual symbol being imported/exported.
|
|
/// For imports: `import { foo as bar }` - ref to the symbol representing "foo" from the source module
|
|
/// For exports: `export { foo as bar }` - ref to the local symbol "foo"
|
|
/// For re-exports: `export { foo as bar } from 'path'` - ref to an intermediate symbol
|
|
name: LocRef,
|
|
|
|
/// This is the original name of the symbol stored in "Name". It's needed for
|
|
/// "SExportClause" statements such as this:
|
|
///
|
|
/// export {foo as bar} from 'path'
|
|
///
|
|
/// In this case both "foo" and "bar" are aliases because it's a re-export.
|
|
/// We need to preserve both aliases in case the symbol is renamed. In this
|
|
/// example, "foo" is "OriginalName" and "bar" is "Alias".
|
|
original_name: string = "",
|
|
|
|
pub const default_alias: string = "default";
|
|
};
|
|
|
|
pub const SlotCounts = struct {
|
|
slots: Symbol.SlotNamespace.CountsArray = Symbol.SlotNamespace.CountsArray.initFill(0),
|
|
|
|
pub fn unionMax(this: *SlotCounts, other: SlotCounts) void {
|
|
for (&this.slots.values, other.slots.values) |*a, b| {
|
|
if (a.* < b) a.* = b;
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const NameMinifier = struct {
|
|
head: std.array_list.Managed(u8),
|
|
tail: std.array_list.Managed(u8),
|
|
|
|
pub const default_head = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
|
|
pub const default_tail = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$";
|
|
|
|
pub fn init(allocator: std.mem.Allocator) NameMinifier {
|
|
return .{
|
|
.head = std.array_list.Managed(u8).init(allocator),
|
|
.tail = std.array_list.Managed(u8).init(allocator),
|
|
};
|
|
}
|
|
|
|
pub fn numberToMinifiedName(this: *NameMinifier, name: *std.array_list.Managed(u8), _i: isize) !void {
|
|
name.clearRetainingCapacity();
|
|
var i = _i;
|
|
var j = @as(usize, @intCast(@mod(i, 54)));
|
|
try name.appendSlice(this.head.items[j .. j + 1]);
|
|
i = @divFloor(i, 54);
|
|
|
|
while (i > 0) {
|
|
i -= 1;
|
|
j = @as(usize, @intCast(@mod(i, char_freq_count)));
|
|
try name.appendSlice(this.tail.items[j .. j + 1]);
|
|
i = @divFloor(i, char_freq_count);
|
|
}
|
|
}
|
|
|
|
pub fn defaultNumberToMinifiedName(allocator: std.mem.Allocator, _i: isize) !string {
|
|
var i = _i;
|
|
var j = @as(usize, @intCast(@mod(i, 54)));
|
|
var name = std.array_list.Managed(u8).init(allocator);
|
|
try name.appendSlice(default_head[j .. j + 1]);
|
|
i = @divFloor(i, 54);
|
|
|
|
while (i > 0) {
|
|
i -= 1;
|
|
j = @as(usize, @intCast(@mod(i, char_freq_count)));
|
|
try name.appendSlice(default_tail[j .. j + 1]);
|
|
i = @divFloor(i, char_freq_count);
|
|
}
|
|
|
|
return name.items;
|
|
}
|
|
};
|
|
|
|
pub const OptionalChain = enum(u1) {
|
|
/// "a?.b"
|
|
start,
|
|
|
|
/// "a?.b.c" => ".c" is .continuation
|
|
/// "(a?.b).c" => ".c" is null
|
|
continuation,
|
|
|
|
pub fn jsonStringify(self: @This(), writer: anytype) !void {
|
|
return try writer.write(@tagName(self));
|
|
}
|
|
};
|
|
|
|
pub const EnumValue = struct {
|
|
loc: logger.Loc,
|
|
ref: Ref,
|
|
name: []const u8,
|
|
value: ?ExprNodeIndex,
|
|
|
|
pub fn nameAsEString(enum_value: EnumValue, allocator: std.mem.Allocator) E.String {
|
|
return E.String.initReEncodeUTF8(enum_value.name, allocator);
|
|
}
|
|
};
|
|
|
|
pub const Catch = struct {
|
|
loc: logger.Loc,
|
|
binding: ?BindingNodeIndex = null,
|
|
body: StmtNodeList,
|
|
body_loc: logger.Loc,
|
|
};
|
|
|
|
pub const Finally = struct {
|
|
loc: logger.Loc,
|
|
stmts: StmtNodeList,
|
|
};
|
|
|
|
pub const Case = struct { loc: logger.Loc, value: ?ExprNodeIndex, body: StmtNodeList };
|
|
|
|
pub const ArrayBinding = struct {
|
|
binding: BindingNodeIndex,
|
|
default_value: ?ExprNodeIndex = null,
|
|
};
|
|
|
|
/// TLA => Top Level Await
|
|
pub const TlaCheck = struct {
|
|
depth: u32 = 0,
|
|
parent: Index.Int = Index.invalid.get(),
|
|
import_record_index: Index.Int = Index.invalid.get(),
|
|
};
|
|
|
|
pub const Span = struct {
|
|
text: string = "",
|
|
range: logger.Range = .{},
|
|
};
|
|
|
|
/// Inlined enum values can only be numbers and strings
|
|
/// This type special cases an encoding similar to JSValue, where nan-boxing is used
|
|
/// to encode both a 64-bit pointer or a 64-bit float using 64 bits.
|
|
pub const InlinedEnumValue = struct {
|
|
raw_data: u64,
|
|
|
|
pub const Decoded = union(enum) {
|
|
string: *E.String,
|
|
number: f64,
|
|
};
|
|
|
|
/// See JSCJSValue.h in WebKit for more details
|
|
const double_encode_offset = 1 << 49;
|
|
/// See PureNaN.h in WebKit for more details
|
|
const pure_nan: f64 = @bitCast(@as(u64, 0x7ff8000000000000));
|
|
|
|
fn purifyNaN(value: f64) f64 {
|
|
return if (std.math.isNan(value)) pure_nan else value;
|
|
}
|
|
|
|
pub fn encode(decoded: Decoded) InlinedEnumValue {
|
|
const encoded: InlinedEnumValue = .{ .raw_data = switch (decoded) {
|
|
.string => |ptr| @as(u48, @truncate(@intFromPtr(ptr))),
|
|
.number => |num| @as(u64, @bitCast(purifyNaN(num))) + double_encode_offset,
|
|
} };
|
|
if (Environment.allow_assert) {
|
|
bun.assert(switch (encoded.decode()) {
|
|
.string => |str| str == decoded.string,
|
|
.number => |num| @as(u64, @bitCast(num)) ==
|
|
@as(u64, @bitCast(purifyNaN(decoded.number))),
|
|
});
|
|
}
|
|
return encoded;
|
|
}
|
|
|
|
pub fn decode(encoded: InlinedEnumValue) Decoded {
|
|
if (encoded.raw_data > 0x0000FFFFFFFFFFFF) {
|
|
return .{ .number = @bitCast(encoded.raw_data - double_encode_offset) };
|
|
} else {
|
|
return .{ .string = @ptrFromInt(encoded.raw_data) };
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const ExportsKind = enum {
|
|
// This file doesn't have any kind of export, so it's impossible to say what
|
|
// kind of file this is. An empty file is in this category, for example.
|
|
none,
|
|
|
|
// The exports are stored on "module" and/or "exports". Calling "require()"
|
|
// on this module returns "module.exports". All imports to this module are
|
|
// allowed but may return undefined.
|
|
cjs,
|
|
|
|
// All export names are known explicitly. Calling "require()" on this module
|
|
// generates an exports object (stored in "exports") with getters for the
|
|
// export names. Named imports to this module are only allowed if they are
|
|
// in the set of export names.
|
|
esm,
|
|
|
|
// Some export names are known explicitly, but others fall back to a dynamic
|
|
// run-time object. This is necessary when using the "export * from" syntax
|
|
// with either a CommonJS module or an external module (i.e. a module whose
|
|
// export names are not known at compile-time).
|
|
//
|
|
// Calling "require()" on this module generates an exports object (stored in
|
|
// "exports") with getters for the export names. All named imports to this
|
|
// module are allowed. Direct named imports reference the corresponding export
|
|
// directly. Other imports go through property accesses on "exports".
|
|
esm_with_dynamic_fallback,
|
|
|
|
// Like "esm_with_dynamic_fallback", but the module was originally a CommonJS
|
|
// module.
|
|
esm_with_dynamic_fallback_from_cjs,
|
|
|
|
pub fn isDynamic(self: ExportsKind) bool {
|
|
return switch (self) {
|
|
.cjs, .esm_with_dynamic_fallback, .esm_with_dynamic_fallback_from_cjs => true,
|
|
.none, .esm => false,
|
|
};
|
|
}
|
|
|
|
pub fn isESMWithDynamicFallback(self: ExportsKind) bool {
|
|
return switch (self) {
|
|
.none, .cjs, .esm => false,
|
|
.esm_with_dynamic_fallback, .esm_with_dynamic_fallback_from_cjs => true,
|
|
};
|
|
}
|
|
|
|
pub fn jsonStringify(self: @This(), writer: anytype) !void {
|
|
return try writer.write(@tagName(self));
|
|
}
|
|
|
|
pub fn toModuleType(self: @This()) bun.options.ModuleType {
|
|
return switch (self) {
|
|
.none => .unknown,
|
|
.cjs => .cjs,
|
|
|
|
.esm_with_dynamic_fallback,
|
|
.esm_with_dynamic_fallback_from_cjs,
|
|
.esm,
|
|
=> .esm,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const DeclaredSymbol = struct {
|
|
ref: Ref,
|
|
is_top_level: bool = false,
|
|
|
|
pub const List = struct {
|
|
entries: bun.MultiArrayList(DeclaredSymbol) = .{},
|
|
|
|
pub fn refs(this: *const List) []Ref {
|
|
return this.entries.items(.ref);
|
|
}
|
|
|
|
pub fn toOwnedSlice(this: *List) List {
|
|
const new = this.*;
|
|
|
|
this.* = .{};
|
|
return new;
|
|
}
|
|
|
|
pub fn clone(this: *const List, allocator: std.mem.Allocator) !List {
|
|
return List{ .entries = try this.entries.clone(allocator) };
|
|
}
|
|
|
|
pub inline fn len(this: List) usize {
|
|
return this.entries.len;
|
|
}
|
|
|
|
pub fn append(this: *List, allocator: std.mem.Allocator, entry: DeclaredSymbol) !void {
|
|
try this.ensureUnusedCapacity(allocator, 1);
|
|
this.appendAssumeCapacity(entry);
|
|
}
|
|
|
|
pub fn appendList(this: *List, allocator: std.mem.Allocator, other: List) !void {
|
|
try this.ensureUnusedCapacity(allocator, other.len());
|
|
this.appendListAssumeCapacity(other);
|
|
}
|
|
|
|
pub fn appendListAssumeCapacity(this: *List, other: List) void {
|
|
this.entries.appendListAssumeCapacity(other.entries);
|
|
}
|
|
|
|
pub fn appendAssumeCapacity(this: *List, entry: DeclaredSymbol) void {
|
|
this.entries.appendAssumeCapacity(entry);
|
|
}
|
|
|
|
pub fn ensureTotalCapacity(this: *List, allocator: std.mem.Allocator, count: usize) !void {
|
|
try this.entries.ensureTotalCapacity(allocator, count);
|
|
}
|
|
|
|
pub fn ensureUnusedCapacity(this: *List, allocator: std.mem.Allocator, count: usize) !void {
|
|
try this.entries.ensureUnusedCapacity(allocator, count);
|
|
}
|
|
|
|
pub fn clearRetainingCapacity(this: *List) void {
|
|
this.entries.clearRetainingCapacity();
|
|
}
|
|
|
|
pub fn deinit(this: *List, allocator: std.mem.Allocator) void {
|
|
this.entries.deinit(allocator);
|
|
}
|
|
|
|
pub fn initCapacity(allocator: std.mem.Allocator, capacity: usize) !List {
|
|
var entries = bun.MultiArrayList(DeclaredSymbol){};
|
|
try entries.ensureUnusedCapacity(allocator, capacity);
|
|
return List{ .entries = entries };
|
|
}
|
|
|
|
pub fn fromSlice(allocator: std.mem.Allocator, entries: []const DeclaredSymbol) !List {
|
|
var this = try List.initCapacity(allocator, entries.len);
|
|
errdefer this.deinit(allocator);
|
|
for (entries) |entry| {
|
|
this.appendAssumeCapacity(entry);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
};
|
|
|
|
fn forEachTopLevelSymbolWithType(decls: *List, comptime Ctx: type, ctx: Ctx, comptime Fn: fn (Ctx, Ref) void) void {
|
|
var entries = decls.entries.slice();
|
|
const is_top_level = entries.items(.is_top_level);
|
|
const refs = entries.items(.ref);
|
|
|
|
// TODO: SIMD
|
|
for (is_top_level, refs) |top, ref| {
|
|
if (top) {
|
|
@call(bun.callmod_inline, Fn, .{ ctx, ref });
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn forEachTopLevelSymbol(decls: *List, ctx: anytype, comptime Fn: anytype) void {
|
|
forEachTopLevelSymbolWithType(decls, @TypeOf(ctx), ctx, Fn);
|
|
}
|
|
};
|
|
|
|
pub const Dependency = struct {
|
|
source_index: Index = Index.invalid,
|
|
part_index: Index.Int = 0,
|
|
|
|
pub const List = BabyList(Dependency);
|
|
};
|
|
|
|
pub const ExprList = std.array_list.Managed(Expr);
|
|
pub const StmtList = std.array_list.Managed(Stmt);
|
|
pub const BindingList = std.array_list.Managed(Binding);
|
|
|
|
// Each file is made up of multiple parts, and each part consists of one or
|
|
// more top-level statements. Parts are used for tree shaking and code
|
|
// splitting analysis. Individual parts of a file can be discarded by tree
|
|
// shaking and can be assigned to separate chunks (i.e. output files) by code
|
|
// splitting.
|
|
pub const Part = struct {
|
|
pub const ImportRecordIndices = BabyList(u32);
|
|
pub const List = BabyList(Part);
|
|
|
|
stmts: []Stmt = &([_]Stmt{}),
|
|
scopes: []*Scope = &([_]*Scope{}),
|
|
|
|
/// Each is an index into the file-level import record list
|
|
import_record_indices: ImportRecordIndices = .{},
|
|
|
|
/// All symbols that are declared in this part. Note that a given symbol may
|
|
/// have multiple declarations, and so may end up being declared in multiple
|
|
/// parts (e.g. multiple "var" declarations with the same name). Also note
|
|
/// that this list isn't deduplicated and may contain duplicates.
|
|
declared_symbols: DeclaredSymbol.List = .{},
|
|
|
|
/// An estimate of the number of uses of all symbols used within this part.
|
|
symbol_uses: SymbolUseMap = .{},
|
|
|
|
/// This tracks property accesses off of imported symbols. We don't know
|
|
/// during parsing if an imported symbol is going to be an inlined enum
|
|
/// value or not. This is only known during linking. So we defer adding
|
|
/// a dependency on these imported symbols until we know whether the
|
|
/// property access is an inlined enum value or not.
|
|
import_symbol_property_uses: SymbolPropertyUseMap = .{},
|
|
|
|
/// The indices of the other parts in this file that are needed if this part
|
|
/// is needed.
|
|
dependencies: Dependency.List = .{},
|
|
|
|
/// If true, this part can be removed if none of the declared symbols are
|
|
/// used. If the file containing this part is imported, then all parts that
|
|
/// don't have this flag enabled must be included.
|
|
can_be_removed_if_unused: bool = false,
|
|
|
|
/// This is used for generated parts that we don't want to be present if they
|
|
/// aren't needed. This enables tree shaking for these parts even if global
|
|
/// tree shaking isn't enabled.
|
|
force_tree_shaking: bool = false,
|
|
|
|
/// This is true if this file has been marked as live by the tree shaking
|
|
/// algorithm.
|
|
is_live: bool = false,
|
|
|
|
tag: Tag = Tag.none,
|
|
|
|
pub const Tag = enum {
|
|
none,
|
|
jsx_import,
|
|
runtime,
|
|
cjs_imports,
|
|
react_fast_refresh,
|
|
dirname_filename,
|
|
bun_test,
|
|
dead_due_to_inlining,
|
|
commonjs_named_export,
|
|
import_to_convert_from_require,
|
|
};
|
|
|
|
pub const SymbolUseMap = std.ArrayHashMapUnmanaged(Ref, Symbol.Use, RefHashCtx, false);
|
|
pub const SymbolPropertyUseMap = std.ArrayHashMapUnmanaged(Ref, bun.StringHashMapUnmanaged(Symbol.Use), RefHashCtx, false);
|
|
|
|
pub fn jsonStringify(self: *const Part, writer: anytype) !void {
|
|
return writer.write(self.stmts);
|
|
}
|
|
};
|
|
|
|
pub const Result = union(enum) {
|
|
already_bundled: AlreadyBundled,
|
|
cached: void,
|
|
ast: Ast,
|
|
|
|
pub const AlreadyBundled = enum {
|
|
bun,
|
|
bun_cjs,
|
|
bytecode,
|
|
bytecode_cjs,
|
|
};
|
|
};
|
|
|
|
pub const StmtOrExpr = union(enum) {
|
|
stmt: Stmt,
|
|
expr: Expr,
|
|
|
|
pub fn toExpr(stmt_or_expr: StmtOrExpr) Expr {
|
|
return switch (stmt_or_expr) {
|
|
.expr => |expr| expr,
|
|
.stmt => |stmt| switch (stmt.data) {
|
|
.s_function => |s| Expr.init(E.Function, .{ .func = s.func }, stmt.loc),
|
|
.s_class => |s| Expr.init(E.Class, s.class, stmt.loc),
|
|
else => Output.panic("Unexpected statement type in default export: .{s}", .{@tagName(stmt.data)}),
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const NamedImport = struct {
|
|
// Parts within this file that use this import
|
|
local_parts_with_uses: BabyList(u32) = BabyList(u32){},
|
|
|
|
// The original export name from the source module being imported.
|
|
// Examples:
|
|
// - `import { foo } from 'module'` → alias = "foo"
|
|
// - `import { foo as bar } from 'module'` → alias = "foo" (original export name)
|
|
// - `import * as ns from 'module'` → alias_is_star = true, alias = ""
|
|
// This field is used by the bundler to match imports with their corresponding
|
|
// exports and for error reporting when imports can't be resolved.
|
|
alias: ?string,
|
|
alias_loc: ?logger.Loc = null,
|
|
namespace_ref: ?Ref,
|
|
import_record_index: u32,
|
|
|
|
// If true, the alias refers to the entire export namespace object of a
|
|
// module. This is no longer represented as an alias called "*" because of
|
|
// the upcoming "Arbitrary module namespace identifier names" feature:
|
|
// https://github.com/tc39/ecma262/pull/2154
|
|
alias_is_star: bool = false,
|
|
|
|
// It's useful to flag exported imports because if they are in a TypeScript
|
|
// file, we can't tell if they are a type or a value.
|
|
is_exported: bool = false,
|
|
};
|
|
|
|
pub const NamedExport = struct {
|
|
ref: Ref,
|
|
alias_loc: logger.Loc,
|
|
};
|
|
|
|
pub const StrictModeKind = enum(u4) {
|
|
sloppy_mode,
|
|
explicit_strict_mode,
|
|
implicit_strict_mode_import,
|
|
implicit_strict_mode_export,
|
|
implicit_strict_mode_top_level_await,
|
|
implicit_strict_mode_class,
|
|
pub fn jsonStringify(self: @This(), writer: anytype) !void {
|
|
return try writer.write(@tagName(self));
|
|
}
|
|
};
|
|
|
|
pub fn printmem(comptime format: string, args: anytype) void {
|
|
defer Output.flush();
|
|
Output.initTest();
|
|
Output.print(format, args);
|
|
}
|
|
|
|
pub const ToJSError = error{
|
|
@"Cannot convert argument type to JS",
|
|
@"Cannot convert identifier to JS. Try a statically-known value",
|
|
MacroError,
|
|
OutOfMemory,
|
|
JSError,
|
|
JSTerminated,
|
|
};
|
|
|
|
/// Say you need to allocate a bunch of tiny arrays
|
|
/// You could just do separate allocations for each, but that is slow
|
|
/// With std.ArrayList, pointers invalidate on resize and that means it will crash.
|
|
/// So a better idea is to batch up your allocations into one larger allocation
|
|
/// and then just make all the arrays point to different parts of the larger allocation
|
|
pub fn NewBatcher(comptime Type: type) type {
|
|
return struct {
|
|
head: []Type,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, count: usize) !@This() {
|
|
const all = try allocator.alloc(Type, count);
|
|
return @This(){ .head = all };
|
|
}
|
|
|
|
pub fn done(this: *@This()) void {
|
|
bun.assert(this.head.len == 0); // count to init() was too large, overallocation
|
|
}
|
|
|
|
pub fn eat(this: *@This(), value: Type) *Type {
|
|
return @as(*Type, @ptrCast(&this.head.eat1(value).ptr));
|
|
}
|
|
|
|
pub fn eat1(this: *@This(), value: Type) []Type {
|
|
var prev = this.head[0..1];
|
|
prev[0] = value;
|
|
this.head = this.head[1..];
|
|
return prev;
|
|
}
|
|
|
|
pub fn next(this: *@This(), values: anytype) []Type {
|
|
this.head[0..values.len].* = values;
|
|
const prev = this.head[0..values.len];
|
|
this.head = this.head[values.len..];
|
|
return prev;
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const ASTMemoryAllocator = @import("./ast/ASTMemoryAllocator.zig");
|
|
pub const Ast = @import("./ast/Ast.zig");
|
|
pub const Binding = @import("./ast/Binding.zig");
|
|
pub const BindingNodeIndex = Binding;
|
|
pub const BundledAst = @import("./ast/BundledAst.zig");
|
|
pub const E = @import("./ast/E.zig");
|
|
pub const Expr = @import("./ast/Expr.zig");
|
|
pub const ExprNodeIndex = Expr;
|
|
pub const G = @import("./ast/G.zig");
|
|
pub const Macro = @import("./ast/Macro.zig");
|
|
pub const Op = @import("./ast/Op.zig");
|
|
pub const S = @import("./ast/S.zig");
|
|
pub const Scope = @import("./ast/Scope.zig");
|
|
pub const ServerComponentBoundary = @import("./ast/ServerComponentBoundary.zig");
|
|
pub const Stmt = @import("./ast/Stmt.zig");
|
|
pub const StmtNodeIndex = Stmt;
|
|
pub const Symbol = @import("./ast/Symbol.zig");
|
|
pub const B = @import("./ast/B.zig").B;
|
|
pub const NewStore = @import("./ast/NewStore.zig").NewStore;
|
|
pub const UseDirective = @import("./ast/UseDirective.zig").UseDirective;
|
|
|
|
pub const CharFreq = @import("./ast/CharFreq.zig");
|
|
const char_freq_count = CharFreq.char_freq_count;
|
|
|
|
pub const TS = @import("./ast/TS.zig");
|
|
pub const TSNamespaceMember = TS.TSNamespaceMember;
|
|
pub const TSNamespaceMemberMap = TS.TSNamespaceMemberMap;
|
|
pub const TSNamespaceScope = TS.TSNamespaceScope;
|
|
|
|
pub const Index = @import("./ast/base.zig").Index;
|
|
pub const Ref = @import("./ast/base.zig").Ref;
|
|
pub const RefCtx = @import("./ast/base.zig").RefCtx;
|
|
pub const RefHashCtx = @import("./ast/base.zig").RefHashCtx;
|
|
|
|
pub const BabyList = bun.BabyList;
|
|
|
|
const string = []const u8;
|
|
|
|
const std = @import("std");
|
|
const TypeScript = @import("./js_parser.zig").TypeScript;
|
|
|
|
const bun = @import("bun");
|
|
const Environment = bun.Environment;
|
|
const Output = bun.Output;
|
|
const logger = bun.logger;
|
|
const strings = bun.strings;
|