tailwind-styled-v4 5.1.9 → 5.1.11

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.d.mts CHANGED
@@ -288,15 +288,45 @@ interface ThemeConfig$1 {
288
288
 
289
289
  /**
290
290
  * tailwind-styled-v4 — createTwMerge()
291
- * Pure Node.js — requires native Rust binding.
291
+ *
292
+ * Native Rust binding (twMergeRaw) is used when available (Node.js, build
293
+ * time, SSR) for maximum performance/parity with the rest of the pipeline.
294
+ *
295
+ * Browser fallback (Bug C — "Uncaught Error: Native binding 'twMergeRaw' is
296
+ * required but not available." crashing in the browser):
297
+ *
298
+ * createComponent.ts calls twMerge() unconditionally inside the React render
299
+ * function for EVERY tw.* component, on EVERY render — including the very
300
+ * first client-side render of any component that runs/re-renders in the
301
+ * browser. A `.node` native addon can never load inside a browser JS engine,
302
+ * so this isn't a rare edge case, it's unconditional: any tw.* component
303
+ * rendering client-side needs a working fallback path.
304
+ *
305
+ * getNativeBinding() returns `null` (not a throw) when running in the
306
+ * browser — see native.ts. When that happens we use mergeFallback.ts, a
307
+ * hand-written pure-TS port of the SAME algorithm as `tw_merge_raw` in
308
+ * tw_merge.rs (conflict_group / split_variants / merge_class_string),
309
+ * instead of the third-party `tailwind-merge` package. This keeps merge
310
+ * behavior defined by one algorithm — ours — rather than risking the
311
+ * browser and the server silently disagreeing on merged className output
312
+ * because two different implementations drifted apart. The cost: when
313
+ * tw_merge.rs's conflict_group() changes, mergeFallback.ts must be updated
314
+ * by hand to match (see comments there).
292
315
  */
293
316
 
294
317
  interface MergeOptions {
318
+ /**
319
+ * NOTE: not currently applied — matches the native path, which calls
320
+ * `native.twMergeRaw(inputs)` without ever forwarding a prefix either.
321
+ * Kept on the type for API stability; wire it through conflictGroup()
322
+ * in mergeFallback.ts (and tw_merge_raw on the Rust side) together if a
323
+ * custom Tailwind prefix needs to be supported correctly end-to-end.
324
+ */
295
325
  prefix?: string;
296
326
  separator?: string;
297
327
  theme?: ThemeConfig$1;
298
328
  }
299
- declare function createTwMerge(_options?: MergeOptions): (...classLists: Array<string | undefined | null | false>) => string;
329
+ declare function createTwMerge(options?: MergeOptions): (...classLists: Array<string | undefined | null | false>) => string;
300
330
  declare const twMerge: (...classLists: Array<string | undefined | null | false>) => string;
301
331
  declare function mergeWithRules(rules: Record<string, (classes: string[]) => string>, ...classLists: string[]): string;
302
332
 
package/dist/index.d.ts CHANGED
@@ -288,15 +288,45 @@ interface ThemeConfig$1 {
288
288
 
289
289
  /**
290
290
  * tailwind-styled-v4 — createTwMerge()
291
- * Pure Node.js — requires native Rust binding.
291
+ *
292
+ * Native Rust binding (twMergeRaw) is used when available (Node.js, build
293
+ * time, SSR) for maximum performance/parity with the rest of the pipeline.
294
+ *
295
+ * Browser fallback (Bug C — "Uncaught Error: Native binding 'twMergeRaw' is
296
+ * required but not available." crashing in the browser):
297
+ *
298
+ * createComponent.ts calls twMerge() unconditionally inside the React render
299
+ * function for EVERY tw.* component, on EVERY render — including the very
300
+ * first client-side render of any component that runs/re-renders in the
301
+ * browser. A `.node` native addon can never load inside a browser JS engine,
302
+ * so this isn't a rare edge case, it's unconditional: any tw.* component
303
+ * rendering client-side needs a working fallback path.
304
+ *
305
+ * getNativeBinding() returns `null` (not a throw) when running in the
306
+ * browser — see native.ts. When that happens we use mergeFallback.ts, a
307
+ * hand-written pure-TS port of the SAME algorithm as `tw_merge_raw` in
308
+ * tw_merge.rs (conflict_group / split_variants / merge_class_string),
309
+ * instead of the third-party `tailwind-merge` package. This keeps merge
310
+ * behavior defined by one algorithm — ours — rather than risking the
311
+ * browser and the server silently disagreeing on merged className output
312
+ * because two different implementations drifted apart. The cost: when
313
+ * tw_merge.rs's conflict_group() changes, mergeFallback.ts must be updated
314
+ * by hand to match (see comments there).
292
315
  */
293
316
 
294
317
  interface MergeOptions {
318
+ /**
319
+ * NOTE: not currently applied — matches the native path, which calls
320
+ * `native.twMergeRaw(inputs)` without ever forwarding a prefix either.
321
+ * Kept on the type for API stability; wire it through conflictGroup()
322
+ * in mergeFallback.ts (and tw_merge_raw on the Rust side) together if a
323
+ * custom Tailwind prefix needs to be supported correctly end-to-end.
324
+ */
295
325
  prefix?: string;
296
326
  separator?: string;
297
327
  theme?: ThemeConfig$1;
298
328
  }
299
- declare function createTwMerge(_options?: MergeOptions): (...classLists: Array<string | undefined | null | false>) => string;
329
+ declare function createTwMerge(options?: MergeOptions): (...classLists: Array<string | undefined | null | false>) => string;
300
330
  declare const twMerge: (...classLists: Array<string | undefined | null | false>) => string;
301
331
  declare function mergeWithRules(rules: Record<string, (classes: string[]) => string>, ...classLists: string[]): string;
302
332
 
package/dist/index.js CHANGED
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  "use strict";
4
2
  var __create = Object.create;
5
3
  var __defProp = Object.defineProperty;
@@ -249,7 +247,7 @@ var nativeBinding = null;
249
247
  var bindingLoadAttempted = false;
250
248
  var getBinding = () => {
251
249
  if (isBrowser2) {
252
- throw new Error(NATIVE_UNAVAILABLE_MESSAGE + "\n\nNative bindings are not available in browser. Use the compiled CSS output instead.");
250
+ return null;
253
251
  }
254
252
  if (nativeBinding) return nativeBinding;
255
253
  if (bindingLoadAttempted) {
@@ -257,10 +255,10 @@ var getBinding = () => {
257
255
  }
258
256
  bindingLoadAttempted = true;
259
257
  try {
260
- const runtimeDir = isBrowser2 ? "" : (0, import_node_path2.dirname)(
258
+ const runtimeDir = (0, import_node_path2.dirname)(
261
259
  typeof __filename !== "undefined" ? __filename : typeof import_meta2 !== "undefined" && importMetaUrl ? (0, import_node_url2.fileURLToPath)(importMetaUrl) : process.cwd()
262
260
  );
263
- const result = isBrowser2 ? { path: null, source: "not-found", platform: "browser", tried: [] } : resolveNativeBinary(runtimeDir);
261
+ const result = resolveNativeBinary(runtimeDir);
264
262
  if (result.path && result.path.endsWith(".node")) {
265
263
  const mod = _loadNative(result.path);
266
264
  if (mod?.batchSplitClasses) {
@@ -399,7 +397,291 @@ function getContainerRegistry() {
399
397
 
400
398
  // packages/domain/core/src/merge.ts
401
399
  init_cjs_shims();
402
- function createTwMerge(_options = {}) {
400
+
401
+ // packages/domain/core/src/mergeFallback.ts
402
+ init_cjs_shims();
403
+ var TEXT_SIZE_SUFFIXES = /* @__PURE__ */ new Set([
404
+ "xs",
405
+ "sm",
406
+ "base",
407
+ "lg",
408
+ "xl",
409
+ "2xl",
410
+ "3xl",
411
+ "4xl",
412
+ "5xl",
413
+ "6xl",
414
+ "7xl",
415
+ "8xl",
416
+ "9xl"
417
+ ]);
418
+ function isTextSize(suffix) {
419
+ if (TEXT_SIZE_SUFFIXES.has(suffix)) return true;
420
+ if (suffix.startsWith("[")) return true;
421
+ return false;
422
+ }
423
+ function isAsciiDigits(s) {
424
+ if (s.length === 0) return true;
425
+ for (let i = 0; i < s.length; i++) {
426
+ const code = s.charCodeAt(i);
427
+ if (code < 48 || code > 57) return false;
428
+ }
429
+ return true;
430
+ }
431
+ function isU32(s) {
432
+ return s.length > 0 && /^[0-9]+$/.test(s);
433
+ }
434
+ var DISPLAY_VALUES = /* @__PURE__ */ new Set([
435
+ "block",
436
+ "inline-block",
437
+ "inline",
438
+ "flex",
439
+ "inline-flex",
440
+ "grid",
441
+ "inline-grid",
442
+ "table",
443
+ "inline-table",
444
+ "table-row",
445
+ "table-cell",
446
+ "table-column",
447
+ "table-caption",
448
+ "contents",
449
+ "list-item",
450
+ "hidden",
451
+ "flow-root"
452
+ ]);
453
+ var POSITION_VALUES = /* @__PURE__ */ new Set(["static", "relative", "absolute", "fixed", "sticky"]);
454
+ var FONT_WEIGHT_NAMES = /* @__PURE__ */ new Set([
455
+ "thin",
456
+ "extralight",
457
+ "light",
458
+ "normal",
459
+ "medium",
460
+ "semibold",
461
+ "bold",
462
+ "extrabold",
463
+ "black"
464
+ ]);
465
+ var TEXT_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right", "justify", "start", "end"]);
466
+ var BORDER_SIDE_PREFIXES = ["t-", "r-", "b-", "l-", "x-", "y-", "s-", "e-"];
467
+ var BORDER_STYLE_VALUES = /* @__PURE__ */ new Set(["solid", "dashed", "dotted", "double", "hidden", "none"]);
468
+ function conflictGroup(base) {
469
+ const bracket = base.indexOf("[");
470
+ if (bracket !== -1) {
471
+ const prefix = base.slice(0, bracket).replace(/-+$/, "");
472
+ return prefix.length > 0 ? prefix : "arbitrary";
473
+ }
474
+ if (DISPLAY_VALUES.has(base)) return "display";
475
+ if (POSITION_VALUES.has(base)) return "position";
476
+ if (base.startsWith("overflow-x-")) return "overflow-x";
477
+ if (base.startsWith("overflow-y-")) return "overflow-y";
478
+ if (base.startsWith("overflow-")) return "overflow";
479
+ if (base.startsWith("flex-")) return "flex";
480
+ if (base.startsWith("grid-cols-")) return "grid-cols";
481
+ if (base.startsWith("grid-rows-")) return "grid-rows";
482
+ if (base.startsWith("grid-flow-")) return "grid-flow";
483
+ if (base.startsWith("col-")) return "col";
484
+ if (base.startsWith("row-")) return "row";
485
+ if (base === "grow" || base.startsWith("grow-")) return "grow";
486
+ if (base === "shrink" || base.startsWith("shrink-")) return "shrink";
487
+ if (base.startsWith("gap-x-")) return "gap-x";
488
+ if (base.startsWith("gap-y-")) return "gap-y";
489
+ if (base.startsWith("gap-")) return "gap";
490
+ if (base.startsWith("justify-items-")) return "justify-items";
491
+ if (base.startsWith("justify-self-")) return "justify-self";
492
+ if (base.startsWith("justify-")) return "justify";
493
+ if (base.startsWith("items-")) return "items";
494
+ if (base.startsWith("self-")) return "self";
495
+ if (base.startsWith("place-content-")) return "place-content";
496
+ if (base.startsWith("place-items-")) return "place-items";
497
+ if (base.startsWith("place-self-")) return "place-self";
498
+ if (base.startsWith("content-")) return "content";
499
+ if (base.startsWith("px-")) return "px";
500
+ if (base.startsWith("py-")) return "py";
501
+ if (base.startsWith("pt-")) return "pt";
502
+ if (base.startsWith("pr-")) return "pr";
503
+ if (base.startsWith("pb-")) return "pb";
504
+ if (base.startsWith("pl-")) return "pl";
505
+ if (base.startsWith("ps-")) return "ps";
506
+ if (base.startsWith("pe-")) return "pe";
507
+ if (base.startsWith("p-")) return "p";
508
+ if (base.startsWith("mx-")) return "mx";
509
+ if (base.startsWith("my-")) return "my";
510
+ if (base.startsWith("mt-")) return "mt";
511
+ if (base.startsWith("mr-")) return "mr";
512
+ if (base.startsWith("mb-")) return "mb";
513
+ if (base.startsWith("ml-")) return "ml";
514
+ if (base.startsWith("ms-")) return "ms";
515
+ if (base.startsWith("me-")) return "me";
516
+ if (base === "-m" || base.startsWith("m-") || base.startsWith("-m-")) return "m";
517
+ if (base.startsWith("space-x-")) return "space-x";
518
+ if (base.startsWith("space-y-")) return "space-y";
519
+ if (base.startsWith("size-")) return "size";
520
+ if (base.startsWith("min-w-")) return "min-w";
521
+ if (base.startsWith("max-w-")) return "max-w";
522
+ if (base.startsWith("w-")) return "w";
523
+ if (base.startsWith("min-h-")) return "min-h";
524
+ if (base.startsWith("max-h-")) return "max-h";
525
+ if (base.startsWith("h-")) return "h";
526
+ if (base.startsWith("inset-x-")) return "inset-x";
527
+ if (base.startsWith("inset-y-")) return "inset-y";
528
+ if (base.startsWith("inset-")) return "inset";
529
+ if (base.startsWith("top-")) return "top";
530
+ if (base.startsWith("right-") || base.startsWith("end-")) return "right";
531
+ if (base.startsWith("bottom-")) return "bottom";
532
+ if (base.startsWith("left-") || base.startsWith("start-")) return "left";
533
+ if (base.startsWith("z-")) return "z";
534
+ if (base.startsWith("opacity-")) return "opacity";
535
+ if (base.startsWith("bg-")) {
536
+ if (base.startsWith("bg-opacity-")) return "bg-opacity";
537
+ return "bg";
538
+ }
539
+ if (base.startsWith("from-")) return "from";
540
+ if (base.startsWith("via-")) return "via";
541
+ if (base.startsWith("to-")) return "to";
542
+ if (base.startsWith("text-")) {
543
+ const suffix = base.slice("text-".length);
544
+ if (isTextSize(suffix)) return "text-size";
545
+ if (suffix.startsWith("opacity-")) return "text-opacity";
546
+ if (TEXT_ALIGN_VALUES.has(suffix)) return "text-align";
547
+ return "text-color";
548
+ }
549
+ if (base.startsWith("font-")) {
550
+ const suffix = base.slice("font-".length);
551
+ if (FONT_WEIGHT_NAMES.has(suffix) || isAsciiDigits(suffix)) return "font-weight";
552
+ return "font-family";
553
+ }
554
+ if (base.startsWith("leading-")) return "leading";
555
+ if (base.startsWith("tracking-")) return "tracking";
556
+ if (base.startsWith("border-")) {
557
+ const suffix = base.slice("border-".length);
558
+ const sidePrefix = BORDER_SIDE_PREFIXES.find((p) => suffix.startsWith(p));
559
+ if (sidePrefix) {
560
+ const side = suffix.slice(0, 1);
561
+ const rest = suffix.slice(2);
562
+ if (isU32(rest) || rest.length === 0) return `border-${side}-width`;
563
+ return `border-${side}-color`;
564
+ }
565
+ if (isU32(suffix) || suffix.length === 0) return "border-width";
566
+ if (suffix.startsWith("opacity-")) return "border-opacity";
567
+ if (suffix === "collapse" || suffix === "separate") return "border-collapse";
568
+ if (BORDER_STYLE_VALUES.has(suffix)) return "border-style";
569
+ return "border-color";
570
+ }
571
+ if (base === "border") return "border-width";
572
+ if (base.startsWith("outline-")) return "outline";
573
+ if (base === "outline") return "outline";
574
+ if (base.startsWith("rounded-t") || base.startsWith("rounded-r") || base.startsWith("rounded-b") || base.startsWith("rounded-l") || base.startsWith("rounded-s") || base.startsWith("rounded-e")) {
575
+ const firstChar = base.slice("rounded-".length).charAt(0) || "x";
576
+ return `rounded-${firstChar}`;
577
+ }
578
+ if (base === "rounded" || base.startsWith("rounded-")) return "rounded";
579
+ if (base === "shadow" || base.startsWith("shadow-")) return "shadow";
580
+ if (base.startsWith("ring-offset-")) return "ring-offset";
581
+ if (base === "ring") return "ring-width";
582
+ if (base.startsWith("ring-")) {
583
+ const rest = base.slice("ring-".length);
584
+ const isWidth = rest === "0" || rest === "1" || rest === "2" || rest === "4" || rest === "8" || /^-?\d+(\.\d+)?$/.test(rest) || rest.startsWith("[") && rest.endsWith("]");
585
+ if (isWidth) return "ring-width";
586
+ if (rest === "inset") return "ring-inset";
587
+ return "ring-color";
588
+ }
589
+ if (base.startsWith("rotate-")) return "rotate";
590
+ if (base.startsWith("scale-x-")) return "scale-x";
591
+ if (base.startsWith("scale-y-")) return "scale-y";
592
+ if (base.startsWith("scale-")) return "scale";
593
+ if (base.startsWith("translate-x-")) return "translate-x";
594
+ if (base.startsWith("translate-y-")) return "translate-y";
595
+ if (base.startsWith("skew-x-")) return "skew-x";
596
+ if (base.startsWith("skew-y-")) return "skew-y";
597
+ if (base === "transition" || base.startsWith("transition-")) return "transition";
598
+ if (base.startsWith("duration-")) return "duration";
599
+ if (base.startsWith("ease-")) return "ease";
600
+ if (base.startsWith("delay-")) return "delay";
601
+ if (base === "animate" || base.startsWith("animate-")) return "animate";
602
+ if (base.startsWith("cursor-")) return "cursor";
603
+ if (base.startsWith("pointer-events-")) return "pointer-events";
604
+ if (base.startsWith("select-")) return "select";
605
+ if (base === "visible" || base === "invisible" || base === "collapse") return "visibility";
606
+ if (base.startsWith("object-")) return "object";
607
+ if (base.startsWith("aspect-")) return "aspect";
608
+ if (base.startsWith("order-")) return "order";
609
+ if (base.startsWith("whitespace-")) return "whitespace";
610
+ if (base.startsWith("list-")) return "list";
611
+ if (base.startsWith("fill-")) return "fill";
612
+ if (base.startsWith("stroke-")) return "stroke";
613
+ if (base.startsWith("backdrop-")) {
614
+ const rest = base.slice("backdrop-".length);
615
+ const seg = rest.split("-")[0] || "x";
616
+ return `backdrop-${seg}`;
617
+ }
618
+ if (base.startsWith("scroll-")) return "scroll";
619
+ if (base.startsWith("snap-")) return "snap";
620
+ if (base.startsWith("touch-")) return "touch";
621
+ if (base.startsWith("decoration-")) return "text-decoration";
622
+ if (base.startsWith("caret-")) return "caret";
623
+ if (base.startsWith("accent-")) return "accent";
624
+ if (base.startsWith("appearance-")) return "appearance";
625
+ if (base === "isolate" || base === "isolation-auto") return "isolation";
626
+ if (base.startsWith("mix-blend-")) return "mix-blend";
627
+ if (base.startsWith("bg-blend-")) return "bg-blend";
628
+ if (base.startsWith("float-")) return "float";
629
+ if (base.startsWith("clear-")) return "clear";
630
+ if (base.startsWith("break-")) return "break";
631
+ if (base.startsWith("columns-")) return "columns";
632
+ return null;
633
+ }
634
+ function splitVariants(klass) {
635
+ let depth = 0;
636
+ let lastColon = 0;
637
+ for (let i = 0; i < klass.length; i++) {
638
+ const ch = klass.charCodeAt(i);
639
+ if (ch === 91) depth++;
640
+ else if (ch === 93) depth = depth > 0 ? depth - 1 : 0;
641
+ else if (ch === 58 && depth === 0) lastColon = i + 1;
642
+ }
643
+ if (lastColon === 0) return ["", klass];
644
+ return [klass.slice(0, lastColon), klass.slice(lastColon)];
645
+ }
646
+ function mergeClassStringJs(input) {
647
+ const tokens = input.split(/\s+/).filter(Boolean);
648
+ if (tokens.length === 0) return "";
649
+ if (tokens.length === 1) return tokens[0];
650
+ const groupOwner = /* @__PURE__ */ new Map();
651
+ const slots = [];
652
+ for (const token of tokens) {
653
+ const [variants, base] = splitVariants(token);
654
+ const group = conflictGroup(base);
655
+ if (group !== null) {
656
+ const key = `${variants}::${group}`;
657
+ const prevIdx = groupOwner.get(key);
658
+ if (prevIdx !== void 0) slots[prevIdx] = null;
659
+ groupOwner.set(key, slots.length);
660
+ slots.push(token);
661
+ } else {
662
+ slots.push(token);
663
+ }
664
+ }
665
+ return slots.filter((s) => s !== null).join(" ");
666
+ }
667
+ function twMergeRawJs(classLists) {
668
+ const joined = classLists.map((s) => s.trim()).filter((s) => s.length > 0).join(" ");
669
+ if (joined.length === 0) return "";
670
+ return mergeClassStringJs(joined);
671
+ }
672
+
673
+ // packages/domain/core/src/merge.ts
674
+ var warnedFallback = false;
675
+ function warnFallbackOnce() {
676
+ if (warnedFallback) return;
677
+ warnedFallback = true;
678
+ if (typeof console !== "undefined") {
679
+ console.warn(
680
+ "[tailwind-styled-v4] Native binding 'twMergeRaw' tidak tersedia (normal di browser) \u2014 pakai pure-TS port dari algoritma Rust-nya. Hasil className tetap benar; ini cuma informasi, bukan error."
681
+ );
682
+ }
683
+ }
684
+ function createTwMerge(options = {}) {
403
685
  return function twMerge2(...classLists) {
404
686
  const inputs = [];
405
687
  for (let i = 0; i < classLists.length; i++) {
@@ -408,10 +690,11 @@ function createTwMerge(_options = {}) {
408
690
  }
409
691
  if (inputs.length === 0) return "";
410
692
  const native = getNativeBinding();
411
- if (!native?.twMergeRaw) {
412
- throw new Error("Native binding 'twMergeRaw' is required but not available.");
693
+ if (native?.twMergeRaw) {
694
+ return native.twMergeRaw(inputs);
413
695
  }
414
- return native.twMergeRaw(inputs);
696
+ warnFallbackOnce();
697
+ return twMergeRawJs(inputs);
415
698
  };
416
699
  }
417
700
  var twMerge = createTwMerge();