This commit is contained in:
Jarred Sumner
2021-04-23 12:30:37 -07:00
parent 862afbaf62
commit de976f2ffe
6 changed files with 528 additions and 284 deletions

View File

@@ -35,30 +35,30 @@ pub const ImportRecord = struct {
// Sometimes the parser creates an import record and decides it isn't needed.
// For example, TypeScript code may have import statements that later turn
// out to be type-only imports after analyzing the whole file.
is_unused: bool,
is_unused: bool = false,
// If this is true, the import contains syntax like "* as ns". This is used
// to determine whether modules that have no exports need to be wrapped in a
// CommonJS wrapper or not.
contains_import_star: bool,
contains_import_star: bool = false,
// If this is true, the import contains an import for the alias "default",
// either via the "import x from" or "import {default as x} from" syntax.
contains_default_alias: bool,
contains_default_alias: bool = false,
// If true, this "export * from 'path'" statement is evaluated at run-time by
// calling the "__reExport()" helper function
calls_run_time_re_export_fn: bool,
calls_run_time_re_export_fn: bool = false,
// Tell the printer to wrap this call to "require()" in "__toModule(...)"
wrap_with_to_module: bool,
wrap_with_to_module: bool = false,
// True for require calls like this: "try { require() } catch {}". In this
// case we shouldn't generate an error if the path could not be resolved.
is_inside_try_body: bool,
is_inside_try_body: bool = false,
// If true, this was originally written as a bare "import 'file'" statement
was_originally_bare_import: bool,
was_originally_bare_import: bool = false,
kind: ImportKind,
};

View File

@@ -237,6 +237,15 @@ pub const G = struct {
alias: string,
};
pub const ExportStarAlias = struct {
loc: logger.Loc,
// Although this alias name starts off as being the same as the statement's
// namespace symbol, it may diverge if the namespace symbol name is minified.
// The original alias name is preserved here to avoid this scenario.
original_name: string,
};
pub const Class = struct {
class_keyword: logger.Range = logger.Range.None,
ts_decorators: ExprNodeList = &([_]Expr{}),
@@ -822,252 +831,228 @@ pub const Stmt = struct {
var None = S.Empty{};
pub fn init(st: anytype, loc: logger.Loc) Stmt {
if (@typeInfo(@TypeOf(st)) != .Pointer) {
pub fn init(origData: anytype, loc: logger.Loc) Stmt {
if (@typeInfo(@TypeOf(origData)) != .Pointer) {
@compileError("Stmt.init needs a pointer.");
}
switch (@TypeOf(st.*)) {
switch (@TypeOf(origData.*)) {
S.Block => {
return Stmt{ .loc = loc, .data = Data{ .s_block = st } };
},
S.SExpr => {
return Stmt{ .loc = loc, .data = Data{ .s_expr = st } };
},
S.Comment => {
return Stmt{ .loc = loc, .data = Data{ .s_comment = st } };
},
S.Directive => {
return Stmt{ .loc = loc, .data = Data{ .s_directive = st } };
},
S.ExportClause => {
return Stmt{ .loc = loc, .data = Data{ .s_export_clause = st } };
},
S.Empty => {
return Stmt{ .loc = loc, .data = Data{ .s_empty = st } };
},
S.TypeScript => {
return Stmt{ .loc = loc, .data = Data{ .s_type_script = st } };
},
S.Debugger => {
return Stmt{ .loc = loc, .data = Data{ .s_debugger = st } };
},
S.ExportFrom => {
return Stmt{ .loc = loc, .data = Data{ .s_export_from = st } };
},
S.ExportDefault => {
return Stmt{ .loc = loc, .data = Data{ .s_export_default = st } };
},
S.Enum => {
return Stmt{ .loc = loc, .data = Data{ .s_enum = st } };
},
S.Namespace => {
return Stmt{ .loc = loc, .data = Data{ .s_namespace = st } };
},
S.Function => {
return Stmt{ .loc = loc, .data = Data{ .s_function = st } };
},
S.Class => {
return Stmt{ .loc = loc, .data = Data{ .s_class = st } };
},
S.If => {
return Stmt{ .loc = loc, .data = Data{ .s_if = st } };
},
S.For => {
return Stmt{ .loc = loc, .data = Data{ .s_for = st } };
},
S.ForIn => {
return Stmt{ .loc = loc, .data = Data{ .s_for_in = st } };
},
S.ForOf => {
return Stmt{ .loc = loc, .data = Data{ .s_for_of = st } };
},
S.DoWhile => {
return Stmt{ .loc = loc, .data = Data{ .s_do_while = st } };
},
S.While => {
return Stmt{ .loc = loc, .data = Data{ .s_while = st } };
},
S.With => {
return Stmt{ .loc = loc, .data = Data{ .s_with = st } };
},
S.Try => {
return Stmt{ .loc = loc, .data = Data{ .s_try = st } };
},
S.Switch => {
return Stmt{ .loc = loc, .data = Data{ .s_switch = st } };
},
S.Import => {
return Stmt{ .loc = loc, .data = Data{ .s_import = st } };
},
S.Return => {
return Stmt{ .loc = loc, .data = Data{ .s_return = st } };
},
S.Throw => {
return Stmt{ .loc = loc, .data = Data{ .s_throw = st } };
},
S.Local => {
return Stmt{ .loc = loc, .data = Data{ .s_local = st } };
return Stmt.comptime_init("s_block", S.Block, origData, loc);
},
S.Break => {
return Stmt{ .loc = loc, .data = Data{ .s_break = st } };
return Stmt.comptime_init("s_break", S.Break, origData, loc);
},
S.Class => {
return Stmt.comptime_init("s_class", S.Class, origData, loc);
},
S.Comment => {
return Stmt.comptime_init("s_comment", S.Comment, origData, loc);
},
S.Continue => {
return Stmt{ .loc = loc, .data = Data{ .s_continue = st } };
return Stmt.comptime_init("s_continue", S.Continue, origData, loc);
},
S.Debugger => {
return Stmt.comptime_init("s_debugger", S.Debugger, origData, loc);
},
S.Directive => {
return Stmt.comptime_init("s_directive", S.Directive, origData, loc);
},
S.DoWhile => {
return Stmt.comptime_init("s_do_while", S.DoWhile, origData, loc);
},
S.Empty => {
return Stmt.comptime_init("s_empty", S.Empty, origData, loc);
},
S.Enum => {
return Stmt.comptime_init("s_enum", S.Enum, origData, loc);
},
S.ExportClause => {
return Stmt.comptime_init("s_export_clause", S.ExportClause, origData, loc);
},
S.ExportDefault => {
return Stmt.comptime_init("s_export_default", S.ExportDefault, origData, loc);
},
S.ExportEquals => {
return Stmt.comptime_init("s_export_equals", S.ExportEquals, origData, loc);
},
S.ExportFrom => {
return Stmt.comptime_init("s_export_from", S.ExportFrom, origData, loc);
},
S.ExportStar => {
return Stmt.comptime_init("s_export_star", S.ExportStar, origData, loc);
},
S.SExpr => {
return Stmt.comptime_init("s_expr", S.SExpr, origData, loc);
},
S.ForIn => {
return Stmt.comptime_init("s_for_in", S.ForIn, origData, loc);
},
S.ForOf => {
return Stmt.comptime_init("s_for_of", S.ForOf, origData, loc);
},
S.For => {
return Stmt.comptime_init("s_for", S.For, origData, loc);
},
S.Function => {
return Stmt.comptime_init("s_function", S.Function, origData, loc);
},
S.If => {
return Stmt.comptime_init("s_if", S.If, origData, loc);
},
S.Import => {
return Stmt.comptime_init("s_import", S.Import, origData, loc);
},
S.Label => {
return Stmt.comptime_init("s_label", S.Label, origData, loc);
},
S.LazyExport => {
return Stmt.comptime_init("s_lazy_export", S.LazyExport, origData, loc);
},
S.Local => {
return Stmt.comptime_init("s_local", S.Local, origData, loc);
},
S.Namespace => {
return Stmt.comptime_init("s_namespace", S.Namespace, origData, loc);
},
S.Return => {
return Stmt.comptime_init("s_return", S.Return, origData, loc);
},
S.Switch => {
return Stmt.comptime_init("s_switch", S.Switch, origData, loc);
},
S.Throw => {
return Stmt.comptime_init("s_throw", S.Throw, origData, loc);
},
S.Try => {
return Stmt.comptime_init("s_try", S.Try, origData, loc);
},
S.TypeScript => {
return Stmt.comptime_init("s_type_script", S.TypeScript, origData, loc);
},
S.While => {
return Stmt.comptime_init("s_while", S.While, origData, loc);
},
S.With => {
return Stmt.comptime_init("s_with", S.With, origData, loc);
},
else => {
@compileError("Invalid type in Stmt.init");
},
}
}
fn comptime_alloc(allocator: *std.mem.Allocator, comptime tag_name: string, comptime typename: type, origData: anytype, loc: logger.Loc) callconv(.Inline) Stmt {
var st = allocator.create(typename) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = @unionInit(Data, tag_name, st) };
}
fn comptime_init(comptime tag_name: string, comptime typename: type, origData: anytype, loc: logger.Loc) callconv(.Inline) Stmt {
return Stmt{ .loc = loc, .data = @unionInit(Data, tag_name, origData) };
}
pub fn alloc(allocator: *std.mem.Allocator, origData: anytype, loc: logger.Loc) Stmt {
switch (@TypeOf(origData)) {
S.Block => {
var st = allocator.create(S.Block) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_block = st } };
},
S.SExpr => {
var st = allocator.create(S.SExpr) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_expr = st } };
},
S.Comment => {
var st = allocator.create(S.Comment) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_comment = st } };
},
S.Directive => {
var st = allocator.create(S.Directive) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_directive = st } };
},
S.ExportClause => {
var st = allocator.create(S.ExportClause) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_export_clause = st } };
},
S.Empty => {
var st = allocator.create(S.Empty) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_empty = st } };
},
S.TypeScript => {
var st = allocator.create(S.TypeScript) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_type_script = st } };
},
S.Debugger => {
var st = allocator.create(S.Debugger) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_debugger = st } };
},
S.ExportFrom => {
var st = allocator.create(S.ExportFrom) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_export_from = st } };
},
S.ExportDefault => {
var st = allocator.create(S.ExportDefault) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_export_default = st } };
},
S.Enum => {
var st = allocator.create(S.Enum) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_enum = st } };
},
S.Namespace => {
var st = allocator.create(S.Namespace) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_namespace = st } };
},
S.Function => {
var st = allocator.create(S.Function) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_function = st } };
},
S.Class => {
var st = allocator.create(S.Class) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_class = st } };
},
S.If => {
var st = allocator.create(S.If) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_if = st } };
},
S.For => {
var st = allocator.create(S.For) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_for = st } };
},
S.ForIn => {
var st = allocator.create(S.ForIn) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_for_in = st } };
},
S.ForOf => {
var st = allocator.create(S.ForOf) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_for_of = st } };
},
S.DoWhile => {
var st = allocator.create(S.DoWhile) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_do_while = st } };
},
S.While => {
var st = allocator.create(S.While) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_while = st } };
},
S.With => {
var st = allocator.create(S.With) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_with = st } };
},
S.Try => {
var st = allocator.create(S.Try) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_try = st } };
},
S.Switch => {
var st = allocator.create(S.Switch) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_switch = st } };
},
S.Import => {
var st = allocator.create(S.Import) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_import = st } };
},
S.Return => {
var st = allocator.create(S.Return) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_return = st } };
},
S.Throw => {
var st = allocator.create(S.Throw) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_throw = st } };
},
S.Local => {
var st = allocator.create(S.Local) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_local = st } };
return Stmt.comptime_alloc(allocator, "s_block", S.Block, origData, loc);
},
S.Break => {
var st = allocator.create(S.Break) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_break = st } };
return Stmt.comptime_alloc(allocator, "s_break", S.Break, origData, loc);
},
S.Class => {
return Stmt.comptime_alloc(allocator, "s_class", S.Class, origData, loc);
},
S.Comment => {
return Stmt.comptime_alloc(allocator, "s_comment", S.Comment, origData, loc);
},
S.Continue => {
var st = allocator.create(S.Continue) catch unreachable;
st.* = origData;
return Stmt{ .loc = loc, .data = Data{ .s_continue = st } };
return Stmt.comptime_alloc(allocator, "s_continue", S.Continue, origData, loc);
},
S.Debugger => {
return Stmt.comptime_alloc(allocator, "s_debugger", S.Debugger, origData, loc);
},
S.Directive => {
return Stmt.comptime_alloc(allocator, "s_directive", S.Directive, origData, loc);
},
S.DoWhile => {
return Stmt.comptime_alloc(allocator, "s_do_while", S.DoWhile, origData, loc);
},
S.Empty => {
return Stmt.comptime_alloc(allocator, "s_empty", S.Empty, origData, loc);
},
S.Enum => {
return Stmt.comptime_alloc(allocator, "s_enum", S.Enum, origData, loc);
},
S.ExportClause => {
return Stmt.comptime_alloc(allocator, "s_export_clause", S.ExportClause, origData, loc);
},
S.ExportDefault => {
return Stmt.comptime_alloc(allocator, "s_export_default", S.ExportDefault, origData, loc);
},
S.ExportEquals => {
return Stmt.comptime_alloc(allocator, "s_export_equals", S.ExportEquals, origData, loc);
},
S.ExportFrom => {
return Stmt.comptime_alloc(allocator, "s_export_from", S.ExportFrom, origData, loc);
},
S.ExportStar => {
return Stmt.comptime_alloc(allocator, "s_export_star", S.ExportStar, origData, loc);
},
S.SExpr => {
return Stmt.comptime_alloc(allocator, "s_expr", S.SExpr, origData, loc);
},
S.ForIn => {
return Stmt.comptime_alloc(allocator, "s_for_in", S.ForIn, origData, loc);
},
S.ForOf => {
return Stmt.comptime_alloc(allocator, "s_for_of", S.ForOf, origData, loc);
},
S.For => {
return Stmt.comptime_alloc(allocator, "s_for", S.For, origData, loc);
},
S.Function => {
return Stmt.comptime_alloc(allocator, "s_function", S.Function, origData, loc);
},
S.If => {
return Stmt.comptime_alloc(allocator, "s_if", S.If, origData, loc);
},
S.Import => {
return Stmt.comptime_alloc(allocator, "s_import", S.Import, origData, loc);
},
S.Label => {
return Stmt.comptime_alloc(allocator, "s_label", S.Label, origData, loc);
},
S.LazyExport => {
return Stmt.comptime_alloc(allocator, "s_lazy_export", S.LazyExport, origData, loc);
},
S.Local => {
return Stmt.comptime_alloc(allocator, "s_local", S.Local, origData, loc);
},
S.Namespace => {
return Stmt.comptime_alloc(allocator, "s_namespace", S.Namespace, origData, loc);
},
S.Return => {
return Stmt.comptime_alloc(allocator, "s_return", S.Return, origData, loc);
},
S.Switch => {
return Stmt.comptime_alloc(allocator, "s_switch", S.Switch, origData, loc);
},
S.Throw => {
return Stmt.comptime_alloc(allocator, "s_throw", S.Throw, origData, loc);
},
S.Try => {
return Stmt.comptime_alloc(allocator, "s_try", S.Try, origData, loc);
},
S.TypeScript => {
return Stmt.comptime_alloc(allocator, "s_type_script", S.TypeScript, origData, loc);
},
S.While => {
return Stmt.comptime_alloc(allocator, "s_while", S.While, origData, loc);
},
S.With => {
return Stmt.comptime_alloc(allocator, "s_with", S.With, origData, loc);
},
else => {
@compileError("Invalid type in Stmt.init");
},
@@ -1076,66 +1061,74 @@ pub const Stmt = struct {
pub const Tag = packed enum {
s_block,
s_comment,
s_directive,
s_export_clause,
s_empty,
s_type_script,
s_debugger,
s_export_from,
s_export_default,
s_enum,
s_namespace,
s_function,
s_break,
s_class,
s_if,
s_for,
s_comment,
s_continue,
s_debugger,
s_directive,
s_do_while,
s_empty,
s_enum,
s_export_clause,
s_export_default,
s_export_equals,
s_export_from,
s_export_star,
s_expr,
s_for_in,
s_for_of,
s_do_while,
s_for,
s_function,
s_if,
s_import,
s_label,
s_lazy_export,
s_local,
s_namespace,
s_return,
s_switch,
s_throw,
s_try,
s_type_script,
s_while,
s_with,
s_try,
s_switch,
s_import,
s_return,
s_throw,
s_local,
s_break,
s_continue,
s_expr,
};
pub const Data = union(Tag) {
s_block: *S.Block,
s_expr: *S.SExpr,
s_comment: *S.Comment,
s_directive: *S.Directive,
s_export_clause: *S.ExportClause,
s_empty: *S.Empty,
s_type_script: *S.TypeScript,
s_debugger: *S.Debugger,
s_export_from: *S.ExportFrom,
s_export_default: *S.ExportDefault,
s_enum: *S.Enum,
s_namespace: *S.Namespace,
s_function: *S.Function,
s_break: *S.Break,
s_class: *S.Class,
s_if: *S.If,
s_for: *S.For,
s_comment: *S.Comment,
s_continue: *S.Continue,
s_debugger: *S.Debugger,
s_directive: *S.Directive,
s_do_while: *S.DoWhile,
s_empty: *S.Empty,
s_enum: *S.Enum,
s_export_clause: *S.ExportClause,
s_export_default: *S.ExportDefault,
s_export_equals: *S.ExportEquals,
s_export_from: *S.ExportFrom,
s_export_star: *S.ExportStar,
s_expr: *S.SExpr,
s_for_in: *S.ForIn,
s_for_of: *S.ForOf,
s_do_while: *S.DoWhile,
s_for: *S.For,
s_function: *S.Function,
s_if: *S.If,
s_import: *S.Import,
s_label: *S.Label,
s_lazy_export: *S.LazyExport,
s_local: *S.Local,
s_namespace: *S.Namespace,
s_return: *S.Return,
s_switch: *S.Switch,
s_throw: *S.Throw,
s_try: *S.Try,
s_type_script: *S.TypeScript,
s_while: *S.While,
s_with: *S.With,
s_try: *S.Try,
s_switch: *S.Switch,
s_import: *S.Import,
s_return: *S.Return,
s_throw: *S.Throw,
s_local: *S.Local,
s_break: *S.Break,
s_continue: *S.Continue,
};
pub fn caresAboutScope(self: *Stmt) bool {
@@ -2141,10 +2134,25 @@ pub const S = struct {
pub const Directive = struct { value: JavascriptString, legacy_octal_loc: logger.Loc };
pub const ExportClause = struct { items: []ClauseItem };
pub const ExportClause = struct { items: []ClauseItem, is_single_line: bool = false };
pub const Empty = struct {};
pub const ExportStar = struct {
namespace_ref: Ref,
alias: ?G.ExportStarAlias = null,
import_record_index: u32,
};
// This is an "export = value;" statement in TypeScript
pub const ExportEquals = struct { value: ExprNodeIndex };
// The decision of whether to export an expression using "module.exports" or
// "export default" is deferred until linking using this statement kind
pub const LazyExport = struct { value: ExprNodeIndex };
pub const Label = struct { name: LocRef, stmt: StmtNodeIndex };
// This is a stand-in for a TypeScript type declaration
pub const TypeScript = struct {};
@@ -2194,9 +2202,13 @@ pub const S = struct {
// May be a SConst, SLet, SVar, or SExpr
init: StmtNodeIndex, value: ExprNodeIndex, body: StmtNodeIndex };
pub const ForOf = struct { is_await: bool,
// May be a SConst, SLet, SVar, or SExpr
init: StmtNodeIndex, value: ExprNodeIndex, body: StmtNodeIndex };
pub const ForOf = struct {
is_await: bool = false,
// May be a SConst, SLet, SVar, or SExpr
init: StmtNodeIndex,
value: ExprNodeIndex,
body: StmtNodeIndex,
};
pub const DoWhile = struct { body: StmtNodeIndex, test_: ExprNodeIndex };

View File

@@ -303,7 +303,7 @@ pub const Lexer = struct {
}
}
pub fn expectContextualKeyword(self: *Lexer, keyword: string) void {
pub fn expectContextualKeyword(self: *Lexer, comptime keyword: string) void {
if (!self.isContextualKeyword(keyword)) {
self.addError(self.start, "\"{s}\"", .{keyword}, true);
}
@@ -765,7 +765,7 @@ pub const Lexer = struct {
return self.source.contents[self.start..self.end];
}
pub fn isContextualKeyword(self: *Lexer, keyword: string) bool {
pub fn isContextualKeyword(self: *Lexer, comptime keyword: string) bool {
return self.token == .t_identifier and strings.eql(self.raw(), keyword);
}
@@ -844,7 +844,7 @@ pub const Lexer = struct {
// TODO: use wtf-8 encoding.
pub fn utf16ToString(lexer: *Lexer, js: JavascriptString) string {
return std.unicode.utf16leToUtf8Alloc(lexer.alloc, js) catch unreachable;
return std.unicode.utf16leToUtf8Alloc(lexer.allocator, js) catch unreachable;
}
pub fn nextInsideJSXElement() void {

View File

@@ -5,10 +5,13 @@ const importRecord = @import("import_record.zig");
const js_ast = @import("js_ast.zig");
const options = @import("options.zig");
const alloc = @import("alloc.zig");
const fs = @import("fs.zig");
usingnamespace @import("strings.zig");
usingnamespace @import("ast/base.zig");
usingnamespace js_ast.G;
const ImportKind = importRecord.ImportKind;
const BindingNodeIndex = js_ast.BindingNodeIndex;
const StmtNodeIndex = js_ast.StmtNodeIndex;
const ExprNodeIndex = js_ast.ExprNodeIndex;
@@ -65,6 +68,8 @@ const ThenCatchChain = struct {
has_catch: bool = false,
};
const ParsedPath = struct { loc: logger.Loc, text: string };
const StrictModeFeature = enum {
with_statement,
delete_bare_name,
@@ -305,6 +310,8 @@ pub const Parser = struct {
}
};
const ExportClauseResult = struct { clauses: []js_ast.ClauseItem = &([_]js_ast.ClauseItem{}), is_single_line: bool = false };
const DeferredTsDecorators = struct {
values: []js_ast.Expr,
@@ -1461,8 +1468,83 @@ const P = struct {
p.lexer.expectOrInsertSemicolon();
return p.s(S.ExportDefault{ .default_name = createDefaultName(p, loc) catch unreachable, .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)) {
p.lexer.unexpected();
}
p.lexer.next();
var namespace_ref: js_ast.Ref = undefined;
var alias: ?js_ast.G.ExportStarAlias = null;
var path_loc: logger.Loc = undefined;
var path_text: string = undefined;
if (p.lexer.isContextualKeyword("as")) {
// "export * as ns from 'path'"
const name = p.lexer.identifier;
namespace_ref = p.storeNameInRef(name) catch unreachable;
alias = G.ExportStarAlias{ .loc = p.lexer.loc(), .original_name = name };
if (!p.lexer.isIdentifierOrKeyword()) {
p.lexer.expect(.t_identifier);
}
p.checkForNonBMPCodePoint((alias orelse unreachable).loc, name);
p.lexer.next();
p.lexer.expectContextualKeyword("from");
const parsedPath = p.parsePath();
path_loc = parsedPath.loc;
path_text = parsedPath.text;
} else {
// "export * from 'path'"
p.lexer.expectContextualKeyword("from");
const parsedPath = p.parsePath();
path_loc = parsedPath.loc;
path_text = parsedPath.text;
var path_name = fs.PathName.init(strings.append(p.allocator, path_text, "_star") catch unreachable);
namespace_ref = p.storeNameInRef(path_name.nonUniqueNameString(p.allocator) catch unreachable) catch unreachable;
}
var import_record_index = p.addImportRecord(ImportKind.stmt, path_loc, path_text);
p.lexer.expectOrInsertSemicolon();
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)) {
p.lexer.unexpected();
}
const export_clause = p.parseExportClause();
if (p.lexer.isContextualKeyword("from")) {
p.lexer.expectContextualKeyword("from");
const parsedPath = p.parsePath();
const import_record_index = p.addImportRecord(.stmt, parsedPath.loc, parsedPath.text);
var path_name = fs.PathName.init(strings.append(p.allocator, "import_", parsedPath.text) catch unreachable);
const namespace_ref = p.storeNameInRef(path_name.nonUniqueNameString(p.allocator) catch unreachable) catch unreachable;
p.lexer.expectOrInsertSemicolon();
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);
}
p.lexer.expectOrInsertSemicolon();
return p.s(S.ExportClause{ .items = export_clause.clauses, .is_single_line = export_clause.is_single_line }, loc);
},
T.t_equals => {
// "export = value;"
p.es6_export_keyword = previousExportKeyword; // This wasn't an ESM export statement after all
if (p.options.ts) {
p.lexer.next();
var value = p.parseExpr(.lowest);
p.lexer.expectOrInsertSemicolon();
return p.s(S.ExportEquals{ .value = value }, loc);
}
p.lexer.unexpected();
return Stmt.empty();
},
else => {
notimpl();
p.lexer.unexpected();
return Stmt.empty();
},
}
},
@@ -1475,6 +1557,114 @@ const P = struct {
return js_ast.Stmt.empty();
}
pub fn parseExportClause(p: *P) ExportClauseResult {
var items = List(js_ast.ClauseItem).initCapacity(p.allocator, 1) catch unreachable;
var first_keyword_item_loc = logger.Loc{};
p.lexer.expect(.t_open_brace);
var is_single_line = !p.lexer.has_newline_before;
while (p.lexer.token != .t_close_brace) {
var alias = p.lexer.identifier;
var alias_loc = p.lexer.loc();
var name = LocRef{
.loc = alias_loc,
.ref = p.storeNameInRef(alias) catch unreachable,
};
var original_name = alias;
// The name can actually be a keyword if we're really an "export from"
// statement. However, we won't know until later. Allow keywords as
// identifiers for now and throw an error later if there's no "from".
//
// // This is fine
// export { default } from 'path'
//
// // This is a syntax error
// export { default }
//
if (p.lexer.token != .t_identifier) {
if (!p.lexer.isIdentifierOrKeyword()) {
p.lexer.expect(.t_identifier);
}
if (first_keyword_item_loc.start == 0) {
first_keyword_item_loc = p.lexer.loc();
}
}
p.checkForNonBMPCodePoint(alias_loc, alias);
p.lexer.next();
if (p.lexer.isContextualKeyword("as")) {
p.lexer.next();
alias = p.lexer.identifier;
alias_loc = p.lexer.loc();
// The alias may be a keyword
if (!p.lexer.isIdentifierOrKeyword()) {
p.lexer.expect(.t_identifier);
}
p.checkForNonBMPCodePoint(alias_loc, alias);
p.lexer.next();
}
items.append(js_ast.ClauseItem{
.alias = alias,
.alias_loc = alias_loc,
.name = name,
.original_name = original_name,
}) catch unreachable;
// we're done if there's no comma
if (p.lexer.token != .t_comma) {
break;
}
if (p.lexer.has_newline_before) {
is_single_line = false;
}
p.lexer.next();
if (p.lexer.has_newline_before) {
is_single_line = false;
}
}
if (p.lexer.has_newline_before) {
is_single_line = false;
}
p.lexer.expect(.t_close_brace);
// Throw an error here if we found a keyword earlier and this isn't an
// "export from" statement after all
if (first_keyword_item_loc.start != 0 and !p.lexer.isContextualKeyword("from")) {
const r = js_lexer.rangeOfIdentifier(&p.source, first_keyword_item_loc);
p.lexer.addRangeError(r, "Expected identifier but found \"{s}\"", .{p.source.textForRange(r)}, true);
}
return ExportClauseResult{
.clauses = items.toOwnedSlice(),
.is_single_line = is_single_line,
};
}
pub fn parsePath(p: *P) ParsedPath {
var path = ParsedPath{
.loc = p.lexer.loc(),
.text = p.lexer.utf16ToString(p.lexer.string_literal),
};
if (p.lexer.token == .t_no_substitution_template_literal) {
p.lexer.next();
} else {
p.lexer.expect(.t_string_literal);
}
return path;
}
// TODO:
pub fn checkForNonBMPCodePoint(p: *P, loc: logger.Loc, name: string) void {}
pub fn parseStmtsUpTo(p: *P, eend: js_lexer.T, opts: *ParseStatementOptions) ![]Stmt {
var stmts = try StmtList.initCapacity(p.allocator, 1);
@@ -1970,6 +2160,17 @@ const P = struct {
return p.parseSuffix(expr, level, errors, flags);
}
pub fn addImportRecord(p: *P, kind: ImportKind, loc: logger.Loc, name: string) u32 {
var index = p.import_records.items.len;
const record = ImportRecord{
.kind = kind,
.range = p.source.rangeOfString(loc),
.path = fs.Path.init(name),
};
p.import_records.append(record) catch unreachable;
return @intCast(u32, index);
}
pub fn popScope(p: *P) void {
const current_scope = p.current_scope orelse unreachable;
// We cannot rename anything inside a scope containing a direct eval() call

View File

@@ -266,6 +266,33 @@ pub const Source = struct {
return Range{ .loc = loc };
}
pub fn rangeOfString(self: *Source, loc: Loc) Range {
const text = self.contents[loc.i()..];
if (text.len == 0) {
return Range.None;
}
const quote = text[0];
if (quote == '"' or quote == '\'') {
var i: usize = 1;
var c: u8 = undefined;
while (i < text.len) {
c = text[i];
if (c == quote) {
return Range{ .loc = loc, .len = @intCast(i32, i + 1) };
} else if (c == '\\') {
i += 1;
}
i += 1;
}
}
return Range{ .loc = loc, .len = 0 };
}
pub fn rangeOfOperatorAfter(self: *Source, loc: Loc, op: string) Range {
const text = self.contents[loc.i()..];
const index = strings.index(text, op);

View File

@@ -29,6 +29,10 @@ pub fn eql(self: string, other: anytype) bool {
return std.mem.eql(u8, self, other);
}
pub fn append(allocator: *std.mem.Allocator, self: string, other: string) !string {
return std.fmt.allocPrint(allocator, "{s}{s}", .{ self, other });
}
pub fn index(self: string, str: string) i32 {
if (std.mem.indexOf(u8, self, str)) |i| {
return @intCast(i32, i);