bun-plugin-stx 0.2.735 → 0.2.739
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/README.md +5 -0
- package/dist/render-cache-store.d.ts +47 -0
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +288 -70
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -55,6 +55,11 @@ like `componentsDir`, whether configured in `stx.config.ts` or passed to
|
|
|
55
55
|
update. These roots do not need a duplicate `watchDirs` entry. Missing roots
|
|
56
56
|
are tolerated; resolution order and app overrides are unchanged.
|
|
57
57
|
|
|
58
|
+
Page roots in `patterns` are watched as well. Removing an app override file or
|
|
59
|
+
directory re-resolves the route against the remaining roots, including the
|
|
60
|
+
fallback page's middleware. Recreating the override restores its precedence
|
|
61
|
+
without restarting the development server.
|
|
62
|
+
|
|
58
63
|
## Usage with ESM
|
|
59
64
|
|
|
60
65
|
### 1. Configure Bun to use the plugin
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A digest of everything an entry's signature does not cover. Files are
|
|
3
|
+
* described by path, mtime and size; a missing file is part of the answer
|
|
4
|
+
* too, since creating one (a new `.env.local`) can change what renders.
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderInputsFingerprint(inputs: RenderInputs): Promise<string>;
|
|
7
|
+
/** Put this process's build id where the rendering process put its own. */
|
|
8
|
+
export declare function restampBuildId(html: string, renderedWith: string, current: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* One file per entry, read on demand. A dashboard's worth of pages is tens of
|
|
11
|
+
* megabytes of HTML, so nothing is loaded up front and a page nobody opens
|
|
12
|
+
* costs nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createRenderCacheStore(options: {
|
|
15
|
+
dir: string
|
|
16
|
+
inputs: () => RenderInputs
|
|
17
|
+
buildId: () => string
|
|
18
|
+
}): RenderCacheStore;
|
|
19
|
+
export declare interface PersistableEntry {
|
|
20
|
+
html: string
|
|
21
|
+
signature: Map<string, number>
|
|
22
|
+
status: number
|
|
23
|
+
headers?: Record<string, string>
|
|
24
|
+
}
|
|
25
|
+
export declare interface RenderInputs {
|
|
26
|
+
roots: readonly string[]
|
|
27
|
+
files: readonly string[]
|
|
28
|
+
extra: unknown
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Taken before a render reads anything, and handed back to `write`.
|
|
32
|
+
*
|
|
33
|
+
* The fingerprint is settled first, so it can only be older than what the
|
|
34
|
+
* render read, never newer. An older one no longer matches after the change
|
|
35
|
+
* that made it older, which costs a miss; a newer one would vouch for HTML
|
|
36
|
+
* rendered from files it never described.
|
|
37
|
+
*/
|
|
38
|
+
export declare interface RenderTicket {
|
|
39
|
+
generation: number
|
|
40
|
+
fingerprint: string
|
|
41
|
+
}
|
|
42
|
+
export declare interface RenderCacheStore {
|
|
43
|
+
begin: () => Promise<RenderTicket>
|
|
44
|
+
read: (key: string) => Promise<PersistableEntry | undefined>
|
|
45
|
+
write: (key: string, entry: PersistableEntry, ticket: RenderTicket) => Promise<void>
|
|
46
|
+
invalidate: () => void
|
|
47
|
+
}
|
package/dist/serve.d.ts
CHANGED
|
@@ -361,6 +361,7 @@ export declare interface ServeOptions {
|
|
|
361
361
|
watchDirs?: string[]
|
|
362
362
|
renderCache?: boolean
|
|
363
363
|
renderCacheVary?: 'request' | 'source'
|
|
364
|
+
renderCachePersist?: boolean
|
|
364
365
|
renderCacheLimit?: number
|
|
365
366
|
clientAddress?: (req: Request, server: ServeServer) => string | null | undefined
|
|
366
367
|
cssCacheLimit?: number
|
package/dist/serve.js
CHANGED
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
// src/serve.ts
|
|
10
10
|
var {serve: bunServe, Glob } = globalThis.Bun;
|
|
11
11
|
import { existsSync as existsSync2, watch as fsWatch, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import nodeFs2 from "fs/promises";
|
|
13
|
+
import nodePath2 from "path";
|
|
14
14
|
import process20 from "process";
|
|
15
15
|
|
|
16
16
|
// ../../node_modules/bunfig/dist/index.js
|
|
17
17
|
import { existsSync, statSync } from "fs";
|
|
18
18
|
import { resolve } from "path";
|
|
19
|
-
import
|
|
19
|
+
import process2 from "process";
|
|
20
20
|
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
21
21
|
import { homedir as homedir3 } from "os";
|
|
22
22
|
import { dirname as dirname4, resolve as resolve12 } from "path";
|
|
@@ -33,7 +33,7 @@ import { homedir } from "os";
|
|
|
33
33
|
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
34
34
|
import process6 from "process";
|
|
35
35
|
import { join, relative, resolve as resolve2 } from "path";
|
|
36
|
-
import
|
|
36
|
+
import process22 from "process";
|
|
37
37
|
import { existsSync as existsSync22, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
38
38
|
import { dirname, resolve as resolve5 } from "path";
|
|
39
39
|
import process3 from "process";
|
|
@@ -352,7 +352,7 @@ function getConfigPaths(options) {
|
|
|
352
352
|
return patterns.flatMap((pattern) => CONFIG_FILE_EXTENSIONS.map((extension) => resolve(options.directory, `${pattern}${extension}`)));
|
|
353
353
|
}
|
|
354
354
|
function getLocalConfigDirectories(options = {}) {
|
|
355
|
-
const cwd = resolve(options.cwd ||
|
|
355
|
+
const cwd = resolve(options.cwd || process2.cwd());
|
|
356
356
|
return Array.from(new Set([
|
|
357
357
|
cwd,
|
|
358
358
|
resolve(cwd, "config"),
|
|
@@ -851,12 +851,12 @@ async function loadConfig({
|
|
|
851
851
|
return defaultConfig;
|
|
852
852
|
}
|
|
853
853
|
function getProjectRoot(filePath, options = {}) {
|
|
854
|
-
let path =
|
|
854
|
+
let path = process22.cwd();
|
|
855
855
|
while (path.includes("storage"))
|
|
856
856
|
path = resolve2(path, "..");
|
|
857
857
|
const finalPath = resolve2(path, filePath || "");
|
|
858
858
|
if (options?.relative)
|
|
859
|
-
return relative(
|
|
859
|
+
return relative(process22.cwd(), finalPath);
|
|
860
860
|
return finalPath;
|
|
861
861
|
}
|
|
862
862
|
async function loadConfig2() {
|
|
@@ -864,7 +864,7 @@ async function loadConfig2() {
|
|
|
864
864
|
const loadedConfig = await loadConfig({
|
|
865
865
|
name: "clarity",
|
|
866
866
|
defaultConfig,
|
|
867
|
-
cwd:
|
|
867
|
+
cwd: process22.cwd(),
|
|
868
868
|
endpoint: "",
|
|
869
869
|
headers: {}
|
|
870
870
|
});
|
|
@@ -5389,7 +5389,7 @@ var init_dist = __esm(async () => {
|
|
|
5389
5389
|
};
|
|
5390
5390
|
defaultConfigDir = resolve5(process3.cwd(), "config");
|
|
5391
5391
|
defaultGeneratedDir = resolve5(process3.cwd(), "src/generated");
|
|
5392
|
-
defaultLogDirectory =
|
|
5392
|
+
defaultLogDirectory = process22.env.CLARITY_LOG_DIR || join(getProjectRoot(), "logs");
|
|
5393
5393
|
defaultConfig = {
|
|
5394
5394
|
level: "info",
|
|
5395
5395
|
defaultName: "clarity",
|
|
@@ -8881,10 +8881,39 @@ async function servedTemplatesUseImageBuiltins(root, scope) {
|
|
|
8881
8881
|
options.additionalComponentsDirs = options.additionalComponentsDirs.map(absolute);
|
|
8882
8882
|
const directories = new Map;
|
|
8883
8883
|
const sources = new Map;
|
|
8884
|
+
const listings = new Map;
|
|
8885
|
+
const isFileCache = new Map;
|
|
8886
|
+
function listing(dir) {
|
|
8887
|
+
let entries = listings.get(dir);
|
|
8888
|
+
if (!entries) {
|
|
8889
|
+
entries = readdir4(dir, { withFileTypes: true }).then((list) => ({ exact: new Map(list.map((entry) => [entry.name, entry])), folded: new Set(list.map((entry) => entry.name.toLowerCase())) }), () => null);
|
|
8890
|
+
listings.set(dir, entries);
|
|
8891
|
+
}
|
|
8892
|
+
return entries;
|
|
8893
|
+
}
|
|
8894
|
+
function isFile(file) {
|
|
8895
|
+
let known = isFileCache.get(file);
|
|
8896
|
+
if (!known) {
|
|
8897
|
+
known = (async () => {
|
|
8898
|
+
const entries = await listing(path.dirname(file));
|
|
8899
|
+
if (!entries)
|
|
8900
|
+
return false;
|
|
8901
|
+
const name = path.basename(file);
|
|
8902
|
+
const entry = entries.exact.get(name);
|
|
8903
|
+
if (entry?.isFile())
|
|
8904
|
+
return true;
|
|
8905
|
+
if (entry?.isSymbolicLink() || !entry && entries.folded.has(name.toLowerCase()))
|
|
8906
|
+
return stat4(file).then((result) => result.isFile(), () => false);
|
|
8907
|
+
return false;
|
|
8908
|
+
})();
|
|
8909
|
+
isFileCache.set(file, known);
|
|
8910
|
+
}
|
|
8911
|
+
return known;
|
|
8912
|
+
}
|
|
8884
8913
|
async function firstFile(candidates) {
|
|
8885
8914
|
for (const candidate of candidates) {
|
|
8886
8915
|
const file = absolute(candidate);
|
|
8887
|
-
if (await
|
|
8916
|
+
if (await isFile(file))
|
|
8888
8917
|
return file;
|
|
8889
8918
|
}
|
|
8890
8919
|
}
|
|
@@ -8914,12 +8943,22 @@ async function servedTemplatesUseImageBuiltins(root, scope) {
|
|
|
8914
8943
|
}
|
|
8915
8944
|
return files;
|
|
8916
8945
|
}
|
|
8917
|
-
|
|
8946
|
+
const resolutions = new Map;
|
|
8947
|
+
function component(name, file, page, imported) {
|
|
8918
8948
|
const pascal = name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
8919
8949
|
for (const alias of [name, name.toLowerCase(), pascal, pascalToKebab(name)]) {
|
|
8920
8950
|
if (imported.has(alias))
|
|
8921
|
-
return imported.get(alias);
|
|
8951
|
+
return Promise.resolve(imported.get(alias));
|
|
8952
|
+
}
|
|
8953
|
+
const key = `${name}\x00${path.dirname(page)}\x00${path.dirname(file)}`;
|
|
8954
|
+
let resolving = resolutions.get(key);
|
|
8955
|
+
if (!resolving) {
|
|
8956
|
+
resolving = resolveComponent(name, pascal, file, page);
|
|
8957
|
+
resolutions.set(key, resolving);
|
|
8922
8958
|
}
|
|
8959
|
+
return resolving;
|
|
8960
|
+
}
|
|
8961
|
+
async function resolveComponent(name, pascal, file, page) {
|
|
8923
8962
|
if (name.startsWith("./") || name.startsWith("../"))
|
|
8924
8963
|
return firstFile([path.resolve(path.dirname(file), name.endsWith(".stx") ? name : `${name}.stx`)]);
|
|
8925
8964
|
const names = [...new Set([name, pascal, pascalToKebab(name)].map((value) => `${value.replace(/\.stx$/, "")}.stx`))];
|
|
@@ -9023,6 +9062,94 @@ async function servedTemplatesUseImageBuiltins(root, scope) {
|
|
|
9023
9062
|
}
|
|
9024
9063
|
return false;
|
|
9025
9064
|
}
|
|
9065
|
+
|
|
9066
|
+
// src/render-cache-store.ts
|
|
9067
|
+
import nodeFs from "fs/promises";
|
|
9068
|
+
import nodePath from "path";
|
|
9069
|
+
import { createHash } from "crypto";
|
|
9070
|
+
var WATCHED_EXTENSIONS = new Set([".stx", ".md", ".html", ".js", ".ts", ".tsx", ".mts", ".cts", ".json"]);
|
|
9071
|
+
async function renderInputsFingerprint(inputs) {
|
|
9072
|
+
const lines = [];
|
|
9073
|
+
for (const root of new Set(inputs.roots)) {
|
|
9074
|
+
const absolute = nodePath.resolve(root);
|
|
9075
|
+
const entries = await nodeFs.readdir(absolute, { recursive: true, withFileTypes: true }).catch(() => null);
|
|
9076
|
+
if (!entries) {
|
|
9077
|
+
lines.push(`root ${absolute} missing`);
|
|
9078
|
+
continue;
|
|
9079
|
+
}
|
|
9080
|
+
const files = entries.filter((entry) => entry.isFile() && WATCHED_EXTENSIONS.has(nodePath.extname(entry.name))).map((entry) => nodePath.join(entry.parentPath, entry.name)).filter((file) => !nodePath.relative(absolute, nodePath.dirname(file)).split(nodePath.sep).some((part) => part === "node_modules" || part.startsWith(".")));
|
|
9081
|
+
const stats = await Promise.all(files.map((file) => nodeFs.stat(file).catch(() => null)));
|
|
9082
|
+
files.forEach((file, index) => lines.push(`file ${file} ${stats[index]?.mtimeMs ?? "gone"} ${stats[index]?.size ?? 0}`));
|
|
9083
|
+
}
|
|
9084
|
+
for (const file of new Set(inputs.files)) {
|
|
9085
|
+
const stat = await nodeFs.stat(file).catch(() => null);
|
|
9086
|
+
lines.push(`input ${nodePath.resolve(file)} ${stat ? `${stat.mtimeMs} ${stat.size}` : "missing"}`);
|
|
9087
|
+
}
|
|
9088
|
+
lines.sort();
|
|
9089
|
+
lines.push(`extra ${JSON.stringify(inputs.extra)}`);
|
|
9090
|
+
return createHash("sha256").update(lines.join(`
|
|
9091
|
+
`)).digest("hex");
|
|
9092
|
+
}
|
|
9093
|
+
function restampBuildId(html, renderedWith, current) {
|
|
9094
|
+
if (renderedWith === current)
|
|
9095
|
+
return html;
|
|
9096
|
+
const before = `<meta name="stx-build" content="${renderedWith}">`;
|
|
9097
|
+
const at = html.indexOf(before);
|
|
9098
|
+
if (at === -1)
|
|
9099
|
+
return html;
|
|
9100
|
+
return `${html.slice(0, at)}<meta name="stx-build" content="${current}">${html.slice(at + before.length)}`;
|
|
9101
|
+
}
|
|
9102
|
+
function createRenderCacheStore(options) {
|
|
9103
|
+
let generation = 0;
|
|
9104
|
+
let fingerprint = null;
|
|
9105
|
+
const current = () => fingerprint ??= renderInputsFingerprint(options.inputs());
|
|
9106
|
+
const fileFor = (key) => nodePath.join(options.dir, `${createHash("sha256").update(key).digest("hex")}.json`);
|
|
9107
|
+
return {
|
|
9108
|
+
async begin() {
|
|
9109
|
+
const at = generation;
|
|
9110
|
+
return { generation: at, fingerprint: await current() };
|
|
9111
|
+
},
|
|
9112
|
+
async read(key) {
|
|
9113
|
+
const file = fileFor(key);
|
|
9114
|
+
const stored = await Bun.file(file).json().catch(() => null);
|
|
9115
|
+
if (!stored)
|
|
9116
|
+
return;
|
|
9117
|
+
if (stored.v !== 1 || stored.key !== key || stored.fingerprint !== await current()) {
|
|
9118
|
+
await nodeFs.rm(file, { force: true }).catch(() => {});
|
|
9119
|
+
return;
|
|
9120
|
+
}
|
|
9121
|
+
return {
|
|
9122
|
+
html: restampBuildId(stored.html, stored.buildId, options.buildId()),
|
|
9123
|
+
signature: new Map(stored.signature),
|
|
9124
|
+
status: stored.status,
|
|
9125
|
+
headers: stored.headers
|
|
9126
|
+
};
|
|
9127
|
+
},
|
|
9128
|
+
async write(key, entry, ticket) {
|
|
9129
|
+
if (ticket.generation !== generation)
|
|
9130
|
+
return;
|
|
9131
|
+
const stored = {
|
|
9132
|
+
v: 1,
|
|
9133
|
+
key,
|
|
9134
|
+
fingerprint: ticket.fingerprint,
|
|
9135
|
+
buildId: options.buildId(),
|
|
9136
|
+
html: entry.html,
|
|
9137
|
+
signature: [...entry.signature],
|
|
9138
|
+
status: entry.status,
|
|
9139
|
+
headers: entry.headers
|
|
9140
|
+
};
|
|
9141
|
+
await nodeFs.mkdir(options.dir, { recursive: true });
|
|
9142
|
+
const file = fileFor(key);
|
|
9143
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
9144
|
+
await Bun.write(temporary, JSON.stringify(stored));
|
|
9145
|
+
await nodeFs.rename(temporary, file);
|
|
9146
|
+
},
|
|
9147
|
+
invalidate() {
|
|
9148
|
+
generation++;
|
|
9149
|
+
fingerprint = null;
|
|
9150
|
+
}
|
|
9151
|
+
};
|
|
9152
|
+
}
|
|
9026
9153
|
// src/page-middleware.ts
|
|
9027
9154
|
var DEFAULT_PRIORITY = 10;
|
|
9028
9155
|
function middlewarePriority(middleware) {
|
|
@@ -9262,9 +9389,9 @@ function publicFileExists(requestPath, publicDir) {
|
|
|
9262
9389
|
}
|
|
9263
9390
|
if (decoded.includes("\x00"))
|
|
9264
9391
|
return false;
|
|
9265
|
-
const publicRoot =
|
|
9266
|
-
const resolved =
|
|
9267
|
-
const inside = resolved === publicRoot || resolved.startsWith(`${publicRoot}${
|
|
9392
|
+
const publicRoot = nodePath2.resolve(process20.cwd(), publicDir);
|
|
9393
|
+
const resolved = nodePath2.resolve(publicRoot, `.${decoded}`);
|
|
9394
|
+
const inside = resolved === publicRoot || resolved.startsWith(`${publicRoot}${nodePath2.sep}`);
|
|
9268
9395
|
if (!inside)
|
|
9269
9396
|
return false;
|
|
9270
9397
|
try {
|
|
@@ -9418,7 +9545,7 @@ function boundedCache(limit) {
|
|
|
9418
9545
|
}
|
|
9419
9546
|
function pageExcluder(exclude, cwd = process20.cwd()) {
|
|
9420
9547
|
const canonical = (entry) => {
|
|
9421
|
-
const resolved =
|
|
9548
|
+
const resolved = nodePath2.resolve(cwd, entry);
|
|
9422
9549
|
try {
|
|
9423
9550
|
return realpathSync2(resolved);
|
|
9424
9551
|
} catch {
|
|
@@ -9430,7 +9557,7 @@ function pageExcluder(exclude, cwd = process20.cwd()) {
|
|
|
9430
9557
|
return () => false;
|
|
9431
9558
|
return (file) => {
|
|
9432
9559
|
const resolved = canonical(file);
|
|
9433
|
-
return roots.some((root) => resolved === root || resolved.startsWith(`${root}${
|
|
9560
|
+
return roots.some((root) => resolved === root || resolved.startsWith(`${root}${nodePath2.sep}`));
|
|
9434
9561
|
};
|
|
9435
9562
|
}
|
|
9436
9563
|
function resolveServeOpenUrl(port, openPath = "/") {
|
|
@@ -9665,9 +9792,9 @@ async function serve(options) {
|
|
|
9665
9792
|
}
|
|
9666
9793
|
const stxModule = options.stxModule ? Promise.resolve(options.stxModule) : defaultStxModule;
|
|
9667
9794
|
const imageCacheDir = process20.env.STX_IMAGE_CACHE_DIR?.trim();
|
|
9668
|
-
const imageDeliveryOutputDir = imageCacheDir ?
|
|
9669
|
-
const imagePlaceholderCachePath = imageCacheDir ?
|
|
9670
|
-
const imageDeliveryRoot =
|
|
9795
|
+
const imageDeliveryOutputDir = imageCacheDir ? nodePath2.resolve(imageCacheDir, "image-delivery") : stateDir(process20.cwd(), "image-delivery");
|
|
9796
|
+
const imagePlaceholderCachePath = imageCacheDir ? nodePath2.resolve(imageCacheDir, "image-placeholders.json") : stateDir(process20.cwd(), "image-placeholders.json");
|
|
9797
|
+
const imageDeliveryRoot = nodePath2.join(imageDeliveryOutputDir, "_stx", "images");
|
|
9671
9798
|
let resolvedStxModule = null;
|
|
9672
9799
|
Promise.resolve(stxModule).then((m) => {
|
|
9673
9800
|
resolvedStxModule = m;
|
|
@@ -9694,6 +9821,28 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9694
9821
|
const pendingShellRenders = new Map;
|
|
9695
9822
|
const ENABLE_HTML_CACHE = options.renderCache === true;
|
|
9696
9823
|
const RENDER_CACHE_VARY = options.renderCacheVary ?? "request";
|
|
9824
|
+
const renderCacheStore = ENABLE_HTML_CACHE && RENDER_CACHE_VARY === "source" && options.renderCachePersist === true ? createRenderCacheStore({ dir: stateDir(process20.cwd(), "render-cache"), inputs: () => renderInputs(), buildId: getBuildId }) : null;
|
|
9825
|
+
async function renderTicket(reqCtx) {
|
|
9826
|
+
if (!renderCacheStore || reqCtx && reqCtx.method !== "GET" && reqCtx.method !== "HEAD")
|
|
9827
|
+
return;
|
|
9828
|
+
return renderCacheStore.begin().catch(() => {
|
|
9829
|
+
return;
|
|
9830
|
+
});
|
|
9831
|
+
}
|
|
9832
|
+
async function readRenderCache(key) {
|
|
9833
|
+
const hit = htmlCache.read(key);
|
|
9834
|
+
if (hit || !renderCacheStore)
|
|
9835
|
+
return hit;
|
|
9836
|
+
const kept = await renderCacheStore.read(key).catch(() => {
|
|
9837
|
+
return;
|
|
9838
|
+
});
|
|
9839
|
+
return kept ? htmlCache.remember(key, kept) : undefined;
|
|
9840
|
+
}
|
|
9841
|
+
function rememberRender(key, entry, ticket) {
|
|
9842
|
+
htmlCache.remember(key, entry);
|
|
9843
|
+
if (renderCacheStore && ticket)
|
|
9844
|
+
renderCacheStore.write(key, entry, ticket).catch(() => {});
|
|
9845
|
+
}
|
|
9697
9846
|
const cssCssCache = boundedCache(options.cssCacheLimit ?? 512);
|
|
9698
9847
|
const hmrClients = new Set;
|
|
9699
9848
|
const hmrEncoder = new TextEncoder;
|
|
@@ -9791,14 +9940,22 @@ function __stxOverlay(errs){
|
|
|
9791
9940
|
currentBuildErrors = errors;
|
|
9792
9941
|
broadcastHmr({ type: "build-error", errors });
|
|
9793
9942
|
}
|
|
9794
|
-
const watchersStarted = new
|
|
9943
|
+
const watchersStarted = new Map;
|
|
9944
|
+
const clearRenderCaches = () => {
|
|
9945
|
+
htmlCache.clear();
|
|
9946
|
+
renderCacheStore?.invalidate();
|
|
9947
|
+
cssCssCache.clear();
|
|
9948
|
+
resolvedStxModule?.clearDevCaches?.();
|
|
9949
|
+
resolvedStxModule?.clearStoreCache?.();
|
|
9950
|
+
resolvedStxModule?.clearComposableCache?.();
|
|
9951
|
+
};
|
|
9795
9952
|
const startWatcher = (dir) => {
|
|
9796
9953
|
if (production)
|
|
9797
9954
|
return;
|
|
9798
9955
|
if (watchersStarted.has(dir))
|
|
9799
9956
|
return;
|
|
9800
9957
|
try {
|
|
9801
|
-
const watcher = fsWatch(dir, { recursive: true }, (
|
|
9958
|
+
const watcher = fsWatch(dir, { recursive: true }, (event, filename) => {
|
|
9802
9959
|
if (!filename)
|
|
9803
9960
|
return;
|
|
9804
9961
|
const f = String(filename);
|
|
@@ -9807,22 +9964,19 @@ function __stxOverlay(errs){
|
|
|
9807
9964
|
const isData = f.endsWith(".json");
|
|
9808
9965
|
const isCss = f.endsWith(".css");
|
|
9809
9966
|
const isAsset = isCss || isCode || f.endsWith(".jpg") || f.endsWith(".jpeg") || f.endsWith(".png") || f.endsWith(".gif") || f.endsWith(".svg") || f.endsWith(".webp") || f.endsWith(".avif") || f.endsWith(".woff") || f.endsWith(".woff2") || f.endsWith(".ttf") || f.endsWith(".otf");
|
|
9810
|
-
|
|
9967
|
+
const topologyChanged = event === "rename";
|
|
9968
|
+
if (!isStxLike && !isAsset && !isData && !topologyChanged)
|
|
9811
9969
|
return;
|
|
9812
|
-
if (isStxLike || isCode || isData) {
|
|
9813
|
-
if (isStxLike)
|
|
9970
|
+
if (isStxLike || isCode || isData || topologyChanged) {
|
|
9971
|
+
if (isStxLike || topologyChanged)
|
|
9814
9972
|
sourceFiles = null;
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
resolvedStxModule?.clearDevCaches?.();
|
|
9818
|
-
resolvedStxModule?.clearStoreCache?.();
|
|
9819
|
-
resolvedStxModule?.clearComposableCache?.();
|
|
9820
|
-
if (layoutsDir && f.endsWith(".stx") && nodePath.resolve(f).startsWith(nodePath.resolve(layoutsDir)))
|
|
9973
|
+
clearRenderCaches();
|
|
9974
|
+
if (layoutsDir && f.endsWith(".stx") && nodePath2.resolve(f).startsWith(nodePath2.resolve(layoutsDir)))
|
|
9821
9975
|
writeLayoutTypes();
|
|
9822
9976
|
}
|
|
9823
|
-
const resolved =
|
|
9824
|
-
const isStore = resolved.startsWith(
|
|
9825
|
-
const isLayout = !!layoutsDir && resolved.startsWith(
|
|
9977
|
+
const resolved = nodePath2.resolve(dir, f);
|
|
9978
|
+
const isStore = resolved.startsWith(nodePath2.resolve(storesDir)) || composableDirs.some((d) => resolved.startsWith(nodePath2.resolve(d)));
|
|
9979
|
+
const isLayout = !!layoutsDir && resolved.startsWith(nodePath2.resolve(layoutsDir));
|
|
9826
9980
|
if (isCss && !isStxLike)
|
|
9827
9981
|
broadcastHmr({ type: "css", file: f });
|
|
9828
9982
|
else if (isStore)
|
|
@@ -9833,7 +9987,30 @@ function __stxOverlay(errs){
|
|
|
9833
9987
|
broadcastHmr({ type: "reload", file: f });
|
|
9834
9988
|
});
|
|
9835
9989
|
watcher.on("error", () => {});
|
|
9836
|
-
watchersStarted.
|
|
9990
|
+
watchersStarted.set(dir, watcher);
|
|
9991
|
+
} catch {}
|
|
9992
|
+
};
|
|
9993
|
+
const pageRootWatchersStarted = new Set;
|
|
9994
|
+
const watchPageRoot = (dir) => {
|
|
9995
|
+
if (production || pageRootWatchersStarted.has(dir))
|
|
9996
|
+
return;
|
|
9997
|
+
const absolute = nodePath2.resolve(dir);
|
|
9998
|
+
const parent = nodePath2.dirname(absolute);
|
|
9999
|
+
if (parent === absolute)
|
|
10000
|
+
return;
|
|
10001
|
+
try {
|
|
10002
|
+
const watcher = fsWatch(parent, { recursive: false }, (event, filename) => {
|
|
10003
|
+
if (event !== "rename" || String(filename) !== nodePath2.basename(absolute))
|
|
10004
|
+
return;
|
|
10005
|
+
watchersStarted.get(dir)?.close();
|
|
10006
|
+
watchersStarted.delete(dir);
|
|
10007
|
+
startWatcher(dir);
|
|
10008
|
+
sourceFiles = null;
|
|
10009
|
+
clearRenderCaches();
|
|
10010
|
+
broadcastHmr({ type: "reload", file: dir });
|
|
10011
|
+
});
|
|
10012
|
+
watcher.on("error", () => {});
|
|
10013
|
+
pageRootWatchersStarted.add(dir);
|
|
9837
10014
|
} catch {}
|
|
9838
10015
|
};
|
|
9839
10016
|
const writeLayoutTypes = async () => {
|
|
@@ -9843,9 +10020,9 @@ function __stxOverlay(errs){
|
|
|
9843
10020
|
} catch {}
|
|
9844
10021
|
};
|
|
9845
10022
|
await writeLayoutTypes();
|
|
9846
|
-
const storesDir =
|
|
9847
|
-
const composableDirs = stxConfig.composablesDir ? [
|
|
9848
|
-
|
|
10023
|
+
const storesDir = nodePath2.resolve(stxConfig.root || ".", stxConfig.storesDir || "stores");
|
|
10024
|
+
const composableDirs = stxConfig.composablesDir ? [nodePath2.resolve(stxConfig.root || ".", stxConfig.composablesDir)] : ["composables", "functions"].map((d) => nodePath2.resolve(stxConfig.root || ".", d));
|
|
10025
|
+
const watchedRoots = [
|
|
9849
10026
|
componentsDir,
|
|
9850
10027
|
...additionalComponentsDirs ?? [],
|
|
9851
10028
|
layoutsDir,
|
|
@@ -9855,10 +10032,46 @@ function __stxOverlay(errs){
|
|
|
9855
10032
|
storesDir,
|
|
9856
10033
|
...composableDirs,
|
|
9857
10034
|
...options.watchDirs ?? []
|
|
9858
|
-
])
|
|
9859
|
-
|
|
9860
|
-
continue;
|
|
10035
|
+
].filter((dir) => !!dir);
|
|
10036
|
+
for (const dir of watchedRoots)
|
|
9861
10037
|
startWatcher(dir);
|
|
10038
|
+
function renderInputs() {
|
|
10039
|
+
const cwd = process20.cwd();
|
|
10040
|
+
const configFiles = ["stx", "css"].flatMap((name) => [
|
|
10041
|
+
`${name}.config.ts`,
|
|
10042
|
+
`${name}.config.js`,
|
|
10043
|
+
`.config/${name}.ts`,
|
|
10044
|
+
`config/${name}.ts`
|
|
10045
|
+
]);
|
|
10046
|
+
let stxEntry;
|
|
10047
|
+
try {
|
|
10048
|
+
stxEntry = Bun.resolveSync("@stacksjs/stx", import.meta.dir);
|
|
10049
|
+
} catch {}
|
|
10050
|
+
return {
|
|
10051
|
+
roots: [...watchedRoots, ...patterns.filter((pattern) => !pattern.includes("*"))],
|
|
10052
|
+
files: [
|
|
10053
|
+
...["package.json", "bun.lock", "bun.lockb", "pantry.lock", ".env", ".env.local", ".env.development", ".env.production", ...configFiles].map((file) => nodePath2.join(cwd, file)),
|
|
10054
|
+
import.meta.path,
|
|
10055
|
+
...stxEntry ? [stxEntry] : []
|
|
10056
|
+
],
|
|
10057
|
+
extra: {
|
|
10058
|
+
bun: Bun.version,
|
|
10059
|
+
production,
|
|
10060
|
+
options: {
|
|
10061
|
+
patterns,
|
|
10062
|
+
root: cwd,
|
|
10063
|
+
componentsDir,
|
|
10064
|
+
additionalComponentsDirs,
|
|
10065
|
+
layoutsDir,
|
|
10066
|
+
fallbackLayoutsDir: options.fallbackLayoutsDir,
|
|
10067
|
+
fallbackComponentsDir: options.fallbackComponentsDir,
|
|
10068
|
+
partialsDir,
|
|
10069
|
+
publicDir,
|
|
10070
|
+
watchDirs: options.watchDirs,
|
|
10071
|
+
domains: options.domains
|
|
10072
|
+
}
|
|
10073
|
+
}
|
|
10074
|
+
};
|
|
9862
10075
|
}
|
|
9863
10076
|
const pageMiddlewareByPath = new Map;
|
|
9864
10077
|
const pageMiddlewarePatterns = [];
|
|
@@ -9897,7 +10110,7 @@ function __stxOverlay(errs){
|
|
|
9897
10110
|
}
|
|
9898
10111
|
async function detectPageMiddleware(file) {
|
|
9899
10112
|
try {
|
|
9900
|
-
const src = await
|
|
10113
|
+
const src = await nodeFs2.readFile(file, "utf-8");
|
|
9901
10114
|
const meta = src.match(/definePageMeta\s*\(\s*\{[\s\S]*?\}\s*\)/);
|
|
9902
10115
|
if (!meta)
|
|
9903
10116
|
return [];
|
|
@@ -10137,7 +10350,9 @@ function __stxOverlay(errs){
|
|
|
10137
10350
|
const supportedExtensions = [".stx", ".md", ".html"];
|
|
10138
10351
|
for (const pattern of patterns) {
|
|
10139
10352
|
try {
|
|
10140
|
-
const stat = await
|
|
10353
|
+
const stat = await nodeFs2.stat(pattern).catch(() => null);
|
|
10354
|
+
if (stat?.isDirectory() || !stat && !pattern.includes("*") && !supportedExtensions.some((ext) => pattern.endsWith(ext)))
|
|
10355
|
+
watchPageRoot(pattern);
|
|
10141
10356
|
if (stat?.isDirectory()) {
|
|
10142
10357
|
startWatcher(pattern);
|
|
10143
10358
|
const excludeDirs = ["layouts", "components", "partials"];
|
|
@@ -10173,7 +10388,7 @@ function __stxOverlay(errs){
|
|
|
10173
10388
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
10174
10389
|
const router = new Router(process20.cwd(), { pagesDirs, stateDir: stateDirName(), exclude: options.exclude });
|
|
10175
10390
|
if (!options.quiet)
|
|
10176
|
-
console.log(`[stx] Generated ${router.routes.length} routes \u2192 ${
|
|
10391
|
+
console.log(`[stx] Generated ${router.routes.length} routes \u2192 ${nodePath2.join(stateDirName(), "routes.ts")}`);
|
|
10177
10392
|
} catch (e) {}
|
|
10178
10393
|
return kept;
|
|
10179
10394
|
}
|
|
@@ -10184,16 +10399,16 @@ function __stxOverlay(errs){
|
|
|
10184
10399
|
const assetsDir = "./resources/assets";
|
|
10185
10400
|
const targetAssetsDir = stateDir(process20.cwd(), "assets");
|
|
10186
10401
|
try {
|
|
10187
|
-
const assetsExist = await
|
|
10402
|
+
const assetsExist = await nodeFs2.stat(assetsDir).then(() => true).catch(() => false);
|
|
10188
10403
|
if (assetsExist) {
|
|
10189
|
-
await
|
|
10190
|
-
await
|
|
10404
|
+
await nodeFs2.rm(targetAssetsDir, { recursive: true, force: true });
|
|
10405
|
+
await nodeFs2.cp(assetsDir, targetAssetsDir, { recursive: true });
|
|
10191
10406
|
}
|
|
10192
10407
|
} catch {}
|
|
10193
10408
|
}
|
|
10194
10409
|
async function templateSignatureFresh(sig) {
|
|
10195
10410
|
for (const [p, expected] of sig) {
|
|
10196
|
-
const stat = await
|
|
10411
|
+
const stat = await nodeFs2.stat(p).catch(() => null);
|
|
10197
10412
|
if (!stat || stat.mtimeMs !== expected)
|
|
10198
10413
|
return false;
|
|
10199
10414
|
}
|
|
@@ -10202,7 +10417,7 @@ function __stxOverlay(errs){
|
|
|
10202
10417
|
async function buildTemplateSignature(filePath, deps) {
|
|
10203
10418
|
const sig = new Map;
|
|
10204
10419
|
const allPaths = [filePath, ...deps];
|
|
10205
|
-
const stats = await Promise.all(allPaths.map((p) =>
|
|
10420
|
+
const stats = await Promise.all(allPaths.map((p) => nodeFs2.stat(p).catch(() => null)));
|
|
10206
10421
|
for (let i = 0;i < allPaths.length; i++) {
|
|
10207
10422
|
const stat = stats[i];
|
|
10208
10423
|
if (stat)
|
|
@@ -10218,10 +10433,10 @@ function __stxOverlay(errs){
|
|
|
10218
10433
|
return cssModule;
|
|
10219
10434
|
cssLoadAttempted = true;
|
|
10220
10435
|
let dir = process20.cwd();
|
|
10221
|
-
while (dir !==
|
|
10436
|
+
while (dir !== nodePath2.dirname(dir)) {
|
|
10222
10437
|
for (const store of ["node_modules", "pantry"]) {
|
|
10223
10438
|
for (const entry of ENGINE_PACKAGE_ENTRIES) {
|
|
10224
|
-
const candidate =
|
|
10439
|
+
const candidate = nodePath2.join(dir, store, ...entry);
|
|
10225
10440
|
try {
|
|
10226
10441
|
if (!await Bun.file(candidate).exists())
|
|
10227
10442
|
continue;
|
|
@@ -10233,7 +10448,7 @@ function __stxOverlay(errs){
|
|
|
10233
10448
|
} catch {}
|
|
10234
10449
|
}
|
|
10235
10450
|
}
|
|
10236
|
-
dir =
|
|
10451
|
+
dir = nodePath2.dirname(dir);
|
|
10237
10452
|
}
|
|
10238
10453
|
for (const specifier of ENGINE_SPECIFIERS) {
|
|
10239
10454
|
try {
|
|
@@ -10245,7 +10460,7 @@ function __stxOverlay(errs){
|
|
|
10245
10460
|
} catch {}
|
|
10246
10461
|
}
|
|
10247
10462
|
try {
|
|
10248
|
-
const localPath =
|
|
10463
|
+
const localPath = nodePath2.join(process20.env.HOME || "", "Code/Tools/crosswind/packages/toolkit/src/engine/index.ts");
|
|
10249
10464
|
const mod = await import(localPath);
|
|
10250
10465
|
cssModule = { CSSGenerator: mod.CSSGenerator, config: mod.config };
|
|
10251
10466
|
return cssModule;
|
|
@@ -10375,6 +10590,7 @@ function __stxOverlay(errs){
|
|
|
10375
10590
|
reqCtx.responseHeaders = { ...reqCtx.responseHeaders, ...headers };
|
|
10376
10591
|
}
|
|
10377
10592
|
async function processTemplate(filePath, reqCtx) {
|
|
10593
|
+
const ticket = await renderTicket(reqCtx);
|
|
10378
10594
|
const content = await Bun.file(filePath).text();
|
|
10379
10595
|
if (reqCtx)
|
|
10380
10596
|
reqCtx.responseStatus = extractPageResponseStatus(content) ?? 200;
|
|
@@ -10382,7 +10598,7 @@ function __stxOverlay(errs){
|
|
|
10382
10598
|
const isMutating = !!reqCtx && reqCtx.method !== "GET" && reqCtx.method !== "HEAD";
|
|
10383
10599
|
if (ENABLE_HTML_CACHE && !skipCacheHint && !isMutating) {
|
|
10384
10600
|
const cacheKey = htmlCacheKey(filePath, reqCtx);
|
|
10385
|
-
const cachedEntry =
|
|
10601
|
+
const cachedEntry = await readRenderCache(cacheKey);
|
|
10386
10602
|
if (cachedEntry && await templateSignatureFresh(cachedEntry.signature)) {
|
|
10387
10603
|
if (reqCtx) {
|
|
10388
10604
|
reqCtx.responseStatus = cachedEntry.status;
|
|
@@ -10394,7 +10610,7 @@ function __stxOverlay(errs){
|
|
|
10394
10610
|
const { serverScripts, templateContent } = extractServerScriptsFromTemplate(content);
|
|
10395
10611
|
const context = {
|
|
10396
10612
|
__filename: filePath,
|
|
10397
|
-
__dirname:
|
|
10613
|
+
__dirname: nodePath2.dirname(filePath),
|
|
10398
10614
|
__stx_options: stxConfig,
|
|
10399
10615
|
__stx_runtime_head: {}
|
|
10400
10616
|
};
|
|
@@ -10461,7 +10677,7 @@ function __stxOverlay(errs){
|
|
|
10461
10677
|
}
|
|
10462
10678
|
if (ENABLE_HTML_CACHE && !context.__stx_skip_cache && !skipCacheHint && !isMutating && isRenderableCacheCandidate(output) && placeholdersAreReady) {
|
|
10463
10679
|
const signature = await buildTemplateSignature(filePath, dependencies);
|
|
10464
|
-
|
|
10680
|
+
rememberRender(htmlCacheKey(filePath, reqCtx), { html: output, signature, status: reqCtx?.responseStatus ?? 200, headers: reqCtx?.responseHeaders }, ticket);
|
|
10465
10681
|
}
|
|
10466
10682
|
return output;
|
|
10467
10683
|
}
|
|
@@ -10483,7 +10699,7 @@ function __stxOverlay(errs){
|
|
|
10483
10699
|
possibleFiles.push(`${normalizedPath}/index.md`);
|
|
10484
10700
|
possibleFiles.push(`${normalizedPath}/index.html`);
|
|
10485
10701
|
}
|
|
10486
|
-
const filename =
|
|
10702
|
+
const filename = nodePath2.basename(normalizedPath, nodePath2.extname(normalizedPath));
|
|
10487
10703
|
if (filename && !normalizedPath.includes("/")) {
|
|
10488
10704
|
possibleFiles.push(`${filename}.stx`);
|
|
10489
10705
|
possibleFiles.push(`${filename}.md`);
|
|
@@ -10565,6 +10781,7 @@ function __stxOverlay(errs){
|
|
|
10565
10781
|
return null;
|
|
10566
10782
|
}
|
|
10567
10783
|
async function processTemplateDynamic(filePath, paramNames, paramValues, routePath, reqCtx) {
|
|
10784
|
+
const ticket = await renderTicket(reqCtx);
|
|
10568
10785
|
const content = await Bun.file(filePath).text();
|
|
10569
10786
|
if (reqCtx)
|
|
10570
10787
|
reqCtx.responseStatus = extractPageResponseStatus(content) ?? 200;
|
|
@@ -10583,7 +10800,7 @@ function __stxOverlay(errs){
|
|
|
10583
10800
|
if (reqCtx)
|
|
10584
10801
|
reqCtx.params = paramsObj;
|
|
10585
10802
|
if (cacheShell) {
|
|
10586
|
-
const cachedEntry =
|
|
10803
|
+
const cachedEntry = await readRenderCache(filePath);
|
|
10587
10804
|
if (cachedEntry && await templateSignatureFresh(cachedEntry.signature)) {
|
|
10588
10805
|
if (reqCtx) {
|
|
10589
10806
|
reqCtx.responseStatus = cachedEntry.status;
|
|
@@ -10606,7 +10823,7 @@ function __stxOverlay(errs){
|
|
|
10606
10823
|
try {
|
|
10607
10824
|
const context = {
|
|
10608
10825
|
__filename: filePath,
|
|
10609
|
-
__dirname:
|
|
10826
|
+
__dirname: nodePath2.dirname(filePath),
|
|
10610
10827
|
__route: routePath,
|
|
10611
10828
|
__stx_runtime_head: {}
|
|
10612
10829
|
};
|
|
@@ -10678,7 +10895,7 @@ ${output}`;
|
|
|
10678
10895
|
const recordedResponse = readResponseStatus(context) !== undefined || !!readResponseHeaders(context);
|
|
10679
10896
|
if (!context.__stx_skip_cache && !recordedResponse && isRenderableCacheCandidate(output) && placeholdersAreReady) {
|
|
10680
10897
|
const signature = await buildTemplateSignature(filePath, dependencies);
|
|
10681
|
-
|
|
10898
|
+
rememberRender(filePath, { html: output, signature, status: reqCtx?.responseStatus ?? 200, headers: reqCtx?.responseHeaders }, ticket);
|
|
10682
10899
|
}
|
|
10683
10900
|
return fillRouteParams(output, paramsObj);
|
|
10684
10901
|
} finally {
|
|
@@ -10842,7 +11059,7 @@ ${block}`;
|
|
|
10842
11059
|
}
|
|
10843
11060
|
try {
|
|
10844
11061
|
const stx = await stxModule;
|
|
10845
|
-
const publicRoot =
|
|
11062
|
+
const publicRoot = nodePath2.resolve(process20.cwd(), publicDir);
|
|
10846
11063
|
const [placeholderResult, deliveryResult] = await Promise.allSettled([
|
|
10847
11064
|
stx.warmImagePlaceholders(publicRoot, {
|
|
10848
11065
|
cachePath: imagePlaceholderCachePath,
|
|
@@ -11077,8 +11294,8 @@ ${block}`;
|
|
|
11077
11294
|
}
|
|
11078
11295
|
if (!decodedPath || decodedPath.includes("\x00"))
|
|
11079
11296
|
return new Response("Invalid image path", { status: 400 });
|
|
11080
|
-
const resolvedPath =
|
|
11081
|
-
if (!resolvedPath.startsWith(`${imageDeliveryRoot}${
|
|
11297
|
+
const resolvedPath = nodePath2.resolve(imageDeliveryRoot, decodedPath);
|
|
11298
|
+
if (!resolvedPath.startsWith(`${imageDeliveryRoot}${nodePath2.sep}`))
|
|
11082
11299
|
return new Response("Invalid image path", { status: 400 });
|
|
11083
11300
|
try {
|
|
11084
11301
|
const file = Bun.file(resolvedPath);
|
|
@@ -11140,6 +11357,7 @@ ${block}`;
|
|
|
11140
11357
|
if (hookResult && typeof hookResult === "object")
|
|
11141
11358
|
Object.assign(reqCtx, hookResult);
|
|
11142
11359
|
}
|
|
11360
|
+
await discoverFiles();
|
|
11143
11361
|
const route = resolveRouteMiddleware(path);
|
|
11144
11362
|
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
11145
11363
|
const requested = [...globalMiddlewareNames, ...route.names, ...extraNames];
|
|
@@ -11221,7 +11439,7 @@ ${block}`;
|
|
|
11221
11439
|
}
|
|
11222
11440
|
const body = await req.json();
|
|
11223
11441
|
const { Database } = await import("bun:sqlite");
|
|
11224
|
-
const dbPath =
|
|
11442
|
+
const dbPath = nodePath2.resolve(process20.cwd(), "database/stacks.sqlite");
|
|
11225
11443
|
const db = new Database(dbPath);
|
|
11226
11444
|
try {
|
|
11227
11445
|
const tableInfo = db.query(`PRAGMA table_info("${tableName}")`).all();
|
|
@@ -11514,7 +11732,7 @@ ${trailingPageScripts.join(`
|
|
|
11514
11732
|
return new Response("Invalid asset path", { status: 400 });
|
|
11515
11733
|
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
11516
11734
|
try {
|
|
11517
|
-
const filePath =
|
|
11735
|
+
const filePath = nodePath2.resolve(process20.cwd(), `.${assetPath}`);
|
|
11518
11736
|
const file = Bun.file(filePath);
|
|
11519
11737
|
if (await file.exists()) {
|
|
11520
11738
|
const ext = getAssetExtension(assetPath);
|
|
@@ -11533,7 +11751,7 @@ ${trailingPageScripts.join(`
|
|
|
11533
11751
|
}
|
|
11534
11752
|
}
|
|
11535
11753
|
if ((req.method === "GET" || req.method === "HEAD") && path !== "/") {
|
|
11536
|
-
const publicRoot =
|
|
11754
|
+
const publicRoot = nodePath2.resolve(process20.cwd(), publicDir);
|
|
11537
11755
|
let safePathname;
|
|
11538
11756
|
try {
|
|
11539
11757
|
safePathname = decodeURIComponent(path);
|
|
@@ -11541,8 +11759,8 @@ ${trailingPageScripts.join(`
|
|
|
11541
11759
|
safePathname = path;
|
|
11542
11760
|
}
|
|
11543
11761
|
if (!safePathname.includes("\x00")) {
|
|
11544
|
-
const resolvedPath =
|
|
11545
|
-
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${
|
|
11762
|
+
const resolvedPath = nodePath2.resolve(publicRoot, `.${safePathname}`);
|
|
11763
|
+
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${nodePath2.sep}`);
|
|
11546
11764
|
if (isInsidePublicRoot) {
|
|
11547
11765
|
try {
|
|
11548
11766
|
const file = Bun.file(resolvedPath);
|
|
@@ -11823,7 +12041,7 @@ async function projectUsesImageBuiltins(root, scope) {
|
|
|
11823
12041
|
for (const entry of entries) {
|
|
11824
12042
|
if (entry.name.startsWith("."))
|
|
11825
12043
|
continue;
|
|
11826
|
-
const full =
|
|
12044
|
+
const full = nodePath2.join(dir, entry.name);
|
|
11827
12045
|
if (entry.isDirectory()) {
|
|
11828
12046
|
if (!TEMPLATE_SCAN_SKIP.has(entry.name))
|
|
11829
12047
|
pending.push(full);
|
|
@@ -11837,7 +12055,7 @@ async function projectUsesImageBuiltins(root, scope) {
|
|
|
11837
12055
|
} catch {
|
|
11838
12056
|
continue;
|
|
11839
12057
|
}
|
|
11840
|
-
const inComponents =
|
|
12058
|
+
const inComponents = nodePath2.relative(root, dir).split(nodePath2.sep).includes("components");
|
|
11841
12059
|
const component = inComponents ? entry.name.slice(0, -".stx".length) : undefined;
|
|
11842
12060
|
if (!component && IMAGE_BUILTIN_PATTERN.test(source))
|
|
11843
12061
|
return true;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-plugin-stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.739",
|
|
5
5
|
"description": "A Bun plugin that allows for using Laravel Blade-like syntax.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"typecheck": "bun tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@stacksjs/stx": "0.2.
|
|
78
|
-
"stx-router": "0.2.
|
|
77
|
+
"@stacksjs/stx": "0.2.739",
|
|
78
|
+
"stx-router": "0.2.739"
|
|
79
79
|
}
|
|
80
80
|
}
|