reroute-js 0.0.6 → 0.0.8
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/_/basic/package.json +1 -1
- package/_/blog/package.json +1 -1
- package/cli/bin.d.ts +1 -1
- package/cli/bin.d.ts.map +1 -1
- package/cli/bin.js +161 -350
- package/cli/bin.js.map +5 -18
- package/cli/index.d.ts +2 -0
- package/cli/index.d.ts.map +1 -0
- package/cli/index.js +147 -0
- package/cli/index.js.map +10 -0
- package/cli/src/{index.d.ts → cli.d.ts} +1 -1
- package/cli/src/cli.d.ts.map +1 -0
- package/cli/src/commands/gen.d.ts.map +1 -1
- package/cli/src/libs/index.d.ts +2 -0
- package/cli/src/libs/index.d.ts.map +1 -0
- package/cli/src/libs/tailwind.d.ts +31 -4
- package/cli/src/libs/tailwind.d.ts.map +1 -1
- package/core/index.js +189 -168
- package/core/index.js.map +6 -6
- package/core/src/bundler/transpile.d.ts.map +1 -1
- package/core/src/index.d.ts +0 -1
- package/core/src/index.d.ts.map +1 -1
- package/elysia/index.js +230 -398
- package/elysia/index.js.map +7 -8
- package/elysia/src/plugin.d.ts.map +1 -1
- package/package.json +5 -1
- package/cli/src/index.d.ts.map +0 -1
- package/core/src/tailwind.d.ts +0 -15
- package/core/src/tailwind.d.ts.map +0 -1
package/elysia/index.js
CHANGED
|
@@ -31,11 +31,11 @@ var exports_path = {};
|
|
|
31
31
|
__export(exports_path, {
|
|
32
32
|
stripStart: () => stripStart,
|
|
33
33
|
stripEnd: () => stripEnd,
|
|
34
|
-
join: () =>
|
|
34
|
+
join: () => join2,
|
|
35
35
|
extname: () => extname,
|
|
36
36
|
basename: () => basename
|
|
37
37
|
});
|
|
38
|
-
function
|
|
38
|
+
function join2(...parts) {
|
|
39
39
|
return parts.join("/").replace(/\/+/g, "/");
|
|
40
40
|
}
|
|
41
41
|
function extname(p) {
|
|
@@ -53,9 +53,6 @@ function stripEnd(p, ch) {
|
|
|
53
53
|
return p.endsWith(ch) ? p.slice(0, -ch.length) : p;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// packages/elysia/src/plugin.ts
|
|
57
|
-
import { watch as fsWatch } from "node:fs";
|
|
58
|
-
|
|
59
56
|
// packages/core/src/bundler/hash.ts
|
|
60
57
|
async function generateContentHash(content) {
|
|
61
58
|
const data = new TextEncoder().encode(content);
|
|
@@ -65,7 +62,100 @@ async function generateContentHash(content) {
|
|
|
65
62
|
hex += b.toString(16).padStart(2, "0");
|
|
66
63
|
return hex.slice(0, 8);
|
|
67
64
|
}
|
|
65
|
+
// packages/cli/src/libs/tailwind.ts
|
|
66
|
+
import { spawn } from "node:child_process";
|
|
67
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
68
|
+
import { join } from "node:path";
|
|
69
|
+
function isTailwindAvailable(cwd) {
|
|
70
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
71
|
+
if (!existsSync(packageJsonPath)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
76
|
+
const deps = {
|
|
77
|
+
...packageJson.dependencies,
|
|
78
|
+
...packageJson.devDependencies
|
|
79
|
+
};
|
|
80
|
+
return "@tailwindcss/cli" in deps;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getTailwindPaths(cwd) {
|
|
86
|
+
const input = join(cwd, "src/client/theme.css");
|
|
87
|
+
const output = join(cwd, ".reroute/theme.css");
|
|
88
|
+
return { input, output };
|
|
89
|
+
}
|
|
90
|
+
function hasTailwindInput(cwd) {
|
|
91
|
+
const { input } = getTailwindPaths(cwd);
|
|
92
|
+
if (!existsSync(input)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const content = readFileSync(input, "utf-8");
|
|
97
|
+
return content.includes('@import "tailwindcss"');
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function resolveTailwindBin(cwd) {
|
|
103
|
+
const pathsToTry = [
|
|
104
|
+
join(cwd, "node_modules/.bin/tailwindcss"),
|
|
105
|
+
join(cwd, "../node_modules/.bin/tailwindcss"),
|
|
106
|
+
join(cwd, "../../node_modules/.bin/tailwindcss"),
|
|
107
|
+
join(cwd, "../../../node_modules/.bin/tailwindcss")
|
|
108
|
+
];
|
|
109
|
+
for (const binPath of pathsToTry) {
|
|
110
|
+
if (existsSync(binPath)) {
|
|
111
|
+
return binPath;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return join(cwd, "node_modules/.bin/tailwindcss");
|
|
115
|
+
}
|
|
116
|
+
async function buildTailwind(cwd) {
|
|
117
|
+
const { input, output } = getTailwindPaths(cwd);
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const tailwindBin = resolveTailwindBin(cwd);
|
|
120
|
+
const args = ["-i", input, "-o", output];
|
|
121
|
+
console.log(`[reroute/tailwind] build start: -i ${input} -o ${output}`);
|
|
122
|
+
const tailwind = spawn(tailwindBin, args, {
|
|
123
|
+
cwd,
|
|
124
|
+
stdio: "pipe"
|
|
125
|
+
});
|
|
126
|
+
tailwind.stdout?.on("data", (data) => {
|
|
127
|
+
const line = data.toString().trim();
|
|
128
|
+
if (line) {
|
|
129
|
+
console.log(`[reroute/tailwind] ${line}`);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
tailwind.stderr?.on("data", (data) => {
|
|
133
|
+
const line = data.toString().trim();
|
|
134
|
+
if (line) {
|
|
135
|
+
console.log(`[reroute/tailwind] ${line}`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
tailwind.on("close", (code) => {
|
|
139
|
+
if (code === 0) {
|
|
140
|
+
console.log("[reroute/tailwind] build done");
|
|
141
|
+
resolve();
|
|
142
|
+
} else {
|
|
143
|
+
reject(new Error(`Tailwind CSS build failed with code ${code}`));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
tailwind.on("error", (error) => {
|
|
147
|
+
reject(error);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
68
151
|
// packages/core/src/bundler/transpile.ts
|
|
152
|
+
function isWatchMode() {
|
|
153
|
+
try {
|
|
154
|
+
return Array.isArray(process.execArgv) && process.execArgv.includes("--watch") || Array.isArray(process.argv) && process.argv.includes("--watch");
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
69
159
|
async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
70
160
|
const src = await Bun.file(filePath).text();
|
|
71
161
|
const cacheKey = `${filePath}-${await generateContentHash(src)}`;
|
|
@@ -73,6 +163,16 @@ async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
|
73
163
|
return bundleCache.get(cacheKey);
|
|
74
164
|
}
|
|
75
165
|
console.log(`[reroute] Building ${originalPath}${options.minify ? " (minified)" : ""}...`);
|
|
166
|
+
try {
|
|
167
|
+
if (isWatchMode()) {
|
|
168
|
+
const cwd = typeof process !== "undefined" && process.cwd ? process.cwd() : "";
|
|
169
|
+
if (cwd && isTailwindAvailable(cwd) && hasTailwindInput(cwd)) {
|
|
170
|
+
await buildTailwind(cwd);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {
|
|
174
|
+
console.warn("[reroute/tailwind] rebuild failed:", e);
|
|
175
|
+
}
|
|
76
176
|
async function doBuild(sm) {
|
|
77
177
|
return await Bun.build({
|
|
78
178
|
entrypoints: [filePath],
|
|
@@ -82,7 +182,7 @@ async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
|
82
182
|
splitting: false,
|
|
83
183
|
sourcemap: sm,
|
|
84
184
|
define: {
|
|
85
|
-
"process.env.
|
|
185
|
+
"process.env.BUN_ENV": options.minify ? '"production"' : '"development"'
|
|
86
186
|
}
|
|
87
187
|
});
|
|
88
188
|
}
|
|
@@ -124,10 +224,10 @@ async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
|
124
224
|
async function getBundleUrlsFor(modules, clientDir, prefix, options, bundleCache) {
|
|
125
225
|
const mods = Array.isArray(modules) ? modules : [modules];
|
|
126
226
|
const urls = [];
|
|
127
|
-
const { join:
|
|
227
|
+
const { join: join3 } = await Promise.resolve().then(() => exports_path);
|
|
128
228
|
for (const mod of mods) {
|
|
129
229
|
const rel = mod.replace(/^\.\//, "");
|
|
130
|
-
const fullPath =
|
|
230
|
+
const fullPath = join3(clientDir, rel);
|
|
131
231
|
try {
|
|
132
232
|
const bundleInfo = await transpileFile(fullPath, rel, options, bundleCache);
|
|
133
233
|
const base = basename(rel, extname(rel));
|
|
@@ -139,171 +239,16 @@ async function getBundleUrlsFor(modules, clientDir, prefix, options, bundleCache
|
|
|
139
239
|
}
|
|
140
240
|
return urls;
|
|
141
241
|
}
|
|
142
|
-
// packages/core/src/content/builder.ts
|
|
143
|
-
import { mkdir, readdir } from "node:fs/promises";
|
|
144
|
-
import { pathToFileURL } from "node:url";
|
|
145
|
-
async function sha8(text) {
|
|
146
|
-
const data = new TextEncoder().encode(text);
|
|
147
|
-
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
148
|
-
let hex = "";
|
|
149
|
-
for (const b of new Uint8Array(buf))
|
|
150
|
-
hex += b.toString(16).padStart(2, "0");
|
|
151
|
-
return hex.slice(0, 8);
|
|
152
|
-
}
|
|
153
|
-
async function buildChunkFor(absSrc, cwd, collection, name) {
|
|
154
|
-
try {
|
|
155
|
-
const result = await Bun.build({
|
|
156
|
-
entrypoints: [absSrc],
|
|
157
|
-
target: "browser",
|
|
158
|
-
format: "esm",
|
|
159
|
-
splitting: false,
|
|
160
|
-
sourcemap: "none",
|
|
161
|
-
minify: false,
|
|
162
|
-
define: { "process.env.NODE_ENV": '"production"' }
|
|
163
|
-
});
|
|
164
|
-
if (!result.success)
|
|
165
|
-
return null;
|
|
166
|
-
const code = await result.outputs[0]?.text();
|
|
167
|
-
const hash2 = await sha8(code);
|
|
168
|
-
const relDir = join(".reroute", "chunks", collection);
|
|
169
|
-
const absDir = join(cwd, relDir);
|
|
170
|
-
const outFile = `${name}.${hash2}.js`;
|
|
171
|
-
const outPath = join(absDir, outFile);
|
|
172
|
-
await mkdir(absDir, { recursive: true });
|
|
173
|
-
const exists = await Bun.file(outPath).exists();
|
|
174
|
-
if (!exists)
|
|
175
|
-
await Bun.write(outPath, code);
|
|
176
|
-
const url = `/${join(relDir, outFile).replace(/\\+/g, "/")}`;
|
|
177
|
-
return { outPath, url, code };
|
|
178
|
-
} catch (e) {
|
|
179
|
-
console.error("[reroute] Failed to build content chunk:", absSrc, e);
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async function getMetaFor(absSrc, isWatchMode) {
|
|
184
|
-
try {
|
|
185
|
-
const url = pathToFileURL(absSrc).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
186
|
-
const m = await import(url);
|
|
187
|
-
return m.meta || m.frontmatter || {};
|
|
188
|
-
} catch {
|
|
189
|
-
return {};
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
async function rebuildContentArtifacts(cwd, clientDir, isWatchMode = true) {
|
|
193
|
-
const routesRoot = join(clientDir, "routes");
|
|
194
|
-
const items = [];
|
|
195
|
-
try {
|
|
196
|
-
const entries = await readdir(routesRoot, { withFileTypes: true });
|
|
197
|
-
for (const dir of entries) {
|
|
198
|
-
if (!dir.isDirectory())
|
|
199
|
-
continue;
|
|
200
|
-
const collection = dir.name;
|
|
201
|
-
const contentDir = join(routesRoot, collection, "content");
|
|
202
|
-
let contentFiles = [];
|
|
203
|
-
try {
|
|
204
|
-
const list = await readdir(contentDir, { withFileTypes: true });
|
|
205
|
-
contentFiles = list.filter((e) => e.isFile() && /\.(tsx|ts)$/.test(e.name) && !e.name.startsWith("_")).map((e) => e.name);
|
|
206
|
-
} catch {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
for (const file of contentFiles) {
|
|
210
|
-
const name = file.replace(/\.(tsx|ts)$/, "");
|
|
211
|
-
const absSrc = join(contentDir, file);
|
|
212
|
-
const chunk = await buildChunkFor(absSrc, cwd, collection, name);
|
|
213
|
-
if (!chunk)
|
|
214
|
-
continue;
|
|
215
|
-
const meta = await getMetaFor(absSrc, isWatchMode);
|
|
216
|
-
items.push({
|
|
217
|
-
collection,
|
|
218
|
-
name,
|
|
219
|
-
slug: name,
|
|
220
|
-
href: `/${collection}/${name}`,
|
|
221
|
-
moduleUrl: chunk.url,
|
|
222
|
-
meta
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
} catch (e) {
|
|
227
|
-
console.error("[reroute] Failed to scan content routes:", e);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
try {
|
|
231
|
-
const lines = [];
|
|
232
|
-
lines.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
|
|
233
|
-
lines.push("/* eslint-disable */");
|
|
234
|
-
lines.push("// @ts-nocheck");
|
|
235
|
-
lines.push("");
|
|
236
|
-
lines.push("export const contents = [");
|
|
237
|
-
for (const e of items) {
|
|
238
|
-
lines.push(" { collection: '" + e.collection + "', slug: '" + e.slug + "', name: '" + e.name + "', href: '" + e.href + "', module: '" + e.moduleUrl + "', meta: " + JSON.stringify(e.meta || {}) + " },");
|
|
239
|
-
}
|
|
240
|
-
lines.push("] as const;");
|
|
241
|
-
lines.push("");
|
|
242
|
-
lines.push("export const byCollection: Record<string, any[]> = {};");
|
|
243
|
-
lines.push("for (const c of contents) { (byCollection[c.collection] ||= []).push(c as any); }");
|
|
244
|
-
lines.push("");
|
|
245
|
-
lines.push("export const byCollectionAndName: Record<string, Record<string, any>> = {};");
|
|
246
|
-
lines.push("for (const c of contents) { ((byCollectionAndName[c.collection] ||= {})[c.name] = c as any); }");
|
|
247
|
-
lines.push("");
|
|
248
|
-
lines.push("export type Collections = keyof typeof byCollection;");
|
|
249
|
-
lines.push("export type Names<C extends Collections> = keyof (typeof byCollectionAndName)[C];");
|
|
250
|
-
lines.push("export type ContentEntry = (typeof contents)[number];");
|
|
251
|
-
lines.push("");
|
|
252
|
-
lines.push("export function getContentEntry<C extends string, N extends string>(collection: C, name: N) {");
|
|
253
|
-
lines.push(" const m = (byCollectionAndName as any)[collection];");
|
|
254
|
-
lines.push(" return m ? (m as Record<string, any>)[name] : undefined;");
|
|
255
|
-
lines.push("}");
|
|
256
|
-
lines.push("");
|
|
257
|
-
await mkdir(join(cwd, ".reroute"), { recursive: true });
|
|
258
|
-
await Bun.write(join(cwd, ".reroute", "content.ts"), lines.join(`
|
|
259
|
-
`));
|
|
260
|
-
} catch (e) {
|
|
261
|
-
console.error("[reroute] Failed to write .reroute/content.ts:", e);
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
const byCol = new Map;
|
|
265
|
-
for (const it of items) {
|
|
266
|
-
const arr = byCol.get(it.collection) || [];
|
|
267
|
-
arr.push(it);
|
|
268
|
-
byCol.set(it.collection, arr);
|
|
269
|
-
}
|
|
270
|
-
await mkdir(join(cwd, ".reroute", "collections"), { recursive: true });
|
|
271
|
-
for (const [collection, list] of byCol) {
|
|
272
|
-
const js = [];
|
|
273
|
-
js.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
|
|
274
|
-
js.push("");
|
|
275
|
-
js.push("export const items = [");
|
|
276
|
-
for (const e of list) {
|
|
277
|
-
js.push(` { collection: '${e.collection}', slug: '${e.slug}', name: '${e.name}', href: '${e.href}', module: '${e.moduleUrl}', meta: ${JSON.stringify(e.meta || {})} },`);
|
|
278
|
-
}
|
|
279
|
-
js.push("];");
|
|
280
|
-
js.push("");
|
|
281
|
-
js.push("export const byName = {};");
|
|
282
|
-
js.push("for (const it of items) { byName[it.name] = it; }");
|
|
283
|
-
js.push("");
|
|
284
|
-
js.push("export function get(collectionName, name) {");
|
|
285
|
-
js.push(` if (collectionName !== '${collection}') return undefined;`);
|
|
286
|
-
js.push(" return byName[name];");
|
|
287
|
-
js.push("}");
|
|
288
|
-
js.push("");
|
|
289
|
-
await Bun.write(join(cwd, ".reroute", "collections", `${collection}.js`), js.join(`
|
|
290
|
-
`));
|
|
291
|
-
}
|
|
292
|
-
} catch (e) {
|
|
293
|
-
console.error("[reroute] Failed to write .reroute/collections/*:", e);
|
|
294
|
-
}
|
|
295
|
-
console.log("[reroute] Content artifacts up-to-date");
|
|
296
|
-
}
|
|
297
242
|
// packages/core/src/content/discovery.ts
|
|
298
|
-
import { readdir
|
|
243
|
+
import { readdir, stat as stat2 } from "node:fs/promises";
|
|
299
244
|
|
|
300
245
|
// packages/core/src/content/metadata.ts
|
|
301
246
|
import { stat } from "node:fs/promises";
|
|
302
|
-
import { pathToFileURL
|
|
303
|
-
async function getContentMeta(absPath,
|
|
247
|
+
import { pathToFileURL } from "node:url";
|
|
248
|
+
async function getContentMeta(absPath, isWatchMode2) {
|
|
304
249
|
try {
|
|
305
|
-
const url =
|
|
306
|
-
const mod = await import(`${url}${
|
|
250
|
+
const url = pathToFileURL(absPath).href;
|
|
251
|
+
const mod = await import(`${url}${isWatchMode2 ? `?t=${Date.now()}` : ""}`);
|
|
307
252
|
const meta = mod.meta || mod.frontmatter || {};
|
|
308
253
|
if (meta && typeof meta === "object")
|
|
309
254
|
return meta;
|
|
@@ -339,10 +284,10 @@ function escapeHtml(input) {
|
|
|
339
284
|
async function listContentFiles(dir, baseRel) {
|
|
340
285
|
const out = [];
|
|
341
286
|
try {
|
|
342
|
-
const entries = await
|
|
287
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
343
288
|
for (const entry of entries) {
|
|
344
|
-
const full =
|
|
345
|
-
const rel =
|
|
289
|
+
const full = join2(dir, entry.name);
|
|
290
|
+
const rel = join2(baseRel, entry.name);
|
|
346
291
|
if (entry.isDirectory()) {
|
|
347
292
|
const nested = await listContentFiles(full, rel);
|
|
348
293
|
out.push(...nested);
|
|
@@ -355,19 +300,19 @@ async function listContentFiles(dir, baseRel) {
|
|
|
355
300
|
} catch {}
|
|
356
301
|
return out;
|
|
357
302
|
}
|
|
358
|
-
async function buildContentDTOs(collectionPath, clientDir, prefix,
|
|
303
|
+
async function buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode2) {
|
|
359
304
|
const normalized = stripStart(stripEnd(collectionPath, "/"), "/");
|
|
360
|
-
const contentRelDir =
|
|
361
|
-
const contentAbsDir =
|
|
305
|
+
const contentRelDir = join2("routes", normalized, "content");
|
|
306
|
+
const contentAbsDir = join2(clientDir, contentRelDir);
|
|
362
307
|
const files = await listContentFiles(contentAbsDir, "");
|
|
363
308
|
const items = [];
|
|
364
309
|
for (const rel of files) {
|
|
365
|
-
const fullRelPath =
|
|
366
|
-
const absPath =
|
|
310
|
+
const fullRelPath = join2(contentRelDir, rel);
|
|
311
|
+
const absPath = join2(clientDir, fullRelPath);
|
|
367
312
|
const noExt = rel.replace(/\.(tsx|ts)$/, "");
|
|
368
313
|
const name = basename(noExt);
|
|
369
314
|
const moduleUrl = `${prefix}/${fullRelPath}`.replace(/\/+/g, "/");
|
|
370
|
-
const meta = await getContentMeta(absPath,
|
|
315
|
+
const meta = await getContentMeta(absPath, isWatchMode2);
|
|
371
316
|
const href = `/${normalized}/${name}`.replace(/\\+/g, "/");
|
|
372
317
|
items.push({
|
|
373
318
|
slug: noExt,
|
|
@@ -381,13 +326,13 @@ async function buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode)
|
|
|
381
326
|
return items;
|
|
382
327
|
}
|
|
383
328
|
async function discoverCollections(clientDir) {
|
|
384
|
-
const root =
|
|
329
|
+
const root = join2(clientDir, "routes");
|
|
385
330
|
const collections = new Set;
|
|
386
331
|
try {
|
|
387
|
-
const entries = await
|
|
332
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
388
333
|
for (const entry of entries) {
|
|
389
334
|
if (entry.isDirectory()) {
|
|
390
|
-
const contentDir =
|
|
335
|
+
const contentDir = join2(root, entry.name, "content");
|
|
391
336
|
try {
|
|
392
337
|
await stat2(contentDir);
|
|
393
338
|
collections.add(entry.name);
|
|
@@ -400,18 +345,18 @@ async function discoverCollections(clientDir) {
|
|
|
400
345
|
// packages/core/src/content/registry.ts
|
|
401
346
|
async function generateContentRegistry(cwd) {
|
|
402
347
|
try {
|
|
403
|
-
const p =
|
|
348
|
+
const p = join2(cwd, ".reroute", "content.ts");
|
|
404
349
|
const exists = await Bun.file(p).exists();
|
|
405
350
|
console.log(exists ? "[reroute] Content registry up-to-date" : "[reroute] Warning: .reroute/content.ts not found");
|
|
406
351
|
} catch {}
|
|
407
352
|
}
|
|
408
353
|
// packages/core/src/ssr/data.ts
|
|
409
|
-
import { pathToFileURL as
|
|
354
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
410
355
|
|
|
411
356
|
// packages/core/src/ssr/modules.ts
|
|
412
|
-
import { readdir as
|
|
413
|
-
import { pathToFileURL as
|
|
414
|
-
async function importContentModuleForPath(pathname, clientDir, cwd,
|
|
357
|
+
import { readdir as readdir2, stat as stat3 } from "node:fs/promises";
|
|
358
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
359
|
+
async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode2) {
|
|
415
360
|
try {
|
|
416
361
|
const parts = pathname.split("/").filter(Boolean);
|
|
417
362
|
if (parts.length < 2)
|
|
@@ -419,51 +364,51 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
419
364
|
const collection = parts[0];
|
|
420
365
|
const name = parts[1];
|
|
421
366
|
try {
|
|
422
|
-
const registryPath =
|
|
423
|
-
const reg = await import(
|
|
367
|
+
const registryPath = join2(cwd, ".reroute", "content.ts");
|
|
368
|
+
const reg = await import(pathToFileURL2(registryPath).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
424
369
|
const get = reg?.getContentEntry;
|
|
425
370
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
426
371
|
const moduleUrl = entry?.module;
|
|
427
372
|
if (moduleUrl?.endsWith(".js")) {
|
|
428
|
-
const abs =
|
|
429
|
-
const href =
|
|
373
|
+
const abs = join2(cwd, moduleUrl.replace(/^\//, ""));
|
|
374
|
+
const href = pathToFileURL2(abs).href + (isWatchMode2 ? `?t=${Date.now()}` : "");
|
|
430
375
|
const mod = await import(href);
|
|
431
376
|
return mod;
|
|
432
377
|
}
|
|
433
378
|
} catch {}
|
|
434
379
|
try {
|
|
435
|
-
const chunkDir =
|
|
436
|
-
const files = await
|
|
380
|
+
const chunkDir = join2(cwd, ".reroute", "chunks", collection);
|
|
381
|
+
const files = await readdir2(chunkDir);
|
|
437
382
|
const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
438
383
|
if (matches.length) {
|
|
439
384
|
let latest = matches[0];
|
|
440
385
|
let latestM = 0;
|
|
441
386
|
for (const f of matches) {
|
|
442
387
|
try {
|
|
443
|
-
const s = await stat3(
|
|
388
|
+
const s = await stat3(join2(chunkDir, f));
|
|
444
389
|
if (s.mtimeMs >= latestM) {
|
|
445
390
|
latestM = s.mtimeMs;
|
|
446
391
|
latest = f;
|
|
447
392
|
}
|
|
448
393
|
} catch {}
|
|
449
394
|
}
|
|
450
|
-
const absChunk =
|
|
451
|
-
const href =
|
|
395
|
+
const absChunk = join2(chunkDir, latest);
|
|
396
|
+
const href = pathToFileURL2(absChunk).href + (isWatchMode2 ? `?t=${Date.now()}` : "");
|
|
452
397
|
const mod = await import(href);
|
|
453
398
|
return mod;
|
|
454
399
|
}
|
|
455
400
|
} catch {}
|
|
456
401
|
try {
|
|
457
|
-
const srcTsx =
|
|
458
|
-
const srcTs =
|
|
402
|
+
const srcTsx = join2(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
403
|
+
const srcTs = join2(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
459
404
|
let absSrc = null;
|
|
460
405
|
if (await Bun.file(srcTsx).exists())
|
|
461
406
|
absSrc = srcTsx;
|
|
462
407
|
else if (await Bun.file(srcTs).exists())
|
|
463
408
|
absSrc = srcTs;
|
|
464
409
|
if (absSrc) {
|
|
465
|
-
const href =
|
|
466
|
-
const mod = await (
|
|
410
|
+
const href = pathToFileURL2(absSrc).href;
|
|
411
|
+
const mod = await (isWatchMode2 ? import(`${href}?t=${Date.now()}`) : import(href));
|
|
467
412
|
return mod;
|
|
468
413
|
}
|
|
469
414
|
} catch {}
|
|
@@ -472,7 +417,7 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
472
417
|
}
|
|
473
418
|
|
|
474
419
|
// packages/core/src/ssr/seed.ts
|
|
475
|
-
async function seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
420
|
+
async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode2) {
|
|
476
421
|
try {
|
|
477
422
|
const parts = pathname.split("/").filter(Boolean);
|
|
478
423
|
if (parts.length < 2)
|
|
@@ -480,7 +425,7 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
|
480
425
|
const collection = parts[0];
|
|
481
426
|
const name = parts[1];
|
|
482
427
|
const key = `${collection}:${name}`;
|
|
483
|
-
const mod = await importContentModuleForPath(pathname, clientDir, cwd,
|
|
428
|
+
const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode2);
|
|
484
429
|
if (!mod)
|
|
485
430
|
return;
|
|
486
431
|
const C = mod.default || mod;
|
|
@@ -500,9 +445,9 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
|
500
445
|
|
|
501
446
|
// packages/core/src/ssr/data.ts
|
|
502
447
|
async function computeSSRDataForPath(params) {
|
|
503
|
-
const { pathname, clientDir, cwd, isWatchMode } = params;
|
|
448
|
+
const { pathname, clientDir, cwd, isWatchMode: isWatchMode2 } = params;
|
|
504
449
|
try {
|
|
505
|
-
await seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
450
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode2);
|
|
506
451
|
} catch {}
|
|
507
452
|
try {
|
|
508
453
|
const parts = pathname.split("/").filter(Boolean);
|
|
@@ -517,15 +462,15 @@ async function computeSSRDataForPath(params) {
|
|
|
517
462
|
}
|
|
518
463
|
} catch {}
|
|
519
464
|
try {
|
|
520
|
-
const routesPath =
|
|
521
|
-
const m = await import(
|
|
465
|
+
const routesPath = join2(cwd, ".reroute", "routes.ts");
|
|
466
|
+
const m = await import(pathToFileURL3(routesPath).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
522
467
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
523
468
|
const r = match?.route;
|
|
524
469
|
const paramsValue = match?.params || {};
|
|
525
470
|
if (r && typeof r.path === "string") {
|
|
526
471
|
try {
|
|
527
|
-
const abs =
|
|
528
|
-
const mod = await import(
|
|
472
|
+
const abs = join2(clientDir, "routes", String(r.path));
|
|
473
|
+
const mod = await import(pathToFileURL3(abs).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
529
474
|
const ssr = mod?.ssr;
|
|
530
475
|
const dataFn = ssr?.data;
|
|
531
476
|
if (typeof dataFn === "function") {
|
|
@@ -537,8 +482,8 @@ async function computeSSRDataForPath(params) {
|
|
|
537
482
|
return null;
|
|
538
483
|
}
|
|
539
484
|
// packages/core/src/ssr/render.ts
|
|
540
|
-
import { readdir as
|
|
541
|
-
import { pathToFileURL as
|
|
485
|
+
import { readdir as readdir3, stat as stat4 } from "node:fs/promises";
|
|
486
|
+
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
542
487
|
|
|
543
488
|
// node_modules/dedent/dist/dedent.mjs
|
|
544
489
|
function ownKeys(object, enumerableOnly) {
|
|
@@ -662,7 +607,7 @@ import { renderToString } from "react-dom/server";
|
|
|
662
607
|
|
|
663
608
|
// packages/core/src/template/html.ts
|
|
664
609
|
async function loadIndexHtml(clientDir) {
|
|
665
|
-
const templatePath =
|
|
610
|
+
const templatePath = join2(clientDir, "index.html");
|
|
666
611
|
try {
|
|
667
612
|
const content = await Bun.file(templatePath).text();
|
|
668
613
|
return content;
|
|
@@ -739,7 +684,7 @@ async function renderSSRDocument(options) {
|
|
|
739
684
|
rootComponent,
|
|
740
685
|
clientDir,
|
|
741
686
|
cwd,
|
|
742
|
-
isWatchMode,
|
|
687
|
+
isWatchMode: isWatchMode2,
|
|
743
688
|
bundleUrl,
|
|
744
689
|
head = "",
|
|
745
690
|
lang = "en",
|
|
@@ -761,7 +706,7 @@ async function renderSSRDocument(options) {
|
|
|
761
706
|
let modulePath = "";
|
|
762
707
|
let isContentCollection = false;
|
|
763
708
|
try {
|
|
764
|
-
const maybeDir =
|
|
709
|
+
const maybeDir = join2(clientDir, "routes", collection, "content");
|
|
765
710
|
const s = await stat4(maybeDir);
|
|
766
711
|
isContentCollection = typeof s?.isDirectory === "function" ? s.isDirectory() : true;
|
|
767
712
|
} catch {
|
|
@@ -771,8 +716,8 @@ async function renderSSRDocument(options) {
|
|
|
771
716
|
throw new Error("skip-content-preload");
|
|
772
717
|
}
|
|
773
718
|
try {
|
|
774
|
-
const registryPath =
|
|
775
|
-
const reg = await import(
|
|
719
|
+
const registryPath = join2(cwd, ".reroute", "content.ts");
|
|
720
|
+
const reg = await import(pathToFileURL4(registryPath).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
776
721
|
const get = reg?.getContentEntry;
|
|
777
722
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
778
723
|
const moduleUrl = entry?.module;
|
|
@@ -782,33 +727,33 @@ async function renderSSRDocument(options) {
|
|
|
782
727
|
} catch {}
|
|
783
728
|
if (!modulePath) {
|
|
784
729
|
try {
|
|
785
|
-
const chunkDir =
|
|
786
|
-
const files = await
|
|
730
|
+
const chunkDir = join2(cwd, ".reroute", "chunks", collection);
|
|
731
|
+
const files = await readdir3(chunkDir);
|
|
787
732
|
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
788
733
|
if (candidates.length) {
|
|
789
734
|
let latest = candidates[0];
|
|
790
735
|
let latestM = 0;
|
|
791
736
|
for (const candidateName of candidates) {
|
|
792
737
|
try {
|
|
793
|
-
const s = await stat4(
|
|
738
|
+
const s = await stat4(join2(chunkDir, candidateName));
|
|
794
739
|
if (s.mtimeMs >= latestM) {
|
|
795
740
|
latestM = s.mtimeMs;
|
|
796
741
|
latest = candidateName;
|
|
797
742
|
}
|
|
798
743
|
} catch {}
|
|
799
744
|
}
|
|
800
|
-
modulePath = `/${
|
|
745
|
+
modulePath = `/${join2(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
|
|
801
746
|
}
|
|
802
747
|
} catch {}
|
|
803
748
|
}
|
|
804
749
|
if (!modulePath) {
|
|
805
|
-
const tsx =
|
|
806
|
-
const ts =
|
|
750
|
+
const tsx = join2(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
751
|
+
const ts = join2(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
807
752
|
let srcUrl = "";
|
|
808
753
|
if (await Bun.file(tsx).exists()) {
|
|
809
|
-
srcUrl = `/${
|
|
754
|
+
srcUrl = `/${join2("routes", collection, "content", `${name}.tsx`).replace(/\\+/g, "/")}`;
|
|
810
755
|
} else if (await Bun.file(ts).exists()) {
|
|
811
|
-
srcUrl = `/${
|
|
756
|
+
srcUrl = `/${join2("routes", collection, "content", `${name}.ts`).replace(/\\+/g, "/")}`;
|
|
812
757
|
}
|
|
813
758
|
if (srcUrl) {
|
|
814
759
|
modulePath = srcUrl;
|
|
@@ -826,7 +771,7 @@ async function renderSSRDocument(options) {
|
|
|
826
771
|
}
|
|
827
772
|
}
|
|
828
773
|
} catch {}
|
|
829
|
-
await seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
774
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode2);
|
|
830
775
|
const __SSR_DATA__ = {};
|
|
831
776
|
try {
|
|
832
777
|
try {
|
|
@@ -846,15 +791,15 @@ async function renderSSRDocument(options) {
|
|
|
846
791
|
}
|
|
847
792
|
} catch {}
|
|
848
793
|
try {
|
|
849
|
-
const routesPath =
|
|
850
|
-
const m = await import(
|
|
794
|
+
const routesPath = join2(cwd, ".reroute", "routes.ts");
|
|
795
|
+
const m = await import(pathToFileURL4(routesPath).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
851
796
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
852
797
|
const r = match?.route;
|
|
853
798
|
const params = match?.params || {};
|
|
854
799
|
if (r && typeof r.path === "string") {
|
|
855
800
|
try {
|
|
856
|
-
const abs =
|
|
857
|
-
const mod = await import(
|
|
801
|
+
const abs = join2(clientDir, "routes", String(r.path));
|
|
802
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
858
803
|
const ssr = mod?.ssr;
|
|
859
804
|
const dataFn = ssr?.data;
|
|
860
805
|
if (typeof dataFn === "function") {
|
|
@@ -870,8 +815,8 @@ async function renderSSRDocument(options) {
|
|
|
870
815
|
} catch {}
|
|
871
816
|
let __byCollectionForSSR = {};
|
|
872
817
|
try {
|
|
873
|
-
const p =
|
|
874
|
-
const mod = await import(
|
|
818
|
+
const p = join2(cwd, ".reroute", "content.ts");
|
|
819
|
+
const mod = await import(pathToFileURL4(p).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
875
820
|
__byCollectionForSSR = mod?.byCollection || {};
|
|
876
821
|
try {
|
|
877
822
|
globalThis.__REROUTE_COLLECTIONS__ = __byCollectionForSSR;
|
|
@@ -885,10 +830,10 @@ async function renderSSRDocument(options) {
|
|
|
885
830
|
let inlineStyleTag = "";
|
|
886
831
|
try {
|
|
887
832
|
const candidates = [
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
833
|
+
join2(clientDir, "..", ".reroute", "theme.css"),
|
|
834
|
+
join2(clientDir, "..", "..", ".reroute", "theme.css"),
|
|
835
|
+
join2(clientDir, "..", "..", "..", ".reroute", "theme.css"),
|
|
836
|
+
join2(clientDir, "..", "..", "..", "..", ".reroute", "theme.css")
|
|
892
837
|
];
|
|
893
838
|
let cssPath = "";
|
|
894
839
|
for (const p of candidates) {
|
|
@@ -971,7 +916,7 @@ async function renderSSRDocument(options) {
|
|
|
971
916
|
</script>`;
|
|
972
917
|
} catch {}
|
|
973
918
|
hydrationScript += scripts.map((src) => `<script type="module" src="${src}"></script>`).join("");
|
|
974
|
-
if (
|
|
919
|
+
if (isWatchMode2) {
|
|
975
920
|
hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
976
921
|
}
|
|
977
922
|
let perPageHead = "";
|
|
@@ -996,8 +941,8 @@ ${ssrHead}`;
|
|
|
996
941
|
}
|
|
997
942
|
} catch {}
|
|
998
943
|
try {
|
|
999
|
-
const routesPath =
|
|
1000
|
-
const m = await import(
|
|
944
|
+
const routesPath = join2(cwd, ".reroute", "routes.ts");
|
|
945
|
+
const m = await import(pathToFileURL4(routesPath).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
1001
946
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
1002
947
|
const r = match?.route;
|
|
1003
948
|
if (!r) {
|
|
@@ -1005,8 +950,8 @@ ${ssrHead}`;
|
|
|
1005
950
|
}
|
|
1006
951
|
if (r && typeof r.path === "string") {
|
|
1007
952
|
try {
|
|
1008
|
-
const abs =
|
|
1009
|
-
const mod = await import(
|
|
953
|
+
const abs = join2(clientDir, "routes", String(r.path));
|
|
954
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
1010
955
|
const meta = mod?.meta;
|
|
1011
956
|
const ssr = mod?.ssr;
|
|
1012
957
|
if (meta)
|
|
@@ -1040,8 +985,8 @@ ${ssrHead}`;
|
|
|
1040
985
|
}
|
|
1041
986
|
if (chosen && typeof chosen.path === "string") {
|
|
1042
987
|
try {
|
|
1043
|
-
const abs =
|
|
1044
|
-
const mod = await import(
|
|
988
|
+
const abs = join2(clientDir, "routes", String(chosen.path));
|
|
989
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode2 ? `?t=${Date.now()}` : ""));
|
|
1045
990
|
const meta = mod?.meta;
|
|
1046
991
|
const ssr = mod?.ssr;
|
|
1047
992
|
if (meta)
|
|
@@ -1074,82 +1019,6 @@ ${ssrHead}`;
|
|
|
1074
1019
|
status: statusOverride || 200
|
|
1075
1020
|
};
|
|
1076
1021
|
}
|
|
1077
|
-
// packages/core/src/tailwind.ts
|
|
1078
|
-
import { spawn } from "node:child_process";
|
|
1079
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1080
|
-
import { join as join2 } from "node:path";
|
|
1081
|
-
function isTailwindAvailable(cwd) {
|
|
1082
|
-
const packageJsonPath = join2(cwd, "package.json");
|
|
1083
|
-
if (!existsSync(packageJsonPath))
|
|
1084
|
-
return false;
|
|
1085
|
-
try {
|
|
1086
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
1087
|
-
const deps = {
|
|
1088
|
-
...packageJson.dependencies,
|
|
1089
|
-
...packageJson.devDependencies
|
|
1090
|
-
};
|
|
1091
|
-
return !!deps && "@tailwindcss/cli" in deps;
|
|
1092
|
-
} catch {
|
|
1093
|
-
return false;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
function getTailwindPaths(cwd) {
|
|
1097
|
-
const input = join2(cwd, "src/client/theme.css");
|
|
1098
|
-
const output = join2(cwd, ".reroute/theme.css");
|
|
1099
|
-
return { input, output };
|
|
1100
|
-
}
|
|
1101
|
-
function hasTailwindInput(cwd) {
|
|
1102
|
-
const { input } = getTailwindPaths(cwd);
|
|
1103
|
-
if (!existsSync(input))
|
|
1104
|
-
return false;
|
|
1105
|
-
try {
|
|
1106
|
-
const content = readFileSync(input, "utf-8");
|
|
1107
|
-
return content.includes('@import "tailwindcss"');
|
|
1108
|
-
} catch {
|
|
1109
|
-
return false;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
function resolveTailwindBin(cwd) {
|
|
1113
|
-
const pathsToTry = [
|
|
1114
|
-
join2(cwd, "node_modules/.bin/tailwindcss"),
|
|
1115
|
-
join2(cwd, "../node_modules/.bin/tailwindcss"),
|
|
1116
|
-
join2(cwd, "../../node_modules/.bin/tailwindcss"),
|
|
1117
|
-
join2(cwd, "../../../node_modules/.bin/tailwindcss")
|
|
1118
|
-
];
|
|
1119
|
-
for (const binPath of pathsToTry)
|
|
1120
|
-
if (existsSync(binPath))
|
|
1121
|
-
return binPath;
|
|
1122
|
-
return join2(cwd, "node_modules/.bin/tailwindcss");
|
|
1123
|
-
}
|
|
1124
|
-
async function buildTailwind(cwd) {
|
|
1125
|
-
const { input, output } = getTailwindPaths(cwd);
|
|
1126
|
-
if (!isTailwindAvailable(cwd) || !hasTailwindInput(cwd))
|
|
1127
|
-
return;
|
|
1128
|
-
await new Promise((resolve, reject) => {
|
|
1129
|
-
const tailwindBin = resolveTailwindBin(cwd);
|
|
1130
|
-
const args = ["-i", input, "-o", output];
|
|
1131
|
-
const tailwind = spawn(tailwindBin, args, { cwd, stdio: "inherit" });
|
|
1132
|
-
tailwind.on("close", (code) => code === 0 ? resolve() : reject(new Error(`Tailwind CSS build failed with code ${code}`)));
|
|
1133
|
-
tailwind.on("error", (error) => reject(error));
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
function watchTailwind(cwd) {
|
|
1137
|
-
if (!isTailwindAvailable(cwd) || !hasTailwindInput(cwd))
|
|
1138
|
-
return null;
|
|
1139
|
-
const { input, output } = getTailwindPaths(cwd);
|
|
1140
|
-
const tailwindBin = resolveTailwindBin(cwd);
|
|
1141
|
-
const args = ["-i", input, "-o", output, "--watch"];
|
|
1142
|
-
console.log("[reroute/tailwind] Starting Tailwind CSS v4 in watch mode...");
|
|
1143
|
-
const child = spawn(tailwindBin, args, { cwd, stdio: "inherit" });
|
|
1144
|
-
child.on("error", (e) => console.error("[reroute/tailwind] Error:", e));
|
|
1145
|
-
const stop = () => {
|
|
1146
|
-
try {
|
|
1147
|
-
console.log("[reroute/tailwind] Stopping Tailwind CSS...");
|
|
1148
|
-
child.kill();
|
|
1149
|
-
} catch {}
|
|
1150
|
-
};
|
|
1151
|
-
return { process: child, stop };
|
|
1152
|
-
}
|
|
1153
1022
|
// packages/core/src/utils/cache.ts
|
|
1154
1023
|
class LRUCache {
|
|
1155
1024
|
cache;
|
|
@@ -1241,7 +1110,7 @@ function registerArtifactsRoutes(app, cwd) {
|
|
|
1241
1110
|
const sub = String(params["*"] || "").replace(/^\/+/, "");
|
|
1242
1111
|
if (!sub)
|
|
1243
1112
|
throw new NotFoundError;
|
|
1244
|
-
const abs =
|
|
1113
|
+
const abs = join2(cwd, ".reroute", sub);
|
|
1245
1114
|
try {
|
|
1246
1115
|
const exists = await Bun.file(abs).exists();
|
|
1247
1116
|
if (!exists)
|
|
@@ -1269,12 +1138,12 @@ function registerArtifactsRoutes(app, cwd) {
|
|
|
1269
1138
|
|
|
1270
1139
|
// packages/elysia/src/routes/content.ts
|
|
1271
1140
|
import { NotFoundError as NotFoundError2 } from "elysia";
|
|
1272
|
-
function registerContentRoutes(app, clientDir, prefix,
|
|
1141
|
+
function registerContentRoutes(app, clientDir, prefix, isWatchMode2) {
|
|
1273
1142
|
app.get("/__content/*", async ({ params, set }) => {
|
|
1274
1143
|
const collectionPath = String(params["*"] || "").trim();
|
|
1275
1144
|
if (!collectionPath)
|
|
1276
1145
|
throw new NotFoundError2;
|
|
1277
|
-
const items = await buildContentDTOs(collectionPath, clientDir, prefix,
|
|
1146
|
+
const items = await buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode2);
|
|
1278
1147
|
const body = JSON.stringify({ collection: collectionPath, items });
|
|
1279
1148
|
set.status = 200;
|
|
1280
1149
|
set.headers["content-type"] = "application/json; charset=utf-8";
|
|
@@ -1336,7 +1205,7 @@ function jsonError(message, status = 500) {
|
|
|
1336
1205
|
}
|
|
1337
1206
|
|
|
1338
1207
|
// packages/elysia/src/routes/dev.ts
|
|
1339
|
-
function registerDevRoutes(app, liveReloadClients,
|
|
1208
|
+
function registerDevRoutes(app, liveReloadClients, isWatchMode2, opts) {
|
|
1340
1209
|
app.get("/__reroute_preload", async ({ query }) => {
|
|
1341
1210
|
const rawSrc = String(query.src || "").trim();
|
|
1342
1211
|
const key = String(query.key || "").trim();
|
|
@@ -1358,7 +1227,7 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode, opts) {
|
|
|
1358
1227
|
return new Response(code, {
|
|
1359
1228
|
headers: {
|
|
1360
1229
|
"content-type": "application/javascript; charset=utf-8",
|
|
1361
|
-
"cache-control":
|
|
1230
|
+
"cache-control": isWatchMode2 ? "no-cache" : "public, max-age=31536000, immutable"
|
|
1362
1231
|
}
|
|
1363
1232
|
});
|
|
1364
1233
|
});
|
|
@@ -1376,14 +1245,14 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode, opts) {
|
|
|
1376
1245
|
pathname,
|
|
1377
1246
|
clientDir: opts?.clientDir || "",
|
|
1378
1247
|
cwd: opts?.cwd || "",
|
|
1379
|
-
isWatchMode
|
|
1248
|
+
isWatchMode: isWatchMode2
|
|
1380
1249
|
});
|
|
1381
1250
|
return jsonWithCache({ data: data2 }, opts?.dataCacheControl || "no-cache", headers);
|
|
1382
1251
|
} catch (_e) {
|
|
1383
1252
|
return jsonError("failed", 500);
|
|
1384
1253
|
}
|
|
1385
1254
|
});
|
|
1386
|
-
if (!
|
|
1255
|
+
if (!isWatchMode2)
|
|
1387
1256
|
return app;
|
|
1388
1257
|
console.log("[reroute] Registering SSE endpoint at /__reroute_watch");
|
|
1389
1258
|
app.get("/__reroute_watch", () => {
|
|
@@ -1455,7 +1324,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1455
1324
|
rootComponent,
|
|
1456
1325
|
clientDir,
|
|
1457
1326
|
cwd,
|
|
1458
|
-
isWatchMode,
|
|
1327
|
+
isWatchMode: isWatchMode2,
|
|
1459
1328
|
getBundleUrl,
|
|
1460
1329
|
head = "",
|
|
1461
1330
|
lang = "en",
|
|
@@ -1480,7 +1349,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1480
1349
|
rootComponent,
|
|
1481
1350
|
clientDir,
|
|
1482
1351
|
cwd,
|
|
1483
|
-
isWatchMode,
|
|
1352
|
+
isWatchMode: isWatchMode2,
|
|
1484
1353
|
bundleUrl,
|
|
1485
1354
|
head,
|
|
1486
1355
|
lang,
|
|
@@ -1513,7 +1382,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1513
1382
|
pathname: p,
|
|
1514
1383
|
clientDir,
|
|
1515
1384
|
cwd,
|
|
1516
|
-
isWatchMode
|
|
1385
|
+
isWatchMode: isWatchMode2
|
|
1517
1386
|
});
|
|
1518
1387
|
return jsonWithCache({ data: data2 }, dataCacheControl || "no-cache", request.headers);
|
|
1519
1388
|
} catch (_e) {
|
|
@@ -1522,7 +1391,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1522
1391
|
}
|
|
1523
1392
|
if (method === "GET" && !isInternal) {
|
|
1524
1393
|
try {
|
|
1525
|
-
if (
|
|
1394
|
+
if (isWatchMode2) {
|
|
1526
1395
|
await generateContentRegistry(cwd);
|
|
1527
1396
|
}
|
|
1528
1397
|
const bundleUrl = await getBundleUrl();
|
|
@@ -1531,7 +1400,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1531
1400
|
rootComponent,
|
|
1532
1401
|
clientDir,
|
|
1533
1402
|
cwd,
|
|
1534
|
-
isWatchMode,
|
|
1403
|
+
isWatchMode: isWatchMode2,
|
|
1535
1404
|
bundleUrl,
|
|
1536
1405
|
head,
|
|
1537
1406
|
lang,
|
|
@@ -1563,15 +1432,15 @@ function registerStaticRoutes(app, options) {
|
|
|
1563
1432
|
shouldIgnore,
|
|
1564
1433
|
bundleCache,
|
|
1565
1434
|
fileCache,
|
|
1566
|
-
isWatchMode
|
|
1435
|
+
isWatchMode: isWatchMode2
|
|
1567
1436
|
} = options;
|
|
1568
1437
|
app.get(`${prefix}/*`, async ({ params, headers }) => {
|
|
1569
1438
|
const requestPath = params["*"];
|
|
1570
1439
|
const hashedJsMatch = requestPath.match(/^(.+)\.([a-f0-9]{8})\.js$/);
|
|
1571
1440
|
if (hashedJsMatch) {
|
|
1572
1441
|
const [, filename, hash2] = hashedJsMatch;
|
|
1573
|
-
const tsxPath =
|
|
1574
|
-
const tsPath =
|
|
1442
|
+
const tsxPath = join2(clientDir, `${filename}.tsx`);
|
|
1443
|
+
const tsPath = join2(clientDir, `${filename}.ts`);
|
|
1575
1444
|
let filePath2 = null;
|
|
1576
1445
|
if (await Bun.file(tsxPath).exists()) {
|
|
1577
1446
|
filePath2 = tsxPath;
|
|
@@ -1601,8 +1470,8 @@ function registerStaticRoutes(app, options) {
|
|
|
1601
1470
|
const sourceMapMatch = requestPath.match(/^(.+)\.([a-f0-9]{8})\.js\.map$/);
|
|
1602
1471
|
if (sourceMapMatch) {
|
|
1603
1472
|
const [, filename, hash2] = sourceMapMatch;
|
|
1604
|
-
const tsxPath =
|
|
1605
|
-
const tsPath =
|
|
1473
|
+
const tsxPath = join2(clientDir, `${filename}.tsx`);
|
|
1474
|
+
const tsPath = join2(clientDir, `${filename}.ts`);
|
|
1606
1475
|
let filePath2 = null;
|
|
1607
1476
|
if (await Bun.file(tsxPath).exists()) {
|
|
1608
1477
|
filePath2 = tsxPath;
|
|
@@ -1637,7 +1506,7 @@ function registerStaticRoutes(app, options) {
|
|
|
1637
1506
|
const name = m[2];
|
|
1638
1507
|
try {
|
|
1639
1508
|
const cwd = process.cwd();
|
|
1640
|
-
const regPath =
|
|
1509
|
+
const regPath = join2(cwd, ".reroute", "content.ts");
|
|
1641
1510
|
const reg = await import((await import("node:url")).pathToFileURL(regPath).href + `?t=${Date.now()}`);
|
|
1642
1511
|
const get = reg?.getContentEntry;
|
|
1643
1512
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
@@ -1650,7 +1519,7 @@ function registerStaticRoutes(app, options) {
|
|
|
1650
1519
|
}
|
|
1651
1520
|
} catch {}
|
|
1652
1521
|
try {
|
|
1653
|
-
const chunkDir =
|
|
1522
|
+
const chunkDir = join2(process.cwd(), ".reroute", "chunks", collection);
|
|
1654
1523
|
const files = await (await import("node:fs/promises")).readdir(chunkDir);
|
|
1655
1524
|
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
1656
1525
|
if (candidates.length) {
|
|
@@ -1659,14 +1528,14 @@ function registerStaticRoutes(app, options) {
|
|
|
1659
1528
|
const { stat: stat5 } = await import("node:fs/promises");
|
|
1660
1529
|
for (const f of candidates) {
|
|
1661
1530
|
try {
|
|
1662
|
-
const s = await stat5(
|
|
1531
|
+
const s = await stat5(join2(chunkDir, f));
|
|
1663
1532
|
if (s.mtimeMs >= latestM) {
|
|
1664
1533
|
latestM = s.mtimeMs;
|
|
1665
1534
|
latest = f;
|
|
1666
1535
|
}
|
|
1667
1536
|
} catch {}
|
|
1668
1537
|
}
|
|
1669
|
-
const loc = `/${
|
|
1538
|
+
const loc = `/${join2(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
|
|
1670
1539
|
return new Response(null, {
|
|
1671
1540
|
status: 302,
|
|
1672
1541
|
headers: { Location: loc }
|
|
@@ -1675,7 +1544,7 @@ function registerStaticRoutes(app, options) {
|
|
|
1675
1544
|
} catch {}
|
|
1676
1545
|
}
|
|
1677
1546
|
} catch {}
|
|
1678
|
-
const filePath2 =
|
|
1547
|
+
const filePath2 = join2(clientDir, requestPath);
|
|
1679
1548
|
if (shouldIgnore(filePath2))
|
|
1680
1549
|
throw new NotFoundError5;
|
|
1681
1550
|
try {
|
|
@@ -1688,7 +1557,7 @@ function registerStaticRoutes(app, options) {
|
|
|
1688
1557
|
return new Response(body, {
|
|
1689
1558
|
headers: {
|
|
1690
1559
|
"Content-Type": contentType,
|
|
1691
|
-
"Cache-Control":
|
|
1560
|
+
"Cache-Control": isWatchMode2 ? "no-cache" : `${directive}, max-age=${maxAge}`,
|
|
1692
1561
|
...extraHeaders,
|
|
1693
1562
|
...staticHeaders
|
|
1694
1563
|
}
|
|
@@ -1700,12 +1569,12 @@ function registerStaticRoutes(app, options) {
|
|
|
1700
1569
|
throw new NotFoundError5;
|
|
1701
1570
|
}
|
|
1702
1571
|
}
|
|
1703
|
-
const filePath =
|
|
1572
|
+
const filePath = join2(clientDir, requestPath);
|
|
1704
1573
|
try {
|
|
1705
1574
|
let actualFilePath = filePath;
|
|
1706
1575
|
let exists = await Bun.file(actualFilePath).exists();
|
|
1707
1576
|
if (!exists && indexHTML) {
|
|
1708
|
-
actualFilePath =
|
|
1577
|
+
actualFilePath = join2(filePath, "index.html");
|
|
1709
1578
|
exists = await Bun.file(actualFilePath).exists();
|
|
1710
1579
|
}
|
|
1711
1580
|
if (!exists)
|
|
@@ -1782,13 +1651,11 @@ var reroute = (options = {}) => async (app) => {
|
|
|
1782
1651
|
}
|
|
1783
1652
|
};
|
|
1784
1653
|
const clientDir = assets.startsWith("/") ? assets : `${cwd.replace(/\/$/, "")}/${assets.replace(/^\.\//, "")}`;
|
|
1785
|
-
const join3 = (...parts) => parts.join("/").replace(/\/+/g, "/");
|
|
1786
1654
|
const shouldIgnore = !ignorePatterns.length ? () => false : (file) => ignorePatterns.find((pattern) => typeof pattern === "string" ? file.includes(pattern) : pattern.test(file));
|
|
1787
|
-
const
|
|
1655
|
+
const isWatchMode2 = Array.isArray(process.execArgv) && process.execArgv.includes("--watch") || Array.isArray(process.argv) && process.argv.includes("--watch");
|
|
1788
1656
|
const dataCacheControl = `${directive}, max-age=${maxAge}`;
|
|
1789
|
-
if (
|
|
1657
|
+
if (isWatchMode2)
|
|
1790
1658
|
console.log(`[reroute] Live reload enabled`);
|
|
1791
|
-
}
|
|
1792
1659
|
await generateContentRegistry(cwd);
|
|
1793
1660
|
let cachedBundleUrlPromise = null;
|
|
1794
1661
|
const ensureBundleUrl = () => {
|
|
@@ -1797,8 +1664,8 @@ var reroute = (options = {}) => async (app) => {
|
|
|
1797
1664
|
}
|
|
1798
1665
|
return cachedBundleUrlPromise;
|
|
1799
1666
|
};
|
|
1800
|
-
registerContentRoutes(app, clientDir, prefix,
|
|
1801
|
-
registerDevRoutes(app, liveReloadClients,
|
|
1667
|
+
registerContentRoutes(app, clientDir, prefix, isWatchMode2);
|
|
1668
|
+
registerDevRoutes(app, liveReloadClients, isWatchMode2, {
|
|
1802
1669
|
clientDir,
|
|
1803
1670
|
cwd,
|
|
1804
1671
|
dataCacheControl
|
|
@@ -1817,62 +1684,27 @@ var reroute = (options = {}) => async (app) => {
|
|
|
1817
1684
|
pathname,
|
|
1818
1685
|
clientDir,
|
|
1819
1686
|
cwd,
|
|
1820
|
-
isWatchMode
|
|
1687
|
+
isWatchMode: isWatchMode2
|
|
1821
1688
|
});
|
|
1822
1689
|
return jsonWithCache({ data: data2 }, dataCacheControl, headers);
|
|
1823
1690
|
} catch (_e) {
|
|
1824
1691
|
return jsonError("failed", 500);
|
|
1825
1692
|
}
|
|
1826
1693
|
});
|
|
1827
|
-
if (
|
|
1828
|
-
|
|
1829
|
-
let debounce;
|
|
1830
|
-
const schedule = () => {
|
|
1831
|
-
clearTimeout(debounce);
|
|
1832
|
-
debounce = setTimeout(async () => {
|
|
1833
|
-
try {
|
|
1834
|
-
bundleCache.clear();
|
|
1835
|
-
cachedBundleUrlPromise = null;
|
|
1836
|
-
await rebuildContentArtifacts(cwd, clientDir, true);
|
|
1837
|
-
try {
|
|
1838
|
-
await buildTailwind(cwd);
|
|
1839
|
-
} catch {}
|
|
1840
|
-
notifyReload();
|
|
1841
|
-
console.log("[reroute] Rebuilt on change and notified clients");
|
|
1842
|
-
} catch (e) {
|
|
1843
|
-
console.error("[reroute] Dev rebuild failed:", e);
|
|
1844
|
-
}
|
|
1845
|
-
}, 120);
|
|
1846
|
-
};
|
|
1847
|
-
let tailwindProc = null;
|
|
1694
|
+
if (isWatchMode2) {
|
|
1695
|
+
app.post("/__reroute_reload", () => {
|
|
1848
1696
|
try {
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
if (shouldIgnore(join3(clientDir, name)))
|
|
1861
|
-
return;
|
|
1862
|
-
schedule();
|
|
1863
|
-
} catch {}
|
|
1864
|
-
});
|
|
1865
|
-
console.log(`[reroute] Watching ${clientDir} for changes`);
|
|
1866
|
-
const stop = () => {
|
|
1867
|
-
try {
|
|
1868
|
-
tailwindProc?.stop?.();
|
|
1869
|
-
} catch {}
|
|
1870
|
-
};
|
|
1871
|
-
process.on("SIGINT", stop);
|
|
1872
|
-
process.on("exit", stop);
|
|
1873
|
-
} catch (e) {
|
|
1874
|
-
console.warn("[reroute] Failed to start dev file watcher:", e);
|
|
1875
|
-
}
|
|
1697
|
+
notifyReload();
|
|
1698
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
1699
|
+
headers: { "content-type": "application/json" }
|
|
1700
|
+
});
|
|
1701
|
+
} catch {
|
|
1702
|
+
return new Response(JSON.stringify({ ok: false }), {
|
|
1703
|
+
status: 500,
|
|
1704
|
+
headers: { "content-type": "application/json" }
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1876
1708
|
}
|
|
1877
1709
|
registerArtifactsRoutes(app, cwd);
|
|
1878
1710
|
if (rootComponent) {
|
|
@@ -1884,7 +1716,7 @@ var reroute = (options = {}) => async (app) => {
|
|
|
1884
1716
|
rootComponent,
|
|
1885
1717
|
clientDir,
|
|
1886
1718
|
cwd,
|
|
1887
|
-
isWatchMode,
|
|
1719
|
+
isWatchMode: isWatchMode2,
|
|
1888
1720
|
getBundleUrl: ensureBundleUrl,
|
|
1889
1721
|
head: ssrHead,
|
|
1890
1722
|
lang: ssrLang,
|
|
@@ -1906,7 +1738,7 @@ var reroute = (options = {}) => async (app) => {
|
|
|
1906
1738
|
shouldIgnore,
|
|
1907
1739
|
bundleCache,
|
|
1908
1740
|
fileCache,
|
|
1909
|
-
isWatchMode
|
|
1741
|
+
isWatchMode: isWatchMode2
|
|
1910
1742
|
});
|
|
1911
1743
|
return app;
|
|
1912
1744
|
};
|
|
@@ -1914,4 +1746,4 @@ export {
|
|
|
1914
1746
|
reroute
|
|
1915
1747
|
};
|
|
1916
1748
|
|
|
1917
|
-
//# debugId=
|
|
1749
|
+
//# debugId=78F127C42E76400F64756E2164756E21
|