@stacksjs/stx 0.2.153 → 0.2.154

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.
Files changed (75) hide show
  1. package/dist/animation.js +6 -6
  2. package/dist/app-shell.js +1 -27
  3. package/dist/build-message.d.ts +10 -0
  4. package/dist/build-message.js +26 -0
  5. package/dist/builtins/attrs.js +6 -1
  6. package/dist/cli.js +1481 -1231
  7. package/dist/client-script-bundler.js +2 -4
  8. package/dist/client-script.d.ts +29 -0
  9. package/dist/client-script.js +140 -95
  10. package/dist/color-mode-boot.d.ts +31 -0
  11. package/dist/color-mode-boot.js +41 -5
  12. package/dist/component-renderer.js +10 -21
  13. package/dist/composables/index.d.ts +2 -0
  14. package/dist/composables/use-clipboard.d.ts +1 -5
  15. package/dist/composables/use-clipboard.js +57 -46
  16. package/dist/composables/use-color-mode.js +13 -5
  17. package/dist/composables/use-router.d.ts +28 -11
  18. package/dist/composables/use-router.js +63 -9
  19. package/dist/config.js +22 -7
  20. package/dist/craft.js +1029 -789
  21. package/dist/dev-server/crosswind.js +8 -6
  22. package/dist/dev-server/serve-app.js +4 -2
  23. package/dist/dev-server/serve-markdown.js +4 -1
  24. package/dist/dev-server/serve-multi.js +32 -20
  25. package/dist/document-shell.d.ts +65 -0
  26. package/dist/document-shell.js +59 -12
  27. package/dist/dynamic-components.js +123 -4
  28. package/dist/events.d.ts +26 -49
  29. package/dist/events.js +23 -9
  30. package/dist/expressions.d.ts +1 -1
  31. package/dist/expressions.js +3 -3
  32. package/dist/framework-composables.d.ts +41 -0
  33. package/dist/framework-composables.js +115 -0
  34. package/dist/hot-reload.js +8 -2
  35. package/dist/includes.js +2 -2
  36. package/dist/isolated-build.d.ts +12 -0
  37. package/dist/isolated-build.js +18 -0
  38. package/dist/plugin-system.d.ts +1 -1
  39. package/dist/process.js +40 -10
  40. package/dist/production-server.js +4 -1
  41. package/dist/prop-sentinels.d.ts +31 -0
  42. package/dist/prop-sentinels.js +7 -0
  43. package/dist/pwa.js +966 -726
  44. package/dist/reactive.js +1 -1
  45. package/dist/reactivity.d.ts +0 -15
  46. package/dist/reactivity.js +15 -6
  47. package/dist/runtime-globals.d.ts +40 -2
  48. package/dist/runtime-globals.js +90 -0
  49. package/dist/runtime-injection.d.ts +51 -0
  50. package/dist/runtime-injection.js +13 -0
  51. package/dist/script-emit.d.ts +20 -0
  52. package/dist/script-emit.js +11 -0
  53. package/dist/script-validation.js +12 -7
  54. package/dist/signal-processing.js +25 -40
  55. package/dist/signals-api.d.ts +30 -0
  56. package/dist/signals-api.js +18 -0
  57. package/dist/signals.js +258 -47
  58. package/dist/site-builder/build.js +21 -7
  59. package/dist/site-builder/seo.js +19 -10
  60. package/dist/site-builder/theme.js +5 -4
  61. package/dist/site-builder/types.d.ts +1 -0
  62. package/dist/ssg.js +986 -746
  63. package/dist/state-management.js +4 -1
  64. package/dist/store-loader.d.ts +0 -11
  65. package/dist/store-loader.js +27 -3
  66. package/dist/strict-lint.d.ts +9 -6
  67. package/dist/strict-lint.js +2 -21
  68. package/dist/stx.d.ts +111 -16
  69. package/dist/unresolved-identifiers.d.ts +50 -0
  70. package/dist/unresolved-identifiers.js +146 -0
  71. package/dist/utils.js +3 -3
  72. package/dist/variable-extractor.js +17 -1
  73. package/dist/x-element.js +1 -1
  74. package/package.json +5 -5
  75. package/stx.d.ts +111 -16
@@ -1,3 +1,4 @@
1
+ import { onDestroy } from "../signals-api.js";
1
2
  export function useClipboard(options = {}) {
2
3
  const { timeout = 1500 } = options, subscribers = new Set;
3
4
  let currentText = "", copied = !1, copiedTimeout = null;
@@ -8,42 +9,17 @@ export function useClipboard(options = {}) {
8
9
  } catch (e) {
9
10
  console.error("[useClipboard] Subscriber error:", e);
10
11
  }
11
- };
12
- return {
13
- get text() {
14
- return currentText;
15
- },
16
- get isSupported() {
17
- return isSupported;
18
- },
19
- get copied() {
20
- return copied;
21
- },
22
- copy: async (text) => {
23
- if (!isSupported)
24
- try {
25
- const textarea = document.createElement("textarea");
26
- textarea.value = text;
27
- textarea.style.position = "fixed";
28
- textarea.style.opacity = "0";
29
- document.body.appendChild(textarea);
30
- textarea.select();
31
- document.execCommand("copy");
32
- document.body.removeChild(textarea);
33
- currentText = text;
34
- copied = !0;
35
- notify(text);
36
- if (copiedTimeout)
37
- clearTimeout(copiedTimeout);
38
- copiedTimeout = setTimeout(() => {
39
- copied = !1;
40
- }, timeout);
41
- return !0;
42
- } catch {
43
- return !1;
44
- }
12
+ }, copy = async (text) => {
13
+ if (!isSupported)
45
14
  try {
46
- await navigator.clipboard.writeText(text);
15
+ const textarea = document.createElement("textarea");
16
+ textarea.value = text;
17
+ textarea.style.position = "fixed";
18
+ textarea.style.opacity = "0";
19
+ document.body.appendChild(textarea);
20
+ textarea.select();
21
+ document.execCommand("copy");
22
+ document.body.removeChild(textarea);
47
23
  currentText = text;
48
24
  copied = !0;
49
25
  notify(text);
@@ -51,27 +27,62 @@ export function useClipboard(options = {}) {
51
27
  clearTimeout(copiedTimeout);
52
28
  copiedTimeout = setTimeout(() => {
53
29
  copied = !1;
30
+ notify(currentText);
54
31
  }, timeout);
55
32
  return !0;
56
33
  } catch {
57
34
  return !1;
58
35
  }
36
+ try {
37
+ await navigator.clipboard.writeText(text);
38
+ currentText = text;
39
+ copied = !0;
40
+ notify(text);
41
+ if (copiedTimeout)
42
+ clearTimeout(copiedTimeout);
43
+ copiedTimeout = setTimeout(() => {
44
+ copied = !1;
45
+ notify(currentText);
46
+ }, timeout);
47
+ return !0;
48
+ } catch {
49
+ return !1;
50
+ }
51
+ }, read = async () => {
52
+ if (!isSupported)
53
+ return "";
54
+ try {
55
+ const text = await navigator.clipboard.readText();
56
+ currentText = text;
57
+ return text;
58
+ } catch {
59
+ return "";
60
+ }
61
+ }, stop = () => {
62
+ if (copiedTimeout) {
63
+ clearTimeout(copiedTimeout);
64
+ copiedTimeout = null;
65
+ }
66
+ subscribers.clear();
67
+ };
68
+ onDestroy(stop);
69
+ return {
70
+ get text() {
71
+ return currentText;
59
72
  },
60
- read: async () => {
61
- if (!isSupported)
62
- return "";
63
- try {
64
- const text = await navigator.clipboard.readText();
65
- currentText = text;
66
- return text;
67
- } catch {
68
- return "";
69
- }
73
+ get isSupported() {
74
+ return isSupported;
70
75
  },
76
+ get copied() {
77
+ return copied;
78
+ },
79
+ copy,
80
+ read,
71
81
  subscribe: (callback) => {
72
82
  subscribers.add(callback);
73
83
  return () => subscribers.delete(callback);
74
- }
84
+ },
85
+ stop
75
86
  };
76
87
  }
77
88
  export async function copyToClipboard(text) {
@@ -7,11 +7,19 @@ export function normalizeColorMode(value) {
7
7
  return null;
8
8
  }
9
9
  export function useColorMode(options = {}) {
10
- const boot = typeof window < "u" ? window.__STX_COLOR_MODE__ : null, {
11
- storageKey = boot?.storageKey ?? "stx-color-mode",
12
- initialMode = boot?.initialMode ?? "auto",
13
- darkClass = boot?.darkClass ?? "dark",
14
- attribute = boot?.attribute ?? null,
10
+ const boot = typeof window < "u" ? window.__STX_COLOR_MODE__ : null;
11
+ function pick(fromOptions, fromBoot, fallback) {
12
+ if (fromOptions !== void 0)
13
+ return fromOptions;
14
+ if (boot && typeof boot === "object" && fromBoot !== void 0)
15
+ return fromBoot;
16
+ return fallback;
17
+ }
18
+ const {
19
+ storageKey = pick(void 0, boot?.storageKey, "stx-color-mode"),
20
+ initialMode = pick(void 0, boot?.initialMode, "auto"),
21
+ darkClass = pick(void 0, boot?.darkClass, "dark"),
22
+ attribute = pick(void 0, boot?.attribute, null),
15
23
  autoValue: autoValueOption = boot?.autoValue,
16
24
  disableTransitions = !0
17
25
  } = options;
@@ -1,13 +1,20 @@
1
1
  /**
2
- * Navigate to a URL. Uses stxRouter if available, otherwise falls back to location.href.
2
+ * Navigate to a URL. Uses stxRouter if available, otherwise falls back to a
3
+ * full document load.
4
+ *
5
+ * Kept in step with the client runtime's `navigate` (#1807), which used to take
6
+ * a bare `forceReload` boolean while the shipped declaration promised
7
+ * `{ replace }` — so `navigate(url, { replace: true })` type-checked and then
8
+ * did a full load that PUSHED a history entry.
3
9
  *
4
10
  * @example
5
11
  * ```ts
6
12
  * navigate('/about')
7
- * navigate('https://example.com')
13
+ * navigate('/dashboard', { replace: true })
14
+ * navigate('/legacy', { reload: true })
8
15
  * ```
9
16
  */
10
- export declare function navigate(url: string): void;
17
+ export declare function navigate(url: string, options?: NavigateOptions | boolean): void;
11
18
  /**
12
19
  * Go back in browser history.
13
20
  */
@@ -42,12 +49,6 @@ export declare function setRouteParams(params: Record<string, string>): void;
42
49
  * ```
43
50
  */
44
51
  export declare function useSearchParams(): SearchParamsRef;
45
- /**
46
- * useRouter - Navigation composable for STX applications
47
- *
48
- * Provides clean APIs for navigation without raw `window.location` or `window.history`.
49
- * These are server-safe stubs; the actual runtime implementations live in signals.ts.
50
- */
51
52
  export declare interface RouteInfo {
52
53
  path: string
53
54
  fullPath: string
@@ -55,9 +56,25 @@ export declare interface RouteInfo {
55
56
  query: Record<string, string>
56
57
  params: Record<string, string>
57
58
  }
59
+ /**
60
+ * How a search-param mutation is written to session history.
61
+ *
62
+ * Spelled `{ replace }` to match `navigate()`, which already takes it — before
63
+ * this the two halves of the routing API disagreed on whether replacing was
64
+ * expressible at all.
65
+ */
66
+ export declare interface SearchParamsCommitOptions {
67
+ replace?: boolean
68
+ }
58
69
  export declare interface SearchParamsRef {
59
70
  data: { (): Record<string, string>, set: (v: Record<string, string>) => void }
60
71
  get: (key: string) => string | undefined
61
- set: (key: string, value: string) => void
62
- setAll: (obj: Record<string, string>) => void
72
+ has: (key: string) => boolean
73
+ set: (key: string, value: string, options?: SearchParamsCommitOptions) => void
74
+ delete: (key: string, options?: SearchParamsCommitOptions) => void
75
+ setAll: (obj: Record<string, string>, options?: SearchParamsCommitOptions) => void
76
+ }
77
+ export declare interface NavigateOptions {
78
+ replace?: boolean
79
+ reload?: boolean
63
80
  }
@@ -1,9 +1,16 @@
1
- export function navigate(url) {
2
- if (typeof window < "u")
3
- if (window.stxRouter?.navigate)
4
- window.stxRouter.navigate(url);
5
- else
6
- window.location.href = url;
1
+ import { state } from "../signals-api.js";
2
+ export function navigate(url, options) {
3
+ if (typeof window > "u")
4
+ return;
5
+ const opts = options && typeof options === "object" ? options : { reload: !!options }, router = window.stxRouter;
6
+ if (!opts.reload && router?.navigate) {
7
+ router.navigate(url, { replace: !!opts.replace });
8
+ return;
9
+ }
10
+ if (opts.replace)
11
+ window.location.replace(url);
12
+ else
13
+ window.location.href = url;
7
14
  }
8
15
  export function goBack() {
9
16
  if (typeof window < "u")
@@ -45,7 +52,54 @@ export function setRouteParams(params) {
45
52
  window.stx.setRouteParams(params);
46
53
  }
47
54
  export function useSearchParams() {
48
- return { data: Object.assign(() => ({}), { set: () => {} }), get: () => {
49
- return;
50
- }, set: () => {}, setAll: () => {} };
55
+ if (typeof window < "u" && window.stx?.useSearchParams)
56
+ return window.stx.useSearchParams();
57
+ if (typeof window > "u")
58
+ return {
59
+ data: Object.assign(() => ({}), { set: () => {} }),
60
+ get: () => {
61
+ return;
62
+ },
63
+ has: () => !1,
64
+ set: () => {},
65
+ delete: () => {},
66
+ setAll: () => {}
67
+ };
68
+ const read = () => {
69
+ const out = {};
70
+ new URLSearchParams(window.location.search).forEach((v, k) => {
71
+ out[k] = v;
72
+ });
73
+ return out;
74
+ }, data = state(read()), sync = () => data.set(read());
75
+ window.addEventListener("popstate", sync);
76
+ window.addEventListener("stx:navigate", sync);
77
+ const commit = (url, options) => {
78
+ if (!!(options && typeof options === "object" && options.replace))
79
+ window.history.replaceState({}, "", url);
80
+ else
81
+ window.history.pushState({}, "", url);
82
+ sync();
83
+ }, has = (key) => Object.prototype.hasOwnProperty.call(data(), key);
84
+ return {
85
+ data,
86
+ get: (key) => has(key) ? data()[key] : void 0,
87
+ has,
88
+ set: (key, value, options) => {
89
+ const url = new URL(window.location.href);
90
+ url.searchParams.set(key, value);
91
+ commit(url, options);
92
+ },
93
+ delete: (key, options) => {
94
+ const url = new URL(window.location.href);
95
+ url.searchParams.delete(key);
96
+ commit(url, options);
97
+ },
98
+ setAll: (obj, options) => {
99
+ const url = new URL(window.location.href);
100
+ for (const [k, v] of Object.entries(obj))
101
+ url.searchParams.set(k, v);
102
+ commit(url, options);
103
+ }
104
+ };
51
105
  }
package/dist/config.js CHANGED
@@ -103,7 +103,7 @@ export const defaultConfig = {
103
103
  },
104
104
  router: {
105
105
  enabled: !0,
106
- container: "#app-content",
106
+ container: "main",
107
107
  linkSelector: "a[href]",
108
108
  viewTransitions: !0,
109
109
  scrollToTop: !0,
@@ -360,18 +360,31 @@ export const defaultConfig = {
360
360
  verbose: !1
361
361
  }
362
362
  };
363
- function resolveStxRoot(configRoot, configPagesDir) {
363
+ function resolveStxRoot(configRoot, configPagesDir, cwd) {
364
364
  const defaultPagesDir = configPagesDir || "pages";
365
365
  if (configRoot)
366
366
  return { root: configRoot, pagesDir: defaultPagesDir };
367
- const resourcesViews = path.join(process.cwd(), "resources", "views"), resourcesLayouts = path.join(process.cwd(), "resources", "layouts");
367
+ const base = cwd || process.cwd(), resourcesViews = path.join(base, "resources", "views"), resourcesLayouts = path.join(base, "resources", "layouts");
368
368
  if (fs.existsSync(resourcesViews) && fs.existsSync(resourcesLayouts))
369
369
  return { root: "resources", pagesDir: "views" };
370
- const resourcesViewsPages = path.join(process.cwd(), "resources", "views", "pages");
370
+ const resourcesViewsPages = path.join(base, "resources", "views", "pages");
371
371
  if (fs.existsSync(resourcesViewsPages))
372
372
  return { root: "resources/views", pagesDir: defaultPagesDir };
373
373
  return { root: ".", pagesDir: defaultPagesDir };
374
374
  }
375
+ function warnMissingDirs(loaded, cwd) {
376
+ const keys = ["componentsDir", "layoutsDir", "partialsDir"];
377
+ for (const key of keys) {
378
+ const value = loaded[key];
379
+ if (typeof value !== "string" || !value)
380
+ continue;
381
+ const abs = path.isAbsolute(value) ? value : path.join(cwd, value);
382
+ if (fs.existsSync(abs))
383
+ continue;
384
+ const prefixed = !!loaded.root && loaded.root !== ".";
385
+ console.warn(`[stx] ${String(key)} resolves to "${abs}", which does not exist. Configured as "${value}"` + (prefixed ? ` and prefixed with root "${loaded.root}" \u2014 a value that already includes the root gets it twice.` : "."));
386
+ }
387
+ }
375
388
  function applyStateDir(loaded) {
376
389
  setStateDir(loaded.stateDir);
377
390
  loaded.stateDir = stateDirName();
@@ -405,7 +418,7 @@ export async function loadStxConfig(cwd) {
405
418
  verbose: !1
406
419
  })).config;
407
420
  applyStateDir(loaded);
408
- const resolved = resolveStxRoot(loaded.root, loaded.pagesDir);
421
+ const resolved = resolveStxRoot(loaded.root, loaded.pagesDir, effectiveCwd);
409
422
  loaded.root = resolved.root;
410
423
  loaded.pagesDir = resolved.pagesDir;
411
424
  if (loaded.root && loaded.root !== ".") {
@@ -417,6 +430,7 @@ export async function loadStxConfig(cwd) {
417
430
  if (loaded.layoutsDir && !path.isAbsolute(loaded.layoutsDir))
418
431
  loaded.layoutsDir = path.join(rootPrefix, loaded.layoutsDir);
419
432
  }
433
+ warnMissingDirs(loaded, effectiveCwd);
420
434
  if (loaded.plugins && loaded.plugins.length > 0) {
421
435
  const { pluginManager } = await import("./plugin-system.js"), pluginComponentDirs = [], pluginPageDirs = [];
422
436
  for (const pluginEntry of loaded.plugins) {
@@ -434,8 +448,9 @@ export async function loadStxConfig(cwd) {
434
448
  const plugin = pluginModule.default || pluginModule;
435
449
  pluginManager.register(plugin, loaded);
436
450
  if (plugin.components) {
437
- const dir = path.resolve(pluginDir, plugin.components);
438
- pluginComponentDirs.push(dir);
451
+ const dirs = Array.isArray(plugin.components) ? plugin.components : [plugin.components];
452
+ for (const entry of dirs)
453
+ pluginComponentDirs.push(path.resolve(pluginDir, entry));
439
454
  }
440
455
  if (plugin.pages) {
441
456
  const dir = path.resolve(pluginDir, plugin.pages), prefix = pluginOptions.path || "/";