aboutsummaryrefslogtreecommitdiff
path: root/src/js_parser/js_parser.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_parser/js_parser.zig')
-rw-r--r--src/js_parser/js_parser.zig112
1 files changed, 71 insertions, 41 deletions
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index f36d51981..291466dd0 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -103,6 +103,29 @@ pub const AllocatedNamesPool = ObjectPool(
4,
);
+fn foldStringAddition(lhs: Expr, rhs: Expr) ?Expr {
+ switch (lhs.data) {
+ .e_string => |left| {
+ if (rhs.data == .e_string and left.isUTF8() and rhs.data.e_string.isUTF8()) {
+ lhs.data.e_string.push(rhs.data.e_string);
+ return lhs;
+ }
+ },
+ .e_binary => |bin| {
+
+ // 123 + "bar" + "baz"
+ if (bin.op == .bin_add) {
+ if (foldStringAddition(bin.right, rhs)) |out| {
+ return Expr.init(E.Binary, E.Binary{ .op = bin.op, .left = bin.left, .right = out }, lhs.loc);
+ }
+ }
+ },
+ else => {},
+ }
+
+ return null;
+}
+
// If we are currently in a hoisted child of the module scope, relocate these
// declarations to the top level and return an equivalent assignment statement.
// Make sure to check that the declaration kind is "var" before calling this.
@@ -222,7 +245,7 @@ const JSXTag = struct {
if (strings.containsComptime(name, "-:") or (p.lexer.token != .t_dot and name[0] >= 'a' and name[0] <= 'z')) {
return JSXTag{
.data = Data{ .tag = p.e(E.String{
- .utf8 = name,
+ .data = name,
}, loc) },
.range = tag_range,
};
@@ -2287,6 +2310,7 @@ pub const Parser = struct {
fn _parse(self: *Parser, comptime ParserType: type) !js_ast.Result {
var p: ParserType = undefined;
try ParserType.init(self.allocator, self.log, self.source, self.define, self.lexer, self.options, &p);
+ p.should_fold_numeric_constants = self.options.features.should_fold_numeric_constants;
defer p.lexer.deinit();
var result: js_ast.Result = undefined;
@@ -2577,7 +2601,7 @@ pub const Parser = struct {
},
loc,
),
- .value = p.e(E.String{ .utf8 = p.source.path.pretty }, loc),
+ .value = p.e(E.String{ .data = p.source.path.pretty }, loc),
};
decl_i += 1;
}
@@ -3032,7 +3056,7 @@ pub const Prefill = struct {
Expr{ .data = .{ .e_boolean = E.Boolean{ .value = false } }, .loc = logger.Loc.Empty },
};
pub var ActivateString = E.String{
- .utf8 = "activate",
+ .data = "activate",
};
pub var ActivateIndex = E.Index{
.index = .{
@@ -3056,11 +3080,11 @@ pub const Prefill = struct {
pub const Zero = E.Number{ .value = 0.0 };
};
pub const String = struct {
- pub var Key = E.String{ .utf8 = &Prefill.StringLiteral.Key };
- pub var Children = E.String{ .utf8 = &Prefill.StringLiteral.Children };
- pub var Filename = E.String{ .utf8 = &Prefill.StringLiteral.Filename };
- pub var LineNumber = E.String{ .utf8 = &Prefill.StringLiteral.LineNumber };
- pub var ColumnNumber = E.String{ .utf8 = &Prefill.StringLiteral.ColumnNumber };
+ pub var Key = E.String{ .data = &Prefill.StringLiteral.Key };
+ pub var Children = E.String{ .data = &Prefill.StringLiteral.Children };
+ pub var Filename = E.String{ .data = &Prefill.StringLiteral.Filename };
+ pub var LineNumber = E.String{ .data = &Prefill.StringLiteral.LineNumber };
+ pub var ColumnNumber = E.String{ .data = &Prefill.StringLiteral.ColumnNumber };
};
pub const Data = struct {
pub var BMissing = B{ .b_missing = BMissing_ };
@@ -7883,7 +7907,7 @@ fn NewParser_(
try p.lexer.next();
- key = p.e(E.String{ .utf8 = name }, loc);
+ key = p.e(E.String{ .data = name }, loc);
if (p.lexer.token != .t_colon and p.lexer.token != .t_open_paren) {
const ref = p.storeNameInRef(name) catch unreachable;
@@ -7997,7 +8021,7 @@ fn NewParser_(
if (p.lexer.token == .t_string_literal) {
value.name = p.lexer.toEString();
} else if (p.lexer.isIdentifierOrKeyword()) {
- value.name = E.String{ .utf8 = p.lexer.identifier };
+ value.name = E.String{ .data = p.lexer.identifier };
needs_symbol = true;
} else {
try p.lexer.expect(.t_identifier);
@@ -9276,7 +9300,7 @@ fn NewParser_(
}
}
- key = p.e(E.String{ .utf8 = name }, name_range.loc);
+ key = p.e(E.String{ .data = name }, name_range.loc);
// Parse a shorthand property
const isShorthandProperty = !opts.is_class and
@@ -11307,7 +11331,7 @@ fn NewParser_(
if (comptime only_scan_imports_and_do_not_visit) {
if (value.data == .e_string and value.data.e_string.isUTF8() and value.data.e_string.isPresent()) {
- const import_record_index = p.addImportRecord(.dynamic, value.loc, value.data.e_string.utf8);
+ const import_record_index = p.addImportRecord(.dynamic, value.loc, value.data.e_string.slice(p.allocator));
return p.e(E.Import{
.expr = value,
@@ -11390,7 +11414,7 @@ fn NewParser_(
continue;
}
- const prop_name = p.e(E.String{ .utf8 = prop_name_literal }, key_range.loc);
+ const prop_name = p.e(E.String{ .data = prop_name_literal }, key_range.loc);
// Parse the value
var value: Expr = undefined;
@@ -11430,13 +11454,13 @@ fn NewParser_(
const key = brk: {
switch (expr.data) {
.e_import_identifier => |ident| {
- break :brk p.e(E.String{ .utf8 = p.loadNameFromRef(ident.ref) }, expr.loc);
+ break :brk p.e(E.String{ .data = p.loadNameFromRef(ident.ref) }, expr.loc);
},
.e_identifier => |ident| {
- break :brk p.e(E.String{ .utf8 = p.loadNameFromRef(ident.ref) }, expr.loc);
+ break :brk p.e(E.String{ .data = p.loadNameFromRef(ident.ref) }, expr.loc);
},
.e_dot => |dot| {
- break :brk p.e(E.String{ .utf8 = dot.name }, dot.name_loc);
+ break :brk p.e(E.String{ .data = dot.name }, dot.name_loc);
},
.e_index => |index| {
if (index.index.data == .e_string) {
@@ -12088,14 +12112,14 @@ fn NewParser_(
const runtime = if (p.options.jsx.runtime == .automatic and !e_.flags.is_key_before_rest) options.JSX.Runtime.automatic else options.JSX.Runtime.classic;
var children_count = e_.children.len;
- const is_childless_tag = FeatureFlags.react_specific_warnings and children_count > 0 and tag.data == .e_string and tag.data.e_string.isUTF8() and js_lexer.ChildlessJSXTags.has(tag.data.e_string.utf8);
+ const is_childless_tag = FeatureFlags.react_specific_warnings and children_count > 0 and tag.data == .e_string and tag.data.e_string.isUTF8() and js_lexer.ChildlessJSXTags.has(tag.data.e_string.slice(p.allocator));
children_count = if (is_childless_tag) 0 else children_count;
if (children_count != e_.children.len) {
// Error: meta is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
// ^ from react-dom
- p.log.addWarningFmt(p.source, tag.loc, p.allocator, "<{s} /> is a void element and must not have \"children\"", .{tag.data.e_string.utf8}) catch {};
+ p.log.addWarningFmt(p.source, tag.loc, p.allocator, "<{s} /> is a void element and must not have \"children\"", .{tag.data.e_string.slice(p.allocator)}) catch {};
}
// TODO: maybe we should split these into two different AST Nodes
@@ -12395,7 +12419,7 @@ fn NewParser_(
const jsx_element = e_.right.data.e_jsx_element;
if (jsx_element.tag) |tag| {
if (tag.data == .e_string) {
- const tag_string = tag.data.e_string.utf8;
+ const tag_string = tag.data.e_string.slice(p.allocator);
if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| {
call_args[1] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) };
} else {
@@ -12420,7 +12444,7 @@ fn NewParser_(
const jsx_element = e_.left.data.e_jsx_element;
if (jsx_element.tag) |tag| {
if (tag.data == .e_string) {
- const tag_string = tag.data.e_string.utf8;
+ const tag_string = tag.data.e_string.slice(p.allocator);
if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| {
call_args[0] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) };
} else {
@@ -12632,7 +12656,9 @@ fn NewParser_(
}
}
- // TODO: fold string addition
+ if (foldStringAddition(e_.left, e_.right)) |res| {
+ return res;
+ }
},
.bin_sub => {
if (p.should_fold_numeric_constants) {
@@ -12822,11 +12848,9 @@ fn NewParser_(
}
if (e_.optional_chain == null and e_.index.data == .e_string and e_.index.data.e_string.isUTF8()) {
- const literal = e_.index.data.e_string.utf8;
+ const literal = e_.index.data.e_string.slice(p.allocator);
if (p.maybeRewritePropertyAccess(
expr.loc,
- in.assign_target,
- is_delete_target,
e_.target,
literal,
e_.index.loc,
@@ -12857,8 +12881,14 @@ fn NewParser_(
}
}
}
+ // "foo"[2]
+ } else if (e_.optional_chain == null and target.data == .e_string and e_.index.data == .e_number and target.data.e_string.isUTF8() and e_.index.data.e_number.value >= 0) {
+ const literal = target.data.e_string.slice(p.allocator);
+ const index = e_.index.data.e_number.toUsize();
+ if (literal.len > index) {
+ return p.e(E.String{ .data = literal[index .. index + 1] }, expr.loc);
+ }
}
-
// Create an error for assigning to an import namespace when bundling. Even
// though this is a run-time error, we make it a compile-time error when
// bundling because scope hoisting means these will no longer be run-time
@@ -12894,7 +12924,7 @@ fn NewParser_(
}
if (SideEffects.typeof(e_.value.data)) |typeof| {
- return p.e(E.String{ .utf8 = typeof }, expr.loc);
+ return p.e(E.String{ .data = typeof }, expr.loc);
}
},
.un_delete => {
@@ -13023,8 +13053,6 @@ fn NewParser_(
if (e_.optional_chain == null) {
if (p.maybeRewritePropertyAccess(
expr.loc,
- in.assign_target,
- is_delete_target,
e_.target,
e_.name,
e_.name_loc,
@@ -13034,11 +13062,9 @@ fn NewParser_(
}
if (comptime allow_macros) {
- if (p.macro_call_count > 0) {
- if (e_.target.data == .e_object and e_.target.data.e_object.was_originally_macro) {
- if (e_.target.get(e_.name)) |obj| {
- return obj;
- }
+ if (p.macro_call_count > 0 and e_.target.data == .e_object and e_.target.data.e_object.was_originally_macro) {
+ if (e_.target.get(e_.name)) |obj| {
+ return obj;
}
}
}
@@ -13166,7 +13192,7 @@ fn NewParser_(
key.data.isStringValue() and
strings.eqlComptime(
// __proto__ is utf8, assume it lives in refs
- key.data.e_string.utf8,
+ key.data.e_string.slice(p.allocator),
"__proto__",
)) {
if (has_proto) {
@@ -13869,8 +13895,6 @@ fn NewParser_(
fn maybeRewritePropertyAccess(
p: *P,
loc: logger.Loc,
- _: js_ast.AssignTarget,
- _: bool,
target: js_ast.Expr,
name: string,
name_loc: logger.Loc,
@@ -13896,6 +13920,12 @@ fn NewParser_(
}
}
},
+ .e_string => |str| {
+ // minify "long-string".length to 11
+ if (strings.eqlComptime(name, "length")) {
+ return p.e(E.Number{ .value = @intToFloat(f64, str.len()) }, loc);
+ }
+ },
else => {},
}
@@ -15513,7 +15543,7 @@ fn NewParser_(
}
const last = parts.len - 1;
- const is_tail_match = strings.eql(parts[last], index.index.data.e_string.utf8);
+ const is_tail_match = strings.eql(parts[last], index.index.data.e_string.slice(p.allocator));
return is_tail_match and p.isDotDefineMatch(index.target, parts[0..last]);
}
},
@@ -15906,7 +15936,7 @@ fn NewParser_(
p.expr_list.appendAssumeCapacity(p.e(E.Identifier{
.ref = ref,
}, loc));
- p.expr_list.appendAssumeCapacity(p.e(E.String{ .utf8 = name }, loc));
+ p.expr_list.appendAssumeCapacity(p.e(E.String{ .data = name }, loc));
return p.s(S.SExpr{
// I believe that this is a spot we can do $RefreshReg$(name)
.value = p.callRuntime(loc, "__name", p.expr_list.items[start..p.expr_list.items.len]),
@@ -16431,7 +16461,7 @@ fn NewParser_(
sourcefile_name = sourcefile_name[end..];
}
}
- commonjs_wrapper.data.e_call.args.ptr[1] = p.e(E.String{ .utf8 = sourcefile_name }, logger.Loc.Empty);
+ commonjs_wrapper.data.e_call.args.ptr[1] = p.e(E.String{ .data = sourcefile_name }, logger.Loc.Empty);
new_stmts_list[imports_list.len] = p.s(
S.ExportDefault{
@@ -16558,7 +16588,7 @@ fn NewParser_(
new_call_args[0] = p.e(E.Number{ .value = @intToFloat(f64, p.options.filepath_hash_for_hmr) }, logger.Loc.Empty);
// This helps us provide better error messages
- new_call_args[1] = p.e(E.String{ .utf8 = p.source.path.pretty }, logger.Loc.Empty);
+ new_call_args[1] = p.e(E.String{ .data = p.source.path.pretty }, logger.Loc.Empty);
if (p.options.features.react_fast_refresh) {
new_call_args[2] = p.e(E.Identifier{ .ref = p.jsx_refresh_runtime.ref }, logger.Loc.Empty);
}
@@ -16685,7 +16715,7 @@ fn NewParser_(
);
export_properties[named_export_i] = G.Property{
- .key = p.e(E.String{ .utf8 = named_export.key_ptr.* }, logger.Loc.Empty),
+ .key = p.e(E.String{ .data = named_export.key_ptr.* }, logger.Loc.Empty),
.value = p.e(
E.Arrow{
.args = &[_]G.Arg{},