bun-plugin-stx 0.2.65 → 0.2.66
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-ap5bhr00.js → chunk-wv4bcqba.js} +12 -8
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +164 -35
- package/package.json +3 -3
|
@@ -913,7 +913,18 @@ else {
|
|
|
913
913
|
// re-execute because each page has its own setup.
|
|
914
914
|
var h=hashScript(code);
|
|
915
915
|
var isSetup=code.indexOf('__stx_setup_')!==-1;
|
|
916
|
-
|
|
916
|
+
// Compute isAlreadyScoped first so the dedup check can exempt
|
|
917
|
+
// self-contained IIFE scripts. data-stx-scoped scope IIFEs
|
|
918
|
+
// (the (function(){ ... })() shape) must re-execute on every
|
|
919
|
+
// navigation. They own their own scope and won't collide
|
|
920
|
+
// with prior runs, AND _cleanupContainer deleted their
|
|
921
|
+
// entry from window.stx._scopes on the way out. Without
|
|
922
|
+
// this exemption, navigating away and back leaves the page
|
|
923
|
+
// leaf with no scope registered: bindIf/@event never re-bind
|
|
924
|
+
// and all :if branches stay visible at once.
|
|
925
|
+
// Mirrors the full-doc swap path's runAlways escape below.
|
|
926
|
+
var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
|
|
927
|
+
if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){
|
|
917
928
|
log('[router] skipping already-executed script (hash dedup)');
|
|
918
929
|
return;
|
|
919
930
|
}
|
|
@@ -923,13 +934,6 @@ else {
|
|
|
923
934
|
if(hasImport){
|
|
924
935
|
ns.type='module';
|
|
925
936
|
}
|
|
926
|
-
// Wrap in block scope to prevent const/let collisions on re-navigation.
|
|
927
|
-
// Without this, navigating away and back throws "Identifier has already
|
|
928
|
-
// been declared" because the previous page's top-level const/let are
|
|
929
|
-
// still in the global scope. Skip the wrap for module scripts \u2014
|
|
930
|
-
// they get their own scope from ESM, and top-level 'import' is
|
|
931
|
-
// illegal inside a block, which would throw SyntaxError.
|
|
932
|
-
var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
|
|
933
937
|
ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
|
|
934
938
|
ns.setAttribute('data-stx-page','');
|
|
935
939
|
document.body.appendChild(ns);
|
package/dist/serve.d.ts
CHANGED
package/dist/serve.js
CHANGED
|
@@ -8730,6 +8730,53 @@ var staticContentTypes = {
|
|
|
8730
8730
|
otf: "font/otf",
|
|
8731
8731
|
eot: "application/vnd.ms-fontobject"
|
|
8732
8732
|
};
|
|
8733
|
+
var bundledAssetExtensions = new Set(["ts", "tsx", "mts", "cts"]);
|
|
8734
|
+
function isBundledAssetExtension(ext) {
|
|
8735
|
+
return Boolean(ext && bundledAssetExtensions.has(ext));
|
|
8736
|
+
}
|
|
8737
|
+
function getAssetExtension(pathname) {
|
|
8738
|
+
return pathname.split(".").pop()?.toLowerCase();
|
|
8739
|
+
}
|
|
8740
|
+
function isSafeAssetPath(pathname) {
|
|
8741
|
+
if (pathname.includes("\x00"))
|
|
8742
|
+
return false;
|
|
8743
|
+
return !pathname.split("/").some((segment) => segment === "..");
|
|
8744
|
+
}
|
|
8745
|
+
function assetRequestPaths(pathname) {
|
|
8746
|
+
const paths = [
|
|
8747
|
+
pathname,
|
|
8748
|
+
pathname.replace(/^\/assets\//, "/resources/assets/"),
|
|
8749
|
+
pathname.replace(/^\/resources\/assets\//, "/assets/")
|
|
8750
|
+
];
|
|
8751
|
+
return [...new Set(paths)];
|
|
8752
|
+
}
|
|
8753
|
+
async function bundleBrowserAsset(entrypoint) {
|
|
8754
|
+
const result = await Bun.build({
|
|
8755
|
+
entrypoints: [entrypoint],
|
|
8756
|
+
format: "esm",
|
|
8757
|
+
minify: false,
|
|
8758
|
+
packages: "bundle",
|
|
8759
|
+
sourcemap: "inline",
|
|
8760
|
+
target: "browser"
|
|
8761
|
+
});
|
|
8762
|
+
if (!result.success) {
|
|
8763
|
+
const message = result.logs.map((log4) => log4.message).join(`
|
|
8764
|
+
`) || "Unable to build TypeScript asset";
|
|
8765
|
+
return new Response(message, {
|
|
8766
|
+
status: 500,
|
|
8767
|
+
headers: {
|
|
8768
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
8769
|
+
"Cache-Control": "no-cache"
|
|
8770
|
+
}
|
|
8771
|
+
});
|
|
8772
|
+
}
|
|
8773
|
+
return new Response(await result.outputs[0].text(), {
|
|
8774
|
+
headers: {
|
|
8775
|
+
"Content-Type": "application/javascript",
|
|
8776
|
+
"Cache-Control": "no-cache"
|
|
8777
|
+
}
|
|
8778
|
+
});
|
|
8779
|
+
}
|
|
8733
8780
|
async function serve(options) {
|
|
8734
8781
|
const stxConfig = await loadConfig6({
|
|
8735
8782
|
name: "stx",
|
|
@@ -8770,6 +8817,32 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8770
8817
|
const routes = new Map;
|
|
8771
8818
|
let sourceFiles = null;
|
|
8772
8819
|
let assetsInitialized = false;
|
|
8820
|
+
const htmlCache = new Map;
|
|
8821
|
+
const crosswindCssCache = new Map;
|
|
8822
|
+
const hmrClients = new Set;
|
|
8823
|
+
const hmrEncoder = new TextEncoder;
|
|
8824
|
+
function broadcastHmr(event) {
|
|
8825
|
+
const payload = hmrEncoder.encode(`data: ${JSON.stringify(event)}
|
|
8826
|
+
|
|
8827
|
+
`);
|
|
8828
|
+
for (const c of hmrClients) {
|
|
8829
|
+
try {
|
|
8830
|
+
c.enqueue(payload);
|
|
8831
|
+
} catch {
|
|
8832
|
+
hmrClients.delete(c);
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
const HMR_CLIENT_SCRIPT = `<script data-stx-hmr>(()=>{if(window.__stxHmr)return;window.__stxHmr=1;function bust(){var ls=document.querySelectorAll('link[rel="stylesheet"]');for(var i=0;i<ls.length;i++){var l=ls[i];var u=new URL(l.href,location.href);u.searchParams.set('v',Date.now().toString(36));l.href=u.toString()}}var es=new EventSource('/_stx/hmr');es.onmessage=function(e){try{var m=JSON.parse(e.data);if(m.type==='reload')location.reload();else if(m.type==='css')bust()}catch(_){}};es.onerror=function(){if(es.readyState===2){setTimeout(function(){location.reload()},400)}}})()</script>`;
|
|
8837
|
+
function injectHmrClient(html) {
|
|
8838
|
+
if (!html)
|
|
8839
|
+
return html;
|
|
8840
|
+
const closeBody = html.lastIndexOf("</body>");
|
|
8841
|
+
if (closeBody === -1) {
|
|
8842
|
+
return html + HMR_CLIENT_SCRIPT;
|
|
8843
|
+
}
|
|
8844
|
+
return html.slice(0, closeBody) + HMR_CLIENT_SCRIPT + html.slice(closeBody);
|
|
8845
|
+
}
|
|
8773
8846
|
const watchersStarted = new Set;
|
|
8774
8847
|
const startWatcher = (dir) => {
|
|
8775
8848
|
if (watchersStarted.has(dir))
|
|
@@ -8779,15 +8852,34 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8779
8852
|
if (!filename)
|
|
8780
8853
|
return;
|
|
8781
8854
|
const f = String(filename);
|
|
8782
|
-
|
|
8855
|
+
const isStxLike = f.endsWith(".stx") || f.endsWith(".md") || f.endsWith(".html");
|
|
8856
|
+
const isCss = f.endsWith(".css");
|
|
8857
|
+
const isAsset = isCss || f.endsWith(".js") || f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".mts") || f.endsWith(".cts") || 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");
|
|
8858
|
+
if (!isStxLike && !isAsset)
|
|
8783
8859
|
return;
|
|
8784
|
-
|
|
8785
|
-
|
|
8860
|
+
if (isStxLike) {
|
|
8861
|
+
sourceFiles = null;
|
|
8862
|
+
routes.clear();
|
|
8863
|
+
htmlCache.clear();
|
|
8864
|
+
crosswindCssCache.clear();
|
|
8865
|
+
}
|
|
8866
|
+
broadcastHmr(isCss && !isStxLike ? { type: "css", file: f } : { type: "reload", file: f });
|
|
8786
8867
|
});
|
|
8787
8868
|
watcher.on("error", () => {});
|
|
8788
8869
|
watchersStarted.add(dir);
|
|
8789
8870
|
} catch {}
|
|
8790
8871
|
};
|
|
8872
|
+
for (const dir of [
|
|
8873
|
+
componentsDir,
|
|
8874
|
+
layoutsDir,
|
|
8875
|
+
partialsDir,
|
|
8876
|
+
publicDir,
|
|
8877
|
+
"resources/assets"
|
|
8878
|
+
]) {
|
|
8879
|
+
if (!dir)
|
|
8880
|
+
continue;
|
|
8881
|
+
startWatcher(dir);
|
|
8882
|
+
}
|
|
8791
8883
|
const pageMiddlewareByPath = new Map;
|
|
8792
8884
|
const pageMiddlewarePatterns = [];
|
|
8793
8885
|
function compileRoutePattern(urlPath) {
|
|
@@ -8971,7 +9063,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8971
9063
|
pageMiddlewarePatterns.length = 0;
|
|
8972
9064
|
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
8973
9065
|
try {
|
|
8974
|
-
const { Router } = await import("./chunk-
|
|
9066
|
+
const { Router } = await import("./chunk-wv4bcqba.js");
|
|
8975
9067
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
8976
9068
|
const router = new Router(process19.cwd(), { pagesDirs });
|
|
8977
9069
|
console.log(`[stx] Generated ${router.routes.length} routes \u2192 .stx/routes.ts`);
|
|
@@ -8992,7 +9084,6 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8992
9084
|
}
|
|
8993
9085
|
} catch {}
|
|
8994
9086
|
}
|
|
8995
|
-
const htmlCache = new Map;
|
|
8996
9087
|
async function templateSignatureFresh(sig) {
|
|
8997
9088
|
for (const [p, expected] of sig) {
|
|
8998
9089
|
const stat4 = await nodeFs.stat(p).catch(() => null);
|
|
@@ -9015,7 +9106,6 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9015
9106
|
let crosswindModule = null;
|
|
9016
9107
|
let crosswindLoadAttempted = false;
|
|
9017
9108
|
let crosswindUserConfigPromise = null;
|
|
9018
|
-
const crosswindCssCache = new Map;
|
|
9019
9109
|
async function loadCrosswind() {
|
|
9020
9110
|
if (crosswindLoadAttempted)
|
|
9021
9111
|
return crosswindModule;
|
|
@@ -9292,9 +9382,20 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9292
9382
|
const regexPatterns = [
|
|
9293
9383
|
new RegExp(`^${routePattern}$`)
|
|
9294
9384
|
];
|
|
9385
|
+
const fileRouteNoIndex = fileRouteBase.replace(/\/index$/, "");
|
|
9386
|
+
if (fileRouteNoIndex !== fileRouteBase) {
|
|
9387
|
+
const noIndexPattern = fileRouteNoIndex.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9388
|
+
regexPatterns.push(new RegExp(`^${noIndexPattern}$`));
|
|
9389
|
+
}
|
|
9295
9390
|
if (fileRouteBase.startsWith("pages/")) {
|
|
9296
|
-
const
|
|
9391
|
+
const prettyBase = fileRouteBase.slice(6);
|
|
9392
|
+
const prettyPattern = prettyBase.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9297
9393
|
regexPatterns.push(new RegExp(`^${prettyPattern}$`));
|
|
9394
|
+
const prettyNoIndex = prettyBase.replace(/\/index$/, "");
|
|
9395
|
+
if (prettyNoIndex !== prettyBase) {
|
|
9396
|
+
const prettyNoIndexPattern = prettyNoIndex.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9397
|
+
regexPatterns.push(new RegExp(`^${prettyNoIndexPattern}$`));
|
|
9398
|
+
}
|
|
9298
9399
|
}
|
|
9299
9400
|
for (const regex of regexPatterns) {
|
|
9300
9401
|
const match = normalizedPath.match(regex);
|
|
@@ -9472,6 +9573,7 @@ ${html}`;
|
|
|
9472
9573
|
try {
|
|
9473
9574
|
_server = bunServe({
|
|
9474
9575
|
port: actualPort,
|
|
9576
|
+
idleTimeout: 0,
|
|
9475
9577
|
async fetch(req) {
|
|
9476
9578
|
const url = new URL(req.url);
|
|
9477
9579
|
let path = url.pathname;
|
|
@@ -9490,6 +9592,40 @@ ${html}`;
|
|
|
9490
9592
|
if (req.method === "OPTIONS") {
|
|
9491
9593
|
return new Response(null, { headers: corsHeaders });
|
|
9492
9594
|
}
|
|
9595
|
+
if (path === "/_stx/hmr") {
|
|
9596
|
+
let controller;
|
|
9597
|
+
let keepalive;
|
|
9598
|
+
const stream = new ReadableStream({
|
|
9599
|
+
start(c) {
|
|
9600
|
+
controller = c;
|
|
9601
|
+
hmrClients.add(c);
|
|
9602
|
+
c.enqueue(hmrEncoder.encode(`data: {"type":"connected"}
|
|
9603
|
+
|
|
9604
|
+
`));
|
|
9605
|
+
keepalive = setInterval(() => {
|
|
9606
|
+
try {
|
|
9607
|
+
c.enqueue(hmrEncoder.encode(`: keepalive
|
|
9608
|
+
|
|
9609
|
+
`));
|
|
9610
|
+
} catch {}
|
|
9611
|
+
}, 15000);
|
|
9612
|
+
},
|
|
9613
|
+
cancel() {
|
|
9614
|
+
if (keepalive)
|
|
9615
|
+
clearInterval(keepalive);
|
|
9616
|
+
if (controller)
|
|
9617
|
+
hmrClients.delete(controller);
|
|
9618
|
+
}
|
|
9619
|
+
});
|
|
9620
|
+
return new Response(stream, {
|
|
9621
|
+
headers: {
|
|
9622
|
+
"Content-Type": "text/event-stream",
|
|
9623
|
+
"Cache-Control": "no-cache, no-transform",
|
|
9624
|
+
Connection: "keep-alive",
|
|
9625
|
+
"X-Accel-Buffering": "no"
|
|
9626
|
+
}
|
|
9627
|
+
});
|
|
9628
|
+
}
|
|
9493
9629
|
if (options.onRequest) {
|
|
9494
9630
|
const customResponse = await options.onRequest(req);
|
|
9495
9631
|
if (customResponse)
|
|
@@ -9737,7 +9873,7 @@ ${pageSetupScripts.join(`
|
|
|
9737
9873
|
runtimeCount++;
|
|
9738
9874
|
return runtimeCount === 1 ? match : "";
|
|
9739
9875
|
});
|
|
9740
|
-
return new Response(cleaned, {
|
|
9876
|
+
return new Response(injectHmrClient(cleaned), {
|
|
9741
9877
|
headers: {
|
|
9742
9878
|
"Content-Type": "text/html; charset=utf-8",
|
|
9743
9879
|
"Cache-Control": "no-cache",
|
|
@@ -9747,7 +9883,7 @@ ${pageSetupScripts.join(`
|
|
|
9747
9883
|
}
|
|
9748
9884
|
const contentWithExt = await getRoute(`${path}.html`);
|
|
9749
9885
|
if (contentWithExt) {
|
|
9750
|
-
return new Response(contentWithExt, {
|
|
9886
|
+
return new Response(injectHmrClient(contentWithExt), {
|
|
9751
9887
|
headers: {
|
|
9752
9888
|
"Content-Type": "text/html; charset=utf-8",
|
|
9753
9889
|
"Cache-Control": "no-cache",
|
|
@@ -9772,34 +9908,26 @@ ${pageSetupScripts.join(`
|
|
|
9772
9908
|
}
|
|
9773
9909
|
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
9774
9910
|
await ensureAssets();
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
path
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9911
|
+
let assetPathname;
|
|
9912
|
+
try {
|
|
9913
|
+
assetPathname = decodeURIComponent(path);
|
|
9914
|
+
} catch {
|
|
9915
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
9916
|
+
}
|
|
9917
|
+
if (!isSafeAssetPath(assetPathname))
|
|
9918
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
9919
|
+
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
9781
9920
|
try {
|
|
9782
|
-
const filePath = `.${assetPath}
|
|
9921
|
+
const filePath = nodePath.resolve(process19.cwd(), `.${assetPath}`);
|
|
9783
9922
|
const file = Bun.file(filePath);
|
|
9784
9923
|
if (await file.exists()) {
|
|
9785
|
-
const ext = assetPath
|
|
9786
|
-
if (ext
|
|
9787
|
-
|
|
9788
|
-
loader: "ts"
|
|
9789
|
-
});
|
|
9790
|
-
const code = await file.text();
|
|
9791
|
-
const transpiled = transpiler.transformSync(code);
|
|
9792
|
-
return new Response(transpiled, {
|
|
9793
|
-
headers: {
|
|
9794
|
-
"Content-Type": "application/javascript",
|
|
9795
|
-
"Cache-Control": "no-cache"
|
|
9796
|
-
}
|
|
9797
|
-
});
|
|
9798
|
-
}
|
|
9924
|
+
const ext = getAssetExtension(assetPath);
|
|
9925
|
+
if (isBundledAssetExtension(ext))
|
|
9926
|
+
return bundleBrowserAsset(filePath);
|
|
9799
9927
|
return new Response(file, {
|
|
9800
9928
|
headers: {
|
|
9801
9929
|
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
9802
|
-
"Cache-Control": "
|
|
9930
|
+
"Cache-Control": "no-cache"
|
|
9803
9931
|
}
|
|
9804
9932
|
});
|
|
9805
9933
|
}
|
|
@@ -9829,7 +9957,7 @@ ${pageSetupScripts.join(`
|
|
|
9829
9957
|
return new Response(file, {
|
|
9830
9958
|
headers: {
|
|
9831
9959
|
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
9832
|
-
"Cache-Control": "
|
|
9960
|
+
"Cache-Control": "no-cache"
|
|
9833
9961
|
}
|
|
9834
9962
|
});
|
|
9835
9963
|
}
|
|
@@ -9860,7 +9988,7 @@ ${pageSetupScripts.join(`
|
|
|
9860
9988
|
}
|
|
9861
9989
|
const routesList = availableRoutes.join(`
|
|
9862
9990
|
`);
|
|
9863
|
-
return new Response(`
|
|
9991
|
+
return new Response(injectHmrClient(`
|
|
9864
9992
|
<!DOCTYPE html>
|
|
9865
9993
|
<html>
|
|
9866
9994
|
<head>
|
|
@@ -9887,7 +10015,7 @@ ${pageSetupScripts.join(`
|
|
|
9887
10015
|
<ul>${routesList}</ul>
|
|
9888
10016
|
</body>
|
|
9889
10017
|
</html>
|
|
9890
|
-
|
|
10018
|
+
`), {
|
|
9891
10019
|
status: 404,
|
|
9892
10020
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
9893
10021
|
});
|
|
@@ -9963,5 +10091,6 @@ if (import.meta.main) {
|
|
|
9963
10091
|
main().catch(console.error);
|
|
9964
10092
|
}
|
|
9965
10093
|
export {
|
|
9966
|
-
serve
|
|
10094
|
+
serve,
|
|
10095
|
+
bundleBrowserAsset
|
|
9967
10096
|
};
|
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.66",
|
|
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",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"typecheck": "bun tsc --noEmit"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@stacksjs/stx": "0.2.
|
|
77
|
-
"stx-router": "0.2.
|
|
76
|
+
"@stacksjs/stx": "0.2.66",
|
|
77
|
+
"stx-router": "0.2.66"
|
|
78
78
|
}
|
|
79
79
|
}
|