pub const std = @import("std"); pub const logger = @import("./logger.zig"); pub const js_lexer = @import("./js_lexer.zig"); pub const importRecord = @import("./import_record.zig"); pub const js_ast = @import("./js_ast.zig"); pub const options = @import("./options.zig"); pub const js_printer = @import("./js_printer.zig"); pub const renamer = @import("./renamer.zig"); const _runtime = @import("./runtime.zig"); pub const RuntimeImports = _runtime.Runtime.Imports; pub const RuntimeFeatures = _runtime.Runtime.Features; pub const RuntimeNames = _runtime.Runtime.Names; pub const fs = @import("./fs.zig"); const _hash_map = @import("./hash_map.zig"); const bun = @import("./global.zig"); const string = bun.string; const Output = bun.Output; const Global = bun.Global; const Environment = bun.Environment; const strings = bun.strings; const MutableString = @import("./string_mutable.zig").MutableString; const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; const C = bun.C; const G = js_ast.G; const Define = @import("./defines.zig").Define; const DefineData = @import("./defines.zig").DefineData; const FeatureFlags = @import("./feature_flags.zig"); pub const isPackagePath = @import("./resolver/resolver.zig").isPackagePath; pub const ImportKind = importRecord.ImportKind; pub const BindingNodeIndex = js_ast.BindingNodeIndex; const Decl = G.Decl; const Property = G.Property; const Arg = G.Arg; const Allocator = std.mem.Allocator; pub const StmtNodeIndex = js_ast.StmtNodeIndex; pub const ExprNodeIndex = js_ast.ExprNodeIndex; pub const ExprNodeList = js_ast.ExprNodeList; pub const StmtNodeList = js_ast.StmtNodeList; pub const BindingNodeList = js_ast.BindingNodeList; const ComptimeStringMap = @import("./comptime_string_map.zig").ComptimeStringMap; const JSC = @import("javascript_core"); fn _disabledAssert(_: bool) void { if (!Environment.allow_assert) @compileLog("assert is missing an if (Environment.allow_assert)"); unreachable; } const assert = if (Environment.allow_assert) std.debug.assert else _disabledAssert; const ExprListLoc = struct { list: ExprNodeList, loc: logger.Loc, }; pub const LocRef = js_ast.LocRef; pub const S = js_ast.S; pub const B = js_ast.B; pub const T = js_lexer.T; pub const E = js_ast.E; pub const Stmt = js_ast.Stmt; pub const Expr = js_ast.Expr; pub const Binding = js_ast.Binding; pub const Symbol = js_ast.Symbol; pub const Level = js_ast.Op.Level; pub const Op = js_ast.Op; pub const Scope = js_ast.Scope; pub const locModuleScope = logger.Loc{ .start = -100 }; const Ref = @import("./ast/base.zig").Ref; pub const StringHashMap = _hash_map.StringHashMap; pub const AutoHashMap = _hash_map.AutoHashMap; const StringHashMapUnamanged = _hash_map.StringHashMapUnamanged; const ObjectPool = @import("./pool.zig").ObjectPool; const NodeFallbackModules = @import("./node_fallbacks.zig"); // Dear reader, // There are some things you should know about this file to make it easier for humans to read // "P" is the internal parts of the parser // "p.e" allocates a new Expr // "p.b" allocates a new Binding // "p.s" allocates a new Stmt // We do it this way so if we want to refactor how these are allocated in the future, we only have to modify one function to change it everywhere // Everything in JavaScript is either an Expression, a Binding, or a Statement. // Expression: foo(1) // Statement: let a = 1; // Binding: a // While the names for Expr, Binding, and Stmt are directly copied from esbuild, those were likely inspired by Go's parser. // which is another example of a very fast parser. const ScopeOrderList = std.ArrayListUnmanaged(?ScopeOrder); const JSXFactoryName = "JSX"; const JSXAutomaticName = "jsx_module"; // kept as a static reference const exports_string_name: string = "exports"; const MacroRefs = std.AutoArrayHashMap(Ref, u32); pub const AllocatedNamesPool = ObjectPool( std.ArrayList(string), struct { pub fn init(allocator: std.mem.Allocator) anyerror!std.ArrayList(string) { return std.ArrayList(string).init(allocator); } }.init, true, 4, ); fn foldStringAddition(lhs: Expr, rhs: Expr) ?Expr { switch (lhs.data) { .e_string => |left| { if (rhs.data == .e_string and left.isUTF8() and rhs.data.e_string.isUTF8()) { lhs.data.e_string.push(rhs.data.e_string); return lhs; } }, .e_binary => |bin| { // 123 + "bar" + "baz" if (bin.op == .bin_add) { if (foldStringAddition(bin.right, rhs)) |out| { return Expr.init(E.Binary, E.Binary{ .op = bin.op, .left = bin.left, .right = out }, lhs.loc); } } }, else => {}, } return null; } // If we are currently in a hoisted child of the module scope, relocate these // declarations to the top level and return an equivalent assignment statement. // Make sure to check that the declaration kind is "var" before calling this. // And make sure to check that the returned statement is not the zero value. // // This is done to make some transformations non-destructive // Without relocating vars to the top level, simplifying this: // if (false) var foo = 1; // to nothing is unsafe // Because "foo" was defined. And now it's not. pub const RelocateVars = struct { pub const Mode = enum { normal, for_in_or_for_of }; stmt: ?Stmt = null, ok: bool = false, }; const VisitArgsOpts = struct { body: []Stmt = &([_]Stmt{}), has_rest_arg: bool = false, // This is true if the function is an arrow function or a method is_unique_formal_parameters: bool = false, }; const BunJSX = struct { pub threadlocal var bun_jsx_identifier: E.Identifier = undefined; }; pub fn ExpressionTransposer( comptime Kontext: type, visitor: fn (ptr: *Kontext, arg: Expr, state: anytype) Expr, ) type { return struct { pub const Context = Kontext; pub const This = @This(); context: *Context, pub fn init(c: *Context) This { return This{ .context = c, }; } pub fn maybeTransposeIf(self: *This, arg: Expr, state: anytype) Expr { switch (arg.data) { .e_if => |ex| { ex.yes = self.maybeTransposeIf(ex.yes, state); ex.no = self.maybeTransposeIf(ex.no, state); return arg; }, else => { return visitor(self.context, arg, state); }, } } }; } pub fn locAfterOp(e: E.Binary) logger.Loc { if (e.left.loc.start < e.right.loc.start) { return e.right.loc; } else { // handle the case when we have transposed the operands return e.left.loc; } } const ExportsStringName = "exports"; const TransposeState = struct { is_await_target: bool = false, is_then_catch_target: bool = false, loc: logger.Loc, }; var true_args = &[_]Expr{ .{ .data = .{ .e_boolean = .{ .value = true } }, .loc = logger.Loc.Empty, }, }; const JSXTag = struct { pub const TagType = enum { fragment, tag }; pub const Data = union(TagType) { fragment: u8, tag: Expr, pub fn asExpr(d: *const Data) ?ExprNodeIndex { switch (d.*) { .tag => |tag| { return tag; }, else => { return null; }, } } }; data: Data, range: logger.Range, name: string = "", pub fn parse(comptime P: type, p: *P) anyerror!JSXTag { const loc = p.lexer.loc(); // A missing tag is a fragment if (p.lexer.token == .t_greater_than) { return JSXTag{ .range = logger.Range{ .loc = loc, .len = 0 }, .data = Data{ .fragment = 1 }, .name = "", }; } // The tag is an identifier var name = p.lexer.identifier; var tag_range = p.lexer.range(); try p.lexer.expectInsideJSXElement(.t_identifier); // Certain identifiers are strings //