mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
cloudflare: enable fs with @cloudflare/vite-plugin (#239)
* init dev vite write plugin * reorganized cf proxy, fixed dev write
This commit is contained in:
@@ -263,6 +263,14 @@ async function buildAdapters() {
|
|||||||
external: ["wrangler", "node:process"],
|
external: ["wrangler", "node:process"],
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
tsup.build(
|
||||||
|
baseConfig("cloudflare/proxy", {
|
||||||
|
entry: ["src/adapter/cloudflare/proxy.ts"],
|
||||||
|
outDir: "dist/adapter/cloudflare",
|
||||||
|
metafile: false,
|
||||||
|
external: [/bknd/, "wrangler", "node:process"],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
|
||||||
tsup.build({
|
tsup.build({
|
||||||
...baseConfig("vite"),
|
...baseConfig("vite"),
|
||||||
|
|||||||
@@ -197,6 +197,11 @@
|
|||||||
"import": "./dist/adapter/cloudflare/index.js",
|
"import": "./dist/adapter/cloudflare/index.js",
|
||||||
"require": "./dist/adapter/cloudflare/index.js"
|
"require": "./dist/adapter/cloudflare/index.js"
|
||||||
},
|
},
|
||||||
|
"./adapter/cloudflare/proxy": {
|
||||||
|
"types": "./dist/types/adapter/cloudflare/proxy.d.ts",
|
||||||
|
"import": "./dist/adapter/cloudflare/proxy.js",
|
||||||
|
"require": "./dist/adapter/cloudflare/proxy.js"
|
||||||
|
},
|
||||||
"./adapter": {
|
"./adapter": {
|
||||||
"types": "./dist/types/adapter/index.d.ts",
|
"types": "./dist/types/adapter/index.d.ts",
|
||||||
"import": "./dist/adapter/index.js"
|
"import": "./dist/adapter/index.js"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export {
|
|||||||
export { constants } from "./config";
|
export { constants } from "./config";
|
||||||
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
||||||
export { registries } from "bknd";
|
export { registries } from "bknd";
|
||||||
export { withPlatformProxy } from "./proxy";
|
export { devFsPlugin, devFsWrite } from "./vite";
|
||||||
|
|
||||||
// for compatibility with old code
|
// for compatibility with old code
|
||||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
registerMedia,
|
registerMedia,
|
||||||
type CloudflareBkndConfig,
|
type CloudflareBkndConfig,
|
||||||
type CloudflareEnv,
|
type CloudflareEnv,
|
||||||
} from ".";
|
} from "bknd/adapter/cloudflare";
|
||||||
import type { PlatformProxy } from "wrangler";
|
import type { PlatformProxy } from "wrangler";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
|
|
||||||
@@ -41,12 +41,13 @@ export function withPlatformProxy<Env extends CloudflareEnv>(
|
|||||||
beforeBuild: async (app, registries) => {
|
beforeBuild: async (app, registries) => {
|
||||||
if (!use_proxy) return;
|
if (!use_proxy) return;
|
||||||
const env = await getEnv();
|
const env = await getEnv();
|
||||||
registerMedia(env, registries);
|
registerMedia(env, registries as any);
|
||||||
await config?.beforeBuild?.(app, registries);
|
await config?.beforeBuild?.(app, registries);
|
||||||
},
|
},
|
||||||
bindings: async (env) => {
|
bindings: async (env) => {
|
||||||
return (await config?.bindings?.(await getEnv(env))) || {};
|
return (await config?.bindings?.(await getEnv(env))) || {};
|
||||||
},
|
},
|
||||||
|
// @ts-ignore
|
||||||
app: async (_env) => {
|
app: async (_env) => {
|
||||||
const env = await getEnv(_env);
|
const env = await getEnv(_env);
|
||||||
|
|
||||||
|
|||||||
135
app/src/adapter/cloudflare/vite.ts
Normal file
135
app/src/adapter/cloudflare/vite.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import type { Plugin } from "vite";
|
||||||
|
import { writeFile as nodeWriteFile } from "node:fs/promises";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin that provides Node.js filesystem access during development
|
||||||
|
* by injecting a polyfill into the SSR environment
|
||||||
|
*/
|
||||||
|
export function devFsPlugin({
|
||||||
|
verbose = false,
|
||||||
|
configFile = "bknd.config.ts",
|
||||||
|
}: {
|
||||||
|
verbose?: boolean;
|
||||||
|
configFile?: string;
|
||||||
|
}): Plugin {
|
||||||
|
let isDev = false;
|
||||||
|
let projectRoot = "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "dev-fs-plugin",
|
||||||
|
enforce: "pre",
|
||||||
|
configResolved(config) {
|
||||||
|
isDev = config.command === "serve";
|
||||||
|
projectRoot = config.root;
|
||||||
|
},
|
||||||
|
configureServer(server) {
|
||||||
|
if (!isDev) return;
|
||||||
|
|
||||||
|
// Intercept stdout to watch for our write requests
|
||||||
|
const originalStdoutWrite = process.stdout.write;
|
||||||
|
process.stdout.write = function (chunk: any, encoding?: any, callback?: any) {
|
||||||
|
const output = chunk.toString();
|
||||||
|
|
||||||
|
// Check if this output contains our special write request
|
||||||
|
if (output.includes("{{DEV_FS_WRITE_REQUEST}}")) {
|
||||||
|
try {
|
||||||
|
// Extract the JSON from the log line
|
||||||
|
const match = output.match(/{{DEV_FS_WRITE_REQUEST}} ({.*})/);
|
||||||
|
if (match) {
|
||||||
|
const writeRequest = JSON.parse(match[1]);
|
||||||
|
if (writeRequest.type === "DEV_FS_WRITE_REQUEST") {
|
||||||
|
if (verbose) {
|
||||||
|
console.debug("[dev-fs-plugin] Intercepted write request via stdout");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the write request immediately
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const fullPath = resolve(projectRoot, writeRequest.filename);
|
||||||
|
await nodeWriteFile(fullPath, writeRequest.data);
|
||||||
|
if (verbose) {
|
||||||
|
console.debug("[dev-fs-plugin] File written successfully!");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[dev-fs-plugin] Error writing file:", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Don't output the raw write request to console
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Not a valid write request, continue with normal output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// biome-ignore lint:
|
||||||
|
return originalStdoutWrite.apply(process.stdout, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Restore stdout when server closes
|
||||||
|
server.httpServer?.on("close", () => {
|
||||||
|
process.stdout.write = originalStdoutWrite;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
transform(code, id, options) {
|
||||||
|
// Only transform in SSR mode during development
|
||||||
|
if (!isDev || !options?.ssr) return;
|
||||||
|
|
||||||
|
// Check if this is the bknd config file
|
||||||
|
if (id.includes(configFile)) {
|
||||||
|
if (verbose) {
|
||||||
|
console.debug("[dev-fs-plugin] Transforming", configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject our filesystem polyfill at the top of the file
|
||||||
|
const polyfill = `
|
||||||
|
// Dev-fs polyfill injected by vite-plugin-dev-fs
|
||||||
|
if (typeof globalThis !== 'undefined') {
|
||||||
|
globalThis.__devFsPolyfill = {
|
||||||
|
writeFile: async (filename, data) => {
|
||||||
|
${verbose ? "console.debug('dev-fs polyfill: Intercepting write request for', filename);" : ""}
|
||||||
|
|
||||||
|
// Use console logging as a communication channel
|
||||||
|
// The main process will watch for this specific log pattern
|
||||||
|
const writeRequest = {
|
||||||
|
type: 'DEV_FS_WRITE_REQUEST',
|
||||||
|
filename: filename,
|
||||||
|
data: data,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output as a specially formatted console message
|
||||||
|
console.log('{{DEV_FS_WRITE_REQUEST}}', JSON.stringify(writeRequest));
|
||||||
|
${verbose ? "console.debug('dev-fs polyfill: Write request sent via console');" : ""}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
return polyfill + code;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write function that uses the dev-fs polyfill injected by our Vite plugin
|
||||||
|
export async function devFsWrite(filename: string, data: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if the dev-fs polyfill is available (injected by our Vite plugin)
|
||||||
|
if (typeof globalThis !== "undefined" && (globalThis as any).__devFsPolyfill) {
|
||||||
|
return (globalThis as any).__devFsPolyfill.writeFile(filename, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to Node.js fs for other environments (Node.js, Bun)
|
||||||
|
const { writeFile } = await import("node:fs/promises");
|
||||||
|
return writeFile(filename, data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[dev-fs-write] Error writing file:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ async function action({
|
|||||||
const app = await makeAppFromEnv({
|
const app = await makeAppFromEnv({
|
||||||
server: "node",
|
server: "node",
|
||||||
});
|
});
|
||||||
await app.build();
|
|
||||||
|
|
||||||
const et = new EntityTypescript(app.em);
|
const et = new EntityTypescript(app.em);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
||||||
|
import { syncTypes } from "bknd/plugins";
|
||||||
|
import { writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
|
const isDev = !import.meta.env.PROD;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
d1: {
|
d1: {
|
||||||
session: true,
|
session: true,
|
||||||
},
|
},
|
||||||
|
options: {
|
||||||
|
plugins: [
|
||||||
|
syncTypes({
|
||||||
|
enabled: isDev,
|
||||||
|
write: async (et) => {
|
||||||
|
await writeFile("bknd-types.d.ts", et.toString());
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
} satisfies CloudflareBkndConfig;
|
} satisfies CloudflareBkndConfig;
|
||||||
|
|||||||
Reference in New Issue
Block a user