bun-plugin-stx 0.2.317 → 0.2.319
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-9gb1t22q.js → chunk-37vp8yms.js} +16 -1
- package/dist/serve.d.ts +30 -0
- package/dist/serve.js +66 -23
- package/package.json +1 -1
|
@@ -205,10 +205,25 @@ class Router {
|
|
|
205
205
|
this.pagesDirs = rawDirs.map((d) => path4.resolve(baseDir, d));
|
|
206
206
|
this.pagesDir = this.pagesDirs[0];
|
|
207
207
|
const extensions = config.extensions || [".stx"];
|
|
208
|
+
const canonical = (entry) => {
|
|
209
|
+
const resolved = path4.resolve(baseDir, entry);
|
|
210
|
+
try {
|
|
211
|
+
return fs3.realpathSync(resolved);
|
|
212
|
+
} catch {
|
|
213
|
+
return resolved;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
const excluded = (config.exclude ?? []).filter((entry) => typeof entry === "string" && entry.trim() !== "").map(canonical);
|
|
217
|
+
const isExcluded = (file) => {
|
|
218
|
+
if (excluded.length === 0)
|
|
219
|
+
return false;
|
|
220
|
+
const real = canonical(file);
|
|
221
|
+
return excluded.some((root) => real === root || real.startsWith(`${root}${path4.sep}`));
|
|
222
|
+
};
|
|
208
223
|
const seenPatterns = new Set;
|
|
209
224
|
this.routes = [];
|
|
210
225
|
for (const dir of this.pagesDirs) {
|
|
211
|
-
const files = scanDirectory(dir, extensions);
|
|
226
|
+
const files = scanDirectory(dir, extensions).filter((file) => !isExcluded(file));
|
|
212
227
|
const localPatterns = new Map;
|
|
213
228
|
for (const filePath of files) {
|
|
214
229
|
const pattern = filePathToPattern(filePath, dir);
|
package/dist/serve.d.ts
CHANGED
|
@@ -60,6 +60,27 @@ export declare function buildDynamicRouteRegexes(fileRouteBase: string): RegExp[
|
|
|
60
60
|
* - `[...rest]` catch-all → -10000 (always last)
|
|
61
61
|
*/
|
|
62
62
|
export declare function routeSpecificity(fileRouteBase: string): number;
|
|
63
|
+
/**
|
|
64
|
+
* A discovered page's path relative to the page root holding it, and that
|
|
65
|
+
* root's position in `patterns`.
|
|
66
|
+
*
|
|
67
|
+
* Specificity has to be measured on the relative path. Measured on the whole
|
|
68
|
+
* path, a root's own directory segments count as static segments, so a page
|
|
69
|
+
* under a deeper root outranks the same route under a shallower one: Stacks
|
|
70
|
+
* passes `['resources/views', '<framework>/defaults/resources/views']`, and a
|
|
71
|
+
* framework default `password/reset/[token].stx` beat the app's own file at
|
|
72
|
+
* the same path purely because its root sits more directories down.
|
|
73
|
+
*/
|
|
74
|
+
export declare function pageRootOf(filePath: string, patterns: string[]): { relative: string, root: number };
|
|
75
|
+
/**
|
|
76
|
+
* Order dynamic route files the way `getRoute` tries them: most specific
|
|
77
|
+
* route first (see {@link routeSpecificity}), and between equally specific
|
|
78
|
+
* routes, the earlier page root first. Earlier roots are the overrides - an
|
|
79
|
+
* app's own views are listed before the framework defaults they replace - so
|
|
80
|
+
* a tie must never go to the later root. Discovery order breaks any remaining
|
|
81
|
+
* tie, which keeps the order deterministic.
|
|
82
|
+
*/
|
|
83
|
+
export declare function orderDynamicRouteFiles(files: string[], patterns: string[]): string[];
|
|
63
84
|
/**
|
|
64
85
|
* True when a request path is for a STATIC ASSET (a non-page file extension like
|
|
65
86
|
* `.jpg`/`.css`/`.js`) rather than a page. Used so a catch-all page can never
|
|
@@ -175,6 +196,14 @@ export declare function boundedCache<T>(limit: number): {
|
|
|
175
196
|
clear: () => void
|
|
176
197
|
readonly size: number
|
|
177
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* A predicate over discovered page files for {@link ServeOptions.exclude}.
|
|
201
|
+
*
|
|
202
|
+
* Paths are compared resolved, because patterns and exclusions reach serve()
|
|
203
|
+
* spelled however the caller built them - relative, absolute, with a trailing
|
|
204
|
+
* slash - and a spelling mismatch must not quietly put a page back.
|
|
205
|
+
*/
|
|
206
|
+
export declare function pageExcluder(exclude: readonly string[] | undefined, cwd?: string): (file: string) => boolean;
|
|
178
207
|
export declare function resolveServeOpenUrl(port: number, openPath?: string): string;
|
|
179
208
|
/**
|
|
180
209
|
* Did rendering fail?
|
|
@@ -271,6 +300,7 @@ export declare interface ServeRequestContext {
|
|
|
271
300
|
}
|
|
272
301
|
export declare interface ServeOptions {
|
|
273
302
|
patterns: string[]
|
|
303
|
+
exclude?: string[]
|
|
274
304
|
port?: number
|
|
275
305
|
openPath?: string
|
|
276
306
|
componentsDir?: string
|
package/dist/serve.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/serve.ts
|
|
11
11
|
var {serve: bunServe, Glob } = globalThis.Bun;
|
|
12
|
-
import { existsSync as existsSync2, watch as fsWatch, statSync as statSync3 } from "fs";
|
|
12
|
+
import { existsSync as existsSync2, watch as fsWatch, realpathSync, statSync as statSync3 } from "fs";
|
|
13
13
|
import nodeFs from "fs/promises";
|
|
14
14
|
import nodePath from "path";
|
|
15
15
|
import process19 from "process";
|
|
@@ -8919,6 +8919,18 @@ function routeSpecificity(fileRouteBase) {
|
|
|
8919
8919
|
}
|
|
8920
8920
|
return score;
|
|
8921
8921
|
}
|
|
8922
|
+
function pageRootOf(filePath, patterns) {
|
|
8923
|
+
const normalized = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
8924
|
+
for (let root = 0;root < patterns.length; root++) {
|
|
8925
|
+
const base = patterns[root].replace(/\\/g, "/").replace(/\/$/, "");
|
|
8926
|
+
if (normalized.startsWith(`${base}/`))
|
|
8927
|
+
return { relative: normalized.slice(base.length + 1), root };
|
|
8928
|
+
}
|
|
8929
|
+
return { relative: normalized, root: patterns.length };
|
|
8930
|
+
}
|
|
8931
|
+
function orderDynamicRouteFiles(files, patterns) {
|
|
8932
|
+
return files.map((file, index) => ({ file, index, ...pageRootOf(file, patterns) })).sort((a, b) => routeSpecificity(b.relative) - routeSpecificity(a.relative) || a.root - b.root || a.index - b.index).map((entry) => entry.file);
|
|
8933
|
+
}
|
|
8922
8934
|
function isStaticAssetPath(requestPath) {
|
|
8923
8935
|
return /\.[a-z0-9]+$/i.test(requestPath) && !/\.(?:stx|md|html)$/i.test(requestPath);
|
|
8924
8936
|
}
|
|
@@ -9084,6 +9096,23 @@ function boundedCache(limit) {
|
|
|
9084
9096
|
}
|
|
9085
9097
|
};
|
|
9086
9098
|
}
|
|
9099
|
+
function pageExcluder(exclude, cwd = process19.cwd()) {
|
|
9100
|
+
const canonical = (entry) => {
|
|
9101
|
+
const resolved = nodePath.resolve(cwd, entry);
|
|
9102
|
+
try {
|
|
9103
|
+
return realpathSync(resolved);
|
|
9104
|
+
} catch {
|
|
9105
|
+
return resolved;
|
|
9106
|
+
}
|
|
9107
|
+
};
|
|
9108
|
+
const roots = (exclude ?? []).filter((entry) => typeof entry === "string" && entry.trim() !== "").map(canonical);
|
|
9109
|
+
if (roots.length === 0)
|
|
9110
|
+
return () => false;
|
|
9111
|
+
return (file) => {
|
|
9112
|
+
const resolved = canonical(file);
|
|
9113
|
+
return roots.some((root) => resolved === root || resolved.startsWith(`${root}${nodePath.sep}`));
|
|
9114
|
+
};
|
|
9115
|
+
}
|
|
9087
9116
|
function resolveServeOpenUrl(port, openPath = "/") {
|
|
9088
9117
|
const baseUrl = `http://localhost:${port}`;
|
|
9089
9118
|
try {
|
|
@@ -9267,6 +9296,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9267
9296
|
throw new Error("No file patterns provided");
|
|
9268
9297
|
}
|
|
9269
9298
|
let sourceFiles = null;
|
|
9299
|
+
const isExcludedPage = pageExcluder(options.exclude);
|
|
9270
9300
|
let discoveredPagePaths = null;
|
|
9271
9301
|
let assetsInitialized = false;
|
|
9272
9302
|
const htmlCache = new Map;
|
|
@@ -9473,21 +9503,32 @@ function __stxOverlay(errs){
|
|
|
9473
9503
|
const src = await nodeFs.readFile(file, "utf-8");
|
|
9474
9504
|
const meta = src.match(/definePageMeta\s*\(\s*\{[\s\S]*?\}\s*\)/);
|
|
9475
9505
|
if (!meta)
|
|
9476
|
-
return;
|
|
9506
|
+
return [];
|
|
9477
9507
|
const mw = meta[0].match(/middleware\s*:\s*(\[[^\]]*\]|['"][^'"]+['"])/);
|
|
9478
9508
|
if (!mw)
|
|
9479
|
-
return;
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9509
|
+
return [];
|
|
9510
|
+
return mw[1].startsWith("[") ? Array.from(mw[1].matchAll(/['"]([^'"]+)['"]/g)).map((m) => m[1]) : [mw[1].replace(/['"]/g, "")];
|
|
9511
|
+
} catch {
|
|
9512
|
+
return [];
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
async function indexPageMiddleware(files) {
|
|
9516
|
+
pageMiddlewareByPath.clear();
|
|
9517
|
+
pageMiddlewarePatterns.length = 0;
|
|
9518
|
+
const detected = await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
9519
|
+
const middlewareOf = new Map(files.map((f, i) => [f, detected[i]]));
|
|
9520
|
+
const dynamic = [];
|
|
9521
|
+
for (const file of files) {
|
|
9483
9522
|
const urlPath = urlPathFromFile(file);
|
|
9484
|
-
if (urlPath.includes("["))
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9523
|
+
if (urlPath.includes("["))
|
|
9524
|
+
dynamic.push(file);
|
|
9525
|
+
else if (!pageMiddlewareByPath.has(urlPath))
|
|
9526
|
+
pageMiddlewareByPath.set(urlPath, middlewareOf.get(file) ?? []);
|
|
9527
|
+
}
|
|
9528
|
+
for (const file of orderDynamicRouteFiles(dynamic, patterns)) {
|
|
9529
|
+
const { re, names } = compileRoutePattern(urlPathFromFile(file));
|
|
9530
|
+
pageMiddlewarePatterns.push({ re, names, middleware: middlewareOf.get(file) ?? [] });
|
|
9531
|
+
}
|
|
9491
9532
|
}
|
|
9492
9533
|
function resolveRouteMiddleware(path) {
|
|
9493
9534
|
const exact = pageMiddlewareByPath.get(path);
|
|
@@ -9718,19 +9759,18 @@ function __stxOverlay(errs){
|
|
|
9718
9759
|
console.error(`Error processing pattern "${pattern}":`, error);
|
|
9719
9760
|
}
|
|
9720
9761
|
}
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
9762
|
+
const kept = files.filter((f) => !isExcludedPage(f));
|
|
9763
|
+
sourceFiles = kept;
|
|
9764
|
+
rebuildDiscoveredPagePaths(kept);
|
|
9765
|
+
await indexPageMiddleware(kept);
|
|
9726
9766
|
try {
|
|
9727
|
-
const { Router } = await import("./chunk-
|
|
9767
|
+
const { Router } = await import("./chunk-37vp8yms.js");
|
|
9728
9768
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9729
|
-
const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName() });
|
|
9769
|
+
const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName(), exclude: options.exclude });
|
|
9730
9770
|
if (!options.quiet)
|
|
9731
9771
|
console.log(`[stx] Generated ${router.routes.length} routes \u2192 ${nodePath.join(stateDirName(), "routes.ts")}`);
|
|
9732
9772
|
} catch (e) {}
|
|
9733
|
-
return
|
|
9773
|
+
return kept;
|
|
9734
9774
|
}
|
|
9735
9775
|
async function ensureAssets() {
|
|
9736
9776
|
if (assetsInitialized)
|
|
@@ -10092,8 +10132,8 @@ function __stxOverlay(errs){
|
|
|
10092
10132
|
if (isAssetRequest)
|
|
10093
10133
|
return false;
|
|
10094
10134
|
return true;
|
|
10095
|
-
})
|
|
10096
|
-
for (const filePath of dynamicFiles) {
|
|
10135
|
+
});
|
|
10136
|
+
for (const filePath of orderDynamicRouteFiles(dynamicFiles, patterns)) {
|
|
10097
10137
|
const normalizedFilePath = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
10098
10138
|
let relativeFilePath = normalizedFilePath;
|
|
10099
10139
|
for (const pattern of patterns) {
|
|
@@ -11185,6 +11225,9 @@ export {
|
|
|
11185
11225
|
render404Page,
|
|
11186
11226
|
publicFileExists,
|
|
11187
11227
|
projectUsesImageBuiltins,
|
|
11228
|
+
pageRootOf,
|
|
11229
|
+
pageExcluder,
|
|
11230
|
+
orderDynamicRouteFiles,
|
|
11188
11231
|
isStaticAssetPath,
|
|
11189
11232
|
isRenderableCacheCandidate,
|
|
11190
11233
|
isRenderFailure,
|
package/package.json
CHANGED