@stacksjs/stx 0.2.179 → 0.2.181

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.
@@ -4,6 +4,7 @@ import { getPublicEnvDefine } from "./public-env.js";
4
4
  import { STX_RUNTIME_GLOBALS } from "./runtime-globals.js";
5
5
  import { readSigned, sourceSignature, writeSigned } from "./source-signature.js";
6
6
  import { stripModuleImports, transformStoreImports } from "./store-imports.js";
7
+ import { getSharedTranspiler } from "./utils.js";
7
8
  const _cachedStoreScripts = new Map;
8
9
  function collectDeclaredNames(code, into) {
9
10
  for (const match of code.matchAll(/\b(?:const|let|var|function|class)\s+([A-Z_a-z$][\w$]*)/g))
@@ -64,7 +65,7 @@ export async function getStoreScript(storesDir) {
64
65
  return 0;
65
66
  return a.hasUseStore ? 1 : -1;
66
67
  });
67
- const sortedFiles = filesWithDeps.map((f) => f.file), chunks = [], declaredNames = new Set, transpiler = new Bun.Transpiler({ loader: "ts", target: "browser", define: getPublicEnvDefine() });
68
+ const sortedFiles = filesWithDeps.map((f) => f.file), chunks = [], declaredNames = new Set, transpiler = getSharedTranspiler({ loader: "ts", target: "browser", define: getPublicEnvDefine() });
68
69
  for (const file of sortedFiles)
69
70
  try {
70
71
  let code = await Bun.file(file).text();
package/dist/stx.d.ts CHANGED
@@ -586,9 +586,36 @@ declare function $watch<T>(
586
586
  // Toast notifications
587
587
  // ============================================================================
588
588
 
589
+ /*
590
+ * Every option `addToast` reads (#1932).
591
+ *
592
+ * `title`, `id` and `dark` shipped in the runtime for #1913 and none of them
593
+ * reached here, so the feature built to unblock a migration could not be called
594
+ * from a typechecked file — `stx typecheck` rejected the exact snippet that
595
+ * issue was closed with.
596
+ *
597
+ * `test/signals/toast-declaration-drift.test.ts` now reads the option names out
598
+ * of the runtime and fails if this list falls behind again. Adding an option
599
+ * here without the runtime reading it fails too: a documented option that does
600
+ * nothing is the quieter half of the same problem.
601
+ */
589
602
  interface StxToastOptions {
590
603
  /** Auto-dismiss duration in ms. 0 = persistent. Default: 3000 */
591
604
  duration?: number
605
+ /** Heading rendered above the message, in bold. */
606
+ title?: string
607
+ /**
608
+ * Semantic key. A second toast with the same id REPLACES the first in place
609
+ * rather than stacking, and `dismiss(id)` ends it — so a persistent
610
+ * "Publishing…" toast and the call that clears it need not thread the numeric
611
+ * handle between them.
612
+ */
613
+ id?: string | number
614
+ /**
615
+ * Force the dark or light palette. Omitted, it follows the document — the
616
+ * `dark` class, then `data-theme` / `data-color-mode`, then the OS setting.
617
+ */
618
+ dark?: boolean
592
619
  }
593
620
 
594
621
  interface StxToast {
@@ -600,8 +627,15 @@ interface StxToast {
600
627
  info: (message: string, options?: StxToastOptions) => number
601
628
  /** Show a warning toast (yellow) */
602
629
  warning: (message: string, options?: StxToastOptions) => number
603
- /** Dismiss a toast by id, or all toasts if no id is given */
604
- dismiss: (id?: number) => void
630
+ /**
631
+ * Dismiss a toast, or all of them if nothing is given.
632
+ *
633
+ * Takes the semantic `id` the toast was created with, or the numeric handle
634
+ * these methods return. The runtime branches on which it got, so a caller does
635
+ * not have to know which kind it is holding — this used to accept `number`
636
+ * only, which made replace-by-id unreachable from a typechecked call site.
637
+ */
638
+ dismiss: (id?: string | number) => void
605
639
  }
606
640
 
607
641
  declare const toast: StxToast
package/dist/utils.d.ts CHANGED
@@ -23,6 +23,7 @@ export declare function isJavaScriptScript(attrs: string): boolean;
23
23
  * Default: ALL scripts are TypeScript unless explicitly marked as JS
24
24
  */
25
25
  export declare function shouldTranspileTypeScript(attrs: string): boolean;
26
+ export declare function getSharedTranspiler(options: ConstructorParameters<typeof Bun.Transpiler>[0]): Bun.Transpiler;
26
27
  /**
27
28
  * Transpile TypeScript code to JavaScript using Bun (sync version)
28
29
  */
package/dist/utils.js CHANGED
@@ -11,6 +11,7 @@ import { getPublicEnvDefine } from "./public-env.js";
11
11
  import { processScopedStyles } from "./style-scoping.js";
12
12
  import { findSfcTemplateBlock } from "./sfc-template.js";
13
13
  import { importOnce } from "./lazy-module.js";
14
+ import { stashScriptElements } from "./html-masking.js";
14
15
  export {
15
16
  extractDirectiveNames,
16
17
  getPositionInfo,
@@ -64,6 +65,23 @@ export function isJavaScriptScript(attrs) {
64
65
  export function shouldTranspileTypeScript(attrs) {
65
66
  return !isJavaScriptScript(attrs);
66
67
  }
68
+ const transpilerPool = new Map;
69
+ export function getSharedTranspiler(options) {
70
+ let key;
71
+ try {
72
+ key = JSON.stringify(options);
73
+ } catch {
74
+ return new Bun.Transpiler(options);
75
+ }
76
+ const pooled = transpilerPool.get(key);
77
+ if (pooled)
78
+ return pooled;
79
+ if (transpilerPool.size >= 16)
80
+ transpilerPool.clear();
81
+ const transpiler = new Bun.Transpiler(options);
82
+ transpilerPool.set(key, transpiler);
83
+ return transpiler;
84
+ }
67
85
  export function transpileTypeScript(code) {
68
86
  try {
69
87
  let processedCode = code.replace(/^\s*import\s+\w+\s+from\s+['"][^'"]*\.stx['"]\s*;?\s*$/gm, "");
@@ -84,7 +102,7 @@ export function transpileTypeScript(code) {
84
102
  placeholders.push(match);
85
103
  return `"__STX_EXPR_${idx}__"`;
86
104
  });
87
- let result = new Bun.Transpiler({
105
+ let result = getSharedTranspiler({
88
106
  loader: "ts",
89
107
  target: "browser",
90
108
  define: getPublicEnvDefine()
@@ -564,7 +582,14 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
564
582
  for (const [key, val] of Object.entries(parentContext))
565
583
  if (!key.startsWith("__") || internalKeysToPreserve.has(key))
566
584
  filteredParentContext[key] = val;
567
- const { extractSlotContent } = await importOnce("stx/slots", () => import("./slots.js")), parsedSlotContent = extractSlotContent(slotContent), $slots = {};
585
+ const stashedCallerScripts = stashScriptElements(slotContent), callerClientScripts = [], componentSlotContent = stashedCallerScripts.output.replace(/\x00STX_SCRIPT_(\d+)\x00/g, (_match, index) => {
586
+ const script = stashedCallerScripts.scripts[Number(index)] || "", attrs = script.match(/^<script\b([^>]*)>/i)?.[1] || "", type = attrs.match(/\btype\s*=\s*["']([^"']+)["']/i)?.[1]?.toLowerCase(), executable = !type || ["module", "text/javascript", "application/javascript"].includes(type);
587
+ if (!/\bserver\b/i.test(attrs) && executable) {
588
+ callerClientScripts.push(script);
589
+ return "";
590
+ }
591
+ return script;
592
+ }), { extractSlotContent } = await importOnce("stx/slots", () => import("./slots.js")), parsedSlotContent = extractSlotContent(componentSlotContent), $slots = {};
568
593
  if (parsedSlotContent.defaultSlot?.trim())
569
594
  $slots.default = parsedSlotContent.defaultSlot;
570
595
  for (const [slotName, slotDef] of parsedSlotContent.namedSlots)
@@ -573,7 +598,7 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
573
598
  ...filteredParentContext,
574
599
  ...props,
575
600
  props,
576
- slot: slotContent,
601
+ slot: componentSlotContent,
577
602
  $slots,
578
603
  __processedComponents: branchComponents,
579
604
  __sections: {},
@@ -762,7 +787,9 @@ finally {
762
787
  output,
763
788
  dependencies: [...dependencies].filter((dependency) => !dependencyBaseline.has(dependency))
764
789
  });
765
- return output;
790
+ return callerClientScripts.length > 0 ? `${output}
791
+ ${callerClientScripts.join(`
792
+ `)}` : output;
766
793
  } catch (error) {
767
794
  return `[Error processing component: ${error instanceof Error ? error.message : String(error)}]`;
768
795
  } finally {
@@ -1,5 +1,6 @@
1
1
  var {require}=import.meta;import path from "node:path";
2
2
  import process from "node:process";
3
+ import { getSharedTranspiler } from "./utils.js";
3
4
  export const STX_ENGINE_BINDING_NAMES = [
4
5
  "module",
5
6
  "exports",
@@ -272,7 +273,7 @@ export async function extractVariables(scriptContent, context, filePath, options
272
273
  return;
273
274
  let jsContent;
274
275
  try {
275
- const transpiler = new Bun.Transpiler({ loader: "ts", target: "browser", define: getPublicEnvDefine() });
276
+ const transpiler = getSharedTranspiler({ loader: "ts", target: "browser", define: getPublicEnvDefine() });
276
277
  let processedCode = scriptContent.replace(/^\s*import\s+\w+\s+from\s+['"][^'"]*\.stx['"]\s*;?\s*$/gm, "");
277
278
  processedCode = processedCode.replace(/^\s*import\s+['"][^'"]*\.stx['"]\s*;?\s*$/gm, "");
278
279
  jsContent = transpiler.transformSync(processedCode);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/stx",
3
3
  "type": "module",
4
- "version": "0.2.179",
4
+ "version": "0.2.181",
5
5
  "description": "A performant UI Framework. Powered by Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -117,12 +117,12 @@
117
117
  "dependencies": {
118
118
  "@cwcss/crosswind": "^0.2.14",
119
119
  "@stacksjs/clapp": "^0.2.0",
120
- "@stacksjs/desktop": "0.2.179",
121
- "@stacksjs/sanitizer": "0.2.179",
120
+ "@stacksjs/desktop": "0.2.181",
121
+ "@stacksjs/sanitizer": "0.2.181",
122
122
  "@stacksjs/ts-i18n": "^0.1.11",
123
- "bun-plugin-stx": "0.2.179",
123
+ "bun-plugin-stx": "0.2.181",
124
124
  "bunfig": "^0.15.17",
125
- "stx-router": "0.2.179",
125
+ "stx-router": "0.2.181",
126
126
  "ts-broadcasting": "^0.0.9",
127
127
  "ts-images": "^0.1.9",
128
128
  "ts-syntax-highlighter": "^0.2.1"
package/stx.d.ts CHANGED
@@ -586,9 +586,36 @@ declare function $watch<T>(
586
586
  // Toast notifications
587
587
  // ============================================================================
588
588
 
589
+ /*
590
+ * Every option `addToast` reads (#1932).
591
+ *
592
+ * `title`, `id` and `dark` shipped in the runtime for #1913 and none of them
593
+ * reached here, so the feature built to unblock a migration could not be called
594
+ * from a typechecked file — `stx typecheck` rejected the exact snippet that
595
+ * issue was closed with.
596
+ *
597
+ * `test/signals/toast-declaration-drift.test.ts` now reads the option names out
598
+ * of the runtime and fails if this list falls behind again. Adding an option
599
+ * here without the runtime reading it fails too: a documented option that does
600
+ * nothing is the quieter half of the same problem.
601
+ */
589
602
  interface StxToastOptions {
590
603
  /** Auto-dismiss duration in ms. 0 = persistent. Default: 3000 */
591
604
  duration?: number
605
+ /** Heading rendered above the message, in bold. */
606
+ title?: string
607
+ /**
608
+ * Semantic key. A second toast with the same id REPLACES the first in place
609
+ * rather than stacking, and `dismiss(id)` ends it — so a persistent
610
+ * "Publishing…" toast and the call that clears it need not thread the numeric
611
+ * handle between them.
612
+ */
613
+ id?: string | number
614
+ /**
615
+ * Force the dark or light palette. Omitted, it follows the document — the
616
+ * `dark` class, then `data-theme` / `data-color-mode`, then the OS setting.
617
+ */
618
+ dark?: boolean
592
619
  }
593
620
 
594
621
  interface StxToast {
@@ -600,8 +627,15 @@ interface StxToast {
600
627
  info: (message: string, options?: StxToastOptions) => number
601
628
  /** Show a warning toast (yellow) */
602
629
  warning: (message: string, options?: StxToastOptions) => number
603
- /** Dismiss a toast by id, or all toasts if no id is given */
604
- dismiss: (id?: number) => void
630
+ /**
631
+ * Dismiss a toast, or all of them if nothing is given.
632
+ *
633
+ * Takes the semantic `id` the toast was created with, or the numeric handle
634
+ * these methods return. The runtime branches on which it got, so a caller does
635
+ * not have to know which kind it is holding — this used to accept `number`
636
+ * only, which made replace-by-id unreachable from a typechecked call site.
637
+ */
638
+ dismiss: (id?: string | number) => void
605
639
  }
606
640
 
607
641
  declare const toast: StxToast