diff --git a/src/ast/parse.zig b/src/ast/parse.zig index c7c026f091..d516092436 100644 --- a/src/ast/parse.zig +++ b/src/ast/parse.zig @@ -40,7 +40,7 @@ pub fn Parse( return p.parseExprCommon(level, null, flags, expr); } - pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags, expr: *Expr) anyerror!void { + pub noinline fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags, expr: *Expr) anyerror!void { if (!p.stack_check.isSafeToRecurse()) { try bun.throwStackOverflow(); } diff --git a/src/ast/parseStmt.zig b/src/ast/parseStmt.zig index b5d9e3b9c8..8d53d35b65 100644 --- a/src/ast/parseStmt.zig +++ b/src/ast/parseStmt.zig @@ -10,197 +10,234 @@ pub fn ParseStmt( const is_typescript_enabled = P.is_typescript_enabled; const track_symbol_usage_during_parse_pass = P.track_symbol_usage_during_parse_pass; - pub fn parseStmt(p: *P, opts: *ParseStatementOptions) anyerror!Stmt { - if (!p.stack_check.isSafeToRecurse()) { - try bun.throwStackOverflow(); + fn t_semicolon(p: *P) anyerror!Stmt { + try p.lexer.next(); + return Stmt.empty(); + } + + fn t_export(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + const previous_export_keyword = p.esm_export_keyword; + if (opts.is_module_scope) { + p.esm_export_keyword = p.lexer.range(); + } else if (!opts.is_namespace_scope) { + try p.lexer.unexpected(); + return error.SyntaxError; + } + try p.lexer.next(); + + // TypeScript decorators only work on class declarations + // "@decorator export class Foo {}" + // "@decorator export abstract class Foo {}" + // "@decorator export default class Foo {}" + // "@decorator export default abstract class Foo {}" + // "@decorator export declare class Foo {}" + // "@decorator export declare abstract class Foo {}" + if (opts.ts_decorators != null and p.lexer.token != js_lexer.T.t_class and + p.lexer.token != js_lexer.T.t_default and + !p.lexer.isContextualKeyword("abstract") and + !p.lexer.isContextualKeyword("declare")) + { + try p.lexer.expected(js_lexer.T.t_class); } - const loc = p.lexer.loc(); - switch (p.lexer.token) { - .t_semicolon => { - try p.lexer.next(); - return Stmt.empty(); + T.t_class, T.t_const, T.t_function, T.t_var => { + opts.is_export = true; + return p.parseStmt(opts); }, - .t_export => { - const previous_export_keyword = p.esm_export_keyword; - if (opts.is_module_scope) { - p.esm_export_keyword = p.lexer.range(); - } else if (!opts.is_namespace_scope) { + T.t_import => { + // "export import foo = bar" + if (is_typescript_enabled and (opts.is_module_scope or opts.is_namespace_scope)) { + opts.is_export = true; + return p.parseStmt(opts); + } + + try p.lexer.unexpected(); + return error.SyntaxError; + }, + + T.t_enum => { + if (!is_typescript_enabled) { try p.lexer.unexpected(); return error.SyntaxError; } + + opts.is_export = true; + return p.parseStmt(opts); + }, + + T.t_identifier => { + if (p.lexer.isContextualKeyword("let")) { + opts.is_export = true; + return p.parseStmt(opts); + } + + if (comptime is_typescript_enabled) { + if (opts.is_typescript_declare and p.lexer.isContextualKeyword("as")) { + // "export as namespace ns;" + try p.lexer.next(); + try p.lexer.expectContextualKeyword("namespace"); + try p.lexer.expect(T.t_identifier); + try p.lexer.expectOrInsertSemicolon(); + + return p.s(S.TypeScript{}, loc); + } + } + + if (p.lexer.isContextualKeyword("async")) { + const asyncRange = p.lexer.range(); + try p.lexer.next(); + if (p.lexer.has_newline_before) { + try p.log.addRangeError(p.source, asyncRange, "Unexpected newline after \"async\""); + } + + try p.lexer.expect(T.t_function); + opts.is_export = true; + return try p.parseFnStmt(loc, opts, asyncRange); + } + + if (is_typescript_enabled) { + if (TypeScript.Identifier.forStr(p.lexer.identifier)) |ident| { + switch (ident) { + .s_type => { + // "export type foo = ..." + const type_range = p.lexer.range(); + try p.lexer.next(); + if (p.lexer.has_newline_before) { + try p.log.addErrorFmt(p.source, type_range.end(), p.allocator, "Unexpected newline after \"type\"", .{}); + return error.SyntaxError; + } + var skipper = ParseStatementOptions{ .is_module_scope = opts.is_module_scope, .is_export = true }; + try p.skipTypeScriptTypeStmt(&skipper); + return p.s(S.TypeScript{}, loc); + }, + .s_namespace, .s_abstract, .s_module, .s_interface => { + // "export namespace Foo {}" + // "export abstract class Foo {}" + // "export module Foo {}" + // "export interface Foo {}" + opts.is_export = true; + return try p.parseStmt(opts); + }, + .s_declare => { + // "export declare class Foo {}" + opts.is_export = true; + opts.lexical_decl = .allow_all; + opts.is_typescript_declare = true; + return try p.parseStmt(opts); + }, + } + } + } + + try p.lexer.unexpected(); + return error.SyntaxError; + }, + + T.t_default => { + if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { + try p.lexer.unexpected(); + return error.SyntaxError; + } + + const defaultLoc = p.lexer.loc(); try p.lexer.next(); // TypeScript decorators only work on class declarations - // "@decorator export class Foo {}" - // "@decorator export abstract class Foo {}" // "@decorator export default class Foo {}" // "@decorator export default abstract class Foo {}" - // "@decorator export declare class Foo {}" - // "@decorator export declare abstract class Foo {}" - if (opts.ts_decorators != null and p.lexer.token != js_lexer.T.t_class and - p.lexer.token != js_lexer.T.t_default and - !p.lexer.isContextualKeyword("abstract") and - !p.lexer.isContextualKeyword("declare")) - { - try p.lexer.expected(js_lexer.T.t_class); + if (opts.ts_decorators != null and p.lexer.token != T.t_class and !p.lexer.isContextualKeyword("abstract")) { + try p.lexer.expected(T.t_class); } - switch (p.lexer.token) { - T.t_class, T.t_const, T.t_function, T.t_var => { - opts.is_export = true; - return p.parseStmt(opts); - }, - - T.t_import => { - // "export import foo = bar" - if (is_typescript_enabled and (opts.is_module_scope or opts.is_namespace_scope)) { - opts.is_export = true; - return p.parseStmt(opts); - } - - try p.lexer.unexpected(); - return error.SyntaxError; - }, - - T.t_enum => { - if (!is_typescript_enabled) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - - opts.is_export = true; - return p.parseStmt(opts); - }, - - T.t_identifier => { - if (p.lexer.isContextualKeyword("let")) { - opts.is_export = true; - return p.parseStmt(opts); - } - - if (comptime is_typescript_enabled) { - if (opts.is_typescript_declare and p.lexer.isContextualKeyword("as")) { - // "export as namespace ns;" - try p.lexer.next(); - try p.lexer.expectContextualKeyword("namespace"); - try p.lexer.expect(T.t_identifier); - try p.lexer.expectOrInsertSemicolon(); - - return p.s(S.TypeScript{}, loc); - } - } - - if (p.lexer.isContextualKeyword("async")) { - const asyncRange = p.lexer.range(); - try p.lexer.next(); - if (p.lexer.has_newline_before) { - try p.log.addRangeError(p.source, asyncRange, "Unexpected newline after \"async\""); - } - - try p.lexer.expect(T.t_function); - opts.is_export = true; - return try p.parseFnStmt(loc, opts, asyncRange); - } - - if (is_typescript_enabled) { - if (TypeScript.Identifier.forStr(p.lexer.identifier)) |ident| { - switch (ident) { - .s_type => { - // "export type foo = ..." - const type_range = p.lexer.range(); - try p.lexer.next(); - if (p.lexer.has_newline_before) { - try p.log.addErrorFmt(p.source, type_range.end(), p.allocator, "Unexpected newline after \"type\"", .{}); - return error.SyntaxError; - } - var skipper = ParseStatementOptions{ .is_module_scope = opts.is_module_scope, .is_export = true }; - try p.skipTypeScriptTypeStmt(&skipper); - return p.s(S.TypeScript{}, loc); - }, - .s_namespace, .s_abstract, .s_module, .s_interface => { - // "export namespace Foo {}" - // "export abstract class Foo {}" - // "export module Foo {}" - // "export interface Foo {}" - opts.is_export = true; - return try p.parseStmt(opts); - }, - .s_declare => { - // "export declare class Foo {}" - opts.is_export = true; - opts.lexical_decl = .allow_all; - opts.is_typescript_declare = true; - return try p.parseStmt(opts); - }, - } - } - } - - try p.lexer.unexpected(); - return error.SyntaxError; - }, - - T.t_default => { - if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - - const defaultLoc = p.lexer.loc(); + if (p.lexer.isContextualKeyword("async")) { + const async_range = p.lexer.range(); + try p.lexer.next(); + if (p.lexer.token == T.t_function and !p.lexer.has_newline_before) { try p.lexer.next(); - - // TypeScript decorators only work on class declarations - // "@decorator export default class Foo {}" - // "@decorator export default abstract class Foo {}" - if (opts.ts_decorators != null and p.lexer.token != T.t_class and !p.lexer.isContextualKeyword("abstract")) { - try p.lexer.expected(T.t_class); + var stmtOpts = ParseStatementOptions{ + .is_name_optional = true, + .lexical_decl = .allow_all, + }; + const stmt = try p.parseFnStmt(loc, &stmtOpts, async_range); + if (@as(Stmt.Tag, stmt.data) == .s_type_script) { + // This was just a type annotation + return stmt; } - if (p.lexer.isContextualKeyword("async")) { - const async_range = p.lexer.range(); - try p.lexer.next(); - if (p.lexer.token == T.t_function and !p.lexer.has_newline_before) { - try p.lexer.next(); - var stmtOpts = ParseStatementOptions{ - .is_name_optional = true, - .lexical_decl = .allow_all, - }; - const stmt = try p.parseFnStmt(loc, &stmtOpts, async_range); - if (@as(Stmt.Tag, stmt.data) == .s_type_script) { - // This was just a type annotation - return stmt; + const defaultName = if (stmt.data.s_function.func.name) |name| + js_ast.LocRef{ .loc = name.loc, .ref = name.ref } + else + try p.createDefaultName(defaultLoc); + + const value = js_ast.StmtOrExpr{ .stmt = stmt }; + return p.s(S.ExportDefault{ .default_name = defaultName, .value = value }, loc); + } + + const defaultName = try createDefaultName(p, loc); + + var expr = try p.parseAsyncPrefixExpr(async_range, Level.comma); + try p.parseSuffix(&expr, Level.comma, null, Expr.EFlags.none); + try p.lexer.expectOrInsertSemicolon(); + const value = js_ast.StmtOrExpr{ .expr = expr }; + p.has_export_default = true; + return p.s(S.ExportDefault{ .default_name = defaultName, .value = value }, loc); + } + + if (p.lexer.token == .t_function or p.lexer.token == .t_class or p.lexer.isContextualKeyword("interface")) { + var _opts = ParseStatementOptions{ + .ts_decorators = opts.ts_decorators, + .is_name_optional = true, + .lexical_decl = .allow_all, + }; + const stmt = try p.parseStmt(&_opts); + + const default_name: js_ast.LocRef = default_name_getter: { + switch (stmt.data) { + // This was just a type annotation + .s_type_script => { + return stmt; + }, + + .s_function => |func_container| { + if (func_container.func.name) |name| { + break :default_name_getter LocRef{ .loc = name.loc, .ref = name.ref }; } - - const defaultName = if (stmt.data.s_function.func.name) |name| - js_ast.LocRef{ .loc = name.loc, .ref = name.ref } - else - try p.createDefaultName(defaultLoc); - - const value = js_ast.StmtOrExpr{ .stmt = stmt }; - return p.s(S.ExportDefault{ .default_name = defaultName, .value = value }, loc); - } - - const defaultName = try createDefaultName(p, loc); - - var expr = try p.parseAsyncPrefixExpr(async_range, Level.comma); - try p.parseSuffix(&expr, Level.comma, null, Expr.EFlags.none); - try p.lexer.expectOrInsertSemicolon(); - const value = js_ast.StmtOrExpr{ .expr = expr }; - p.has_export_default = true; - return p.s(S.ExportDefault{ .default_name = defaultName, .value = value }, loc); + }, + .s_class => |class| { + if (class.class.class_name) |name| { + break :default_name_getter LocRef{ .loc = name.loc, .ref = name.ref }; + } + }, + else => {}, } - if (p.lexer.token == .t_function or p.lexer.token == .t_class or p.lexer.isContextualKeyword("interface")) { - var _opts = ParseStatementOptions{ + break :default_name_getter createDefaultName(p, defaultLoc) catch unreachable; + }; + p.has_export_default = true; + p.has_es_module_syntax = true; + return p.s( + S.ExportDefault{ .default_name = default_name, .value = js_ast.StmtOrExpr{ .stmt = stmt } }, + loc, + ); + } + + const is_identifier = p.lexer.token == .t_identifier; + const name = p.lexer.identifier; + const expr = try p.parseExpr(.comma); + + // Handle the default export of an abstract class in TypeScript + if (is_typescript_enabled and is_identifier and (p.lexer.token == .t_class or opts.ts_decorators != null) and strings.eqlComptime(name, "abstract")) { + switch (expr.data) { + .e_identifier => { + var stmtOpts = ParseStatementOptions{ .ts_decorators = opts.ts_decorators, .is_name_optional = true, - .lexical_decl = .allow_all, }; - const stmt = try p.parseStmt(&_opts); + const stmt: Stmt = try p.parseClassStmt(loc, &stmtOpts); + // Use the statement name if present, since it's a better name const default_name: js_ast.LocRef = default_name_getter: { switch (stmt.data) { // This was just a type annotation @@ -209,13 +246,13 @@ pub fn ParseStmt( }, .s_function => |func_container| { - if (func_container.func.name) |name| { - break :default_name_getter LocRef{ .loc = name.loc, .ref = name.ref }; + if (func_container.func.name) |_name| { + break :default_name_getter LocRef{ .loc = defaultLoc, .ref = _name.ref }; } }, .s_class => |class| { - if (class.class.class_name) |name| { - break :default_name_getter LocRef{ .loc = name.loc, .ref = name.ref }; + if (class.class.class_name) |_name| { + break :default_name_getter LocRef{ .loc = defaultLoc, .ref = _name.ref }; } }, else => {}, @@ -224,1110 +261,1096 @@ pub fn ParseStmt( break :default_name_getter createDefaultName(p, defaultLoc) catch unreachable; }; p.has_export_default = true; - p.has_es_module_syntax = true; - return p.s( - S.ExportDefault{ .default_name = default_name, .value = js_ast.StmtOrExpr{ .stmt = stmt } }, - loc, - ); - } - - const is_identifier = p.lexer.token == .t_identifier; - const name = p.lexer.identifier; - const expr = try p.parseExpr(.comma); - - // Handle the default export of an abstract class in TypeScript - if (is_typescript_enabled and is_identifier and (p.lexer.token == .t_class or opts.ts_decorators != null) and strings.eqlComptime(name, "abstract")) { - switch (expr.data) { - .e_identifier => { - var stmtOpts = ParseStatementOptions{ - .ts_decorators = opts.ts_decorators, - .is_name_optional = true, - }; - const stmt: Stmt = try p.parseClassStmt(loc, &stmtOpts); - - // Use the statement name if present, since it's a better name - const default_name: js_ast.LocRef = default_name_getter: { - switch (stmt.data) { - // This was just a type annotation - .s_type_script => { - return stmt; - }, - - .s_function => |func_container| { - if (func_container.func.name) |_name| { - break :default_name_getter LocRef{ .loc = defaultLoc, .ref = _name.ref }; - } - }, - .s_class => |class| { - if (class.class.class_name) |_name| { - break :default_name_getter LocRef{ .loc = defaultLoc, .ref = _name.ref }; - } - }, - else => {}, - } - - break :default_name_getter createDefaultName(p, defaultLoc) catch unreachable; - }; - p.has_export_default = true; - return p.s(S.ExportDefault{ .default_name = default_name, .value = js_ast.StmtOrExpr{ .stmt = stmt } }, loc); - }, - else => { - p.panic("internal error: unexpected", .{}); - }, - } - } - - try p.lexer.expectOrInsertSemicolon(); - - // Use the expression name if present, since it's a better name - p.has_export_default = true; - return p.s( - S.ExportDefault{ - .default_name = p.defaultNameForExpr(expr, defaultLoc), - .value = js_ast.StmtOrExpr{ - .expr = expr, - }, - }, - loc, - ); - }, - T.t_asterisk => { - if (!opts.is_module_scope and !(opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - - try p.lexer.next(); - var namespace_ref: Ref = Ref.None; - var alias: ?js_ast.G.ExportStarAlias = null; - var path: ParsedPath = undefined; - - if (p.lexer.isContextualKeyword("as")) { - // "export * as ns from 'path'" - try p.lexer.next(); - const name = try p.parseClauseAlias("export"); - namespace_ref = try p.storeNameInRef(name); - alias = G.ExportStarAlias{ .loc = p.lexer.loc(), .original_name = name }; - try p.lexer.next(); - try p.lexer.expectContextualKeyword("from"); - path = try p.parsePath(); - } else { - // "export * from 'path'" - try p.lexer.expectContextualKeyword("from"); - path = try p.parsePath(); - const name = try fs.PathName.init(path.text).nonUniqueNameString(p.allocator); - namespace_ref = try p.storeNameInRef(name); - } - - const import_record_index = p.addImportRecord( - ImportKind.stmt, - path.loc, - path.text, - // TODO: import assertions - // path.assertions - ); - - if (path.is_macro) { - try p.log.addError(p.source, path.loc, "cannot use macro in export statement"); - } else if (path.import_tag != .none) { - try p.log.addError(p.source, loc, "cannot use export statement with \"type\" attribute"); - } - - if (comptime track_symbol_usage_during_parse_pass) { - // In the scan pass, we need _some_ way of knowing *not* to mark as unused - p.import_records.items[import_record_index].calls_runtime_re_export_fn = true; - } - - try p.lexer.expectOrInsertSemicolon(); - p.has_es_module_syntax = true; - return p.s(S.ExportStar{ - .namespace_ref = namespace_ref, - .alias = alias, - .import_record_index = import_record_index, - }, loc); - }, - T.t_open_brace => { - if (!opts.is_module_scope and !(opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - - const export_clause = try p.parseExportClause(); - if (p.lexer.isContextualKeyword("from")) { - try p.lexer.expectContextualKeyword("from"); - const parsedPath = try p.parsePath(); - - try p.lexer.expectOrInsertSemicolon(); - - if (comptime is_typescript_enabled) { - // export {type Foo} from 'bar'; - // -> - // nothing - // https://www.typescriptlang.org/play?useDefineForClassFields=true&esModuleInterop=false&declaration=false&target=99&isolatedModules=false&ts=4.5.4#code/KYDwDg9gTgLgBDAnmYcDeAxCEC+cBmUEAtnAOQBGAhlGQNwBQQA - if (export_clause.clauses.len == 0 and export_clause.had_type_only_exports) { - return p.s(S.TypeScript{}, loc); - } - } - - if (parsedPath.is_macro) { - try p.log.addError(p.source, loc, "export from cannot be used with \"type\": \"macro\""); - } else if (parsedPath.import_tag != .none) { - try p.log.addError(p.source, loc, "export from cannot be used with \"type\" attribute"); - } - - const import_record_index = p.addImportRecord(.stmt, parsedPath.loc, parsedPath.text); - const path_name = fs.PathName.init(parsedPath.text); - const namespace_ref = p.storeNameInRef( - std.fmt.allocPrint( - p.allocator, - "import_{}", - .{ - path_name.fmtIdentifier(), - }, - ) catch bun.outOfMemory(), - ) catch bun.outOfMemory(); - - if (comptime track_symbol_usage_during_parse_pass) { - // In the scan pass, we need _some_ way of knowing *not* to mark as unused - p.import_records.items[import_record_index].calls_runtime_re_export_fn = true; - } - p.current_scope.is_after_const_local_prefix = true; - p.has_es_module_syntax = true; - return p.s( - S.ExportFrom{ - .items = export_clause.clauses, - .is_single_line = export_clause.is_single_line, - .namespace_ref = namespace_ref, - .import_record_index = import_record_index, - }, - loc, - ); - } - try p.lexer.expectOrInsertSemicolon(); - - if (comptime is_typescript_enabled) { - // export {type Foo}; - // -> - // nothing - // https://www.typescriptlang.org/play?useDefineForClassFields=true&esModuleInterop=false&declaration=false&target=99&isolatedModules=false&ts=4.5.4#code/KYDwDg9gTgLgBDAnmYcDeAxCEC+cBmUEAtnAOQBGAhlGQNwBQQA - if (export_clause.clauses.len == 0 and export_clause.had_type_only_exports) { - return p.s(S.TypeScript{}, loc); - } - } - p.has_es_module_syntax = true; - return p.s(S.ExportClause{ - .items = export_clause.clauses, - .is_single_line = export_clause.is_single_line, - }, loc); - }, - T.t_equals => { - // "export = value;" - - p.esm_export_keyword = previous_export_keyword; // This wasn't an ESM export statement after all - if (is_typescript_enabled) { - try p.lexer.next(); - const value = try p.parseExpr(.lowest); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.ExportEquals{ .value = value }, loc); - } - try p.lexer.unexpected(); - return error.SyntaxError; - }, - else => { - try p.lexer.unexpected(); - return error.SyntaxError; - }, + return p.s(S.ExportDefault{ .default_name = default_name, .value = js_ast.StmtOrExpr{ .stmt = stmt } }, loc); + }, + else => { + p.panic("internal error: unexpected", .{}); + }, + } } - }, - .t_function => { - try p.lexer.next(); - return try p.parseFnStmt(loc, opts, null); + try p.lexer.expectOrInsertSemicolon(); + + // Use the expression name if present, since it's a better name + p.has_export_default = true; + return p.s( + S.ExportDefault{ + .default_name = p.defaultNameForExpr(expr, defaultLoc), + .value = js_ast.StmtOrExpr{ + .expr = expr, + }, + }, + loc, + ); }, - .t_enum => { - if (!is_typescript_enabled) { + T.t_asterisk => { + if (!opts.is_module_scope and !(opts.is_namespace_scope or !opts.is_typescript_declare)) { try p.lexer.unexpected(); return error.SyntaxError; } - return p.parseTypescriptEnumStmt(loc, opts); + + try p.lexer.next(); + var namespace_ref: Ref = Ref.None; + var alias: ?js_ast.G.ExportStarAlias = null; + var path: ParsedPath = undefined; + + if (p.lexer.isContextualKeyword("as")) { + // "export * as ns from 'path'" + try p.lexer.next(); + const name = try p.parseClauseAlias("export"); + namespace_ref = try p.storeNameInRef(name); + alias = G.ExportStarAlias{ .loc = p.lexer.loc(), .original_name = name }; + try p.lexer.next(); + try p.lexer.expectContextualKeyword("from"); + path = try p.parsePath(); + } else { + // "export * from 'path'" + try p.lexer.expectContextualKeyword("from"); + path = try p.parsePath(); + const name = try fs.PathName.init(path.text).nonUniqueNameString(p.allocator); + namespace_ref = try p.storeNameInRef(name); + } + + const import_record_index = p.addImportRecord( + ImportKind.stmt, + path.loc, + path.text, + // TODO: import assertions + // path.assertions + ); + + if (path.is_macro) { + try p.log.addError(p.source, path.loc, "cannot use macro in export statement"); + } else if (path.import_tag != .none) { + try p.log.addError(p.source, loc, "cannot use export statement with \"type\" attribute"); + } + + if (comptime track_symbol_usage_during_parse_pass) { + // In the scan pass, we need _some_ way of knowing *not* to mark as unused + p.import_records.items[import_record_index].calls_runtime_re_export_fn = true; + } + + try p.lexer.expectOrInsertSemicolon(); + p.has_es_module_syntax = true; + return p.s(S.ExportStar{ + .namespace_ref = namespace_ref, + .alias = alias, + .import_record_index = import_record_index, + }, loc); }, - .t_at => { - // Parse decorators before class statements, which are potentially exported - if (is_typescript_enabled) { - const scope_index = p.scopes_in_order.items.len; - const ts_decorators = try p.parseTypeScriptDecorators(); + T.t_open_brace => { + if (!opts.is_module_scope and !(opts.is_namespace_scope or !opts.is_typescript_declare)) { + try p.lexer.unexpected(); + return error.SyntaxError; + } - // If this turns out to be a "declare class" statement, we need to undo the - // scopes that were potentially pushed while parsing the decorator arguments. - // That can look like any one of the following: - // - // "@decorator declare class Foo {}" - // "@decorator declare abstract class Foo {}" - // "@decorator export declare class Foo {}" - // "@decorator export declare abstract class Foo {}" - // - opts.ts_decorators = DeferredTsDecorators{ - .values = ts_decorators, - .scope_index = scope_index, - }; + const export_clause = try p.parseExportClause(); + if (p.lexer.isContextualKeyword("from")) { + try p.lexer.expectContextualKeyword("from"); + const parsedPath = try p.parsePath(); - // "@decorator class Foo {}" - // "@decorator abstract class Foo {}" - // "@decorator declare class Foo {}" - // "@decorator declare abstract class Foo {}" - // "@decorator export class Foo {}" - // "@decorator export abstract class Foo {}" - // "@decorator export declare class Foo {}" - // "@decorator export declare abstract class Foo {}" - // "@decorator export default class Foo {}" - // "@decorator export default abstract class Foo {}" - if (p.lexer.token != .t_class and p.lexer.token != .t_export and !p.lexer.isContextualKeyword("abstract") and !p.lexer.isContextualKeyword("declare")) { - try p.lexer.expected(.t_class); + try p.lexer.expectOrInsertSemicolon(); + + if (comptime is_typescript_enabled) { + // export {type Foo} from 'bar'; + // -> + // nothing + // https://www.typescriptlang.org/play?useDefineForClassFields=true&esModuleInterop=false&declaration=false&target=99&isolatedModules=false&ts=4.5.4#code/KYDwDg9gTgLgBDAnmYcDeAxCEC+cBmUEAtnAOQBGAhlGQNwBQQA + if (export_clause.clauses.len == 0 and export_clause.had_type_only_exports) { + return p.s(S.TypeScript{}, loc); + } } - return p.parseStmt(opts); - } - // notimpl(); + if (parsedPath.is_macro) { + try p.log.addError(p.source, loc, "export from cannot be used with \"type\": \"macro\""); + } else if (parsedPath.import_tag != .none) { + try p.log.addError(p.source, loc, "export from cannot be used with \"type\" attribute"); + } + const import_record_index = p.addImportRecord(.stmt, parsedPath.loc, parsedPath.text); + const path_name = fs.PathName.init(parsedPath.text); + const namespace_ref = p.storeNameInRef( + std.fmt.allocPrint( + p.allocator, + "import_{}", + .{ + path_name.fmtIdentifier(), + }, + ) catch bun.outOfMemory(), + ) catch bun.outOfMemory(); + + if (comptime track_symbol_usage_during_parse_pass) { + // In the scan pass, we need _some_ way of knowing *not* to mark as unused + p.import_records.items[import_record_index].calls_runtime_re_export_fn = true; + } + p.current_scope.is_after_const_local_prefix = true; + p.has_es_module_syntax = true; + return p.s( + S.ExportFrom{ + .items = export_clause.clauses, + .is_single_line = export_clause.is_single_line, + .namespace_ref = namespace_ref, + .import_record_index = import_record_index, + }, + loc, + ); + } + try p.lexer.expectOrInsertSemicolon(); + + if (comptime is_typescript_enabled) { + // export {type Foo}; + // -> + // nothing + // https://www.typescriptlang.org/play?useDefineForClassFields=true&esModuleInterop=false&declaration=false&target=99&isolatedModules=false&ts=4.5.4#code/KYDwDg9gTgLgBDAnmYcDeAxCEC+cBmUEAtnAOQBGAhlGQNwBQQA + if (export_clause.clauses.len == 0 and export_clause.had_type_only_exports) { + return p.s(S.TypeScript{}, loc); + } + } + p.has_es_module_syntax = true; + return p.s(S.ExportClause{ + .items = export_clause.clauses, + .is_single_line = export_clause.is_single_line, + }, loc); + }, + T.t_equals => { + // "export = value;" + + p.esm_export_keyword = previous_export_keyword; // This wasn't an ESM export statement after all + if (is_typescript_enabled) { + try p.lexer.next(); + const value = try p.parseExpr(.lowest); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.ExportEquals{ .value = value }, loc); + } try p.lexer.unexpected(); return error.SyntaxError; }, - .t_class => { - if (opts.lexical_decl != .allow_all) { - try p.forbidLexicalDecl(loc); + else => { + try p.lexer.unexpected(); + return error.SyntaxError; + }, + } + } + + fn t_function(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + return try p.parseFnStmt(loc, opts, null); + } + fn t_enum(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + if (!is_typescript_enabled) { + try p.lexer.unexpected(); + return error.SyntaxError; + } + return p.parseTypescriptEnumStmt(loc, opts); + } + fn t_at(p: *P, opts: *ParseStatementOptions) anyerror!Stmt { + // Parse decorators before class statements, which are potentially exported + if (is_typescript_enabled) { + const scope_index = p.scopes_in_order.items.len; + const ts_decorators = try p.parseTypeScriptDecorators(); + + // If this turns out to be a "declare class" statement, we need to undo the + // scopes that were potentially pushed while parsing the decorator arguments. + // That can look like any one of the following: + // + // "@decorator declare class Foo {}" + // "@decorator declare abstract class Foo {}" + // "@decorator export declare class Foo {}" + // "@decorator export declare abstract class Foo {}" + // + opts.ts_decorators = DeferredTsDecorators{ + .values = ts_decorators, + .scope_index = scope_index, + }; + + // "@decorator class Foo {}" + // "@decorator abstract class Foo {}" + // "@decorator declare class Foo {}" + // "@decorator declare abstract class Foo {}" + // "@decorator export class Foo {}" + // "@decorator export abstract class Foo {}" + // "@decorator export declare class Foo {}" + // "@decorator export declare abstract class Foo {}" + // "@decorator export default class Foo {}" + // "@decorator export default abstract class Foo {}" + if (p.lexer.token != .t_class and p.lexer.token != .t_export and !p.lexer.isContextualKeyword("abstract") and !p.lexer.isContextualKeyword("declare")) { + try p.lexer.expected(.t_class); + } + + return p.parseStmt(opts); + } + // notimpl(); + + try p.lexer.unexpected(); + return error.SyntaxError; + } + fn t_class(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + if (opts.lexical_decl != .allow_all) { + try p.forbidLexicalDecl(loc); + } + + return try p.parseClassStmt(loc, opts); + } + fn t_var(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + const decls = try p.parseAndDeclareDecls(.hoisted, opts); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.Local{ .kind = .k_var, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); + } + fn t_const(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + if (opts.lexical_decl != .allow_all) { + try p.forbidLexicalDecl(loc); + } + // p.markSyntaxFeature(compat.Const, p.lexer.Range()) + + try p.lexer.next(); + + if (is_typescript_enabled and p.lexer.token == T.t_enum) { + return p.parseTypescriptEnumStmt(loc, opts); + } + + const decls = try p.parseAndDeclareDecls(.constant, opts); + try p.lexer.expectOrInsertSemicolon(); + + if (!opts.is_typescript_declare) { + try p.requireInitializers(.k_const, decls.items); + } + + return p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); + } + fn t_if(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + var current_loc = loc; + var root_if: ?Stmt = null; + var current_if: ?*S.If = null; + + while (true) { + try p.lexer.next(); + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + var stmtOpts = ParseStatementOptions{ + .lexical_decl = .allow_fn_inside_if, + }; + const yes = try p.parseStmt(&stmtOpts); + + // Create the if node + const if_stmt = p.s(S.If{ + .test_ = test_, + .yes = yes, + .no = null, + }, current_loc); + + // First if statement becomes root + if (root_if == null) { + root_if = if_stmt; + } + + // Link to previous if statement's else branch + if (current_if) |prev_if| { + prev_if.no = if_stmt; + } + + // Set current if for next iteration + current_if = if_stmt.data.s_if; + + if (p.lexer.token != .t_else) { + return root_if.?; + } + + try p.lexer.next(); + + // Handle final else + if (p.lexer.token != .t_if) { + stmtOpts = ParseStatementOptions{ + .lexical_decl = .allow_fn_inside_if, + }; + current_if.?.no = try p.parseStmt(&stmtOpts); + return root_if.?; + } + + // Continue with else if + current_loc = p.lexer.loc(); + } + + unreachable; + } + fn t_do(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + try p.lexer.expect(.t_while); + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + + // This is a weird corner case where automatic semicolon insertion applies + // even without a newline present + if (p.lexer.token == .t_semicolon) { + try p.lexer.next(); + } + return p.s(S.DoWhile{ .body = body, .test_ = test_ }, loc); + } + fn t_while(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + + return p.s(S.While{ + .body = body, + .test_ = test_, + }, loc); + } + fn t_with(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + const body_loc = p.lexer.loc(); + try p.lexer.expect(.t_close_paren); + + // Push a scope so we make sure to prevent any bare identifiers referenced + // within the body from being renamed. Renaming them might change the + // semantics of the code. + _ = try p.pushScopeForParsePass(.with, body_loc); + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + p.popScope(); + + return p.s(S.With{ .body = body, .body_loc = body_loc, .value = test_ }, loc); + } + fn t_switch(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + + const body_loc = p.lexer.loc(); + _ = try p.pushScopeForParsePass(.block, body_loc); + defer p.popScope(); + + try p.lexer.expect(.t_open_brace); + var cases = ListManaged(js_ast.Case).init(p.allocator); + var foundDefault = false; + var stmtOpts = ParseStatementOptions{ .lexical_decl = .allow_all }; + var value: ?js_ast.Expr = null; + while (p.lexer.token != .t_close_brace) { + var body = StmtList.init(p.allocator); + value = null; + if (p.lexer.token == .t_default) { + if (foundDefault) { + try p.log.addRangeError(p.source, p.lexer.range(), "Multiple default clauses are not allowed"); + return error.SyntaxError; } - return try p.parseClassStmt(loc, opts); - }, + foundDefault = true; + try p.lexer.next(); + try p.lexer.expect(.t_colon); + } else { + try p.lexer.expect(.t_case); + value = try p.parseExpr(.lowest); + try p.lexer.expect(.t_colon); + } + + caseBody: while (true) { + switch (p.lexer.token) { + .t_close_brace, .t_case, .t_default => { + break :caseBody; + }, + else => { + stmtOpts = ParseStatementOptions{ .lexical_decl = .allow_all }; + try body.append(try p.parseStmt(&stmtOpts)); + }, + } + } + try cases.append(js_ast.Case{ .value = value, .body = body.items, .loc = logger.Loc.Empty }); + } + try p.lexer.expect(.t_close_brace); + return p.s(S.Switch{ .test_ = test_, .body_loc = body_loc, .cases = cases.items }, loc); + } + fn t_try(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + const body_loc = p.lexer.loc(); + try p.lexer.expect(.t_open_brace); + _ = try p.pushScopeForParsePass(.block, loc); + var stmt_opts = ParseStatementOptions{}; + const body = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); + p.popScope(); + try p.lexer.next(); + + var catch_: ?js_ast.Catch = null; + var finally: ?js_ast.Finally = null; + + if (p.lexer.token == .t_catch) { + const catch_loc = p.lexer.loc(); + _ = try p.pushScopeForParsePass(.catch_binding, catch_loc); + try p.lexer.next(); + var binding: ?js_ast.Binding = null; + + // The catch binding is optional, and can be omitted + if (p.lexer.token != .t_open_brace) { + try p.lexer.expect(.t_open_paren); + var value = try p.parseBinding(.{}); + + // Skip over types + if (is_typescript_enabled and p.lexer.token == .t_colon) { + try p.lexer.expect(.t_colon); + try p.skipTypeScriptType(.lowest); + } + + try p.lexer.expect(.t_close_paren); + + // Bare identifiers are a special case + var kind = Symbol.Kind.other; + switch (value.data) { + .b_identifier => { + kind = .catch_identifier; + }, + else => {}, + } + try p.declareBinding(kind, &value, &stmt_opts); + binding = value; + } + + const catch_body_loc = p.lexer.loc(); + try p.lexer.expect(.t_open_brace); + + _ = try p.pushScopeForParsePass(.block, catch_body_loc); + const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); + p.popScope(); + try p.lexer.next(); + catch_ = js_ast.Catch{ + .loc = catch_loc, + .binding = binding, + .body = stmts, + .body_loc = catch_body_loc, + }; + p.popScope(); + } + + if (p.lexer.token == .t_finally or catch_ == null) { + const finally_loc = p.lexer.loc(); + _ = try p.pushScopeForParsePass(.block, finally_loc); + try p.lexer.expect(.t_finally); + try p.lexer.expect(.t_open_brace); + const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); + try p.lexer.next(); + finally = js_ast.Finally{ .loc = finally_loc, .stmts = stmts }; + p.popScope(); + } + + return p.s( + S.Try{ .body_loc = body_loc, .body = body, .catch_ = catch_, .finally = finally }, + loc, + ); + } + fn t_for(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + _ = try p.pushScopeForParsePass(.block, loc); + defer p.popScope(); + + try p.lexer.next(); + + // "for await (let x of y) {}" + var isForAwait = p.lexer.isContextualKeyword("await"); + if (isForAwait) { + const await_range = p.lexer.range(); + if (p.fn_or_arrow_data_parse.allow_await != .allow_expr) { + try p.log.addRangeError(p.source, await_range, "Cannot use \"await\" outside an async function"); + isForAwait = false; + } else { + // TODO: improve error handling here + // didGenerateError := p.markSyntaxFeature(compat.ForAwait, awaitRange) + if (p.fn_or_arrow_data_parse.is_top_level) { + p.top_level_await_keyword = await_range; + // p.markSyntaxFeature(compat.TopLevelAwait, awaitRange) + } + } + try p.lexer.next(); + } + + try p.lexer.expect(.t_open_paren); + + var init_: ?Stmt = null; + var test_: ?Expr = null; + var update: ?Expr = null; + + // "in" expressions aren't allowed here + p.allow_in = false; + + var bad_let_range: ?logger.Range = null; + if (p.lexer.isContextualKeyword("let")) { + bad_let_range = p.lexer.range(); + } + + var decls: G.Decl.List = .{}; + const init_loc = p.lexer.loc(); + var is_var = false; + switch (p.lexer.token) { + // for (var ) .t_var => { + is_var = true; try p.lexer.next(); - const decls = try p.parseAndDeclareDecls(.hoisted, opts); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.Local{ .kind = .k_var, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); + var stmtOpts = ParseStatementOptions{}; + decls.update(try p.parseAndDeclareDecls(.hoisted, &stmtOpts)); + init_ = p.s(S.Local{ .kind = .k_var, .decls = Decl.List.fromList(decls) }, init_loc); }, + // for (const ) .t_const => { - if (opts.lexical_decl != .allow_all) { - try p.forbidLexicalDecl(loc); - } - // p.markSyntaxFeature(compat.Const, p.lexer.Range()) - try p.lexer.next(); + var stmtOpts = ParseStatementOptions{}; + decls.update(try p.parseAndDeclareDecls(.constant, &stmtOpts)); + init_ = p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls) }, init_loc); + }, + // for (;) + .t_semicolon => {}, + else => { + var stmtOpts = ParseStatementOptions{ + .lexical_decl = .allow_all, + .is_for_loop_init = true, + }; - if (is_typescript_enabled and p.lexer.token == T.t_enum) { - return p.parseTypescriptEnumStmt(loc, opts); + const res = try p.parseExprOrLetStmt(&stmtOpts); + switch (res.stmt_or_expr) { + .stmt => |stmt| { + bad_let_range = null; + init_ = stmt; + }, + .expr => |expr| { + init_ = p.s(S.SExpr{ + .value = expr, + }, init_loc); + }, } + }, + } - const decls = try p.parseAndDeclareDecls(.constant, opts); + // "in" expressions are allowed again + p.allow_in = true; + + // Detect for-of loops + if (p.lexer.isContextualKeyword("of") or isForAwait) { + if (bad_let_range) |r| { + try p.log.addRangeError(p.source, r, "\"let\" must be wrapped in parentheses to be used as an expression here"); + return error.SyntaxError; + } + + if (isForAwait and !p.lexer.isContextualKeyword("of")) { + if (init_ != null) { + try p.lexer.expectedString("\"of\""); + } else { + try p.lexer.unexpected(); + return error.SyntaxError; + } + } + + try p.forbidInitializers(decls.slice(), "of", false); + try p.lexer.next(); + const value = try p.parseExpr(.comma); + try p.lexer.expect(.t_close_paren); + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + return p.s(S.ForOf{ .is_await = isForAwait, .init = init_ orelse unreachable, .value = value, .body = body }, loc); + } + + // Detect for-in loops + if (p.lexer.token == .t_in) { + try p.forbidInitializers(decls.slice(), "in", is_var); + try p.lexer.next(); + const value = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + return p.s(S.ForIn{ .init = init_ orelse unreachable, .value = value, .body = body }, loc); + } + + // Only require "const" statement initializers when we know we're a normal for loop + if (init_) |init_stmt| { + switch (init_stmt.data) { + .s_local => { + if (init_stmt.data.s_local.kind == .k_const) { + try p.requireInitializers(.k_const, decls.slice()); + } + }, + else => {}, + } + } + + try p.lexer.expect(.t_semicolon); + if (p.lexer.token != .t_semicolon) { + test_ = try p.parseExpr(.lowest); + } + + try p.lexer.expect(.t_semicolon); + + if (p.lexer.token != .t_close_paren) { + update = try p.parseExpr(.lowest); + } + + try p.lexer.expect(.t_close_paren); + var stmtOpts = ParseStatementOptions{}; + const body = try p.parseStmt(&stmtOpts); + return p.s( + S.For{ .init = init_, .test_ = test_, .update = update, .body = body }, + loc, + ); + } + fn t_import(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + const previous_import_keyword = p.esm_import_keyword; + p.esm_import_keyword = p.lexer.range(); + try p.lexer.next(); + var stmt: S.Import = S.Import{ + .namespace_ref = Ref.None, + .import_record_index = std.math.maxInt(u32), + }; + var was_originally_bare_import = false; + + // "export import foo = bar" + if ((opts.is_export or (opts.is_namespace_scope and !opts.is_typescript_declare)) and p.lexer.token != .t_identifier) { + try p.lexer.expected(.t_identifier); + } + + switch (p.lexer.token) { + // "import('path')" + // "import.meta" + .t_open_paren, .t_dot => { + p.esm_import_keyword = previous_import_keyword; // this wasn't an esm import statement after all + var expr = try p.parseImportExpr(loc, .lowest); + try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none); try p.lexer.expectOrInsertSemicolon(); - - if (!opts.is_typescript_declare) { - try p.requireInitializers(.k_const, decls.items); - } - - return p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); - }, - .t_if => { - var current_loc = loc; - var root_if: ?Stmt = null; - var current_if: ?*S.If = null; - - while (true) { - try p.lexer.next(); - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - var stmtOpts = ParseStatementOptions{ - .lexical_decl = .allow_fn_inside_if, - }; - const yes = try p.parseStmt(&stmtOpts); - - // Create the if node - const if_stmt = p.s(S.If{ - .test_ = test_, - .yes = yes, - .no = null, - }, current_loc); - - // First if statement becomes root - if (root_if == null) { - root_if = if_stmt; - } - - // Link to previous if statement's else branch - if (current_if) |prev_if| { - prev_if.no = if_stmt; - } - - // Set current if for next iteration - current_if = if_stmt.data.s_if; - - if (p.lexer.token != .t_else) { - return root_if.?; - } - - try p.lexer.next(); - - // Handle final else - if (p.lexer.token != .t_if) { - stmtOpts = ParseStatementOptions{ - .lexical_decl = .allow_fn_inside_if, - }; - current_if.?.no = try p.parseStmt(&stmtOpts); - return root_if.?; - } - - // Continue with else if - current_loc = p.lexer.loc(); - } - - unreachable; - }, - .t_do => { - try p.lexer.next(); - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - try p.lexer.expect(.t_while); - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - - // This is a weird corner case where automatic semicolon insertion applies - // even without a newline present - if (p.lexer.token == .t_semicolon) { - try p.lexer.next(); - } - return p.s(S.DoWhile{ .body = body, .test_ = test_ }, loc); - }, - .t_while => { - try p.lexer.next(); - - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - - return p.s(S.While{ - .body = body, - .test_ = test_, + return p.s(S.SExpr{ + .value = expr, }, loc); }, - .t_with => { - try p.lexer.next(); - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - const body_loc = p.lexer.loc(); - try p.lexer.expect(.t_close_paren); - - // Push a scope so we make sure to prevent any bare identifiers referenced - // within the body from being renamed. Renaming them might change the - // semantics of the code. - _ = try p.pushScopeForParsePass(.with, body_loc); - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - p.popScope(); - - return p.s(S.With{ .body = body, .body_loc = body_loc, .value = test_ }, loc); + .t_string_literal, .t_no_substitution_template_literal => { + // "import 'path'" + if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { + try p.lexer.unexpected(); + return error.SyntaxError; + } + was_originally_bare_import = true; }, - .t_switch => { - try p.lexer.next(); - - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - - const body_loc = p.lexer.loc(); - _ = try p.pushScopeForParsePass(.block, body_loc); - defer p.popScope(); - - try p.lexer.expect(.t_open_brace); - var cases = ListManaged(js_ast.Case).init(p.allocator); - var foundDefault = false; - var stmtOpts = ParseStatementOptions{ .lexical_decl = .allow_all }; - var value: ?js_ast.Expr = null; - while (p.lexer.token != .t_close_brace) { - var body = StmtList.init(p.allocator); - value = null; - if (p.lexer.token == .t_default) { - if (foundDefault) { - try p.log.addRangeError(p.source, p.lexer.range(), "Multiple default clauses are not allowed"); - return error.SyntaxError; - } - - foundDefault = true; - try p.lexer.next(); - try p.lexer.expect(.t_colon); - } else { - try p.lexer.expect(.t_case); - value = try p.parseExpr(.lowest); - try p.lexer.expect(.t_colon); - } - - caseBody: while (true) { - switch (p.lexer.token) { - .t_close_brace, .t_case, .t_default => { - break :caseBody; - }, - else => { - stmtOpts = ParseStatementOptions{ .lexical_decl = .allow_all }; - try body.append(try p.parseStmt(&stmtOpts)); - }, - } - } - try cases.append(js_ast.Case{ .value = value, .body = body.items, .loc = logger.Loc.Empty }); + .t_asterisk => { + // "import * as ns from 'path'" + if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { + try p.lexer.unexpected(); + return error.SyntaxError; } - try p.lexer.expect(.t_close_brace); - return p.s(S.Switch{ .test_ = test_, .body_loc = body_loc, .cases = cases.items }, loc); - }, - .t_try => { - try p.lexer.next(); - const body_loc = p.lexer.loc(); - try p.lexer.expect(.t_open_brace); - _ = try p.pushScopeForParsePass(.block, loc); - var stmt_opts = ParseStatementOptions{}; - const body = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); - p.popScope(); - try p.lexer.next(); - - var catch_: ?js_ast.Catch = null; - var finally: ?js_ast.Finally = null; - - if (p.lexer.token == .t_catch) { - const catch_loc = p.lexer.loc(); - _ = try p.pushScopeForParsePass(.catch_binding, catch_loc); - try p.lexer.next(); - var binding: ?js_ast.Binding = null; - - // The catch binding is optional, and can be omitted - if (p.lexer.token != .t_open_brace) { - try p.lexer.expect(.t_open_paren); - var value = try p.parseBinding(.{}); - - // Skip over types - if (is_typescript_enabled and p.lexer.token == .t_colon) { - try p.lexer.expect(.t_colon); - try p.skipTypeScriptType(.lowest); - } - - try p.lexer.expect(.t_close_paren); - - // Bare identifiers are a special case - var kind = Symbol.Kind.other; - switch (value.data) { - .b_identifier => { - kind = .catch_identifier; - }, - else => {}, - } - try p.declareBinding(kind, &value, &stmt_opts); - binding = value; - } - - const catch_body_loc = p.lexer.loc(); - try p.lexer.expect(.t_open_brace); - - _ = try p.pushScopeForParsePass(.block, catch_body_loc); - const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); - p.popScope(); - try p.lexer.next(); - catch_ = js_ast.Catch{ - .loc = catch_loc, - .binding = binding, - .body = stmts, - .body_loc = catch_body_loc, - }; - p.popScope(); - } - - if (p.lexer.token == .t_finally or catch_ == null) { - const finally_loc = p.lexer.loc(); - _ = try p.pushScopeForParsePass(.block, finally_loc); - try p.lexer.expect(.t_finally); - try p.lexer.expect(.t_open_brace); - const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmt_opts); - try p.lexer.next(); - finally = js_ast.Finally{ .loc = finally_loc, .stmts = stmts }; - p.popScope(); - } - - return p.s( - S.Try{ .body_loc = body_loc, .body = body, .catch_ = catch_, .finally = finally }, - loc, - ); - }, - .t_for => { - _ = try p.pushScopeForParsePass(.block, loc); - defer p.popScope(); try p.lexer.next(); - - // "for await (let x of y) {}" - var isForAwait = p.lexer.isContextualKeyword("await"); - if (isForAwait) { - const await_range = p.lexer.range(); - if (p.fn_or_arrow_data_parse.allow_await != .allow_expr) { - try p.log.addRangeError(p.source, await_range, "Cannot use \"await\" outside an async function"); - isForAwait = false; - } else { - // TODO: improve error handling here - // didGenerateError := p.markSyntaxFeature(compat.ForAwait, awaitRange) - if (p.fn_or_arrow_data_parse.is_top_level) { - p.top_level_await_keyword = await_range; - // p.markSyntaxFeature(compat.TopLevelAwait, awaitRange) - } - } - try p.lexer.next(); - } - - try p.lexer.expect(.t_open_paren); - - var init_: ?Stmt = null; - var test_: ?Expr = null; - var update: ?Expr = null; - - // "in" expressions aren't allowed here - p.allow_in = false; - - var bad_let_range: ?logger.Range = null; - if (p.lexer.isContextualKeyword("let")) { - bad_let_range = p.lexer.range(); - } - - var decls: G.Decl.List = .{}; - const init_loc = p.lexer.loc(); - var is_var = false; - switch (p.lexer.token) { - // for (var ) - .t_var => { - is_var = true; - try p.lexer.next(); - var stmtOpts = ParseStatementOptions{}; - decls.update(try p.parseAndDeclareDecls(.hoisted, &stmtOpts)); - init_ = p.s(S.Local{ .kind = .k_var, .decls = Decl.List.fromList(decls) }, init_loc); - }, - // for (const ) - .t_const => { - try p.lexer.next(); - var stmtOpts = ParseStatementOptions{}; - decls.update(try p.parseAndDeclareDecls(.constant, &stmtOpts)); - init_ = p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls) }, init_loc); - }, - // for (;) - .t_semicolon => {}, - else => { - var stmtOpts = ParseStatementOptions{ - .lexical_decl = .allow_all, - .is_for_loop_init = true, - }; - - const res = try p.parseExprOrLetStmt(&stmtOpts); - switch (res.stmt_or_expr) { - .stmt => |stmt| { - bad_let_range = null; - init_ = stmt; - }, - .expr => |expr| { - init_ = p.s(S.SExpr{ - .value = expr, - }, init_loc); - }, - } - }, - } - - // "in" expressions are allowed again - p.allow_in = true; - - // Detect for-of loops - if (p.lexer.isContextualKeyword("of") or isForAwait) { - if (bad_let_range) |r| { - try p.log.addRangeError(p.source, r, "\"let\" must be wrapped in parentheses to be used as an expression here"); - return error.SyntaxError; - } - - if (isForAwait and !p.lexer.isContextualKeyword("of")) { - if (init_ != null) { - try p.lexer.expectedString("\"of\""); - } else { - try p.lexer.unexpected(); - return error.SyntaxError; - } - } - - try p.forbidInitializers(decls.slice(), "of", false); - try p.lexer.next(); - const value = try p.parseExpr(.comma); - try p.lexer.expect(.t_close_paren); - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - return p.s(S.ForOf{ .is_await = isForAwait, .init = init_ orelse unreachable, .value = value, .body = body }, loc); - } - - // Detect for-in loops - if (p.lexer.token == .t_in) { - try p.forbidInitializers(decls.slice(), "in", is_var); - try p.lexer.next(); - const value = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - return p.s(S.ForIn{ .init = init_ orelse unreachable, .value = value, .body = body }, loc); - } - - // Only require "const" statement initializers when we know we're a normal for loop - if (init_) |init_stmt| { - switch (init_stmt.data) { - .s_local => { - if (init_stmt.data.s_local.kind == .k_const) { - try p.requireInitializers(.k_const, decls.slice()); - } - }, - else => {}, - } - } - - try p.lexer.expect(.t_semicolon); - if (p.lexer.token != .t_semicolon) { - test_ = try p.parseExpr(.lowest); - } - - try p.lexer.expect(.t_semicolon); - - if (p.lexer.token != .t_close_paren) { - update = try p.parseExpr(.lowest); - } - - try p.lexer.expect(.t_close_paren); - var stmtOpts = ParseStatementOptions{}; - const body = try p.parseStmt(&stmtOpts); - return p.s( - S.For{ .init = init_, .test_ = test_, .update = update, .body = body }, - loc, - ); - }, - .t_import => { - const previous_import_keyword = p.esm_import_keyword; - p.esm_import_keyword = p.lexer.range(); - try p.lexer.next(); - var stmt: S.Import = S.Import{ - .namespace_ref = Ref.None, + try p.lexer.expectContextualKeyword("as"); + stmt = S.Import{ + .namespace_ref = try p.storeNameInRef(p.lexer.identifier), + .star_name_loc = p.lexer.loc(), .import_record_index = std.math.maxInt(u32), }; - var was_originally_bare_import = false; - - // "export import foo = bar" - if ((opts.is_export or (opts.is_namespace_scope and !opts.is_typescript_declare)) and p.lexer.token != .t_identifier) { - try p.lexer.expected(.t_identifier); + try p.lexer.expect(.t_identifier); + try p.lexer.expectContextualKeyword("from"); + }, + .t_open_brace => { + // "import {item1, item2} from 'path'" + if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { + try p.lexer.unexpected(); + return error.SyntaxError; + } + const importClause = try p.parseImportClause(); + if (comptime is_typescript_enabled) { + if (importClause.had_type_only_imports and importClause.items.len == 0) { + try p.lexer.expectContextualKeyword("from"); + _ = try p.parsePath(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.TypeScript{}, loc); + } } - switch (p.lexer.token) { - // "import('path')" - // "import.meta" - .t_open_paren, .t_dot => { - p.esm_import_keyword = previous_import_keyword; // this wasn't an esm import statement after all - var expr = try p.parseImportExpr(loc, .lowest); - try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.SExpr{ - .value = expr, - }, loc); - }, - .t_string_literal, .t_no_substitution_template_literal => { - // "import 'path'" - if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - was_originally_bare_import = true; - }, - .t_asterisk => { - // "import * as ns from 'path'" - if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } + stmt = S.Import{ + .namespace_ref = Ref.None, + .import_record_index = std.math.maxInt(u32), + .items = importClause.items, + .is_single_line = importClause.is_single_line, + }; + try p.lexer.expectContextualKeyword("from"); + }, + .t_identifier => { + // "import defaultItem from 'path'" + // "import foo = bar" + if (!opts.is_module_scope and (!opts.is_namespace_scope)) { + try p.lexer.unexpected(); + return error.SyntaxError; + } - try p.lexer.next(); - try p.lexer.expectContextualKeyword("as"); - stmt = S.Import{ - .namespace_ref = try p.storeNameInRef(p.lexer.identifier), - .star_name_loc = p.lexer.loc(), - .import_record_index = std.math.maxInt(u32), - }; - try p.lexer.expect(.t_identifier); - try p.lexer.expectContextualKeyword("from"); - }, - .t_open_brace => { - // "import {item1, item2} from 'path'" - if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) { - try p.lexer.unexpected(); - return error.SyntaxError; - } - const importClause = try p.parseImportClause(); - if (comptime is_typescript_enabled) { - if (importClause.had_type_only_imports and importClause.items.len == 0) { + var default_name = p.lexer.identifier; + stmt = S.Import{ .namespace_ref = Ref.None, .import_record_index = std.math.maxInt(u32), .default_name = LocRef{ + .loc = p.lexer.loc(), + .ref = try p.storeNameInRef(default_name), + } }; + try p.lexer.next(); + + if (comptime is_typescript_enabled) { + // Skip over type-only imports + if (strings.eqlComptime(default_name, "type")) { + switch (p.lexer.token) { + .t_identifier => { + if (!strings.eqlComptime(p.lexer.identifier, "from")) { + default_name = p.lexer.identifier; + stmt.default_name.?.loc = p.lexer.loc(); + try p.lexer.next(); + + if (p.lexer.token == .t_equals) { + // "import type foo = require('bar');" + // "import type foo = bar.baz;" + opts.is_typescript_declare = true; + return try p.parseTypeScriptImportEqualsStmt(loc, opts, stmt.default_name.?.loc, default_name); + } else { + // "import type foo from 'bar';" + try p.lexer.expectContextualKeyword("from"); + _ = try p.parsePath(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.TypeScript{}, loc); + } + } + }, + .t_asterisk => { + // "import type * as foo from 'bar';" + try p.lexer.next(); + try p.lexer.expectContextualKeyword("as"); + try p.lexer.expect(.t_identifier); try p.lexer.expectContextualKeyword("from"); _ = try p.parsePath(); try p.lexer.expectOrInsertSemicolon(); return p.s(S.TypeScript{}, loc); - } - } + }, - stmt = S.Import{ - .namespace_ref = Ref.None, - .import_record_index = std.math.maxInt(u32), - .items = importClause.items, - .is_single_line = importClause.is_single_line, - }; - try p.lexer.expectContextualKeyword("from"); - }, - .t_identifier => { - // "import defaultItem from 'path'" - // "import foo = bar" - if (!opts.is_module_scope and (!opts.is_namespace_scope)) { + .t_open_brace => { + // "import type {foo} from 'bar';" + _ = try p.parseImportClause(); + try p.lexer.expectContextualKeyword("from"); + _ = try p.parsePath(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.TypeScript{}, loc); + }, + else => {}, + } + } + + // Parse TypeScript import assignment statements + if (p.lexer.token == .t_equals or opts.is_export or (opts.is_namespace_scope and !opts.is_typescript_declare)) { + p.esm_import_keyword = previous_import_keyword; // This wasn't an ESM import statement after all; + return p.parseTypeScriptImportEqualsStmt(loc, opts, logger.Loc.Empty, default_name); + } + } + + if (p.lexer.token == .t_comma) { + try p.lexer.next(); + + switch (p.lexer.token) { + // "import defaultItem, * as ns from 'path'" + .t_asterisk => { + try p.lexer.next(); + try p.lexer.expectContextualKeyword("as"); + stmt.namespace_ref = try p.storeNameInRef(p.lexer.identifier); + stmt.star_name_loc = p.lexer.loc(); + try p.lexer.expect(.t_identifier); + }, + // "import defaultItem, {item1, item2} from 'path'" + .t_open_brace => { + const importClause = try p.parseImportClause(); + + stmt.items = importClause.items; + stmt.is_single_line = importClause.is_single_line; + }, + else => { try p.lexer.unexpected(); return error.SyntaxError; - } - - var default_name = p.lexer.identifier; - stmt = S.Import{ .namespace_ref = Ref.None, .import_record_index = std.math.maxInt(u32), .default_name = LocRef{ - .loc = p.lexer.loc(), - .ref = try p.storeNameInRef(default_name), - } }; - try p.lexer.next(); - - if (comptime is_typescript_enabled) { - // Skip over type-only imports - if (strings.eqlComptime(default_name, "type")) { - switch (p.lexer.token) { - .t_identifier => { - if (!strings.eqlComptime(p.lexer.identifier, "from")) { - default_name = p.lexer.identifier; - stmt.default_name.?.loc = p.lexer.loc(); - try p.lexer.next(); - - if (p.lexer.token == .t_equals) { - // "import type foo = require('bar');" - // "import type foo = bar.baz;" - opts.is_typescript_declare = true; - return try p.parseTypeScriptImportEqualsStmt(loc, opts, stmt.default_name.?.loc, default_name); - } else { - // "import type foo from 'bar';" - try p.lexer.expectContextualKeyword("from"); - _ = try p.parsePath(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.TypeScript{}, loc); - } - } - }, - .t_asterisk => { - // "import type * as foo from 'bar';" - try p.lexer.next(); - try p.lexer.expectContextualKeyword("as"); - try p.lexer.expect(.t_identifier); - try p.lexer.expectContextualKeyword("from"); - _ = try p.parsePath(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.TypeScript{}, loc); - }, - - .t_open_brace => { - // "import type {foo} from 'bar';" - _ = try p.parseImportClause(); - try p.lexer.expectContextualKeyword("from"); - _ = try p.parsePath(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.TypeScript{}, loc); - }, - else => {}, - } - } - - // Parse TypeScript import assignment statements - if (p.lexer.token == .t_equals or opts.is_export or (opts.is_namespace_scope and !opts.is_typescript_declare)) { - p.esm_import_keyword = previous_import_keyword; // This wasn't an ESM import statement after all; - return p.parseTypeScriptImportEqualsStmt(loc, opts, logger.Loc.Empty, default_name); - } - } - - if (p.lexer.token == .t_comma) { - try p.lexer.next(); - - switch (p.lexer.token) { - // "import defaultItem, * as ns from 'path'" - .t_asterisk => { - try p.lexer.next(); - try p.lexer.expectContextualKeyword("as"); - stmt.namespace_ref = try p.storeNameInRef(p.lexer.identifier); - stmt.star_name_loc = p.lexer.loc(); - try p.lexer.expect(.t_identifier); - }, - // "import defaultItem, {item1, item2} from 'path'" - .t_open_brace => { - const importClause = try p.parseImportClause(); - - stmt.items = importClause.items; - stmt.is_single_line = importClause.is_single_line; - }, - else => { - try p.lexer.unexpected(); - return error.SyntaxError; - }, - } - } - - try p.lexer.expectContextualKeyword("from"); - }, - else => { - try p.lexer.unexpected(); - return error.SyntaxError; - }, + }, + } } - const path = try p.parsePath(); - try p.lexer.expectOrInsertSemicolon(); - - return try p.processImportStatement(stmt, path, loc, was_originally_bare_import); + try p.lexer.expectContextualKeyword("from"); }, - .t_break => { - try p.lexer.next(); - const name = try p.parseLabelName(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.Break{ .label = name }, loc); - }, - .t_continue => { - try p.lexer.next(); - const name = try p.parseLabelName(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.Continue{ .label = name }, loc); - }, - .t_return => { - if (p.fn_or_arrow_data_parse.is_return_disallowed) { - try p.log.addRangeError(p.source, p.lexer.range(), "A return statement cannot be used here"); - } - try p.lexer.next(); - var value: ?Expr = null; - if ((p.lexer.token != .t_semicolon and - !p.lexer.has_newline_before and - p.lexer.token != .t_close_brace and - p.lexer.token != .t_end_of_file)) - { - value = try p.parseExpr(.lowest); - } - p.latest_return_had_semicolon = p.lexer.token == .t_semicolon; - try p.lexer.expectOrInsertSemicolon(); - - return p.s(S.Return{ .value = value }, loc); - }, - .t_throw => { - try p.lexer.next(); - if (p.lexer.has_newline_before) { - try p.log.addError(p.source, logger.Loc{ - .start = loc.start + 5, - }, "Unexpected newline after \"throw\""); - return error.SyntaxError; - } - const expr = try p.parseExpr(.lowest); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.Throw{ .value = expr }, loc); - }, - .t_debugger => { - try p.lexer.next(); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.Debugger{}, loc); - }, - .t_open_brace => { - _ = try p.pushScopeForParsePass(.block, loc); - defer p.popScope(); - try p.lexer.next(); - var stmtOpts = ParseStatementOptions{}; - const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmtOpts); - const close_brace_loc = p.lexer.loc(); - try p.lexer.next(); - return p.s(S.Block{ - .stmts = stmts, - .close_brace_loc = close_brace_loc, - }, loc); - }, - else => { - const is_identifier = p.lexer.token == .t_identifier; - const name = p.lexer.identifier; - // Parse either an async function, an async expression, or a normal expression - var expr: Expr = Expr{ .loc = loc, .data = Expr.Data{ .e_missing = .{} } }; - if (is_identifier and strings.eqlComptime(p.lexer.raw(), "async")) { - const async_range = p.lexer.range(); - try p.lexer.next(); - if (p.lexer.token == .t_function and !p.lexer.has_newline_before) { - try p.lexer.next(); - - return try p.parseFnStmt(async_range.loc, opts, async_range); - } - - expr = try p.parseAsyncPrefixExpr(async_range, .lowest); - try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none); - } else { - const exprOrLet = try p.parseExprOrLetStmt(opts); - switch (exprOrLet.stmt_or_expr) { - .stmt => |stmt| { - try p.lexer.expectOrInsertSemicolon(); - return stmt; - }, - .expr => |_expr| { - expr = _expr; - }, - } - } - if (is_identifier) { - switch (expr.data) { - .e_identifier => |ident| { - if (p.lexer.token == .t_colon and !opts.hasDecorators()) { - _ = try p.pushScopeForParsePass(.label, loc); - defer p.popScope(); - - // Parse a labeled statement - try p.lexer.next(); - - const _name = LocRef{ .loc = expr.loc, .ref = ident.ref }; - var nestedOpts = ParseStatementOptions{}; - - switch (opts.lexical_decl) { - .allow_all, .allow_fn_inside_label => { - nestedOpts.lexical_decl = .allow_fn_inside_label; - }, - else => {}, - } - const stmt = try p.parseStmt(&nestedOpts); - return p.s(S.Label{ .name = _name, .stmt = stmt }, loc); - } - }, - else => {}, - } - - if (is_typescript_enabled) { - if (js_lexer.TypescriptStmtKeyword.List.get(name)) |ts_stmt| { - switch (ts_stmt) { - .ts_stmt_type => { - if (p.lexer.token == .t_identifier and !p.lexer.has_newline_before) { - // "type Foo = any" - var stmtOpts = ParseStatementOptions{ .is_module_scope = opts.is_module_scope }; - try p.skipTypeScriptTypeStmt(&stmtOpts); - return p.s(S.TypeScript{}, loc); - } - }, - .ts_stmt_namespace, .ts_stmt_module => { - // "namespace Foo {}" - // "module Foo {}" - // "declare module 'fs' {}" - // "declare module 'fs';" - if (((opts.is_module_scope or opts.is_namespace_scope) and (p.lexer.token == .t_identifier or - (p.lexer.token == .t_string_literal and opts.is_typescript_declare)))) - { - return p.parseTypeScriptNamespaceStmt(loc, opts); - } - }, - .ts_stmt_interface => { - // "interface Foo {}" - var stmtOpts = ParseStatementOptions{ .is_module_scope = opts.is_module_scope }; - - try p.skipTypeScriptInterfaceStmt(&stmtOpts); - return p.s(S.TypeScript{}, loc); - }, - .ts_stmt_abstract => { - if (p.lexer.token == .t_class or opts.ts_decorators != null) { - return try p.parseClassStmt(loc, opts); - } - }, - .ts_stmt_global => { - // "declare module 'fs' { global { namespace NodeJS {} } }" - if (opts.is_namespace_scope and opts.is_typescript_declare and p.lexer.token == .t_open_brace) { - try p.lexer.next(); - _ = try p.parseStmtsUpTo(.t_close_brace, opts); - try p.lexer.next(); - return p.s(S.TypeScript{}, loc); - } - }, - .ts_stmt_declare => { - opts.lexical_decl = .allow_all; - opts.is_typescript_declare = true; - - // "@decorator declare class Foo {}" - // "@decorator declare abstract class Foo {}" - if (opts.ts_decorators != null and p.lexer.token != .t_class and !p.lexer.isContextualKeyword("abstract")) { - try p.lexer.expected(.t_class); - } - - // "declare global { ... }" - if (p.lexer.isContextualKeyword("global")) { - try p.lexer.next(); - try p.lexer.expect(.t_open_brace); - _ = try p.parseStmtsUpTo(.t_close_brace, opts); - try p.lexer.next(); - return p.s(S.TypeScript{}, loc); - } - - // "declare const x: any" - const stmt = try p.parseStmt(opts); - if (opts.ts_decorators) |decs| { - p.discardScopesUpTo(decs.scope_index); - } - - // Unlike almost all uses of "declare", statements that use - // "export declare" with "var/let/const" inside a namespace affect - // code generation. They cause any declared bindings to be - // considered exports of the namespace. Identifier references to - // those names must be converted into property accesses off the - // namespace object: - // - // namespace ns { - // export declare const x - // export function y() { return x } - // } - // - // (ns as any).x = 1 - // console.log(ns.y()) - // - // In this example, "return x" must be replaced with "return ns.x". - // This is handled by replacing each "export declare" statement - // inside a namespace with an "export var" statement containing all - // of the declared bindings. That "export var" statement will later - // cause identifiers to be transformed into property accesses. - if (opts.is_namespace_scope and opts.is_export) { - var decls: G.Decl.List = .{}; - switch (stmt.data) { - .s_local => |local| { - var _decls = try ListManaged(G.Decl).initCapacity(p.allocator, local.decls.len); - for (local.decls.slice()) |decl| { - try extractDeclsForBinding(decl.binding, &_decls); - } - decls.update(_decls); - }, - else => {}, - } - - if (decls.len > 0) { - return p.s(S.Local{ - .kind = .k_var, - .is_export = true, - .decls = decls, - }, loc); - } - } - - return p.s(S.TypeScript{}, loc); - }, - } - } - } - } - // Output.print("\n\nmVALUE {s}:{s}\n", .{ expr, name }); - try p.lexer.expectOrInsertSemicolon(); - return p.s(S.SExpr{ .value = expr }, loc); + try p.lexer.unexpected(); + return error.SyntaxError; }, } - return js_ast.Stmt.empty(); + const path = try p.parsePath(); + try p.lexer.expectOrInsertSemicolon(); + + return try p.processImportStatement(stmt, path, loc, was_originally_bare_import); + } + fn t_break(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + const name = try p.parseLabelName(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.Break{ .label = name }, loc); + } + fn t_continue(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + const name = try p.parseLabelName(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.Continue{ .label = name }, loc); + } + fn t_return(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + if (p.fn_or_arrow_data_parse.is_return_disallowed) { + try p.log.addRangeError(p.source, p.lexer.range(), "A return statement cannot be used here"); + } + try p.lexer.next(); + var value: ?Expr = null; + if ((p.lexer.token != .t_semicolon and + !p.lexer.has_newline_before and + p.lexer.token != .t_close_brace and + p.lexer.token != .t_end_of_file)) + { + value = try p.parseExpr(.lowest); + } + p.latest_return_had_semicolon = p.lexer.token == .t_semicolon; + try p.lexer.expectOrInsertSemicolon(); + + return p.s(S.Return{ .value = value }, loc); + } + fn t_throw(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + if (p.lexer.has_newline_before) { + try p.log.addError(p.source, logger.Loc{ + .start = loc.start + 5, + }, "Unexpected newline after \"throw\""); + return error.SyntaxError; + } + const expr = try p.parseExpr(.lowest); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.Throw{ .value = expr }, loc); + } + fn t_debugger(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + try p.lexer.next(); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.Debugger{}, loc); + } + fn t_open_brace(p: *P, _: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + _ = try p.pushScopeForParsePass(.block, loc); + defer p.popScope(); + try p.lexer.next(); + var stmtOpts = ParseStatementOptions{}; + const stmts = try p.parseStmtsUpTo(.t_close_brace, &stmtOpts); + const close_brace_loc = p.lexer.loc(); + try p.lexer.next(); + return p.s(S.Block{ + .stmts = stmts, + .close_brace_loc = close_brace_loc, + }, loc); + } + + fn parseStmtFallthrough(p: *P, opts: *ParseStatementOptions, loc: logger.Loc) anyerror!Stmt { + const is_identifier = p.lexer.token == .t_identifier; + const name = p.lexer.identifier; + // Parse either an async function, an async expression, or a normal expression + var expr: Expr = Expr{ .loc = loc, .data = Expr.Data{ .e_missing = .{} } }; + if (is_identifier and strings.eqlComptime(p.lexer.raw(), "async")) { + const async_range = p.lexer.range(); + try p.lexer.next(); + if (p.lexer.token == .t_function and !p.lexer.has_newline_before) { + try p.lexer.next(); + + return try p.parseFnStmt(async_range.loc, opts, async_range); + } + + expr = try p.parseAsyncPrefixExpr(async_range, .lowest); + try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none); + } else { + const exprOrLet = try p.parseExprOrLetStmt(opts); + switch (exprOrLet.stmt_or_expr) { + .stmt => |stmt| { + try p.lexer.expectOrInsertSemicolon(); + return stmt; + }, + .expr => |_expr| { + expr = _expr; + }, + } + } + if (is_identifier) { + switch (expr.data) { + .e_identifier => |ident| { + if (p.lexer.token == .t_colon and !opts.hasDecorators()) { + _ = try p.pushScopeForParsePass(.label, loc); + defer p.popScope(); + + // Parse a labeled statement + try p.lexer.next(); + + const _name = LocRef{ .loc = expr.loc, .ref = ident.ref }; + var nestedOpts = ParseStatementOptions{}; + + switch (opts.lexical_decl) { + .allow_all, .allow_fn_inside_label => { + nestedOpts.lexical_decl = .allow_fn_inside_label; + }, + else => {}, + } + const stmt = try p.parseStmt(&nestedOpts); + return p.s(S.Label{ .name = _name, .stmt = stmt }, loc); + } + }, + else => {}, + } + + if (is_typescript_enabled) { + if (js_lexer.TypescriptStmtKeyword.List.get(name)) |ts_stmt| { + switch (ts_stmt) { + .ts_stmt_type => { + if (p.lexer.token == .t_identifier and !p.lexer.has_newline_before) { + // "type Foo = any" + var stmtOpts = ParseStatementOptions{ .is_module_scope = opts.is_module_scope }; + try p.skipTypeScriptTypeStmt(&stmtOpts); + return p.s(S.TypeScript{}, loc); + } + }, + .ts_stmt_namespace, .ts_stmt_module => { + // "namespace Foo {}" + // "module Foo {}" + // "declare module 'fs' {}" + // "declare module 'fs';" + if (((opts.is_module_scope or opts.is_namespace_scope) and (p.lexer.token == .t_identifier or + (p.lexer.token == .t_string_literal and opts.is_typescript_declare)))) + { + return p.parseTypeScriptNamespaceStmt(loc, opts); + } + }, + .ts_stmt_interface => { + // "interface Foo {}" + var stmtOpts = ParseStatementOptions{ .is_module_scope = opts.is_module_scope }; + + try p.skipTypeScriptInterfaceStmt(&stmtOpts); + return p.s(S.TypeScript{}, loc); + }, + .ts_stmt_abstract => { + if (p.lexer.token == .t_class or opts.ts_decorators != null) { + return try p.parseClassStmt(loc, opts); + } + }, + .ts_stmt_global => { + // "declare module 'fs' { global { namespace NodeJS {} } }" + if (opts.is_namespace_scope and opts.is_typescript_declare and p.lexer.token == .t_open_brace) { + try p.lexer.next(); + _ = try p.parseStmtsUpTo(.t_close_brace, opts); + try p.lexer.next(); + return p.s(S.TypeScript{}, loc); + } + }, + .ts_stmt_declare => { + opts.lexical_decl = .allow_all; + opts.is_typescript_declare = true; + + // "@decorator declare class Foo {}" + // "@decorator declare abstract class Foo {}" + if (opts.ts_decorators != null and p.lexer.token != .t_class and !p.lexer.isContextualKeyword("abstract")) { + try p.lexer.expected(.t_class); + } + + // "declare global { ... }" + if (p.lexer.isContextualKeyword("global")) { + try p.lexer.next(); + try p.lexer.expect(.t_open_brace); + _ = try p.parseStmtsUpTo(.t_close_brace, opts); + try p.lexer.next(); + return p.s(S.TypeScript{}, loc); + } + + // "declare const x: any" + const stmt = try p.parseStmt(opts); + if (opts.ts_decorators) |decs| { + p.discardScopesUpTo(decs.scope_index); + } + + // Unlike almost all uses of "declare", statements that use + // "export declare" with "var/let/const" inside a namespace affect + // code generation. They cause any declared bindings to be + // considered exports of the namespace. Identifier references to + // those names must be converted into property accesses off the + // namespace object: + // + // namespace ns { + // export declare const x + // export function y() { return x } + // } + // + // (ns as any).x = 1 + // console.log(ns.y()) + // + // In this example, "return x" must be replaced with "return ns.x". + // This is handled by replacing each "export declare" statement + // inside a namespace with an "export var" statement containing all + // of the declared bindings. That "export var" statement will later + // cause identifiers to be transformed into property accesses. + if (opts.is_namespace_scope and opts.is_export) { + var decls: G.Decl.List = .{}; + switch (stmt.data) { + .s_local => |local| { + var _decls = try ListManaged(G.Decl).initCapacity(p.allocator, local.decls.len); + for (local.decls.slice()) |decl| { + try extractDeclsForBinding(decl.binding, &_decls); + } + decls.update(_decls); + }, + else => {}, + } + + if (decls.len > 0) { + return p.s(S.Local{ + .kind = .k_var, + .is_export = true, + .decls = decls, + }, loc); + } + } + + return p.s(S.TypeScript{}, loc); + }, + } + } + } + } + // Output.print("\n\nmVALUE {s}:{s}\n", .{ expr, name }); + try p.lexer.expectOrInsertSemicolon(); + return p.s(S.SExpr{ .value = expr }, loc); + } + + pub fn parseStmt(p: *P, opts: *ParseStatementOptions) anyerror!Stmt { + if (!p.stack_check.isSafeToRecurse()) { + try bun.throwStackOverflow(); + } + + return switch (p.lexer.token) { + .t_semicolon => t_semicolon(p), + .t_at => t_at(p, opts), + + inline .t_export, + .t_function, + .t_enum, + .t_class, + .t_var, + .t_const, + .t_if, + .t_do, + .t_while, + .t_with, + .t_switch, + .t_try, + .t_for, + .t_import, + .t_break, + .t_continue, + .t_return, + .t_throw, + .t_debugger, + .t_open_brace, + => |function| @field(@This(), @tagName(function))(p, opts, p.lexer.loc()), + + else => parseStmtFallthrough(p, opts, p.lexer.loc()), + }; } }; }