aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--integration/bunjs-only-snippets/baz.js2
-rw-r--r--integration/bunjs-only-snippets/resolve-typescript-file.tsx1
-rw-r--r--integration/bunjs-only-snippets/resolve.test.js106
-rw-r--r--integration/bunjs-only-snippets/tsconfig.json10
-rw-r--r--src/javascript/jsc/api/bun.zig42
-rw-r--r--src/javascript/jsc/bindings/BunBuiltinNames.h1
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp67
7 files changed, 198 insertions, 31 deletions
diff --git a/integration/bunjs-only-snippets/baz.js b/integration/bunjs-only-snippets/baz.js
new file mode 100644
index 000000000..5837bb3bb
--- /dev/null
+++ b/integration/bunjs-only-snippets/baz.js
@@ -0,0 +1,2 @@
+// this file is used in resolve.test.js
+export default {};
diff --git a/integration/bunjs-only-snippets/resolve-typescript-file.tsx b/integration/bunjs-only-snippets/resolve-typescript-file.tsx
new file mode 100644
index 000000000..ff8b4c563
--- /dev/null
+++ b/integration/bunjs-only-snippets/resolve-typescript-file.tsx
@@ -0,0 +1 @@
+export default {};
diff --git a/integration/bunjs-only-snippets/resolve.test.js b/integration/bunjs-only-snippets/resolve.test.js
new file mode 100644
index 000000000..d5d14dfef
--- /dev/null
+++ b/integration/bunjs-only-snippets/resolve.test.js
@@ -0,0 +1,106 @@
+import { it, expect } from "bun:test";
+import { mkdirSync, writeFileSync } from "fs";
+import { join } from "path";
+
+it("import.meta.resolve", async () => {
+ expect(await import.meta.resolve("./resolve.test.js")).toBe(import.meta.url);
+
+ expect(await import.meta.resolve("./resolve.test.js", import.meta.url)).toBe(
+ import.meta.url
+ );
+
+ expect(
+ // optional second param can be any path, including a dir
+ await import.meta.resolve(
+ "./bunjs-only-snippets/resolve.test.js",
+ join(import.meta.url, "../")
+ )
+ ).toBe(import.meta.url);
+
+ // can be a package path
+ expect((await import.meta.resolve("react", import.meta.url)).length > 0).toBe(
+ true
+ );
+
+ // file extensions are optional
+ expect(await import.meta.resolve("./resolve.test")).toBe(import.meta.url);
+
+ // works with tsconfig.json "paths"
+ expect(await import.meta.resolve("foo/bar")).toBe(
+ join(import.meta.url, "../baz.js")
+ );
+
+ // works with package.json "exports"
+ writePackageJSONExportsFixture();
+ expect(await import.meta.resolve("package-json-exports/baz")).toBe(
+ join(import.meta.url, "../node_modules/package-json-exports/foo/bar.js")
+ );
+
+ expect(await import.meta.resolve("./resolve-typescript-file.tsx")).toBe(
+ join(import.meta.url, "../resolve-typescript-file.tsx")
+ );
+ expect(await import.meta.resolve("./resolve-typescript-file.js")).toBe(
+ join(import.meta.url, "../resolve-typescript-file.tsx")
+ );
+
+ // works with typescript edgecases like:
+ // - If the file ends with .js and it doesn't exist, try again with .ts and .tsx
+ expect(await import.meta.resolve("./resolve-typescript-file.js")).toBe(
+ join(import.meta.url, "../resolve-typescript-file.tsx")
+ );
+ expect(await import.meta.resolve("./resolve-typescript-file.tsx")).toBe(
+ join(import.meta.url, "../resolve-typescript-file.tsx")
+ );
+
+ try {
+ await import.meta.resolve("THIS FILE DOESNT EXIST");
+ throw new Error("Test failed");
+ } catch (exception) {
+ expect(exception instanceof ResolveError).toBe(true);
+ expect(exception.referrer).toBe(import.meta.url);
+ expect(exception.name).toBe("ResolveError");
+ }
+});
+
+// the slightly lower level API, which doesn't prefill the second param
+// and expects a directory instead of a filepath
+it("Bun.resolve", async () => {
+ expect(await Bun.resolve("./resolve.test.js", import.meta.dir)).toBe(
+ import.meta.url
+ );
+});
+
+// synchronous
+it("Bun.resolveSync", () => {
+ expect(Bun.resolveSync("./resolve.test.js", import.meta.dir)).toBe(
+ import.meta.url
+ );
+});
+
+function writePackageJSONExportsFixture() {
+ try {
+ mkdirSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/foo"),
+ {
+ recursive: true,
+ }
+ );
+ } catch (exception) {}
+ writeFileSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/foo/bar.js"),
+ "export const bar = 1;"
+ );
+ writeFileSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/package.json"),
+ JSON.stringify(
+ {
+ name: "package-json-exports",
+ exports: {
+ "./baz": "./foo/bar.js",
+ },
+ },
+ null,
+ 2
+ )
+ );
+}
diff --git a/integration/bunjs-only-snippets/tsconfig.json b/integration/bunjs-only-snippets/tsconfig.json
new file mode 100644
index 000000000..e76eae4ac
--- /dev/null
+++ b/integration/bunjs-only-snippets/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "lib": ["esnext", { "replace": "node", "with": "dom" }],
+ "baseUrl": ".",
+ "paths": {
+ "foo/bar": ["baz.js"]
+ },
+ "typeRoots": ["./node_modules/@types"]
+ }
+}
diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig
index e2af7ed95..c66c5f0f1 100644
--- a/src/javascript/jsc/api/bun.zig
+++ b/src/javascript/jsc/api/bun.zig
@@ -698,7 +698,7 @@ fn doResolve(
return null;
}
- return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception);
+ return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception, false);
}
fn doResolveWithArgs(
@@ -706,15 +706,25 @@ fn doResolveWithArgs(
specifier: ZigString,
from: ZigString,
exception: js.ExceptionRef,
+ comptime is_file_path: bool,
) ?JSC.JSValue {
var errorable: ErrorableZigString = undefined;
- VirtualMachine.resolveForAPI(
- &errorable,
- ctx.ptr(),
- specifier,
- from,
- );
+ if (comptime is_file_path) {
+ VirtualMachine.resolve(
+ &errorable,
+ ctx.ptr(),
+ specifier,
+ from,
+ );
+ } else {
+ VirtualMachine.resolveForAPI(
+ &errorable,
+ ctx.ptr(),
+ specifier,
+ from,
+ );
+ }
if (!errorable.success) {
exception.* = bun.cast(JSC.JSValueRef, errorable.result.err.ptr.?);
@@ -759,22 +769,7 @@ export fn Bun__resolve(
) JSC.JSValue {
var exception_ = [1]JSC.JSValueRef{null};
var exception = &exception_;
- const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception) orelse {
- return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0]));
- };
- return JSC.JSPromise.resolvedPromiseValue(global, value);
-}
-
-export fn Bun__resolveSync(
- global: *JSGlobalObject,
- specifier: JSValue,
- source: JSValue,
- exception_: ?*JSValue,
-) JSC.JSValue {
- var exception_ = [1]JSC.JSValueRef{null};
- var exception = &exception_;
- exception_.* = exception[0];
- const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception) orelse {
+ const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception, true) orelse {
return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0]));
};
return JSC.JSPromise.resolvedPromiseValue(global, value);
@@ -783,7 +778,6 @@ export fn Bun__resolveSync(
comptime {
if (!is_bindgen) {
_ = Bun__resolve;
- _ = Bun__resolveSync;
}
}
diff --git a/src/javascript/jsc/bindings/BunBuiltinNames.h b/src/javascript/jsc/bindings/BunBuiltinNames.h
index 5600f9675..709ab28a5 100644
--- a/src/javascript/jsc/bindings/BunBuiltinNames.h
+++ b/src/javascript/jsc/bindings/BunBuiltinNames.h
@@ -67,6 +67,7 @@ using namespace JSC;
macro(parse) \
macro(relative) \
macro(resolve) \
+ macro(url) \
macro(sep) \
macro(delimiter) \
macro(toNamespacedPath) \
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index ab35ff4d9..16d4786a3 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -491,6 +491,55 @@ static JSC_DEFINE_HOST_FUNCTION(functionATOB,
return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(decodedData->data(), decodedData->size())));
}
+extern "C" JSC__JSValue Bun__resolve(JSC::JSGlobalObject* global, JSC__JSValue specifier, JSC__JSValue from);
+
+static JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolve);
+
+static JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolve,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ 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, "import.meta.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, "import.meta.resolve expects a string"_s);
+ scope.release();
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ JSC__JSValue from;
+
+ if (callFrame->argumentCount() > 1) {
+ from = JSC::JSValue::encode(callFrame->argument(1));
+ } else {
+ JSC::JSObject* thisObject = JSC::jsDynamicCast<JSC::JSObject*>(vm, callFrame->thisValue());
+ if (UNLIKELY(!thisObject)) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwTypeError(globalObject, scope, "import.meta.resolve must be bound to an import.meta object"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ auto clientData = Bun::clientData(vm);
+
+ from = JSC::JSValue::encode(thisObject->get(globalObject, clientData->builtinNames().urlPublicName()));
+ }
+
+ return Bun__resolve(globalObject, JSC::JSValue::encode(moduleName), from);
+ }
+ }
+}
+
// This is not a publicly exposed API currently.
// This is used by the bundler to make Response, Request, FetchEvent,
// and any other objects available globally.
@@ -728,19 +777,23 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
metaProperties->putDirect(
vm, clientData->builtinNames().filePublicName(),
JSC::jsSubstring(globalObject, keyString, index + 1, keyString->length() - index - 1));
+
+ metaProperties->putDirect(
+ vm, clientData->builtinNames().filePublicName(),
+ JSC::jsSubstring(globalObject, keyString, index + 1, keyString->length() - index - 1));
+
+ metaProperties->putDirect(vm, clientData->builtinNames().resolvePublicName(),
+ JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
+ WTF::String("resolve"), functionImportMeta__resolve),
+ 0);
}
metaProperties->putDirect(vm, clientData->builtinNames().pathPublicName(), key);
+ // this is a lie
+ metaProperties->putDirect(vm, clientData->builtinNames().urlPublicName(), key);
RETURN_IF_EXCEPTION(scope, nullptr);
- // metaProperties->putDirect(vm, Identifier::fromString(vm, "resolve"),
- // globalObject->globalThis()
- // ->get(vm, Identifier::fromString("Bun"))
- // .getObject()
- // ->get(vm, Identifier::fromString("resolve"))); );
- // RETURN_IF_EXCEPTION(scope, nullptr);
-
return metaProperties;
}