@@ -4,13 +4,15 @@ const Fs = @import("../../fs.zig");
4
4
const Resolver = @import ("../../resolver/resolver.zig" );
5
5
const ast = @import ("../../import_record.zig" );
6
6
const NodeModuleBundle = @import ("../../node_module_bundle.zig" ).NodeModuleBundle ;
7
+ const MacroEntryPoint = @import ("../../bundler.zig" ).MacroEntryPoint ;
7
8
const logger = @import ("../../logger.zig" );
8
9
const Api = @import ("../../api/schema.zig" ).Api ;
9
10
const options = @import ("../../options.zig" );
10
11
const Bundler = @import ("../../bundler.zig" ).Bundler ;
11
12
const ServerEntryPoint = @import ("../../bundler.zig" ).ServerEntryPoint ;
12
13
const js_printer = @import ("../../js_printer.zig" );
13
14
const js_parser = @import ("../../js_parser.zig" );
15
+ const js_ast = @import ("../../js_ast.zig" );
14
16
const hash_map = @import ("../../hash_map.zig" );
15
17
const http = @import ("../../http.zig" );
16
18
const ImportKind = ast .ImportKind ;
@@ -87,6 +89,41 @@ pub const Bun = struct {
87
89
return css_imports_list_strings [0.. tail ];
88
90
}
89
91
92
+ pub fn registerMacro (
93
+ this : void ,
94
+ ctx : js.JSContextRef ,
95
+ function : js.JSObjectRef ,
96
+ thisObject : js.JSObjectRef ,
97
+ arguments : []const js.JSValueRef ,
98
+ exception : js.ExceptionRef ,
99
+ ) js.JSValueRef {
100
+ if (arguments .len != 2 or ! js .JSValueIsNumber (ctx , arguments [0 ])) {
101
+ JSError (getAllocator (ctx ), "Internal error registering macros: invalid args" , .{}, ctx , exception );
102
+ return js .JSValueMakeUndefined (ctx );
103
+ }
104
+ // TODO: make this faster
105
+ const id = @truncate (i32 , @floatToInt (i64 , js .JSValueToNumber (ctx , arguments [0 ], exception )));
106
+ if (id == -1 or id == 0 ) {
107
+ JSError (getAllocator (ctx ), "Internal error registering macros: invalid id" , .{}, ctx , exception );
108
+ return js .JSValueMakeUndefined (ctx );
109
+ }
110
+
111
+ if (! js .JSValueIsObject (ctx , arguments [1 ]) or ! js .JSObjectIsFunction (ctx , arguments [1 ])) {
112
+ JSError (getAllocator (ctx ), "Macro must be a function. Received: {s}" , .{@tagName (js .JSValueGetType (ctx , arguments [1 ]))}, ctx , exception );
113
+ return js .JSValueMakeUndefined (ctx );
114
+ }
115
+
116
+ var get_or_put_result = VirtualMachine .vm .macros .getOrPut (id ) catch unreachable ;
117
+ if (get_or_put_result .found_existing ) {
118
+ js .JSValueUnprotect (ctx , get_or_put_result .value_ptr .* );
119
+ }
120
+
121
+ js .JSValueProtect (ctx , arguments [1 ]);
122
+ get_or_put_result .value_ptr .* = arguments [1 ];
123
+
124
+ return js .JSValueMakeUndefined (ctx );
125
+ }
126
+
90
127
pub fn getCWD (
91
128
this : void ,
92
129
ctx : js.JSContextRef ,
@@ -403,6 +440,13 @@ pub const Bun = struct {
403
440
.@"return" = "string" ,
404
441
},
405
442
},
443
+ .registerMacro = .{
444
+ .rfn = Bun .registerMacro ,
445
+ .ts = d.ts {
446
+ .name = "registerMacro" ,
447
+ .@"return" = "undefined" ,
448
+ },
449
+ },
406
450
},
407
451
.{
408
452
.main = .{
@@ -462,8 +506,25 @@ pub const VirtualMachine = struct {
462
506
transpiled_count : usize = 0 ,
463
507
resolved_count : usize = 0 ,
464
508
had_errors : bool = false ,
465
- pub var vm_loaded = false ;
466
- pub var vm : * VirtualMachine = undefined ;
509
+
510
+ macros : MacroMap ,
511
+ macro_entry_points : std .AutoArrayHashMap (i32 , * MacroEntryPoint ),
512
+ macro_mode : bool = false ,
513
+
514
+ pub const MacroMap = std .AutoArrayHashMap (i32 , js .JSObjectRef );
515
+
516
+ pub threadlocal var vm_loaded = false ;
517
+ pub threadlocal var vm : * VirtualMachine = undefined ;
518
+
519
+ pub fn enableMacroMode (this : * VirtualMachine ) void {
520
+ this .bundler .options .platform = .bun_macro ;
521
+ this .macro_mode = true ;
522
+ }
523
+
524
+ pub fn disableMacroMode (this : * VirtualMachine ) void {
525
+ this .bundler .options .platform = .bun ;
526
+ this .macro_mode = false ;
527
+ }
467
528
468
529
pub fn init (
469
530
allocator : * std.mem.Allocator ,
@@ -502,6 +563,9 @@ pub const VirtualMachine = struct {
502
563
.log = log ,
503
564
.flush_list = std .ArrayList (string ).init (allocator ),
504
565
.blobs = try Blob .Group .init (allocator ),
566
+
567
+ .macros = MacroMap .init (allocator ),
568
+ .macro_entry_points = @TypeOf (VirtualMachine .vm .macro_entry_points ).init (allocator ),
505
569
};
506
570
507
571
VirtualMachine .vm .bundler .configureLinker ();
@@ -771,10 +835,16 @@ pub const VirtualMachine = struct {
771
835
ret .result = null ;
772
836
ret .path = vm .entry_point .source .path .text ;
773
837
return ;
838
+ } else if (specifier .len > js_ast .Macro .namespaceWithColon .len and strings .eqlComptimeIgnoreLen (specifier [0.. js_ast .Macro .namespaceWithColon .len ], js_ast .Macro .namespaceWithColon )) {
839
+ ret .result = null ;
840
+ ret .path = specifier ;
841
+ return ;
774
842
}
775
843
844
+ const is_special_source = strings .eqlComptime (source , main_file_name ) or js_ast .Macro .isMacroPath (source );
845
+
776
846
const result = try vm .bundler .resolver .resolve (
777
- if (! strings . eqlComptime ( source , main_file_name ) ) Fs .PathName .init (source ).dirWithTrailingSlash () else VirtualMachine .vm .bundler .fs .top_level_dir ,
847
+ if (! is_special_source ) Fs .PathName .init (source ).dirWithTrailingSlash () else VirtualMachine .vm .bundler .fs .top_level_dir ,
778
848
specifier ,
779
849
.stmt ,
780
850
);
@@ -1050,6 +1120,46 @@ pub const VirtualMachine = struct {
1050
1120
return promise ;
1051
1121
}
1052
1122
1123
+ pub fn loadMacroEntryPoint (this : * VirtualMachine , entry_path : string , function_name : string , specifier : string , hash : i32 ) ! * JSInternalPromise {
1124
+ var entry_point_entry = try this .macro_entry_points .getOrPut (hash );
1125
+
1126
+ if (! entry_point_entry .found_existing ) {
1127
+ var macro_entry_pointer : * MacroEntryPoint = this .allocator .create (MacroEntryPoint ) catch unreachable ;
1128
+ entry_point_entry .value_ptr .* = macro_entry_pointer ;
1129
+ try macro_entry_pointer .generate (& this .bundler , Fs .PathName .init (entry_path ), function_name , hash , specifier );
1130
+ }
1131
+ var entry_point = entry_point_entry .value_ptr .* ;
1132
+
1133
+ var promise : * JSInternalPromise = undefined ;
1134
+ // We first import the node_modules bundle. This prevents any potential TDZ issues.
1135
+ // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
1136
+ if (this .node_modules != null ) {
1137
+ promise = JSModuleLoader .loadAndEvaluateModule (this .global , ZigString .init (std .mem .span (bun_file_import_path )));
1138
+
1139
+ this .global .vm ().drainMicrotasks ();
1140
+
1141
+ while (promise .status (this .global .vm ()) == JSPromise .Status .Pending ) {
1142
+ this .global .vm ().drainMicrotasks ();
1143
+ }
1144
+
1145
+ if (promise .status (this .global .vm ()) == JSPromise .Status .Rejected ) {
1146
+ return promise ;
1147
+ }
1148
+
1149
+ _ = promise .result (this .global .vm ());
1150
+ }
1151
+
1152
+ promise = JSModuleLoader .loadAndEvaluateModule (this .global , ZigString .init (entry_point .source .path .text ));
1153
+
1154
+ this .global .vm ().drainMicrotasks ();
1155
+
1156
+ while (promise .status (this .global .vm ()) == JSPromise .Status .Pending ) {
1157
+ this .global .vm ().drainMicrotasks ();
1158
+ }
1159
+
1160
+ return promise ;
1161
+ }
1162
+
1053
1163
// When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException.
1054
1164
// This is for:
1055
1165
// - BuildError
0 commit comments