@torpor/adapter-cloudflare 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -14,7 +14,8 @@ function detectSourceMode(siteRoot) {
14
14
  if (process.env.TORPOR_SOURCE_DEV) return true;
15
15
  return ["@torpor/build", "@torpor/view"].every((pkg) => {
16
16
  try {
17
- return !realpathSync(path.resolve(siteRoot, "node_modules", pkg, "dist/index.mjs")).includes("node_modules");
17
+ const dist = path.resolve(siteRoot, "node_modules", pkg, "dist/index.mjs");
18
+ return !realpathSync(dist).includes("node_modules");
18
19
  } catch {
19
20
  return false;
20
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["fs","fs"],"sources":["../src/entryPaths.ts","../src/prepareTemplate.ts","../src/cloudflareDev.ts","../src/adapter.ts"],"sourcesContent":["import { realpathSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Mirrors the entry-path logic in @torpor/build's `utils/entryPaths.ts` (kept\n// local so the adapter stays compatible with older @torpor/build installs).\n\n/**\n * Whether the framework is symlinked into the app from outside node_modules\n * (workspace links and `link:` overrides), meaning tb runs it from source via\n * the `development` export conditions. Registry installs always run dist.\n */\nexport function detectSourceMode(siteRoot: string): boolean {\n\tif (process.env.TORPOR_SOURCE_DEV) return true;\n\tconst packages = [\"@torpor/build\", \"@torpor/view\"];\n\treturn packages.every((pkg) => {\n\t\ttry {\n\t\t\tconst dist = path.resolve(siteRoot, \"node_modules\", pkg, \"dist/index.mjs\");\n\t\t\tconst real = realpathSync(dist);\n\t\t\treturn !real.includes(\"node_modules\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\n/**\n * The site client entry files for the given mode: raw TypeScript sources when\n * the framework is linked, compiled `.mjs` bundles for registry installs.\n */\nexport function siteEntryPaths(\n\tsiteRoot: string,\n\tsourceMode: boolean,\n): { clientEntry: string; clientDevEntry: string } {\n\tconst folder = path.resolve(\n\t\tsiteRoot,\n\t\t\"node_modules/@torpor/build\",\n\t\tsourceMode ? \"src/site\" : \"dist\",\n\t);\n\tconst ext = sourceMode ? \".ts\" : \".mjs\";\n\treturn {\n\t\tclientEntry: path.join(folder, `clientEntry${ext}`),\n\t\tclientDevEntry: path.join(folder, `clientEntryDev${ext}`),\n\t};\n}\n\n/**\n * The torpor `Server` class module that the production `_worker.ts` imports,\n * for the given mode.\n */\nexport function serverClassPath(siteRoot: string, sourceMode: boolean): string {\n\treturn path.resolve(\n\t\tsiteRoot,\n\t\t\"node_modules/@torpor/build\",\n\t\tsourceMode ? \"src/server/Server.ts\" : \"dist/server/Server.mjs\",\n\t);\n}\n","export default function prepareTemplate(template: string, clientScript: string): string {\n\tlet result = template;\n\n\t// Put %COMPONENT_HEAD% before </head>\n\t// This is where styles will go\n\tlet headStart = result.indexOf(\"</head>\");\n\tif (headStart === -1) {\n\t\tthrow new Error(`Couldn't find <head> end tag`);\n\t}\n\tresult = result.substring(0, headStart) + \"%COMPONENT_HEAD%\" + result.substring(headStart);\n\n\t// Put %COMPONENT_BODY% inside <div id=\"app\"></div>\n\t// This is where the component's HTML will go\n\tlet bodyStart = regexIndexOf(result, /<div\\s+id=(\"app\"|'app'|app)\\s+/);\n\tbodyStart = result.indexOf(\">\", bodyStart) + 1;\n\tlet bodyEnd = result.indexOf(\"</div>\", bodyStart);\n\tif (bodyStart === -1 || bodyEnd === -1) {\n\t\tthrow new Error(`Couldn't find <div id=\"app\"></div>`);\n\t}\n\tresult = result.substring(0, bodyStart) + \"%COMPONENT_BODY%\" + result.substring(bodyEnd);\n\n\t// Add the clientEntry script at the very end, for hydrating and navigating\n\tresult += `<script type=\"module\" src=\"${clientScript}\"></script>`;\n\n\treturn result;\n}\n\n// From https://stackoverflow.com/a/274094\nfunction regexIndexOf(string: string, regex: RegExp, position?: number) {\n\tvar indexOf = string.substring(position || 0).search(regex);\n\treturn indexOf >= 0 ? indexOf + (position || 0) : indexOf;\n}\n","import { cloudflare } from \"@cloudflare/vite-plugin\";\nimport { type Site } from \"@torpor/build\";\nimport { existsSync, promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { type Plugin, type ViteDevServer } from \"vite\";\nimport { detectSourceMode, siteEntryPaths } from \"./entryPaths\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nconst TEMPLATE_ID = \"virtual:torpor-template\";\nconst RESOLVED_TEMPLATE_ID = `\\0${TEMPLATE_ID}`;\n\n/**\n * Vite plugin(s) for Cloudflare dev. Delegates the workerd runtime + bindings +\n * HMR to `@cloudflare/vite-plugin` (assigning the Worker to Vite's `ssr`\n * environment, so torpor's server code runs in workerd), overriding `main` to\n * point at the adapter's dev worker, and exposing the prepared `site.html`\n * template through a virtual module (since `transformIndexHtml` has to run\n * Node-side but the template is consumed inside workerd).\n */\nexport default function cloudflareDev(site: Site): Plugin[] {\n\tlet viteServer: ViteDevServer | undefined;\n\tlet templatePromise: Promise<string | undefined> | undefined;\n\n\tconst templatePlugin: Plugin = {\n\t\tname: \"torpor-cloudflare-template\",\n\t\tconfigureServer(server) {\n\t\t\tviteServer = server;\n\t\t},\n\t\tresolveId(id) {\n\t\t\tif (id === TEMPLATE_ID) return RESOLVED_TEMPLATE_ID;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id !== RESOLVED_TEMPLATE_ID) return;\n\t\t\ttemplatePromise ??= buildDevTemplate(viteServer!, site);\n\t\t\treturn `export default ${JSON.stringify(await templatePromise)};`;\n\t\t},\n\t};\n\n\tconst cfPlugins = cloudflare({\n\t\tviteEnvironment: { name: \"ssr\" },\n\t\tconfig: {\n\t\t\tmain: \"./node_modules/@torpor/adapter-cloudflare/dist/_worker.dev.ts\",\n\t\t},\n\t});\n\n\treturn [templatePlugin, ...cfPlugins];\n}\n\nasync function buildDevTemplate(vite: ViteDevServer, site: Site): Promise<string | undefined> {\n\t// Read site.html if present. An endpoints-only site has no site.html, and\n\t// no template is needed\n\tconst templateFile = path.resolve(site.root, \"src/site.html\");\n\tif (!existsSync(templateFile)) {\n\t\treturn undefined;\n\t}\n\tlet template = await fs.readFile(templateFile, \"utf-8\");\n\ttemplate = await vite.transformIndexHtml(\"\", template);\n\n\tconst sourceMode = detectSourceMode(site.root);\n\tconst entries = siteEntryPaths(site.root, sourceMode);\n\n\t// Splice component placeholders and append the production client entry\n\ttemplate = prepareTemplate(template, entries.clientEntry);\n\n\t// Inject the dev client entry (for HMR) just before the client entry\n\tconst clientTag = `<script type=\"module\" src=\"${entries.clientEntry}\"></script>`;\n\ttemplate = template.replace(\n\t\tclientTag,\n\t\t`<script type=\"module\" src=\"${entries.clientDevEntry}\"></script>\\n${clientTag}`,\n\t);\n\n\treturn template;\n}\n","import { type Adapter, Site } from \"@torpor/build\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { existsSync, promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { build, defineConfig } from \"vite\";\nimport { detectSourceMode, serverClassPath } from \"./entryPaths\";\nimport cloudflareDev from \"./cloudflareDev\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nexport default {\n\tpostbuild,\n\tdev: (site: Site) => cloudflareDev(site),\n\tserve: (_server, site: Site) => {\n\t\t// Preview the built Cloudflare worker (from postbuild) with `wrangler dev`\n\t\t// rather than a Node server, so preview matches production (workerd).\n\t\tconst configPath = path.join(site.root, \"dist\", \"wrangler.toml\");\n\t\tconst args = [\"dev\", \"--config\", configPath];\n\n\t\t// Resolve the wrangler bin from the consuming site's node_modules; fall\n\t\t// back to `wrangler` on PATH (e.g. when run via a package manager).\n\t\tlet child;\n\t\ttry {\n\t\t\tconst require = createRequire(path.join(site.root, \"package.json\"));\n\t\t\tconst wranglerBin = require.resolve(\"wrangler/bin/wrangler.js\");\n\t\t\tchild = spawn(process.execPath, [wranglerBin, ...args], {\n\t\t\t\tstdio: \"inherit\",\n\t\t\t\tcwd: site.root,\n\t\t\t});\n\t\t} catch {\n\t\t\tchild = spawn(\"wrangler\", args, { stdio: \"inherit\", cwd: site.root });\n\t\t}\n\t\tchild.on(\"exit\", (code) => process.exit(code ?? 0));\n\t},\n} satisfies Adapter as Adapter;\n\nasync function postbuild(site: Site): Promise<void> {\n\t// Build _worker.js as per\n\t// https://developers.cloudflare.com/pages/functions/advanced-mode/\n\n\tconst distFolder = path.resolve(site.root, \"dist\");\n\tconst clientFolder = path.join(distFolder, \"client\");\n\tconst serverFolder = path.join(distFolder, \"server\");\n\tconst tempFolder = path.join(distFolder, \"temp\");\n\tconst cloudflareFolder = path.join(distFolder, \"cloudflare\");\n\n\tconst sourceMode = detectSourceMode(site.root);\n\tconst adapterDistFolder = path.resolve(\n\t\tsite.root,\n\t\t\"./node_modules/@torpor/adapter-cloudflare/dist/\",\n\t);\n\n\t// Compile _worker.ts\n\tlet workerFile = path.join(adapterDistFolder, \"_worker.ts\");\n\tlet workerSource = await fs.readFile(workerFile, \"utf-8\");\n\n\t// Read and prepare site.html so that we can just splice components into it.\n\t// An endpoints-only site has no site.html, and no template is needed\n\tlet template: string | undefined;\n\tconst siteHtml = path.join(clientFolder, \"site.html\");\n\tif (existsSync(siteHtml)) {\n\t\tlet clientScript = (await fs.readdir(path.join(clientFolder, \"assets\"))).find((f) =>\n\t\t\tf.startsWith(\"clientEntry-\"),\n\t\t);\n\t\tif (!clientScript) {\n\t\t\tthrow new Error(\"clientEntry.js not found\");\n\t\t}\n\t\tclientScript = `/assets/${clientScript}`;\n\n\t\ttemplate = prepareTemplate(await fs.readFile(siteHtml, \"utf-8\"), clientScript);\n\t}\n\n\tconst serverClass = serverClassPath(site.root, sourceMode);\n\tconst serverScript = path.join(serverFolder, \"serverEntry.js\");\n\n\t// Splice file names into _worker.ts\n\tworkerSource = workerSource\n\t\t.replace(\"%SERVER_CLASS%\", serverClass)\n\t\t.replace(\"%SERVER_SCRIPT%\", serverScript)\n\t\t.replace(\"`%HTML_TEMPLATE%`\", () => (template ? JSON.stringify(template) : \"undefined\"));\n\n\tworkerFile = path.join(tempFolder, \"_worker.ts\");\n\tawait fs.mkdir(tempFolder);\n\tawait fs.writeFile(workerFile, workerSource);\n\n\tconst cloudflareConfig = structuredClone(site.viteConfig ?? {});\n\tcloudflareConfig.build ??= {};\n\tcloudflareConfig.build.outDir = cloudflareFolder;\n\tcloudflareConfig.build.rollupOptions ??= {};\n\tcloudflareConfig.build.rollupOptions.input = [workerFile, ...site.inputs];\n\tcloudflareConfig.build.rollupOptions.output = {\n\t\tentryFileNames: `[name].js`,\n\t\tchunkFileNames: `[name].js`,\n\t\tassetFileNames: `[name].[ext]`,\n\t};\n\tcloudflareConfig.build.rollupOptions.preserveEntrySignatures = \"strict\";\n\t// HACK: We don't need to minify as the worker isn't downloaded?\n\t// And it makes debugging easier\n\tcloudflareConfig.build.minify = false;\n\t// HACK: Build for SSR so we don't get document and window references\n\tcloudflareConfig.build.ssr = true;\n\tawait build(defineConfig(cloudflareConfig));\n\n\t// Copy the client assets into the Cloudflare folder\n\tawait fs.mkdir(path.join(cloudflareFolder, \"assets\"));\n\tfor (let file of await fs.readdir(path.join(clientFolder, \"assets\"))) {\n\t\tawait fs.copyFile(\n\t\t\tpath.join(clientFolder, \"assets\", file),\n\t\t\tpath.join(cloudflareFolder, \"assets\", file),\n\t\t);\n\t}\n\n\t// Build a wrangler.toml file for running with `wrangler dev`\n\tconst wranglerConfig = `\nname = \"torpor-miniflare\"\nmain = \"./cloudflare/_worker.js\"\ncompatibility_date = \"2025-01-01\"\n\n[assets]\ndirectory = \"./cloudflare\"\nbinding = \"ASSETS\"\n\t`;\n\tconst wranglerFile = path.join(distFolder, \"wrangler.toml\");\n\tawait fs.writeFile(wranglerFile, wranglerConfig);\n\n\t// Build a .assetsignore file\n\tconst assetsIgnore = `\n**/node_modules\n**/.DS_Store\n**/.git\n_worker.js\n`;\n\tconst assetsIgnoreFile = path.join(cloudflareFolder, \".assetsignore\");\n\tawait fs.writeFile(assetsIgnoreFile, assetsIgnore);\n\n\tawait fs.rm(tempFolder, { recursive: true });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["fs","fs"],"sources":["../src/entryPaths.ts","../src/prepareTemplate.ts","../src/cloudflareDev.ts","../src/adapter.ts"],"sourcesContent":["import { realpathSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Mirrors the entry-path logic in @torpor/build's `utils/entryPaths.ts` (kept\n// local so the adapter stays compatible with older @torpor/build installs).\n\n/**\n * Whether the framework is symlinked into the app from outside node_modules\n * (workspace links and `link:` overrides), meaning tb runs it from source via\n * the `development` export conditions. Registry installs always run dist.\n */\nexport function detectSourceMode(siteRoot: string): boolean {\n\tif (process.env.TORPOR_SOURCE_DEV) return true;\n\tconst packages = [\"@torpor/build\", \"@torpor/view\"];\n\treturn packages.every((pkg) => {\n\t\ttry {\n\t\t\tconst dist = path.resolve(siteRoot, \"node_modules\", pkg, \"dist/index.mjs\");\n\t\t\tconst real = realpathSync(dist);\n\t\t\treturn !real.includes(\"node_modules\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\n/**\n * The site client entry files for the given mode: raw TypeScript sources when\n * the framework is linked, compiled `.mjs` bundles for registry installs.\n */\nexport function siteEntryPaths(\n\tsiteRoot: string,\n\tsourceMode: boolean,\n): { clientEntry: string; clientDevEntry: string } {\n\tconst folder = path.resolve(\n\t\tsiteRoot,\n\t\t\"node_modules/@torpor/build\",\n\t\tsourceMode ? \"src/site\" : \"dist\",\n\t);\n\tconst ext = sourceMode ? \".ts\" : \".mjs\";\n\treturn {\n\t\tclientEntry: path.join(folder, `clientEntry${ext}`),\n\t\tclientDevEntry: path.join(folder, `clientEntryDev${ext}`),\n\t};\n}\n\n/**\n * The torpor `Server` class module that the production `_worker.ts` imports,\n * for the given mode.\n */\nexport function serverClassPath(siteRoot: string, sourceMode: boolean): string {\n\treturn path.resolve(\n\t\tsiteRoot,\n\t\t\"node_modules/@torpor/build\",\n\t\tsourceMode ? \"src/server/Server.ts\" : \"dist/server/Server.mjs\",\n\t);\n}\n","export default function prepareTemplate(template: string, clientScript: string): string {\n\tlet result = template;\n\n\t// Put %COMPONENT_HEAD% before </head>\n\t// This is where styles will go\n\tlet headStart = result.indexOf(\"</head>\");\n\tif (headStart === -1) {\n\t\tthrow new Error(`Couldn't find <head> end tag`);\n\t}\n\tresult = result.substring(0, headStart) + \"%COMPONENT_HEAD%\" + result.substring(headStart);\n\n\t// Put %COMPONENT_BODY% inside <div id=\"app\"></div>\n\t// This is where the component's HTML will go\n\tlet bodyStart = regexIndexOf(result, /<div\\s+id=(\"app\"|'app'|app)\\s+/);\n\tbodyStart = result.indexOf(\">\", bodyStart) + 1;\n\tlet bodyEnd = result.indexOf(\"</div>\", bodyStart);\n\tif (bodyStart === -1 || bodyEnd === -1) {\n\t\tthrow new Error(`Couldn't find <div id=\"app\"></div>`);\n\t}\n\tresult = result.substring(0, bodyStart) + \"%COMPONENT_BODY%\" + result.substring(bodyEnd);\n\n\t// Add the clientEntry script at the very end, for hydrating and navigating\n\tresult += `<script type=\"module\" src=\"${clientScript}\"></script>`;\n\n\treturn result;\n}\n\n// From https://stackoverflow.com/a/274094\nfunction regexIndexOf(string: string, regex: RegExp, position?: number) {\n\tvar indexOf = string.substring(position || 0).search(regex);\n\treturn indexOf >= 0 ? indexOf + (position || 0) : indexOf;\n}\n","import { cloudflare } from \"@cloudflare/vite-plugin\";\nimport { type Site } from \"@torpor/build\";\nimport { existsSync, promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { type Plugin, type ViteDevServer } from \"vite\";\nimport { detectSourceMode, siteEntryPaths } from \"./entryPaths\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nconst TEMPLATE_ID = \"virtual:torpor-template\";\nconst RESOLVED_TEMPLATE_ID = `\\0${TEMPLATE_ID}`;\n\n/**\n * Vite plugin(s) for Cloudflare dev. Delegates the workerd runtime + bindings +\n * HMR to `@cloudflare/vite-plugin` (assigning the Worker to Vite's `ssr`\n * environment, so torpor's server code runs in workerd), overriding `main` to\n * point at the adapter's dev worker, and exposing the prepared `site.html`\n * template through a virtual module (since `transformIndexHtml` has to run\n * Node-side but the template is consumed inside workerd).\n */\nexport default function cloudflareDev(site: Site): Plugin[] {\n\tlet viteServer: ViteDevServer | undefined;\n\tlet templatePromise: Promise<string | undefined> | undefined;\n\n\tconst templatePlugin: Plugin = {\n\t\tname: \"torpor-cloudflare-template\",\n\t\tconfigureServer(server) {\n\t\t\tviteServer = server;\n\t\t},\n\t\tresolveId(id) {\n\t\t\tif (id === TEMPLATE_ID) return RESOLVED_TEMPLATE_ID;\n\t\t},\n\t\tasync load(id) {\n\t\t\tif (id !== RESOLVED_TEMPLATE_ID) return;\n\t\t\ttemplatePromise ??= buildDevTemplate(viteServer!, site);\n\t\t\treturn `export default ${JSON.stringify(await templatePromise)};`;\n\t\t},\n\t};\n\n\tconst cfPlugins = cloudflare({\n\t\tviteEnvironment: { name: \"ssr\" },\n\t\tconfig: {\n\t\t\tmain: \"./node_modules/@torpor/adapter-cloudflare/dist/_worker.dev.ts\",\n\t\t},\n\t});\n\n\treturn [templatePlugin, ...cfPlugins];\n}\n\nasync function buildDevTemplate(vite: ViteDevServer, site: Site): Promise<string | undefined> {\n\t// Read site.html if present. An endpoints-only site has no site.html, and\n\t// no template is needed\n\tconst templateFile = path.resolve(site.root, \"src/site.html\");\n\tif (!existsSync(templateFile)) {\n\t\treturn undefined;\n\t}\n\tlet template = await fs.readFile(templateFile, \"utf-8\");\n\ttemplate = await vite.transformIndexHtml(\"\", template);\n\n\tconst sourceMode = detectSourceMode(site.root);\n\tconst entries = siteEntryPaths(site.root, sourceMode);\n\n\t// Splice component placeholders and append the production client entry\n\ttemplate = prepareTemplate(template, entries.clientEntry);\n\n\t// Inject the dev client entry (for HMR) just before the client entry\n\tconst clientTag = `<script type=\"module\" src=\"${entries.clientEntry}\"></script>`;\n\ttemplate = template.replace(\n\t\tclientTag,\n\t\t`<script type=\"module\" src=\"${entries.clientDevEntry}\"></script>\\n${clientTag}`,\n\t);\n\n\treturn template;\n}\n","import { type Adapter, Site } from \"@torpor/build\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { existsSync, promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { build, defineConfig } from \"vite\";\nimport { detectSourceMode, serverClassPath } from \"./entryPaths\";\nimport cloudflareDev from \"./cloudflareDev\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nexport default {\n\tpostbuild,\n\tdev: (site: Site) => cloudflareDev(site),\n\tserve: (_server, site: Site) => {\n\t\t// Preview the built Cloudflare worker (from postbuild) with `wrangler dev`\n\t\t// rather than a Node server, so preview matches production (workerd).\n\t\tconst configPath = path.join(site.root, \"dist\", \"wrangler.toml\");\n\t\tconst args = [\"dev\", \"--config\", configPath];\n\n\t\t// Resolve the wrangler bin from the consuming site's node_modules; fall\n\t\t// back to `wrangler` on PATH (e.g. when run via a package manager).\n\t\tlet child;\n\t\ttry {\n\t\t\tconst require = createRequire(path.join(site.root, \"package.json\"));\n\t\t\tconst wranglerBin = require.resolve(\"wrangler/bin/wrangler.js\");\n\t\t\tchild = spawn(process.execPath, [wranglerBin, ...args], {\n\t\t\t\tstdio: \"inherit\",\n\t\t\t\tcwd: site.root,\n\t\t\t});\n\t\t} catch {\n\t\t\tchild = spawn(\"wrangler\", args, { stdio: \"inherit\", cwd: site.root });\n\t\t}\n\t\tchild.on(\"exit\", (code) => process.exit(code ?? 0));\n\t},\n} satisfies Adapter as Adapter;\n\nasync function postbuild(site: Site): Promise<void> {\n\t// Build _worker.js as per\n\t// https://developers.cloudflare.com/pages/functions/advanced-mode/\n\n\tconst distFolder = path.resolve(site.root, \"dist\");\n\tconst clientFolder = path.join(distFolder, \"client\");\n\tconst serverFolder = path.join(distFolder, \"server\");\n\tconst tempFolder = path.join(distFolder, \"temp\");\n\tconst cloudflareFolder = path.join(distFolder, \"cloudflare\");\n\n\tconst sourceMode = detectSourceMode(site.root);\n\tconst adapterDistFolder = path.resolve(\n\t\tsite.root,\n\t\t\"./node_modules/@torpor/adapter-cloudflare/dist/\",\n\t);\n\n\t// Compile _worker.ts\n\tlet workerFile = path.join(adapterDistFolder, \"_worker.ts\");\n\tlet workerSource = await fs.readFile(workerFile, \"utf-8\");\n\n\t// Read and prepare site.html so that we can just splice components into it.\n\t// An endpoints-only site has no site.html, and no template is needed\n\tlet template: string | undefined;\n\tconst siteHtml = path.join(clientFolder, \"site.html\");\n\tif (existsSync(siteHtml)) {\n\t\tlet clientScript = (await fs.readdir(path.join(clientFolder, \"assets\"))).find((f) =>\n\t\t\tf.startsWith(\"clientEntry-\"),\n\t\t);\n\t\tif (!clientScript) {\n\t\t\tthrow new Error(\"clientEntry.js not found\");\n\t\t}\n\t\tclientScript = `/assets/${clientScript}`;\n\n\t\ttemplate = prepareTemplate(await fs.readFile(siteHtml, \"utf-8\"), clientScript);\n\t}\n\n\tconst serverClass = serverClassPath(site.root, sourceMode);\n\tconst serverScript = path.join(serverFolder, \"serverEntry.js\");\n\n\t// Splice file names into _worker.ts\n\tworkerSource = workerSource\n\t\t.replace(\"%SERVER_CLASS%\", serverClass)\n\t\t.replace(\"%SERVER_SCRIPT%\", serverScript)\n\t\t.replace(\"`%HTML_TEMPLATE%`\", () => (template ? JSON.stringify(template) : \"undefined\"));\n\n\tworkerFile = path.join(tempFolder, \"_worker.ts\");\n\tawait fs.mkdir(tempFolder);\n\tawait fs.writeFile(workerFile, workerSource);\n\n\tconst cloudflareConfig = structuredClone(site.viteConfig ?? {});\n\tcloudflareConfig.build ??= {};\n\tcloudflareConfig.build.outDir = cloudflareFolder;\n\tcloudflareConfig.build.rollupOptions ??= {};\n\tcloudflareConfig.build.rollupOptions.input = [workerFile, ...site.inputs];\n\tcloudflareConfig.build.rollupOptions.output = {\n\t\tentryFileNames: `[name].js`,\n\t\tchunkFileNames: `[name].js`,\n\t\tassetFileNames: `[name].[ext]`,\n\t};\n\tcloudflareConfig.build.rollupOptions.preserveEntrySignatures = \"strict\";\n\t// HACK: We don't need to minify as the worker isn't downloaded?\n\t// And it makes debugging easier\n\tcloudflareConfig.build.minify = false;\n\t// HACK: Build for SSR so we don't get document and window references\n\tcloudflareConfig.build.ssr = true;\n\tawait build(defineConfig(cloudflareConfig));\n\n\t// Copy the client assets into the Cloudflare folder\n\tawait fs.mkdir(path.join(cloudflareFolder, \"assets\"));\n\tfor (let file of await fs.readdir(path.join(clientFolder, \"assets\"))) {\n\t\tawait fs.copyFile(\n\t\t\tpath.join(clientFolder, \"assets\", file),\n\t\t\tpath.join(cloudflareFolder, \"assets\", file),\n\t\t);\n\t}\n\n\t// Build a wrangler.toml file for running with `wrangler dev`\n\tconst wranglerConfig = `\nname = \"torpor-miniflare\"\nmain = \"./cloudflare/_worker.js\"\ncompatibility_date = \"2025-01-01\"\n\n[assets]\ndirectory = \"./cloudflare\"\nbinding = \"ASSETS\"\n\t`;\n\tconst wranglerFile = path.join(distFolder, \"wrangler.toml\");\n\tawait fs.writeFile(wranglerFile, wranglerConfig);\n\n\t// Build a .assetsignore file\n\tconst assetsIgnore = `\n**/node_modules\n**/.DS_Store\n**/.git\n_worker.js\n`;\n\tconst assetsIgnoreFile = path.join(cloudflareFolder, \".assetsignore\");\n\tawait fs.writeFile(assetsIgnoreFile, assetsIgnore);\n\n\tawait fs.rm(tempFolder, { recursive: true });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torpor/adapter-cloudflare",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A @torpor/build adapter for deploying to Cloudflare Pages",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,8 +17,8 @@
17
17
  "author": "",
18
18
  "license": "ISC",
19
19
  "dependencies": {
20
- "vite": "npm:@voidzero-dev/vite-plus-core@0.2.7",
21
- "@torpor/build": "^1.0.1"
20
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
21
+ "@torpor/build": "^1.1.0"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "@cloudflare/vite-plugin": "^1.0.0",
@@ -28,7 +28,7 @@
28
28
  "@cloudflare/vite-plugin": "^1.50.0",
29
29
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
30
30
  "typescript": "^7.0.2",
31
- "vite-plus": "0.2.7",
31
+ "vite-plus": "0.3.0",
32
32
  "wrangler": "^4.118.0"
33
33
  },
34
34
  "scripts": {