mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 11:59:00 +00:00
Fixes ENG-21287
Build times, from `bun run build && echo '//' >> src/main.zig && time
bun run build`
|Platform|0.14.1|0.15.2|Speedup|
|-|-|-|-|
|macos debug asan|126.90s|106.27s|1.19x|
|macos debug noasan|60.62s|50.85s|1.19x|
|linux debug asan|292.77s|241.45s|1.21x|
|linux debug noasan|146.58s|130.94s|1.12x|
|linux debug use_llvm=false|n/a|78.27s|1.87x|
|windows debug asan|177.13s|142.55s|1.24x|
Runtime performance:
- next build memory usage may have gone up by 5%. Otherwise seems the
same. Some code with writers may have gotten slower, especially one
instance of a counting writer and a few instances of unbuffered writers
that now have vtable overhead.
- File size reduced by 800kb (from 100.2mb to 99.4mb)
Improvements:
- `@export` hack is no longer needed for watch
- native x86_64 backend for linux builds faster. to use it, set use_llvm
false and no_link_obj false. also set `ASAN_OPTIONS=detect_leaks=0`
otherwise it will spam the output with tens of thousands of lines of
debug info errors. may need to use the zig lldb fork for debugging.
- zig test-obj, which we will be able to use for zig unit tests
Still an issue:
- false 'dependency loop' errors remain in watch mode
- watch mode crashes observed
Follow-up:
- [ ] search `comptime Writer: type` and `comptime W: type` and remove
- [ ] remove format_mode in our zig fork
- [ ] remove deprecated.zig autoFormatLabelFallback
- [ ] remove deprecated.zig autoFormatLabel
- [ ] remove deprecated.BufferedWriter and BufferedReader
- [ ] remove override_no_export_cpp_apis as it is no longer needed
- [ ] css Parser(W) -> Parser, and remove all the comptime writer: type
params
- [ ] remove deprecated writer fully
Files that add lines:
```
649 src/deprecated.zig
167 scripts/pack-codegen-for-zig-team.ts
54 scripts/cleartrace-impl.js
46 scripts/cleartrace.ts
43 src/windows.zig
18 src/fs.zig
17 src/bun.js/ConsoleObject.zig
16 src/output.zig
12 src/bun.js/test/debug.zig
12 src/bun.js/node/node_fs.zig
8 src/env_loader.zig
7 src/css/printer.zig
7 src/cli/init_command.zig
7 src/bun.js/node.zig
6 src/string/escapeRegExp.zig
6 src/install/PnpmMatcher.zig
5 src/bun.js/webcore/Blob.zig
4 src/crash_handler.zig
4 src/bun.zig
3 src/install/lockfile/bun.lock.zig
3 src/cli/update_interactive_command.zig
3 src/cli/pack_command.zig
3 build.zig
2 src/Progress.zig
2 src/install/lockfile/lockfile_json_stringify_for_debugging.zig
2 src/css/small_list.zig
2 src/bun.js/webcore/prompt.zig
1 test/internal/ban-words.test.ts
1 test/internal/ban-limits.json
1 src/watcher/WatcherTrace.zig
1 src/transpiler.zig
1 src/shell/builtin/cp.zig
1 src/js_printer.zig
1 src/io/PipeReader.zig
1 src/install/bin.zig
1 src/css/selectors/selector.zig
1 src/cli/run_command.zig
1 src/bun.js/RuntimeTranspilerStore.zig
1 src/bun.js/bindings/JSRef.zig
1 src/bake/DevServer.zig
```
Files that remove lines:
```
-1 src/test/recover.zig
-1 src/sql/postgres/SocketMonitor.zig
-1 src/sql/mysql/MySQLRequestQueue.zig
-1 src/sourcemap/CodeCoverage.zig
-1 src/css/values/color_js.zig
-1 src/compile_target.zig
-1 src/bundler/linker_context/convertStmtsForChunk.zig
-1 src/bundler/bundle_v2.zig
-1 src/bun.js/webcore/blob/read_file.zig
-1 src/ast/base.zig
-2 src/sql/postgres/protocol/ArrayList.zig
-2 src/shell/builtin/mkdir.zig
-2 src/install/PackageManager/patchPackage.zig
-2 src/install/PackageManager/PackageManagerDirectories.zig
-2 src/fmt.zig
-2 src/css/declaration.zig
-2 src/css/css_parser.zig
-2 src/collections/baby_list.zig
-2 src/bun.js/bindings/ZigStackFrame.zig
-2 src/ast/E.zig
-3 src/StandaloneModuleGraph.zig
-3 src/deps/picohttp.zig
-3 src/deps/libuv.zig
-3 src/btjs.zig
-4 src/threading/Futex.zig
-4 src/shell/builtin/touch.zig
-4 src/meta.zig
-4 src/install/lockfile.zig
-4 src/css/selectors/parser.zig
-5 src/shell/interpreter.zig
-5 src/css/error.zig
-5 src/bun.js/web_worker.zig
-5 src/bun.js.zig
-6 src/cli/test_command.zig
-6 src/bun.js/VirtualMachine.zig
-6 src/bun.js/uuid.zig
-6 src/bun.js/bindings/JSValue.zig
-9 src/bun.js/test/pretty_format.zig
-9 src/bun.js/api/BunObject.zig
-14 src/install/install_binding.zig
-14 src/fd.zig
-14 src/bun.js/node/path.zig
-14 scripts/pack-codegen-for-zig-team.sh
-17 src/bun.js/test/diff_format.zig
```
`git diff --numstat origin/main...HEAD | awk '{ print ($1-$2)"\t"$3 }' |
sort -rn`
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Meghan Denny <meghan@bun.com>
Co-authored-by: tayor.fish <contact@taylor.fish>
576 lines
29 KiB
Zig
576 lines
29 KiB
Zig
pub fn ParseProperty(
|
|
comptime parser_feature__typescript: bool,
|
|
comptime parser_feature__jsx: JSXTransformType,
|
|
comptime parser_feature__scan_only: bool,
|
|
) type {
|
|
return struct {
|
|
const P = js_parser.NewParser_(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only);
|
|
const is_typescript_enabled = P.is_typescript_enabled;
|
|
|
|
fn parseMethodExpression(p: *P, kind: Property.Kind, opts: *PropertyOpts, is_computed: bool, key: *Expr, key_range: logger.Range) anyerror!?G.Property {
|
|
if (p.lexer.token == .t_open_paren and kind != .get and kind != .set) {
|
|
// markSyntaxFeature object extensions
|
|
}
|
|
|
|
const loc = p.lexer.loc();
|
|
const scope_index = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
|
|
var is_constructor = false;
|
|
|
|
// Forbid the names "constructor" and "prototype" in some cases
|
|
if (opts.is_class and !is_computed) {
|
|
switch (key.data) {
|
|
.e_string => |str| {
|
|
if (!opts.is_static and str.eqlComptime("constructor")) {
|
|
if (kind == .get) {
|
|
p.log.addRangeError(p.source, key_range, "Class constructor cannot be a getter") catch unreachable;
|
|
} else if (kind == .set) {
|
|
p.log.addRangeError(p.source, key_range, "Class constructor cannot be a setter") catch unreachable;
|
|
} else if (opts.is_async) {
|
|
p.log.addRangeError(p.source, key_range, "Class constructor cannot be an async function") catch unreachable;
|
|
} else if (opts.is_generator) {
|
|
p.log.addRangeError(p.source, key_range, "Class constructor cannot be a generator function") catch unreachable;
|
|
} else {
|
|
is_constructor = true;
|
|
}
|
|
} else if (opts.is_static and str.eqlComptime("prototype")) {
|
|
p.log.addRangeError(p.source, key_range, "Invalid static method name \"prototype\"") catch unreachable;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
var func = try p.parseFn(null, FnOrArrowDataParse{
|
|
.async_range = opts.async_range,
|
|
.needs_async_loc = key.loc,
|
|
.has_async_range = !opts.async_range.isEmpty(),
|
|
.allow_await = if (opts.is_async) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident,
|
|
.allow_yield = if (opts.is_generator) AwaitOrYield.allow_expr else AwaitOrYield.allow_ident,
|
|
.allow_super_call = opts.class_has_extends and is_constructor,
|
|
.allow_super_property = true,
|
|
.allow_ts_decorators = opts.allow_ts_decorators,
|
|
.is_constructor = is_constructor,
|
|
.has_decorators = opts.ts_decorators.len > 0 or (opts.has_class_decorators and is_constructor),
|
|
|
|
// Only allow omitting the body if we're parsing TypeScript class
|
|
.allow_missing_body_for_type_script = is_typescript_enabled and opts.is_class,
|
|
});
|
|
|
|
opts.has_argument_decorators = opts.has_argument_decorators or p.fn_or_arrow_data_parse.has_argument_decorators;
|
|
p.fn_or_arrow_data_parse.has_argument_decorators = false;
|
|
|
|
// "class Foo { foo(): void; foo(): void {} }"
|
|
if (func.flags.contains(.is_forward_declaration)) {
|
|
// Skip this property entirely
|
|
p.popAndDiscardScope(scope_index);
|
|
return null;
|
|
}
|
|
|
|
p.popScope();
|
|
func.flags.insert(.is_unique_formal_parameters);
|
|
const value = p.newExpr(E.Function{ .func = func }, loc);
|
|
|
|
// Enforce argument rules for accessors
|
|
switch (kind) {
|
|
.get => {
|
|
if (func.args.len > 0) {
|
|
const r = js_lexer.rangeOfIdentifier(p.source, func.args[0].binding.loc);
|
|
p.log.addRangeErrorFmt(p.source, r, p.allocator, "Getter {s} must have zero arguments", .{p.keyNameForError(key)}) catch unreachable;
|
|
}
|
|
},
|
|
.set => {
|
|
if (func.args.len != 1) {
|
|
var r = js_lexer.rangeOfIdentifier(p.source, if (func.args.len > 0) func.args[0].binding.loc else loc);
|
|
if (func.args.len > 1) {
|
|
r = js_lexer.rangeOfIdentifier(p.source, func.args[1].binding.loc);
|
|
}
|
|
p.log.addRangeErrorFmt(p.source, r, p.allocator, "Setter {s} must have exactly 1 argument (there are {d})", .{ p.keyNameForError(key), func.args.len }) catch unreachable;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
// Special-case private identifiers
|
|
switch (key.data) {
|
|
.e_private_identifier => |*private| {
|
|
const declare: Symbol.Kind = switch (kind) {
|
|
.get => if (opts.is_static)
|
|
.private_static_get
|
|
else
|
|
.private_get,
|
|
|
|
.set => if (opts.is_static)
|
|
.private_static_set
|
|
else
|
|
.private_set,
|
|
else => if (opts.is_static)
|
|
.private_static_method
|
|
else
|
|
.private_method,
|
|
};
|
|
|
|
const name = p.loadNameFromRef(private.ref);
|
|
if (strings.eqlComptime(name, "#constructor")) {
|
|
p.log.addRangeError(p.source, key_range, "Invalid method name \"#constructor\"") catch unreachable;
|
|
}
|
|
private.ref = p.declareSymbol(declare, key.loc, name) catch unreachable;
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
return G.Property{
|
|
.ts_decorators = try ExprNodeList.fromSlice(p.allocator, opts.ts_decorators),
|
|
.kind = kind,
|
|
.flags = Flags.Property.init(.{
|
|
.is_computed = is_computed,
|
|
.is_method = true,
|
|
.is_static = opts.is_static,
|
|
}),
|
|
.key = key.*,
|
|
.value = value,
|
|
.ts_metadata = .m_function,
|
|
};
|
|
}
|
|
|
|
pub fn parseProperty(p: *P, kind_: Property.Kind, opts: *PropertyOpts, errors_: ?*DeferredErrors) anyerror!?G.Property {
|
|
var kind = kind_;
|
|
var errors = errors_;
|
|
// This while loop exists to conserve stack space by reducing (but not completely eliminating) recursion.
|
|
restart: while (true) {
|
|
var key: Expr = Expr{ .loc = logger.Loc.Empty, .data = .{ .e_missing = E.Missing{} } };
|
|
const key_range = p.lexer.range();
|
|
var is_computed = false;
|
|
|
|
switch (p.lexer.token) {
|
|
.t_numeric_literal => {
|
|
key = p.newExpr(E.Number{
|
|
.value = p.lexer.number,
|
|
}, p.lexer.loc());
|
|
// p.checkForLegacyOctalLiteral()
|
|
try p.lexer.next();
|
|
},
|
|
.t_string_literal => {
|
|
key = try p.parseStringLiteral();
|
|
},
|
|
.t_big_integer_literal => {
|
|
key = p.newExpr(E.BigInt{ .value = p.lexer.identifier }, p.lexer.loc());
|
|
// markSyntaxFeature
|
|
try p.lexer.next();
|
|
},
|
|
.t_private_identifier => {
|
|
if (!opts.is_class or opts.ts_decorators.len > 0) {
|
|
try p.lexer.expected(.t_identifier);
|
|
}
|
|
|
|
key = p.newExpr(E.PrivateIdentifier{ .ref = p.storeNameInRef(p.lexer.identifier) catch unreachable }, p.lexer.loc());
|
|
try p.lexer.next();
|
|
},
|
|
.t_open_bracket => {
|
|
is_computed = true;
|
|
// p.markSyntaxFeature(compat.objectExtensions, p.lexer.range())
|
|
try p.lexer.next();
|
|
const wasIdentifier = p.lexer.token == .t_identifier;
|
|
const expr = try p.parseExpr(.comma);
|
|
|
|
if (comptime is_typescript_enabled) {
|
|
|
|
// Handle index signatures
|
|
if (p.lexer.token == .t_colon and wasIdentifier and opts.is_class) {
|
|
switch (expr.data) {
|
|
.e_identifier => {
|
|
try p.lexer.next();
|
|
try p.skipTypeScriptType(.lowest);
|
|
try p.lexer.expect(.t_close_bracket);
|
|
try p.lexer.expect(.t_colon);
|
|
try p.skipTypeScriptType(.lowest);
|
|
try p.lexer.expectOrInsertSemicolon();
|
|
|
|
// Skip this property entirely
|
|
return null;
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
try p.lexer.expect(.t_close_bracket);
|
|
key = expr;
|
|
},
|
|
.t_asterisk => {
|
|
if (kind != .normal or opts.is_generator) {
|
|
try p.lexer.unexpected();
|
|
return error.SyntaxError;
|
|
}
|
|
|
|
try p.lexer.next();
|
|
opts.is_generator = true;
|
|
kind = .normal;
|
|
continue :restart;
|
|
},
|
|
|
|
else => {
|
|
const name = p.lexer.identifier;
|
|
const raw = p.lexer.raw();
|
|
const name_range = p.lexer.range();
|
|
|
|
if (!p.lexer.isIdentifierOrKeyword()) {
|
|
try p.lexer.expect(.t_identifier);
|
|
}
|
|
|
|
try p.lexer.next();
|
|
|
|
// Support contextual keywords
|
|
if (kind == .normal and !opts.is_generator) {
|
|
// Does the following token look like a key?
|
|
const couldBeModifierKeyword = p.lexer.isIdentifierOrKeyword() or switch (p.lexer.token) {
|
|
.t_open_bracket, .t_numeric_literal, .t_string_literal, .t_asterisk, .t_private_identifier => true,
|
|
else => false,
|
|
};
|
|
|
|
// If so, check for a modifier keyword
|
|
if (couldBeModifierKeyword) {
|
|
// TODO: micro-optimization, use a smaller list for non-typescript files.
|
|
if (js_lexer.PropertyModifierKeyword.List.get(name)) |keyword| {
|
|
switch (keyword) {
|
|
.p_get => {
|
|
if (!opts.is_async and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_get) {
|
|
kind = .get;
|
|
errors = null;
|
|
continue :restart;
|
|
}
|
|
},
|
|
|
|
.p_set => {
|
|
if (!opts.is_async and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_set) {
|
|
// p.markSyntaxFeature(ObjectAccessors, name_range)
|
|
kind = .set;
|
|
errors = null;
|
|
continue :restart;
|
|
}
|
|
},
|
|
.p_async => {
|
|
if (!opts.is_async and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_async and !p.lexer.has_newline_before) {
|
|
opts.is_async = true;
|
|
opts.async_range = name_range;
|
|
|
|
// p.markSyntaxFeature(ObjectAccessors, name_range)
|
|
|
|
errors = null;
|
|
continue :restart;
|
|
}
|
|
},
|
|
.p_static => {
|
|
if (!opts.is_static and !opts.is_async and opts.is_class and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_get) == .p_static) {
|
|
opts.is_static = true;
|
|
kind = .normal;
|
|
errors = null;
|
|
continue :restart;
|
|
}
|
|
},
|
|
.p_declare => {
|
|
// skip declare keyword entirely
|
|
// https://github.com/oven-sh/bun/issues/1907
|
|
if (opts.is_class and is_typescript_enabled and strings.eqlComptime(raw, "declare")) {
|
|
const scope_index = p.scopes_in_order.items.len;
|
|
if (try p.parseProperty(kind, opts, null)) |_prop| {
|
|
var prop = _prop;
|
|
if (prop.kind == .normal and prop.value == null and opts.ts_decorators.len > 0) {
|
|
prop.kind = .declare;
|
|
return prop;
|
|
}
|
|
}
|
|
|
|
p.discardScopesUpTo(scope_index);
|
|
return null;
|
|
}
|
|
},
|
|
.p_abstract => {
|
|
if (opts.is_class and is_typescript_enabled and !opts.is_ts_abstract and strings.eqlComptime(raw, "abstract")) {
|
|
opts.is_ts_abstract = true;
|
|
const scope_index = p.scopes_in_order.items.len;
|
|
if (try p.parseProperty(kind, opts, null)) |*prop| {
|
|
if (prop.kind == .normal and prop.value == null and opts.ts_decorators.len > 0) {
|
|
var prop_ = prop.*;
|
|
prop_.kind = .abstract;
|
|
return prop_;
|
|
}
|
|
}
|
|
p.discardScopesUpTo(scope_index);
|
|
return null;
|
|
}
|
|
},
|
|
.p_private, .p_protected, .p_public, .p_readonly, .p_override => {
|
|
// Skip over TypeScript keywords
|
|
if (opts.is_class and is_typescript_enabled and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == keyword) {
|
|
errors = null;
|
|
continue :restart;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
} else if (p.lexer.token == .t_open_brace and strings.eqlComptime(name, "static")) {
|
|
const loc = p.lexer.loc();
|
|
try p.lexer.next();
|
|
|
|
const old_fn_or_arrow_data_parse = p.fn_or_arrow_data_parse;
|
|
p.fn_or_arrow_data_parse = .{
|
|
.is_return_disallowed = true,
|
|
.allow_super_property = true,
|
|
.allow_await = .forbid_all,
|
|
};
|
|
|
|
_ = try p.pushScopeForParsePass(.class_static_init, loc);
|
|
var _parse_opts = ParseStatementOptions{};
|
|
const stmts = try p.parseStmtsUpTo(.t_close_brace, &_parse_opts);
|
|
|
|
p.popScope();
|
|
|
|
p.fn_or_arrow_data_parse = old_fn_or_arrow_data_parse;
|
|
try p.lexer.expect(.t_close_brace);
|
|
|
|
const block = p.allocator.create(
|
|
G.ClassStaticBlock,
|
|
) catch unreachable;
|
|
|
|
block.* = G.ClassStaticBlock{
|
|
.stmts = js_ast.BabyList(Stmt).fromOwnedSlice(stmts),
|
|
.loc = loc,
|
|
};
|
|
|
|
return G.Property{
|
|
.kind = .class_static_block,
|
|
.class_static_block = block,
|
|
};
|
|
}
|
|
}
|
|
|
|
// Handle invalid identifiers in property names
|
|
// https://github.com/oven-sh/bun/issues/12039
|
|
if (p.lexer.token == .t_syntax_error) {
|
|
bun.handleOom(p.log.addRangeErrorFmt(p.source, name_range, p.allocator, "Unexpected {f}", .{bun.fmt.quote(name)}));
|
|
return error.SyntaxError;
|
|
}
|
|
|
|
key = p.newExpr(E.String{ .data = name }, name_range.loc);
|
|
|
|
// Parse a shorthand property
|
|
const isShorthandProperty = !opts.is_class and
|
|
kind == .normal and
|
|
p.lexer.token != .t_colon and
|
|
p.lexer.token != .t_open_paren and
|
|
p.lexer.token != .t_less_than and
|
|
!opts.is_generator and
|
|
!opts.is_async and
|
|
!js_lexer.Keywords.has(name);
|
|
|
|
if (isShorthandProperty) {
|
|
if ((p.fn_or_arrow_data_parse.allow_await != .allow_ident and
|
|
strings.eqlComptime(name, "await")) or
|
|
(p.fn_or_arrow_data_parse.allow_yield != .allow_ident and
|
|
strings.eqlComptime(name, "yield")))
|
|
{
|
|
if (strings.eqlComptime(name, "await")) {
|
|
p.log.addRangeError(p.source, name_range, "Cannot use \"await\" here") catch unreachable;
|
|
} else {
|
|
p.log.addRangeError(p.source, name_range, "Cannot use \"yield\" here") catch unreachable;
|
|
}
|
|
}
|
|
|
|
const ref = p.storeNameInRef(name) catch unreachable;
|
|
const value = p.newExpr(E.Identifier{ .ref = ref }, key.loc);
|
|
|
|
// Destructuring patterns have an optional default value
|
|
var initializer: ?Expr = null;
|
|
if (errors != null and p.lexer.token == .t_equals) {
|
|
errors.?.invalid_expr_default_value = p.lexer.range();
|
|
try p.lexer.next();
|
|
initializer = try p.parseExpr(.comma);
|
|
}
|
|
|
|
return G.Property{
|
|
.kind = kind,
|
|
.key = key,
|
|
.value = value,
|
|
.initializer = initializer,
|
|
.flags = Flags.Property.init(.{
|
|
.was_shorthand = true,
|
|
}),
|
|
};
|
|
}
|
|
},
|
|
}
|
|
|
|
var has_type_parameters = false;
|
|
var has_definite_assignment_assertion_operator = false;
|
|
|
|
if (comptime is_typescript_enabled) {
|
|
if (opts.is_class) {
|
|
if (p.lexer.token == .t_question) {
|
|
// "class X { foo?: number }"
|
|
// "class X { foo!: number }"
|
|
try p.lexer.next();
|
|
} else if (p.lexer.token == .t_exclamation and
|
|
!p.lexer.has_newline_before and
|
|
kind == .normal and
|
|
!opts.is_async and
|
|
!opts.is_generator)
|
|
{
|
|
// "class X { foo!: number }"
|
|
try p.lexer.next();
|
|
has_definite_assignment_assertion_operator = true;
|
|
}
|
|
}
|
|
|
|
// "class X { foo?<T>(): T }"
|
|
// "const x = { foo<T>(): T {} }"
|
|
if (!has_definite_assignment_assertion_operator) {
|
|
has_type_parameters = try p.skipTypeScriptTypeParameters(.{ .allow_const_modifier = true }) != .did_not_skip_anything;
|
|
}
|
|
}
|
|
|
|
// Parse a class field with an optional initial value
|
|
if (opts.is_class and
|
|
kind == .normal and !opts.is_async and
|
|
!opts.is_generator and
|
|
p.lexer.token != .t_open_paren and
|
|
!has_type_parameters and
|
|
(p.lexer.token != .t_open_paren or has_definite_assignment_assertion_operator))
|
|
{
|
|
var initializer: ?Expr = null;
|
|
var ts_metadata = TypeScript.Metadata.default;
|
|
|
|
// Forbid the names "constructor" and "prototype" in some cases
|
|
if (!is_computed) {
|
|
switch (key.data) {
|
|
.e_string => |str| {
|
|
if (str.eqlComptime("constructor") or (opts.is_static and str.eqlComptime("prototype"))) {
|
|
// TODO: fmt error message to include string value.
|
|
p.log.addRangeError(p.source, key_range, "Invalid field name") catch unreachable;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
if (comptime is_typescript_enabled) {
|
|
// Skip over types
|
|
if (p.lexer.token == .t_colon) {
|
|
try p.lexer.next();
|
|
if (p.options.features.emit_decorator_metadata and opts.is_class and opts.ts_decorators.len > 0) {
|
|
ts_metadata = try p.skipTypeScriptTypeWithMetadata(.lowest);
|
|
} else {
|
|
try p.skipTypeScriptType(.lowest);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p.lexer.token == .t_equals) {
|
|
if (comptime is_typescript_enabled) {
|
|
if (!opts.declare_range.isEmpty()) {
|
|
try p.log.addRangeError(p.source, p.lexer.range(), "Class fields that use \"declare\" cannot be initialized");
|
|
}
|
|
}
|
|
|
|
try p.lexer.next();
|
|
|
|
// "this" and "super" property access is allowed in field initializers
|
|
const old_is_this_disallowed = p.fn_or_arrow_data_parse.is_this_disallowed;
|
|
const old_allow_super_property = p.fn_or_arrow_data_parse.allow_super_property;
|
|
p.fn_or_arrow_data_parse.is_this_disallowed = false;
|
|
p.fn_or_arrow_data_parse.allow_super_property = true;
|
|
|
|
initializer = try p.parseExpr(.comma);
|
|
|
|
p.fn_or_arrow_data_parse.is_this_disallowed = old_is_this_disallowed;
|
|
p.fn_or_arrow_data_parse.allow_super_property = old_allow_super_property;
|
|
}
|
|
|
|
// Special-case private identifiers
|
|
switch (key.data) {
|
|
.e_private_identifier => |*private| {
|
|
const name = p.loadNameFromRef(private.ref);
|
|
if (strings.eqlComptime(name, "#constructor")) {
|
|
p.log.addRangeError(p.source, key_range, "Invalid field name \"#constructor\"") catch unreachable;
|
|
}
|
|
|
|
const declare: js_ast.Symbol.Kind = if (opts.is_static)
|
|
.private_static_field
|
|
else
|
|
.private_field;
|
|
|
|
private.ref = p.declareSymbol(declare, key.loc, name) catch unreachable;
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
try p.lexer.expectOrInsertSemicolon();
|
|
|
|
return G.Property{
|
|
.ts_decorators = try ExprNodeList.fromSlice(p.allocator, opts.ts_decorators),
|
|
.kind = kind,
|
|
.flags = Flags.Property.init(.{
|
|
.is_computed = is_computed,
|
|
.is_static = opts.is_static,
|
|
}),
|
|
.key = key,
|
|
.initializer = initializer,
|
|
.ts_metadata = ts_metadata,
|
|
};
|
|
}
|
|
|
|
// Parse a method expression
|
|
if (p.lexer.token == .t_open_paren or kind != .normal or opts.is_class or opts.is_async or opts.is_generator) {
|
|
return parseMethodExpression(p, kind, opts, is_computed, &key, key_range);
|
|
}
|
|
|
|
// Parse an object key/value pair
|
|
try p.lexer.expect(.t_colon);
|
|
var property: G.Property = .{
|
|
.kind = kind,
|
|
.flags = Flags.Property.init(.{
|
|
.is_computed = is_computed,
|
|
}),
|
|
.key = key,
|
|
.value = Expr{ .data = .e_missing, .loc = .{} },
|
|
};
|
|
|
|
try p.parseExprOrBindings(.comma, errors, &property.value.?);
|
|
return property;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const string = []const u8;
|
|
|
|
const bun = @import("bun");
|
|
const logger = bun.logger;
|
|
const strings = bun.strings;
|
|
|
|
const js_ast = bun.ast;
|
|
const E = js_ast.E;
|
|
const Expr = js_ast.Expr;
|
|
const ExprNodeList = js_ast.ExprNodeList;
|
|
const Flags = js_ast.Flags;
|
|
const Stmt = js_ast.Stmt;
|
|
const Symbol = js_ast.Symbol;
|
|
|
|
const G = js_ast.G;
|
|
const Property = G.Property;
|
|
|
|
const js_lexer = bun.js_lexer;
|
|
const T = js_lexer.T;
|
|
|
|
const js_parser = bun.js_parser;
|
|
const AwaitOrYield = js_parser.AwaitOrYield;
|
|
const DeferredErrors = js_parser.DeferredErrors;
|
|
const FnOrArrowDataParse = js_parser.FnOrArrowDataParse;
|
|
const JSXTransformType = js_parser.JSXTransformType;
|
|
const ParseStatementOptions = js_parser.ParseStatementOptions;
|
|
const PropertyOpts = js_parser.PropertyOpts;
|
|
const TypeScript = js_parser.TypeScript;
|
|
const options = js_parser.options;
|
|
|
|
const std = @import("std");
|
|
const List = std.ArrayListUnmanaged;
|