Files
bun.sh/src/ast/TS.zig
taylor.fish 07cd45deae Refactor Zig imports and file structure (part 1) (#21270)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-22 17:51:38 -07:00

140 lines
4.4 KiB
Zig

/// This is for TypeScript "enum" and "namespace" blocks. Each block can
/// potentially be instantiated multiple times. The exported members of each
/// block are merged into a single namespace while the non-exported code is
/// still scoped to just within that block:
///
/// let x = 1;
/// namespace Foo {
/// let x = 2;
/// export let y = 3;
/// }
/// namespace Foo {
/// console.log(x); // 1
/// console.log(y); // 3
/// }
///
/// Doing this also works inside an enum:
///
/// enum Foo {
/// A = 3,
/// B = A + 1,
/// }
/// enum Foo {
/// C = A + 2,
/// }
/// console.log(Foo.B) // 4
/// console.log(Foo.C) // 5
///
/// This is a form of identifier lookup that works differently than the
/// hierarchical scope-based identifier lookup in JavaScript. Lookup now needs
/// to search sibling scopes in addition to parent scopes. This is accomplished
/// by sharing the map of exported members between all matching sibling scopes.
pub const TSNamespaceScope = struct {
/// This is specific to this namespace block. It's the argument of the
/// immediately-invoked function expression that the namespace block is
/// compiled into:
///
/// var ns;
/// (function (ns2) {
/// ns2.x = 123;
/// })(ns || (ns = {}));
///
/// This variable is "ns2" in the above example. It's the symbol to use when
/// generating property accesses off of this namespace when it's in scope.
arg_ref: Ref,
/// This is shared between all sibling namespace blocks
exported_members: *TSNamespaceMemberMap,
/// This is a lazily-generated map of identifiers that actually represent
/// property accesses to this namespace's properties. For example:
///
/// namespace x {
/// export let y = 123
/// }
/// namespace x {
/// export let z = y
/// }
///
/// This should be compiled into the following code:
///
/// var x;
/// (function(x2) {
/// x2.y = 123;
/// })(x || (x = {}));
/// (function(x3) {
/// x3.z = x3.y;
/// })(x || (x = {}));
///
/// When we try to find the symbol "y", we instead return one of these lazily
/// generated proxy symbols that represent the property access "x3.y". This
/// map is unique per namespace block because "x3" is the argument symbol that
/// is specific to that particular namespace block.
property_accesses: bun.StringArrayHashMapUnmanaged(Ref) = .{},
/// Even though enums are like namespaces and both enums and namespaces allow
/// implicit references to properties of sibling scopes, they behave like
/// separate, er, namespaces. Implicit references only work namespace-to-
/// namespace and enum-to-enum. They do not work enum-to-namespace. And I'm
/// not sure what's supposed to happen for the namespace-to-enum case because
/// the compiler crashes: https://github.com/microsoft/TypeScript/issues/46891.
/// So basically these both work:
///
/// enum a { b = 1 }
/// enum a { c = b }
///
/// namespace x { export let y = 1 }
/// namespace x { export let z = y }
///
/// This doesn't work:
///
/// enum a { b = 1 }
/// namespace a { export let c = b }
///
/// And this crashes the TypeScript compiler:
///
/// namespace a { export let b = 1 }
/// enum a { c = b }
///
/// Therefore we only allow enum/enum and namespace/namespace interactions.
is_enum_scope: bool,
};
pub const TSNamespaceMemberMap = bun.StringArrayHashMapUnmanaged(TSNamespaceMember);
pub const TSNamespaceMember = struct {
loc: logger.Loc,
data: Data,
pub const Data = union(enum) {
/// "namespace ns { export let it }"
property,
/// "namespace ns { export namespace it {} }"
namespace: *TSNamespaceMemberMap,
/// "enum ns { it }"
enum_number: f64,
/// "enum ns { it = 'it' }"
enum_string: *E.String,
/// "enum ns { it = something() }"
enum_property: void,
pub fn isEnum(data: Data) bool {
return switch (data) {
inline else => |_, tag| comptime std.mem.startsWith(u8, @tagName(tag), "enum_"),
};
}
};
};
pub const Class = G.Class;
const std = @import("std");
const bun = @import("bun");
const logger = bun.logger;
const js_ast = bun.ast;
const E = js_ast.E;
const G = js_ast.G;
const Ref = js_ast.Ref;