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 _global = @import("../global.zig"); const string = _global.string; const Output = _global.Output; const Global = _global.Global; const Environment = _global.Environment; const strings = _global.strings; const MutableString = _global.MutableString; const stringZ = _global.stringZ; const default_allocator = _global.default_allocator; const C = _global.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; 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; 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: 1 // 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, ); // 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, }; pub const TypeScript = struct { // This function is taken from the official TypeScript compiler source code: // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts pub fn canFollowTypeArgumentsInExpression(token: js_lexer.T) bool { switch (token) { // These are the only tokens can legally follow a type argument list. So we // definitely want to treat them as type arg lists. .t_open_paren, // foo( .t_no_substitution_template_literal, // foo `...` // foo `...${100}...` .t_template_head, => { return true; }, // These cases can't legally follow a type arg list. However, they're not // legal expressions either. The user is probably in the middle of a // generic type. So treat it as such. .t_dot, // foo. .t_close_paren, // foo) .t_close_bracket, // foo] .t_colon, // foo: .t_semicolon, // foo; .t_question, // foo? .t_equals_equals, // foo == .t_equals_equals_equals, // foo === .t_exclamation_equals, // foo != .t_exclamation_equals_equals, // foo !== .t_ampersand_ampersand, // foo && .t_bar_bar, // foo || .t_question_question, // foo ?? .t_caret, // foo ^ .t_ampersand, // foo & .t_bar, // foo | .t_close_brace, // foo } .t_end_of_file, // foo => { return true; }, // We don't want to treat these as type arguments. Otherwise we'll parse // this as an invocation expression. Instead, we want to parse out the // expression in isolation from the type arguments. .t_comma, // foo, .t_open_brace, // foo { => { return false; }, else => { // Anything else treat as an expression return false; }, } } pub const Identifier = struct { pub const StmtIdentifier = enum { s_type, s_namespace, s_abstract, s_module, s_interface, s_declare, }; pub fn forStr(str: string) ?StmtIdentifier { switch (str.len) { "type".len => return if (strings.eqlComptimeIgnoreLen(str, "type")) .s_type else null, "interface".len => { if (strings.eqlComptime(str, "interface")) { return .s_interface; } else if (strings.eqlComptime(str, "namespace")) { return .s_namespace; } else { return null; } }, "abstract".len => { if (strings.eqlComptime(str, "abstract")) { return .s_abstract; } else { return null; } }, "declare".len => { if (strings.eqlComptime(str, "declare")) { return .s_declare; } else { return null; } }, "module".len => { if (strings.eqlComptime(str, "module")) { return .s_module; } else { return null; } }, else => return null, } } pub const IMap = std.ComptimeStringMap(Kind, .{ .{ "unique", .unique }, .{ "abstract", .abstract }, .{ "asserts", .asserts }, .{ "keyof", .prefix }, .{ "readonly", .prefix }, .{ "infer", .prefix }, .{ "any", .primitive }, .{ "never", .primitive }, .{ "unknown", .primitive }, .{ "undefined", .primitive }, .{ "object", .primitive }, .{ "number", .primitive }, .{ "string", .primitive }, .{ "boolean", .primitive }, .{ "bigint", .primitive }, .{ "symbol", .primitive }, }); pub const Kind = enum { normal, unique, abstract, asserts, prefix, primitive, }; }; pub const SkipTypeOptions = struct { is_return_type: bool = false, }; }; // We must prevent collisions from generated names. // We want to avoid adding a pass over all the symbols in the file. // To do that: // For every generated symbol, we reserve two backup symbol names // If any usages of the preferred ref, we swap original_name with the backup // If any usages of the backup ref, we swap original_name with the internal // We *assume* the internal name is never used. // In practice, it is possible. But, the internal names are so crazy long you'd have to be deliberately trying to use them. const GeneratedSymbol = @import("../runtime.zig").Runtime.GeneratedSymbol; pub const ImportScanner = struct { stmts: []Stmt = &([_]Stmt{}), kept_import_equals: bool = false, removed_import_equals: bool = false, pub fn scan(comptime P: type, p: *P, stmts: []Stmt, will_transform_to_common_js: bool) !ImportScanner { var scanner = ImportScanner{}; var stmts_end: usize = 0; const allocator = p.allocator; const is_typescript_enabled: bool = comptime P.parser_features.typescript; for (stmts) |_stmt| { // zls needs the hint, it seems. var stmt: Stmt = _stmt; switch (stmt.data) { .s_import => |st__| { var st = st__.*; defer { st__.* = st; } var record: *ImportRecord = &p.import_records.items[st.import_record_index]; if (record.path.isMacro()) { record.is_unused = true; record.path.is_disabled = true; continue; } // The official TypeScript compiler always removes unused imported // symbols. However, we deliberately deviate from the official // TypeScript compiler's behavior doing this in a specific scenario: // we are not bundling, symbol renaming is off, and the tsconfig.json // "importsNotUsedAsValues" setting is present and is not set to // "remove". // // This exists to support the use case of compiling partial modules for // compile-to-JavaScript languages such as Svelte. These languages try // to reference imports in ways that are impossible for esbuild to know // about when esbuild is only given a partial module to compile. Here // is an example of some Svelte code that might use esbuild to convert // TypeScript to JavaScript: // // //
//

Hello {name}!

// //
// // Tools that use esbuild to compile TypeScript code inside a Svelte // file like this only give esbuild the contents of the