aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-06-28 16:09:08 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-28 16:09:08 -0700
commit292647bd531c154f81d2d3d8f3344e01752549f6 (patch)
tree0bd9619a719382e65dd5c7b3ac30556490468f74 /src
parent42ded7033623feec80afd13ce199752eaf20e438 (diff)
downloadbun-292647bd531c154f81d2d3d8f3344e01752549f6.tar.gz
bun-292647bd531c154f81d2d3d8f3344e01752549f6.tar.zst
bun-292647bd531c154f81d2d3d8f3344e01752549f6.zip
Introduce `await Bun.file(path).exists()` (#3446)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp31
-rw-r--r--src/bun.js/bindings/generated_classes.zig3
-rw-r--r--src/bun.js/webcore/blob.zig29
-rw-r--r--src/bun.js/webcore/response.classes.ts1
4 files changed, 64 insertions, 0 deletions
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 387580d54..b7461b5f0 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -103,6 +103,9 @@ extern "C" void BlobClass__finalize(void*);
extern "C" EncodedJSValue BlobPrototype__getArrayBuffer(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(BlobPrototype__arrayBufferCallback);
+extern "C" EncodedJSValue BlobPrototype__getExists(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(BlobPrototype__existsCallback);
+
extern "C" EncodedJSValue BlobPrototype__getFormData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(BlobPrototype__formDataCallback);
@@ -137,6 +140,7 @@ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBlobPrototype, JSBlobPrototype::Base);
static const HashTableValue JSBlobPrototypeTableValues[] = {
{ "arrayBuffer"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__arrayBufferCallback, 0 } },
+ { "exists"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__existsCallback, 0 } },
{ "formData"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__formDataCallback, 0 } },
{ "json"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, BlobPrototype__jsonCallback, 0 } },
{ "lastModified"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, BlobPrototype__lastModifiedGetterWrap, 0 } },
@@ -190,6 +194,33 @@ JSC_DEFINE_HOST_FUNCTION(BlobPrototype__arrayBufferCallback, (JSGlobalObject * l
return BlobPrototype__getArrayBuffer(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(BlobPrototype__existsCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSBlob* thisObject = jsDynamicCast<JSBlob*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return BlobPrototype__getExists(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(BlobPrototype__formDataCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index bdde69c1a..04a72d7ed 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -108,6 +108,8 @@ pub const JSBlob = struct {
if (@TypeOf(Blob.getArrayBuffer) != CallbackType)
@compileLog("Expected Blob.getArrayBuffer to be a callback but received " ++ @typeName(@TypeOf(Blob.getArrayBuffer)));
+ if (@TypeOf(Blob.getExists) != CallbackType)
+ @compileLog("Expected Blob.getExists to be a callback but received " ++ @typeName(@TypeOf(Blob.getExists)));
if (@TypeOf(Blob.getFormData) != CallbackType)
@compileLog("Expected Blob.getFormData to be a callback but received " ++ @typeName(@TypeOf(Blob.getFormData)));
if (@TypeOf(Blob.getJSON) != CallbackType)
@@ -136,6 +138,7 @@ pub const JSBlob = struct {
@export(Blob.constructor, .{ .name = "BlobClass__construct" });
@export(Blob.finalize, .{ .name = "BlobClass__finalize" });
@export(Blob.getArrayBuffer, .{ .name = "BlobPrototype__getArrayBuffer" });
+ @export(Blob.getExists, .{ .name = "BlobPrototype__getExists" });
@export(Blob.getFormData, .{ .name = "BlobPrototype__getFormData" });
@export(Blob.getJSON, .{ .name = "BlobPrototype__getJSON" });
@export(Blob.getLastModified, .{ .name = "BlobPrototype__getLastModified" });
diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig
index 868acbb80..faf503a3f 100644
--- a/src/bun.js/webcore/blob.zig
+++ b/src/bun.js/webcore/blob.zig
@@ -319,6 +319,7 @@ pub const Blob = struct {
},
);
}
+
pub fn writeFormat(this: *const Blob, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void {
const Writer = @TypeOf(writer);
@@ -2324,6 +2325,34 @@ pub const Blob = struct {
return promisified(this.toFormData(globalThis, .temporary), globalThis);
}
+ fn getExistsSync(this: *Blob) JSC.JSValue {
+ if (this.size == Blob.max_size) {
+ this.resolveSize();
+ }
+
+ // If there's no store that means it's empty and we just return true
+ // it will not error to return an empty Blob
+ var store = this.store orelse return JSValue.jsBoolean(true);
+
+ if (store.data == .bytes) {
+ // Bytes will never error
+ return JSValue.jsBoolean(true);
+ }
+
+ // We say regular files and pipes exist.
+ // This is mostly meant for "Can we use this in new Response(file)?"
+ return JSValue.jsBoolean(std.os.S.ISREG(store.data.file.mode) or std.os.S.ISFIFO(store.data.file.mode));
+ }
+
+ // This mostly means 'can it be read?'
+ pub fn getExists(
+ this: *Blob,
+ globalThis: *JSC.JSGlobalObject,
+ _: *JSC.CallFrame,
+ ) callconv(.C) JSValue {
+ return JSC.JSPromise.resolvedPromiseValue(globalThis, this.getExistsSync());
+ }
+
pub fn getWriter(
this: *Blob,
globalThis: *JSC.JSGlobalObject,
diff --git a/src/bun.js/webcore/response.classes.ts b/src/bun.js/webcore/response.classes.ts
index b6ad452d2..c11cb10b2 100644
--- a/src/bun.js/webcore/response.classes.ts
+++ b/src/bun.js/webcore/response.classes.ts
@@ -132,6 +132,7 @@ export default [
slice: { fn: "getSlice", length: 2 },
stream: { fn: "getStream", length: 1 },
formData: { fn: "getFormData" },
+ exists: { fn: "getExists", length: 0 },
type: {
getter: "getType",