mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
okie dokie
This commit is contained in:
@@ -93,6 +93,11 @@ pub fn VisitExpr(
|
||||
}
|
||||
|
||||
// Transform Response -> BakeResponse for server-side code
|
||||
// TODO: this does the incorrect thing in this case:
|
||||
// ```
|
||||
// const Response = 'ooga booga!'
|
||||
// console.log(Response)
|
||||
// ```
|
||||
if (p.options.features.server_components.isServerSide() and
|
||||
bun.strings.eqlComptime(name, "Response"))
|
||||
{
|
||||
@@ -101,6 +106,8 @@ pub fn VisitExpr(
|
||||
e_.ref = bake_response_result.ref;
|
||||
e_.must_keep_due_to_with_stmt = bake_response_result.is_inside_with_scope;
|
||||
|
||||
std.debug.print("{s}: Response -> SSRResponse\n", .{p.source.path.pretty});
|
||||
|
||||
// Handle the rest of the identifier processing with the new ref
|
||||
return p.handleIdentifier(expr.loc, e_, "SSRResponse", IdentifierOpts{
|
||||
.assign_target = in.assign_target,
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
|
||||
namespace Bun {
|
||||
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsBakeResponseConstructorRender);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsBakeResponseConstructorRedirect);
|
||||
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetSymbolFor);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetType);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetKey);
|
||||
@@ -36,36 +33,114 @@ static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugInfo);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugStack);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugTask);
|
||||
|
||||
extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructForSSR(JSC::JSGlobalObject*, JSC::CallFrame*, JSC::EncodedJSValue);
|
||||
extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructForSSR(JSC::JSGlobalObject*, JSC::CallFrame*, JSC::EncodedJSValue, int*);
|
||||
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructError(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
|
||||
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructJSON(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
|
||||
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRender(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
|
||||
extern "C" SYSV_ABI JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ResponseClass__constructRedirect(JSC::JSGlobalObject*, JSC::CallFrame*) SYSV_ABI;
|
||||
extern JSC_CALLCONV size_t Response__estimatedSize(void* ptr);
|
||||
|
||||
bool isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
|
||||
// React does this:
|
||||
// export const REACT_LEGACY_ELEMENT_TYPE: symbol = Symbol.for('react.element');
|
||||
// export const REACT_ELEMENT_TYPE: symbol = renameElementSymbol
|
||||
// ? Symbol.for('react.transitional.element')
|
||||
// : REACT_LEGACY_ELEMENT_TYPE;
|
||||
|
||||
// TODO: cache these, i cri everytim
|
||||
auto react_legacy_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.element"_s));
|
||||
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
|
||||
|
||||
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
|
||||
|
||||
// TODO: primitive values (strings, numbers, booleans, null, undefined) are also valid
|
||||
if (value.isObject()) {
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::JSObject* object = value.getObject();
|
||||
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
|
||||
JSC::JSValue typeofValue = object->get(globalObject, typeofProperty);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (typeofValue.isSymbol() && (typeofValue == react_legacy_element_symbol || typeofValue == react_element_symbol)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" bool JSC__JSValue__isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return isJSXElement(JSValue0, globalObject);
|
||||
}
|
||||
|
||||
extern JSC_CALLCONV JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES Response__createForSSR(Zig::GlobalObject* globalObject, void* ptr, uint8_t kind)
|
||||
{
|
||||
Structure* structure = globalObject->JSBakeResponseStructure();
|
||||
printf("Creating JSBakeResponse for kind: %d\n", kind);
|
||||
|
||||
JSBakeResponse* instance = JSBakeResponse::create(globalObject->vm(), globalObject, structure, ptr);
|
||||
|
||||
if (kind == JSBakeResponseKind::Render) {
|
||||
instance->kind(JSBakeResponseKind::Render);
|
||||
} else if (kind == JSBakeResponseKind::Redirect) {
|
||||
instance->kind(JSBakeResponseKind::Redirect);
|
||||
} else {
|
||||
// Should not be called with JSBakeResponseKind::Regular or anything
|
||||
// else
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
instance->setToThrow(globalObject, globalObject->vm());
|
||||
|
||||
return JSValue::encode(instance);
|
||||
}
|
||||
|
||||
static const HashTableValue JSBakeResponseConstructorTableValues[] = {
|
||||
{ "error"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructError, 0 } },
|
||||
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructJSON, 0 } },
|
||||
|
||||
{ "render"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBakeResponseConstructorRender, 1 } },
|
||||
{ "redirect"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBakeResponseConstructorRedirect, 1 } },
|
||||
{ "redirect"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructRedirect, 0 } },
|
||||
{ "render"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ResponseClass__constructRender, 0 } }
|
||||
|
||||
};
|
||||
|
||||
static const HashTableValue JSBakeResponsePrototypeTableValues[] = {
|
||||
{ "$$typeof"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetSymbolFor, nullptr } },
|
||||
{ "type"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetType, nullptr } },
|
||||
{ "key"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetKey, nullptr } },
|
||||
{ "props"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetProps, nullptr } },
|
||||
{ "_store"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetStore, nullptr } },
|
||||
{ "_owner"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetOwner, nullptr } },
|
||||
{ "_debugInfo"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugInfo, nullptr } },
|
||||
{ "_debugStack"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugStack, nullptr } },
|
||||
{ "_debugTask"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsBakeResponsePrototypeGetDebugTask, nullptr } }
|
||||
};
|
||||
|
||||
JSBakeResponse* JSBakeResponse::create(JSC::VM& vm, JSC::Structure* structure, void* ctx)
|
||||
JSBakeResponse* JSBakeResponse::create(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::Structure* structure, void* ctx)
|
||||
{
|
||||
JSBakeResponse* ptr = new (NotNull, JSC::allocateCell<JSBakeResponse>(vm)) JSBakeResponse(vm, structure, ctx);
|
||||
ptr->finishCreation(vm);
|
||||
|
||||
auto builtinNames = WebCore::builtinNames(vm);
|
||||
|
||||
// $$typeof = Symbol.for("react.transitional.element")
|
||||
ptr->putDirect(vm, builtinNames.$$typeofPublicName(), JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s)), 0);
|
||||
// type = false
|
||||
ptr->putDirect(vm, builtinNames.typePublicName(), JSC::jsNull(), 0);
|
||||
// key = null
|
||||
ptr->putDirect(vm, builtinNames.keyPublicName(), JSC::jsNull(), 0);
|
||||
// props = {}
|
||||
ptr->putDirect(vm, builtinNames.propsPublicName(), JSC::constructEmptyObject(globalObject), 0);
|
||||
|
||||
// _store = { _validated: 0 }
|
||||
JSObject* storeObject = JSC::constructEmptyObject(globalObject);
|
||||
auto validatedIdent = JSC::Identifier::fromString(vm, "validated"_s);
|
||||
storeObject->putDirect(vm, builtinNames.validatedPublicName(), jsNumber(0), 0);
|
||||
ptr->putDirect(vm, builtinNames._storePublicName(), storeObject, 0);
|
||||
|
||||
// _owner = null
|
||||
ptr->putDirect(vm, builtinNames._ownerPublicName(), JSC::jsNull(), 0);
|
||||
// _debugInfo = null
|
||||
ptr->putDirect(vm, builtinNames._debugInfoPublicName(), JSC::jsNull(), 0);
|
||||
// _debugStack = null
|
||||
ptr->putDirect(vm, builtinNames._debugStackPublicName(), JSC::jsNull(), 0);
|
||||
// _debugTask = null
|
||||
ptr->putDirect(vm, builtinNames._debugTaskPublicName(), JSC::jsNull(), 0);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -103,47 +178,6 @@ void JSBakeResponse::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSBakeResponse);
|
||||
|
||||
class JSBakeResponsePrototype final : public JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSNonFinalObject;
|
||||
|
||||
static JSBakeResponsePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
auto* ptr = new (NotNull, JSC::allocateCell<JSBakeResponsePrototype>(vm)) JSBakeResponsePrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), NonArray);
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBakeResponsePrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
private:
|
||||
JSBakeResponsePrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSBakeResponse::info(), JSBakeResponsePrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
};
|
||||
|
||||
JSC_DECLARE_HOST_FUNCTION(callBakeResponse);
|
||||
JSC_DECLARE_HOST_FUNCTION(constructBakeResponse);
|
||||
|
||||
@@ -159,8 +193,8 @@ public:
|
||||
return constructor;
|
||||
}
|
||||
|
||||
// DECLARE_INFO;
|
||||
DECLARE_EXPORT_INFO;
|
||||
DECLARE_INFO;
|
||||
// DECLARE_EXPORT_INFO;
|
||||
|
||||
// Must be defined for each specialization class.
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
|
||||
@@ -169,7 +203,7 @@ public:
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSObject* newTarget = asObject(callFrame->newTarget());
|
||||
auto* constructor = globalObject->JSResponseConstructor();
|
||||
auto* constructor = globalObject->JSBakeResponseConstructor();
|
||||
Structure* structure = globalObject->JSBakeResponseStructure();
|
||||
if (constructor != newTarget) [[unlikely]] {
|
||||
auto* functionGlobalObject = defaultGlobalObject(
|
||||
@@ -180,9 +214,10 @@ public:
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
JSBakeResponse* instance = JSBakeResponse::create(vm, structure, nullptr);
|
||||
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
|
||||
|
||||
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance));
|
||||
int arg_was_jsx = 0;
|
||||
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance), &arg_was_jsx);
|
||||
if (scope.exception()) [[unlikely]] {
|
||||
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new SSRResponse() allocated memory without checking for exceptions.");
|
||||
return JSValue::encode(JSC::jsUndefined());
|
||||
@@ -190,6 +225,12 @@ public:
|
||||
|
||||
instance->m_ctx = ptr;
|
||||
|
||||
if (arg_was_jsx == 1 && callFrame->argumentCount() > 0) {
|
||||
JSValue arg = callFrame->argument(0);
|
||||
JSValue responseOptions = callFrame->argumentCount() > 1 ? callFrame->argument(1) : JSC::jsUndefined();
|
||||
instance->wrapInnerComponent(globalObject, vm, arg, responseOptions);
|
||||
}
|
||||
|
||||
auto size = Response__estimatedSize(ptr);
|
||||
vm.heap.reportExtraMemoryAllocated(instance, size);
|
||||
|
||||
@@ -204,9 +245,9 @@ public:
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
Structure* structure = globalObject->JSBakeResponseStructure();
|
||||
JSBakeResponse* instance = JSBakeResponse::create(vm, structure, nullptr);
|
||||
JSBakeResponse* instance = JSBakeResponse::create(vm, globalObject, structure, nullptr);
|
||||
|
||||
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance));
|
||||
void* ptr = ResponseClass__constructForSSR(globalObject, callFrame, JSValue::encode(instance), nullptr);
|
||||
if (scope.exception()) [[unlikely]] {
|
||||
ASSERT_WITH_MESSAGE(!ptr, "Memory leak detected: new SSRResponse() allocated memory without checking for exceptions.");
|
||||
return JSValue::encode(JSC::jsUndefined());
|
||||
@@ -247,130 +288,32 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
const JSC::ClassInfo JSBakeResponsePrototype::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponsePrototype) };
|
||||
const JSC::ClassInfo JSBakeResponse::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponse) };
|
||||
const JSC::ClassInfo JSBakeResponseConstructor::s_info = { "SSRResponse"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponseConstructor) };
|
||||
const JSC::ClassInfo JSBakeResponse::s_info = { "SSRResponse"_s, &JSResponse::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponse) };
|
||||
const JSC::ClassInfo JSBakeResponseConstructor::s_info = { ""_s, &JSC::InternalFunction::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBakeResponseConstructor) };
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetSymbolFor, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
Structure* createJSBakeResponseStructure(JSC::VM& vm, Zig::GlobalObject* globalObject, JSObject* prototype)
|
||||
{
|
||||
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
|
||||
if (!response)
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
auto& vm = globalObject->vm();
|
||||
auto symbolKey = "react.transitional.element"_s;
|
||||
return JSValue::encode(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(symbolKey)));
|
||||
}
|
||||
auto structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, 0), JSBakeResponse::info(), NonArray, 0);
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetType, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
|
||||
if (!response)
|
||||
return JSValue::encode(jsUndefined());
|
||||
// Unfortunately we cannot use structure->addPropertyTransition as it does
|
||||
// not with with JSC::JSNonFinalObject
|
||||
|
||||
printf("m_ctx: %p\n", response->m_ctx);
|
||||
|
||||
// auto& type = response->type();
|
||||
// auto typeValue = type.get();
|
||||
// return JSValue::encode(typeValue);
|
||||
String wtfstring = "Hello"_s;
|
||||
auto* jsstring = JSC::jsString(globalObject->vm(), wtfstring);
|
||||
return JSValue::encode(jsstring);
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetKey, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetProps, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
|
||||
if (!response)
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
return JSValue::encode(JSC::constructEmptyObject(globalObject));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetStore, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
JSBakeResponse* response = jsDynamicCast<JSBakeResponse*>(JSValue::decode(thisValue));
|
||||
if (!response)
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
auto& vm = globalObject->vm();
|
||||
JSObject* storeObject = JSC::constructEmptyObject(globalObject);
|
||||
auto validatedIdent = JSC::Identifier::fromString(vm, "validated"_s);
|
||||
storeObject->putDirect(vm, validatedIdent, jsNumber(0), 0);
|
||||
return JSValue::encode(storeObject);
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetOwner, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugInfo, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugStack, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsBakeResponsePrototypeGetDebugTask, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsBakeResponseConstructorRender, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsBakeResponseConstructorRedirect, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(callBakeResponse, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
throwScope.throwException(globalObject, createTypeError(globalObject, "BakeResponse constructor cannot be called as a function"_s));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(constructBakeResponse, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
|
||||
auto* structure = createJSBakeResponseStructure(vm, zigGlobalObject);
|
||||
|
||||
return JSValue::encode(JSBakeResponse::create(vm, structure, nullptr));
|
||||
return structure;
|
||||
}
|
||||
|
||||
void setupJSBakeResponseClassStructure(JSC::LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* zigGlobal = reinterpret_cast<Zig::GlobalObject*>(init.global);
|
||||
auto* prototypeStructure = JSBakeResponsePrototype::createStructure(init.vm, init.global, zigGlobal->JSResponsePrototype());
|
||||
auto* prototype = JSBakeResponsePrototype::create(init.vm, init.global, prototypeStructure);
|
||||
auto* prototype = JSC::constructEmptyObject(zigGlobal, zigGlobal->JSResponsePrototype());
|
||||
|
||||
auto* constructorStructure = JSBakeResponseConstructor::createStructure(init.vm, init.global, init.global->functionPrototype());
|
||||
auto* constructor = JSBakeResponseConstructor::create(init.vm, constructorStructure, prototype);
|
||||
|
||||
auto* structure = JSBakeResponse::createStructure(init.vm, init.global, prototype);
|
||||
auto* structure = createJSBakeResponseStructure(init.vm, zigGlobal, prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
}
|
||||
|
||||
Structure* createJSBakeResponseStructure(JSC::VM& vm, Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return globalObject->JSBakeResponseStructure();
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
@@ -8,14 +8,24 @@ namespace Bun {
|
||||
using namespace JSC;
|
||||
using namespace WebCore;
|
||||
|
||||
enum JSBakeResponseKind : uint8_t {
|
||||
Regular = 0,
|
||||
Redirect = 1,
|
||||
Render = 2,
|
||||
};
|
||||
|
||||
class JSBakeResponse : public JSResponse {
|
||||
public:
|
||||
using Base = JSResponse;
|
||||
|
||||
static JSBakeResponse* create(JSC::VM& vm, JSC::Structure* structure, void* ctx);
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSBakeResponse* create(JSC::VM& vm, Zig::GlobalObject* globalObject, JSC::Structure* structure, void* ctx);
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype);
|
||||
|
||||
// JSC::Strong<JSC::Unknown>& type() { return m_type; }
|
||||
JSBakeResponseKind kind() const { return m_kind; }
|
||||
void kind(JSBakeResponseKind kind) { m_kind = kind; }
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
@@ -29,17 +39,54 @@ public:
|
||||
|
||||
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
|
||||
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
DECLARE_INFO;
|
||||
void setToThrow(JSC::JSGlobalObject* globalObject, JSC::VM& vm)
|
||||
{
|
||||
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
|
||||
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
// component
|
||||
args.append(jsUndefined());
|
||||
// responseObject
|
||||
args.append(this);
|
||||
// responseOptions
|
||||
args.append(jsUndefined());
|
||||
// kind
|
||||
args.append(JSC::jsNumber(static_cast<unsigned char>(this->kind())));
|
||||
|
||||
auto callData = JSC::getCallData(wrapComponentFn);
|
||||
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
|
||||
|
||||
this->putDirect(vm, WebCore::builtinNames(vm).typePublicName(), wrappedComponent, 0);
|
||||
}
|
||||
|
||||
void wrapInnerComponent(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSValue component, JSValue responseOptions)
|
||||
{
|
||||
this->kind(JSBakeResponseKind::Regular);
|
||||
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
|
||||
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
// component
|
||||
args.append(component);
|
||||
// responseObject
|
||||
args.append(this);
|
||||
// responseOptions
|
||||
args.append(responseOptions);
|
||||
// kind
|
||||
args.append(JSC::jsNumber(static_cast<unsigned char>(JSBakeResponseKind::Regular)));
|
||||
|
||||
auto callData = JSC::getCallData(wrapComponentFn);
|
||||
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
|
||||
|
||||
this->putDirect(vm, WebCore::builtinNames(vm).typePublicName(), wrappedComponent, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
JSBakeResponse(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr);
|
||||
void finishCreation(JSC::VM& vm);
|
||||
|
||||
// JSC::Strong<JSC::Unknown> m_type;
|
||||
JSBakeResponseKind m_kind;
|
||||
};
|
||||
|
||||
JSC::Structure* createJSBakeResponseStructure(JSC::VM&, Zig::GlobalObject*);
|
||||
void setupJSBakeResponseClassStructure(JSC::LazyClassStructure::Initializer& init);
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
@@ -67,11 +67,6 @@ pub const JSValue = enum(i64) {
|
||||
JSC__JSValue__transformToReactElement(responseValue, componentValue, globalThis);
|
||||
}
|
||||
|
||||
extern fn JSC__JSValue__transformToReactElementWithOptions(responseValue: JSValue, componentValue: JSValue, responseOptions: JSValue, globalObject: *JSGlobalObject) void;
|
||||
pub fn transformToReactElementWithOptions(responseValue: JSValue, componentValue: JSValue, responseOptions: JSValue, globalThis: *jsc.JSGlobalObject) JSError!void {
|
||||
return bun.jsc.fromJSHostCallGeneric(globalThis, @src(), JSC__JSValue__transformToReactElementWithOptions, .{ responseValue, componentValue, responseOptions, globalThis });
|
||||
}
|
||||
|
||||
extern fn JSC__JSValue__getDirectIndex(JSValue, *JSGlobalObject, u32) JSValue;
|
||||
pub fn getDirectIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue {
|
||||
return JSC__JSValue__getDirectIndex(this, globalThis, i);
|
||||
|
||||
@@ -234,9 +234,8 @@ public:
|
||||
JSC::JSValue HTTPSResponseSinkPrototype() const { return m_JSHTTPSResponseSinkClassStructure.prototypeInitializedOnMainThread(this); }
|
||||
JSC::JSValue JSReadableHTTPSResponseSinkControllerPrototype() const { return m_JSHTTPSResponseControllerPrototype.getInitializedOnMainThread(this); }
|
||||
|
||||
JSC::JSObject* JSBakeResponseConstructor() const { return m_JSBakeResponseClassStructure.constructorInitializedOnMainThread(this); }
|
||||
JSC::Structure* JSBakeResponseStructure() const { return m_JSBakeResponseClassStructure.getInitializedOnMainThread(this); }
|
||||
JSC::JSObject* JSBakeResponseConstructor() { return m_JSBakeResponseClassStructure.constructorInitializedOnMainThread(this); }
|
||||
JSC::JSValue JSBakeResponsePrototype() const { return m_JSBakeResponseClassStructure.prototypeInitializedOnMainThread(this); }
|
||||
|
||||
JSC::Structure* NetworkSinkStructure() const { return m_JSNetworkSinkClassStructure.getInitializedOnMainThread(this); }
|
||||
JSC::JSObject* NetworkSink() { return m_JSNetworkSinkClassStructure.constructorInitializedOnMainThread(this); }
|
||||
|
||||
@@ -5888,300 +5888,6 @@ restart:
|
||||
JSC__JSValue__forEachPropertyImpl<false>(JSValue0, globalObject, arg2, iter);
|
||||
}
|
||||
|
||||
extern "C" bool JSC__JSValue__isJSXElement(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
|
||||
// React does this:
|
||||
// export const REACT_LEGACY_ELEMENT_TYPE: symbol = Symbol.for('react.element');
|
||||
// export const REACT_ELEMENT_TYPE: symbol = renameElementSymbol
|
||||
// ? Symbol.for('react.transitional.element')
|
||||
// : REACT_LEGACY_ELEMENT_TYPE;
|
||||
|
||||
// TODO: cache these, i cri everytim
|
||||
auto react_legacy_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.element"_s));
|
||||
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
|
||||
|
||||
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
|
||||
|
||||
// TODO: primitive values (strings, numbers, booleans, null, undefined) are also valid
|
||||
if (value.isObject()) {
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::JSObject* object = value.getObject();
|
||||
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
|
||||
JSC::JSValue typeofValue = object->get(globalObject, typeofProperty);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (typeofValue.isSymbol() && (typeofValue == react_legacy_element_symbol || typeofValue == react_element_symbol)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Forward declaration
|
||||
extern "C" void JSC__JSValue__transformToReactElementWithOptions(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::EncodedJSValue responseOptionsValue, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
extern "C" void JSC__JSValue__transformToReactElement(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
// Call the version with options, passing undefined for options
|
||||
JSC__JSValue__transformToReactElementWithOptions(responseValue, componentValue, JSC::JSValue::encode(JSC::jsUndefined()), globalObject);
|
||||
}
|
||||
|
||||
/// TODO: This could just be a builtin function and be 10x less lines of code why is it in C++
|
||||
/// TODO: this should actually just be a special Response sub-class and the transpiler rewrites the code to use this
|
||||
///
|
||||
/// What this function does is make a Response object pretend to be a React
|
||||
/// component. To do this we have to put a couple properties on it:
|
||||
///
|
||||
/// ```ts
|
||||
/// response["$$typeof"] = REACT_ELEMENT_TYPE;
|
||||
/// response.type = Component;
|
||||
/// response.key = null;
|
||||
/// response.props = {};
|
||||
|
||||
/// // Add the _store object that React expects in dev mode
|
||||
/// response._store = {};
|
||||
/// Object.defineProperty(response._store, 'validated', {
|
||||
/// configurable: false,
|
||||
/// enumerable: false,
|
||||
/// writable: true,
|
||||
/// value: 0 // or 1 to mark it as already validated
|
||||
/// });
|
||||
|
||||
/// // Add debug properties expected in dev mode
|
||||
/// response._owner = null;
|
||||
/// response._debugInfo = null;
|
||||
/// response._debugStack = null; // or new Error() if you want a stack trace
|
||||
/// response._debugTask = null;
|
||||
/// ```
|
||||
extern "C" void JSC__JSValue__transformToReactElementWithOptions(JSC::EncodedJSValue responseValue, JSC::EncodedJSValue componentValue, JSC::EncodedJSValue responseOptionsValue, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
|
||||
JSC::JSValue response = JSC::JSValue::decode(responseValue);
|
||||
JSC::JSValue component = JSC::JSValue::decode(componentValue);
|
||||
JSC::JSValue responseOptions = JSC::JSValue::decode(responseOptionsValue);
|
||||
|
||||
if (!response.isObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSC::JSObject* responseObject = response.getObject();
|
||||
|
||||
// Get the React element symbol - same as in isJSXElement
|
||||
// For now, use the transitional element symbol (React 19+)
|
||||
// TODO: check for legacy symbol as fallback
|
||||
auto react_element_symbol = JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey("react.transitional.element"_s));
|
||||
|
||||
// If we have response options, we need to wrap the component
|
||||
// For now, we'll store the response options directly on the response object
|
||||
// The actual wrapping with AsyncLocalStorage update will happen when rendered
|
||||
if (!responseOptions.isUndefinedOrNull() && responseOptions.isObject()) {
|
||||
// Store the response options on the response object for later use
|
||||
// This will be accessed when the component is rendered
|
||||
auto responseOptionsIdentifier = JSC::Identifier::fromString(vm, "__responseOptions"_s);
|
||||
responseObject->putDirect(vm, responseOptionsIdentifier, responseOptions, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
|
||||
// Transform the Response object itself into a React element
|
||||
// The component parameter is what will be stored in the 'type' field
|
||||
|
||||
// Set $$typeof property to mark this as a React element
|
||||
auto typeofIdentifier = JSC::Identifier::fromString(vm, "$$typeof"_s);
|
||||
responseObject->putDirect(vm, typeofIdentifier, react_element_symbol, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
// Set type property
|
||||
auto typeIdentifier = JSC::Identifier::fromString(vm, "type"_s);
|
||||
|
||||
// TODO: this is fucking awful
|
||||
// Check if this is a render redirect (Response.render() case) or regular redirect (Response.redirect() case)
|
||||
// We detect this by checking if component is the special marker string "__bun_render_redirect__" or "__bun_redirect__"
|
||||
bool isRenderRedirect = false;
|
||||
bool isRedirect = false;
|
||||
if (component.isString()) {
|
||||
JSC::JSString* componentString = component.toString(globalObject);
|
||||
if (componentString) {
|
||||
String componentStr = componentString->value(globalObject);
|
||||
if (componentStr == "__bun_render_redirect__"_s) {
|
||||
isRenderRedirect = true;
|
||||
} else if (componentStr == "__bun_redirect__"_s) {
|
||||
isRedirect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Response.render() case - use BakeSSRResponse builtin to create wrapper
|
||||
if (isRenderRedirect) {
|
||||
// Get the render path and params from the response object
|
||||
auto renderPathIdentifier = JSC::Identifier::fromString(vm, "__renderPath"_s);
|
||||
auto renderParamsIdentifier = JSC::Identifier::fromString(vm, "__renderParams"_s);
|
||||
|
||||
JSC::JSValue renderPath = responseObject->get(globalObject, renderPathIdentifier);
|
||||
JSC::JSValue renderParams = responseObject->get(globalObject, renderParamsIdentifier);
|
||||
|
||||
// Use the BakeSSRResponse builtin's wrapComponent function
|
||||
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
|
||||
|
||||
// Call wrapComponent(path, params, true, responseObject) where true indicates this is a render redirect
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(renderPath);
|
||||
args.append(renderParams);
|
||||
args.append(JSC::jsBoolean(true)); // This is a render redirect
|
||||
args.append(response); // Pass the Response object
|
||||
|
||||
// Call the wrapComponent function
|
||||
auto callData = JSC::getCallData(wrapComponentFn);
|
||||
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
|
||||
|
||||
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
|
||||
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
} else {
|
||||
// If there was an error, clear it and set type to null
|
||||
scope.clearException();
|
||||
responseObject->putDirect(vm, typeIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
}
|
||||
// Handle Response.redirect() case - use BakeSSRResponse builtin to create wrapper
|
||||
else if (isRedirect) {
|
||||
// Use the BakeSSRResponse builtin's wrapComponent function
|
||||
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
|
||||
|
||||
// Call wrapComponent(undefined, undefined, "redirect", responseObject)
|
||||
// For redirect, we pass "redirect" as the third parameter and the Response object as the fourth
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(JSC::jsUndefined()); // No component for redirect
|
||||
args.append(JSC::jsUndefined()); // No response options for redirect
|
||||
|
||||
// Pass "redirect" string as the third parameter to indicate this is a redirect
|
||||
// The responseOptions parameter from transformToReactElementWithOptions contains "redirect"
|
||||
args.append(responseOptions); // This should be the "redirect" string
|
||||
args.append(response); // Pass the Response object
|
||||
|
||||
// Call the wrapComponent function
|
||||
auto callData = JSC::getCallData(wrapComponentFn);
|
||||
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
|
||||
|
||||
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
|
||||
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
} else {
|
||||
// If there was an error, clear it and set type to null
|
||||
scope.clearException();
|
||||
responseObject->putDirect(vm, typeIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
}
|
||||
// TODO: this is stupid
|
||||
// Check if the component is a JSX element (has $$typeof property)
|
||||
// If it is, we need to wrap it in a function for React to work properly
|
||||
// because `<Component />` is already the result of calling Component()
|
||||
else if (component.isObject()) {
|
||||
JSC::JSObject* componentObject = component.getObject();
|
||||
auto typeofProperty = JSC::Identifier::fromString(vm, "$$typeof"_s);
|
||||
JSC::JSValue typeofValue = componentObject->get(globalObject, typeofProperty);
|
||||
|
||||
if (!scope.exception() && typeofValue.isSymbol()) {
|
||||
// It's a JSX element - wrap it in a function that returns it
|
||||
// If we have response options, use the BakeSSRResponse builtin to wrap the component
|
||||
// so it can update AsyncLocalStorage when rendered
|
||||
|
||||
if (!responseOptions.isUndefinedOrNull() && responseOptions.isObject()) {
|
||||
// Use the BakeSSRResponse builtin's wrapComponent function
|
||||
// This will create a wrapper that updates AsyncLocalStorage before returning the component
|
||||
|
||||
// Create the wrapComponent function from the BakeSSRResponse builtin
|
||||
// The pattern is: <filename><functionName>CodeGenerator
|
||||
// So for BakeSSRResponse.ts with export function wrapComponent:
|
||||
JSC::JSFunction* wrapComponentFn = JSC::JSFunction::create(vm, globalObject, bakeSSRResponseWrapComponentCodeGenerator(vm), globalObject);
|
||||
|
||||
// Call wrapComponent(component, responseOptions, false, undefined)
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(component);
|
||||
args.append(responseOptions);
|
||||
args.append(JSC::jsBoolean(false)); // Not a render redirect
|
||||
args.append(JSC::jsUndefined()); // No response object for regular case
|
||||
|
||||
// Call the wrapComponent function
|
||||
auto callData = JSC::getCallData(wrapComponentFn);
|
||||
JSC::JSValue wrappedComponent = JSC::call(globalObject, wrapComponentFn, callData, JSC::jsUndefined(), args);
|
||||
|
||||
if (!scope.exception() && !wrappedComponent.isUndefinedOrNull()) {
|
||||
responseObject->putDirect(vm, typeIdentifier, wrappedComponent, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
} else {
|
||||
// Fall back to simple wrapper if there was an exception or undefined result
|
||||
scope.clearException();
|
||||
JSC::Strong<JSC::Unknown> strongComponent(vm, component);
|
||||
auto* wrapperFunction = JSC::JSNativeStdFunction::create(
|
||||
vm,
|
||||
globalObject,
|
||||
0, // arity
|
||||
String(), // name
|
||||
[strongComponent](JSC::JSGlobalObject* execGlobalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
return JSC::JSValue::encode(strongComponent.get());
|
||||
});
|
||||
|
||||
responseObject->putDirect(vm, typeIdentifier, wrapperFunction, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
} else {
|
||||
// No response options - create a simple wrapper
|
||||
JSC::Strong<JSC::Unknown> strongComponent(vm, component);
|
||||
|
||||
auto* wrapperFunction = JSC::JSNativeStdFunction::create(
|
||||
vm,
|
||||
globalObject,
|
||||
0, // arity
|
||||
String(), // name
|
||||
[strongComponent](JSC::JSGlobalObject* execGlobalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
return JSC::JSValue::encode(strongComponent.get());
|
||||
});
|
||||
|
||||
responseObject->putDirect(vm, typeIdentifier, wrapperFunction, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
} else {
|
||||
// It's not a JSX element, use it directly
|
||||
responseObject->putDirect(vm, typeIdentifier, component, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
} else {
|
||||
// It's not an object (could be a function or primitive), use it directly
|
||||
responseObject->putDirect(vm, typeIdentifier, component, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
|
||||
// Set key property to null
|
||||
auto keyIdentifier = JSC::Identifier::fromString(vm, "key"_s);
|
||||
responseObject->putDirect(vm, keyIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
// Set props property to empty object
|
||||
auto propsIdentifier = JSC::Identifier::fromString(vm, "props"_s);
|
||||
responseObject->putDirect(vm, propsIdentifier, JSC::constructEmptyObject(globalObject), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
// Add _store object for dev mode
|
||||
auto storeIdentifier = JSC::Identifier::fromString(vm, "_store"_s);
|
||||
JSC::JSObject* storeObject = JSC::constructEmptyObject(globalObject);
|
||||
|
||||
// Add validated property to _store
|
||||
auto validatedIdentifier = JSC::Identifier::fromString(vm, "validated"_s);
|
||||
storeObject->putDirect(vm, validatedIdentifier, JSC::jsNumber(0), 0);
|
||||
|
||||
responseObject->putDirect(vm, storeIdentifier, storeObject, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
// Add debug properties (all set to null)
|
||||
auto ownerIdentifier = JSC::Identifier::fromString(vm, "_owner"_s);
|
||||
responseObject->putDirect(vm, ownerIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
auto debugInfoIdentifier = JSC::Identifier::fromString(vm, "_debugInfo"_s);
|
||||
responseObject->putDirect(vm, debugInfoIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
auto debugStackIdentifier = JSC::Identifier::fromString(vm, "_debugStack"_s);
|
||||
// TODO: we should put an error here if we want a stack trace apparently
|
||||
responseObject->putDirect(vm, debugStackIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
auto debugTaskIdentifier = JSC::Identifier::fromString(vm, "_debugTask"_s);
|
||||
responseObject->putDirect(vm, debugTaskIdentifier, JSC::jsNull(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0);
|
||||
}
|
||||
|
||||
extern "C" void JSC__JSValue__forEachPropertyNonIndexed(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* globalObject, void* arg2, void (*iter)(JSC::JSGlobalObject* arg0, void* ctx, ZigString* arg2, JSC::EncodedJSValue JSValue3, bool isSymbol, bool isPrivateSymbol))
|
||||
{
|
||||
JSC__JSValue__forEachPropertyImpl<true>(JSValue0, globalObject, arg2, iter);
|
||||
|
||||
@@ -66,6 +66,21 @@ pub fn toJS(this: *Response, globalObject: *JSGlobalObject) JSValue {
|
||||
return js.toJSUnchecked(globalObject, this);
|
||||
}
|
||||
|
||||
/// Corresponds to `JSBakeResponseKind` in
|
||||
/// `src/bun.js/bindings/JSBakeResponse.h`
|
||||
const SSRKind = enum(u8) {
|
||||
regular = 0,
|
||||
redirect = 1,
|
||||
render = 2,
|
||||
};
|
||||
|
||||
extern "C" fn Response__createForSSR(globalObject: *JSGlobalObject, this: *Response, kind: SSRKind) JSValue;
|
||||
|
||||
pub fn toJSForSSR(this: *Response, globalObject: *JSGlobalObject, kind: SSRKind) JSValue {
|
||||
this.calculateEstimatedByteSize();
|
||||
return Response__createForSSR(globalObject, this, kind);
|
||||
}
|
||||
|
||||
pub fn getBodyValue(
|
||||
this: *Response,
|
||||
) *Body.Value {
|
||||
@@ -515,25 +530,19 @@ pub fn constructRedirect(
|
||||
try headers_ref.put(.Location, url_string_slice.slice(), globalThis);
|
||||
const ptr = bun.new(Response, response);
|
||||
|
||||
const response_js = ptr.toJS(globalThis);
|
||||
|
||||
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
|
||||
const vm = globalThis.bunVM();
|
||||
// Check if dev_server_async_local_storage is set (indicating we're in Bun dev server)
|
||||
if (vm.dev_server_async_local_storage.get()) |async_local_storage| {
|
||||
// Mark this as a redirect Response that should be handled specially
|
||||
// when used in a React component
|
||||
const redirect_marker = ZigString.init("__bun_redirect__").toJS(globalThis);
|
||||
|
||||
// Transform the Response to act as a React element with special redirect handling
|
||||
// Pass "redirect" as the third parameter to indicate this is a redirect
|
||||
const redirect_flag = ZigString.init("redirect").toJS(globalThis);
|
||||
try assertStreamingDisabled(globalThis, async_local_storage, "Response.redirect");
|
||||
try JSValue.transformToReactElementWithOptions(response_js, redirect_marker, redirect_flag, globalThis);
|
||||
return ptr.toJSForSSR(globalThis, .redirect);
|
||||
}
|
||||
|
||||
const response_js = ptr.toJS(globalThis);
|
||||
|
||||
return response_js;
|
||||
}
|
||||
|
||||
/// This function is only available on JSBakeResponse
|
||||
pub fn constructRender(
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
callframe: *jsc.CallFrame,
|
||||
@@ -582,25 +591,9 @@ pub fn constructRender(
|
||||
},
|
||||
});
|
||||
|
||||
const response_js = response.toJS(globalThis);
|
||||
const response_js = response.toJSForSSR(globalThis, .render);
|
||||
response_js.ensureStillAlive();
|
||||
|
||||
// Store the render path and params on the response for later use
|
||||
// When React tries to render this component, we'll check for these and throw RenderAbortError
|
||||
response_js.put(globalThis, "__renderPath", path_arg);
|
||||
const params_arg = if (arguments.len >= 2) arguments.ptr[1] else JSValue.jsNull();
|
||||
response_js.put(globalThis, "__renderParams", params_arg);
|
||||
|
||||
// TODO: this is terrible
|
||||
// Create a simple wrapper function that will be called by React
|
||||
// This needs to be handled specially in transformToReactElementWithOptions
|
||||
// We'll pass a special marker as the component to indicate this is a render redirect
|
||||
const render_marker = ZigString.init("__bun_render_redirect__").toJS(globalThis);
|
||||
|
||||
// Transform the Response to act as a React element
|
||||
// The C++ code will need to check for this special marker
|
||||
try JSValue.transformToReactElementWithOptions(response_js, render_marker, params_arg, globalThis);
|
||||
|
||||
return response_js;
|
||||
}
|
||||
|
||||
@@ -624,11 +617,11 @@ pub fn constructError(
|
||||
}
|
||||
|
||||
pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue) bun.JSError!*Response {
|
||||
return constructorImpl(globalThis, callframe, this_value, false);
|
||||
return constructorImpl(globalThis, callframe, this_value, null);
|
||||
}
|
||||
|
||||
pub fn ResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, thisValue: jsc.JSValue) callconv(jsc.conv) ?*anyopaque {
|
||||
return @as(*Response, Response.constructor(globalObject, callFrame, thisValue) catch |err| switch (err) {
|
||||
pub fn ResponseClass__constructForSSR(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, thisValue: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) callconv(jsc.conv) ?*anyopaque {
|
||||
return @as(*Response, Response.constructorForSSR(globalObject, callFrame, thisValue, bake_ssr_has_jsx) catch |err| switch (err) {
|
||||
error.JSError => return null,
|
||||
error.OutOfMemory => {
|
||||
globalObject.throwOutOfMemory() catch {};
|
||||
@@ -641,11 +634,11 @@ comptime {
|
||||
@export(&ResponseClass__constructForSSR, .{ .name = "ResponseClass__constructForSSR" });
|
||||
}
|
||||
|
||||
pub fn constructorForSSR(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue) bun.JSError!*Response {
|
||||
return constructorImpl(globalThis, callframe, this_value);
|
||||
pub fn constructorForSSR(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
|
||||
return constructorImpl(globalThis, callframe, this_value, bake_ssr_has_jsx);
|
||||
}
|
||||
|
||||
pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_response_enabled: bool) bun.JSError!*Response {
|
||||
pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, this_value: jsc.JSValue, bake_ssr_has_jsx: ?*c_int) bun.JSError!*Response {
|
||||
var arguments = callframe.argumentsAsArray(2);
|
||||
|
||||
if (!arguments[0].isUndefinedOrNull() and arguments[0].isObject()) {
|
||||
@@ -683,7 +676,15 @@ pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFram
|
||||
|
||||
// Special case for bake: allow `return new Response(<jsx> ... </jsx>, { ... }`
|
||||
// inside of a react component
|
||||
if (bake_ssr_response_enabled and globalThis.allowJSXInResponseConstructor()) {
|
||||
if (bake_ssr_has_jsx != null) {
|
||||
bake_ssr_has_jsx.?.* = 0;
|
||||
if (globalThis.allowJSXInResponseConstructor() and try arguments[0].isJSXElement(globalThis)) {
|
||||
const vm = globalThis.bunVM();
|
||||
if (vm.dev_server_async_local_storage.get()) |async_local_storage| {
|
||||
try assertStreamingDisabled(globalThis, async_local_storage, "new Response(<jsx />, { ... })");
|
||||
}
|
||||
bake_ssr_has_jsx.?.* = 1;
|
||||
}
|
||||
_ = this_value;
|
||||
// const arg = arguments[0];
|
||||
// // Check if it's a JSX element (object with $$typeof)
|
||||
@@ -695,7 +696,7 @@ pub fn constructorImpl(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFram
|
||||
|
||||
// // Pass the response options (arguments[1]) to transformToReactElement
|
||||
// // so it can store them for later use when the component is rendered
|
||||
// const responseOptions = if (arguments[1].isObject()) arguments[1] else .js_undefined;
|
||||
// const responseOptions = if (arguments[1].isObject()) adirectrguments[1] else .js_undefined;
|
||||
// try JSValue.transformToReactElementWithOptions(this_value, arg, responseOptions, globalThis);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
// Used to make a Response fake being a component
|
||||
// When this is called, it will render the component and then update async local
|
||||
// storage with the options of the Response
|
||||
// For Response.render(), we pass the response as strongComponent and need a 4th parameter
|
||||
// For Response.redirect(), isRenderRedirect will be "redirect" instead of true
|
||||
export function wrapComponent(strongComponent, responseOptions, isRenderRedirect, responseObject) {
|
||||
export function wrapComponent(component, responseObject, responseOptions, kind) {
|
||||
const bakeGetAsyncLocalStorage = $newZigFunction("bun.js/webcore/Response.zig", "bakeGetAsyncLocalStorage", 0);
|
||||
|
||||
return function () {
|
||||
// For Response.redirect(), we need to throw a RedirectAbortError
|
||||
if (isRenderRedirect === "redirect") {
|
||||
if (kind === 1 /* JSBakeResponseKind.Redirect */) {
|
||||
// responseObject is the Response from Response.redirect()
|
||||
const RedirectAbortError = globalThis.RedirectAbortError;
|
||||
if (RedirectAbortError) {
|
||||
@@ -19,12 +14,12 @@ export function wrapComponent(strongComponent, responseOptions, isRenderRedirect
|
||||
}
|
||||
|
||||
// For Response.render(), we need to throw a RenderAbortError
|
||||
if (isRenderRedirect === true || isRenderRedirect === "render") {
|
||||
if (kind === 2 /* JSBakeResponseKind.Render */) {
|
||||
// strongComponent is the path string, responseOptions is params, responseObject is the Response
|
||||
// We need to get the RenderAbortError from the global
|
||||
const RenderAbortError = globalThis.RenderAbortError;
|
||||
if (RenderAbortError) {
|
||||
throw new RenderAbortError(strongComponent, responseOptions, responseObject);
|
||||
throw new RenderAbortError(component, responseOptions, responseObject);
|
||||
}
|
||||
// Fallback if RenderAbortError is not available
|
||||
throw new Error("RenderAbortError not available this is a bug");
|
||||
@@ -38,6 +33,6 @@ export function wrapComponent(strongComponent, responseOptions, isRenderRedirect
|
||||
store.responseOptions = responseOptions;
|
||||
}
|
||||
}
|
||||
return strongComponent;
|
||||
return component;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -281,6 +281,16 @@ using namespace JSC;
|
||||
macro(writer) \
|
||||
macro(writing) \
|
||||
macro(written) \
|
||||
macro($$typeof) \
|
||||
macro(type) \
|
||||
macro(key) \
|
||||
macro(props) \
|
||||
macro(validated) \
|
||||
macro(_store) \
|
||||
macro(_owner) \
|
||||
macro(_debugInfo) \
|
||||
macro(_debugStack) \
|
||||
macro(_debugTask) \
|
||||
BUN_ADDITIONAL_BUILTIN_NAMES(macro)
|
||||
// --- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME ---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user