@stacksjs/stx 0.2.151 → 0.2.153
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.js +28 -1
- package/dist/browser-composables.d.ts +24 -9
- package/dist/build-id.d.ts +49 -0
- package/dist/build-id.js +20 -0
- package/dist/cli.js +897 -659
- package/dist/client-script.js +2 -2
- package/dist/color-mode-boot.d.ts +15 -10
- package/dist/color-mode-boot.js +12 -8
- package/dist/component-client-factories.js +3 -2
- package/dist/component-processing.d.ts +16 -0
- package/dist/component-processing.js +4 -1
- package/dist/component-renderer.js +2 -2
- package/dist/composables/index.d.ts +6 -1
- package/dist/composables/use-color-mode.d.ts +19 -4
- package/dist/composables/use-color-mode.js +37 -24
- package/dist/composables/use-device-orientation.d.ts +8 -1
- package/dist/composables/use-event-source.d.ts +14 -1
- package/dist/composables/use-eye-dropper.d.ts +10 -1
- package/dist/composables/use-idle.d.ts +21 -3
- package/dist/composables/use-storage.d.ts +15 -10
- package/dist/composables/use-storage.js +63 -2
- package/dist/composables/use-text-selection.d.ts +27 -3
- package/dist/composables/use-wake-lock.d.ts +2 -2
- package/dist/craft.js +625 -387
- package/dist/dev-server/serve-app.js +9 -5
- package/dist/dev-server/types.d.ts +8 -1
- package/dist/document-shell.d.ts +55 -0
- package/dist/document-shell.js +103 -3
- package/dist/edge-runtime.d.ts +1 -1
- package/dist/head.js +5 -1
- package/dist/includes.js +4 -2
- package/dist/index.d.ts +17 -0
- package/dist/index.js +9 -0
- package/dist/process.js +7 -3
- package/dist/production-builder.js +3 -1
- package/dist/pwa.js +591 -353
- package/dist/render.d.ts +1 -1
- package/dist/render.js +2 -2
- package/dist/signals.js +280 -44
- package/dist/ssg.js +603 -365
- package/dist/ssr.d.ts +17 -1
- package/dist/stx.d.ts +14 -6
- package/dist/types/config-types.d.ts +1 -0
- package/dist/utils.js +1 -8
- package/dist/variable-extractor.d.ts +8 -0
- package/dist/variable-extractor.js +24 -9
- package/dist/visual-testing.d.ts +26 -3
- package/package.json +5 -5
- package/stx.d.ts +14 -6
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
stopHmrServer
|
|
12
12
|
} from "../hot-reload.js";
|
|
13
13
|
import { partialsCache } from "../includes.js";
|
|
14
|
+
import { BUILD_ID_HEADER, getBuildId } from "../build-id.js";
|
|
14
15
|
import { stateDir } from "../state-dir.js";
|
|
15
16
|
import { plugin as stxPlugin } from "../plugin.js";
|
|
16
17
|
import { createRouter, matchRoute, formatRoutes, findErrorPage } from "../router.js";
|
|
@@ -74,14 +75,16 @@ function serveStaticFile(filePath) {
|
|
|
74
75
|
export async function serveApp(appDir = ".", options = {}) {
|
|
75
76
|
const port = options.port || 3000, watch = options.watch !== !1, hotReload = options.hotReload !== !1 && watch;
|
|
76
77
|
let hmrServer = null, actualHmrPort = options.hmrPort || port + 1;
|
|
77
|
-
const absoluteAppDir = path.resolve(appDir), { loadStxConfig } = await import("../index.js"), projectConfig = await loadStxConfig(absoluteAppDir), stxRoot = projectConfig.root && projectConfig.root !== "." ? path.join(absoluteAppDir, projectConfig.root) : absoluteAppDir, pagesDirName = projectConfig.pagesDir || "pages", pagesDir = path.join(stxRoot, pagesDirName);
|
|
78
|
+
const absoluteAppDir = path.resolve(appDir), { loadStxConfig } = await import("../index.js"), projectConfig = await loadStxConfig(absoluteAppDir), stxRoot = projectConfig.root && projectConfig.root !== "." ? path.join(absoluteAppDir, projectConfig.root) : absoluteAppDir, pagesDirName = options.pagesDir || projectConfig.pagesDir || "pages", pagesDir = path.join(stxRoot, pagesDirName);
|
|
78
79
|
if (!fs.existsSync(pagesDir)) {
|
|
79
|
-
console.error(`${colors.red}Error: No '${pagesDirName}' directory found
|
|
80
|
-
console.log(`${colors.dim}
|
|
81
|
-
console.log(`${colors.dim}
|
|
80
|
+
console.error(`${colors.red}Error: No '${pagesDirName}' directory found${colors.reset}`);
|
|
81
|
+
console.log(`${colors.dim}Looked in: ${colors.bright}${pagesDir}${colors.reset}`);
|
|
82
|
+
console.log(`${colors.dim}Run stx dev from the app directory, pass it as an argument (stx dev <dir>),${colors.reset}`);
|
|
83
|
+
console.log(`${colors.dim}point at another directory with --pages <dir>, or set pagesDir in stx.config.ts.${colors.reset}`);
|
|
84
|
+
console.log(`${colors.dim}Starting fresh? Create ${pagesDirName}/index.stx for the homepage.${colors.reset}`);
|
|
82
85
|
return !1;
|
|
83
86
|
}
|
|
84
|
-
const routes = createRouter(stxRoot);
|
|
87
|
+
const routes = createRouter(stxRoot, { pagesDir: pagesDirName });
|
|
85
88
|
if (routes.length === 0) {
|
|
86
89
|
console.error(`${colors.red}Error: No page files found in ${colors.bright}${pagesDir}${colors.reset}`);
|
|
87
90
|
return !1;
|
|
@@ -447,6 +450,7 @@ export async function serveApp(appDir = ".", options = {}) {
|
|
|
447
450
|
"X-STX-Layout": layoutMetadata.layout,
|
|
448
451
|
"X-STX-Layout-Group": layoutMetadata.group,
|
|
449
452
|
...pageTitle && { "X-STX-Title": encodeURIComponent(pageTitle) },
|
|
453
|
+
[BUILD_ID_HEADER]: getBuildId(),
|
|
450
454
|
"Cache-Control": "no-store, no-cache, must-revalidate"
|
|
451
455
|
}
|
|
452
456
|
});
|
|
@@ -37,8 +37,14 @@ export declare const MIME_TYPES: {
|
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
39
|
* Default dev server options
|
|
40
|
+
*
|
|
41
|
+
* `pagesDir` is excluded rather than defaulted: it has no static default. Its
|
|
42
|
+
* absence is meaningful — it's what lets `stx.config.ts` `pagesDir` apply.
|
|
43
|
+
* Baking `'pages'` in here would override a configured `views/` for anyone who
|
|
44
|
+
* spread these defaults (#1781). serveApp resolves it as
|
|
45
|
+
* option → config → `'pages'`.
|
|
40
46
|
*/
|
|
41
|
-
export declare const DEFAULT_DEV_OPTIONS: Required<Omit<DevServerOptions, 'stxOptions' | 'markdown'>>;
|
|
47
|
+
export declare const DEFAULT_DEV_OPTIONS: Required<Omit<DevServerOptions, 'stxOptions' | 'markdown' | 'pagesDir'>>;
|
|
42
48
|
/**
|
|
43
49
|
* Markdown rendering options
|
|
44
50
|
*/
|
|
@@ -59,6 +65,7 @@ export declare interface DevServerOptions {
|
|
|
59
65
|
watch?: boolean
|
|
60
66
|
native?: boolean
|
|
61
67
|
titlebarHidden?: boolean
|
|
68
|
+
pagesDir?: string
|
|
62
69
|
stxOptions?: StxOptions
|
|
63
70
|
markdown?: MarkdownOptions
|
|
64
71
|
cache?: boolean
|
package/dist/document-shell.d.ts
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
import type { AppHeadConfig } from './types/config-types';
|
|
2
2
|
import type { ColorModeBootConfig } from './color-mode-boot';
|
|
3
|
+
/**
|
|
4
|
+
* Build the attribute string for the shell's `<html>` open tag.
|
|
5
|
+
*
|
|
6
|
+
* `htmlAttrs.lang` wins over the `lang` option so `useHead({ htmlAttrs: { lang } })`
|
|
7
|
+
* is not emitted twice (two `lang=` attributes on one element is a parse error
|
|
8
|
+
* that browsers resolve by keeping the first — i.e. silently ignoring the
|
|
9
|
+
* more specific one).
|
|
10
|
+
*
|
|
11
|
+
* @param htmlAttrs - Attributes from config `app.head.htmlAttrs` / `useHead()`
|
|
12
|
+
* @param lang - The `lang` option, used unless `htmlAttrs.lang` overrides it
|
|
13
|
+
* @returns Leading-space-prefixed attribute string, e.g. ` lang="en" class="marketing"`
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildHtmlAttrs(htmlAttrs?: Record<string, string>, lang?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Merge a page's `htmlAttrs` over the config's.
|
|
18
|
+
*
|
|
19
|
+
* Per-key override, except `class`, which unions: a global
|
|
20
|
+
* `app.head.htmlAttrs.class` (`h-full`, a font class, …) shouldn't silently
|
|
21
|
+
* vanish the moment one page adds a class of its own. Same rule as
|
|
22
|
+
* `applyHtmlAttrs`, so "class unions, everything else overrides" holds at every
|
|
23
|
+
* point htmlAttrs are combined.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeHtmlAttrs(base?: Record<string, string>, override?: Record<string, string>): Record<string, string>;
|
|
26
|
+
/**
|
|
27
|
+
* Merge `htmlAttrs` into a document that rendered its OWN `<html>` tag.
|
|
28
|
+
*
|
|
29
|
+
* `generateDocumentShell` only runs for pages without a shell, so a page or
|
|
30
|
+
* layout that writes `<html>` itself — the common case, and what SSG emits —
|
|
31
|
+
* would otherwise drop `useHead({ htmlAttrs })` entirely. Whether the attribute
|
|
32
|
+
* lands should not depend on who wrote the document element.
|
|
33
|
+
*
|
|
34
|
+
* `class` unions with what the template already put there (the template's own
|
|
35
|
+
* classes are never dropped); every other attribute is overwritten. Only the
|
|
36
|
+
* classes stx contributed are recorded in the marker, so an SPA navigation
|
|
37
|
+
* removes those and leaves the hand-written ones in place.
|
|
38
|
+
*/
|
|
39
|
+
export declare function applyHtmlAttrs(html: string, htmlAttrs?: Record<string, string>): string;
|
|
3
40
|
/**
|
|
4
41
|
* Insert the inline x-cloak <style> before </head> if a head exists and the
|
|
5
42
|
* style isn't already present. Idempotent (guards on the data-stx-cloak
|
|
@@ -106,3 +143,21 @@ export declare function ensureDocumentShell(html: string, headConfig?: AppHeadCo
|
|
|
106
143
|
* emission.
|
|
107
144
|
*/
|
|
108
145
|
export declare const CLOAK_STYLE: '<style data-stx-cloak>[x-cloak]{display:none !important}</style>';
|
|
146
|
+
/**
|
|
147
|
+
* Markers recording which `<html>` attributes stx itself wrote from `htmlAttrs`
|
|
148
|
+
* (stacksjs/stx#1798).
|
|
149
|
+
*
|
|
150
|
+
* The router needs them to reconcile the root element across an SPA layout
|
|
151
|
+
* change. It cannot just diff `<html>` against the incoming document: by then
|
|
152
|
+
* the color-mode boot script has added its own `dark` class / `data-theme`, and
|
|
153
|
+
* `animation.ts` has added `data-reduced-motion`, none of which exist in the
|
|
154
|
+
* server response — a blind diff would strip them on every navigation and
|
|
155
|
+
* flash the wrong theme. These say exactly what stx put there, so the router
|
|
156
|
+
* can remove precisely that much and leave runtime state alone.
|
|
157
|
+
*
|
|
158
|
+
* `class` is tracked separately because it is the one attribute that gets
|
|
159
|
+
* *merged* into at runtime, so it has to be reconciled token-by-token rather
|
|
160
|
+
* than replaced wholesale.
|
|
161
|
+
*/
|
|
162
|
+
export declare const HTML_ATTRS_MARKER: 'data-stx-html-attrs';
|
|
163
|
+
export declare const HTML_CLASS_MARKER: 'data-stx-html-class';
|
package/dist/document-shell.js
CHANGED
|
@@ -1,5 +1,104 @@
|
|
|
1
1
|
import { generateColorModeBootScript } from "./color-mode-boot.js";
|
|
2
|
-
export const CLOAK_STYLE = "<style data-stx-cloak>[x-cloak]{display:none !important}</style>";
|
|
2
|
+
export const CLOAK_STYLE = "<style data-stx-cloak>[x-cloak]{display:none !important}</style>", HTML_ATTRS_MARKER = "data-stx-html-attrs", HTML_CLASS_MARKER = "data-stx-html-class";
|
|
3
|
+
const SAFE_ATTR_NAME = /^[A-Za-z_:][\w:.-]*$/;
|
|
4
|
+
function escapeAttr(value) {
|
|
5
|
+
return String(value).replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
6
|
+
}
|
|
7
|
+
export function buildHtmlAttrs(htmlAttrs = {}, lang = "en") {
|
|
8
|
+
const rest = [], managed = [];
|
|
9
|
+
let className = "", resolvedLang = lang;
|
|
10
|
+
for (const [rawName, value] of Object.entries(htmlAttrs || {})) {
|
|
11
|
+
if (value == null)
|
|
12
|
+
continue;
|
|
13
|
+
const name = rawName.trim();
|
|
14
|
+
if (!SAFE_ATTR_NAME.test(name))
|
|
15
|
+
continue;
|
|
16
|
+
const lower = name.toLowerCase();
|
|
17
|
+
if (lower === "lang") {
|
|
18
|
+
resolvedLang = String(value);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (lower === "class") {
|
|
22
|
+
className = String(value).trim();
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
rest.push(` ${name}="${escapeAttr(String(value))}"`);
|
|
26
|
+
managed.push(name);
|
|
27
|
+
}
|
|
28
|
+
let out = ` lang="${escapeAttr(resolvedLang)}"`;
|
|
29
|
+
if (className)
|
|
30
|
+
out += ` class="${escapeAttr(className)}"`;
|
|
31
|
+
out += rest.join("");
|
|
32
|
+
if (className)
|
|
33
|
+
out += ` ${HTML_CLASS_MARKER}="${escapeAttr(className)}"`;
|
|
34
|
+
if (managed.length)
|
|
35
|
+
out += ` ${HTML_ATTRS_MARKER}="${escapeAttr(managed.join(" "))}"`;
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
export function mergeHtmlAttrs(base = {}, override = {}) {
|
|
39
|
+
const merged = { ...base, ...override };
|
|
40
|
+
if (base.class && override.class) {
|
|
41
|
+
const tokens = base.class.split(/\s+/).filter(Boolean);
|
|
42
|
+
for (const token of override.class.split(/\s+/).filter(Boolean))
|
|
43
|
+
if (!tokens.includes(token))
|
|
44
|
+
tokens.push(token);
|
|
45
|
+
merged.class = tokens.join(" ");
|
|
46
|
+
}
|
|
47
|
+
return merged;
|
|
48
|
+
}
|
|
49
|
+
export function applyHtmlAttrs(html, htmlAttrs = {}) {
|
|
50
|
+
if (!htmlAttrs || Object.keys(htmlAttrs).length === 0)
|
|
51
|
+
return html;
|
|
52
|
+
const openTag = html.match(/<html\b([^>]*)>/i);
|
|
53
|
+
if (!openTag)
|
|
54
|
+
return html;
|
|
55
|
+
const existing = {}, order = [], attrRe = /([A-Za-z_:][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
|
|
56
|
+
let m;
|
|
57
|
+
while ((m = attrRe.exec(openTag[1])) !== null) {
|
|
58
|
+
const name = m[1];
|
|
59
|
+
if (!(name in existing))
|
|
60
|
+
order.push(name);
|
|
61
|
+
existing[name] = m[2] ?? m[3] ?? m[4] ?? "";
|
|
62
|
+
}
|
|
63
|
+
const addedClasses = [], managed = [];
|
|
64
|
+
for (const [rawName, value] of Object.entries(htmlAttrs)) {
|
|
65
|
+
if (value == null)
|
|
66
|
+
continue;
|
|
67
|
+
const name = rawName.trim();
|
|
68
|
+
if (!SAFE_ATTR_NAME.test(name))
|
|
69
|
+
continue;
|
|
70
|
+
const lower = name.toLowerCase();
|
|
71
|
+
if (lower === "class") {
|
|
72
|
+
const present = (existing.class || "").split(/\s+/).filter(Boolean);
|
|
73
|
+
for (const token of String(value).split(/\s+/).filter(Boolean))
|
|
74
|
+
if (!present.includes(token)) {
|
|
75
|
+
present.push(token);
|
|
76
|
+
addedClasses.push(token);
|
|
77
|
+
}
|
|
78
|
+
if (!("class" in existing))
|
|
79
|
+
order.push("class");
|
|
80
|
+
existing.class = present.join(" ");
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (!(name in existing))
|
|
84
|
+
order.push(name);
|
|
85
|
+
existing[name] = String(value);
|
|
86
|
+
if (lower !== "lang")
|
|
87
|
+
managed.push(name);
|
|
88
|
+
}
|
|
89
|
+
if (addedClasses.length) {
|
|
90
|
+
if (!(HTML_CLASS_MARKER in existing))
|
|
91
|
+
order.push(HTML_CLASS_MARKER);
|
|
92
|
+
existing[HTML_CLASS_MARKER] = addedClasses.join(" ");
|
|
93
|
+
}
|
|
94
|
+
if (managed.length) {
|
|
95
|
+
if (!(HTML_ATTRS_MARKER in existing))
|
|
96
|
+
order.push(HTML_ATTRS_MARKER);
|
|
97
|
+
existing[HTML_ATTRS_MARKER] = managed.join(" ");
|
|
98
|
+
}
|
|
99
|
+
const rebuilt = order.map((name) => ` ${name}="${escapeAttr(existing[name])}"`).join("");
|
|
100
|
+
return html.replace(openTag[0], () => `<html${rebuilt}>`);
|
|
101
|
+
}
|
|
3
102
|
export function injectCloakStyle(html) {
|
|
4
103
|
if (html.includes("data-stx-cloak"))
|
|
5
104
|
return html;
|
|
@@ -86,6 +185,7 @@ export function generateDocumentShell(content, headConfig = {}, options = {}) {
|
|
|
86
185
|
link = [],
|
|
87
186
|
script: headScriptTags = [],
|
|
88
187
|
headRaw = "",
|
|
188
|
+
htmlAttrs = {},
|
|
89
189
|
bodyClass = "",
|
|
90
190
|
bodyAttrs = {}
|
|
91
191
|
} = headConfig, pageTitle = options.title || configTitle, metaTags = [...[
|
|
@@ -104,7 +204,7 @@ export function generateDocumentShell(content, headConfig = {}, options = {}) {
|
|
|
104
204
|
return ` <script>${s.content}</script>`;
|
|
105
205
|
return "";
|
|
106
206
|
}).filter(Boolean).join(`
|
|
107
|
-
`), bodyAttrStr = Object.entries(bodyAttrs).map(([k, v]) => ` ${k}="${v}"`).join(""), bodyClassStr = bodyClass ? ` class="${bodyClass}"` : "", { body: pageBody, hoisted: pageHeadTags } = hoistLeadingHeadTags(content), headParts = [
|
|
207
|
+
`), htmlAttrStr = buildHtmlAttrs(htmlAttrs, lang), bodyAttrStr = Object.entries(bodyAttrs).map(([k, v]) => ` ${k}="${v}"`).join(""), bodyClassStr = bodyClass ? ` class="${bodyClass}"` : "", { body: pageBody, hoisted: pageHeadTags } = hoistLeadingHeadTags(content), headParts = [
|
|
108
208
|
metaTags,
|
|
109
209
|
options.colorMode ? ` ${generateColorModeBootScript(options.colorMode)}` : "",
|
|
110
210
|
` <title>${pageTitle}</title>`,
|
|
@@ -122,7 +222,7 @@ export function generateDocumentShell(content, headConfig = {}, options = {}) {
|
|
|
122
222
|
options.bodyStack || ""
|
|
123
223
|
].filter(Boolean);
|
|
124
224
|
return `<!DOCTYPE html>
|
|
125
|
-
<html
|
|
225
|
+
<html${htmlAttrStr}>
|
|
126
226
|
<head>
|
|
127
227
|
${headParts.join(`
|
|
128
228
|
`)}
|
package/dist/edge-runtime.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare function createEdgeCache(): EdgeCache | undefined;
|
|
|
25
25
|
/**
|
|
26
26
|
* Create an edge handler.
|
|
27
27
|
*/
|
|
28
|
-
export declare function createEdgeHandler(config: EdgeHandlerConfig):
|
|
28
|
+
export declare function createEdgeHandler(config: EdgeHandlerConfig): (_request: Request, _platformContext?: Record<string, unknown>) => Promise<Response>;
|
|
29
29
|
/**
|
|
30
30
|
* Create a streaming response for edge platforms.
|
|
31
31
|
*/
|
package/dist/head.js
CHANGED
|
@@ -194,7 +194,11 @@ export function applyHead(config = currentHead) {
|
|
|
194
194
|
}
|
|
195
195
|
if (config.htmlAttrs)
|
|
196
196
|
for (const [key, value] of Object.entries(config.htmlAttrs))
|
|
197
|
-
|
|
197
|
+
if (key === "class")
|
|
198
|
+
for (const cls of String(value).split(/\s+/).filter(Boolean))
|
|
199
|
+
document.documentElement.classList.add(cls);
|
|
200
|
+
else
|
|
201
|
+
document.documentElement.setAttribute(key, value);
|
|
198
202
|
if (config.bodyAttrs)
|
|
199
203
|
for (const [key, value] of Object.entries(config.bodyAttrs))
|
|
200
204
|
document.body.setAttribute(key, value);
|
package/dist/includes.js
CHANGED
|
@@ -531,6 +531,8 @@ export async function processIncludes(template, context, filePath, options, depe
|
|
|
531
531
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
532
532
|
return createDetailedErrorMessage("Include", `Error loading include file ${includePath}: ${errorMessage}`, filePath, templateStr, offsetPos);
|
|
533
533
|
}
|
|
534
|
+
if (partialContent)
|
|
535
|
+
partialContent = partialContent.replace(/\{\{--[\s\S]*?--\}\}/g, "");
|
|
534
536
|
if (partialContent && partialContent.includes("@stream")) {
|
|
535
537
|
const { processStreamDirectives } = await import("./streaming.js");
|
|
536
538
|
partialContent = processStreamDirectives(partialContent, context);
|
|
@@ -595,7 +597,7 @@ export async function processIncludes(template, context, filePath, options, depe
|
|
|
595
597
|
if (isSignalScript && !isServerScript) {
|
|
596
598
|
signalScopeId = `stx_scope_${path.basename(includeFilePath, ".stx").replace(/[^a-zA-Z0-9]/g, "_")}_${++scopeIdCounter}`;
|
|
597
599
|
const resolvedContent = transformStoreImports(scriptContent), transformedScript = transformSignalScript(resolvedContent, signalScopeId);
|
|
598
|
-
preservedScript += `${vendorStyleTags}<script data-stx-scoped>${transformedScript}</script>
|
|
600
|
+
preservedScript += `${vendorStyleTags}<script data-stx-scoped data-stx-run="always">${transformedScript}</script>
|
|
599
601
|
`;
|
|
600
602
|
continue;
|
|
601
603
|
}
|
|
@@ -611,7 +613,7 @@ export async function processIncludes(template, context, filePath, options, depe
|
|
|
611
613
|
const extraAttrs = scriptAttrs.replace(/\bclient\b/i, "").trim();
|
|
612
614
|
signalScopeId = `stx_scope_${path.basename(includeFilePath, ".stx").replace(/[^a-zA-Z0-9]/g, "_")}_${++scopeIdCounter}`;
|
|
613
615
|
const resolvedContent = transformStoreImports(scriptContent), tail = buildScopeRegistrationTail(resolvedContent, signalScopeId), wrapped = wrapClientScript(resolvedContent, tail);
|
|
614
|
-
preservedScript += `${vendorStyleTags}<script data-stx-scoped${extraAttrs ? ` ${extraAttrs}` : ""}>${wrapped}</script>
|
|
616
|
+
preservedScript += `${vendorStyleTags}<script data-stx-scoped data-stx-run="always"${extraAttrs ? ` ${extraAttrs}` : ""}>${wrapped}</script>
|
|
615
617
|
`;
|
|
616
618
|
}
|
|
617
619
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,22 @@ export { type HydrationOptions } from './hydration';
|
|
|
28
28
|
// shapes with different field sets; those are kept private to their modules
|
|
29
29
|
// to avoid silently shadowing the user-facing one. Surfaced by stacksjs/stx#1707.
|
|
30
30
|
export { type CookieOptions } from './composables/use-cookie';
|
|
31
|
+
// Storage composables were reachable ONLY via the './composables' subpath, so
|
|
32
|
+
// `import { useLocalStorage } from '@stacksjs/stx'` failed with TS2614 while
|
|
33
|
+
// `state`/`derived` imported fine — and a `functions/` composable (which the
|
|
34
|
+
// docs recommend for anything with persistence) had to reach for the subpath,
|
|
35
|
+
// where the same identifier used to return a different shape. See #1797.
|
|
36
|
+
export {
|
|
37
|
+
clearStorage,
|
|
38
|
+
getStorageKeys,
|
|
39
|
+
getStorageSize,
|
|
40
|
+
type StorageRef,
|
|
41
|
+
type StorageType,
|
|
42
|
+
useLocalStorage,
|
|
43
|
+
useSessionStorage,
|
|
44
|
+
useStorage,
|
|
45
|
+
type UseStorageOptions,
|
|
46
|
+
} from './composables/use-storage';
|
|
31
47
|
export { formatSize, getTotalSize } from './deploy/index';
|
|
32
48
|
export { type ImageRenderResult, type ImageVariant, type ProcessedImage, clearImageCache, getFallbackVariant, getMimeType, groupVariantsByFormat, optimizeImage, processImage } from './image-optimization/index';
|
|
33
49
|
export { type AnalysisResult } from './analyzer';
|
|
@@ -62,6 +78,7 @@ export * from './async-components';
|
|
|
62
78
|
export * from './auth';
|
|
63
79
|
export * from './build';
|
|
64
80
|
export * from './build-assets';
|
|
81
|
+
export * from './build-id';
|
|
65
82
|
export * from './build-mode-detector';
|
|
66
83
|
export * from './build-optimizer';
|
|
67
84
|
export * from './broadcasting';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export {
|
|
2
|
+
clearStorage,
|
|
3
|
+
getStorageKeys,
|
|
4
|
+
getStorageSize,
|
|
5
|
+
useLocalStorage,
|
|
6
|
+
useSessionStorage,
|
|
7
|
+
useStorage
|
|
8
|
+
} from "./composables/use-storage.js";
|
|
1
9
|
export { formatSize, getTotalSize } from "./deploy/index.js";
|
|
2
10
|
export { clearImageCache, getFallbackVariant, getMimeType, groupVariantsByFormat, optimizeImage, processImage } from "./image-optimization/index.js";
|
|
3
11
|
export { fileExists } from "./utils.js";
|
|
@@ -21,6 +29,7 @@ export * from "./async-components.js";
|
|
|
21
29
|
export * from "./auth.js";
|
|
22
30
|
export * from "./build.js";
|
|
23
31
|
export * from "./build-assets.js";
|
|
32
|
+
export * from "./build-id.js";
|
|
24
33
|
export * from "./build-mode-detector.js";
|
|
25
34
|
export * from "./build-optimizer.js";
|
|
26
35
|
export * from "./broadcasting.js";
|
package/dist/process.js
CHANGED
|
@@ -37,7 +37,8 @@ import { processVueTemplate } from "./vue-template.js";
|
|
|
37
37
|
import { processDynamicComponents } from "./dynamic-components.js";
|
|
38
38
|
import { dedupeScopedStyles, processScopedStyles } from "./style-scoping.js";
|
|
39
39
|
import { injectColorModeBootScript } from "./color-mode-boot.js";
|
|
40
|
-
import {
|
|
40
|
+
import { injectBuildId } from "./build-id.js";
|
|
41
|
+
import { applyHtmlAttrs, ensureDocumentShell, hasDocumentShell, injectCloakStyle, injectConfigHeadTags, mergeHtmlAttrs } from "./document-shell.js";
|
|
41
42
|
import { hasSignalsSyntax, convertSignalDirectivesToAttributes, convertSignalLoopsToAttributes, preEvalLiteralReactiveIfs, processSignals } from "./signal-processing.js";
|
|
42
43
|
import { processComponents } from "./component-renderer.js";
|
|
43
44
|
import { processInlineAssets } from "./inline-assets.js";
|
|
@@ -276,14 +277,16 @@ export async function processDirectives(template, context, filePath, options, de
|
|
|
276
277
|
script: [...baseHeadConfig.script || [], ...runtimeHead.script || []],
|
|
277
278
|
headRaw: [baseHeadConfig.headRaw, pageHeadRaw].filter(Boolean).join(`
|
|
278
279
|
`),
|
|
279
|
-
...runtimeHead.htmlAttrs && { htmlAttrs:
|
|
280
|
+
...runtimeHead.htmlAttrs && { htmlAttrs: mergeHtmlAttrs(baseHeadConfig.htmlAttrs, runtimeHead.htmlAttrs) },
|
|
280
281
|
...runtimeHead.bodyAttrs && { bodyAttrs: { ...baseHeadConfig.bodyAttrs || {}, ...runtimeHead.bodyAttrs } }
|
|
281
282
|
}, alreadyShelled = hasDocumentShell(result);
|
|
282
283
|
result = ensureDocumentShell(result, headConfig, {
|
|
283
284
|
...options.app?.colorMode && { colorMode: options.app.colorMode }
|
|
284
285
|
});
|
|
285
|
-
if (alreadyShelled)
|
|
286
|
+
if (alreadyShelled) {
|
|
286
287
|
result = injectConfigHeadTags(result, baseHeadConfig);
|
|
288
|
+
result = applyHtmlAttrs(result, headConfig.htmlAttrs);
|
|
289
|
+
}
|
|
287
290
|
const layoutComment = result.match(/<!-- stx-layout: ([^ ]+) -->/);
|
|
288
291
|
if (layoutComment && result.includes("<head")) {
|
|
289
292
|
const layoutName = layoutComment[1], layoutGroup = deriveLayoutGroup(layoutName);
|
|
@@ -303,6 +306,7 @@ export async function processDirectives(template, context, filePath, options, de
|
|
|
303
306
|
const colorModeConfig = options.app?.colorMode;
|
|
304
307
|
if (colorModeConfig)
|
|
305
308
|
result = injectColorModeBootScript(result, colorModeConfig);
|
|
309
|
+
result = injectBuildId(result);
|
|
306
310
|
result = placeSignalsRuntimeBeforeScripts(result);
|
|
307
311
|
}
|
|
308
312
|
if (isTopLevel)
|
|
@@ -5,7 +5,7 @@ import { buildRuntimeAsset, buildRouterAsset } from "./build-assets.js";
|
|
|
5
5
|
import { compileTemplate } from "./template-compiler.js";
|
|
6
6
|
import { generateManifest, writeManifest } from "./manifest.js";
|
|
7
7
|
import { injectColorModeBootScript } from "./color-mode-boot.js";
|
|
8
|
-
import { ensureDocumentShell } from "./document-shell.js";
|
|
8
|
+
import { applyHtmlAttrs, ensureDocumentShell } from "./document-shell.js";
|
|
9
9
|
import { extractContainerContent } from "./app-shell.js";
|
|
10
10
|
import { injectCrosswindCSS } from "./dev-server/crosswind.js";
|
|
11
11
|
import { loadStxConfig } from "./config.js";
|
|
@@ -88,6 +88,8 @@ export async function buildForProduction(options = {}) {
|
|
|
88
88
|
});
|
|
89
89
|
if (colorModeConfig)
|
|
90
90
|
compiled.html = injectColorModeBootScript(compiled.html, colorModeConfig);
|
|
91
|
+
if (headConfigDefault.htmlAttrs)
|
|
92
|
+
compiled.html = applyHtmlAttrs(compiled.html, headConfigDefault.htmlAttrs);
|
|
91
93
|
if (!/<body[^>]*\bdata-stx=/.test(compiled.html)) {
|
|
92
94
|
const dataStxMatch = compiled.html.match(/<(?!body\b)[a-zA-Z][a-zA-Z0-9-]*\b[^>]*?\s(data-stx="[^"]+")[^>]*>/);
|
|
93
95
|
if (dataStxMatch && dataStxMatch[1]) {
|