@csszyx/runtime 0.10.2 → 0.10.4

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/index.cjs CHANGED
@@ -1313,8 +1313,112 @@ function omitSz(sz, selector) {
1313
1313
  return filterSz(flattenSz(sz, 0), selector, false, 0);
1314
1314
  }
1315
1315
 
1316
+ const AMBIGUOUS_PREFIXES = /* @__PURE__ */ new Set([
1317
+ "flex",
1318
+ // flex-1 (flex shorthand) vs flex-row (flex-direction)
1319
+ "text",
1320
+ // text-sm (font-size) vs text-red-500 (color)
1321
+ "bg",
1322
+ // bg-red-500 (color) vs bg-cover (size) vs bg-center (position)
1323
+ "border",
1324
+ // border-2 (width) vs border-red-500 (color) vs border-solid (style)
1325
+ "divide",
1326
+ // divide-x (width) vs divide-red-500 (color)
1327
+ "ring",
1328
+ // ring-2 (width) vs ring-red-500 (color)
1329
+ "outline"
1330
+ // outline-2 (width) vs outline-red-500 (color)
1331
+ ]);
1332
+ function decodeToken(token) {
1333
+ const decode = globalThis.__csszyx?.decode;
1334
+ if (typeof decode === "function") {
1335
+ return decode(token) ?? token;
1336
+ }
1337
+ return token;
1338
+ }
1339
+ const SHORTHAND_COVERAGE = {
1340
+ p: ["p", "px", "py", "pt", "pr", "pb", "pl", "ps", "pe"],
1341
+ px: ["px", "pl", "pr", "ps", "pe"],
1342
+ py: ["py", "pt", "pb"],
1343
+ m: ["m", "mx", "my", "mt", "mr", "mb", "ml", "ms", "me"],
1344
+ mx: ["mx", "ml", "mr", "ms", "me"],
1345
+ my: ["my", "mt", "mb"],
1346
+ // inset (position) — physical sides only.
1347
+ inset: ["inset", "inset-x", "inset-y", "top", "right", "bottom", "left"],
1348
+ "inset-x": ["inset-x", "left", "right"],
1349
+ "inset-y": ["inset-y", "top", "bottom"],
1350
+ // border-radius — physical corners only (logical rounded-s*/e* stay keep-both).
1351
+ rounded: [
1352
+ "rounded",
1353
+ "rounded-t",
1354
+ "rounded-r",
1355
+ "rounded-b",
1356
+ "rounded-l",
1357
+ "rounded-tl",
1358
+ "rounded-tr",
1359
+ "rounded-br",
1360
+ "rounded-bl"
1361
+ ],
1362
+ "rounded-t": ["rounded-t", "rounded-tl", "rounded-tr"],
1363
+ "rounded-r": ["rounded-r", "rounded-tr", "rounded-br"],
1364
+ "rounded-b": ["rounded-b", "rounded-bl", "rounded-br"],
1365
+ "rounded-l": ["rounded-l", "rounded-tl", "rounded-bl"]
1366
+ };
1367
+ function mergeClassify(token) {
1368
+ const base = stripVariant(token);
1369
+ const variant = token.slice(0, token.length - base.length);
1370
+ const norm = normalizeBase(base);
1371
+ if (!norm) {
1372
+ return null;
1373
+ }
1374
+ if (BOX_ROLE_TOKENS.has(norm)) {
1375
+ return null;
1376
+ }
1377
+ for (const [prefix] of BOX_ROLE_PREFIXES) {
1378
+ if (norm === prefix || norm.startsWith(`${prefix}-`)) {
1379
+ if (AMBIGUOUS_PREFIXES.has(prefix)) {
1380
+ return null;
1381
+ }
1382
+ const coveredPrefixes = SHORTHAND_COVERAGE[prefix] ?? [prefix];
1383
+ return {
1384
+ key: `${variant} ${prefix}`,
1385
+ covers: coveredPrefixes.map((p) => `${variant} ${p}`)
1386
+ };
1387
+ }
1388
+ }
1389
+ return null;
1390
+ }
1391
+ function szcn(...inputs) {
1392
+ const order = [];
1393
+ const byKey = /* @__PURE__ */ new Map();
1394
+ for (const input of inputs) {
1395
+ if (!input || typeof input !== "string") {
1396
+ continue;
1397
+ }
1398
+ for (const token of input.split(/\s+/)) {
1399
+ if (!token) {
1400
+ continue;
1401
+ }
1402
+ const original = decodeToken(token);
1403
+ const info = mergeClassify(original);
1404
+ const key = info ? info.key : original;
1405
+ for (const covered of info ? info.covers : [key]) {
1406
+ if (byKey.delete(covered)) {
1407
+ const at = order.indexOf(covered);
1408
+ if (at !== -1) {
1409
+ order.splice(at, 1);
1410
+ }
1411
+ }
1412
+ }
1413
+ byKey.set(key, token);
1414
+ order.push(key);
1415
+ }
1416
+ }
1417
+ return order.map((key) => byKey.get(key)).join(" ");
1418
+ }
1419
+
1316
1420
  const warned$1 = /* @__PURE__ */ new Set();
1317
- const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package to `compilePackages`; otherwise check that the bundler is not skipping the file.';
1421
+ const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package directory to `compileSources`; otherwise check that the bundler is not skipping the file.';
1318
1422
  function warnRawSz() {
1319
1423
  if (warned$1.has(RAW_SZ_WARNING)) return;
1320
1424
  warned$1.add(RAW_SZ_WARNING);
@@ -1567,6 +1671,7 @@ exports.splitBox = splitBox;
1567
1671
  exports.splitBoxSz = splitBoxSz;
1568
1672
  exports.startHydration = startHydration;
1569
1673
  exports.stripSzProps = stripSzProps;
1674
+ exports.szcn = szcn;
1570
1675
  exports.szv = szv;
1571
1676
  exports.validateHydrationClass = validateHydrationClass;
1572
1677
  exports.verifyAllTokens = verifyAllTokens;
package/dist/index.d.cts CHANGED
@@ -598,6 +598,20 @@ declare function startHydration(): void;
598
598
  */
599
599
  declare function endHydration(): void;
600
600
 
601
+ /**
602
+ * Merge className strings with last-wins override per utility, mangle-aware.
603
+ *
604
+ * Intended for the single resolution point in a layered design-system component
605
+ * (typically at the leaf Box): combine the component's default classes with the
606
+ * forwarded override so the override wins on a same-utility collision, while
607
+ * keeping production mangling intact (unlike npm tailwind-merge).
608
+ *
609
+ * @param inputs - Class strings; falsy inputs (`false`/`null`/`undefined`/`''`) are skipped.
610
+ * @returns The merged className string.
611
+ * @example szcn('gap-2 p-4', 'gap-8') // → 'p-4 gap-8' (gap-8 overrides gap-2)
612
+ */
613
+ declare function szcn(...inputs: (string | false | null | undefined)[]): string;
614
+
601
615
  /** Which side of the CSS box-model border a property acts on. */
602
616
  type BoxRole = 'outer' | 'inner';
603
617
 
@@ -768,7 +782,7 @@ declare function omitSz(sz: SzInput, selector: BoxSelector): SzObject;
768
782
  *
769
783
  * The compiler rewrites `sz` to `className` at build time, so a compiled
770
784
  * component never carries a leftover `sz` prop. But when a file is NOT compiled
771
- * — e.g. a workspace package missing from `compilePackages`, or any source the
785
+ * — e.g. a workspace package missing from `compileSources`, or any source the
772
786
  * bundler skipped — a hand-forwarded `sz` survives and React spreads it to the
773
787
  * DOM as `sz="[object Object]"`. `stripSzProps` removes `sz` from the forwarded
774
788
  * props so it never reaches the DOM, and in development warns once when the
@@ -947,5 +961,5 @@ declare function isRuntimeInitialized(): boolean;
947
961
  */
948
962
  declare function resetRuntime(): void;
949
963
 
950
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
964
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
951
965
  export type { BoxRole, BoxSelector, Classification, HydrationError, HydrationErrorType, MangleMap, RecoveryManifest, RecoveryMode, RuntimeConfig, SSRContext, SplitBoxOptions, SplitBoxResult, SplitBoxSzOptions, SplitBoxSzResult, SzInput, TokenData, VerificationResult };
package/dist/index.d.mts CHANGED
@@ -598,6 +598,20 @@ declare function startHydration(): void;
598
598
  */
599
599
  declare function endHydration(): void;
600
600
 
601
+ /**
602
+ * Merge className strings with last-wins override per utility, mangle-aware.
603
+ *
604
+ * Intended for the single resolution point in a layered design-system component
605
+ * (typically at the leaf Box): combine the component's default classes with the
606
+ * forwarded override so the override wins on a same-utility collision, while
607
+ * keeping production mangling intact (unlike npm tailwind-merge).
608
+ *
609
+ * @param inputs - Class strings; falsy inputs (`false`/`null`/`undefined`/`''`) are skipped.
610
+ * @returns The merged className string.
611
+ * @example szcn('gap-2 p-4', 'gap-8') // → 'p-4 gap-8' (gap-8 overrides gap-2)
612
+ */
613
+ declare function szcn(...inputs: (string | false | null | undefined)[]): string;
614
+
601
615
  /** Which side of the CSS box-model border a property acts on. */
602
616
  type BoxRole = 'outer' | 'inner';
603
617
 
@@ -768,7 +782,7 @@ declare function omitSz(sz: SzInput, selector: BoxSelector): SzObject;
768
782
  *
769
783
  * The compiler rewrites `sz` to `className` at build time, so a compiled
770
784
  * component never carries a leftover `sz` prop. But when a file is NOT compiled
771
- * — e.g. a workspace package missing from `compilePackages`, or any source the
785
+ * — e.g. a workspace package missing from `compileSources`, or any source the
772
786
  * bundler skipped — a hand-forwarded `sz` survives and React spreads it to the
773
787
  * DOM as `sz="[object Object]"`. `stripSzProps` removes `sz` from the forwarded
774
788
  * props so it never reaches the DOM, and in development warns once when the
@@ -947,5 +961,5 @@ declare function isRuntimeInitialized(): boolean;
947
961
  */
948
962
  declare function resetRuntime(): void;
949
963
 
950
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
964
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
951
965
  export type { BoxRole, BoxSelector, Classification, HydrationError, HydrationErrorType, MangleMap, RecoveryManifest, RecoveryMode, RuntimeConfig, SSRContext, SplitBoxOptions, SplitBoxResult, SplitBoxSzOptions, SplitBoxSzResult, SzInput, TokenData, VerificationResult };
package/dist/index.mjs CHANGED
@@ -1311,8 +1311,112 @@ function omitSz(sz, selector) {
1311
1311
  return filterSz(flattenSz(sz, 0), selector, false, 0);
1312
1312
  }
1313
1313
 
1314
+ const AMBIGUOUS_PREFIXES = /* @__PURE__ */ new Set([
1315
+ "flex",
1316
+ // flex-1 (flex shorthand) vs flex-row (flex-direction)
1317
+ "text",
1318
+ // text-sm (font-size) vs text-red-500 (color)
1319
+ "bg",
1320
+ // bg-red-500 (color) vs bg-cover (size) vs bg-center (position)
1321
+ "border",
1322
+ // border-2 (width) vs border-red-500 (color) vs border-solid (style)
1323
+ "divide",
1324
+ // divide-x (width) vs divide-red-500 (color)
1325
+ "ring",
1326
+ // ring-2 (width) vs ring-red-500 (color)
1327
+ "outline"
1328
+ // outline-2 (width) vs outline-red-500 (color)
1329
+ ]);
1330
+ function decodeToken(token) {
1331
+ const decode = globalThis.__csszyx?.decode;
1332
+ if (typeof decode === "function") {
1333
+ return decode(token) ?? token;
1334
+ }
1335
+ return token;
1336
+ }
1337
+ const SHORTHAND_COVERAGE = {
1338
+ p: ["p", "px", "py", "pt", "pr", "pb", "pl", "ps", "pe"],
1339
+ px: ["px", "pl", "pr", "ps", "pe"],
1340
+ py: ["py", "pt", "pb"],
1341
+ m: ["m", "mx", "my", "mt", "mr", "mb", "ml", "ms", "me"],
1342
+ mx: ["mx", "ml", "mr", "ms", "me"],
1343
+ my: ["my", "mt", "mb"],
1344
+ // inset (position) — physical sides only.
1345
+ inset: ["inset", "inset-x", "inset-y", "top", "right", "bottom", "left"],
1346
+ "inset-x": ["inset-x", "left", "right"],
1347
+ "inset-y": ["inset-y", "top", "bottom"],
1348
+ // border-radius — physical corners only (logical rounded-s*/e* stay keep-both).
1349
+ rounded: [
1350
+ "rounded",
1351
+ "rounded-t",
1352
+ "rounded-r",
1353
+ "rounded-b",
1354
+ "rounded-l",
1355
+ "rounded-tl",
1356
+ "rounded-tr",
1357
+ "rounded-br",
1358
+ "rounded-bl"
1359
+ ],
1360
+ "rounded-t": ["rounded-t", "rounded-tl", "rounded-tr"],
1361
+ "rounded-r": ["rounded-r", "rounded-tr", "rounded-br"],
1362
+ "rounded-b": ["rounded-b", "rounded-bl", "rounded-br"],
1363
+ "rounded-l": ["rounded-l", "rounded-tl", "rounded-bl"]
1364
+ };
1365
+ function mergeClassify(token) {
1366
+ const base = stripVariant(token);
1367
+ const variant = token.slice(0, token.length - base.length);
1368
+ const norm = normalizeBase(base);
1369
+ if (!norm) {
1370
+ return null;
1371
+ }
1372
+ if (BOX_ROLE_TOKENS.has(norm)) {
1373
+ return null;
1374
+ }
1375
+ for (const [prefix] of BOX_ROLE_PREFIXES) {
1376
+ if (norm === prefix || norm.startsWith(`${prefix}-`)) {
1377
+ if (AMBIGUOUS_PREFIXES.has(prefix)) {
1378
+ return null;
1379
+ }
1380
+ const coveredPrefixes = SHORTHAND_COVERAGE[prefix] ?? [prefix];
1381
+ return {
1382
+ key: `${variant} ${prefix}`,
1383
+ covers: coveredPrefixes.map((p) => `${variant} ${p}`)
1384
+ };
1385
+ }
1386
+ }
1387
+ return null;
1388
+ }
1389
+ function szcn(...inputs) {
1390
+ const order = [];
1391
+ const byKey = /* @__PURE__ */ new Map();
1392
+ for (const input of inputs) {
1393
+ if (!input || typeof input !== "string") {
1394
+ continue;
1395
+ }
1396
+ for (const token of input.split(/\s+/)) {
1397
+ if (!token) {
1398
+ continue;
1399
+ }
1400
+ const original = decodeToken(token);
1401
+ const info = mergeClassify(original);
1402
+ const key = info ? info.key : original;
1403
+ for (const covered of info ? info.covers : [key]) {
1404
+ if (byKey.delete(covered)) {
1405
+ const at = order.indexOf(covered);
1406
+ if (at !== -1) {
1407
+ order.splice(at, 1);
1408
+ }
1409
+ }
1410
+ }
1411
+ byKey.set(key, token);
1412
+ order.push(key);
1413
+ }
1414
+ }
1415
+ return order.map((key) => byKey.get(key)).join(" ");
1416
+ }
1417
+
1314
1418
  const warned$1 = /* @__PURE__ */ new Set();
1315
- const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package to `compilePackages`; otherwise check that the bundler is not skipping the file.';
1419
+ const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package directory to `compileSources`; otherwise check that the bundler is not skipping the file.';
1316
1420
  function warnRawSz() {
1317
1421
  if (warned$1.has(RAW_SZ_WARNING)) return;
1318
1422
  warned$1.add(RAW_SZ_WARNING);
@@ -1521,4 +1625,4 @@ function resetRuntime() {
1521
1625
  runtimeState.initialized = false;
1522
1626
  }
1523
1627
 
1524
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
1628
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csszyx/runtime",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "Runtime helpers and hydration guards for csszyx",
5
5
  "keywords": [
6
6
  "csszyx",
@@ -49,8 +49,8 @@
49
49
  "dist"
50
50
  ],
51
51
  "dependencies": {
52
- "@csszyx/compiler": "0.10.2",
53
- "@csszyx/core": "0.10.2"
52
+ "@csszyx/compiler": "0.10.4",
53
+ "@csszyx/core": "0.10.4"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^20.11.0",