diff options
author | 2022-08-09 01:33:12 -0700 | |
---|---|---|
committer | 2022-08-09 01:41:13 -0700 | |
commit | b36b7fee16a5239cfb50cfde94ed871d60032b3c (patch) | |
tree | 1456e78bd1434ed92c9e0720cdc1bf07d3a6d63c | |
parent | 01847cabd20d82733874a4b78eb8ee570f08c943 (diff) | |
download | bun-b36b7fee16a5239cfb50cfde94ed871d60032b3c.tar.gz bun-b36b7fee16a5239cfb50cfde94ed871d60032b3c.tar.zst bun-b36b7fee16a5239cfb50cfde94ed871d60032b3c.zip |
[node compat] Implement `require.resolve`
-rw-r--r-- | src/bun.js/bindings/ImportMetaObject.cpp | 108 | ||||
-rw-r--r-- | src/bun.js/bindings/ImportMetaObject.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 20 | ||||
-rw-r--r-- | test/bun.js/import-meta.test.js | 16 |
4 files changed, 137 insertions, 9 deletions
diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index dc4290c91..8c0f573f3 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -35,6 +35,8 @@ #include "JSBufferConstructorBuiltins.h" #include "JavaScriptCore/JSBase.h" +#include "JavaScriptCore/JSNativeStdFunction.h" + namespace Zig { using namespace JSC; using namespace WebCore; @@ -42,6 +44,104 @@ using namespace WebCore; extern "C" JSC__JSValue Bun__resolve(JSC::JSGlobalObject* global, JSC__JSValue specifier, JSC__JSValue from); extern "C" JSC__JSValue Bun__resolveSync(JSC::JSGlobalObject* global, JSC__JSValue specifier, JSC__JSValue from); +static EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame, JSC::EncodedJSValue from) +{ + JSC::VM& vm = globalObject->vm(); + + switch (callFrame->argumentCount()) { + case 0: { + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + // not "requires" because "require" could be confusing + JSC::throwTypeError(globalObject, scope, "require.resolve needs 1 argument (a string)"_s); + scope.release(); + return JSC::JSValue::encode(JSC::JSValue {}); + } + default: { + JSC::JSValue moduleName = callFrame->argument(0); + + if (moduleName.isUndefinedOrNull()) { + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + JSC::throwTypeError(globalObject, scope, "require.resolve expects a string"_s); + scope.release(); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + if (callFrame->argumentCount() > 1) { + JSC::JSValue fromValue = callFrame->argument(1); + + // require.resolve also supports a paths array + // we only support a single path + if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { + if (JSC::JSArray* array = JSC::jsDynamicCast<JSC::JSArray*>(fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s)))) { + if (array->length() > 0) { + fromValue = array->getIndex(globalObject, 0); + } + } + } + } + + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + + if (!JSC::JSValue::decode(result).isString()) { + JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + scope.release(); + return result; + } + } +} + +JSC_DEFINE_CUSTOM_SETTER(functionRequireResolveLazySetter, + (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, + JSC::EncodedJSValue value, JSC::PropertyName)) +{ + Zig::GlobalObject* global = static_cast<Zig::GlobalObject*>(JSC::jsCast<JSC::JSObject*>(JSC::JSValue::decode(thisValue))); + JSC::VM& vm = globalObject->vm(); + JSC::JSFunction* require = JSC::jsCast<JSC::JSFunction*>(JSC::JSValue::decode(thisValue)); + auto clientData = WebCore::clientData(vm); + return require->putDirect(vm, PropertyName(clientData->builtinNames().resolvePrivateName()), JSValue::decode(value), 0); +} + +JSC_DEFINE_CUSTOM_GETTER(functionRequireResolveLazyGetter, + (JSC::JSGlobalObject * _globalObject, JSC::EncodedJSValue thisValue, + JSC::PropertyName)) +{ + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(_globalObject); + + JSC::VM& vm = globalObject->vm(); + auto clientData = WebCore::clientData(vm); + auto& builtinNames = clientData->builtinNames(); + + JSC::JSFunction* require = JSC::jsCast<JSC::JSFunction*>(JSC::JSValue::decode(thisValue)); + + if (JSC::JSValue resolveFunctionValue = require->getIfPropertyExists(globalObject, PropertyName(builtinNames.resolvePrivateName()))) { + return JSValue::encode(resolveFunctionValue); + } + + JSValue pathStringValue = require->get(globalObject, PropertyName(builtinNames.pathPrivateName())); + JSC::Strong<JSC::JSString> pathString = JSC::Strong<JSC::JSString>(vm, pathStringValue.toStringOrNull(globalObject)); + + JSC::JSFunction* resolverFunction + = JSC::JSNativeStdFunction::create( + globalObject->vm(), globalObject, 1, "resolve"_s, [pathString_ = WTFMove(pathString)](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> const JSC::EncodedJSValue { + return functionRequireResolve(globalObject, callFrame, JSValue::encode(pathString_.get())); + }); + require->putDirect(vm, builtinNames.resolvePrivateName(), resolverFunction, 0); + return JSValue::encode(JSValue(resolverFunction)); +} + +JSObject* Zig::ImportMetaObject::createRequireFunction(VM& vm, JSGlobalObject* globalObject, WTF::String& pathString) +{ + JSFunction* requireFunction = JSFunction::create(vm, importMetaObjectRequireCodeGenerator(vm), globalObject); + auto clientData = WebCore::clientData(vm); + requireFunction->putDirectCustomAccessor(vm, clientData->builtinNames().resolvePublicName(), JSC::CustomGetterSetter::create(vm, functionRequireResolveLazyGetter, functionRequireResolveLazySetter), 0); + requireFunction->putDirect(vm, clientData->builtinNames().pathPrivateName(), jsOwnedString(vm, pathString), JSC::PropertyAttribute::DontEnum | 0); + return requireFunction; +} + static JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolveSync); static JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolveSync, @@ -204,8 +304,10 @@ void ImportMetaObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj this->putDirect(vm, clientData->builtinNames().pathPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, clientData->builtinNames().urlPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, clientData->builtinNames().mainPublicName(), jsBoolean(false), 0); - this->putDirectBuiltinFunction(vm, globalObject, - clientData->builtinNames().requirePublicName(), importMetaObjectRequireCodeGenerator(vm), 0); + + String requireString = "[[require]]"_s; + this->putDirect(vm, clientData->builtinNames().requirePublicName(), Zig::ImportMetaObject::createRequireFunction(vm, globalObject, requireString), PropertyAttribute::Builtin | PropertyAttribute::Function | 0); + this->putDirectBuiltinFunction(vm, globalObject, clientData->builtinNames().loadModulePublicName(), importMetaObjectLoadModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); this->putDirectBuiltinFunction(vm, globalObject, @@ -221,6 +323,7 @@ void ImportMetaObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj functionImportMeta__resolveSync, NoIntrinsic, JSC::PropertyAttribute::Function | 0); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } @@ -245,5 +348,4 @@ const JSC::ClassInfo ImportMetaObjectPrototype::s_info = { "ImportMeta"_s, &Base const JSC::ClassInfo ImportMetaObject::s_info = { "ImportMeta"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ImportMetaObject) }; - } diff --git a/src/bun.js/bindings/ImportMetaObject.h b/src/bun.js/bindings/ImportMetaObject.h index 69348f1f6..7527dcb51 100644 --- a/src/bun.js/bindings/ImportMetaObject.h +++ b/src/bun.js/bindings/ImportMetaObject.h @@ -40,6 +40,8 @@ public: static JSObject* createPrototype(VM& vm, JSDOMGlobalObject& globalObject); static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static JSObject* createRequireFunction(VM& vm, JSGlobalObject* globalObject, WTF::String& pathString); + private: ImportMetaObject(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) : Base(vm, structure) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index daf6d3f14..bfc8e2ad4 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2466,23 +2466,31 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje } RETURN_IF_EXCEPTION(scope, nullptr); + auto& builtinNames = clientData->builtinNames(); + auto view = keyString->value(globalObject); auto index = view.reverseFind('/', view.length()); if (index != WTF::notFound) { - metaProperties->putDirect(vm, clientData->builtinNames().dirPublicName(), + metaProperties->putDirect(vm, builtinNames.dirPublicName(), JSC::jsSubstring(globalObject, keyString, 0, index)); metaProperties->putDirect( - vm, clientData->builtinNames().filePublicName(), + vm, builtinNames.filePublicName(), JSC::jsSubstring(globalObject, keyString, index + 1, keyString->length() - index - 1)); } else { - metaProperties->putDirect(vm, clientData->builtinNames().filePublicName(), keyString); + metaProperties->putDirect(vm, builtinNames.filePublicName(), keyString); } - metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), keyString); + metaProperties->putDirect(vm, builtinNames.pathPublicName(), keyString); + metaProperties->putDirect( + vm, + builtinNames.requirePublicName(), + Zig::ImportMetaObject::createRequireFunction(vm, globalObject, view), + PropertyAttribute::Builtin | PropertyAttribute::Function | 0); + if (view.startsWith('/')) { - metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string()))); + metaProperties->putDirect(vm, builtinNames.urlPublicName(), JSC::JSValue(JSC::jsString(vm, WTF::URL::fileURLWithFileSystemPath(view).string()))); } else { - metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), keyString); + metaProperties->putDirect(vm, builtinNames.urlPublicName(), keyString); } RELEASE_AND_RETURN(scope, metaProperties); diff --git a/test/bun.js/import-meta.test.js b/test/bun.js/import-meta.test.js index f0989d296..8b0cccea1 100644 --- a/test/bun.js/import-meta.test.js +++ b/test/bun.js/import-meta.test.js @@ -24,6 +24,22 @@ it("import.meta.require (json)", () => { expect(require("./require-json.json").hello).toBe(sync.hello); }); +it("Module.createRequire().resolve", () => { + const expected = Bun.resolveSync("./require-json.json", import.meta.dir); + + const createdRequire = Module.createRequire(import.meta.path); + const result = createdRequire.resolve("./require-json.json"); + + expect(result).toBe(expected); +}); + +it("import.meta.require.resolve", () => { + const expected = Bun.resolveSync("./require-json.json", import.meta.dir); + var { resolve } = import.meta.require; + const result = resolve("./require-json.json"); + expect(result).toBe(expected); +}); + it("import.meta.require (javascript)", () => { expect(import.meta.require("./require-js.js").hello).toBe(sync.hello); const require = Module.createRequire(import.meta.path); |