@stacksjs/stx 0.2.153 → 0.2.155
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/animation.js +6 -6
- package/dist/app-shell.js +1 -27
- package/dist/build-message.d.ts +10 -0
- package/dist/build-message.js +26 -0
- package/dist/builtins/attrs.js +6 -1
- package/dist/cli.js +1481 -1231
- package/dist/client-script-bundler.js +2 -4
- package/dist/client-script.d.ts +29 -0
- package/dist/client-script.js +140 -95
- package/dist/color-mode-boot.d.ts +31 -0
- package/dist/color-mode-boot.js +41 -5
- package/dist/component-renderer.js +10 -21
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/use-clipboard.d.ts +1 -5
- package/dist/composables/use-clipboard.js +57 -46
- package/dist/composables/use-color-mode.js +13 -5
- package/dist/composables/use-router.d.ts +28 -11
- package/dist/composables/use-router.js +63 -9
- package/dist/config.js +22 -7
- package/dist/craft.js +1029 -789
- package/dist/dev-server/crosswind.js +8 -6
- package/dist/dev-server/serve-app.js +4 -2
- package/dist/dev-server/serve-markdown.js +4 -1
- package/dist/dev-server/serve-multi.js +32 -20
- package/dist/document-shell.d.ts +65 -0
- package/dist/document-shell.js +59 -12
- package/dist/dynamic-components.js +123 -4
- package/dist/events.d.ts +26 -49
- package/dist/events.js +23 -9
- package/dist/expressions.d.ts +1 -1
- package/dist/expressions.js +3 -3
- package/dist/framework-composables.d.ts +41 -0
- package/dist/framework-composables.js +115 -0
- package/dist/hot-reload.js +8 -2
- package/dist/includes.js +2 -2
- package/dist/isolated-build.d.ts +12 -0
- package/dist/isolated-build.js +18 -0
- package/dist/plugin-system.d.ts +1 -1
- package/dist/process.js +40 -10
- package/dist/production-server.js +4 -1
- package/dist/prop-sentinels.d.ts +31 -0
- package/dist/prop-sentinels.js +7 -0
- package/dist/pwa.js +966 -726
- package/dist/reactive.js +1 -1
- package/dist/reactivity.d.ts +0 -15
- package/dist/reactivity.js +15 -6
- package/dist/runtime-globals.d.ts +40 -2
- package/dist/runtime-globals.js +90 -0
- package/dist/runtime-injection.d.ts +51 -0
- package/dist/runtime-injection.js +13 -0
- package/dist/script-emit.d.ts +20 -0
- package/dist/script-emit.js +11 -0
- package/dist/script-validation.js +12 -7
- package/dist/signal-processing.js +25 -40
- package/dist/signals-api.d.ts +30 -0
- package/dist/signals-api.js +18 -0
- package/dist/signals.js +258 -47
- package/dist/site-builder/build.js +21 -7
- package/dist/site-builder/seo.js +19 -10
- package/dist/site-builder/theme.js +5 -4
- package/dist/site-builder/types.d.ts +1 -0
- package/dist/ssg.js +986 -746
- package/dist/state-management.js +4 -1
- package/dist/store-loader.d.ts +0 -11
- package/dist/store-loader.js +27 -3
- package/dist/strict-lint.d.ts +9 -6
- package/dist/strict-lint.js +2 -21
- package/dist/stx.d.ts +111 -16
- package/dist/unresolved-identifiers.d.ts +50 -0
- package/dist/unresolved-identifiers.js +146 -0
- package/dist/utils.js +3 -3
- package/dist/variable-extractor.js +17 -1
- package/dist/x-element.js +1 -1
- package/package.json +5 -5
- package/stx.d.ts +111 -16
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { getPublicEnvDefine } from "./public-env.js";
|
|
4
4
|
import { stateDir } from "./state-dir.js";
|
|
5
|
+
import { formatBuildFailure } from "./build-message.js";
|
|
5
6
|
const BUNDLE_CACHE_VERSION = 5, BUNDLE_CACHE_METADATA_VERSION = 1;
|
|
6
7
|
export function shouldLogBundlerDiagnostics(environment = process.env) {
|
|
7
8
|
return environment.STX_DEBUG === "true" || environment.STX_DEBUG === "1";
|
|
@@ -267,10 +268,7 @@ ${publicAssignments}`.trim();
|
|
|
267
268
|
}));
|
|
268
269
|
return bundled;
|
|
269
270
|
} catch (error) {
|
|
270
|
-
|
|
271
|
-
${bunErrors.map((e) => `- ${e?.message ?? e}`).join(`
|
|
272
|
-
`)}` : "";
|
|
273
|
-
console.warn(`[stx:bundler] error: ${msg}${detail}`);
|
|
271
|
+
console.warn(`[stx:bundler] error: ${formatBuildFailure(error, filePath || void 0)}`);
|
|
274
272
|
return code;
|
|
275
273
|
} finally {
|
|
276
274
|
try {
|
package/dist/client-script.d.ts
CHANGED
|
@@ -46,6 +46,13 @@ export declare function injectBrowserCoreAutoImports(code: string): BrowserCoreA
|
|
|
46
46
|
/**
|
|
47
47
|
* Blank out comments and string literals, preserving length and line structure
|
|
48
48
|
* so offsets still line up, so that identifier detection only ever sees code.
|
|
49
|
+
*
|
|
50
|
+
* Template-literal INTERPOLATIONS are kept, because `${...}` is code, not text.
|
|
51
|
+
* Blanking them hid every identifier that appears only inside one — a helper
|
|
52
|
+
* called as `${formatDate(d)}` looked unused to the auto-import scanner, and a
|
|
53
|
+
* server value read as `` `/api/users/${userId}` `` looked unreferenced to the
|
|
54
|
+
* data bridge, which would have withheld it and left the client throwing on an
|
|
55
|
+
* undefined name.
|
|
49
56
|
*/
|
|
50
57
|
export declare function stripCommentsAndLiterals(code: string): string;
|
|
51
58
|
/**
|
|
@@ -91,6 +98,28 @@ export declare function generateServerDataBridge(code: string, serverData?: Reco
|
|
|
91
98
|
* @returns A complete `<script>...</script>` tag ready for browser injection
|
|
92
99
|
*/
|
|
93
100
|
export declare function processClientScript(scriptContent: string, options?: ClientScriptOptions): Promise<string>;
|
|
101
|
+
/**
|
|
102
|
+
* Exports from 'stx' auto-imported into classic `<script>` / `<script client>`
|
|
103
|
+
* blocks.
|
|
104
|
+
*
|
|
105
|
+
* Derived from {@link STX_RUNTIME_GLOBALS} rather than hand-maintained (#1804).
|
|
106
|
+
* This was a separate 82-name literal, and it had drifted: 16 of those names
|
|
107
|
+
* were not on `window.stx` at all. Because the generated prologue destructures
|
|
108
|
+
* (`var { … } = window.stx`), a missing name is not an error — it binds
|
|
109
|
+
* `undefined`, and the author sees "undefined is not a function" at the call
|
|
110
|
+
* site with nothing naming the identifier or saying why. `provide` worked and
|
|
111
|
+
* `inject` did not; `onMounted` worked and `onUpdated` did not.
|
|
112
|
+
*
|
|
113
|
+
* Five of the 16 (`isDerived`, `inject`, `useSlots`, `watchMultiple`,
|
|
114
|
+
* `onBeforeMount`) are now implemented in the runtime and live in the shared
|
|
115
|
+
* list. The remaining 11 are server- or compile-time only — `h` and `Fragment`
|
|
116
|
+
* belong to the JSX runtime, `useMeta`/`getCurrentInstance`/`useAttrs`/
|
|
117
|
+
* `onErrorCaptured`/`onBeforeUpdate`/`onUpdated`/`createStore`/`createSelector`
|
|
118
|
+
* are module exports with no client counterpart, and `action` has no
|
|
119
|
+
* implementation anywhere — so they are no longer offered. A `ReferenceError`
|
|
120
|
+
* naming the identifier beats a silent `undefined`.
|
|
121
|
+
*/
|
|
122
|
+
export declare const STX_AUTO_IMPORTS: readonly string[];
|
|
94
123
|
export declare interface VendorCssImport {
|
|
95
124
|
source: string
|
|
96
125
|
resolvedPath: string
|
package/dist/client-script.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { asInvocableStatement } from "./events.js";
|
|
3
4
|
import { transformStoreImports } from "./store-imports.js";
|
|
4
5
|
import { shouldTranspileTypeScript, transpileTypeScript } from "./utils.js";
|
|
5
6
|
import { importOnce } from "./lazy-module.js";
|
|
7
|
+
import { STX_RUNTIME_GLOBALS, usesReactiveRuntime } from "./runtime-globals.js";
|
|
8
|
+
import { escapeScriptBody } from "./script-emit.js";
|
|
6
9
|
const CSS_SIDE_EFFECT_IMPORT_REGEX = /^[ \t]*import\s+['"]([^'"]+?\.css)(\?[^'"]*)?['"]\s*;?\s*$/gm;
|
|
7
10
|
function splitBareSpecifier(spec) {
|
|
8
11
|
if (spec.startsWith(".") || spec.startsWith("/"))
|
|
@@ -141,90 +144,8 @@ ${s.contents}
|
|
|
141
144
|
`)}
|
|
142
145
|
`;
|
|
143
146
|
}
|
|
144
|
-
const STX_AUTO_IMPORTS =
|
|
145
|
-
|
|
146
|
-
"derived",
|
|
147
|
-
"effect",
|
|
148
|
-
"batch",
|
|
149
|
-
"untrack",
|
|
150
|
-
"peek",
|
|
151
|
-
"isSignal",
|
|
152
|
-
"isDerived",
|
|
153
|
-
"onMount",
|
|
154
|
-
"onDestroy",
|
|
155
|
-
"useRef",
|
|
156
|
-
"navigate",
|
|
157
|
-
"goBack",
|
|
158
|
-
"goForward",
|
|
159
|
-
"useRoute",
|
|
160
|
-
"setRouteParams",
|
|
161
|
-
"useSearchParams",
|
|
162
|
-
"useFetch",
|
|
163
|
-
"useQuery",
|
|
164
|
-
"useMutation",
|
|
165
|
-
"useOptimistic",
|
|
166
|
-
"useId",
|
|
167
|
-
"useReactiveProp",
|
|
168
|
-
"useLocalStorage",
|
|
169
|
-
"useSessionStorage",
|
|
170
|
-
"useCookie",
|
|
171
|
-
"useWebSocket",
|
|
172
|
-
"useHead",
|
|
173
|
-
"useSeoMeta",
|
|
174
|
-
"definePageMeta",
|
|
175
|
-
"useEventListener",
|
|
176
|
-
"useMeta",
|
|
177
|
-
"useDebounce",
|
|
178
|
-
"useDebouncedValue",
|
|
179
|
-
"useThrottle",
|
|
180
|
-
"useInterval",
|
|
181
|
-
"useTimeout",
|
|
182
|
-
"useToggle",
|
|
183
|
-
"useCounter",
|
|
184
|
-
"useClickOutside",
|
|
185
|
-
"useFocus",
|
|
186
|
-
"useAsync",
|
|
187
|
-
"useColorMode",
|
|
188
|
-
"useDark",
|
|
189
|
-
"useMediaQuery",
|
|
190
|
-
"useScrollLock",
|
|
191
|
-
"usePreferredDark",
|
|
192
|
-
"usePreferredLight",
|
|
193
|
-
"usePreferredReducedMotion",
|
|
194
|
-
"usePreferredContrast",
|
|
195
|
-
"ref",
|
|
196
|
-
"reactive",
|
|
197
|
-
"computed",
|
|
198
|
-
"watch",
|
|
199
|
-
"watchEffect",
|
|
200
|
-
"watchMultiple",
|
|
201
|
-
"onBeforeMount",
|
|
202
|
-
"onMounted",
|
|
203
|
-
"onBeforeUpdate",
|
|
204
|
-
"onUpdated",
|
|
205
|
-
"onBeforeUnmount",
|
|
206
|
-
"onUnmounted",
|
|
207
|
-
"defineProps",
|
|
208
|
-
"withDefaults",
|
|
209
|
-
"defineEmits",
|
|
210
|
-
"defineExpose",
|
|
211
|
-
"defineSlots",
|
|
212
|
-
"provide",
|
|
213
|
-
"inject",
|
|
214
|
-
"nextTick",
|
|
215
|
-
"getCurrentInstance",
|
|
216
|
-
"onErrorCaptured",
|
|
217
|
-
"useSlots",
|
|
218
|
-
"useAttrs",
|
|
219
|
-
"createStore",
|
|
220
|
-
"defineStore",
|
|
221
|
-
"registerStoresClient",
|
|
222
|
-
"useStore",
|
|
223
|
-
"action",
|
|
224
|
-
"createSelector",
|
|
225
|
-
"h",
|
|
226
|
-
"Fragment"
|
|
227
|
-
], BROWSER_CORE_IMPORTS = [
|
|
147
|
+
export const STX_AUTO_IMPORTS = STX_RUNTIME_GLOBALS;
|
|
148
|
+
const BROWSER_CORE_IMPORTS = [
|
|
228
149
|
"browserQuery",
|
|
229
150
|
"BrowserQueryBuilder",
|
|
230
151
|
"BrowserQueryError",
|
|
@@ -342,6 +263,84 @@ const JS_BUILTINS = new Set([
|
|
|
342
263
|
"Modal",
|
|
343
264
|
"Intl"
|
|
344
265
|
]);
|
|
266
|
+
function skipQuoted(code, start) {
|
|
267
|
+
const quote = code[start];
|
|
268
|
+
let i = start + 1;
|
|
269
|
+
while (i < code.length) {
|
|
270
|
+
if (code[i] === "\\") {
|
|
271
|
+
i += 2;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (code[i] === quote)
|
|
275
|
+
return i + 1;
|
|
276
|
+
i++;
|
|
277
|
+
}
|
|
278
|
+
return code.length;
|
|
279
|
+
}
|
|
280
|
+
function skipTemplate(code, start) {
|
|
281
|
+
let i = start + 1;
|
|
282
|
+
while (i < code.length) {
|
|
283
|
+
if (code[i] === "\\") {
|
|
284
|
+
i += 2;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (code[i] === "`")
|
|
288
|
+
return i + 1;
|
|
289
|
+
if (code[i] === "$" && code[i + 1] === "{") {
|
|
290
|
+
i = findInterpolationEnd(code, i + 2);
|
|
291
|
+
if (i < code.length)
|
|
292
|
+
i++;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
i++;
|
|
296
|
+
}
|
|
297
|
+
return code.length;
|
|
298
|
+
}
|
|
299
|
+
function findInterpolationEnd(code, start) {
|
|
300
|
+
let depth = 1, i = start;
|
|
301
|
+
while (i < code.length) {
|
|
302
|
+
const ch = code[i];
|
|
303
|
+
if (ch === "\\") {
|
|
304
|
+
i += 2;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (ch === '"' || ch === "'") {
|
|
308
|
+
i = skipQuoted(code, i);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (ch === "`") {
|
|
312
|
+
i = skipTemplate(code, i);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (ch === "/" && code[i + 1] === "/") {
|
|
316
|
+
while (i < code.length && code[i] !== `
|
|
317
|
+
`)
|
|
318
|
+
i++;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (ch === "/" && code[i + 1] === "*") {
|
|
322
|
+
i += 2;
|
|
323
|
+
while (i < code.length && code.slice(i, i + 2) !== "*/")
|
|
324
|
+
i++;
|
|
325
|
+
i += 2;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (ch === "{") {
|
|
329
|
+
depth++;
|
|
330
|
+
i++;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (ch === "}") {
|
|
334
|
+
depth--;
|
|
335
|
+
if (depth === 0)
|
|
336
|
+
return i;
|
|
337
|
+
i++;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
i++;
|
|
341
|
+
}
|
|
342
|
+
return code.length;
|
|
343
|
+
}
|
|
345
344
|
export function stripCommentsAndLiterals(code) {
|
|
346
345
|
let out = "", i = 0;
|
|
347
346
|
while (i < code.length) {
|
|
@@ -362,7 +361,7 @@ export function stripCommentsAndLiterals(code) {
|
|
|
362
361
|
continue;
|
|
363
362
|
}
|
|
364
363
|
const ch = code[i];
|
|
365
|
-
if (ch === '"' || ch === "'"
|
|
364
|
+
if (ch === '"' || ch === "'") {
|
|
366
365
|
const quote = ch;
|
|
367
366
|
out += " ";
|
|
368
367
|
i++;
|
|
@@ -374,6 +373,33 @@ export function stripCommentsAndLiterals(code) {
|
|
|
374
373
|
}
|
|
375
374
|
out += code[i] === `
|
|
376
375
|
` ? `
|
|
376
|
+
` : " ";
|
|
377
|
+
i++;
|
|
378
|
+
}
|
|
379
|
+
out += " ";
|
|
380
|
+
i++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (ch === "`") {
|
|
384
|
+
out += " ";
|
|
385
|
+
i++;
|
|
386
|
+
while (i < code.length && code[i] !== "`") {
|
|
387
|
+
if (code[i] === "\\") {
|
|
388
|
+
out += " ";
|
|
389
|
+
i += 2;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (code[i] === "$" && code[i + 1] === "{") {
|
|
393
|
+
const bodyStart = i + 2, bodyEnd = findInterpolationEnd(code, bodyStart);
|
|
394
|
+
out += " ";
|
|
395
|
+
out += stripCommentsAndLiterals(code.slice(bodyStart, bodyEnd));
|
|
396
|
+
if (bodyEnd < code.length)
|
|
397
|
+
out += " ";
|
|
398
|
+
i = bodyEnd + 1;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
out += code[i] === `
|
|
402
|
+
` ? `
|
|
377
403
|
` : " ";
|
|
378
404
|
i++;
|
|
379
405
|
}
|
|
@@ -535,14 +561,32 @@ function declaresClientIdentifier(code, name) {
|
|
|
535
561
|
return !0;
|
|
536
562
|
return !1;
|
|
537
563
|
}
|
|
564
|
+
function referencesIdentifier(searchable, name) {
|
|
565
|
+
return new RegExp(`(?<![.#\\w$])${name}(?![\\w$])`).test(searchable);
|
|
566
|
+
}
|
|
567
|
+
const CREDENTIAL_NAME = /token|secret|passw|apikey|api_key|privatekey|private_key|credential|webhook|bearer|jwt|signature/i, warnedBridgeKeys = new Set;
|
|
568
|
+
function warnAboutBridgedValue(name, json) {
|
|
569
|
+
const credential = CREDENTIAL_NAME.test(name), oversized = json.length > 32768;
|
|
570
|
+
if (!credential && !oversized)
|
|
571
|
+
return;
|
|
572
|
+
if (warnedBridgeKeys.has(name))
|
|
573
|
+
return;
|
|
574
|
+
warnedBridgeKeys.add(name);
|
|
575
|
+
if (credential)
|
|
576
|
+
console.warn(`[stx] server\u2192client bridge is publishing "${name}" into the page body. If it holds a credential, rename it with a "__" prefix (never bridged) and pass only what the template needs. See stacksjs/stx#1831.`);
|
|
577
|
+
if (oversized)
|
|
578
|
+
console.warn(`[stx] server\u2192client bridge is publishing ${(json.length / 1024).toFixed(1)}KB under "${name}". Every byte ships in the HTML on each request; prefix the binding with "__" and project just the fields the client reads.`);
|
|
579
|
+
}
|
|
538
580
|
export function generateServerDataBridge(code, serverData) {
|
|
539
581
|
if (!serverData)
|
|
540
582
|
return "";
|
|
541
|
-
const lines = [];
|
|
583
|
+
const searchable = stripCommentsAndLiterals(code), lines = [];
|
|
542
584
|
for (const [name, value] of Object.entries(serverData)) {
|
|
543
585
|
if (!/^[A-Za-z_$][\w$]*$/.test(name))
|
|
544
586
|
continue;
|
|
545
|
-
if (
|
|
587
|
+
if (name.startsWith("__") || name.startsWith("$"))
|
|
588
|
+
continue;
|
|
589
|
+
if (!referencesIdentifier(searchable, name))
|
|
546
590
|
continue;
|
|
547
591
|
if (declaresClientIdentifier(code, name))
|
|
548
592
|
continue;
|
|
@@ -554,6 +598,7 @@ export function generateServerDataBridge(code, serverData) {
|
|
|
554
598
|
}
|
|
555
599
|
if (json === void 0)
|
|
556
600
|
continue;
|
|
601
|
+
warnAboutBridgedValue(name, json);
|
|
557
602
|
lines.push(` var ${name} = ${json.replace(/</g, "\\u003c")};`);
|
|
558
603
|
}
|
|
559
604
|
if (lines.length === 0)
|
|
@@ -589,7 +634,7 @@ function generateModifierChecks(modifiers) {
|
|
|
589
634
|
return checks.join("; ");
|
|
590
635
|
}
|
|
591
636
|
function generateSingleEventBinding(binding, index) {
|
|
592
|
-
const { elementId, event,
|
|
637
|
+
const { elementId, event, modifiers } = binding, handler = asInvocableStatement(binding.handler), checks = generateModifierChecks(modifiers), options = [];
|
|
593
638
|
if (modifiers.once)
|
|
594
639
|
options.push("once: true");
|
|
595
640
|
if (modifiers.capture)
|
|
@@ -910,19 +955,19 @@ export async function processClientScript(scriptContent, options = {}) {
|
|
|
910
955
|
if (/\btype\s*=\s*["']module["']/i.test(attrs)) {
|
|
911
956
|
const extraAttrs = attrs.replace(/\btype\s*=\s*["']module["']/i, "").trim(), attrStr = `type="module" data-stx-scoped${extraAttrs ? ` ${extraAttrs}` : ""}`;
|
|
912
957
|
return `${vendorStyleTags}<script ${attrStr}>
|
|
913
|
-
${autoImportCode}${code}
|
|
914
|
-
${eventCode}
|
|
958
|
+
${escapeScriptBody(`${autoImportCode}${code}
|
|
959
|
+
${eventCode}`)}
|
|
915
960
|
</script>`;
|
|
916
961
|
}
|
|
917
|
-
const usesSignals =
|
|
962
|
+
const usesSignals = usesReactiveRuntime(scriptContent), isSfcWrapped = /function __stx_setup_/.test(code), alreadyMounts = /\bstx\.mount\s*\(|\bstx\.mountEl\s*\(/.test(scriptContent);
|
|
918
963
|
function wrapInMount(declarations) {
|
|
919
964
|
const returnStmt = declarations.length > 0 ? `
|
|
920
965
|
return { ${declarations.join(", ")} };` : "";
|
|
921
966
|
return `${vendorStyleTags}<script data-stx-scoped data-stx-run="always">
|
|
922
967
|
window.stx.mount(function() {
|
|
923
968
|
'use strict';
|
|
924
|
-
${autoImportCode}${code}
|
|
925
|
-
${eventCode}${returnStmt}
|
|
969
|
+
${escapeScriptBody(`${autoImportCode}${code}
|
|
970
|
+
${eventCode}${returnStmt}`)}
|
|
926
971
|
})</script>`;
|
|
927
972
|
}
|
|
928
973
|
if (!alreadyMounts && !isSfcWrapped) {
|
|
@@ -940,7 +985,7 @@ ${eventCode}${returnStmt}
|
|
|
940
985
|
return `${vendorStyleTags}<script data-stx-scoped data-stx-run="always">
|
|
941
986
|
;(function() {
|
|
942
987
|
'use strict';
|
|
943
|
-
${autoImportCode}${code}
|
|
944
|
-
${eventCode}
|
|
988
|
+
${escapeScriptBody(`${autoImportCode}${code}
|
|
989
|
+
${eventCode}`)}
|
|
945
990
|
})()</script>`;
|
|
946
991
|
}
|
|
@@ -21,6 +21,37 @@ export declare function generateColorModeBootScript(options?: ColorModeBootConfi
|
|
|
21
21
|
* unchanged, so this is safe on paths that run it more than once.
|
|
22
22
|
*/
|
|
23
23
|
export declare function injectColorModeBootScript(html: string, options?: ColorModeBootConfig): string;
|
|
24
|
+
/**
|
|
25
|
+
* Re-assert the ordering of the two head tags whose position is load-bearing:
|
|
26
|
+
* `<meta charset>` first, then the color-mode boot script, then everything else.
|
|
27
|
+
*
|
|
28
|
+
* Run this LAST, after every other head injection. Ordering the injectors
|
|
29
|
+
* against each other would hold only until the next one is added; re-asserting
|
|
30
|
+
* the result once at the end holds regardless of what ran before.
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeCriticalHeadOrder(html: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Move an already-injected boot script back to the top of `<head>`.
|
|
35
|
+
*
|
|
36
|
+
* `injectColorModeBootScript` puts it first, but later stages of the pipeline
|
|
37
|
+
* then insert *ahead* of it and push it down (#1803): the signals runtime is
|
|
38
|
+
* relocated before the first `<script>` on the page — which is this one — and
|
|
39
|
+
* the store and composable bundles anchor themselves to the runtime tag. The
|
|
40
|
+
* boot script ended up fifth, behind all three.
|
|
41
|
+
*
|
|
42
|
+
* That is not merely a paint-timing problem. The bundles that overtook it read
|
|
43
|
+
* `window.__STX_COLOR_MODE__`, which this script publishes, so a `useColorMode()`
|
|
44
|
+
* inside a store saw `undefined` and silently fell back to the default storage
|
|
45
|
+
* key and a null attribute — writing the user's preference somewhere the boot
|
|
46
|
+
* script will never read it back, and never applying the configured attribute.
|
|
47
|
+
*
|
|
48
|
+
* Run this last, after every other head injection. Ordering the injectors
|
|
49
|
+
* against each other would work only until the next one is added; re-asserting
|
|
50
|
+
* the position once at the end holds regardless of what ran before.
|
|
51
|
+
*
|
|
52
|
+
* A no-op when the document has no boot script or it is already in place.
|
|
53
|
+
*/
|
|
54
|
+
export declare function hoistColorModeBootScript(html: string): string;
|
|
24
55
|
/**
|
|
25
56
|
* Pre-paint color-mode bootstrap (stacksjs/stx#1794).
|
|
26
57
|
*
|
package/dist/color-mode-boot.js
CHANGED
|
@@ -26,14 +26,50 @@ export function generateColorModeBootScript(options = {}) {
|
|
|
26
26
|
export function injectColorModeBootScript(html, options = {}) {
|
|
27
27
|
if (html.includes(COLOR_MODE_BOOT_MARKER))
|
|
28
28
|
return html;
|
|
29
|
+
const insertAt = bootInsertionPoint(html);
|
|
30
|
+
if (insertAt === null)
|
|
31
|
+
return html;
|
|
32
|
+
const script = generateColorModeBootScript(options);
|
|
33
|
+
return `${html.slice(0, insertAt)}
|
|
34
|
+
${script}${html.slice(insertAt)}`;
|
|
35
|
+
}
|
|
36
|
+
function bootInsertionPoint(html) {
|
|
29
37
|
const headOpen = /<head\b[^>]*>/i.exec(html);
|
|
30
38
|
if (!headOpen)
|
|
31
|
-
return
|
|
39
|
+
return null;
|
|
32
40
|
let insertAt = headOpen.index + headOpen[0].length;
|
|
33
|
-
const
|
|
41
|
+
const charset = /^\s*<meta\b[^>]*\bcharset\b[^>]*>/i.exec(html.slice(insertAt));
|
|
34
42
|
if (charset)
|
|
35
43
|
insertAt += charset[0].length;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
return insertAt;
|
|
45
|
+
}
|
|
46
|
+
const BOOT_SCRIPT_TAG = new RegExp(`<script\\b[^>]*\\b${COLOR_MODE_BOOT_MARKER}\\b[^>]*>[\\s\\S]*?<\\/script>`, "i"), CHARSET_META = /<meta\b[^>]*\bcharset\b[^>]*>/i;
|
|
47
|
+
function hoistCharsetMeta(html) {
|
|
48
|
+
const headOpen = /<head\b[^>]*>/i.exec(html);
|
|
49
|
+
if (!headOpen)
|
|
50
|
+
return html;
|
|
51
|
+
const headStart = headOpen.index + headOpen[0].length, headEnd = html.toLowerCase().indexOf("</head>", headStart), head = html.slice(headStart, headEnd === -1 ? html.length : headEnd), meta = CHARSET_META.exec(head);
|
|
52
|
+
if (!meta || head.slice(0, meta.index).trim() === "")
|
|
53
|
+
return html;
|
|
54
|
+
const withoutMeta = head.slice(0, meta.index) + head.slice(meta.index + meta[0].length);
|
|
55
|
+
return html.slice(0, headStart) + `
|
|
56
|
+
${meta[0]}` + withoutMeta + html.slice(headStart + head.length);
|
|
57
|
+
}
|
|
58
|
+
export function normalizeCriticalHeadOrder(html) {
|
|
59
|
+
return hoistColorModeBootScript(hoistCharsetMeta(html));
|
|
60
|
+
}
|
|
61
|
+
export function hoistColorModeBootScript(html) {
|
|
62
|
+
const tag = BOOT_SCRIPT_TAG.exec(html);
|
|
63
|
+
if (!tag)
|
|
64
|
+
return html;
|
|
65
|
+
const target = bootInsertionPoint(html);
|
|
66
|
+
if (target === null)
|
|
67
|
+
return html;
|
|
68
|
+
if (tag.index <= target || html.slice(target, tag.index).trim() === "")
|
|
69
|
+
return html;
|
|
70
|
+
const withoutTag = html.slice(0, tag.index) + html.slice(tag.index + tag[0].length), insertAt = bootInsertionPoint(withoutTag);
|
|
71
|
+
if (insertAt === null)
|
|
72
|
+
return html;
|
|
73
|
+
return `${withoutTag.slice(0, insertAt)}
|
|
74
|
+
${tag[0]}${withoutTag.slice(insertAt)}`;
|
|
39
75
|
}
|
|
@@ -4,10 +4,12 @@ import { decodeAttributeEntities, decodeStxProp, findComponentTags, parseMultili
|
|
|
4
4
|
import { maskAtElementPosition, matchHtmlComment } from "./html-masking.js";
|
|
5
5
|
import { renderComponentWithSlot, userComponentFileExists } from "./utils.js";
|
|
6
6
|
import { createSafeFunction, isExpressionSafe, safeEvaluateObject } from "./safe-evaluator.js";
|
|
7
|
+
import { BOOLEAN_ATTRIBUTE_SENTINEL } from "./prop-sentinels.js";
|
|
8
|
+
import { COMPONENT_PASSTHROUGH_X_ATTRS } from "./runtime-globals.js";
|
|
7
9
|
import fs from "node:fs";
|
|
8
10
|
import path from "node:path";
|
|
9
11
|
let builtinsRegistered = !1;
|
|
10
|
-
const
|
|
12
|
+
const htmlTags = new Set([
|
|
11
13
|
"a",
|
|
12
14
|
"abbr",
|
|
13
15
|
"address",
|
|
@@ -29,6 +31,7 @@ const BOOLEAN_ATTRIBUTE_SENTINEL = "\x00stx-boolean-attribute", htmlTags = new S
|
|
|
29
31
|
"code",
|
|
30
32
|
"col",
|
|
31
33
|
"colgroup",
|
|
34
|
+
"component",
|
|
32
35
|
"data",
|
|
33
36
|
"datalist",
|
|
34
37
|
"dd",
|
|
@@ -180,24 +183,7 @@ function parseComponentProps(rawProps, context, options) {
|
|
|
180
183
|
serverDynamic: {},
|
|
181
184
|
clientReactive: {},
|
|
182
185
|
events: {}
|
|
183
|
-
}, ALPINE_PASSTHROUGH = new Set(
|
|
184
|
-
"x-cloak",
|
|
185
|
-
"x-show",
|
|
186
|
-
"x-if",
|
|
187
|
-
"x-for",
|
|
188
|
-
"x-data",
|
|
189
|
-
"x-init",
|
|
190
|
-
"x-transition",
|
|
191
|
-
"x-ref",
|
|
192
|
-
"x-effect",
|
|
193
|
-
"x-model",
|
|
194
|
-
"x-text",
|
|
195
|
-
"x-html",
|
|
196
|
-
"x-class",
|
|
197
|
-
"x-style",
|
|
198
|
-
"x-on",
|
|
199
|
-
"x-bind"
|
|
200
|
-
]), kebabToCamel = (key) => {
|
|
186
|
+
}, ALPINE_PASSTHROUGH = new Set(COMPONENT_PASSTHROUGH_X_ATTRS), kebabToCamel = (key) => {
|
|
201
187
|
if (!key.includes("-") || key.startsWith("@") || key.startsWith(":") || key.startsWith("x-") || key.startsWith("__"))
|
|
202
188
|
return key;
|
|
203
189
|
if (key.startsWith("aria-") || key.startsWith("data-"))
|
|
@@ -343,6 +329,9 @@ function parseAllAttributes(attributesStr) {
|
|
|
343
329
|
}
|
|
344
330
|
return props;
|
|
345
331
|
}
|
|
332
|
+
function serialiseForwardedEvents(events) {
|
|
333
|
+
return Object.entries(events).map(([event, handler]) => handler === BOOLEAN_ATTRIBUTE_SENTINEL ? event : `${event}="${escapeAttribute(handler)}"`).join(" ");
|
|
334
|
+
}
|
|
346
335
|
function escapeAttribute(value) {
|
|
347
336
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
348
337
|
}
|
|
@@ -617,7 +606,7 @@ async function processCustomElementTags(template, context, filePath, options, de
|
|
|
617
606
|
if (Object.keys(resolvedProps.clientReactive).length > 0)
|
|
618
607
|
rendered = emitClientReactiveAttrs(rendered, resolvedProps.clientReactive);
|
|
619
608
|
if (Object.keys(resolvedProps.events).length > 0) {
|
|
620
|
-
const eventAttrs =
|
|
609
|
+
const eventAttrs = serialiseForwardedEvents(resolvedProps.events);
|
|
621
610
|
rendered = rendered.replace(/^(\s*<[a-zA-Z][a-zA-Z0-9-]*)/, `$1 ${eventAttrs}`);
|
|
622
611
|
}
|
|
623
612
|
result = result.substring(0, tag.startIndex) + rendered + result.substring(tag.endIndex);
|
|
@@ -636,7 +625,7 @@ async function processCustomElementTags(template, context, filePath, options, de
|
|
|
636
625
|
if (Object.keys(resolvedProps.events).length > 0) {
|
|
637
626
|
const root = findRenderedComponentRoot(finalContent);
|
|
638
627
|
if (root) {
|
|
639
|
-
const eventAttrs =
|
|
628
|
+
const eventAttrs = serialiseForwardedEvents(resolvedProps.events), parentEvents = Object.keys(resolvedProps.events).map((event) => event.replace(/^@/, "").split(".")[0]).join(" ");
|
|
640
629
|
finalContent = finalContent.substring(0, root.insertPos) + ` data-stx-parent-events="${parentEvents}" ${eventAttrs}` + finalContent.substring(root.insertPos);
|
|
641
630
|
}
|
|
642
631
|
}
|
|
@@ -19,11 +19,6 @@ export declare function useClipboard(options?: { timeout?: number }): ClipboardR
|
|
|
19
19
|
* Copy text to clipboard (one-shot helper)
|
|
20
20
|
*/
|
|
21
21
|
export declare function copyToClipboard(text: string): Promise<boolean>;
|
|
22
|
-
/**
|
|
23
|
-
* useClipboard - Reactive clipboard composable
|
|
24
|
-
*
|
|
25
|
-
* Provides easy access to the Clipboard API with fallbacks.
|
|
26
|
-
*/
|
|
27
22
|
export declare interface ClipboardRef {
|
|
28
23
|
text: string
|
|
29
24
|
isSupported: boolean
|
|
@@ -31,4 +26,5 @@ export declare interface ClipboardRef {
|
|
|
31
26
|
copy: (text: string) => Promise<boolean>
|
|
32
27
|
read: () => Promise<string>
|
|
33
28
|
subscribe: (callback: (text: string) => void) => () => void
|
|
29
|
+
stop: () => void
|
|
34
30
|
}
|