@stacksjs/stx 0.2.113 → 0.2.115
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/app-shell.d.ts +1 -1
- package/dist/app-shell.js +7 -1
- package/dist/caching.js +2 -1
- package/dist/cli.js +1437 -956
- package/dist/client-script-bundler.js +33 -16
- package/dist/client-script.js +50 -5
- package/dist/component-renderer.js +56 -13
- package/dist/composable-loader.d.ts +15 -0
- package/dist/composable-loader.js +117 -0
- package/dist/desktop.d.ts +15 -0
- package/dist/desktop.js +1 -0
- package/dist/dev-server/serve-app.js +1 -1
- package/dist/expressions.js +9 -2
- package/dist/includes.js +6 -4
- package/dist/menubar.d.ts +63 -0
- package/dist/menubar.js +103 -0
- package/dist/plugin.js +4 -3
- package/dist/process.js +83 -23
- package/dist/pwa.js +7359 -6878
- package/dist/render.js +4 -24
- package/dist/runtime-globals.d.ts +59 -0
- package/dist/runtime-globals.js +65 -0
- package/dist/runtime-injection.js +2 -2
- package/dist/serve.js +4 -21
- package/dist/sfc-template.d.ts +17 -0
- package/dist/sfc-template.js +32 -0
- package/dist/signal-processing.js +27 -12
- package/dist/signals.js +558 -128
- package/dist/slots.js +105 -20
- package/dist/ssg.js +7381 -6900
- package/dist/stx.d.ts +8 -0
- package/dist/testing.js +8 -1
- package/dist/types/config-types.d.ts +2 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.js +66 -19
- package/dist/variable-extractor.js +3 -3
- package/package.json +5 -7
- package/stx.d.ts +8 -0
package/dist/menubar.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createPreferences, createWindow, setAutoLaunch } from "@stacksjs/desktop";
|
|
2
|
+
import { renderTemplate } from "./render.js";
|
|
3
|
+
const MENU_BAR_WINDOW = {
|
|
4
|
+
width: 320,
|
|
5
|
+
height: 640,
|
|
6
|
+
systemTray: !0,
|
|
7
|
+
hideDockIcon: !0,
|
|
8
|
+
titlebarHidden: !0,
|
|
9
|
+
resizable: !1,
|
|
10
|
+
alwaysOnTop: !0,
|
|
11
|
+
darkMode: !0
|
|
12
|
+
}, JSON_HEADERS = { "Content-Type": "application/json; charset=utf-8" };
|
|
13
|
+
export function normalizeRouteKey(key) {
|
|
14
|
+
const [first, ...rest] = key.trim().split(/\s+/);
|
|
15
|
+
return rest.length ? `${first.toUpperCase()} ${rest.join(" ")}` : `GET ${first}`;
|
|
16
|
+
}
|
|
17
|
+
export function toResponse(result) {
|
|
18
|
+
if (result instanceof Response)
|
|
19
|
+
return result;
|
|
20
|
+
if (result === void 0)
|
|
21
|
+
return new Response(null, { status: 204 });
|
|
22
|
+
return new Response(JSON.stringify(result), { headers: JSON_HEADERS });
|
|
23
|
+
}
|
|
24
|
+
export function applyPreferencePatch(preferences, patch) {
|
|
25
|
+
const known = preferences.getAll();
|
|
26
|
+
for (const [key, value] of Object.entries(patch))
|
|
27
|
+
if (key in known)
|
|
28
|
+
preferences.set(key, value);
|
|
29
|
+
return preferences.getAll();
|
|
30
|
+
}
|
|
31
|
+
export function createMenuBarApp(options) {
|
|
32
|
+
const preferences = createPreferences({
|
|
33
|
+
name: options.name.toLowerCase(),
|
|
34
|
+
defaults: options.preferences ?? {}
|
|
35
|
+
}), routes = new Map(Object.entries(options.routes ?? {}).map(([key, handler]) => [normalizeRouteKey(key), handler])), builtins = {
|
|
36
|
+
"GET /api/preferences": () => preferences.getAll(),
|
|
37
|
+
"POST /api/preferences": async (request) => applyPreferencePatch(preferences, await request.json()),
|
|
38
|
+
"GET /api/menu": () => options.menu?.(preferences) ?? []
|
|
39
|
+
};
|
|
40
|
+
for (const [key, handler] of Object.entries(builtins))
|
|
41
|
+
if (!routes.has(key))
|
|
42
|
+
routes.set(key, handler);
|
|
43
|
+
let server = null, window = null, url = null;
|
|
44
|
+
async function renderPopup() {
|
|
45
|
+
const context = await options.context?.(preferences) ?? {}, html = await renderTemplate(options.template, {
|
|
46
|
+
context,
|
|
47
|
+
wrapInDocument: !0,
|
|
48
|
+
title: options.name
|
|
49
|
+
});
|
|
50
|
+
return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
51
|
+
}
|
|
52
|
+
function serve() {
|
|
53
|
+
server = Bun.serve({
|
|
54
|
+
port: options.port ?? 0,
|
|
55
|
+
hostname: "127.0.0.1",
|
|
56
|
+
async fetch(request) {
|
|
57
|
+
const { pathname } = new URL(request.url);
|
|
58
|
+
if (request.method === "GET" && (pathname === "/" || pathname === "/index.html"))
|
|
59
|
+
return renderPopup();
|
|
60
|
+
const handler = routes.get(`${request.method} ${pathname}`);
|
|
61
|
+
if (!handler)
|
|
62
|
+
return new Response("Not Found", { status: 404 });
|
|
63
|
+
return toResponse(await handler(request, preferences));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
url = `http://127.0.0.1:${server.port}`;
|
|
67
|
+
}
|
|
68
|
+
function bindLaunchAtLogin() {
|
|
69
|
+
const key = options.launchAtLogin;
|
|
70
|
+
if (!key)
|
|
71
|
+
return;
|
|
72
|
+
const sync = (enabled) => {
|
|
73
|
+
setAutoLaunch(Boolean(enabled), { appName: options.name, isHidden: !0 }).catch(() => {});
|
|
74
|
+
};
|
|
75
|
+
sync(preferences.get(key));
|
|
76
|
+
preferences.onChange(key, sync);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
preferences,
|
|
80
|
+
get url() {
|
|
81
|
+
return url;
|
|
82
|
+
},
|
|
83
|
+
async start() {
|
|
84
|
+
if (!server)
|
|
85
|
+
serve();
|
|
86
|
+
bindLaunchAtLogin();
|
|
87
|
+
window = await createWindow(url, {
|
|
88
|
+
...MENU_BAR_WINDOW,
|
|
89
|
+
title: options.name,
|
|
90
|
+
...options.window
|
|
91
|
+
});
|
|
92
|
+
return this;
|
|
93
|
+
},
|
|
94
|
+
stop() {
|
|
95
|
+
window?.close();
|
|
96
|
+
window = null;
|
|
97
|
+
server?.stop();
|
|
98
|
+
server = null;
|
|
99
|
+
url = null;
|
|
100
|
+
preferences.close();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
package/dist/plugin.js
CHANGED
|
@@ -5,6 +5,7 @@ import { isDevelopment } from "./env.js";
|
|
|
5
5
|
import { escapeHtml } from "./expressions.js";
|
|
6
6
|
import { performanceMonitor } from "./performance-utils.js";
|
|
7
7
|
import { injectRouterScript } from "./process.js";
|
|
8
|
+
import { findSfcTemplateBlock } from "./sfc-template.js";
|
|
8
9
|
export const plugin = {
|
|
9
10
|
name: "bun-plugin-stx",
|
|
10
11
|
async setup(build) {
|
|
@@ -42,9 +43,9 @@ export const plugin = {
|
|
|
42
43
|
throw new StxFileError(`stx file is empty: ${filePath}`, filePath);
|
|
43
44
|
const { scriptContent, templateContent, allScripts } = performanceMonitor.time("script-extraction", () => {
|
|
44
45
|
let workingContent = content;
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
workingContent =
|
|
46
|
+
const templateBlock = findSfcTemplateBlock(content);
|
|
47
|
+
if (templateBlock)
|
|
48
|
+
workingContent = templateBlock.content.trim();
|
|
48
49
|
const allScriptMatches = content.match(/<script\b[^>]*>[\s\S]*?<\/script>/gi) || [];
|
|
49
50
|
let serverScriptContent = "";
|
|
50
51
|
const clientScripts = [];
|
package/dist/process.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { injectCrosswindCSS } from "./dev-server/crosswind.js";
|
|
3
|
-
import { matchHtmlComment, maskAtElementPosition } from "./html-masking.js";
|
|
3
|
+
import { matchHtmlComment, matchStyleElement, maskAtElementPosition, stashScriptElements } from "./html-masking.js";
|
|
4
4
|
import { processA11yDirectives } from "./a11y.js";
|
|
5
5
|
import { generateLifecycleRuntime } from "./composables.js";
|
|
6
6
|
import { processTemplateBindings } from "./reactive-bindings.js";
|
|
@@ -186,6 +186,58 @@ function findScriptBodyEnd(src, from) {
|
|
|
186
186
|
}
|
|
187
187
|
return -1;
|
|
188
188
|
}
|
|
189
|
+
function findScriptBlockByAttribute(html, attribute) {
|
|
190
|
+
const escapedAttribute = attribute.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), openingTag = new RegExp(`<script\\b(?=[^>]*\\b${escapedAttribute}(?:\\s|=|/?>))[^>]*>`, "i").exec(html);
|
|
191
|
+
if (!openingTag)
|
|
192
|
+
return null;
|
|
193
|
+
const bodyStart = openingTag.index + openingTag[0].length, closeStart = html.indexOf("</script>", bodyStart);
|
|
194
|
+
if (closeStart === -1)
|
|
195
|
+
return null;
|
|
196
|
+
const closeEnd = html.indexOf(">", closeStart);
|
|
197
|
+
if (closeEnd === -1)
|
|
198
|
+
return null;
|
|
199
|
+
return {
|
|
200
|
+
start: openingTag.index,
|
|
201
|
+
end: closeEnd + 1
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function findFirstScriptTag(html) {
|
|
205
|
+
let searchFrom = 0;
|
|
206
|
+
while (searchFrom < html.length) {
|
|
207
|
+
const tagStart = html.indexOf("<", searchFrom);
|
|
208
|
+
if (tagStart === -1)
|
|
209
|
+
return -1;
|
|
210
|
+
if (html.startsWith("<!--", tagStart)) {
|
|
211
|
+
const commentEnd = html.indexOf("-->", tagStart + 4);
|
|
212
|
+
searchFrom = commentEnd === -1 ? html.length : commentEnd + 3;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const remainder = html.slice(tagStart), styleTag = /^<style\b[^>]*>/i.exec(remainder);
|
|
216
|
+
if (styleTag) {
|
|
217
|
+
const styleEnd = html.toLowerCase().indexOf("</style>", tagStart + styleTag[0].length);
|
|
218
|
+
searchFrom = styleEnd === -1 ? html.length : styleEnd + 8;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (/^<script\b[^>]*>/i.test(remainder))
|
|
222
|
+
return tagStart;
|
|
223
|
+
searchFrom = tagStart + 1;
|
|
224
|
+
}
|
|
225
|
+
return -1;
|
|
226
|
+
}
|
|
227
|
+
function placeSignalsRuntimeBeforeScripts(html) {
|
|
228
|
+
const runtime = findScriptBlockByAttribute(html, "data-stx-runtime");
|
|
229
|
+
if (!runtime)
|
|
230
|
+
return html;
|
|
231
|
+
const firstScript = findFirstScriptTag(html);
|
|
232
|
+
if (firstScript === -1 || firstScript === runtime.start)
|
|
233
|
+
return html;
|
|
234
|
+
const runtimeTag = html.slice(runtime.start, runtime.end), withoutRuntime = html.slice(0, runtime.start) + html.slice(runtime.end), insertionPoint = findFirstScriptTag(withoutRuntime);
|
|
235
|
+
if (insertionPoint === -1)
|
|
236
|
+
return runtimeTag + `
|
|
237
|
+
` + withoutRuntime;
|
|
238
|
+
return withoutRuntime.slice(0, insertionPoint) + runtimeTag + `
|
|
239
|
+
` + withoutRuntime.slice(insertionPoint);
|
|
240
|
+
}
|
|
189
241
|
export async function processDirectives(template, context, filePath, options, dependencies) {
|
|
190
242
|
if (options.ssr === !1 && !options.autoShell && options.buildMode === "spa")
|
|
191
243
|
return template;
|
|
@@ -238,21 +290,28 @@ export async function processDirectives(template, context, filePath, options, de
|
|
|
238
290
|
result = result.replace(wrongAttrRegex, "$1");
|
|
239
291
|
}
|
|
240
292
|
}
|
|
241
|
-
if (isTopLevel)
|
|
293
|
+
if (isTopLevel) {
|
|
242
294
|
result = injectCloakStyle(result);
|
|
295
|
+
result = placeSignalsRuntimeBeforeScripts(result);
|
|
296
|
+
}
|
|
243
297
|
if (isTopLevel)
|
|
244
298
|
try {
|
|
245
299
|
const { getStoreScript } = await import("./store-loader.js"), resolvedStoresDir = options.storesDir, storeCode = await getStoreScript(resolvedStoresDir);
|
|
246
300
|
if (storeCode) {
|
|
247
|
-
const storeTag = `<script data-stx-stores>${storeCode}</script>`,
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
301
|
+
const storeTag = `<script data-stx-stores>${storeCode}</script>`, runtime = findScriptBlockByAttribute(result, "data-stx-runtime");
|
|
302
|
+
if (runtime)
|
|
303
|
+
result = result.slice(0, runtime.end) + `
|
|
304
|
+
` + storeTag + result.slice(runtime.end);
|
|
305
|
+
}
|
|
306
|
+
} catch {}
|
|
307
|
+
if (isTopLevel)
|
|
308
|
+
try {
|
|
309
|
+
const { getComposableScript } = await import("./composable-loader.js"), resolvedComposablesDir = options.composablesDir, composableCode = await getComposableScript(resolvedComposablesDir);
|
|
310
|
+
if (composableCode) {
|
|
311
|
+
const composableTag = `<script data-stx-composables>${composableCode}</script>`, anchor = findScriptBlockByAttribute(result, "data-stx-stores") || findScriptBlockByAttribute(result, "data-stx-runtime");
|
|
312
|
+
if (anchor)
|
|
313
|
+
result = result.slice(0, anchor.end) + `
|
|
314
|
+
` + composableTag + result.slice(anchor.end);
|
|
256
315
|
}
|
|
257
316
|
} catch {}
|
|
258
317
|
if (isTopLevel && context.__stx_inject_css !== !1)
|
|
@@ -370,9 +429,11 @@ ${output.trim()}
|
|
|
370
429
|
});
|
|
371
430
|
output = processStackReplacements(output, stacks);
|
|
372
431
|
if (layoutPath) {
|
|
373
|
-
const orphans = [];
|
|
374
|
-
output = output.replace(
|
|
375
|
-
|
|
432
|
+
const orphans = [], scripts = stashScriptElements(output), styles = maskAtElementPosition(scripts.output, matchStyleElement, (_token, index) => `\x00STX_ORPHAN_STYLE_${index}\x00`);
|
|
433
|
+
output = styles.output.replace(/\x00STX_(SCRIPT|ORPHAN_STYLE)_(\d+)\x00/g, (_match, kind, index) => {
|
|
434
|
+
const token = kind === "SCRIPT" ? scripts.scripts[Number(index)] : styles.tokens[Number(index)];
|
|
435
|
+
if (token)
|
|
436
|
+
orphans.push(token);
|
|
376
437
|
return "";
|
|
377
438
|
});
|
|
378
439
|
if (orphans.length > 0) {
|
|
@@ -670,18 +731,17 @@ ${runtimeScript}`);
|
|
|
670
731
|
` + output;
|
|
671
732
|
}
|
|
672
733
|
{
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
if (
|
|
734
|
+
const { scanScriptTags } = await import("./signal-processing.js"), serverRemoveRanges = [];
|
|
735
|
+
for (const script of scanScriptTags(output)) {
|
|
736
|
+
if (!/\bserver\b/i.test(script.attrs))
|
|
737
|
+
continue;
|
|
738
|
+
const bodyStart = script.start + script.fullMatch.indexOf(">") + 1, closeStart = findScriptBodyEnd(output, bodyStart);
|
|
739
|
+
if (closeStart === -1)
|
|
679
740
|
continue;
|
|
680
|
-
let rangeEnd =
|
|
741
|
+
let rangeEnd = closeStart + 9;
|
|
681
742
|
while (rangeEnd < output.length && /\s/.test(output[rangeEnd]))
|
|
682
743
|
rangeEnd++;
|
|
683
|
-
serverRemoveRanges.push({ start:
|
|
684
|
-
serverScriptRe.lastIndex = rangeEnd;
|
|
744
|
+
serverRemoveRanges.push({ start: script.start, end: rangeEnd });
|
|
685
745
|
}
|
|
686
746
|
for (let ri = serverRemoveRanges.length - 1;ri >= 0; ri--) {
|
|
687
747
|
const { start, end } = serverRemoveRanges[ri];
|