@torpor/adapter-cloudflare 0.0.1
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/LICENSE.md +5 -0
- package/README.md +9 -0
- package/dist/index.cjs +139 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/package.json +32 -0
- package/src/_worker.ts +39 -0
- package/src/adapter.ts +108 -0
- package/src/index.ts +3 -0
- package/src/prepareTemplate.ts +20 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +3 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright 2024 <OWNER>
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
cloudflare: () => adapter_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/adapter.ts
|
|
38
|
+
var import_node_fs = require("fs");
|
|
39
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
40
|
+
var import_vite = require("vite");
|
|
41
|
+
|
|
42
|
+
// src/prepareTemplate.ts
|
|
43
|
+
function prepareTemplate(template, clientScript) {
|
|
44
|
+
let contentStart = regexIndexOf(template, /\<div\s+id=("app"|'app'|app)\s+/);
|
|
45
|
+
contentStart = template.indexOf(">", contentStart) + 1;
|
|
46
|
+
let contentEnd = template.indexOf("</div>", contentStart);
|
|
47
|
+
if (contentStart === -1 || contentEnd === -1) {
|
|
48
|
+
throw new Error(`Couldn't find <div id="app"></div>`);
|
|
49
|
+
}
|
|
50
|
+
return template.substring(0, contentStart) + "%COMPONENT_HTML%" + template.substring(contentEnd) + `<script type="module" src="${clientScript}"></script>`;
|
|
51
|
+
}
|
|
52
|
+
function regexIndexOf(string, regex, position) {
|
|
53
|
+
var indexOf = string.substring(position || 0).search(regex);
|
|
54
|
+
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/adapter.ts
|
|
58
|
+
var adapter_default = {
|
|
59
|
+
postbuild,
|
|
60
|
+
serve: () => {
|
|
61
|
+
console.log("TODO");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
async function postbuild(site) {
|
|
65
|
+
const distFolder = import_node_path.default.resolve(site.root, "dist");
|
|
66
|
+
const clientFolder = import_node_path.default.join(distFolder, "client");
|
|
67
|
+
const serverFolder = import_node_path.default.join(distFolder, "server");
|
|
68
|
+
const tempFolder = import_node_path.default.join(distFolder, "temp");
|
|
69
|
+
const cloudflareFolder = import_node_path.default.join(distFolder, "cloudflare");
|
|
70
|
+
const buildSrcFolder = import_node_path.default.resolve(site.root, "./node_modules/@torpor/build/src/");
|
|
71
|
+
const adapterSrcFolder = import_node_path.default.resolve(
|
|
72
|
+
site.root,
|
|
73
|
+
"./node_modules/@torpor/adapter-cloudflare/src/"
|
|
74
|
+
);
|
|
75
|
+
let workerFile = import_node_path.default.join(adapterSrcFolder, "_worker.ts");
|
|
76
|
+
let workerSource = await import_node_fs.promises.readFile(workerFile, "utf-8");
|
|
77
|
+
let clientScript = (await import_node_fs.promises.readdir(import_node_path.default.join(clientFolder, "assets"))).find(
|
|
78
|
+
(f) => f.startsWith("clientEntry-")
|
|
79
|
+
);
|
|
80
|
+
if (!clientScript) {
|
|
81
|
+
throw new Error("clientEntry.js not found");
|
|
82
|
+
}
|
|
83
|
+
clientScript = `/assets/${clientScript}`;
|
|
84
|
+
let siteHtml = import_node_path.default.join(clientFolder, "site.html");
|
|
85
|
+
let template = await import_node_fs.promises.readFile(siteHtml, "utf-8");
|
|
86
|
+
template = prepareTemplate(template, clientScript);
|
|
87
|
+
const serverClass = import_node_path.default.join(buildSrcFolder, "server", "Server.ts");
|
|
88
|
+
const serverScript = import_node_path.default.join(serverFolder, "serverEntry.js");
|
|
89
|
+
workerSource = workerSource.replace("%SERVER_CLASS%", serverClass).replace("%SERVER_SCRIPT%", serverScript).replace("%HTML_TEMPLATE%", template);
|
|
90
|
+
workerFile = import_node_path.default.join(tempFolder, "_worker.ts");
|
|
91
|
+
await import_node_fs.promises.mkdir(tempFolder);
|
|
92
|
+
await import_node_fs.promises.writeFile(workerFile, workerSource);
|
|
93
|
+
await (0, import_vite.build)(
|
|
94
|
+
(0, import_vite.defineConfig)({
|
|
95
|
+
build: {
|
|
96
|
+
outDir: cloudflareFolder,
|
|
97
|
+
rollupOptions: {
|
|
98
|
+
input: [workerFile],
|
|
99
|
+
output: {
|
|
100
|
+
entryFileNames: `[name].js`,
|
|
101
|
+
chunkFileNames: `[name].js`,
|
|
102
|
+
assetFileNames: `[name].[ext]`
|
|
103
|
+
},
|
|
104
|
+
preserveEntrySignatures: "strict"
|
|
105
|
+
},
|
|
106
|
+
// HACK: We don't need to minify as the worker isn't downloaded?
|
|
107
|
+
// And it makes debugging easier
|
|
108
|
+
minify: false,
|
|
109
|
+
// HACK: Disable preload so that the _worker.js file doesn't end
|
|
110
|
+
// up with document and window references that are not available
|
|
111
|
+
modulePreload: false
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
await import_node_fs.promises.mkdir(import_node_path.default.join(cloudflareFolder, "assets"));
|
|
116
|
+
for (let file of await import_node_fs.promises.readdir(import_node_path.default.join(clientFolder, "assets"))) {
|
|
117
|
+
await import_node_fs.promises.copyFile(
|
|
118
|
+
import_node_path.default.join(clientFolder, "assets", file),
|
|
119
|
+
import_node_path.default.join(cloudflareFolder, "assets", file)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const wranglerConfig = `
|
|
123
|
+
name = "torpor-miniflare"
|
|
124
|
+
main = "./cloudflare/_worker.js"
|
|
125
|
+
compatibility_date = "2025-01-01"
|
|
126
|
+
|
|
127
|
+
[assets]
|
|
128
|
+
directory = "./cloudflare"
|
|
129
|
+
binding = "ASSETS"
|
|
130
|
+
`;
|
|
131
|
+
const wranglerFile = import_node_path.default.join(distFolder, "wrangler.toml");
|
|
132
|
+
await import_node_fs.promises.writeFile(wranglerFile, wranglerConfig);
|
|
133
|
+
await import_node_fs.promises.rm(tempFolder, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
136
|
+
0 && (module.exports = {
|
|
137
|
+
cloudflare
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/prepareTemplate.ts"],"sourcesContent":["import cloudflare from \"./adapter\";\n\nexport { cloudflare };\n","import { type Adapter, Site } from \"@torpor/build\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { build, defineConfig } from \"vite\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nexport default {\n\tpostbuild,\n\tserve: (/* server: Server, site: Site*/) => {\n\t\tconsole.log(\"TODO\");\n\t},\n} satisfies Adapter;\n\nasync function postbuild(site: Site) {\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\t// TODO: Is this going to be the correct path after installing from npm?\n\tconst buildSrcFolder = path.resolve(site.root, \"./node_modules/@torpor/build/src/\");\n\tconst adapterSrcFolder = path.resolve(\n\t\tsite.root,\n\t\t\"./node_modules/@torpor/adapter-cloudflare/src/\",\n\t);\n\n\t// Compile _worker.ts\n\tlet workerFile = path.join(adapterSrcFolder, \"_worker.ts\");\n\tlet workerSource = await fs.readFile(workerFile, \"utf-8\");\n\n\t// Find the client script file in /assets\n\tlet clientScript = (await fs.readdir(path.join(clientFolder, \"assets\"))).find((f) =>\n\t\tf.startsWith(\"clientEntry-\"),\n\t);\n\tif (!clientScript) {\n\t\tthrow new Error(\"clientEntry.js not found\");\n\t}\n\tclientScript = `/assets/${clientScript}`;\n\n\t// Read and prepare site.html so that we can just splice components into it\n\tlet siteHtml = path.join(clientFolder, \"site.html\");\n\tlet template = await fs.readFile(siteHtml, \"utf-8\");\n\ttemplate = prepareTemplate(template, clientScript);\n\n\tconst serverClass = path.join(buildSrcFolder, \"server\", \"Server.ts\");\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);\n\n\tworkerFile = path.join(tempFolder, \"_worker.ts\");\n\tawait fs.mkdir(tempFolder);\n\tawait fs.writeFile(workerFile, workerSource);\n\n\tawait build(\n\t\tdefineConfig({\n\t\t\tbuild: {\n\t\t\t\toutDir: cloudflareFolder,\n\t\t\t\trollupOptions: {\n\t\t\t\t\tinput: [workerFile],\n\t\t\t\t\toutput: {\n\t\t\t\t\t\tentryFileNames: `[name].js`,\n\t\t\t\t\t\tchunkFileNames: `[name].js`,\n\t\t\t\t\t\tassetFileNames: `[name].[ext]`,\n\t\t\t\t\t},\n\t\t\t\t\tpreserveEntrySignatures: \"strict\",\n\t\t\t\t},\n\t\t\t\t// HACK: We don't need to minify as the worker isn't downloaded?\n\t\t\t\t// And it makes debugging easier\n\t\t\t\tminify: false,\n\t\t\t\t// HACK: Disable preload so that the _worker.js file doesn't end\n\t\t\t\t// up with document and window references that are not available\n\t\t\t\tmodulePreload: false,\n\t\t\t},\n\t\t}),\n\t);\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\tawait fs.rm(tempFolder, { recursive: true });\n}\n","export default function prepareTemplate(template: string, clientScript: string) {\n\tlet contentStart = regexIndexOf(template, /\\<div\\s+id=(\"app\"|'app'|app)\\s+/);\n\tcontentStart = template.indexOf(\">\", contentStart) + 1;\n\tlet contentEnd = template.indexOf(\"</div>\", contentStart);\n\tif (contentStart === -1 || contentEnd === -1) {\n\t\tthrow new Error(`Couldn't find <div id=\"app\"></div>`);\n\t}\n\treturn (\n\t\ttemplate.substring(0, contentStart) +\n\t\t\"%COMPONENT_HTML%\" +\n\t\ttemplate.substring(contentEnd) +\n\t\t`<script type=\"module\" src=\"${clientScript}\"></script>`\n\t);\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"],"mappings":"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","names":["path","fs"]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/adapter.ts
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { build, defineConfig } from "vite";
|
|
5
|
+
|
|
6
|
+
// src/prepareTemplate.ts
|
|
7
|
+
function prepareTemplate(template, clientScript) {
|
|
8
|
+
let contentStart = regexIndexOf(template, /\<div\s+id=("app"|'app'|app)\s+/);
|
|
9
|
+
contentStart = template.indexOf(">", contentStart) + 1;
|
|
10
|
+
let contentEnd = template.indexOf("</div>", contentStart);
|
|
11
|
+
if (contentStart === -1 || contentEnd === -1) {
|
|
12
|
+
throw new Error(`Couldn't find <div id="app"></div>`);
|
|
13
|
+
}
|
|
14
|
+
return template.substring(0, contentStart) + "%COMPONENT_HTML%" + template.substring(contentEnd) + `<script type="module" src="${clientScript}"></script>`;
|
|
15
|
+
}
|
|
16
|
+
function regexIndexOf(string, regex, position) {
|
|
17
|
+
var indexOf = string.substring(position || 0).search(regex);
|
|
18
|
+
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/adapter.ts
|
|
22
|
+
var adapter_default = {
|
|
23
|
+
postbuild,
|
|
24
|
+
serve: () => {
|
|
25
|
+
console.log("TODO");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
async function postbuild(site) {
|
|
29
|
+
const distFolder = path.resolve(site.root, "dist");
|
|
30
|
+
const clientFolder = path.join(distFolder, "client");
|
|
31
|
+
const serverFolder = path.join(distFolder, "server");
|
|
32
|
+
const tempFolder = path.join(distFolder, "temp");
|
|
33
|
+
const cloudflareFolder = path.join(distFolder, "cloudflare");
|
|
34
|
+
const buildSrcFolder = path.resolve(site.root, "./node_modules/@torpor/build/src/");
|
|
35
|
+
const adapterSrcFolder = path.resolve(
|
|
36
|
+
site.root,
|
|
37
|
+
"./node_modules/@torpor/adapter-cloudflare/src/"
|
|
38
|
+
);
|
|
39
|
+
let workerFile = path.join(adapterSrcFolder, "_worker.ts");
|
|
40
|
+
let workerSource = await fs.readFile(workerFile, "utf-8");
|
|
41
|
+
let clientScript = (await fs.readdir(path.join(clientFolder, "assets"))).find(
|
|
42
|
+
(f) => f.startsWith("clientEntry-")
|
|
43
|
+
);
|
|
44
|
+
if (!clientScript) {
|
|
45
|
+
throw new Error("clientEntry.js not found");
|
|
46
|
+
}
|
|
47
|
+
clientScript = `/assets/${clientScript}`;
|
|
48
|
+
let siteHtml = path.join(clientFolder, "site.html");
|
|
49
|
+
let template = await fs.readFile(siteHtml, "utf-8");
|
|
50
|
+
template = prepareTemplate(template, clientScript);
|
|
51
|
+
const serverClass = path.join(buildSrcFolder, "server", "Server.ts");
|
|
52
|
+
const serverScript = path.join(serverFolder, "serverEntry.js");
|
|
53
|
+
workerSource = workerSource.replace("%SERVER_CLASS%", serverClass).replace("%SERVER_SCRIPT%", serverScript).replace("%HTML_TEMPLATE%", template);
|
|
54
|
+
workerFile = path.join(tempFolder, "_worker.ts");
|
|
55
|
+
await fs.mkdir(tempFolder);
|
|
56
|
+
await fs.writeFile(workerFile, workerSource);
|
|
57
|
+
await build(
|
|
58
|
+
defineConfig({
|
|
59
|
+
build: {
|
|
60
|
+
outDir: cloudflareFolder,
|
|
61
|
+
rollupOptions: {
|
|
62
|
+
input: [workerFile],
|
|
63
|
+
output: {
|
|
64
|
+
entryFileNames: `[name].js`,
|
|
65
|
+
chunkFileNames: `[name].js`,
|
|
66
|
+
assetFileNames: `[name].[ext]`
|
|
67
|
+
},
|
|
68
|
+
preserveEntrySignatures: "strict"
|
|
69
|
+
},
|
|
70
|
+
// HACK: We don't need to minify as the worker isn't downloaded?
|
|
71
|
+
// And it makes debugging easier
|
|
72
|
+
minify: false,
|
|
73
|
+
// HACK: Disable preload so that the _worker.js file doesn't end
|
|
74
|
+
// up with document and window references that are not available
|
|
75
|
+
modulePreload: false
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
await fs.mkdir(path.join(cloudflareFolder, "assets"));
|
|
80
|
+
for (let file of await fs.readdir(path.join(clientFolder, "assets"))) {
|
|
81
|
+
await fs.copyFile(
|
|
82
|
+
path.join(clientFolder, "assets", file),
|
|
83
|
+
path.join(cloudflareFolder, "assets", file)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const wranglerConfig = `
|
|
87
|
+
name = "torpor-miniflare"
|
|
88
|
+
main = "./cloudflare/_worker.js"
|
|
89
|
+
compatibility_date = "2025-01-01"
|
|
90
|
+
|
|
91
|
+
[assets]
|
|
92
|
+
directory = "./cloudflare"
|
|
93
|
+
binding = "ASSETS"
|
|
94
|
+
`;
|
|
95
|
+
const wranglerFile = path.join(distFolder, "wrangler.toml");
|
|
96
|
+
await fs.writeFile(wranglerFile, wranglerConfig);
|
|
97
|
+
await fs.rm(tempFolder, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
adapter_default as cloudflare
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/prepareTemplate.ts"],"sourcesContent":["import { type Adapter, Site } from \"@torpor/build\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { build, defineConfig } from \"vite\";\nimport prepareTemplate from \"./prepareTemplate\";\n\nexport default {\n\tpostbuild,\n\tserve: (/* server: Server, site: Site*/) => {\n\t\tconsole.log(\"TODO\");\n\t},\n} satisfies Adapter;\n\nasync function postbuild(site: Site) {\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\t// TODO: Is this going to be the correct path after installing from npm?\n\tconst buildSrcFolder = path.resolve(site.root, \"./node_modules/@torpor/build/src/\");\n\tconst adapterSrcFolder = path.resolve(\n\t\tsite.root,\n\t\t\"./node_modules/@torpor/adapter-cloudflare/src/\",\n\t);\n\n\t// Compile _worker.ts\n\tlet workerFile = path.join(adapterSrcFolder, \"_worker.ts\");\n\tlet workerSource = await fs.readFile(workerFile, \"utf-8\");\n\n\t// Find the client script file in /assets\n\tlet clientScript = (await fs.readdir(path.join(clientFolder, \"assets\"))).find((f) =>\n\t\tf.startsWith(\"clientEntry-\"),\n\t);\n\tif (!clientScript) {\n\t\tthrow new Error(\"clientEntry.js not found\");\n\t}\n\tclientScript = `/assets/${clientScript}`;\n\n\t// Read and prepare site.html so that we can just splice components into it\n\tlet siteHtml = path.join(clientFolder, \"site.html\");\n\tlet template = await fs.readFile(siteHtml, \"utf-8\");\n\ttemplate = prepareTemplate(template, clientScript);\n\n\tconst serverClass = path.join(buildSrcFolder, \"server\", \"Server.ts\");\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);\n\n\tworkerFile = path.join(tempFolder, \"_worker.ts\");\n\tawait fs.mkdir(tempFolder);\n\tawait fs.writeFile(workerFile, workerSource);\n\n\tawait build(\n\t\tdefineConfig({\n\t\t\tbuild: {\n\t\t\t\toutDir: cloudflareFolder,\n\t\t\t\trollupOptions: {\n\t\t\t\t\tinput: [workerFile],\n\t\t\t\t\toutput: {\n\t\t\t\t\t\tentryFileNames: `[name].js`,\n\t\t\t\t\t\tchunkFileNames: `[name].js`,\n\t\t\t\t\t\tassetFileNames: `[name].[ext]`,\n\t\t\t\t\t},\n\t\t\t\t\tpreserveEntrySignatures: \"strict\",\n\t\t\t\t},\n\t\t\t\t// HACK: We don't need to minify as the worker isn't downloaded?\n\t\t\t\t// And it makes debugging easier\n\t\t\t\tminify: false,\n\t\t\t\t// HACK: Disable preload so that the _worker.js file doesn't end\n\t\t\t\t// up with document and window references that are not available\n\t\t\t\tmodulePreload: false,\n\t\t\t},\n\t\t}),\n\t);\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\tawait fs.rm(tempFolder, { recursive: true });\n}\n","export default function prepareTemplate(template: string, clientScript: string) {\n\tlet contentStart = regexIndexOf(template, /\\<div\\s+id=(\"app\"|'app'|app)\\s+/);\n\tcontentStart = template.indexOf(\">\", contentStart) + 1;\n\tlet contentEnd = template.indexOf(\"</div>\", contentStart);\n\tif (contentStart === -1 || contentEnd === -1) {\n\t\tthrow new Error(`Couldn't find <div id=\"app\"></div>`);\n\t}\n\treturn (\n\t\ttemplate.substring(0, contentStart) +\n\t\t\"%COMPONENT_HTML%\" +\n\t\ttemplate.substring(contentEnd) +\n\t\t`<script type=\"module\" src=\"${clientScript}\"></script>`\n\t);\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"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"src/prepareTemplate.ts":{"bytes":801,"imports":[],"format":"esm"},"src/adapter.ts":{"bytes":3467,"imports":[{"path":"@torpor/build","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"vite","kind":"import-statement","external":true},{"path":"src/prepareTemplate.ts","kind":"import-statement","original":"./prepareTemplate"}],"format":"esm"},"src/index.ts":{"bytes":60,"imports":[{"path":"src/adapter.ts","kind":"import-statement","original":"./adapter"}],"format":"esm"}},"outputs":{"dist/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":7312},"dist/index.cjs":{"imports":[{"path":"fs","kind":"require-call","external":true},{"path":"path","kind":"require-call","external":true},{"path":"vite","kind":"require-call","external":true}],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":136},"src/adapter.ts":{"bytesInOutput":3569},"src/prepareTemplate.ts":{"bytesInOutput":698}},"bytes":5985}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"src/prepareTemplate.ts":{"bytes":801,"imports":[],"format":"esm"},"src/adapter.ts":{"bytes":3467,"imports":[{"path":"@torpor/build","kind":"import-statement","external":true},{"path":"node:fs","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"vite","kind":"import-statement","external":true},{"path":"src/prepareTemplate.ts","kind":"import-statement","original":"./prepareTemplate"}],"format":"esm"},"src/index.ts":{"bytes":60,"imports":[{"path":"src/adapter.ts","kind":"import-statement","original":"./adapter"}],"format":"esm"}},"outputs":{"dist/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":6990},"dist/index.js":{"imports":[{"path":"node:fs","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"vite","kind":"import-statement","external":true}],"exports":["cloudflare"],"entryPoint":"src/index.ts","inputs":{"src/adapter.ts":{"bytesInOutput":2958},"src/prepareTemplate.ts":{"bytesInOutput":698},"src/index.ts":{"bytesInOutput":0}},"bytes":3764}}}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@torpor/adapter-cloudflare",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A @torpor/build adapter for deploying to Cloudflare Pages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"vite": "^6.2.4",
|
|
17
|
+
"@torpor/build": "^0.1.3"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
21
|
+
"tsup": "^8.4.0",
|
|
22
|
+
"tsx": "^4.19.3",
|
|
23
|
+
"typescript": "^5.8.2",
|
|
24
|
+
"vitest": "^3.1.1"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"build:watch": "tsup --watch",
|
|
29
|
+
"check": "tsc --noEmit",
|
|
30
|
+
"test": "vitest"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/_worker.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
import Server from "%SERVER_CLASS%";
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { load } from "%SERVER_SCRIPT%";
|
|
5
|
+
|
|
6
|
+
// This file has the above imports replaced and is then compiled to create a
|
|
7
|
+
// Cloudflare Pages worker file
|
|
8
|
+
|
|
9
|
+
const template = `%HTML_TEMPLATE%`;
|
|
10
|
+
|
|
11
|
+
// Send all requests through Server to handle middleware, cookies, headers, etc
|
|
12
|
+
const server = new Server();
|
|
13
|
+
server.add("*", async (ev: any) => {
|
|
14
|
+
try {
|
|
15
|
+
// Render the app HTML (or fetch server data etc) via serverEntry's
|
|
16
|
+
// exported `load` function
|
|
17
|
+
return await load(ev, template);
|
|
18
|
+
} catch (e: any) {
|
|
19
|
+
// TODO: Message?
|
|
20
|
+
console.log("ERROR:", e);
|
|
21
|
+
return new Response(null, {
|
|
22
|
+
status: 500,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
async fetch(request: Request, env: any) {
|
|
29
|
+
const url = new URL(request.url);
|
|
30
|
+
|
|
31
|
+
// Serve dist/client/assets statically
|
|
32
|
+
if (url.pathname.startsWith("/assets/")) {
|
|
33
|
+
return env.ASSETS.fetch(request);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Every request (GET, POST, etc) goes through loadEndPoint
|
|
37
|
+
return await server.fetch(request);
|
|
38
|
+
},
|
|
39
|
+
};
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type Adapter, Site } from "@torpor/build";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { build, defineConfig } from "vite";
|
|
5
|
+
import prepareTemplate from "./prepareTemplate";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
postbuild,
|
|
9
|
+
serve: (/* server: Server, site: Site*/) => {
|
|
10
|
+
console.log("TODO");
|
|
11
|
+
},
|
|
12
|
+
} satisfies Adapter;
|
|
13
|
+
|
|
14
|
+
async function postbuild(site: Site) {
|
|
15
|
+
// Build _worker.js as per
|
|
16
|
+
// https://developers.cloudflare.com/pages/functions/advanced-mode/
|
|
17
|
+
|
|
18
|
+
const distFolder = path.resolve(site.root, "dist");
|
|
19
|
+
const clientFolder = path.join(distFolder, "client");
|
|
20
|
+
const serverFolder = path.join(distFolder, "server");
|
|
21
|
+
const tempFolder = path.join(distFolder, "temp");
|
|
22
|
+
const cloudflareFolder = path.join(distFolder, "cloudflare");
|
|
23
|
+
|
|
24
|
+
// TODO: Is this going to be the correct path after installing from npm?
|
|
25
|
+
const buildSrcFolder = path.resolve(site.root, "./node_modules/@torpor/build/src/");
|
|
26
|
+
const adapterSrcFolder = path.resolve(
|
|
27
|
+
site.root,
|
|
28
|
+
"./node_modules/@torpor/adapter-cloudflare/src/",
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Compile _worker.ts
|
|
32
|
+
let workerFile = path.join(adapterSrcFolder, "_worker.ts");
|
|
33
|
+
let workerSource = await fs.readFile(workerFile, "utf-8");
|
|
34
|
+
|
|
35
|
+
// Find the client script file in /assets
|
|
36
|
+
let clientScript = (await fs.readdir(path.join(clientFolder, "assets"))).find((f) =>
|
|
37
|
+
f.startsWith("clientEntry-"),
|
|
38
|
+
);
|
|
39
|
+
if (!clientScript) {
|
|
40
|
+
throw new Error("clientEntry.js not found");
|
|
41
|
+
}
|
|
42
|
+
clientScript = `/assets/${clientScript}`;
|
|
43
|
+
|
|
44
|
+
// Read and prepare site.html so that we can just splice components into it
|
|
45
|
+
let siteHtml = path.join(clientFolder, "site.html");
|
|
46
|
+
let template = await fs.readFile(siteHtml, "utf-8");
|
|
47
|
+
template = prepareTemplate(template, clientScript);
|
|
48
|
+
|
|
49
|
+
const serverClass = path.join(buildSrcFolder, "server", "Server.ts");
|
|
50
|
+
const serverScript = path.join(serverFolder, "serverEntry.js");
|
|
51
|
+
|
|
52
|
+
// Splice file names into _worker.ts
|
|
53
|
+
workerSource = workerSource
|
|
54
|
+
.replace("%SERVER_CLASS%", serverClass)
|
|
55
|
+
.replace("%SERVER_SCRIPT%", serverScript)
|
|
56
|
+
.replace("%HTML_TEMPLATE%", template);
|
|
57
|
+
|
|
58
|
+
workerFile = path.join(tempFolder, "_worker.ts");
|
|
59
|
+
await fs.mkdir(tempFolder);
|
|
60
|
+
await fs.writeFile(workerFile, workerSource);
|
|
61
|
+
|
|
62
|
+
await build(
|
|
63
|
+
defineConfig({
|
|
64
|
+
build: {
|
|
65
|
+
outDir: cloudflareFolder,
|
|
66
|
+
rollupOptions: {
|
|
67
|
+
input: [workerFile],
|
|
68
|
+
output: {
|
|
69
|
+
entryFileNames: `[name].js`,
|
|
70
|
+
chunkFileNames: `[name].js`,
|
|
71
|
+
assetFileNames: `[name].[ext]`,
|
|
72
|
+
},
|
|
73
|
+
preserveEntrySignatures: "strict",
|
|
74
|
+
},
|
|
75
|
+
// HACK: We don't need to minify as the worker isn't downloaded?
|
|
76
|
+
// And it makes debugging easier
|
|
77
|
+
minify: false,
|
|
78
|
+
// HACK: Disable preload so that the _worker.js file doesn't end
|
|
79
|
+
// up with document and window references that are not available
|
|
80
|
+
modulePreload: false,
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Copy the client assets into the Cloudflare folder
|
|
86
|
+
await fs.mkdir(path.join(cloudflareFolder, "assets"));
|
|
87
|
+
for (let file of await fs.readdir(path.join(clientFolder, "assets"))) {
|
|
88
|
+
await fs.copyFile(
|
|
89
|
+
path.join(clientFolder, "assets", file),
|
|
90
|
+
path.join(cloudflareFolder, "assets", file),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Build a wrangler.toml file for running with `wrangler dev`
|
|
95
|
+
const wranglerConfig = `
|
|
96
|
+
name = "torpor-miniflare"
|
|
97
|
+
main = "./cloudflare/_worker.js"
|
|
98
|
+
compatibility_date = "2025-01-01"
|
|
99
|
+
|
|
100
|
+
[assets]
|
|
101
|
+
directory = "./cloudflare"
|
|
102
|
+
binding = "ASSETS"
|
|
103
|
+
`;
|
|
104
|
+
const wranglerFile = path.join(distFolder, "wrangler.toml");
|
|
105
|
+
await fs.writeFile(wranglerFile, wranglerConfig);
|
|
106
|
+
|
|
107
|
+
await fs.rm(tempFolder, { recursive: true });
|
|
108
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default function prepareTemplate(template: string, clientScript: string) {
|
|
2
|
+
let contentStart = regexIndexOf(template, /\<div\s+id=("app"|'app'|app)\s+/);
|
|
3
|
+
contentStart = template.indexOf(">", contentStart) + 1;
|
|
4
|
+
let contentEnd = template.indexOf("</div>", contentStart);
|
|
5
|
+
if (contentStart === -1 || contentEnd === -1) {
|
|
6
|
+
throw new Error(`Couldn't find <div id="app"></div>`);
|
|
7
|
+
}
|
|
8
|
+
return (
|
|
9
|
+
template.substring(0, contentStart) +
|
|
10
|
+
"%COMPONENT_HTML%" +
|
|
11
|
+
template.substring(contentEnd) +
|
|
12
|
+
`<script type="module" src="${clientScript}"></script>`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// From https://stackoverflow.com/a/274094
|
|
17
|
+
function regexIndexOf(string: string, regex: RegExp, position?: number) {
|
|
18
|
+
var indexOf = string.substring(position || 0).search(regex);
|
|
19
|
+
return indexOf >= 0 ? indexOf + (position || 0) : indexOf;
|
|
20
|
+
}
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
package/vitest.config.ts
ADDED