diff options
-rw-r--r-- | examples/macros/example.js | 4 | ||||
-rw-r--r-- | examples/macros/mystery-box.tsx | 15 | ||||
-rw-r--r-- | src/feature_flags.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 4 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 11 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 19 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.h | 5 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.zig | 54 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 15 | ||||
-rw-r--r-- | src/js_ast.zig | 1982 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 78 | ||||
-rw-r--r-- | src/js_printer.zig | 6 | ||||
-rw-r--r-- | src/string_immutable.zig | 8 |
14 files changed, 1576 insertions, 629 deletions
diff --git a/examples/macros/example.js b/examples/macros/example.js new file mode 100644 index 000000000..d612c1fa4 --- /dev/null +++ b/examples/macros/example.js @@ -0,0 +1,4 @@ +// source code +import { mysteryBox } from "macro:./mystery-box"; + +export default "You roll! " + mysteryBox(123); diff --git a/examples/macros/mystery-box.tsx b/examples/macros/mystery-box.tsx new file mode 100644 index 000000000..9e72ee1c0 --- /dev/null +++ b/examples/macros/mystery-box.tsx @@ -0,0 +1,15 @@ +// macro code: +export function mysteryBox(node) { + const dice = Math.round(Math.random() * 100); + if (dice < 25) { + return <number value={5} />; + } else if (dice < 50) { + return <true />; + } else if (dice < 75) { + return <false />; + } else if (dice < 90) { + return <string value="a string" />; + } else { + return <string value={"a very rare string " + dice.toString(10)} />; + } +} diff --git a/src/feature_flags.zig b/src/feature_flags.zig index 9aa809879..2819c1cbd 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -69,3 +69,5 @@ pub const remote_inspector = false; pub const auto_import_buffer = false; pub const is_macro_enabled = env.isDebug; + +pub const force_macro = false; diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 36f00f59e..a5f2d336d 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -1539,7 +1539,7 @@ export fn MarkedArrayBuffer_deallocator(bytes_: *c_void, ctx_: *c_void) void { pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type { return JSPrivateDataPtr.from(js.JSObjectGetPrivate(obj)).as(Type); } -const JSExpr = @import("../../js_ast.zig").Macro.JSExpr; +const JSNode = @import("../../js_ast.zig").Macro.JSNode; pub const JSPrivateDataPtr = TaggedPointerUnion(.{ ResolveError, @@ -1550,7 +1550,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{ Headers, Body, Router, - JSExpr, + JSNode, }); pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type { diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 50e6978b5..d847a3121 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -10,6 +10,7 @@ #include <JavaScriptCore/Identifier.h> #include <JavaScriptCore/IteratorOperations.h> #include <JavaScriptCore/JSArray.h> +#include <JavaScriptCore/JSArrayInlines.h> #include <JavaScriptCore/JSCInlines.h> #include <JavaScriptCore/JSCallbackObject.h> #include <JavaScriptCore/JSClassRef.h> @@ -51,6 +52,12 @@ JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject *globalObject, JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), initialCapacity)); } +uint32_t JSC__JSValue__getLengthOfArray(JSC__JSValue value, JSC__JSGlobalObject *globalObject) { + JSC::JSValue jsValue = JSC::JSValue::decode(value); + JSC::JSObject *object = jsValue.toObject(globalObject); + return JSC::toLength(globalObject, object); +} + void JSC__JSObject__putRecord(JSC__JSObject *object, JSC__JSGlobalObject *global, ZigString *key, ZigString *values, size_t valuesLen) { auto scope = DECLARE_THROW_SCOPE(global->vm()); @@ -220,9 +227,9 @@ JSC__JSValue JSC__Exception__value(JSC__Exception *arg0) { // JSC__PropertyNameArray__next(JSC__PropertyNameArray* arg0, size_t arg1); // CPP_DECL void JSC__PropertyNameArray__release(JSC__PropertyNameArray* arg0); size_t JSC__JSObject__getArrayLength(JSC__JSObject *arg0) { return arg0->getArrayLength(); } -JSC__JSValue JSC__JSObject__getIndex(JSC__JSObject *arg0, JSC__JSGlobalObject *arg1, +JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue jsValue, JSC__JSGlobalObject *arg1, uint32_t arg3) { - return JSC::JSValue::encode(arg0->getIndex(arg1, arg3)); + return JSC::JSValue::encode(JSC::JSValue::decode(jsValue).toObject(arg1)->getIndex(arg1, arg3)); } JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject *arg0, JSC__JSGlobalObject *arg1, ZigString arg2) { diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index f9cc9bad3..cdc53927a 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -41,7 +41,7 @@ pub const JSObject = extern struct { return create(global, length, creator, Type.call); } - pub fn getIndex(this: *JSObject, globalThis: *JSGlobalObject, i: u32) JSValue { + pub fn getIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue { return cppFn("getIndex", .{ this, globalThis, @@ -1411,6 +1411,17 @@ pub const JSValue = enum(i64) { }); } + pub inline fn toU16(this: JSValue) u36 { + return @intCast(u16, this.toInt32()); + } + + pub fn getLengthOfArray(this: JSValue, globalThis: *JSGlobalObject) u32 { + return cppFn("getLengthOfArray", .{ + this, + globalThis, + }); + } + pub fn isAggregateError(this: JSValue, globalObject: *JSGlobalObject) bool { return cppFn("isAggregateError", .{ this, globalObject }); } @@ -1427,11 +1438,11 @@ pub const JSValue = enum(i64) { } pub inline fn asRef(this: JSValue) C_API.JSValueRef { - return @intToPtr(C_API.JSValueRef, @intCast(usize, @enumToInt(this))); + return @intToPtr(C_API.JSValueRef, @bitCast(usize, @enumToInt(this))); } pub inline fn fromRef(this: C_API.JSValueRef) JSValue { - return @intToEnum(JSValue, @intCast(i64, @ptrToInt(this))); + return @intToEnum(JSValue, @bitCast(i64, @ptrToInt(this))); } pub inline fn asObjectRef(this: JSValue) C_API.JSObjectRef { @@ -1442,7 +1453,7 @@ pub const JSValue = enum(i64) { return @intToPtr(*c_void, @intCast(usize, @enumToInt(this))); } - pub const Extern = [_][]const u8{ "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" }; + pub const Extern = [_][]const u8{ "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" }; }; pub const PropertyName = extern struct { diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index 7ea94d875..63510791c 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1631749917 +//-- AUTOGENERATED FILE -- 1632635195 // clang-format off #pragma once diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index e40651476..657d4a4f4 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1631749917 +//-- AUTOGENERATED FILE -- 1632635195 // clang-format: off #pragma once @@ -234,7 +234,7 @@ typedef void* JSClassRef; CPP_DECL JSC__JSValue JSC__JSObject__create(JSC__JSGlobalObject* arg0, size_t arg1, void* arg2, void (* ArgFn3)(void* arg0, JSC__JSObject* arg1, JSC__JSGlobalObject* arg2)); CPP_DECL size_t JSC__JSObject__getArrayLength(JSC__JSObject* arg0); CPP_DECL JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString arg2); -CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, uint32_t arg2); +CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, uint32_t arg2); CPP_DECL void JSC__JSObject__putDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString arg2, JSC__JSValue JSValue3); CPP_DECL void JSC__JSObject__putRecord(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4); CPP_DECL JSC__JSValue ZigString__toErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1); @@ -421,6 +421,7 @@ CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void (* ArgFn2)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2)); CPP_DECL void JSC__JSValue__getClassName(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2); CPP_DECL JSC__JSValue JSC__JSValue__getErrorsProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); +CPP_DECL uint32_t JSC__JSValue__getLengthOfArray(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); CPP_DECL void JSC__JSValue__getNameProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2); CPP_DECL JSC__JSValue JSC__JSValue__getPrototype(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); CPP_DECL bool JSC__JSValue__isAggregateError(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index 1b295fbad..a2c111d2d 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -37,69 +37,70 @@ pub const __mbstate_t = extern union { pub const __darwin_mbstate_t = __mbstate_t; pub const __darwin_ptrdiff_t = c_long; pub const __darwin_size_t = c_ulong; - + pub const JSC__RegExpPrototype = struct_JSC__RegExpPrototype; - + pub const JSC__GeneratorPrototype = struct_JSC__GeneratorPrototype; - + pub const JSC__ArrayIteratorPrototype = struct_JSC__ArrayIteratorPrototype; - + pub const JSC__StringPrototype = struct_JSC__StringPrototype; pub const WTF__StringView = bWTF__StringView; - + pub const JSC__JSPromisePrototype = struct_JSC__JSPromisePrototype; pub const JSC__CatchScope = bJSC__CatchScope; pub const JSC__ThrowScope = bJSC__ThrowScope; pub const JSC__PropertyName = bJSC__PropertyName; pub const JSC__JSObject = bJSC__JSObject; pub const WTF__ExternalStringImpl = bWTF__ExternalStringImpl; - + pub const JSC__AsyncIteratorPrototype = struct_JSC__AsyncIteratorPrototype; pub const WTF__StringImpl = bWTF__StringImpl; pub const JSC__JSLock = bJSC__JSLock; pub const JSC__JSModuleLoader = bJSC__JSModuleLoader; pub const JSC__VM = bJSC__VM; - + pub const JSC__AsyncGeneratorPrototype = struct_JSC__AsyncGeneratorPrototype; - + pub const JSC__AsyncGeneratorFunctionPrototype = struct_JSC__AsyncGeneratorFunctionPrototype; pub const JSC__JSGlobalObject = bJSC__JSGlobalObject; pub const JSC__JSFunction = bJSC__JSFunction; - + pub const JSC__ArrayPrototype = struct_JSC__ArrayPrototype; - + pub const JSC__AsyncFunctionPrototype = struct_JSC__AsyncFunctionPrototype; pub const JSC__Identifier = bJSC__Identifier; pub const JSC__JSPromise = bJSC__JSPromise; - + pub const JSC__SetIteratorPrototype = struct_JSC__SetIteratorPrototype; pub const JSC__SourceCode = bJSC__SourceCode; pub const JSC__JSCell = bJSC__JSCell; - + pub const JSC__BigIntPrototype = struct_JSC__BigIntPrototype; - + pub const JSC__GeneratorFunctionPrototype = struct_JSC__GeneratorFunctionPrototype; pub const JSC__SourceOrigin = bJSC__SourceOrigin; pub const JSC__JSModuleRecord = bJSC__JSModuleRecord; pub const WTF__String = bWTF__String; pub const WTF__URL = bWTF__URL; - + + pub const JSC__IteratorPrototype = struct_JSC__IteratorPrototype; pub const JSC__JSInternalPromise = bJSC__JSInternalPromise; - + pub const JSC__FunctionPrototype = struct_JSC__FunctionPrototype; pub const Inspector__ScriptArguments = bInspector__ScriptArguments; pub const JSC__Exception = bJSC__Exception; pub const JSC__JSString = bJSC__JSString; - + pub const JSC__ObjectPrototype = struct_JSC__ObjectPrototype; pub const JSC__CallFrame = bJSC__CallFrame; - + pub const JSC__MapIteratorPrototype = struct_JSC__MapIteratorPrototype; pub extern fn JSC__JSObject__create(arg0: [*c]JSC__JSGlobalObject, arg1: usize, arg2: ?*c_void, ArgFn3: ?fn (?*c_void, [*c]JSC__JSObject, [*c]JSC__JSGlobalObject) callconv(.C) void) JSC__JSValue; pub extern fn JSC__JSObject__getArrayLength(arg0: [*c]JSC__JSObject) usize; pub extern fn JSC__JSObject__getDirect(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: ZigString) JSC__JSValue; -pub extern fn JSC__JSObject__getIndex(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: u32) JSC__JSValue; +pub extern fn JSC__JSObject__getIndex(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: u32) JSC__JSValue; pub extern fn JSC__JSObject__putDirect(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: ZigString, JSValue3: JSC__JSValue) void; pub extern fn JSC__JSObject__putRecord(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString, arg3: [*c]ZigString, arg4: usize) void; pub extern fn ZigString__toErrorInstance(arg0: [*c]const ZigString, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; @@ -154,14 +155,14 @@ pub extern fn JSC__JSInternalPromise__then(arg0: [*c]JSC__JSInternalPromise, arg pub extern fn JSC__SourceOrigin__fromURL(arg0: [*c]const WTF__URL) bJSC__SourceOrigin; pub extern fn JSC__SourceCode__fromString(arg0: [*c]JSC__SourceCode, arg1: [*c]const WTF__String, arg2: [*c]const JSC__SourceOrigin, arg3: [*c]WTF__String, SourceType4: u8) void; pub extern fn JSC__JSFunction__calculatedDisplayName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String; -pub extern fn JSC__JSFunction__callWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception, arg5: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__callWithArgumentsAndThis(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception, arg6: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__callWithoutAnyArgumentsOrThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception, arg3: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__callWithThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception, arg4: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__constructWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception, arg5: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__constructWithArgumentsAndNewTarget(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception, arg6: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__constructWithNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception, arg4: [*c]const u8) JSC__JSValue; -pub extern fn JSC__JSFunction__constructWithoutAnyArgumentsOrNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception, arg3: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__callWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception , arg5: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__callWithArgumentsAndThis(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception , arg6: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__callWithoutAnyArgumentsOrThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception , arg3: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__callWithThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception , arg4: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__constructWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception , arg5: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__constructWithArgumentsAndNewTarget(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception , arg6: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__constructWithNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception , arg4: [*c]const u8) JSC__JSValue; +pub extern fn JSC__JSFunction__constructWithoutAnyArgumentsOrNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception , arg3: [*c]const u8) JSC__JSValue; pub extern fn JSC__JSFunction__createFromNative(arg0: [*c]JSC__JSGlobalObject, arg1: u16, arg2: [*c]const WTF__String, arg3: ?*c_void, ArgFn4: ?fn (?*c_void, [*c]JSC__JSGlobalObject, [*c]JSC__CallFrame) callconv(.C) JSC__JSValue) [*c]JSC__JSFunction; pub extern fn JSC__JSFunction__displayName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String; pub extern fn JSC__JSFunction__getName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String; @@ -244,6 +245,7 @@ pub extern fn JSC__JSValue__eqlValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSVa pub extern fn JSC__JSValue__forEach(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, ArgFn2: ?fn ([*c]JSC__VM, [*c]JSC__JSGlobalObject, JSC__JSValue) callconv(.C) void) void; pub extern fn JSC__JSValue__getClassName(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString) void; pub extern fn JSC__JSValue__getErrorsProperty(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; +pub extern fn JSC__JSValue__getLengthOfArray(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) u32; pub extern fn JSC__JSValue__getNameProperty(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString) void; pub extern fn JSC__JSValue__getPrototype(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; pub extern fn JSC__JSValue__isAggregateError(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) bool; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 8d7626afb..4dfacffdd 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -36,6 +36,7 @@ pub const GlobalClasses = [_]type{ ResolveError.Class, Bun.Class, Fetch.Class, + js_ast.Macro.JSNode.BunJSXCallbackFunction, }; const Blob = @import("../../blob.zig"); @@ -723,6 +724,19 @@ pub const VirtualMachine = struct { .hash = 0, .bytecodecache_fd = 0, }; + } else if (_specifier.len > js_ast.Macro.namespaceWithColon.len and + strings.eqlComptimeIgnoreLen(_specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) + { + if (vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(entry.source.contents), + .specifier = ZigString.init(_specifier), + .source_url = ZigString.init(_specifier), + .hash = 0, + .bytecodecache_fd = 0, + }; + } } const specifier = normalizeSpecifier(_specifier); @@ -752,6 +766,7 @@ pub const VirtualMachine = struct { vm.bundler.log = log; vm.bundler.linker.log = log; vm.bundler.resolver.log = log; + defer { vm.bundler.log = old; vm.bundler.linker.log = old; diff --git a/src/js_ast.zig b/src/js_ast.zig index c2fafaf07..28512365a 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -1042,6 +1042,8 @@ pub const E = struct { pub const BigInt = struct { value: string, + pub var empty = BigInt{ .value = "" }; + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { return try std.json.stringify(self.value, opts, o); } @@ -1057,11 +1059,15 @@ pub const E = struct { pub const Spread = struct { value: ExprNodeIndex }; pub const String = struct { - value: JavascriptString = &([_]u16{}), + value: []const u16 = &.{}, utf8: string = &([_]u8{}), prefer_template: bool = false, pub var empty = String{}; + pub var @"true" = String{ .utf8 = "true" }; + pub var @"false" = String{ .utf8 = "false" }; + pub var @"null" = String{ .utf8 = "null" }; + pub var @"undefined" = String{ .utf8 = "undefined" }; pub fn clone(str: *const String, allocator: *std.mem.Allocator) !String { if (str.isUTF8()) { @@ -1102,7 +1108,7 @@ pub const E = struct { string => { return strings.eql(s.utf8, other); }, - JavascriptString => { + []u16, []const u16 => { return strings.utf16EqlString(other, s.utf8); }, else => { @@ -1121,7 +1127,7 @@ pub const E = struct { string => { return strings.utf16EqlString(s.value, other); }, - JavascriptString => { + []u16, []const u16 => { return std.mem.eql(u16, other.value, s.value); }, else => { @@ -1147,7 +1153,7 @@ pub const E = struct { return std.hash.Wyhash.hash(0, s.utf8); } else { // hash utf-16 - return std.hash.Wyhash.hash(0, @ptrCast([*]u8, s.value.ptr)[0 .. s.value.len * 2]); + return std.hash.Wyhash.hash(0, @ptrCast([*]const u8, s.value.ptr)[0 .. s.value.len * 2]); } } @@ -1182,6 +1188,8 @@ pub const E = struct { pub const RegExp = struct { value: string, + pub var empty = RegExp{ .value = "" }; + pub fn jsonStringify(self: *const RegExp, opts: anytype, o: anytype) !void { return try std.json.stringify(self.value, opts, o); } @@ -2546,160 +2554,8 @@ pub const Expr = struct { e_class, e_require, - pub inline fn toPublicValue(this: Tag) u16 { - return @intCast(u16, @enumToInt(this)) + 16; - } - - pub inline fn fromPublicValue(comptime ValueType: type, value: ValueType) ?Tag { - if (value < 16 or value > @enumToInt(Tag.e_require)) return null; - - switch (comptime ValueType) { - f64 => { - return @intToEnum(@floatToInt(u16, value - 16), Tag); - }, - else => { - return @intToEnum(@intCast(u6, @intCast(u16, value) - 16), Tag); - }, - } - } - - pub const names_strings = [_]string{ - "<array>", - "<unary>", - "<binary>", - "<boolean>", - "<super>", - "<null>", - "<void>", - "<new>", - "<function>", - "<ntarget>", - "<import>", - "<call>", - "<dot>", - "<index>", - "<arrow>", - "<id>", - "<importid>", - "<private>", - "<jsx>", - "<missing>", - "<number>", - "<bigint>", - "<object>", - "<spread>", - "<string>", - "<tpart>", - "<template>", - "<regexp>", - "<await>", - "<yield>", - "<if>", - "<resolve>", - "<import>", - "<this>", - "<class>", - "<require>", - }; - pub const valid_names_list: string = brk: { - var names_list = names_strings[0]; - for (names_strings[1..]) |name_str, i| { - names_list = names_list ++ "\n " ++ name_str; - } - break :brk " " ++ names_list; - }; - - pub const TagName = std.EnumArray(Tag, string); - - pub const names: TagName = brk: { - var array = TagName.initUndefined(); - array.set(.e_array, names_strings[0]); - array.set(.e_unary, names_strings[1]); - array.set(.e_binary, names_strings[2]); - array.set(.e_boolean, names_strings[3]); - array.set(.e_super, names_strings[4]); - array.set(.e_null, names_strings[5]); - array.set(.e_undefined, names_strings[6]); - array.set(.e_new, names_strings[7]); - array.set(.e_function, names_strings[8]); - array.set(.e_new_target, names_strings[9]); - array.set(.e_import_meta, names_strings[10]); - array.set(.e_call, names_strings[11]); - array.set(.e_dot, names_strings[12]); - array.set(.e_index, names_strings[13]); - array.set(.e_arrow, names_strings[14]); - array.set(.e_identifier, names_strings[15]); - array.set(.e_import_identifier, names_strings[16]); - array.set(.e_private_identifier, names_strings[17]); - array.set(.e_jsx_element, names_strings[18]); - array.set(.e_missing, names_strings[19]); - array.set(.e_number, names_strings[20]); - array.set(.e_big_int, names_strings[21]); - array.set(.e_object, names_strings[22]); - array.set(.e_spread, names_strings[23]); - array.set(.e_string, names_strings[24]); - array.set(.e_template_part, names_strings[25]); - array.set(.e_template, names_strings[26]); - array.set(.e_reg_exp, names_strings[27]); - array.set(.e_await, names_strings[28]); - array.set(.e_yield, names_strings[29]); - array.set(.e_if, names_strings[30]); - array.set(.e_require_or_require_resolve, names_strings[31]); - array.set(.e_import, names_strings[32]); - array.set(.e_this, names_strings[33]); - array.set(.e_class, names_strings[34]); - array.set(.e_require, names_strings[35]); - break :brk array; - }; - pub const TagExactSizeMatcher = strings.ExactSizeMatcher(8); - pub fn find(name_: string) ?Tag { - return switch (TagExactSizeMatcher.match(name_)) { - TagExactSizeMatcher.case("array") => Tag.e_array, - TagExactSizeMatcher.case("unary") => Tag.e_unary, - TagExactSizeMatcher.case("binary") => Tag.e_binary, - TagExactSizeMatcher.case("boolean") => Tag.e_boolean, - TagExactSizeMatcher.case("true") => Tag.e_boolean, - TagExactSizeMatcher.case("false") => Tag.e_boolean, - TagExactSizeMatcher.case("super") => Tag.e_super, - TagExactSizeMatcher.case("null") => Tag.e_null, - TagExactSizeMatcher.case("void") => Tag.e_undefined, - TagExactSizeMatcher.case("new") => Tag.e_new, - TagExactSizeMatcher.case("function") => Tag.e_function, - TagExactSizeMatcher.case("ntarget") => Tag.e_new_target, - TagExactSizeMatcher.case("imeta") => Tag.e_import_meta, - TagExactSizeMatcher.case("call") => Tag.e_call, - TagExactSizeMatcher.case("dot") => Tag.e_dot, - TagExactSizeMatcher.case("index") => Tag.e_index, - TagExactSizeMatcher.case("arrow") => Tag.e_arrow, - TagExactSizeMatcher.case("id") => Tag.e_identifier, - TagExactSizeMatcher.case("importid") => Tag.e_import_identifier, - TagExactSizeMatcher.case("jsx") => Tag.e_jsx_element, - TagExactSizeMatcher.case("missing") => Tag.e_missing, - TagExactSizeMatcher.case("number") => Tag.e_number, - TagExactSizeMatcher.case("bigint") => Tag.e_big_int, - TagExactSizeMatcher.case("object") => Tag.e_object, - TagExactSizeMatcher.case("spread") => Tag.e_spread, - TagExactSizeMatcher.case("string") => Tag.e_string, - TagExactSizeMatcher.case("tpart") => Tag.e_template_part, - TagExactSizeMatcher.case("template") => Tag.e_template, - TagExactSizeMatcher.case("regexp") => Tag.e_reg_exp, - TagExactSizeMatcher.case("await") => Tag.e_await, - TagExactSizeMatcher.case("yield") => Tag.e_yield, - TagExactSizeMatcher.case("if") => Tag.e_if, - TagExactSizeMatcher.case("import") => Tag.e_import, - TagExactSizeMatcher.case("this") => Tag.e_this, - TagExactSizeMatcher.case("class") => Tag.e_class, - TagExactSizeMatcher.case("require") => Tag.e_require, - else => null, - }; - } - - pub inline fn name(this: Tag) string { - return names.get(this); - } - pub fn jsonStringify(self: @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(self.name(), opts, o); + return try std.json.stringify(@tagName(self), opts, o); } pub fn isArray(self: Tag) bool { @@ -3354,7 +3210,7 @@ pub const S = struct { pub const Comment = struct { text: string }; pub const Directive = struct { - value: JavascriptString, + value: []const u16, }; pub const ExportClause = struct { items: []ClauseItem, is_single_line: bool = false }; @@ -4211,14 +4067,13 @@ pub const Macro = struct { replacement: Expr, }; - pub const JSExpr = struct { - expr: Expr, - import_statements: []S.Import = &[_]S.Import{}, - + pub const JSNode = struct { + loc: logger.Loc, + data: Data, pub const Class = JSCBase.NewClass( - JSExpr, + JSNode, .{ - .name = "JSExpr", + .name = "JSNode", .read_only = true, }, .{ @@ -4245,243 +4100,606 @@ pub const Macro = struct { }, ); - pub const JSNode = struct { - loc: logger.Loc, - data: Data, + pub fn initExpr(this: Expr) JSNode { + switch (this.data) { + .e_array => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_array = value } }; + }, + .e_unary => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_unary = value } }; + }, + .e_binary => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_binary = value } }; + }, + .e_function => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_function = value } }; + }, + .e_new_target => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_new_target = value } }; + }, + .e_import_meta => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_import_meta = value } }; + }, + .e_call => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_call = value } }; + }, + .e_dot => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_dot = value } }; + }, + .e_index => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_index = value } }; + }, + .e_arrow => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_arrow = value } }; + }, + .e_identifier => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_identifier = value } }; + }, + .e_import_identifier => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_import_identifier = value } }; + }, + .e_private_identifier => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_private_identifier = value } }; + }, + .e_jsx_element => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_jsx_element = value } }; + }, + .e_big_int => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_big_int = value } }; + }, + .e_object => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_object = value } }; + }, + .e_spread => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_spread = value } }; + }, + .e_string => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_string = value } }; + }, + .e_template_part => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_template_part = value } }; + }, + .e_template => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_template = value } }; + }, + .e_reg_exp => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_reg_exp = value } }; + }, + .e_await => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_await = value } }; + }, + .e_yield => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_yield = value } }; + }, + .e_if => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_if = value } }; + }, + .e_require_or_require_resolve => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_require_or_require_resolve = value } }; + }, + .e_import => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_import = value } }; + }, + .e_this => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_this = value } }; + }, + .e_class => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_class = value } }; + }, + .e_require => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_require = value } }; + }, + .e_missing => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_missing = value } }; + }, + .e_boolean => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_boolean = value } }; + }, + .e_super => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_super = value } }; + }, + .e_null => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_null = value } }; + }, + .e_number => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_number = value } }; + }, + .e_undefined => |value| { + return JSNode{ .loc = this.loc, .data = .{ .e_undefined = value } }; + }, + else => { + return JSNode{ .loc = this.loc, .data = .{ .e_missing = .{} } }; + }, + } + } - pub fn toExpr(this: JSNode) Expr { - switch (this.data) { - .e_array => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_array = value } }; - }, - .e_unary => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_unary = value } }; - }, - .e_binary => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_binary = value } }; - }, - .e_function => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_function = value } }; - }, - .e_new_target => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_new_target = value } }; - }, - .e_import_meta => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_import_meta = value } }; - }, - .e_call => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_call = value } }; - }, - .e_dot => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_dot = value } }; - }, - .e_index => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_index = value } }; - }, - .e_arrow => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_arrow = value } }; - }, - .e_identifier => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_identifier = value } }; - }, - .e_import_identifier => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_import_identifier = value } }; - }, - .e_private_identifier => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_private_identifier = value } }; - }, - .e_jsx_element => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_jsx_element = value } }; - }, - .e_big_int => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_big_int = value } }; - }, - .e_object => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_object = value } }; - }, - .e_spread => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_spread = value } }; - }, - .e_string => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_string = value } }; - }, - .e_template_part => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_template_part = value } }; - }, - .e_template => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_template = value } }; - }, - .e_reg_exp => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_reg_exp = value } }; - }, - .e_await => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_await = value } }; - }, - .e_yield => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_yield = value } }; - }, - .e_if => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_if = value } }; - }, - .e_require_or_require_resolve => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_require_or_require_resolve = value } }; - }, - .e_import => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_import = value } }; - }, - .e_this => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_this = value } }; - }, - .e_class => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_class = value } }; - }, - .e_require => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_require = value } }; - }, - .e_missing => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_missing = value } }; - }, - .e_boolean => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_boolean = value } }; - }, - .e_super => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_super = value } }; - }, - .e_null => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_null = value } }; - }, - .e_number => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_number = value } }; - }, - .e_undefined => |value| { - return Expr{ .loc = this.loc, .data = .{ .e_undefined = value } }; - }, - wip, .s_import => { - return Expr{ .loc = this.loc, .data = .{ .e_missing = .{} } }; - }, - } + pub fn toExpr(this: JSNode) Expr { + switch (this.data) { + .e_array => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_array = value } }; + }, + .e_unary => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_unary = value } }; + }, + .e_binary => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_binary = value } }; + }, + .e_function => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_function = value } }; + }, + .e_new_target => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_new_target = value } }; + }, + .e_import_meta => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_import_meta = value } }; + }, + .e_call => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_call = value } }; + }, + .e_dot => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_dot = value } }; + }, + .e_index => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_index = value } }; + }, + .e_arrow => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_arrow = value } }; + }, + .e_identifier => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_identifier = value } }; + }, + .e_import_identifier => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_import_identifier = value } }; + }, + .e_private_identifier => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_private_identifier = value } }; + }, + .e_jsx_element => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_jsx_element = value } }; + }, + .e_big_int => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_big_int = value } }; + }, + .e_object => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_object = value } }; + }, + .e_spread => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_spread = value } }; + }, + .e_string => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_string = value } }; + }, + .e_template_part => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_template_part = value } }; + }, + .e_template => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_template = value } }; + }, + .e_reg_exp => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_reg_exp = value } }; + }, + .e_await => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_await = value } }; + }, + .e_yield => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_yield = value } }; + }, + .e_if => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_if = value } }; + }, + .e_require_or_require_resolve => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_require_or_require_resolve = value } }; + }, + .e_import => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_import = value } }; + }, + .e_this => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_this = value } }; + }, + .e_class => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_class = value } }; + }, + .e_require => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_require = value } }; + }, + .e_missing => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_missing = value } }; + }, + .e_boolean => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_boolean = value } }; + }, + .e_super => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_super = value } }; + }, + .e_null => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_null = value } }; + }, + .e_number => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_number = value } }; + }, + .e_undefined => |value| { + return Expr{ .loc = this.loc, .data = .{ .e_undefined = value } }; + }, + else => { + return Expr{ .loc = this.loc, .data = .{ .e_missing = .{} } }; + }, } + } - pub const Data = union(Tag) { - e_array: *E.Array, - e_unary: *E.Unary, - e_binary: *E.Binary, - e_function: *E.Function, - e_new_target: *E.NewTarget, - e_import_meta: *E.ImportMeta, - e_call: *E.Call, - e_dot: *E.Dot, - e_index: *E.Index, - e_arrow: *E.Arrow, - e_identifier: *E.Identifier, - e_import_identifier: *E.ImportIdentifier, - e_private_identifier: *E.PrivateIdentifier, - e_jsx_element: *E.JsxElement, - - e_big_int: *E.BigInt, - e_object: *E.Object, - e_spread: *E.Spread, - e_string: *E.String, - e_template_part: *E.TemplatePart, - e_template: *E.Template, - e_reg_exp: *E.RegExp, - e_await: *E.Await, - e_yield: *E.Yield, - e_if: *E.If, - e_require_or_require_resolve: *E.RequireOrRequireResolve, - e_import: *E.Import, - e_this: *E.This, - e_class: *E.Class, - e_require: *E.Require, - - s_import: *S.Import, - - e_missing: E.Missing, - e_boolean: E.Boolean, - e_super: E.Super, - e_null: E.Null, - e_number: E.Number, - e_undefined: E.Undefined, + pub const Data = union(Tag) { + inline_false: void, + inline_true: void, + e_boolean: E.Boolean, + e_super: E.Super, + e_null: E.Null, + e_number: E.Number, + e_undefined: E.Undefined, + e_new_target: E.NewTarget, + e_import_meta: E.ImportMeta, + e_missing: E.Missing, + e_this: E.This, + + e_array: *E.Array, + e_unary: *E.Unary, + e_binary: *E.Binary, + e_function: *E.Function, + + e_call: *E.Call, + e_dot: *E.Dot, + e_index: *E.Index, + e_arrow: *E.Arrow, + e_identifier: *E.Identifier, + e_import_identifier: *E.ImportIdentifier, + e_private_identifier: *E.PrivateIdentifier, + e_jsx_element: *E.JSXElement, + + e_big_int: *E.BigInt, + e_object: *E.Object, + e_spread: *E.Spread, + e_string: *E.String, + e_template_part: *E.TemplatePart, + e_template: *E.Template, + e_reg_exp: *E.RegExp, + e_await: *E.Await, + e_yield: *E.Yield, + e_if: *E.If, + e_require_or_require_resolve: *E.RequireOrRequireResolve, + e_import: *E.Import, + + e_class: *E.Class, + e_require: *E.Require, + + s_import: *S.Import, + s_block: *S.Block, + + g_property: *G.Property, + }; + pub const Tag = enum(u8) { + e_array, + e_unary, + e_binary, + e_function, + e_new_target, + e_import_meta, + e_call, + e_dot, + e_index, + e_arrow, + e_identifier, + e_import_identifier, + e_private_identifier, + e_jsx_element, + e_big_int, + e_object, + e_spread, + e_string, + e_template_part, + e_template, + e_reg_exp, + e_await, + e_yield, + e_if, + e_require_or_require_resolve, + e_import, + e_this, + e_class, + e_require, + s_import, + s_block, + + g_property, + + e_missing, + e_boolean, + e_super, + e_null, + e_number, + e_undefined, + + inline_true, + inline_false, + + pub const ids: std.EnumArray(Tag, Expr.Data) = brk: { + var list = std.EnumArray(Tag, Expr.Data).initFill(Expr.Data{ .e_number = E.Number{ .value = 0.0 } }); + list.set(Tag.e_array, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_array)) }, + }); + list.set(Tag.e_unary, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_unary)) }, + }); + list.set(Tag.e_binary, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_binary)) }, + }); + list.set(Tag.e_boolean, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_boolean)) }, + }); + list.set(Tag.e_super, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_super)) }, + }); + list.set(Tag.e_null, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_null)) }, + }); + list.set(Tag.e_undefined, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_undefined)) }, + }); + list.set(Tag.e_function, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_function)) }, + }); + list.set(Tag.e_new_target, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_new_target)) }, + }); + list.set(Tag.e_import_meta, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import_meta)) }, + }); + list.set(Tag.e_call, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_call)) }, + }); + list.set(Tag.e_dot, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_dot)) }, + }); + list.set(Tag.e_index, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_index)) }, + }); + list.set(Tag.e_arrow, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_arrow)) }, + }); + list.set(Tag.e_identifier, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_identifier)) }, + }); + list.set(Tag.e_import_identifier, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import_identifier)) }, + }); + list.set(Tag.e_private_identifier, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_private_identifier)) }, + }); + list.set(Tag.e_jsx_element, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_jsx_element)) }, + }); + list.set(Tag.e_missing, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_missing)) }, + }); + list.set(Tag.e_number, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_number)) }, + }); + list.set(Tag.e_big_int, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_big_int)) }, + }); + list.set(Tag.e_object, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_object)) }, + }); + list.set(Tag.e_spread, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_spread)) }, + }); + list.set(Tag.e_string, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_string)) }, + }); + list.set(Tag.e_template_part, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_template_part)) }, + }); + list.set(Tag.e_template, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_template)) }, + }); + list.set(Tag.e_reg_exp, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_reg_exp)) }, + }); + list.set(Tag.e_await, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_await)) }, + }); + list.set(Tag.e_yield, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_yield)) }, + }); + list.set(Tag.e_if, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_if)) }, + }); + list.set(Tag.e_import, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import)) }, + }); + list.set(Tag.e_this, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_this)) }, + }); + list.set(Tag.e_class, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_class)) }, + }); + list.set(Tag.e_require, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_require)) }, + }); + list.set(Tag.s_import, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.s_import)) }, + }); + list.set(Tag.g_property, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.g_property)) }, + }); + list.set(Tag.s_block, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.s_block)) }, + }); + list.set(Tag.inline_true, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.inline_true)) }, + }); + list.set(Tag.inline_false, Expr.Data{ + .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.inline_false)) }, + }); + break :brk list; }; - pub const Tag = enum(u8) { - e_array, - e_unary, - e_binary, - e_boolean, - e_super, - e_null, - e_undefined, - e_function, - e_new_target, - e_import_meta, - e_call, - e_dot, - e_index, - e_arrow, - e_identifier, - e_import_identifier, - e_private_identifier, - e_jsx_element, - e_missing, - e_number, - e_big_int, - e_object, - e_spread, - e_string, - e_template_part, - e_template, - e_reg_exp, - e_await, - e_yield, - e_if, - e_require_or_require_resolve, - e_import, - e_this, - e_class, - e_require, - s_import, - - wip, - - pub const as_expr_tag: std.EnumArray(Tag, Expr.Tag) = brk: { - var list = std.EnumArray(Tag, Expr.Tag).initFill(Expr.Tag.e_missing); - list.set(Tag.e_array, Expr.Tag.e_array); - list.set(Tag.e_unary, Expr.Tag.e_unary); - list.set(Tag.e_binary, Expr.Tag.e_binary); - list.set(Tag.e_boolean, Expr.Tag.e_boolean); - list.set(Tag.e_super, Expr.Tag.e_super); - list.set(Tag.e_null, Expr.Tag.e_null); - list.set(Tag.e_undefined, Expr.Tag.e_undefined); - list.set(Tag.e_function, Expr.Tag.e_function); - list.set(Tag.e_new_target, Expr.Tag.e_new_target); - list.set(Tag.e_import_meta, Expr.Tag.e_import_meta); - list.set(Tag.e_call, Expr.Tag.e_call); - list.set(Tag.e_dot, Expr.Tag.e_dot); - list.set(Tag.e_index, Expr.Tag.e_index); - list.set(Tag.e_arrow, Expr.Tag.e_arrow); - list.set(Tag.e_identifier, Expr.Tag.e_identifier); - list.set(Tag.e_import_identifier, Expr.Tag.e_import_identifier); - list.set(Tag.e_private_identifier, Expr.Tag.e_private_identifier); - list.set(Tag.e_jsx_element, Expr.Tag.e_jsx_element); - list.set(Tag.e_missing, Expr.Tag.e_missing); - list.set(Tag.e_number, Expr.Tag.e_number); - list.set(Tag.e_big_int, Expr.Tag.e_big_int); - list.set(Tag.e_object, Expr.Tag.e_object); - list.set(Tag.e_spread, Expr.Tag.e_spread); - list.set(Tag.e_string, Expr.Tag.e_string); - list.set(Tag.e_template_part, Expr.Tag.e_template_part); - list.set(Tag.e_template, Expr.Tag.e_template); - list.set(Tag.e_reg_exp, Expr.Tag.e_reg_exp); - list.set(Tag.e_await, Expr.Tag.e_await); - list.set(Tag.e_yield, Expr.Tag.e_yield); - list.set(Tag.e_if, Expr.Tag.e_if); - list.set(Tag.e_require_or_require_resolve, Expr.Tag.e_require_or_require_resolve); - list.set(Tag.e_import, Expr.Tag.e_import); - list.set(Tag.e_this, Expr.Tag.e_this); - list.set(Tag.e_class, Expr.Tag.e_class); - list.set(Tag.e_require, Expr.Tag.e_require); - break :brk list; - }; + + pub const names = std.ComptimeStringMap(Tag, .{ + .{ "array", Tag.e_array }, + .{ "unary", Tag.e_unary }, + .{ "binary", Tag.e_binary }, + .{ "bool", Tag.e_boolean }, + .{ "super", Tag.e_super }, + .{ "null", Tag.e_null }, + .{ "undefined", Tag.e_undefined }, + .{ "function", Tag.e_function }, + .{ "new_target", Tag.e_new_target }, + .{ "import_meta", Tag.e_import_meta }, + .{ "call", Tag.e_call }, + .{ "dot", Tag.e_dot }, + .{ "index", Tag.e_index }, + .{ "arrow", Tag.e_arrow }, + .{ "id", Tag.e_identifier }, + .{ "import-id", Tag.e_import_identifier }, + .{ "private-id", Tag.e_private_identifier }, + .{ "jsx", Tag.e_jsx_element }, + .{ "missing", Tag.e_missing }, + .{ "number", Tag.e_number }, + .{ "bigint", Tag.e_big_int }, + .{ "object", Tag.e_object }, + .{ "spread", Tag.e_spread }, + .{ "string", Tag.e_string }, + .{ "template-part", Tag.e_template_part }, + .{ "template", Tag.e_template }, + .{ "regex", Tag.e_reg_exp }, + .{ "await", Tag.e_await }, + .{ "yield", Tag.e_yield }, + .{ "if", Tag.e_if }, + .{ "dynamic", Tag.e_import }, + .{ "this", Tag.e_this }, + .{ "class", Tag.e_class }, + .{ "require", Tag.e_require }, + .{ "import", Tag.s_import }, + .{ "property", Tag.g_property }, + .{ "block", Tag.s_block }, + .{ "true", Tag.inline_true }, + .{ "false", Tag.inline_false }, + }); + + pub const as_expr_tag: std.EnumArray(Tag, Expr.Tag) = brk: { + var list = std.EnumArray(Tag, Expr.Tag).initFill(Expr.Tag.e_missing); + list.set(Tag.e_array, Expr.Tag.e_array); + list.set(Tag.e_unary, Expr.Tag.e_unary); + list.set(Tag.e_binary, Expr.Tag.e_binary); + list.set(Tag.e_boolean, Expr.Tag.e_boolean); + list.set(Tag.e_super, Expr.Tag.e_super); + list.set(Tag.e_null, Expr.Tag.e_null); + list.set(Tag.e_undefined, Expr.Tag.e_undefined); + list.set(Tag.e_function, Expr.Tag.e_function); + list.set(Tag.e_new_target, Expr.Tag.e_new_target); + list.set(Tag.e_import_meta, Expr.Tag.e_import_meta); + list.set(Tag.e_call, Expr.Tag.e_call); + list.set(Tag.e_dot, Expr.Tag.e_dot); + list.set(Tag.e_index, Expr.Tag.e_index); + list.set(Tag.e_arrow, Expr.Tag.e_arrow); + list.set(Tag.e_identifier, Expr.Tag.e_identifier); + list.set(Tag.e_import_identifier, Expr.Tag.e_import_identifier); + list.set(Tag.e_private_identifier, Expr.Tag.e_private_identifier); + list.set(Tag.e_jsx_element, Expr.Tag.e_jsx_element); + list.set(Tag.e_missing, Expr.Tag.e_missing); + list.set(Tag.e_number, Expr.Tag.e_number); + list.set(Tag.e_big_int, Expr.Tag.e_big_int); + list.set(Tag.e_object, Expr.Tag.e_object); + list.set(Tag.e_spread, Expr.Tag.e_spread); + list.set(Tag.e_string, Expr.Tag.e_string); + list.set(Tag.e_template_part, Expr.Tag.e_template_part); + list.set(Tag.e_template, Expr.Tag.e_template); + list.set(Tag.e_reg_exp, Expr.Tag.e_reg_exp); + list.set(Tag.e_await, Expr.Tag.e_await); + list.set(Tag.e_yield, Expr.Tag.e_yield); + list.set(Tag.e_if, Expr.Tag.e_if); + list.set(Tag.e_require_or_require_resolve, Expr.Tag.e_require_or_require_resolve); + list.set(Tag.e_import, Expr.Tag.e_import); + list.set(Tag.e_this, Expr.Tag.e_this); + list.set(Tag.e_class, Expr.Tag.e_class); + list.set(Tag.e_require, Expr.Tag.e_require); + break :brk list; + }; + + pub const to_expr_tag: std.EnumArray(Expr.Tag, Tag) = brk: { + var list = std.EnumArray(Expr.Tag, Tag).initFill(Tag.wip); + list.set(Expr.Tag.e_array, Tag.e_array); + list.set(Expr.Tag.e_unary, Tag.e_unary); + list.set(Expr.Tag.e_binary, Tag.e_binary); + list.set(Expr.Tag.e_boolean, Tag.e_boolean); + list.set(Expr.Tag.e_super, Tag.e_super); + list.set(Expr.Tag.e_null, Tag.e_null); + list.set(Expr.Tag.e_undefined, Tag.e_undefined); + list.set(Expr.Tag.e_function, Tag.e_function); + list.set(Expr.Tag.e_new_target, Tag.e_new_target); + list.set(Expr.Tag.e_import_meta, Tag.e_import_meta); + list.set(Expr.Tag.e_call, Tag.e_call); + list.set(Expr.Tag.e_dot, Tag.e_dot); + list.set(Expr.Tag.e_index, Tag.e_index); + list.set(Expr.Tag.e_arrow, Tag.e_arrow); + list.set(Expr.Tag.e_identifier, Tag.e_identifier); + list.set(Expr.Tag.e_import_identifier, Tag.e_import_identifier); + list.set(Expr.Tag.e_private_identifier, Tag.e_private_identifier); + list.set(Expr.Tag.e_jsx_element, Tag.e_jsx_element); + list.set(Expr.Tag.e_missing, Tag.e_missing); + list.set(Expr.Tag.e_number, Tag.e_number); + list.set(Expr.Tag.e_big_int, Tag.e_big_int); + list.set(Expr.Tag.e_object, Tag.e_object); + list.set(Expr.Tag.e_spread, Tag.e_spread); + list.set(Expr.Tag.e_string, Tag.e_string); + list.set(Expr.Tag.e_template_part, Tag.e_template_part); + list.set(Expr.Tag.e_template, Tag.e_template); + list.set(Expr.Tag.e_reg_exp, Tag.e_reg_exp); + list.set(Expr.Tag.e_await, Tag.e_await); + list.set(Expr.Tag.e_yield, Tag.e_yield); + list.set(Expr.Tag.e_if, Tag.e_if); + list.set(Expr.Tag.e_require_or_require_resolve, Tag.e_require_or_require_resolve); + list.set(Expr.Tag.e_import, Tag.e_import); + list.set(Expr.Tag.e_this, Tag.e_this); + list.set(Expr.Tag.e_class, Tag.e_class); + list.set(Expr.Tag.e_require, Tag.e_require); + break :brk list; + }; + + pub const Validator = struct { + pub const List = std.EnumArray(JSNode.Tag, bool); + fn NewList(comptime valid_tags: anytype) List { + return comptime brk: { + var list = List.initFill(false); + for (std.meta.fieldNames(@TypeOf(valid_tags))) |index| { + const name = @tagName(@field(valid_tags, index)); + + if (!@hasField(JSNode.Tag, name)) { + @compileError( + "JSNode.Tag does not have a \"" ++ name ++ "\" field. Valid fields are " ++ std.fmt.comptimePrint( + "{s}", + .{ + std.meta.fieldNames(@TypeOf(valid_tags)), + }, + ), + ); + } + list.set(@field(JSNode.Tag, name), true); + } + + break :brk list; + }; + } + + pub const valid_object_tags = Tag.Validator.NewList(.{ + .g_property, + .e_spread, + .e_identifier, + .e_import_identifier, + .e_index, + .e_call, + .e_private_identifier, + .e_dot, + .e_unary, + .e_binary, + }); }; pub const max_tag: u8 = brk: { @@ -4503,6 +4721,563 @@ pub const Macro = struct { }; }; + pub fn NewJSXWriter(comptime P: type) type { + return struct { + const JSXWriter = @This(); + p: *P, + bun_jsx_ref: Ref, + log: *logger.Log, + args: ExprList, + bun_identifier: *E.Identifier, + allocator: *std.mem.Allocator, + parent_tag: Tag = Tag.e_missing, + + pub fn initWriter(p: *P, bun_identifier: *E.Identifier) JSXWriter { + return JSXWriter{ + .p = p, + .log = p.log, + .bun_jsx_ref = p.bun_jsx_ref, + .args = ExprList.init(p.allocator), + .allocator = p.allocator, + .bun_identifier = bun_identifier, + }; + } + + fn hasPropertyNamed(props: []G.Property, comptime name: string) bool { + return indexOfPropertyByName(props, name) != null; + } + + fn indexOfPropertyByName(props: []G.Property, comptime name: string) ?u32 { + for (props) |prop, i| { + const key = prop.key orelse continue; + if (key.data != .e_string or !key.data.e_string.isUTF8()) continue; + if (strings.eqlComptime(key.data.e_string.utf8, name)) return @intCast(u32, i); + } + + return null; + } + + fn propertyValueNamed(props: []G.Property, comptime name: string) ?Expr { + for (props) |prop| { + const key = prop.key orelse continue; + if (key.data != .e_string or !key.data.e_string.isUTF8()) continue; + if (strings.eqlComptime(key.data.e_string.utf8, name)) return prop.value; + } + + return null; + } + + pub fn writeExprType(self: *JSXWriter, expr: Expr) bool {} + + pub fn writeNodeType(self: *JSXWriter, tag: JSNode.Tag, props: []G.Property, children: []Expr, loc: logger.Loc) bool { + switch (tag) { + + // <bool value={foo} /> + // intended for dynamic values + Tag.e_boolean => { + self.args.ensureUnusedCapacity(2) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_boolean) }); + const value_i = indexOfPropertyByName(props, "value") orelse { + self.log.addError(self.p.source, loc, "<bool> should have a \"value\" prop") catch unreachable; + self.args.append(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = loc }) catch unreachable; + return true; + }; + const value = props[value_i].value orelse Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = loc }; + + switch (value.data) { + .e_jsx_element => |el| { + return self.writeElement(el.*); + }, + .e_string => { + self.log.addError(self.p.source, value.loc, "\"value\" shouldn't be a string") catch unreachable; + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = value.loc }); + }, + .e_boolean => { + self.args.appendAssumeCapacity(value); + }, + .e_missing => { + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = value.loc }); + }, + // null and undefined literals are coerced to false + .e_null, .e_undefined => { + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = false } }, .loc = value.loc }); + }, + .e_number => { + // Numbers are cooerced to booleans + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = value.data.e_number.value > 0.0 } }, .loc = value.loc }); + }, + // these ones are not statically analyzable so we just leave them in as-is + .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.appendAssumeCapacity(self.p.visitExpr(value)); + }, + // everything else is invalid + else => { + self.log.addError(self.p.source, value.loc, "\"value\" should be a bool, jsx element, number, identifier, index, call, private identifier, or dot") catch unreachable; + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = false } }, .loc = value.loc }); + }, + } + + return true; + }, + // <number value={1.0} /> + Tag.e_number => { + self.args.ensureUnusedCapacity(2) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_number) }); + const invalid_value = Expr{ .data = .{ .e_number = .{ .value = 0.0 } }, .loc = loc }; + const value_i = indexOfPropertyByName(props, "value") orelse { + self.log.addError(self.p.source, loc, "<number> should have a \"value\" prop") catch unreachable; + self.args.append(invalid_value) catch unreachable; + return true; + }; + const value = props[value_i].value orelse invalid_value; + + switch (value.data) { + .e_jsx_element => |el| { + return self.writeElement(el.*); + }, + .e_string => { + self.log.addError(self.p.source, loc, "<number> should not be a string.") catch unreachable; + self.args.appendAssumeCapacity(invalid_value); + }, + .e_boolean => { + // Booleans are cooerced to numbers + self.args.appendAssumeCapacity( + Expr{ + .data = .{ + .e_number = E.Number{ + .value = @intToFloat(f64, @boolToInt(value.data.e_boolean.value)), + }, + }, + .loc = value.loc, + }, + ); + }, + .e_missing => { + self.args.appendAssumeCapacity(invalid_value); + }, + // null and undefined literals are coerced to 0 + .e_null, .e_undefined => { + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_number = .{ .value = 0 } }, .loc = value.loc }); + }, + // <number>123</number> + .e_number => { + // Numbers are cooerced to booleans + self.args.appendAssumeCapacity(value); + }, + // these ones are not statically analyzable so we just leave them in as-is + .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.appendAssumeCapacity(self.p.visitExpr(value)); + }, + // everything else is invalid + else => { + self.log.addError(self.p.source, value.loc, "<number value> should be a number, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable; + self.args.appendAssumeCapacity(invalid_value); + }, + } + + return true; + }, + Tag.e_big_int => { + self.args.ensureUnusedCapacity(2) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_big_int) }); + const invalid_value = Expr{ .data = .{ .e_big_int = &E.BigInt.empty }, .loc = loc }; + const value_i = indexOfPropertyByName(props, "value") orelse { + self.log.addError(self.p.source, loc, "<big-int> should have a \"value\" prop") catch unreachable; + self.args.append(invalid_value) catch unreachable; + return true; + }; + const value = props[value_i].value orelse invalid_value; + + switch (value.data) { + .e_jsx_element => |el| { + return self.writeElement(el.*); + }, + .e_string => |str| { + self.args.appendAssumeCapacity(Expr.alloc(self.allocator, E.BigInt, E.BigInt{ .value = std.mem.trimRight(u8, str.utf8, "n") }, value.loc)); + }, + .e_big_int => |bigint| { + self.args.appendAssumeCapacity(value); + }, + .e_missing => { + self.args.appendAssumeCapacity(invalid_value); + }, + // null and undefined literals are coerced to 0 + .e_null, .e_undefined => { + self.args.appendAssumeCapacity(Expr{ .data = .{ .e_big_int = &E.BigInt.empty }, .loc = value.loc }); + }, + // these ones are not statically analyzable so we just leave them in as-is + .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.appendAssumeCapacity(self.p.visitExpr(value)); + }, + // everything else is invalid + else => { + self.log.addError(self.p.source, value.loc, "\"value\" should be a BigInt, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable; + self.args.appendAssumeCapacity(invalid_value); + }, + } + + return true; + }, + Tag.e_array => { + self.args.ensureUnusedCapacity(2 + children.len) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_array) }); + const children_count = @truncate(u16, children.len); + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = .{ .e_number = E.Number{ .value = @intToFloat(f64, children_count) } } }); + + var old_parent = self.parent_tag; + self.parent_tag = Tag.e_array; + defer self.parent_tag = old_parent; + for (children) |child, i| { + switch (child.data) { + .e_jsx_element => |el| { + if (!self.writeElement(el.*)) return false; + }, + // TODO: handle when simplification changes the expr type + .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + const visited_expr = self.p.visitExpr(child); + switch (visited_expr.data) { + .e_jsx_element => |el| { + if (!self.writeElement(el.*)) return false; + }, + .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.append(visited_expr) catch unreachable; + }, + else => { + self.log.addError(self.p.source, visited_expr.loc, "<array> should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = visited_expr.loc }) catch unreachable; + }, + } + }, + else => { + self.log.addError(self.p.source, child.loc, "<array> should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable; + }, + } + } + + return true; + }, + Tag.e_object => { + self.args.ensureUnusedCapacity(2 + children.len) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_object) }); + const children_count = @truncate(u16, children.len); + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = .{ .e_number = E.Number{ .value = @intToFloat(f64, children_count) } } }); + + var old_parent = self.parent_tag; + self.parent_tag = Tag.e_object; + defer self.parent_tag = old_parent; + + for (children) |child, i| { + switch (child.data) { + .e_jsx_element => |el| { + if (!self.writeElementWithValidTagList(el.*, comptime Tag.Validator.valid_object_tags)) return false; + }, + .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + const visited = self.p.visitExpr(child); + switch (visited.data) { + .e_jsx_element => |el| { + if (!self.writeElementWithValidTagList(el.*, comptime Tag.Validator.valid_object_tags)) return false; + }, + .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.append(visited) catch unreachable; + }, + else => { + self.log.addError(self.p.source, child.loc, "<object> should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable; + }, + } + }, + else => { + self.log.addError(self.p.source, child.loc, "<object> should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable; + }, + } + } + + return true; + }, + + Tag.g_property => { + const name_property = propertyValueNamed(props, "name"); + const value_property = propertyValueNamed(props, "value"); + const init_property = propertyValueNamed(props, "init"); + + var old_parent = self.parent_tag; + if (old_parent != .e_object) { + self.args.append(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.g_property) }) catch unreachable; + } + + self.parent_tag = Tag.g_property; + defer self.parent_tag = old_parent; + + var is_spread = false; + if (value_property) |prop| { + switch (prop.data) { + .e_jsx_element => |el| { + if (!self.writeElement(el.*)) return false; + }, + .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.append(self.p.visitExpr(prop)) catch unreachable; + }, + else => { + self.log.addError(self.p.source, prop.loc, "value should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable; + }, + } + } else { + self.args.append(Expr{ .data = comptime Tag.ids.get(.e_undefined), .loc = loc }) catch unreachable; + } + + if (init_property) |prop| { + switch (prop.data) { + .e_jsx_element => |el| { + if (!self.writeElement(el.*)) return false; + }, + + .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.append(self.p.visitExpr(prop)) catch unreachable; + }, + else => { + self.log.addError(self.p.source, prop.loc, "init should only contain other jsx elements") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable; + }, + } + } else { + self.args.append(Expr{ .data = comptime Tag.ids.get(.e_undefined), .loc = loc }) catch unreachable; + } + + if (name_property) |prop| { + switch (prop.data) { + .e_jsx_element => |el| { + if (!self.writeElement(el.*)) return false; + }, + .e_string => |str| { + self.args.append(prop) catch unreachable; + }, + .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.append(self.p.visitExpr(prop)) catch unreachable; + }, + else => { + self.log.addError(self.p.source, prop.loc, "should only contain other jsx elements or a string") catch unreachable; + self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable; + }, + } + } + + return true; + }, + Tag.e_string => { + self.args.ensureUnusedCapacity(2) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_string) }); + const invalid_value = Expr{ .data = .{ .e_string = &E.String.empty }, .loc = loc }; + const value_i = indexOfPropertyByName(props, "value") orelse { + self.log.addError(self.p.source, loc, "<string> should have a \"value\" prop") catch unreachable; + self.args.append(invalid_value) catch unreachable; + return true; + }; + const value = props[value_i].value orelse invalid_value; + + switch (value.data) { + .e_jsx_element => |el| { + return self.writeElement(el.*); + }, + .e_string => { + self.args.appendAssumeCapacity(value); + }, + .e_missing => { + self.args.appendAssumeCapacity(invalid_value); + }, + // null is cooerced to "null" + .e_null => { + self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = &E.String.@"null" } }); + }, + // undefined is cooerced to "undefined" + .e_undefined => { + self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = &E.String.@"undefined" } }); + }, + .e_boolean => |boolean| { + self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = if (boolean.value) &E.String.@"true" else &E.String.@"false" } }); + }, + // these ones are not statically analyzable so we just leave them in as-is + .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.appendAssumeCapacity(self.p.visitExpr(value)); + }, + // everything else is invalid + else => { + self.log.addError(self.p.source, value.loc, "<string value> should be a string, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable; + self.args.appendAssumeCapacity(invalid_value); + }, + } + }, + Tag.e_reg_exp => { + self.args.ensureUnusedCapacity(2) catch unreachable; + self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_reg_exp) }); + const invalid_value = Expr{ .data = .{ .e_reg_exp = &E.RegExp.empty }, .loc = loc }; + + const value_i = indexOfPropertyByName(props, "value") orelse { + self.log.addError(self.p.source, loc, "<regex> should have a \"value\" prop") catch unreachable; + self.args.append(invalid_value) catch unreachable; + return true; + }; + + const value = props[value_i].value orelse invalid_value; + + switch (value.data) { + .e_string => |str| { + self.args.appendAssumeCapacity(Expr.alloc(self.allocator, E.RegExp, E.RegExp{ .value = str.utf8 }, value.loc)); + }, + .e_reg_exp => { + self.args.appendAssumeCapacity(value); + }, + .e_missing, .e_null, .e_undefined => { + self.args.appendAssumeCapacity(invalid_value); + }, + // these ones are not statically analyzable so we just leave them in as-is + .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => { + self.args.appendAssumeCapacity(self.p.visitExpr(value)); + }, + // everything else is invalid + else => { + self.log.addError(self.p.source, value.loc, "<regex value> should be a string, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable; + self.args.appendAssumeCapacity(invalid_value); + }, + } + + return true; + }, + // Tag.e_jsx_element => unreachable, + // Tag.e_identifier => { + // // self.args.ensureUnusedCapacity(2) catch unreachable; + // Global.notimpl(); + // }, + // Tag.e_import_identifier => { + // Global.notimpl(); + // }, + // Tag.e_private_identifier => { + // Global.notimpl(); + // }, + + // Tag.e_unary => { + + // }, + // Tag.e_binary => {}, + + // Tag.e_function => {}, + // Tag.e_new_target => {}, + // Tag.e_import_meta => {}, + // Tag.e_call => {}, + // Tag.e_dot => {}, + // Tag.e_index => {}, + // Tag.e_arrow => {}, + + // Tag.e_spread => {}, + + // Tag.e_template_part => {}, + // Tag.e_template => {}, + // Tag.e_regex => {}, + // Tag.e_await => {}, + // Tag.e_yield => {}, + // Tag.e_if => {}, + // Tag.e_import => {}, + + // Tag.e_class => {}, + // Tag.e_require => {}, + // Tag.s_import => {}, + + // Tag.s_block => {}, + + // The valueless ones + Tag.e_super, Tag.e_null, Tag.e_undefined, Tag.e_missing, Tag.inline_true, Tag.inline_false, Tag.e_this => { + self.args.append(Expr{ .loc = loc, .data = Tag.ids.get(tag) }) catch unreachable; + }, + else => Global.panic("Tag \"{s}\" is not implemented yet.", .{@tagName(tag)}), + } + + return true; + } + + pub fn writeFunctionCall(self: *JSXWriter, element: E.JSXElement) Expr { + if (element.tag) |tag_expr| { + switch (tag_expr.data) { + .e_string => |str| { + self.p.recordUsage(self.bun_jsx_ref); + _ = self.writeElement(element); + var call_args = self.p.allocator.alloc(Expr, 1) catch unreachable; + call_args[0] = Expr.alloc(self.p.allocator, E.Array, E.Array{ .items = self.args.items }, tag_expr.loc); + + return Expr.alloc( + self.p.allocator, + E.Call, + E.Call{ + .target = Expr{ + .data = .{ + .e_identifier = self.bun_identifier, + }, + .loc = tag_expr.loc, + }, + .can_be_unwrapped_if_unused = true, + .args = call_args, + }, + tag_expr.loc, + ); + }, + else => Global.panic("Not implemented yet top-level jsx element: {s}", .{@tagName(tag_expr.data)}), + } + } + + return Expr{ .data = .{ .e_missing = .{} }, .loc = logger.Loc.Empty }; + } + + pub fn writeRootElement(self: JSXWriter, element: E.JSXElement) Expr { + var tag = element.tag orelse E.Array{ .items = &.{} }; + switch (tag.data) { + .e_string, .e_array => {}, + else => {}, + } + } + + fn writeElementWithValidTagList(self: *JSXWriter, element: E.JSXElement, comptime valid_tags: Tag.Validator.List) bool { + const tag_expr = element.tag orelse return false; + if (tag_expr.data != .e_string) return false; + const str = tag_expr.data.e_string; + var p = self.p; + + const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse { + if (!str.isUTF8()) { + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable; + } else { + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable; + } + return false; + }; + + if (!valid_tags.get(node_type)) { + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid here", .{str.utf8}) catch unreachable; + } + + return self.writeNodeType(node_type, element.properties, element.children, tag_expr.loc); + } + + pub fn writeElement(self: *JSXWriter, element: E.JSXElement) bool { + const tag_expr = element.tag orelse return false; + if (tag_expr.data != .e_string) return false; + const str = tag_expr.data.e_string; + var p = self.p; + + const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse { + if (!str.isUTF8()) { + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable; + } else { + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable; + } + return false; + }; + + return self.writeNodeType(node_type, element.properties, element.children, tag_expr.loc); + } + }; + } + pub const Writer = struct { log: *logger.Log, exception: JSCBase.ExceptionValueRef = null, @@ -4510,6 +5285,25 @@ pub const Macro = struct { errored: bool = false, allocator: *std.mem.Allocator, loc: logger.Loc, + args_value: JSC.JSValue, + args_i: u32 = 0, + args_len: u32 = 0, + + pub inline fn eatArg(this: *Writer) ?JSC.JSValue { + if (this.args_i >= this.args_len) return null; + const i = this.args_i; + this.args_i += 1; + return JSC.JSObject.getIndex(this.args_value, JavaScript.VirtualMachine.vm.global, i); + } + + pub inline fn peekArg(this: *Writer) ?JSC.JSValue { + if (this.args_i >= this.args_len) return null; + return JSC.JSObject.getIndex(this.args_value, JavaScript.VirtualMachine.vm.global, this.args_i); + } + + pub inline fn nextJSValue(this: *Writer) ?JSC.JSValue { + return this.eatArg(); + } pub const TagOrJSNode = union(TagOrNodeType) { tag: JSNode.Tag, @@ -4525,13 +5319,13 @@ pub const Macro = struct { pub fn fromJSValueRef(writer: *Writer, ctx: js.JSContextRef, value: js.JSValueRef) TagOrJSNode { switch (js.JSValueGetType(ctx, value)) { js.JSType.kJSTypeNumber => { - const tag_int = @floatToInt(u8, JSC.JSValue.fromRef(first_arg).asNumber()); - if (tag_int < JSNode.min_tag or tag_int > JSNode.max_tag) { + const tag_int = @floatToInt(u8, JSC.JSValue.fromRef(value).asNumber()); + if (tag_int < Tag.min_tag or tag_int > Tag.max_tag) { throwTypeError(ctx, "Node type has invalid value", writer.exception); writer.errored = true; return TagOrJSNode{ .invalid = .{} }; } - return TagOrJSNode{ .tag = @intToEnum(JSNode.Tag, tag) }; + return TagOrJSNode{ .tag = @intToEnum(JSNode.Tag, tag_int) }; }, js.JSType.kJSTypeObject => { if (JSCBase.GetJSPrivateData(JSNode, value)) |node| { @@ -4546,60 +5340,166 @@ pub const Macro = struct { }, } } + + pub fn fromJSValue(writer: *Writer, value: JSC.JSValue) TagOrJSNode { + return fromJSValueRef(writer, JavaScript.VirtualMachine.vm.global.ref(), value.asRef()); + } }; - fn writeFromJSWithTagInExpr(writer: *Writer, tag: JSNode.Tag, expr: *Expr, args: []const js.JSValueRef) ?u32 { + fn writeProperty(writer: *Writer, property: *G.Property) bool { + + // Property is + // value + // initializer + // if property value is an e.spread, then key is skipped + // key + + // value is first + var expect_key = true; + switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) { + TagOrJSNode.tag => |tag| { + var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } }; + + if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false; + property.value = switch (expr.data) { + .e_missing, .e_undefined => null, + else => expr, + }; + property.flags.is_spread = expr.data == .e_spread; + expect_key = property.value == null or !property.flags.is_spread; + }, + TagOrJSNode.node => |node| { + const expr = node.toExpr(); + property.value = switch (expr.data) { + .e_missing, .e_undefined => null, + else => expr, + }; + property.flags.is_spread = expr.data == .e_spread; + expect_key = property.value == null or !property.flags.is_spread; + }, + TagOrJSNode.invalid => { + return false; + }, + } + + switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) { + TagOrJSNode.tag => |tag| { + var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } }; + + if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false; + property.initializer = switch (expr.data) { + .e_missing, .e_undefined => null, + else => expr, + }; + }, + TagOrJSNode.node => |node| { + const expr = node.toExpr(); + property.initializer = switch (expr.data) { + .e_missing, .e_undefined => null, + else => expr, + }; + }, + TagOrJSNode.invalid => { + return false; + }, + } + + if (expect_key) { + var next_arg = writer.peekArg() orelse return false; + // its okay for property keys to literally be strings + // <property name="foo"> + if (next_arg.isString()) { + var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_string = &E.String.empty } }; + if (!writer.writeFromJSWithTagInExpr(JSNode.Tag.e_string, &expr)) return false; + property.key = expr; + } else { + switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) { + TagOrJSNode.tag => |tag| { + var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } }; + if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false; + property.key = expr; + }, + TagOrJSNode.node => |node| { + property.key = node.toExpr(); + }, + TagOrJSNode.invalid => { + return false; + }, + } + } + } + + return true; + } + + fn writeFromJSWithTagInExpr(writer: *Writer, tag: JSNode.Tag, expr: *Expr) bool { switch (tag) { - e_array => { - var items = std.ArrayList(Expr).init(writer.allocator); + .e_array => { // var e_array: E.Array = E.Array{ .items = writer.allocator.alloc(E.Array, args.len) catch return false }; - var i: u32 = 0; - while (i < args.len) { - switch (TagOrJSNode.fromJSValueRef(writer, writer.ctx, args[i])) { + const count = (writer.nextJSValue() orelse return false).toU16(); + var i: u16 = 0; + var items = writer.allocator.alloc(ExprNodeIndex, count) catch return false; + + while (i < items.len) : (i += 1) { + switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) { TagOrJSNode.tag => |tag_| { - items.ensureUnusedCapacity(1) catch return null; - items.expandToCapacity(); + if (!writer.writeFromJSWithTagInExpr(tag_, &items[i])) return false; i += 1; - i += writer.writeFromJSWithTagInExpr(tag_, &items[i], args[i..]) orelse return null; }, TagOrJSNode.node => |node_| { const node: JSNode = node_; switch (node.data) { JSNode.Tag.s_import => |import| { - return null; + return false; }, else => { - items.append(node.toExpr()) catch return null; - i += 1; + items[i] = node.toExpr(); }, } }, TagOrJSNode.invalid => { - return null; + return false; }, } } - expr.* = Expr.alloc(writer.allocator, E.Array, E.Array{ .items = items.items }, writer.loc); - return i; + expr.* = Expr.alloc(writer.allocator, E.Array, E.Array{ .items = items }, writer.loc); + return true; + }, + .e_boolean => { + expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{ + .value = JSC.JSValue.toBoolean(writer.nextJSValue() orelse return false), + } } }; + return true; + }, + .inline_true => { + expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{ .value = true } } }; + return true; }, - e_boolean => { - expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = JSC.JSValue.toBoolean(JSValue.fromRef(args[0])) } }; - return 1; + .inline_false => { + expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{ .value = false } } }; + return true; }, - e_null => { + .e_null => { expr.* = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } }; - return 0; + return true; }, - e_undefined => { - expr.* = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Undefined{} } }; - return 0; + .e_undefined => { + expr.* = Expr{ .loc = writer.loc, .data = .{ .e_undefined = E.Undefined{} } }; + return true; }, - e_number => { - expr.* = Expr{ .loc = writer.loc, .data = .{ .e_number = JSC.JSValue.asNumber(JSValue.fromRef(args[0])) } }; - return 1; + .e_number => { + expr.* = Expr{ + .loc = writer.loc, + .data = .{ + .e_number = .{ + .value = JSC.JSValue.asNumber(writer.nextJSValue() orelse return false), + }, + }, + }; + return true; }, - e_string => { - var wtf_string = JSC.JSValue.toWTFString(JSValue.fromRef(args[0]), JavaScript.VirtualMachine.vm.global); + .e_string => { + var wtf_string = JSC.JSValue.toWTFString(writer.nextJSValue() orelse return false, JavaScript.VirtualMachine.vm.global); if (wtf_string.isEmpty()) { expr.* = Expr{ .loc = writer.loc, @@ -4614,102 +5514,93 @@ pub const Macro = struct { } else { unreachable; } - return 1; + return true; }, - e_reg_exp => { - var jsstring = js.JSValueToStringCopy(writer.ctx, args[0], writer.exception); + .e_reg_exp => { + var jsstring = js.JSValueToStringCopy(writer.ctx, (writer.eatArg() orelse return false).asRef(), writer.exception); + defer js.JSStringRelease(jsstring); + const len = js.JSStringGetLength(jsstring); - var str = try writer.allocator.alloc(u8, len + 1); - const outlen = js.JSStringGetUTF8CString(jsstring, str, len + 1); + var str = writer.allocator.alloc(u8, len + 1) catch unreachable; + const outlen = js.JSStringGetUTF8CString(jsstring, str.ptr, len + 1); expr.* = Expr.alloc(writer.allocator, E.RegExp, E.RegExp{ .value = str[0..outlen] }, writer.loc); - return 1; + return true; }, - e_object => { - var i: u32 = 0; + .e_object => { + const len = (writer.nextJSValue() orelse return false).toU16(); - const len = @truncate(u16, JSC.JSValue.fromRef(args[0]).toInt32()); - }, + var properties = writer.allocator.alloc(G.Property, len) catch return false; + var property_i: u16 = 0; - e_call => {}, + while (property_i < properties.len) : (property_i += 1) { + switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) { + TagOrJSNode.tag => |tag_| { + if (tag_ != JSNode.Tag.g_property) return false; - e_dot => {}, - e_index => {}, - e_identifier => {}, - e_import_identifier => {}, + if (!writer.writeProperty( + &properties[property_i], + )) return false; + }, + TagOrJSNode.node => |node_| { + const node: JSNode = node_; + switch (node.data) { + .g_property => |property| { + properties[property_i] = property.*; + }, + else => { + return false; + }, + } + }, + TagOrJSNode.invalid => { + return false; + }, + } + } - e_big_int => {}, - e_object => {}, - e_spread => {}, + return true; + }, + else => { + return false; + }, - e_template_part => {}, - e_template => {}, + // .e_call => {}, - e_await => {}, - e_yield => {}, - e_if => {}, - e_import => {}, - e_this => {}, - e_class => {}, - s_import => {}, - } - } + // .e_dot => {}, + // .e_index => {}, + // .e_identifier => {}, + // .e_import_identifier => {}, - fn writeFromJSWithTag(writer: *Writer, tag: JSNode.Tag, node: *JSNode, args: []const js.JSValueRef) bool { - switch (tag) { - e_array => { - var e_array: E.Array = E.Array{ .items = writer.allocator.alloc(E.Array, args.len) catch return false }; + // .e_spread => {}, - var i: u32 = 0; - while (i < args.len) : (i += 1) {} - }, - e_boolean => {}, - e_null => {}, - e_undefined => {}, - e_call => {}, - e_dot => {}, - e_index => {}, - e_identifier => {}, - e_import_identifier => {}, - e_number => {}, - e_big_int => {}, - e_object => {}, - e_spread => {}, - e_string => {}, - e_template_part => {}, - e_template => {}, - e_reg_exp => {}, - e_await => {}, - e_yield => {}, - e_if => {}, - e_import => {}, - e_this => {}, - e_class => {}, - s_import => {}, + // .e_template_part => {}, + // .e_template => {}, + + // .e_await => {}, + // .e_yield => {}, + // .e_if => {}, + // .e_import => {}, + // .e_this => {}, + // .e_class => {}, + // s_import => {}, } + + return false; } - pub fn writeFromJS(writer: *Writer, node: *JSNode, args: []const js.JSValueRef) bool { - if (writer.errored) return false; - const first_arg = args[0]; - - switch (js.JSValueGetType(ctx, first_arg)) { - js.JSType.kJSTypeNumber => { - const value = @floatToInt(u8, JSC.JSValue.fromRef(first_arg).asNumber()); - const tag = std.meta.intToEnum(JSNode.Tag, value) catch { - throwTypeError(ctx, "Node type has invalid value", writer.exception); - writer.errored = true; - return false; - }; - if (writer.writeFromJSWithTag(writer, tag, node, args[1..])) { - return true; - } + pub fn writeFromJS(writer: *Writer) ?JSNode { + switch (TagOrJSNode.fromJSValueRef(writer, writer.ctx, (writer.eatArg() orelse return null).asRef())) { + TagOrJSNode.tag => |tag| { + var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } }; - return false; + if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return null; + return JSNode.initExpr(expr); }, - js.JSType.kJSTypeObject => {}, - else => { - throwTypeError(writer.ctx, "Invalid Bun AST", writer.exception); - return false; + TagOrJSNode.node => |node| { + return node; + }, + TagOrJSNode.invalid => { + return null; }, } } @@ -4728,29 +5619,59 @@ pub const Macro = struct { JSCBase.JSError(JSCBase.getAllocator(ctx), msg, .{}, ctx, exception); } + pub const BunJSXCallbackFunction = JSCBase.NewClass( + void, + .{ .name = "bunJSX" }, + .{ .call = .{ .rfn = createFromJavaScript } }, + .{}, + ); + pub fn createFromJavaScript( - this: *JSExpr, + this: void, ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - if (arguments.len != 3 or !js.JSValueIsNumber(ctx, arguments[0]) or !js.JSValueIsObject(ctx, arguments[1]) or !js.JSValueIsArray(ctx, arguments[2])) { - this.throwTypeError(ctx, "Invalid arguments for JSExpr", exception); + if (arguments.len != 1 or !js.JSValueIsArray(ctx, arguments[0])) { + throwTypeError(ctx, "bunJSX requires one array argument", exception); return null; } + + js.JSValueProtect(ctx, arguments[0]); + defer Output.flush(); + const args_value = JSC.JSValue.fromRef(arguments[0]); + var writer = Writer{ + .log = JavaScript.VirtualMachine.vm.log, + .ctx = ctx, + .loc = logger.Loc.Empty, + .allocator = JSCBase.getAllocator(ctx), + .exception = exception, + .args_value = args_value, + .args_len = args_value.getLengthOfArray(JavaScript.VirtualMachine.vm.global), + .args_i = 0, + .errored = false, + }; + + if (writer.writeFromJS()) |node| { + var ptr = writer.allocator.create(JSNode) catch unreachable; + ptr.* = node; + return JSNode.Class.make(ctx, ptr); + } + + return null; } pub fn toString( - this: *JSExpr, + this: *JSNode, ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - switch (this.expr.data) { + switch (this.data) { .e_string => |str| { if (str.isBlank()) { return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef(); @@ -4782,31 +5703,31 @@ pub const Macro = struct { } pub fn getTag( - this: *JSExpr, + this: *JSNode, ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, exception: js.ExceptionRef, ) js.JSObjectRef { - return JSC.JSValue.jsNumberFromU16(@intCast(u16, @enumToInt(std.meta.activeTag(this.expr.data)))).asRef(); + return JSC.JSValue.jsNumberFromU16(@intCast(u16, @enumToInt(std.meta.activeTag(this.data)))).asRef(); } pub fn getTagName( - this: *JSExpr, + this: *JSNode, ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, exception: js.ExceptionRef, ) js.JSObjectRef { - return JSC.ZigString.init(@tagName(this.expr.data)).toValue(JavaScript.VirtualMachine.vm.global).asRef(); + return JSC.ZigString.init(@tagName(this.data)).toValue(JavaScript.VirtualMachine.vm.global).asRef(); } pub fn getPosition( - this: *JSExpr, + this: *JSNode, ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, exception: js.ExceptionRef, ) js.JSObjectRef { - return JSC.JSValue.jsNumberFromInt32(this.expr.loc.start).asRef(); + return JSC.JSValue.jsNumberFromInt32(this.loc.start).asRef(); } }; @@ -4863,8 +5784,8 @@ pub const Macro = struct { } pub const Runner = struct { - threadlocal var args_buf: [32]js.JSObjectRef = undefined; - threadlocal var expr_nodes_buf: [32]JSExpr = undefined; + threadlocal var args_buf: [2]js.JSObjectRef = undefined; + threadlocal var expr_nodes_buf: [1]JSNode = undefined; threadlocal var exception_holder: Zig.ZigException.Holder = undefined; pub fn run( macro: Macro, @@ -4879,20 +5800,13 @@ pub const Macro = struct { if (comptime isDebug) Output.prettyln("<r><d>[macro]<r> call <d><b>{s}<r>", .{function_name}); exception_holder = Zig.ZigException.Holder.init(); - expr_nodes_buf[0] = JSExpr{ .expr = caller }; - args_buf[0] = JSExpr.Class.make( + expr_nodes_buf[0] = JSNode.initExpr(caller); + args_buf[0] = JSNode.Class.make( macro.vm.global.ref(), &expr_nodes_buf[0], ); - for (args) |arg, i| { - expr_nodes_buf[i + 1] = JSExpr{ .expr = arg }; - args_buf[i + 1] = - JSExpr.Class.make( - macro.vm.global.ref(), - &expr_nodes_buf[i + 1], - ); - } - args_buf[args.len + 2] = null; + + args_buf[1] = null; var macro_callback = macro.vm.macros.get(id) orelse return caller; var result = js.JSObjectCallAsFunctionReturnValue(macro.vm.global.ref(), macro_callback, null, args.len + 1, &args_buf); @@ -4906,7 +5820,11 @@ pub const Macro = struct { const value = promise.result(macro.vm.global.vm()); - return caller; + if (JSCBase.GetJSPrivateData(JSNode, value.asObjectRef())) |node| { + return node.toExpr(); + } else { + return Expr{ .data = .{ .e_missing = .{} }, .loc = caller.loc }; + } } }; }; diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 916e3e061..52c21c156 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -22,6 +22,9 @@ const JSXFactoryName = "JSX"; const JSXAutomaticName = "jsx_module"; const MacroRefs = std.AutoArrayHashMap(Ref, u32); +const BunJSX = struct { + pub threadlocal var bun_jsx_identifier: E.Identifier = undefined; +}; pub fn ExpressionTransposer( comptime Kontext: type, visitor: fn (ptr: *Kontext, arg: Expr, state: anytype) Expr, @@ -88,6 +91,12 @@ pub const ImportScanner = struct { .s_import => |st| { var record: *ImportRecord = &p.import_records.items[st.import_record_index]; + if (strings.eqlComptime(record.path.namespace, "macro")) { + record.is_unused = true; + record.path.is_disabled = true; + continue; + } + // The official TypeScript compiler always removes unused imported // symbols. However, we deliberately deviate from the official // TypeScript compiler's behavior doing this in a specific scenario: @@ -1996,7 +2005,7 @@ pub const Parser = struct { } // Auto-import JSX - if (p.options.jsx.parse) { + if (ParserType.jsx_transform_type == .react) { const jsx_filename_symbol = p.symbols.items[p.jsx_filename.ref.inner_index]; { @@ -2693,8 +2702,9 @@ const ImportItemForNamespaceMap = std.StringArrayHashMap(LocRef); pub fn NewParser( comptime js_parser_features: ParserFeatures, ) type { + const js_parser_jsx = if (FeatureFlags.force_macro) JSXTransformType.macro else js_parser_features.jsx; const is_typescript_enabled = js_parser_features.typescript; - const is_jsx_enabled = js_parser_features.jsx != .none; + const is_jsx_enabled = js_parser_jsx != .none; const only_scan_imports_and_do_not_visit = js_parser_features.scan_only; const is_react_fast_refresh_enabled = js_parser_features.react_fast_refresh; @@ -2706,7 +2716,7 @@ pub fn NewParser( // public only because of Binding.ToExpr return struct { const P = @This(); - pub const jsx_transform_type: JSXTransformType = js_parser_features.jsx; + pub const jsx_transform_type: JSXTransformType = js_parser_jsx; macro_refs: MacroRefs = undefined, allocator: *std.mem.Allocator, options: Parser.Options, @@ -2796,6 +2806,8 @@ pub fn NewParser( // only applicable when is_react_fast_refresh_enabled jsx_refresh_runtime: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None }, + bun_jsx_ref: Ref = Ref.None, + // Imports (both ES6 and CommonJS) are tracked at the top level import_records: ImportRecordList, import_records_for_current_part: List(u32), @@ -3627,6 +3639,8 @@ pub fn NewParser( } }, .macro => { + p.bun_jsx_ref = p.declareSymbol(.other, logger.Loc.Empty, "bunJSX") catch unreachable; + BunJSX.bun_jsx_identifier = E.Identifier{ .ref = p.bun_jsx_ref, .can_be_removed_if_unused = true }; p.jsx_fragment = p.declareGeneratedSymbol(.other, "Fragment") catch unreachable; }, else => {}, @@ -10733,7 +10747,8 @@ pub fn NewParser( }); } - fn visitExpr(p: *P, expr: Expr) Expr { + // public for JSNode.JSXWriter usage + pub fn visitExpr(p: *P, expr: Expr) Expr { if (only_scan_imports_and_do_not_visit) { @compileError("only_scan_imports_and_do_not_visit must not run this."); } @@ -10925,52 +10940,9 @@ pub fn NewParser( .e_jsx_element => |e_| { switch (comptime jsx_transform_type) { .macro => { - const IdentifierOrNodeType = union(Tag) { - identifier: Expr, - expression: Expr.Tag, - pub const Tag = enum { identifier, expression }; - }; - const tag: IdentifierOrNodeType = tagger: { - if (e_.tag) |_tag| { - switch (_tag.data) { - .e_string => |str| { - if (Expr.Tag.find(str.utf8)) |tagname| { - break :tagger IdentifierOrNodeType{ .expression = tagname }; - } - - p.log.addErrorFmt( - p.source, - expr.loc, - p.allocator, - "Invalid expression tag: \"<{s}>\". Valid tags are:\n" ++ Expr.Tag.valid_names_list ++ "\n", - .{str.utf8}, - ) catch unreachable; - break :tagger IdentifierOrNodeType{ .identifier = p.visitExpr(_tag) }; - }, - else => { - break :tagger IdentifierOrNodeType{ .identifier = p.visitExpr(_tag) }; - }, - } - } else { - break :tagger IdentifierOrNodeType{ .expression = Expr.Tag.e_array }; - } - }; - - for (e_.properties) |property, i| { - if (property.kind != .spread) { - e_.properties[i].key = p.visitExpr(e_.properties[i].key.?); - } - - if (property.value != null) { - e_.properties[i].value = p.visitExpr(e_.properties[i].value.?); - } - - if (property.initializer != null) { - e_.properties[i].initializer = p.visitExpr(e_.properties[i].initializer.?); - } - } - - return p.e(E.Missing{}, expr.loc); + const WriterType = js_ast.Macro.JSNode.NewJSXWriter(P); + var writer = WriterType.initWriter(p, &BunJSX.bun_jsx_identifier); + return writer.writeFunctionCall(e_.*); }, .react => { const tag: Expr = tagger: { @@ -12571,7 +12543,7 @@ pub fn NewParser( data.value.expr = p.visitExpr(expr); // // Optionally preserve the name - data.value.expr = p.maybeKeepExprSymbolName(expr, "default", was_anonymous_named_expr); + data.value.expr = p.maybeKeepExprSymbolName(data.value.expr, "default", was_anonymous_named_expr); // Discard type-only export default statements if (is_typescript_enabled) { @@ -12594,7 +12566,7 @@ pub fn NewParser( if (p.options.enable_bundling) { var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, expr.loc); - export_default_args[1] = expr; + export_default_args[1] = data.value.expr; stmts.append(p.s(S.SExpr{ .value = p.callRuntime(expr.loc, "__exportDefault", export_default_args) }, expr.loc)) catch unreachable; return; } @@ -15250,7 +15222,7 @@ const JSParserMacro = NewParser(.{ .jsx = .macro, }); const TSParserMacro = NewParser(.{ - .jsx = .react, + .jsx = .macro, .typescript = true, }); diff --git a/src/js_printer.zig b/src/js_printer.zig index 3bdeb09ba..983444d1a 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -567,7 +567,7 @@ pub fn NewPrinter( std.fmt.formatFloatScientific(float, .{}, p) catch unreachable; } - pub fn printQuotedUTF16(e: *Printer, text: JavascriptString, quote: u8) void { + pub fn printQuotedUTF16(e: *Printer, text: []const u16, quote: u8) void { // utf-8 is a max of 4 bytes // we leave two extra chars for "\" and "u" var temp = [6]u8{ 0, 0, 0, 0, 0, 0 }; @@ -872,7 +872,7 @@ pub fn NewPrinter( } } - pub inline fn canPrintIdentifierUTF16(p: *Printer, name: JavascriptString) bool { + pub inline fn canPrintIdentifierUTF16(p: *Printer, name: []const u16) bool { // TODO: fix this // this is commented out because something isn't quite right // the problem may lie in isIdentifierUTF16, or it may lie in how these are allocated. @@ -3707,7 +3707,7 @@ pub fn NewPrinter( p.print(identifier); } - pub fn printIdentifierUTF16(p: *Printer, name: JavascriptString) !void { + pub fn printIdentifierUTF16(p: *Printer, name: []const u16) !void { var temp = [_]u8{ 0, 0, 0, 0, 0, 0 }; const n = name.len; var i: usize = 0; diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 1a144a747..c9437b757 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -437,11 +437,11 @@ pub fn index(self: string, str: string) i32 { } } -pub fn eqlUtf16(comptime self: string, other: JavascriptString) bool { +pub fn eqlUtf16(comptime self: string, other: []const u16) bool { return std.mem.eql(u16, std.unicode.utf8ToUtf16LeStringLiteral(self), other); } -pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: JavascriptString) !string { +pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: []const u16) !string { var temp: [4]u8 = undefined; var list = std.ArrayList(u8).initCapacity(allocator, js.len) catch unreachable; var i: usize = 0; @@ -461,7 +461,7 @@ pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: JavascriptString) !string } // Check utf16 string equals utf8 string without allocating extra memory -pub fn utf16EqlString(text: []u16, str: string) bool { +pub fn utf16EqlString(text: []const u16, str: string) bool { if (text.len > str.len) { // Strings can't be equal if UTF-16 encoding is longer than UTF-8 encoding return false; @@ -603,7 +603,7 @@ pub fn trim(slice: anytype, values_to_strip: []const u8) @TypeOf(slice) { return slice[begin..end]; } -pub fn containsNonBmpCodePointUTF16(_text: JavascriptString) bool { +pub fn containsNonBmpCodePointUTF16(_text: []const u16) bool { const n = _text.len; if (n > 0) { var i: usize = 0; |