tailwind-styled-v4 5.0.8 → 5.0.10
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/CHANGELOG.md +204 -416
- package/README.md +45 -15
- package/dist/{analyzeWorkspace-DuJKh7Ty.d.mts → analyzeWorkspace-BS5O4rhC.d.mts} +47 -2
- package/dist/{analyzeWorkspace-Ct_NTAWt.d.ts → analyzeWorkspace-DDOQdzzI.d.ts} +47 -2
- package/dist/analyzer.d.mts +5 -3
- package/dist/analyzer.d.ts +5 -3
- package/dist/analyzer.js +563 -468
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzer.mjs +562 -467
- package/dist/analyzer.mjs.map +1 -1
- package/dist/animate.d.mts +4 -7
- package/dist/animate.d.ts +4 -7
- package/dist/animate.js +171 -265
- package/dist/animate.js.map +1 -1
- package/dist/animate.mjs +165 -264
- package/dist/animate.mjs.map +1 -1
- package/dist/atomic.d.mts +22 -1
- package/dist/atomic.d.ts +22 -1
- package/dist/atomic.js +221 -165
- package/dist/atomic.js.map +1 -1
- package/dist/atomic.mjs +200 -165
- package/dist/atomic.mjs.map +1 -1
- package/dist/cli.d.mts +60 -1
- package/dist/cli.d.ts +60 -1
- package/dist/cli.js +1261 -1517
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1238 -1513
- package/dist/cli.mjs.map +1 -1
- package/dist/compiler.d.mts +38 -7
- package/dist/compiler.d.ts +38 -7
- package/dist/compiler.js +174 -197
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.mjs +151 -194
- package/dist/compiler.mjs.map +1 -1
- package/dist/devtools.js +7 -31
- package/dist/devtools.js.map +1 -1
- package/dist/devtools.mjs +7 -31
- package/dist/devtools.mjs.map +1 -1
- package/dist/engine.d.mts +134 -63
- package/dist/engine.d.ts +134 -63
- package/dist/engine.js +2863 -2482
- package/dist/engine.js.map +1 -1
- package/dist/engine.mjs +2852 -2485
- package/dist/engine.mjs.map +1 -1
- package/dist/{index-eWAocnD2.d.mts → index-NDINUhLN.d.mts} +3 -1
- package/dist/{index-eWAocnD2.d.ts → index-NDINUhLN.d.ts} +3 -1
- package/dist/index.d.mts +63 -32
- package/dist/index.d.ts +63 -32
- package/dist/index.js +335 -169
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +315 -169
- package/dist/index.mjs.map +1 -1
- package/dist/{liveTokenEngine-DSUk88P6.d.ts → liveTokenEngine-CN9ian1R.d.ts} +1 -1
- package/dist/{liveTokenEngine-CX5_0c4q.d.mts → liveTokenEngine-DKoWRtqH.d.mts} +1 -1
- package/dist/next.d.mts +10 -4
- package/dist/next.d.ts +10 -4
- package/dist/next.js +32 -45
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +30 -43
- package/dist/next.mjs.map +1 -1
- package/dist/plugin-api.d.mts +8 -2
- package/dist/plugin-api.d.ts +8 -2
- package/dist/plugin-api.js +14 -2
- package/dist/plugin-api.js.map +1 -1
- package/dist/plugin-api.mjs +14 -3
- package/dist/plugin-api.mjs.map +1 -1
- package/dist/plugin-registry.js +51 -11
- package/dist/plugin-registry.js.map +1 -1
- package/dist/plugin-registry.mjs +51 -11
- package/dist/plugin-registry.mjs.map +1 -1
- package/dist/plugin.d.mts +5 -7
- package/dist/plugin.d.ts +5 -7
- package/dist/plugin.js +16 -15
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +16 -16
- package/dist/plugin.mjs.map +1 -1
- package/dist/rspack.js +17 -38
- package/dist/rspack.js.map +1 -1
- package/dist/rspack.mjs +15 -36
- package/dist/rspack.mjs.map +1 -1
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/scanner.d.mts +10 -1
- package/dist/scanner.d.ts +10 -1
- package/dist/scanner.js +298 -124
- package/dist/scanner.js.map +1 -1
- package/dist/scanner.mjs +296 -124
- package/dist/scanner.mjs.map +1 -1
- package/dist/shared.d.mts +1 -1
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +104 -176
- package/dist/shared.js.map +1 -1
- package/dist/shared.mjs +85 -176
- package/dist/shared.mjs.map +1 -1
- package/dist/storybook-addon.d.mts +1 -1
- package/dist/storybook-addon.d.ts +1 -1
- package/dist/svelte.d.mts +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/svelte.js +166 -3
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +143 -1
- package/dist/svelte.mjs.map +1 -1
- package/dist/syntax.js +21 -21
- package/dist/syntax.js.map +1 -1
- package/dist/syntax.mjs +21 -21
- package/dist/syntax.mjs.map +1 -1
- package/dist/testing.js +9 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +9 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/theme.d.mts +2 -2
- package/dist/theme.d.ts +2 -2
- package/dist/theme.js +40 -112
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +37 -110
- package/dist/theme.mjs.map +1 -1
- package/dist/turbopackLoader.js +84 -126
- package/dist/turbopackLoader.js.map +1 -1
- package/dist/turbopackLoader.mjs +68 -124
- package/dist/turbopackLoader.mjs.map +1 -1
- package/dist/tw.js +1256 -1517
- package/dist/tw.js.map +1 -1
- package/dist/tw.mjs +1236 -1513
- package/dist/tw.mjs.map +1 -1
- package/dist/vite.js +1783 -823
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +1767 -821
- package/dist/vite.mjs.map +1 -1
- package/dist/vue.d.mts +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +165 -4
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +141 -1
- package/dist/vue.mjs.map +1 -1
- package/dist/webpackLoader.js +69 -108
- package/dist/webpackLoader.js.map +1 -1
- package/dist/webpackLoader.mjs +49 -104
- package/dist/webpackLoader.mjs.map +1 -1
- package/native/tailwind-styled-native.node +0 -0
- package/package.json +22 -24
package/dist/svelte.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/presentation/svelte/src/index.ts"],"names":[],"mappings":";;;;AAsCA,IAAM,WAAA,GAAc,CAAC,KAAA,KAA4C;AAC/D,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,OAAO,KAAK,CAAA;AAClD,EAAA,OAAO,MAAA;AACT,CAAA;AAiBA,SAAS,eAAA,CACP,QAAA,EACA,KAAA,EACA,QAAA,GAAmC,EAAC,EAC5B;AACR,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAG,CAAA,IAAK,SAAS,GAAG,CAAA;AACtC,IAAA,IAAI,GAAA,KAAQ,UAAa,QAAA,CAAS,GAAG,IAAI,MAAA,CAAO,GAAG,CAAC,CAAA,EAAG;AACrD,MAAA,OAAA,CAAQ,KAAK,QAAA,CAAS,GAAG,EAAE,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA,IACzC;AAAA,EACF;AACA,EAAA,OAAO,OAAA,CAAQ,KAAK,GAAG,CAAA;AACzB;AAEA,SAAS,eAAA,CACP,WACA,KAAA,EACQ;AACR,EAAA,OAAO,SAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAM;AACb,IAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,GAAG,YAAW,GAAI,CAAA;AACvC,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,MAAM,CAAC,CAAA;AAAA,EACpE,CAAC,EACA,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,KAAK,CAAA,CAClB,IAAA,CAAK,GAAG,CAAA;AACb;AAoBO,SAAS,GAAG,MAAA,EAA+B;AAChD,EAAA,OAAO,CAAC,KAAA,GAAe,EAAC,KAAM;AAC5B,IAAA,MAAM,EAAE,IAAA,GAAO,EAAA,EAAI,QAAA,GAAW,EAAC,EAAG,gBAAA,GAAmB,EAAC,EAAG,eAAA,GAAkB,EAAC,EAAE,GAAI,MAAA;AAElF,IAAA,MAAM,MAAA,GAAS,EAAE,GAAG,eAAA,EAAiB,GAAG,KAAA,EAAM;AAC9C,IAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,QAAA,EAAU,MAAA,EAAQ,eAAe,CAAA;AACxE,IAAA,MAAM,eAAA,GAAkB,eAAA,CAAgB,gBAAA,EAAkB,MAAM,CAAA;AAEhE,IAAA,OAAO,QAAQ,IAAA,EAAM,cAAA,EAAgB,iBAAiB,WAAA,CAAY,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,EAChF,CAAA;AACF;AAeO,SAAS,MAAM,OAAA,EAA6D;AACjF,EAAA,OAAO,OAAA,CAAQ,GAAI,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAc,CAAA;AACzD;AAmBO,SAAS,OACd,IAAA,EACA,EAAE,QAAQ,KAAA,GAAQ,IAAG,EACrB;AAGA,EAAA,SAAS,MAAA,CAAO;AAAA,IACd,MAAA,EAAQ,GAAA;AAAA,IACR,KAAA,EAAO,IAAI;AAAC,GACd,EAGG;AACD,IAAA,MAAM,OAAA,GAAU,EAAA,CAAG,GAAG,CAAA,CAAE,CAAC,CAAA;AAEzB,IAAA,IAAA,CAAK,SAAA,GAAY,OAAA;AAAA,EACnB;AAGA,EAAA,MAAA,CAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAA;AAExB,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,OAAA,GAAU;AAAA,IAEV;AAAA,GACF;AACF;AAmBO,SAAS,cAAA,CAAe,QAA+B,QAAA,EAAuB;AACnF,EAAA,MAAM,QAAA,GAAW,GAAG,MAAM,CAAA;AAE1B,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,MAAM,QAAA,CAAS,QAAA,EAAU,CAAA;AAAA,IACpC;AAAA,GACF;AACF","file":"svelte.mjs","sourcesContent":["/**\r\n * tailwind-styled-v4 — Svelte Adapter\r\n *\r\n * Svelte 4+ adapter menggunakan action dan store pattern.\r\n * Kompatibel dengan Svelte 5 runes mode.\r\n *\r\n * Dua pola yang didukung:\r\n * 1. `tw()` — buat class string resolver untuk dipakai di template\r\n * 2. `createSvelteComponent()` — buat Svelte component via Svelte API\r\n *\r\n * @example\r\n * <!-- Pola 1: tw() sebagai class resolver -->\r\n * <script>\r\n * import { cv } from '@tailwind-styled/svelte'\r\n *\r\n * const buttonClass = cv({\r\n * base: 'px-4 py-2 rounded font-medium',\r\n * variants: {\r\n * intent: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },\r\n * size: { sm: 'h-8 text-sm', lg: 'h-12 text-lg' },\r\n * },\r\n * defaultVariants: { intent: 'primary', size: 'sm' },\r\n * })\r\n *\r\n * export let intent = 'primary'\r\n * export let size = 'sm'\r\n * </script>\r\n *\r\n * <button class={buttonClass({ intent, size })}>\r\n * <slot />\r\n * </button>\r\n */\r\n\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\nimport type { VariantValue, VariantProps as Props } from '@tailwind-styled/shared'\r\n\r\n\r\nconst toClassName = (value: VariantValue): string | undefined => {\r\n if (typeof value === \"string\") return value\r\n if (typeof value === \"number\") return String(value)\r\n return undefined\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Types\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SvelteComponentConfig {\r\n base?: string\r\n variants?: Record<string, Record<string, string>>\r\n compoundVariants?: Array<{ class: string; [key: string]: VariantValue }>\r\n defaultVariants?: Record<string, string>\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Variant resolvers (framework-agnostic core)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nfunction resolveVariants(\r\n variants: Record<string, Record<string, string>>,\r\n props: Props,\r\n defaults: Record<string, string> = {}\r\n): string {\r\n const classes: string[] = []\r\n for (const key in variants) {\r\n const val = props[key] ?? defaults[key]\r\n if (val !== undefined && variants[key]?.[String(val)]) {\r\n classes.push(variants[key][String(val)])\r\n }\r\n }\r\n return classes.join(\" \")\r\n}\r\n\r\nfunction resolveCompound(\r\n compounds: Array<{ class: string; [key: string]: VariantValue }>,\r\n props: Props\r\n): string {\r\n return compounds\r\n .filter((c) => {\r\n const { class: _cls, ...conditions } = c\r\n return Object.entries(conditions).every(([k, v]) => props[k] === v)\r\n })\r\n .map((c) => c.class)\r\n .join(\" \")\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// cv() — Class Variant (utama untuk Svelte)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Class variant resolver — pola utama untuk Svelte.\r\n * Returns fungsi yang menerima props dan mengembalikan class string.\r\n *\r\n * Gunakan ini di `<script>` block, lalu panggil di template.\r\n *\r\n * @example\r\n * const button = cv({\r\n * base: 'px-4 py-2 rounded',\r\n * variants: { size: { sm: 'h-8', lg: 'h-12' } },\r\n * })\r\n *\r\n * // Di template: class={button({ size: 'lg' })}\r\n */\r\nexport function cv(config: SvelteComponentConfig) {\r\n return (props: Props = {}) => {\r\n const { base = \"\", variants = {}, compoundVariants = [], defaultVariants = {} } = config\r\n\r\n const merged = { ...defaultVariants, ...props }\r\n const variantClasses = resolveVariants(variants, merged, defaultVariants)\r\n const compoundClasses = resolveCompound(compoundVariants, merged)\r\n\r\n return twMerge(base, variantClasses, compoundClasses, toClassName(props.class))\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// tw() — Simple class merger dengan template literal support\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Simple class merger — berguna untuk merge class dinamis.\r\n *\r\n * @example\r\n * import { tw } from '@tailwind-styled/svelte'\r\n *\r\n * // Merge class string\r\n * const cls = tw('px-4 py-2', isActive && 'bg-blue-500', className)\r\n */\r\nexport function tw(...classes: Array<string | boolean | null | undefined>): string {\r\n return twMerge(...(classes.filter(Boolean) as string[]))\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Svelte action — use:styled\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Svelte action untuk menerapkan class dinamis.\r\n *\r\n * @example\r\n * <script>\r\n * import { styled } from '@tailwind-styled/svelte'\r\n * const buttonConfig = { base: 'px-4 py-2', variants: { size: { sm: 'h-8', lg: 'h-12' } } }\r\n * </script>\r\n *\r\n * <button use:styled={{ config: buttonConfig, props: { size: 'lg' } }}>\r\n * Click me\r\n * </button>\r\n */\r\nexport function styled(\r\n node: HTMLElement,\r\n { config, props = {} }: { config: SvelteComponentConfig; props?: Props }\r\n) {\r\n const _resolver = cv(config)\r\n\r\n function update({\r\n config: cfg,\r\n props: p = {},\r\n }: {\r\n config: SvelteComponentConfig\r\n props?: Props\r\n }) {\r\n const classes = cv(cfg)(p)\r\n // Hapus class lama yang dikelola action, tambah yang baru\r\n node.className = classes\r\n }\r\n\r\n // Apply initial classes\r\n update({ config, props })\r\n\r\n return {\r\n update,\r\n destroy() {\r\n // Cleanup tidak diperlukan\r\n },\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// createVariants() — Svelte 5 runes compatible\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Svelte 5 runes-compatible variant factory.\r\n * Mengembalikan reactive class string menggunakan $derived pattern.\r\n *\r\n * @example\r\n * // Dalam Svelte 5 component:\r\n * const { className, variantProps } = createVariants({\r\n * base: 'px-4 py-2',\r\n * variants: { size: { sm: 'h-8', lg: 'h-12' } },\r\n * }, () => ({ size: currentSize }))\r\n *\r\n * // className adalah fungsi yang return class string terkini\r\n */\r\nexport function createVariants(config: SvelteComponentConfig, getProps: () => Props) {\r\n const resolver = cv(config)\r\n\r\n return {\r\n className: () => resolver(getProps()),\r\n config,\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../packages/domain/shared/src/native-resolution.ts","../packages/domain/shared/src/index.ts","../packages/domain/core/src/native.ts","../packages/domain/core/src/merge.ts","../packages/presentation/svelte/src/index.ts"],"names":["createRequire","isBrowser","path","fileURLToPath","twMerge"],"mappings":"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","file":"svelte.mjs","sourcesContent":["/**\r\n * Prebuilt binary resolution untuk native NAPI bindings.\r\n * QA #1: Resolve native binary dari prebuilt packages atau local build.\r\n *\r\n * Prioritas:\r\n * 1. TW_NATIVE_PATH env var (explicit override)\r\n * 2. Prebuilt binary dari platform-specific npm package\r\n * 3. Local build dari source (developer mode)\r\n */\r\n\r\nimport { createRequire } from \"node:module\"\r\nimport * as fs from \"node:fs\"\r\nimport * as path from \"node:path\"\r\n\r\nconst isBrowser = typeof window !== \"undefined\" || typeof document !== \"undefined\"\r\n\r\n// ESM-safe require — works in both ESM and CJS contexts\r\nconst _require = typeof require !== \"undefined\" ? require : createRequire(import.meta.url)\r\n\r\nexport interface NativeResolutionResult {\r\n path: string | null\r\n source: \"env\" | \"prebuilt\" | \"local\" | \"not-found\"\r\n platform: string\r\n tried: string[]\r\n}\r\n\r\n/** Platform key → prebuilt npm package name */\r\nconst PLATFORM_MAP: Record<string, string[]> = {\r\n \"linux-x64\": [\"@tailwind-styled/native-linux-x64\"],\r\n \"linux-arm64\": [\"@tailwind-styled/native-linux-arm64\"],\r\n \"darwin-x64\": [\"@tailwind-styled/native-darwin-x64\"],\r\n \"darwin-arm64\": [\"@tailwind-styled/native-darwin-arm64\"],\r\n \"win32-x64\": [\"@tailwind-styled/native-win32-x64\"],\r\n \"win32-arm64\": [\"@tailwind-styled/native-win32-arm64\"],\r\n}\r\n\r\nfunction platformKey(): string {\r\n if (isBrowser) return \"browser\"\r\n return `${process.platform}-${process.arch}`\r\n}\r\n\r\n/**\r\n * Resolve native binary path dari semua sumber yang tersedia.\r\n *\r\n * @example\r\n * const result = resolveNativeBinary()\r\n * if (result.path) {\r\n * const binding = require(result.path)\r\n * } else {\r\n * throw new Error(\"Native binding not found — run npm run build:rust\")\r\n * }\r\n */\r\nexport function resolveNativeBinary(runtimeDir?: string): NativeResolutionResult {\r\n const platform = platformKey()\r\n const tried: string[] = []\r\n\r\n if (isBrowser) {\r\n return { path: null, source: \"not-found\", platform, tried: [\"not available in browser\"] }\r\n }\r\n\r\n // 0. Disabled flag — always short-circuit before any I/O\r\n if (process.env.TWS_DISABLE_NATIVE === \"1\") {\r\n return { path: null, source: \"not-found\", platform, tried: [] }\r\n }\r\n\r\n // 1. Env var override\r\n const envPath = process.env.TW_NATIVE_PATH?.trim()\r\n if (envPath) {\r\n if (fs.existsSync(envPath)) {\r\n return { path: envPath, source: \"env\", platform, tried }\r\n }\r\n tried.push(`env:${envPath} (not found)`)\r\n }\r\n\r\n // 2. Prebuilt binary dari platform-specific npm package\r\n const prebuiltPkgs = PLATFORM_MAP[platform] ?? []\r\n for (const pkg of prebuiltPkgs) {\r\n try {\r\n const candidate = _require.resolve(`${pkg}/tailwind_styled_parser.node`)\r\n if (fs.existsSync(candidate)) {\r\n return { path: candidate, source: \"prebuilt\", platform, tried }\r\n }\r\n tried.push(`prebuilt:${pkg} (resolved but missing)`)\r\n } catch {\r\n tried.push(`prebuilt:${pkg} (not installed)`)\r\n }\r\n }\r\n\r\n // 3. Local build candidates\r\n const cwd = process.cwd()\r\n const base = runtimeDir ?? cwd\r\n // napi-rs naming: platform key may have -gnu suffix on Linux\r\n const napiPlatform = platform === \"linux-x64\" ? \"linux-x64-gnu\"\r\n : platform === \"linux-arm64\" ? \"linux-arm64-gnu\"\r\n : platform\r\n\r\n // Both possible binary names:\r\n // - \"tailwind_styled_parser\" (old hardcoded name in resolvers)\r\n // - \"tailwind-styled-native\" (actual binaryName in native/package.json)\r\n const BINARY_NAMES = [\"tailwind-styled-native\", \"tailwind_styled_parser\"]\r\n\r\n const localCandidates: string[] = []\r\n\r\n for (const bin of BINARY_NAMES) {\r\n localCandidates.push(path.resolve(base, `${bin}.node`))\r\n localCandidates.push(path.resolve(base, \"..\", `${bin}.node`))\r\n localCandidates.push(path.resolve(base, `${bin}.${platform}.node`))\r\n localCandidates.push(path.resolve(base, `${bin}.${napiPlatform}.node`))\r\n }\r\n\r\n // Walk up from cwd AND base to find repo root native/ dir\r\n // Needed when npm workspaces sets cwd to the package subdir\r\n for (const startDir of [cwd, base]) {\r\n let dir = startDir\r\n for (let i = 0; i < 6; i++) {\r\n const nativeDir = path.resolve(dir, \"native\")\r\n for (const bin of BINARY_NAMES) {\r\n localCandidates.push(path.resolve(nativeDir, `${bin}.node`))\r\n localCandidates.push(path.resolve(nativeDir, `${bin}.${platform}.node`))\r\n localCandidates.push(path.resolve(nativeDir, `${bin}.${napiPlatform}.node`))\r\n localCandidates.push(path.resolve(nativeDir, \"target\", \"release\", `${bin}.node`))\r\n }\r\n const parent = path.resolve(dir, \"..\")\r\n if (parent === dir) break\r\n dir = parent\r\n }\r\n }\r\n\r\n for (const candidate of localCandidates) {\r\n tried.push(`local:${candidate}`)\r\n if (fs.existsSync(candidate)) {\r\n return { path: candidate, source: \"local\", platform, tried }\r\n }\r\n }\r\n\r\n return { path: null, source: \"not-found\", platform, tried }\r\n}\r\n\r\n/**\r\n * Format human-readable error untuk \"binary not found\".\r\n */\r\nexport function formatNativeNotFoundError(result: NativeResolutionResult): string {\r\n const lines = [\r\n `[tailwind-styled] Native binding not found for ${result.platform}`,\r\n ``,\r\n `Tried:`,\r\n ...result.tried.map(t => ` - ${t}`),\r\n ``,\r\n `Solutions:`,\r\n ` 1. Build locally: npm run build:rust`,\r\n ` 2. Install prebuilt: npm install @tailwind-styled/native-${result.platform}`,\r\n ` 3. Override path: TW_NATIVE_PATH=/path/to/parser.node`,\r\n ]\r\n return lines.join(\"\\n\")\r\n}","import { createHash } from \"node:crypto\"\r\nimport fs from \"node:fs\"\r\nimport path from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\nimport { createRequire } from \"node:module\"\r\n\r\n// Native-only: Node.js is always available. No browser fallback.\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Types\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type TokenMap = Record<string, string>\r\n\r\nexport type VariantValue = string | number | boolean | undefined\r\n\r\nexport type VariantProps = Record<string, VariantValue>\r\n\r\nexport type HtmlTagName = keyof HTMLElementTagNameMap\r\n\r\nexport type CompoundCondition = Record<string, string | number | boolean>\r\n\r\nexport type VariantMatrix = Record<string, Array<string | number | boolean>>\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Logging\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface Logger {\r\n warn(...args: unknown[]): void\r\n debug(...args: unknown[]): void\r\n error(...args: unknown[]): void\r\n log(...args: unknown[]): void\r\n}\r\n\r\nexport function createLogger(namespace: string): Logger {\r\n const prefix = `[${namespace}]`\r\n return {\r\n warn(...args: unknown[]) {\r\n process.stderr.write(`${prefix} ${args.map(a => typeof a === \"string\" ? a : String(a)).join(\" \")}\\n`)\r\n },\r\n debug(...args: unknown[]) {\r\n process.stderr.write(`${prefix} ${args.map(a => typeof a === \"string\" ? a : String(a)).join(\" \")}\\n`)\r\n },\r\n error(...args: unknown[]) {\r\n process.stderr.write(`${prefix} ${args.map(a => typeof a === \"string\" ? a : String(a)).join(\" \")}\\n`)\r\n },\r\n log(...args: unknown[]) {\r\n process.stderr.write(`${prefix} ${args.map(a => typeof a === \"string\" ? a : String(a)).join(\" \")}\\n`)\r\n },\r\n }\r\n}\r\n\r\nexport function createDebugLogger(namespace: string, label?: string): (msg: string) => void {\r\n const prefix = label ? `[${namespace}:${label}]` : `[${namespace}]`\r\n return (msg: string) => {\r\n if (process.env.DEBUG?.includes(namespace) || process.env.TW_DEBUG) {\r\n console.debug(prefix, msg)\r\n }\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Error handling\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type ErrorSource = \"rust\" | \"validation\" | \"compile\" | \"io\" | \"config\" | \"unknown\"\r\n\r\ntype ZodLikeIssue = {\r\n path?: readonly PropertyKey[]\r\n message?: string\r\n}\r\n\r\nfunction formatIssuePath(path?: readonly PropertyKey[]): string {\r\n if (!path || path.length === 0) return \"(root)\"\r\n return path\r\n .map((segment) =>\r\n typeof segment === \"symbol\" ? segment.description ?? segment.toString() : String(segment)\r\n )\r\n .join(\".\")\r\n}\r\n\r\nexport class TwError extends Error {\r\n /** @deprecated Gunakan source */\r\n public readonly domain: string\r\n public readonly source: ErrorSource\r\n public readonly code: string\r\n public readonly originalCause?: unknown\r\n\r\n constructor(domainOrSource: string, code: string, message: string, cause?: unknown) {\r\n super(message)\r\n this.name = \"TwError\"\r\n this.domain = domainOrSource\r\n this.source = domainOrSource as ErrorSource\r\n this.code = code\r\n this.originalCause = cause\r\n if (Error.captureStackTrace) Error.captureStackTrace(this, TwError)\r\n }\r\n\r\n static fromIo(code: string, message: string): TwError {\r\n return new TwError(\"io\", code, message)\r\n }\r\n\r\n static fromCompile(code: string, message: string): TwError {\r\n return new TwError(\"compile\", code, message)\r\n }\r\n\r\n static fromRust(err: { code?: string; message?: string } | Error | unknown): TwError {\r\n if (err instanceof TwError) return err\r\n if (err instanceof Error) return new TwError(\"rust\", \"RUST_ERROR\", err.message, err)\r\n if (err && typeof err === \"object\") {\r\n const e = err as { code?: string; message?: string }\r\n return new TwError(\"rust\", e.code ?? \"RUST_ERROR\", e.message ?? String(err), err)\r\n }\r\n return new TwError(\"rust\", \"RUST_ERROR\", String(err), err)\r\n }\r\n\r\n /** Buat TwError dari ZodError — dukung shape Zod v3 (`errors`) dan v4 (`issues`). */\r\n static fromZod(err: { issues?: ZodLikeIssue[]; errors?: ZodLikeIssue[] }): TwError {\r\n const first = err.issues?.[0] ?? err.errors?.[0]\r\n const path = formatIssuePath(first?.path)\r\n const message = first ? `${path}: ${first.message}` : \"Schema validation failed\"\r\n return new TwError(\"validation\", \"SCHEMA_VALIDATION_FAILED\", message, err)\r\n }\r\n\r\n static wrap(source: string, code: string, err: unknown): TwError {\r\n if (err instanceof TwError) return err\r\n if (err instanceof Error) return new TwError(source, code, err.message, err)\r\n return new TwError(source, code, String(err), err)\r\n }\r\n\r\n override toString(): string {\r\n return `TwError [${this.source}:${this.code}] ${this.message}`\r\n }\r\n\r\n toJSON(): { name: string; source: string; code: string; message: string } {\r\n return { name: this.name, source: this.source, code: this.code, message: this.message }\r\n }\r\n\r\n toCliMessage(): string {\r\n return `[${this.source.toUpperCase()}:${this.code}] ${this.message}`\r\n }\r\n}\r\n\r\nexport function wrapUnknownError(domain: string, code: string, error: unknown): TwError {\r\n return TwError.wrap(domain, code, error)\r\n}\r\n\r\nexport function isTwError(err: unknown): err is TwError {\r\n return err instanceof TwError\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Native binding resolution\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface LoadNativeBindingOptions<T> {\r\n runtimeDir: string\r\n candidates: string[]\r\n isValid: (module: unknown) => module is T\r\n invalidExportMessage: string\r\n}\r\n\r\nexport interface LoadNativeBindingResult<T> {\r\n binding: T | null\r\n loadErrors: Array<{ path: string; message: string }>\r\n loadedPath?: string\r\n}\r\n\r\nexport function loadNativeBinding<T>(options: LoadNativeBindingOptions<T>): LoadNativeBindingResult<T> {\r\n const { runtimeDir, candidates, isValid } = options\r\n const loadErrors: Array<{ path: string; message: string }> = []\r\n\r\n for (const candidate of candidates) {\r\n const candidatePath = path.resolve(runtimeDir, candidate)\r\n try {\r\n if (!fs.existsSync(candidatePath) && !fs.existsSync(candidatePath + \".node\")) {\r\n continue\r\n }\r\n const mod = requireNativeModule(candidatePath)\r\n if (mod && isValid(mod)) {\r\n return { binding: mod, loadErrors, loadedPath: candidatePath }\r\n }\r\n loadErrors.push({ path: candidatePath, message: options.invalidExportMessage })\r\n } catch (e) {\r\n loadErrors.push({ path: candidatePath, message: e instanceof Error ? e.message : String(e) })\r\n }\r\n }\r\n\r\n return { binding: null, loadErrors }\r\n}\r\n\r\nconst _require = createRequire(import.meta.url)\r\n\r\nfunction requireNativeModule(p: string): unknown {\r\n return _require(p)\r\n}\r\n\r\nexport interface ResolveCandidatesOptions {\r\n runtimeDir?: string\r\n envVarNames?: string[]\r\n includeDefaultCandidates?: boolean\r\n enforceNodeExtensionForEnvPath?: boolean\r\n /** @deprecated — ignored, kept for backward compat */\r\n packageName?: string\r\n}\r\n\r\nexport function resolveNativeBindingCandidates(options: ResolveCandidatesOptions): string[] {\r\n const {\r\n envVarNames = [\"TW_NATIVE_PATH\", \"TWS_NATIVE_PATH\"],\r\n includeDefaultCandidates = true,\r\n enforceNodeExtensionForEnvPath = false,\r\n } = options\r\n // Default ke cwd kalau runtimeDir tidak disediakan\r\n const runtimeDir = options.runtimeDir || process.cwd()\r\n const candidates: string[] = []\r\n\r\n for (const envVar of envVarNames) {\r\n const envPath = process.env[envVar]\r\n if (envPath) {\r\n candidates.push(enforceNodeExtensionForEnvPath && !envPath.endsWith(\".node\") ? envPath + \".node\" : envPath)\r\n }\r\n }\r\n\r\n if (!includeDefaultCandidates) return candidates\r\n\r\n if (fs.existsSync(runtimeDir)) {\r\n try {\r\n for (const entry of fs.readdirSync(runtimeDir)) {\r\n if (entry.endsWith(\".node\")) candidates.push(entry)\r\n }\r\n } catch { /* ignore read errors */ }\r\n }\r\n\r\n const BINARY_NAMES = [\"tailwind-styled-native\", \"tailwind_styled_parser\"]\r\n const napiPlatform = process.platform === \"linux\" && process.arch === \"x64\" ? \"linux-x64-gnu\"\r\n : process.platform === \"linux\" && process.arch === \"arm64\" ? \"linux-arm64-gnu\"\r\n : `${process.platform}-${process.arch}`\r\n\r\n for (const bin of BINARY_NAMES) {\r\n candidates.push(path.resolve(runtimeDir, `${bin}.node`))\r\n candidates.push(path.resolve(runtimeDir, `${bin}.${napiPlatform}.node`))\r\n // 4 level: dist/ → package/ → domain/ → packages/ → repo-root/\r\n candidates.push(path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", `${bin}.node`))\r\n candidates.push(path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", `${bin}.${napiPlatform}.node`))\r\n // 3 level fallback\r\n candidates.push(path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", `${bin}.node`))\r\n candidates.push(path.resolve(process.cwd(), \"native\", `${bin}.node`))\r\n candidates.push(path.resolve(process.cwd(), \"native\", `${bin}.${napiPlatform}.node`))\r\n }\r\n\r\n return Array.from(new Set(candidates))\r\n}\r\n\r\nexport function resolveRuntimeDir(dir: string | undefined, importMetaUrl: string): string {\r\n if (dir) return path.resolve(dir)\r\n try {\r\n return path.dirname(fileURLToPath(importMetaUrl))\r\n } catch {\r\n return process.cwd()\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Hashing\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport function hashContent(content: string, algorithm: string = \"md5\", length?: number): string {\r\n const hash = createHash(algorithm).update(content).digest(\"hex\")\r\n return length ? hash.slice(0, length) : hash\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Error formatting\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport function formatErrorMessage(error: unknown): string {\r\n if (error instanceof TwError) return error.toString()\r\n if (error instanceof Error) return error.message\r\n return String(error)\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// LRU Cache\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport class LRUCache<K, V> {\r\n private capacity: number\r\n private cache: Map<K, V>\r\n\r\n constructor(capacity: number) {\r\n this.capacity = capacity\r\n this.cache = new Map()\r\n }\r\n\r\n get(key: K): V | undefined {\r\n if (!this.cache.has(key)) return undefined\r\n const value = this.cache.get(key)!\r\n this.cache.delete(key)\r\n this.cache.set(key, value)\r\n return value\r\n }\r\n\r\n set(key: K, value: V): void {\r\n if (this.cache.has(key)) {\r\n this.cache.delete(key)\r\n } else if (this.cache.size >= this.capacity) {\r\n const firstKey = this.cache.keys().next().value\r\n if (firstKey !== undefined) {\r\n this.cache.delete(firstKey)\r\n }\r\n }\r\n this.cache.set(key, value)\r\n }\r\n\r\n delete(key: K): boolean {\r\n return this.cache.delete(key)\r\n }\r\n\r\n has(key: K): boolean {\r\n return this.cache.has(key)\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear()\r\n }\r\n\r\n entries(): IterableIterator<[K, V]> {\r\n return this.cache.entries()\r\n }\r\n\r\n get size(): number {\r\n return this.cache.size\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Trace Utilities\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type { TraceSnapshot, TraceSummary } from \"./trace\"\r\nexport {\r\n getHealthColor,\r\n getModeColor,\r\n formatMemory,\r\n formatDuration,\r\n calculateHealth,\r\n getBuildTimeColor,\r\n getMemoryColor,\r\n createTraceSnapshot,\r\n getPipelinePercentages,\r\n} from \"./trace\"\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Performance Telemetry\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Error Codes\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type { ErrorCode } from \"./error-codes\"\r\nexport { ERROR_CODES, getSuggestion, formatErrorCode } from \"./error-codes\"\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Tailwind Compatibility\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type { TailwindInfo } from \"./compatibility\"\r\nexport { detectTailwind, assertTailwindV4, getTailwindVersion, isTailwindV4 } from \"./compatibility\"\r\n\r\n// ── Native binding schemas (Zod boundary validation)\r\nexport {\r\n NativeScanFileSchema, NativeScanResultSchema,\r\n NativeAnalyzerReportSchema, NativeTransformResultSchema,\r\n NativeCssCompileResultSchema, NativeWatchResultSchema,\r\n NativeCacheEntrySchema, NativeCacheReadResultSchema,\r\n safeParseNative, parseNative,\r\n} from './native-schemas'\r\n\r\n// ── ESM-safe runtime helpers ──────────────────────────────────────────────\r\nexport {\r\n createEsmRequire,\r\n getDirname,\r\n getFilename,\r\n resolveFromRoot,\r\n tryRequire,\r\n resolveNativeNodePath,\r\n} from \"./esmHelpers\"\r\n\r\n// ── Performance telemetry ────────────────────────────────────────────────────\r\nexport {\r\n TelemetryCollector,\r\n getGlobalTelemetry,\r\n resetGlobalTelemetry,\r\n createBuildTimer,\r\n type BuildTelemetry,\r\n type TelemetrySummary,\r\n type BuildPhases,\r\n} from \"./telemetry\"\r\n\r\n// ── Config/JSON schema validation ─────────────────────────────────────────────\r\nexport {\r\n ScanCacheSchema,\r\n ScanCacheClassEntrySchema,\r\n TailwindConfigSchema,\r\n RegistryPluginEntrySchema,\r\n RegistryFileSchema,\r\n PackageJsonSchema,\r\n parseJsonWithSchema,\r\n parseJsonFileWithSchema,\r\n type ScanCache,\r\n type ScanCacheClassEntry,\r\n type TailwindConfig,\r\n type RegistryPluginEntry,\r\n type RegistryFile,\r\n type PackageJson,\r\n} from \"./configSchemas\"\r\n\r\n// ── Worker/bootstrap path resolution ─────────────────────────────────────────\r\nexport {\r\n resolveWorkerPath,\r\n resolveLoaderPath,\r\n type WorkerPathOptions,\r\n type WorkerPathResult,\r\n} from \"./workerResolver\"\r\n\r\n// ── Codegen helpers ───────────────────────────────────────────────────────────\r\nexport {\r\n generateComponentCode,\r\n generateStorybookStory,\r\n generateClassRenameCodemod,\r\n generateBarrelFile,\r\n type ComponentCodegenOptions,\r\n} from \"./codegen\"\r\n\r\n// ── Native binary resolution (QA #1) ─────────────────────────────────────────\r\nexport {\r\n resolveNativeBinary,\r\n formatNativeNotFoundError,\r\n type NativeResolutionResult,\r\n} from \"./native-resolution\"\r\n\r\n// ── Shared observability contract ────────────────────────────────────────────\r\nexport {\r\n createObservabilityClient,\r\n type ClassInspection,\r\n type ClassProperty,\r\n type ClassUsageLocation,\r\n type BuildTrace,\r\n type BuildPhaseTrace,\r\n type DashboardMetrics,\r\n type DashboardSummary,\r\n type ObservabilityClient,\r\n} from \"./observability\"","/**\r\n * tailwind-styled-v5 — Native Rust Bindings\r\n *\r\n * All functions require native Rust bindings.\r\n * Uses @tailwind-styled/shared for native resolution.\r\n */\r\n\r\nimport { dirname } from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\nimport { resolveNativeBinary } from \"@tailwind-styled/shared\"\r\n\r\nconst isBrowser = typeof window !== \"undefined\" || typeof document !== \"undefined\"\r\nconst NATIVE_UNAVAILABLE_MESSAGE =\r\n \"[tailwind-styled/core] Native binding is required but not available.\\n\" +\r\n \"Please ensure you have run: npm run build:rust\"\r\n\r\n// require() is safe here — tsup banner injects CJS-compatible require into ESM output.\r\n// See tsup.config.ts esbuildOptions banner for how this is set up.\r\nconst _loadNative = (path: string): unknown => require(path)\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Type Definitions\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface ParsedClassModifier {\r\n type: \"opacity\" | \"arbitrary\"\r\n value: string\r\n}\r\n\r\nexport interface ParsedClass {\r\n raw: string\r\n base: string\r\n variants: string[]\r\n modifier?: ParsedClassModifier\r\n}\r\n\r\nexport interface ThemeConfig {\r\n colors: Record<string, string>\r\n spacing: Record<string, string>\r\n fonts: Record<string, string>\r\n breakpoints: Record<string, string>\r\n animations: Record<string, string>\r\n raw: Record<string, string>\r\n}\r\n\r\ninterface NativeBinding {\r\n batchSplitClasses?: (input: string[]) => Array<{\r\n variantKey: string\r\n base: string\r\n variants: string[]\r\n isArbitrary: boolean\r\n hasModifier: boolean\r\n }>\r\n compileTheme?: (themeConfig: string) => { css: string; variables: Record<string, string> }\r\n extractCssVars?: (css: string) => Record<string, string>\r\n extractThemeFromCss?: (css: string) => Array<{ key: string; value: string }>\r\n parseCssRules?: (css: string) => Array<{\r\n className: string\r\n property: string\r\n value: string\r\n isImportant: boolean\r\n variants: string[]\r\n specificity: number\r\n }>\r\n parseCssToRules?: (css: string) => string\r\n detectDeadCode?: (css: string, usedClasses: string[]) => string[]\r\n classifyKnownClasses?: (classes: string[]) => Array<{ className: string; category: string }>\r\n detectClassConflicts?: (classes: string) => { conflicts: Array<{ class1: string; class2: string; reason: string }>; conflictedClassNames: string[] }\r\n resolveVariants?: (configJson: string, propsJson: string) => { classes: string; resolvedCount: number }\r\n resolveSimpleVariants?: (base: string | null, variants: Record<string, Record<string, string>>, defaults: Record<string, string>, props: Record<string, string>) => string\r\n /** Parse template literal yang sudah di-join. Menggantikan parseTemplate() di twProxy.ts */\r\n parseTemplate?: (raw: string) => {\r\n base: string\r\n subsJson: string\r\n hasSubs: boolean\r\n }\r\n /** Validate ComponentConfig — menggantikan validateVariantConfig() di cv.ts */\r\n validateVariantConfig?: (configJson: string) => {\r\n valid: boolean\r\n errors: Array<{ errorType: string; key: string; value?: string; message: string }>\r\n warnings: string[]\r\n }\r\n /** Build lookup key untuk generated registry — menggantikan key builder di lookupGenerated() */\r\n buildVariantLookupKey?: (defaultVariantsJson: string, propsJson: string) => string\r\n /** Menggantikan cn() — filter+join class names dalam satu Rust pass. (class_utils.rs) */\r\n resolveClassNames?: (inputs: string[]) => string\r\n /** tw_merge dengan custom separator dan optional debug */\r\n twMergeWithSeparator?: (classString: string, opts: { separator?: string; debug?: boolean }) => string\r\n /** tw_merge_many dengan custom separator */\r\n twMergeManyWithSeparator?: (classStrings: string[], opts: { separator?: string; debug?: boolean }) => string\r\n /** conflict-aware Tailwind class merger — port of tailwind-merge. (tw_merge.rs) */\r\n twMerge?: (classString: string) => string\r\n /** variadic convenience wrapper untuk twMerge. (tw_merge.rs) */\r\n twMergeMany?: (classStrings: string[]) => string\r\n /** Tailwind classes → semicolon-separated inline CSS declarations. (state_css.rs) */\r\n twClassesToCss?: (classes: string) => string\r\n /** Iterative CSS var() chain resolver. (theme.rs) */\r\n resolveThemeValue?: (key: string, rawMapJson: string) => string\r\n /** Parse sub-component block syntax from tw`` template. (tw_merge.rs) */\r\n parseSubcomponentBlocksNapi?: (template: string, componentName: string) => {\r\n baseClasses: string\r\n subMapJson: string\r\n }\r\n /** Menggantikan layoutClassesToCss() — static lookup + split dalam satu Rust pass. (container_query.rs) */\r\n layoutClassesToCss?: (classes: string) => string\r\n /** Menggantikan buildContainerRules() — generate @container CSS rules. (container_query.rs) */\r\n buildContainerRules?: (\r\n id: string,\r\n breakpoints: Array<{ key: string; classes: string }>,\r\n containerName: string | null\r\n ) => string\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Binding Loader\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nlet nativeBinding: NativeBinding | null = null\r\nlet bindingLoadAttempted = false\r\n\r\nconst getBinding = (): NativeBinding => {\r\n if (isBrowser) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE + \"\\n\\nNative bindings are not available in browser. Use the compiled CSS output instead.\")\r\n }\r\n \r\n if (nativeBinding) return nativeBinding\r\n\r\n if (bindingLoadAttempted) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n\r\n bindingLoadAttempted = true\r\n\r\n try {\r\n const runtimeDir = isBrowser ? \"\" : dirname(fileURLToPath(import.meta.url))\r\n const result = isBrowser\r\n ? { path: null, source: \"not-found\", platform: \"browser\", tried: [] }\r\n : resolveNativeBinary(runtimeDir)\r\n\r\n if (result.path && result.path.endsWith(\".node\")) {\r\n const mod = _loadNative(result.path) as NativeBinding\r\n if (mod?.batchSplitClasses) {\r\n nativeBinding = mod\r\n return nativeBinding\r\n }\r\n }\r\n\r\n throw new Error(`${NATIVE_UNAVAILABLE_MESSAGE}\\n\\nTried: ${result.tried.join(\"\\n\")}`)\r\n } catch (err) {\r\n throw err instanceof Error ? err : new Error(String(err))\r\n }\r\n}\r\n\r\nexport const resetNativeBinding = (): void => {\r\n nativeBinding = null\r\n bindingLoadAttempted = false\r\n}\r\n\r\nexport const getNativeBinding = getBinding\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Parser Functions\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nfunction splitClassListNative(input: string): string[] {\r\n const binding = getBinding()\r\n if (!binding?.batchSplitClasses) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n const result = binding.batchSplitClasses([input])\r\n if (result.length === 0) return []\r\n return result[0].base.split(\" \").filter(Boolean)\r\n}\r\n\r\nfunction parseClassTokenNative(rawToken: string): ParsedClass {\r\n const binding = getBinding()\r\n if (!binding?.batchSplitClasses) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n const result = binding.batchSplitClasses([rawToken])\r\n if (result.length === 0) {\r\n return { raw: rawToken, base: rawToken, variants: [] }\r\n }\r\n const r = result[0]\r\n const parsed: ParsedClass = {\r\n raw: r.base,\r\n base: r.base,\r\n variants: r.variants,\r\n }\r\n if (r.hasModifier) {\r\n const opacityMatch = r.base.match(/^(.*)\\/(\\d{1,3})$/)\r\n if (opacityMatch && opacityMatch[1].length > 0) {\r\n parsed.base = opacityMatch[1]\r\n parsed.modifier = { type: \"opacity\", value: opacityMatch[2] }\r\n } else {\r\n const arbitraryMatch = r.base.match(/\\((--[a-zA-Z0-9_-]+)\\)/)\r\n if (arbitraryMatch) {\r\n parsed.modifier = { type: \"arbitrary\", value: arbitraryMatch[1] }\r\n }\r\n }\r\n }\r\n return parsed\r\n}\r\n\r\nexport function splitClassList(input: string): string[] {\r\n return splitClassListNative(input)\r\n}\r\n\r\nexport function parseClassToken(rawToken: string): ParsedClass {\r\n return parseClassTokenNative(rawToken)\r\n}\r\n\r\nexport function parseTailwindClasses(input: string): ParsedClass[] {\r\n const classes = splitClassListNative(input)\r\n return classes.map((c) => parseClassTokenNative(c))\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// CSS/Theme Functions\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport function compileTheme(themeConfig: Record<string, unknown>) {\r\n const binding = getBinding()\r\n if (!binding?.compileTheme) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.compileTheme(JSON.stringify(themeConfig))\r\n}\r\n\r\nexport function extractCssVars(css: string): Record<string, string> {\r\n const binding = getBinding()\r\n if (!binding?.extractCssVars) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.extractCssVars(css)\r\n}\r\n\r\nexport function parseCssRules(css: string) {\r\n const binding = getBinding()\r\n if (!binding?.parseCssRules) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.parseCssRules(css)\r\n}\r\n\r\nexport function parseCssToRules(css: string): string {\r\n const binding = getBinding()\r\n if (!binding?.parseCssToRules) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.parseCssToRules(css)\r\n}\r\n\r\nexport function detectDeadCode(css: string, usedClasses: string[]): string[] {\r\n const binding = getBinding()\r\n if (!binding?.detectDeadCode) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.detectDeadCode(css, usedClasses)\r\n}\r\n\r\nexport function classifyKnownClasses(classes: string[]) {\r\n const binding = getBinding()\r\n if (!binding?.classifyKnownClasses) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.classifyKnownClasses(classes)\r\n}\r\n\r\nexport function detectClassConflicts(classes: string[]) {\r\n const binding = getBinding()\r\n if (!binding?.detectClassConflicts) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n return binding.detectClassConflicts(classes.join(\" \"))\r\n}\r\n\r\nexport function extractThemeFromCSS(cssContent: string): ThemeConfig {\r\n const binding = getBinding()\r\n if (!binding?.parseCssRules) {\r\n throw new Error(NATIVE_UNAVAILABLE_MESSAGE)\r\n }\r\n \r\n const properties = binding.parseCssRules(cssContent)\r\n const theme: ThemeConfig = {\r\n colors: {},\r\n spacing: {},\r\n fonts: {},\r\n breakpoints: {},\r\n animations: {},\r\n raw: {},\r\n }\r\n \r\n const prefixMap: Record<string, keyof ThemeConfig> = {\r\n \"color-\": \"colors\",\r\n \"spacing-\": \"spacing\",\r\n \"font-\": \"fonts\",\r\n \"breakpoint-\": \"breakpoints\",\r\n \"animate-\": \"animations\",\r\n }\r\n \r\n for (const prop of properties) {\r\n const cssVar = prop.property.replace(/^--/, \"\")\r\n theme.raw[cssVar] = prop.value\r\n \r\n for (const [prefix, category] of Object.entries(prefixMap)) {\r\n if (cssVar.startsWith(prefix)) {\r\n const name = cssVar.slice(prefix.length)\r\n if (name) theme[category][name] = prop.value\r\n break\r\n }\r\n }\r\n }\r\n \r\n return theme\r\n}","/**\r\n * tailwind-styled-v4 — createTwMerge()\r\n *\r\n * Native-only: uses Rust `tw_merge_many`; requires native binding.\r\n * No JS fallback — tailwind-merge removed from bundle.\r\n */\r\n\r\nimport { getNativeBinding } from \"./native\"\r\n\r\nimport type { ThemeConfig } from \"./themeReader\"\r\n\r\nexport interface MergeOptions {\r\n prefix?: string\r\n separator?: string\r\n theme?: ThemeConfig\r\n}\r\n\r\nfunction normalizeClassInput(classLists: Array<string | undefined | null | false>): string[] {\r\n return classLists\r\n .filter(Boolean)\r\n .map((v) => String(v).trim())\r\n .filter((v) => v.length > 0)\r\n}\r\n\r\n/**\r\n * createTwMerge — returns a conflict-aware merge function.\r\n * Native-only: uses Rust `tw_merge_many`; throws if native binding unavailable.\r\n *\r\n * Note: `prefix` and `separator` options are not supported in native mode\r\n * (Tailwind v3/v4 defaults are used).\r\n */\r\nexport function createTwMerge(_options: MergeOptions = {}) {\r\n return function twMerge(...classLists: Array<string | undefined | null | false>): string {\r\n const clean = normalizeClassInput(classLists)\r\n if (clean.length === 0) return \"\"\r\n\r\n const native = getNativeBinding()\r\n if (!native?.twMergeMany) {\r\n throw new Error(\"FATAL: Native binding 'twMergeMany' is required but not available.\")\r\n }\r\n return native.twMergeMany(clean)\r\n }\r\n}\r\n\r\nexport const twMerge = createTwMerge()\r\n\r\nexport function mergeWithRules(\r\n rules: Record<string, (classes: string[]) => string>,\r\n ...classLists: string[]\r\n): string {\r\n const base = twMerge(...classLists)\r\n const classes = Object.values(rules).reduce(\r\n (acc, rule) => twMerge(rule(acc)).split(/\\s+/).filter(Boolean),\r\n base.split(/\\s+/).filter(Boolean)\r\n )\r\n\r\n return classes.join(\" \")\r\n}\r\n","/**\r\n * tailwind-styled-v4 — Svelte Adapter\r\n *\r\n * Svelte 4+ adapter menggunakan action dan store pattern.\r\n * Kompatibel dengan Svelte 5 runes mode.\r\n *\r\n * Dua pola yang didukung:\r\n * 1. `tw()` — buat class string resolver untuk dipakai di template\r\n * 2. `createSvelteComponent()` — buat Svelte component via Svelte API\r\n *\r\n * @example\r\n * <!-- Pola 1: tw() sebagai class resolver -->\r\n * <script>\r\n * import { cv } from '@tailwind-styled/svelte'\r\n *\r\n * const buttonClass = cv({\r\n * base: 'px-4 py-2 rounded font-medium',\r\n * variants: {\r\n * intent: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },\r\n * size: { sm: 'h-8 text-sm', lg: 'h-12 text-lg' },\r\n * },\r\n * defaultVariants: { intent: 'primary', size: 'sm' },\r\n * })\r\n *\r\n * export let intent = 'primary'\r\n * export let size = 'sm'\r\n * </script>\r\n *\r\n * <button class={buttonClass({ intent, size })}>\r\n * <slot />\r\n * </button>\r\n */\r\n\r\nimport { twMerge } from \"@tailwind-styled/core\"\r\n\r\nimport type { VariantValue, VariantProps as Props } from '@tailwind-styled/shared'\r\n\r\n\r\nconst toClassName = (value: VariantValue): string | undefined => {\r\n if (typeof value === \"string\") return value\r\n if (typeof value === \"number\") return String(value)\r\n return undefined\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Types\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SvelteComponentConfig {\r\n base?: string\r\n variants?: Record<string, Record<string, string>>\r\n compoundVariants?: Array<{ class: string; [key: string]: VariantValue }>\r\n defaultVariants?: Record<string, string>\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Variant resolvers (framework-agnostic core)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nfunction resolveVariants(\r\n variants: Record<string, Record<string, string>>,\r\n props: Props,\r\n defaults: Record<string, string> = {}\r\n): string {\r\n const classes: string[] = []\r\n for (const key in variants) {\r\n const val = props[key] ?? defaults[key]\r\n if (val !== undefined && variants[key]?.[String(val)]) {\r\n classes.push(variants[key][String(val)])\r\n }\r\n }\r\n return classes.join(\" \")\r\n}\r\n\r\nfunction resolveCompound(\r\n compounds: Array<{ class: string; [key: string]: VariantValue }>,\r\n props: Props\r\n): string {\r\n return compounds\r\n .filter((c) => {\r\n const { class: _cls, ...conditions } = c\r\n return Object.entries(conditions).every(([k, v]) => props[k] === v)\r\n })\r\n .map((c) => c.class)\r\n .join(\" \")\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// cv() — Class Variant (utama untuk Svelte)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Class variant resolver — pola utama untuk Svelte.\r\n * Returns fungsi yang menerima props dan mengembalikan class string.\r\n *\r\n * Gunakan ini di `<script>` block, lalu panggil di template.\r\n *\r\n * @example\r\n * const button = cv({\r\n * base: 'px-4 py-2 rounded',\r\n * variants: { size: { sm: 'h-8', lg: 'h-12' } },\r\n * })\r\n *\r\n * // Di template: class={button({ size: 'lg' })}\r\n */\r\nexport function cv(config: SvelteComponentConfig) {\r\n return (props: Props = {}) => {\r\n const { base = \"\", variants = {}, compoundVariants = [], defaultVariants = {} } = config\r\n\r\n const merged = { ...defaultVariants, ...props }\r\n const variantClasses = resolveVariants(variants, merged, defaultVariants)\r\n const compoundClasses = resolveCompound(compoundVariants, merged)\r\n\r\n return twMerge(base, variantClasses, compoundClasses, toClassName(props.class))\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// tw() — Simple class merger dengan template literal support\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Simple class merger — berguna untuk merge class dinamis.\r\n *\r\n * @example\r\n * import { tw } from '@tailwind-styled/svelte'\r\n *\r\n * // Merge class string\r\n * const cls = tw('px-4 py-2', isActive && 'bg-blue-500', className)\r\n */\r\nexport function tw(...classes: Array<string | boolean | null | undefined>): string {\r\n return twMerge(...(classes.filter(Boolean) as string[]))\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Svelte action — use:styled\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Svelte action untuk menerapkan class dinamis.\r\n *\r\n * @example\r\n * <script>\r\n * import { styled } from '@tailwind-styled/svelte'\r\n * const buttonConfig = { base: 'px-4 py-2', variants: { size: { sm: 'h-8', lg: 'h-12' } } }\r\n * </script>\r\n *\r\n * <button use:styled={{ config: buttonConfig, props: { size: 'lg' } }}>\r\n * Click me\r\n * </button>\r\n */\r\nexport function styled(\r\n node: HTMLElement,\r\n { config, props = {} }: { config: SvelteComponentConfig; props?: Props }\r\n) {\r\n const _resolver = cv(config)\r\n\r\n function update({\r\n config: cfg,\r\n props: p = {},\r\n }: {\r\n config: SvelteComponentConfig\r\n props?: Props\r\n }) {\r\n const classes = cv(cfg)(p)\r\n // Hapus class lama yang dikelola action, tambah yang baru\r\n node.className = classes\r\n }\r\n\r\n // Apply initial classes\r\n update({ config, props })\r\n\r\n return {\r\n update,\r\n destroy() {\r\n // Cleanup tidak diperlukan\r\n },\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// createVariants() — Svelte 5 runes compatible\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Svelte 5 runes-compatible variant factory.\r\n * Mengembalikan reactive class string menggunakan $derived pattern.\r\n *\r\n * @example\r\n * // Dalam Svelte 5 component:\r\n * const { className, variantProps } = createVariants({\r\n * base: 'px-4 py-2',\r\n * variants: { size: { sm: 'h-8', lg: 'h-12' } },\r\n * }, () => ({ size: currentSize }))\r\n *\r\n * // className adalah fungsi yang return class string terkini\r\n */\r\nexport function createVariants(config: SvelteComponentConfig, getProps: () => Props) {\r\n const resolver = cv(config)\r\n\r\n return {\r\n className: () => resolver(getProps()),\r\n config,\r\n }\r\n}\r\n"]}
|
package/dist/syntax.js
CHANGED
|
@@ -11,7 +11,6 @@ var path__default = /*#__PURE__*/_interopDefault(path);
|
|
|
11
11
|
|
|
12
12
|
/* tailwind-styled-v4 v5.0.4 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
13
13
|
|
|
14
|
-
var VALID_CLASS_RE = /^[-a-z0-9:/[\]!.()+%]+$/;
|
|
15
14
|
function getRuntimeDir() {
|
|
16
15
|
if (typeof __dirname !== "undefined") return __dirname;
|
|
17
16
|
if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('syntax.js', document.baseURI).href)) }) !== "undefined" && (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('syntax.js', document.baseURI).href))) {
|
|
@@ -42,16 +41,26 @@ function getNativeBridge() {
|
|
|
42
41
|
const runtimeDir = getRuntimeDir();
|
|
43
42
|
const candidates = [
|
|
44
43
|
"@tailwind-styled/native",
|
|
44
|
+
// runtimeDir = dist/ → naik 4 level ke repo root
|
|
45
|
+
path__default.default.resolve(process.cwd(), "native", "tailwind-styled-native.node"),
|
|
46
|
+
path__default.default.resolve(process.cwd(), "native", `tailwind-styled-native.${process.platform}-${process.arch}.node`),
|
|
47
|
+
path__default.default.resolve(process.cwd(), "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
48
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "..", "native", "tailwind-styled-native.node"),
|
|
49
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "..", "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
50
|
+
// 3 level fallback
|
|
51
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", "tailwind-styled-native.node"),
|
|
52
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
53
|
+
// backward compat
|
|
45
54
|
path__default.default.resolve(process.cwd(), "native", "index.mjs"),
|
|
46
|
-
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", "index.mjs"),
|
|
47
55
|
path__default.default.resolve(runtimeDir, "..", "..", "..", "..", "native", "index.mjs"),
|
|
56
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", "index.mjs"),
|
|
48
57
|
path__default.default.resolve(process.cwd(), "native", "index.node"),
|
|
49
|
-
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", "index.node"),
|
|
50
|
-
path__default.default.resolve(runtimeDir, "..", "..", "..", "
|
|
58
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "..", "native", "index.node"),
|
|
59
|
+
path__default.default.resolve(runtimeDir, "..", "..", "..", "native", "index.node")
|
|
51
60
|
];
|
|
52
61
|
for (const candidate of candidates) {
|
|
53
62
|
const loaded = tryRequire(candidate);
|
|
54
|
-
if (loaded?.
|
|
63
|
+
if (loaded?.extractClassesFromSource) {
|
|
55
64
|
bridgeState.current = loaded;
|
|
56
65
|
return bridgeState.current;
|
|
57
66
|
}
|
|
@@ -62,28 +71,19 @@ function getNativeBridge() {
|
|
|
62
71
|
);
|
|
63
72
|
}
|
|
64
73
|
function extractAllClasses(source) {
|
|
65
|
-
const result = getNativeBridge().
|
|
74
|
+
const result = getNativeBridge().extractClassesFromSource?.(source);
|
|
66
75
|
if (result === null || result === void 0) {
|
|
67
76
|
throw new Error("[tailwind-styled/syntax] Native extractClassesFromSource returned null.");
|
|
68
77
|
}
|
|
69
|
-
|
|
78
|
+
const arr = Array.isArray(result) ? result : typeof result === "string" ? JSON.parse(result) : Array.from(result);
|
|
79
|
+
return arr.sort();
|
|
70
80
|
}
|
|
71
81
|
function parseClasses(raw) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return bridge.parseClassesFromStringNative(raw);
|
|
76
|
-
}
|
|
77
|
-
} catch {
|
|
78
|
-
}
|
|
79
|
-
const parsed = [];
|
|
80
|
-
for (const token of raw.split(/[\n\s]+/)) {
|
|
81
|
-
if (!token) continue;
|
|
82
|
-
const normalized = token.trim();
|
|
83
|
-
if (!normalized || !VALID_CLASS_RE.test(normalized)) continue;
|
|
84
|
-
parsed.push(normalized);
|
|
82
|
+
const bridge = getNativeBridge();
|
|
83
|
+
if (!bridge?.parseClassesFromString) {
|
|
84
|
+
throw new Error("FATAL: Native binding 'parseClassesFromString' is required but not available.");
|
|
85
85
|
}
|
|
86
|
-
return
|
|
86
|
+
return bridge.parseClassesFromString(raw);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
exports.extractAllClasses = extractAllClasses;
|
package/dist/syntax.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/domain/syntax/src/index.ts"],"names":["path","fileURLToPath","createRequire"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../packages/domain/syntax/src/index.ts"],"names":["path","fileURLToPath","createRequire"],"mappings":"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","file":"syntax.js","sourcesContent":["import { createRequire } from \"node:module\"\r\nimport path from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\n\r\ninterface NativeSyntaxBridge {\r\n extractClassesFromSource?: (source: string) => string[] | null\r\n parseClassesFromString?: (raw: string) => string[]\r\n}\r\n\r\nconst VALID_CLASS_RE = /^[-a-z0-9:/[\\]!.()+%]+$/\r\n\r\nfunction getRuntimeDir(): string {\r\n if (typeof __dirname !== \"undefined\") return __dirname\r\n if (typeof import.meta !== \"undefined\" && import.meta.url) {\r\n return path.dirname(fileURLToPath(import.meta.url))\r\n }\r\n return process.cwd()\r\n}\r\n\r\nfunction tryRequire(id: string): NativeSyntaxBridge | null {\r\n try {\r\n const runtimeDir = getRuntimeDir()\r\n const requireFromRuntime =\r\n typeof module !== \"undefined\" && typeof module.require === \"function\"\r\n ? module.require.bind(module)\r\n : createRequire(path.join(runtimeDir, \"noop.cjs\"))\r\n const loaded = requireFromRuntime(id) as NativeSyntaxBridge\r\n return loaded ?? null\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nconst bridgeState = { current: undefined as NativeSyntaxBridge | null | undefined }\r\n\r\nfunction getNativeBridge(): NativeSyntaxBridge {\r\n if (bridgeState.current !== undefined) {\r\n if (bridgeState.current === null) {\r\n throw new Error(\r\n \"[tailwind-styled/syntax] Native syntax binding is required but not available.\"\r\n )\r\n }\r\n return bridgeState.current\r\n }\r\n\r\n const runtimeDir = getRuntimeDir()\r\n const candidates = [\r\n \"@tailwind-styled/native\",\r\n // runtimeDir = dist/ → naik 4 level ke repo root\r\n path.resolve(process.cwd(), \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(process.cwd(), \"native\", `tailwind-styled-native.${process.platform}-${process.arch}.node`),\r\n path.resolve(process.cwd(), \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n // 3 level fallback\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n // backward compat\r\n path.resolve(process.cwd(), \"native\", \"index.mjs\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"index.mjs\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"index.mjs\"),\r\n path.resolve(process.cwd(), \"native\", \"index.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"index.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"index.node\"),\r\n ]\r\n\r\n for (const candidate of candidates) {\r\n const loaded = tryRequire(candidate)\r\n if (loaded?.extractClassesFromSource) {\r\n bridgeState.current = loaded\r\n return bridgeState.current\r\n }\r\n }\r\n\r\n bridgeState.current = null\r\n throw new Error(\r\n \"[tailwind-styled/syntax] Native syntax binding not found. Run `npm run build:rust` first.\"\r\n )\r\n}\r\n\r\nexport function extractAllClasses(source: string): string[] {\r\n const result = getNativeBridge().extractClassesFromSource?.(source)\r\n if (result === null || result === undefined) {\r\n throw new Error(\"[tailwind-styled/syntax] Native extractClassesFromSource returned null.\")\r\n }\r\n // NAPI kadang return non-Array (misal string JSON) — normalize dulu sebelum sort\r\n const arr: string[] = Array.isArray(result)\r\n ? result\r\n : typeof result === \"string\"\r\n ? (JSON.parse(result) as string[])\r\n : Array.from(result as Iterable<string>)\r\n return arr.sort()\r\n}\r\n\r\nexport function parseClasses(raw: string): string[] {\r\n const bridge = getNativeBridge()\r\n if (!bridge?.parseClassesFromString) {\r\n throw new Error(\"FATAL: Native binding 'parseClassesFromString' is required but not available.\")\r\n }\r\n return (bridge.parseClassesFromString as (r: string) => string[])(raw)\r\n}"]}
|
package/dist/syntax.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
|
|
5
5
|
/* tailwind-styled-v4 v5.0.4 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
6
6
|
|
|
7
|
-
var VALID_CLASS_RE = /^[-a-z0-9:/[\]!.()+%]+$/;
|
|
8
7
|
function getRuntimeDir() {
|
|
9
8
|
if (typeof __dirname !== "undefined") return __dirname;
|
|
10
9
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
@@ -35,16 +34,26 @@ function getNativeBridge() {
|
|
|
35
34
|
const runtimeDir = getRuntimeDir();
|
|
36
35
|
const candidates = [
|
|
37
36
|
"@tailwind-styled/native",
|
|
37
|
+
// runtimeDir = dist/ → naik 4 level ke repo root
|
|
38
|
+
path.resolve(process.cwd(), "native", "tailwind-styled-native.node"),
|
|
39
|
+
path.resolve(process.cwd(), "native", `tailwind-styled-native.${process.platform}-${process.arch}.node`),
|
|
40
|
+
path.resolve(process.cwd(), "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
41
|
+
path.resolve(runtimeDir, "..", "..", "..", "..", "native", "tailwind-styled-native.node"),
|
|
42
|
+
path.resolve(runtimeDir, "..", "..", "..", "..", "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
43
|
+
// 3 level fallback
|
|
44
|
+
path.resolve(runtimeDir, "..", "..", "..", "native", "tailwind-styled-native.node"),
|
|
45
|
+
path.resolve(runtimeDir, "..", "..", "..", "native", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),
|
|
46
|
+
// backward compat
|
|
38
47
|
path.resolve(process.cwd(), "native", "index.mjs"),
|
|
39
|
-
path.resolve(runtimeDir, "..", "..", "..", "native", "index.mjs"),
|
|
40
48
|
path.resolve(runtimeDir, "..", "..", "..", "..", "native", "index.mjs"),
|
|
49
|
+
path.resolve(runtimeDir, "..", "..", "..", "native", "index.mjs"),
|
|
41
50
|
path.resolve(process.cwd(), "native", "index.node"),
|
|
42
|
-
path.resolve(runtimeDir, "..", "..", "..", "native", "index.node"),
|
|
43
|
-
path.resolve(runtimeDir, "..", "..", "..", "
|
|
51
|
+
path.resolve(runtimeDir, "..", "..", "..", "..", "native", "index.node"),
|
|
52
|
+
path.resolve(runtimeDir, "..", "..", "..", "native", "index.node")
|
|
44
53
|
];
|
|
45
54
|
for (const candidate of candidates) {
|
|
46
55
|
const loaded = tryRequire(candidate);
|
|
47
|
-
if (loaded?.
|
|
56
|
+
if (loaded?.extractClassesFromSource) {
|
|
48
57
|
bridgeState.current = loaded;
|
|
49
58
|
return bridgeState.current;
|
|
50
59
|
}
|
|
@@ -55,28 +64,19 @@ function getNativeBridge() {
|
|
|
55
64
|
);
|
|
56
65
|
}
|
|
57
66
|
function extractAllClasses(source) {
|
|
58
|
-
const result = getNativeBridge().
|
|
67
|
+
const result = getNativeBridge().extractClassesFromSource?.(source);
|
|
59
68
|
if (result === null || result === void 0) {
|
|
60
69
|
throw new Error("[tailwind-styled/syntax] Native extractClassesFromSource returned null.");
|
|
61
70
|
}
|
|
62
|
-
|
|
71
|
+
const arr = Array.isArray(result) ? result : typeof result === "string" ? JSON.parse(result) : Array.from(result);
|
|
72
|
+
return arr.sort();
|
|
63
73
|
}
|
|
64
74
|
function parseClasses(raw) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return bridge.parseClassesFromStringNative(raw);
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
}
|
|
72
|
-
const parsed = [];
|
|
73
|
-
for (const token of raw.split(/[\n\s]+/)) {
|
|
74
|
-
if (!token) continue;
|
|
75
|
-
const normalized = token.trim();
|
|
76
|
-
if (!normalized || !VALID_CLASS_RE.test(normalized)) continue;
|
|
77
|
-
parsed.push(normalized);
|
|
75
|
+
const bridge = getNativeBridge();
|
|
76
|
+
if (!bridge?.parseClassesFromString) {
|
|
77
|
+
throw new Error("FATAL: Native binding 'parseClassesFromString' is required but not available.");
|
|
78
78
|
}
|
|
79
|
-
return
|
|
79
|
+
return bridge.parseClassesFromString(raw);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export { extractAllClasses, parseClasses };
|
package/dist/syntax.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/domain/syntax/src/index.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"sources":["../packages/domain/syntax/src/index.ts"],"names":[],"mappings":";;;;;;AAWA,SAAS,aAAA,GAAwB;AAC/B,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,EAAa,OAAO,SAAA;AAC7C,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AACzD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,QAAQ,GAAA,EAAI;AACrB;AAEA,SAAS,WAAW,EAAA,EAAuC;AACzD,EAAA,IAAI;AACF,IAAA,MAAM,aAAa,aAAA,EAAc;AACjC,IAAA,MAAM,qBACJ,OAAO,MAAA,KAAW,eAAe,OAAO,MAAA,CAAO,YAAY,UAAA,GACvD,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,GAC1B,aAAA,CAAc,KAAK,IAAA,CAAK,UAAA,EAAY,UAAU,CAAC,CAAA;AACrD,IAAA,MAAM,MAAA,GAAS,mBAAmB,EAAE,CAAA;AACpC,IAAA,OAAO,MAAA,IAAU,IAAA;AAAA,EACnB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,IAAM,WAAA,GAAc,EAAE,OAAA,EAAS,MAAA,EAAmD;AAElF,SAAS,eAAA,GAAsC;AAC7C,EAAA,IAAI,WAAA,CAAY,YAAY,MAAA,EAAW;AACrC,IAAA,IAAI,WAAA,CAAY,YAAY,IAAA,EAAM;AAChC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,WAAA,CAAY,OAAA;AAAA,EACrB;AAEA,EAAA,MAAM,aAAa,aAAA,EAAc;AACjC,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,yBAAA;AAAA;AAAA,IAEA,KAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,UAAU,6BAA6B,CAAA;AAAA,IACnE,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAA,EAAU,CAAA,uBAAA,EAA0B,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAI,CAAA,KAAA,CAAO,CAAA;AAAA,IACvG,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAA,EAAU,CAAA,uBAAA,EAA0B,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAI,CAAA,SAAA,CAAW,CAAA;AAAA,IAC3G,IAAA,CAAK,QAAQ,UAAA,EAAY,IAAA,EAAM,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,6BAA6B,CAAA;AAAA,IACxF,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,IAAA,EAAM,MAAM,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA,uBAAA,EAA0B,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAI,CAAA,SAAA,CAAW,CAAA;AAAA;AAAA,IAEhI,KAAK,OAAA,CAAQ,UAAA,EAAY,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,6BAA6B,CAAA;AAAA,IAClF,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA,uBAAA,EAA0B,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAI,CAAA,SAAA,CAAW,CAAA;AAAA;AAAA,IAE1H,KAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,UAAU,WAAW,CAAA;AAAA,IACjD,IAAA,CAAK,QAAQ,UAAA,EAAY,IAAA,EAAM,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,WAAW,CAAA;AAAA,IACtE,KAAK,OAAA,CAAQ,UAAA,EAAY,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,WAAW,CAAA;AAAA,IAChE,KAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,UAAU,YAAY,CAAA;AAAA,IAClD,IAAA,CAAK,QAAQ,UAAA,EAAY,IAAA,EAAM,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,YAAY,CAAA;AAAA,IACvE,KAAK,OAAA,CAAQ,UAAA,EAAY,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,YAAY;AAAA,GACnE;AAEA,EAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,IAAA,MAAM,MAAA,GAAS,WAAW,SAAS,CAAA;AACnC,IAAA,IAAI,QAAQ,wBAAA,EAA0B;AACpC,MAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AACtB,MAAA,OAAO,WAAA,CAAY,OAAA;AAAA,IACrB;AAAA,EACF;AAEA,EAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,EAAA,MAAM,IAAI,KAAA;AAAA,IACR;AAAA,GACF;AACF;AAEO,SAAS,kBAAkB,MAAA,EAA0B;AAC1D,EAAA,MAAM,MAAA,GAAS,eAAA,EAAgB,CAAE,wBAAA,GAA2B,MAAM,CAAA;AAClE,EAAA,IAAI,MAAA,KAAW,IAAA,IAAQ,MAAA,KAAW,MAAA,EAAW;AAC3C,IAAA,MAAM,IAAI,MAAM,yEAAyE,CAAA;AAAA,EAC3F;AAEA,EAAA,MAAM,GAAA,GAAgB,KAAA,CAAM,OAAA,CAAQ,MAAM,IACtC,MAAA,GACA,OAAO,MAAA,KAAW,QAAA,GACf,KAAK,KAAA,CAAM,MAAM,CAAA,GAClB,KAAA,CAAM,KAAK,MAA0B,CAAA;AAC3C,EAAA,OAAO,IAAI,IAAA,EAAK;AAClB;AAEO,SAAS,aAAa,GAAA,EAAuB;AAClD,EAAA,MAAM,SAAS,eAAA,EAAgB;AAC/B,EAAA,IAAI,CAAC,QAAQ,sBAAA,EAAwB;AACnC,IAAA,MAAM,IAAI,MAAM,+EAA+E,CAAA;AAAA,EACjG;AACA,EAAA,OAAQ,MAAA,CAAO,uBAAmD,GAAG,CAAA;AACvE","file":"syntax.mjs","sourcesContent":["import { createRequire } from \"node:module\"\r\nimport path from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\n\r\ninterface NativeSyntaxBridge {\r\n extractClassesFromSource?: (source: string) => string[] | null\r\n parseClassesFromString?: (raw: string) => string[]\r\n}\r\n\r\nconst VALID_CLASS_RE = /^[-a-z0-9:/[\\]!.()+%]+$/\r\n\r\nfunction getRuntimeDir(): string {\r\n if (typeof __dirname !== \"undefined\") return __dirname\r\n if (typeof import.meta !== \"undefined\" && import.meta.url) {\r\n return path.dirname(fileURLToPath(import.meta.url))\r\n }\r\n return process.cwd()\r\n}\r\n\r\nfunction tryRequire(id: string): NativeSyntaxBridge | null {\r\n try {\r\n const runtimeDir = getRuntimeDir()\r\n const requireFromRuntime =\r\n typeof module !== \"undefined\" && typeof module.require === \"function\"\r\n ? module.require.bind(module)\r\n : createRequire(path.join(runtimeDir, \"noop.cjs\"))\r\n const loaded = requireFromRuntime(id) as NativeSyntaxBridge\r\n return loaded ?? null\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nconst bridgeState = { current: undefined as NativeSyntaxBridge | null | undefined }\r\n\r\nfunction getNativeBridge(): NativeSyntaxBridge {\r\n if (bridgeState.current !== undefined) {\r\n if (bridgeState.current === null) {\r\n throw new Error(\r\n \"[tailwind-styled/syntax] Native syntax binding is required but not available.\"\r\n )\r\n }\r\n return bridgeState.current\r\n }\r\n\r\n const runtimeDir = getRuntimeDir()\r\n const candidates = [\r\n \"@tailwind-styled/native\",\r\n // runtimeDir = dist/ → naik 4 level ke repo root\r\n path.resolve(process.cwd(), \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(process.cwd(), \"native\", `tailwind-styled-native.${process.platform}-${process.arch}.node`),\r\n path.resolve(process.cwd(), \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n // 3 level fallback\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"tailwind-styled-native.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", `tailwind-styled-native.${process.platform}-${process.arch}-gnu.node`),\r\n // backward compat\r\n path.resolve(process.cwd(), \"native\", \"index.mjs\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"index.mjs\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"index.mjs\"),\r\n path.resolve(process.cwd(), \"native\", \"index.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"..\", \"native\", \"index.node\"),\r\n path.resolve(runtimeDir, \"..\", \"..\", \"..\", \"native\", \"index.node\"),\r\n ]\r\n\r\n for (const candidate of candidates) {\r\n const loaded = tryRequire(candidate)\r\n if (loaded?.extractClassesFromSource) {\r\n bridgeState.current = loaded\r\n return bridgeState.current\r\n }\r\n }\r\n\r\n bridgeState.current = null\r\n throw new Error(\r\n \"[tailwind-styled/syntax] Native syntax binding not found. Run `npm run build:rust` first.\"\r\n )\r\n}\r\n\r\nexport function extractAllClasses(source: string): string[] {\r\n const result = getNativeBridge().extractClassesFromSource?.(source)\r\n if (result === null || result === undefined) {\r\n throw new Error(\"[tailwind-styled/syntax] Native extractClassesFromSource returned null.\")\r\n }\r\n // NAPI kadang return non-Array (misal string JSON) — normalize dulu sebelum sort\r\n const arr: string[] = Array.isArray(result)\r\n ? result\r\n : typeof result === \"string\"\r\n ? (JSON.parse(result) as string[])\r\n : Array.from(result as Iterable<string>)\r\n return arr.sort()\r\n}\r\n\r\nexport function parseClasses(raw: string): string[] {\r\n const bridge = getNativeBridge()\r\n if (!bridge?.parseClassesFromString) {\r\n throw new Error(\"FATAL: Native binding 'parseClassesFromString' is required but not available.\")\r\n }\r\n return (bridge.parseClassesFromString as (r: string) => string[])(raw)\r\n}"]}
|
package/dist/testing.js
CHANGED
|
@@ -56,7 +56,15 @@ function expectNoClasses(element, classes) {
|
|
|
56
56
|
}
|
|
57
57
|
function getClassList(element) {
|
|
58
58
|
if (!element) return [];
|
|
59
|
-
|
|
59
|
+
try {
|
|
60
|
+
const list = Array.from(element.classList);
|
|
61
|
+
if (list.length > 0) return list.sort();
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
if (element.className && typeof element.className === "string") {
|
|
65
|
+
return element.className.trim().split(/\s+/).filter(Boolean).sort();
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
60
68
|
}
|
|
61
69
|
function snapshotVariants(render, variants) {
|
|
62
70
|
return variants.map((variant) => ({
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/domain/testing/src/index.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\r\n * tailwind-styled-v4 — Testing Utilities\r\n *\r\n * Integrasi dengan Jest / Vitest untuk test komponen tw().\r\n *\r\n * @example\r\n * // vitest.config.ts\r\n * import { tailwindStyledSetup } from '@tailwind-styled/testing'\r\n * export default defineConfig({ test: { setupFiles: ['@tailwind-styled/testing/setup'] } })\r\n *\r\n * // Button.test.ts\r\n * import { render } from '@testing-library/react'\r\n * import { expectClasses, getVariantClass } from '@tailwind-styled/testing'\r\n *\r\n * test('Button renders primary variant', () => {\r\n * const { container } = render(<Button intent=\"primary\" />)\r\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white'])\r\n * })\r\n */\r\n\r\n// ─── Jest/Vitest custom matchers ─────────────────────────────────────────────\r\n\r\n/**\r\n * Custom matcher: toHaveClass\r\n * Dipakai langsung atau via expect.extend(tailwindMatchers)\r\n *\r\n * @example\r\n * expect(element).toHaveClass('bg-blue-500')\r\n */\r\nexport function toHaveClass(className: string) {\r\n return (value: { classList?: { contains: (name: string) => boolean } }) => {\r\n const pass = Boolean(value?.classList?.contains(className))\r\n return {\r\n pass,\r\n message: () => `expected element ${pass ? \"not \" : \"\"}to contain class '${className}'`,\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom matcher: toHaveClasses\r\n * Cek beberapa class sekaligus\r\n *\r\n * @example\r\n * expect(element).toHaveClasses(['px-4', 'py-2', 'rounded'])\r\n */\r\nexport function toHaveClasses(classNames: string[]) {\r\n return (value: Element | null | undefined) => {\r\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\r\n const missing = classNames.filter((c) => !value.classList.contains(c))\r\n const pass = missing.length === 0\r\n return {\r\n pass,\r\n message: () =>\r\n pass\r\n ? `expected element not to have all classes: ${classNames.join(\", \")}`\r\n : `expected element to have classes: ${missing.join(\", \")}`,\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom matcher: toNotHaveClass\r\n *\r\n * @example\r\n * expect(element).toNotHaveClass('hidden')\r\n */\r\nexport function toNotHaveClass(className: string) {\r\n return (value: Element | null | undefined) => {\r\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\r\n const pass = !value.classList.contains(className)\r\n return {\r\n pass,\r\n message: () => `expected element ${pass ? \"\" : \"not \"}to have class '${className}'`,\r\n }\r\n }\r\n}\r\n\r\n/** Semua matchers — pakai dengan expect.extend(tailwindMatchers) */\r\nexport const tailwindMatchers = { toHaveClass, toHaveClasses, toNotHaveClass }\r\n\r\n// ─── Helper utilities ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Assert element punya semua class yang diharapkan.\r\n * Lebih ergonomis dari `expect(el).toHaveClasses([...])` untuk banyak class.\r\n *\r\n * @example\r\n * const { container } = render(<Button intent=\"primary\" size=\"lg\" />)\r\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white', 'h-12'])\r\n */\r\nexport function expectClasses(element: Element | null | undefined, classes: string[]): void {\r\n if (!element) throw new Error(\"expectClasses: element is null or undefined\")\r\n const missing = classes.filter((c) => !element.classList.contains(c))\r\n if (missing.length > 0) {\r\n throw new Error(\r\n `Expected element to have classes: ${missing.join(\", \")}\\n` +\r\n ` Actual classes: ${element.className}`\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Assert element tidak punya class tertentu.\r\n *\r\n * @example\r\n * expectNoClasses(container.firstChild, ['opacity-50', 'cursor-not-allowed'])\r\n */\r\nexport function expectNoClasses(element: Element | null | undefined, classes: string[]): void {\r\n if (!element) throw new Error(\"expectNoClasses: element is null or undefined\")\r\n const found = classes.filter((c) => element.classList.contains(c))\r\n if (found.length > 0) {\r\n throw new Error(\r\n `Expected element NOT to have classes: ${found.join(\", \")}\\n` +\r\n ` Actual classes: ${element.className}`\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Extract class list dari element sebagai sorted array.\r\n * Berguna untuk snapshot testing.\r\n *\r\n * @example\r\n * expect(getClassList(element)).toMatchSnapshot()\r\n */\r\nexport function getClassList(element: Element | null | undefined): string[] {\r\n if (!element) return []\r\n return Array.from(element.classList).sort()\r\n}\r\n\r\n// ─── Variant snapshot helpers ────────────────────────────────────────────────\r\n\r\n/**\r\n * Buat snapshot dari semua kombinasi variant.\r\n * Berguna untuk regression testing pada styled components.\r\n *\r\n * @example\r\n * const buttonVariants = snapshotVariants(\r\n * (props) => {\r\n * const { container } = render(<Button {...props} />)\r\n * return container.firstChild?.className ?? ''\r\n * },\r\n * [\r\n * { intent: 'primary', size: 'sm' },\r\n * { intent: 'primary', size: 'lg' },\r\n * { intent: 'danger', size: 'sm' },\r\n * ]\r\n * )\r\n * expect(buttonVariants).toMatchSnapshot()\r\n */\r\nexport function snapshotVariants<T>(render: (variant: T) => string, variants: T[]) {\r\n return variants.map((variant) => ({\r\n variant,\r\n output: render(variant),\r\n }))\r\n}\r\n\r\n/**\r\n * Generate semua kombinasi dari variant matrix.\r\n *\r\n * @example\r\n * const combinations = expandVariantMatrix({\r\n * intent: ['primary', 'danger'],\r\n * size: ['sm', 'md', 'lg'],\r\n * disabled: [true, false],\r\n * })\r\n * // → 2 × 3 × 2 = 12 kombinasi\r\n */\r\nexport function expandVariantMatrix(\r\n matrix: Record<string, Array<string | number | boolean>>\r\n): Array<Record<string, string | number | boolean>> {\r\n const keys = Object.keys(matrix)\r\n if (keys.length === 0) return [{}]\r\n\r\n const result: Array<Record<string, string | number | boolean>> = []\r\n\r\n function walk(index: number, current: Record<string, string | number | boolean>) {\r\n if (index >= keys.length) {\r\n result.push({ ...current })\r\n return\r\n }\r\n const key = keys[index]!\r\n for (const value of matrix[key] ?? []) {\r\n current[key] = value\r\n walk(index + 1, current)\r\n }\r\n }\r\n\r\n walk(0, {})\r\n return result\r\n}\r\n\r\n/**\r\n * Test semua kombinasi variant sekaligus — no missing coverage.\r\n *\r\n * @example\r\n * testAllVariants(\r\n * Button,\r\n * { intent: ['primary','danger'], size: ['sm','lg'] },\r\n * (el, variant) => {\r\n * expect(el).not.toBeNull()\r\n * if (variant.intent === 'primary') expectClasses(el, ['bg-blue-500'])\r\n * }\r\n * )\r\n */\r\nexport function testAllVariants(\r\n matrix: Record<string, Array<string | number | boolean>>,\r\n testFn: (variant: Record<string, string | number | boolean>) => void\r\n): void {\r\n const combinations = expandVariantMatrix(matrix)\r\n for (const variant of combinations) {\r\n testFn(variant)\r\n }\r\n}\r\n\r\n// ─── CSS-in-JS output assertions ─────────────────────────────────────────────\r\n\r\n/**\r\n * Bandingkan dua class string secara semantik (urutan tidak penting).\r\n *\r\n * @example\r\n * expectClassesEqual('px-4 py-2 bg-blue-500', 'bg-blue-500 px-4 py-2') // pass\r\n */\r\nexport function expectClassesEqual(actual: string, expected: string): void {\r\n const actualSet = new Set(actual.trim().split(/\\s+/).filter(Boolean))\r\n const expectedSet = new Set(expected.trim().split(/\\s+/).filter(Boolean))\r\n\r\n const missing = [...expectedSet].filter((c) => !actualSet.has(c))\r\n const extra = [...actualSet].filter((c) => !expectedSet.has(c))\r\n\r\n if (missing.length > 0 || extra.length > 0) {\r\n const parts: string[] = []\r\n if (missing.length > 0) parts.push(`Missing: ${missing.join(\", \")}`)\r\n if (extra.length > 0) parts.push(`Extra: ${extra.join(\", \")}`)\r\n throw new Error(`Class mismatch:\\n ${parts.join(\"\\n \")}`)\r\n }\r\n}\r\n\r\n// ─── Engine metrics matchers ──────────────────────────────────────────────────\r\n\r\nexport interface EngineMetricsSnapshot {\r\n totalFiles?: number\r\n uniqueClasses?: number\r\n buildTimeMs?: number\r\n cssBytes?: number\r\n cacheHits?: number\r\n cacheMisses?: number\r\n incrementalRuns?: number\r\n fullRescans?: number\r\n}\r\n\r\n/**\r\n * Assert engine metrics snapshot meets minimum thresholds.\r\n *\r\n * @example\r\n * const result = await engine.build()\r\n * expectEngineMetrics(metrics.snapshot(), {\r\n * totalFiles: 10,\r\n * buildTimeMs: 5000, // max\r\n * })\r\n */\r\nexport function expectEngineMetrics(\r\n metrics: EngineMetricsSnapshot,\r\n expectations: {\r\n minFiles?: number\r\n maxBuildTimeMs?: number\r\n minUniqueClasses?: number\r\n cacheHitRateMin?: number // 0–1\r\n }\r\n): void {\r\n if (expectations.minFiles !== undefined) {\r\n const actual = metrics.totalFiles ?? 0\r\n if (actual < expectations.minFiles) {\r\n throw new Error(\r\n `Engine metrics: expected at least ${expectations.minFiles} files, got ${actual}`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.maxBuildTimeMs !== undefined) {\r\n const actual = metrics.buildTimeMs ?? 0\r\n if (actual > expectations.maxBuildTimeMs) {\r\n throw new Error(\r\n `Engine metrics: build took ${actual}ms, expected ≤ ${expectations.maxBuildTimeMs}ms`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.minUniqueClasses !== undefined) {\r\n const actual = metrics.uniqueClasses ?? 0\r\n if (actual < expectations.minUniqueClasses) {\r\n throw new Error(\r\n `Engine metrics: expected at least ${expectations.minUniqueClasses} unique classes, got ${actual}`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.cacheHitRateMin !== undefined) {\r\n const hits = metrics.cacheHits ?? 0\r\n const total = hits + (metrics.cacheMisses ?? 0)\r\n const rate = total > 0 ? hits / total : 0\r\n if (rate < expectations.cacheHitRateMin) {\r\n throw new Error(\r\n `Engine metrics: cache hit rate ${(rate * 100).toFixed(1)}%, expected ≥ ${(expectations.cacheHitRateMin * 100).toFixed(1)}%`\r\n )\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom Jest/Vitest matcher: toHaveEngineMetrics\r\n *\r\n * @example\r\n * expect(metrics).toHaveEngineMetrics({ minFiles: 1 })\r\n */\r\nexport function toHaveEngineMetrics(expectations: Parameters<typeof expectEngineMetrics>[1]) {\r\n return (metrics: EngineMetricsSnapshot) => {\r\n try {\r\n expectEngineMetrics(metrics, expectations)\r\n return { pass: true, message: () => \"engine metrics matched expectations\" }\r\n } catch (e: unknown) {\r\n const msg = e instanceof Error ? e.message : String(e)\r\n return { pass: false, message: () => msg }\r\n }\r\n }\r\n}\r\n\r\n/** All matchers including engine metrics */\r\nexport const tailwindMatchersWithMetrics = {\r\n toHaveClass,\r\n toHaveClasses,\r\n toNotHaveClass,\r\n toHaveEngineMetrics,\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../packages/domain/testing/src/index.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\r\n * tailwind-styled-v4 — Testing Utilities\r\n *\r\n * Integrasi dengan Jest / Vitest untuk test komponen tw().\r\n *\r\n * @example\r\n * // vitest.config.ts\r\n * import { tailwindStyledSetup } from '@tailwind-styled/testing'\r\n * export default defineConfig({ test: { setupFiles: ['@tailwind-styled/testing/setup'] } })\r\n *\r\n * // Button.test.ts\r\n * import { render } from '@testing-library/react'\r\n * import { expectClasses, getVariantClass } from '@tailwind-styled/testing'\r\n *\r\n * test('Button renders primary variant', () => {\r\n * const { container } = render(<Button intent=\"primary\" />)\r\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white'])\r\n * })\r\n */\r\n\r\n// ─── Jest/Vitest custom matchers ─────────────────────────────────────────────\r\n\r\n/**\r\n * Custom matcher: toHaveClass\r\n * Dipakai langsung atau via expect.extend(tailwindMatchers)\r\n *\r\n * @example\r\n * expect(element).toHaveClass('bg-blue-500')\r\n */\r\nexport function toHaveClass(className: string) {\r\n return (value: { classList?: { contains: (name: string) => boolean } }) => {\r\n const pass = Boolean(value?.classList?.contains(className))\r\n return {\r\n pass,\r\n message: () => `expected element ${pass ? \"not \" : \"\"}to contain class '${className}'`,\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom matcher: toHaveClasses\r\n * Cek beberapa class sekaligus\r\n *\r\n * @example\r\n * expect(element).toHaveClasses(['px-4', 'py-2', 'rounded'])\r\n */\r\nexport function toHaveClasses(classNames: string[]) {\r\n return (value: Element | null | undefined) => {\r\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\r\n const missing = classNames.filter((c) => !value.classList.contains(c))\r\n const pass = missing.length === 0\r\n return {\r\n pass,\r\n message: () =>\r\n pass\r\n ? `expected element not to have all classes: ${classNames.join(\", \")}`\r\n : `expected element to have classes: ${missing.join(\", \")}`,\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom matcher: toNotHaveClass\r\n *\r\n * @example\r\n * expect(element).toNotHaveClass('hidden')\r\n */\r\nexport function toNotHaveClass(className: string) {\r\n return (value: Element | null | undefined) => {\r\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\r\n const pass = !value.classList.contains(className)\r\n return {\r\n pass,\r\n message: () => `expected element ${pass ? \"\" : \"not \"}to have class '${className}'`,\r\n }\r\n }\r\n}\r\n\r\n/** Semua matchers — pakai dengan expect.extend(tailwindMatchers) */\r\nexport const tailwindMatchers = { toHaveClass, toHaveClasses, toNotHaveClass }\r\n\r\n// ─── Helper utilities ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Assert element punya semua class yang diharapkan.\r\n * Lebih ergonomis dari `expect(el).toHaveClasses([...])` untuk banyak class.\r\n *\r\n * @example\r\n * const { container } = render(<Button intent=\"primary\" size=\"lg\" />)\r\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white', 'h-12'])\r\n */\r\nexport function expectClasses(element: Element | null | undefined, classes: string[]): void {\r\n if (!element) throw new Error(\"expectClasses: element is null or undefined\")\r\n const missing = classes.filter((c) => !element.classList.contains(c))\r\n if (missing.length > 0) {\r\n throw new Error(\r\n `Expected element to have classes: ${missing.join(\", \")}\\n` +\r\n ` Actual classes: ${element.className}`\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Assert element tidak punya class tertentu.\r\n *\r\n * @example\r\n * expectNoClasses(container.firstChild, ['opacity-50', 'cursor-not-allowed'])\r\n */\r\nexport function expectNoClasses(element: Element | null | undefined, classes: string[]): void {\r\n if (!element) throw new Error(\"expectNoClasses: element is null or undefined\")\r\n const found = classes.filter((c) => element.classList.contains(c))\r\n if (found.length > 0) {\r\n throw new Error(\r\n `Expected element NOT to have classes: ${found.join(\", \")}\\n` +\r\n ` Actual classes: ${element.className}`\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Extract class list dari element sebagai sorted array.\r\n * Berguna untuk snapshot testing.\r\n *\r\n * @example\r\n * expect(getClassList(element)).toMatchSnapshot()\r\n */\r\nexport function getClassList(element: Element | null | undefined): string[] {\r\n if (!element) return []\r\n // classList mungkin tidak iterable di test environment (mock object)\r\n // Fallback ke parsing className string jika Array.from gagal atau hasilkan []\r\n try {\r\n const list = Array.from(element.classList)\r\n if (list.length > 0) return list.sort()\r\n } catch {}\r\n // Fallback: parse dari className string\r\n if (element.className && typeof element.className === \"string\") {\r\n return element.className.trim().split(/\\s+/).filter(Boolean).sort()\r\n }\r\n return []\r\n}\r\n\r\n// ─── Variant snapshot helpers ────────────────────────────────────────────────\r\n\r\n/**\r\n * Buat snapshot dari semua kombinasi variant.\r\n * Berguna untuk regression testing pada styled components.\r\n *\r\n * @example\r\n * const buttonVariants = snapshotVariants(\r\n * (props) => {\r\n * const { container } = render(<Button {...props} />)\r\n * return container.firstChild?.className ?? ''\r\n * },\r\n * [\r\n * { intent: 'primary', size: 'sm' },\r\n * { intent: 'primary', size: 'lg' },\r\n * { intent: 'danger', size: 'sm' },\r\n * ]\r\n * )\r\n * expect(buttonVariants).toMatchSnapshot()\r\n */\r\nexport function snapshotVariants<T>(render: (variant: T) => string, variants: T[]) {\r\n return variants.map((variant) => ({\r\n variant,\r\n output: render(variant),\r\n }))\r\n}\r\n\r\n/**\r\n * Generate semua kombinasi dari variant matrix.\r\n *\r\n * @example\r\n * const combinations = expandVariantMatrix({\r\n * intent: ['primary', 'danger'],\r\n * size: ['sm', 'md', 'lg'],\r\n * disabled: [true, false],\r\n * })\r\n * // → 2 × 3 × 2 = 12 kombinasi\r\n */\r\nexport function expandVariantMatrix(\r\n matrix: Record<string, Array<string | number | boolean>>\r\n): Array<Record<string, string | number | boolean>> {\r\n const keys = Object.keys(matrix)\r\n if (keys.length === 0) return [{}]\r\n\r\n const result: Array<Record<string, string | number | boolean>> = []\r\n\r\n function walk(index: number, current: Record<string, string | number | boolean>) {\r\n if (index >= keys.length) {\r\n result.push({ ...current })\r\n return\r\n }\r\n const key = keys[index]!\r\n for (const value of matrix[key] ?? []) {\r\n current[key] = value\r\n walk(index + 1, current)\r\n }\r\n }\r\n\r\n walk(0, {})\r\n return result\r\n}\r\n\r\n/**\r\n * Test semua kombinasi variant sekaligus — no missing coverage.\r\n *\r\n * @example\r\n * testAllVariants(\r\n * Button,\r\n * { intent: ['primary','danger'], size: ['sm','lg'] },\r\n * (el, variant) => {\r\n * expect(el).not.toBeNull()\r\n * if (variant.intent === 'primary') expectClasses(el, ['bg-blue-500'])\r\n * }\r\n * )\r\n */\r\nexport function testAllVariants(\r\n matrix: Record<string, Array<string | number | boolean>>,\r\n testFn: (variant: Record<string, string | number | boolean>) => void\r\n): void {\r\n const combinations = expandVariantMatrix(matrix)\r\n for (const variant of combinations) {\r\n testFn(variant)\r\n }\r\n}\r\n\r\n// ─── CSS-in-JS output assertions ─────────────────────────────────────────────\r\n\r\n/**\r\n * Bandingkan dua class string secara semantik (urutan tidak penting).\r\n *\r\n * @example\r\n * expectClassesEqual('px-4 py-2 bg-blue-500', 'bg-blue-500 px-4 py-2') // pass\r\n */\r\nexport function expectClassesEqual(actual: string, expected: string): void {\r\n const actualSet = new Set(actual.trim().split(/\\s+/).filter(Boolean))\r\n const expectedSet = new Set(expected.trim().split(/\\s+/).filter(Boolean))\r\n\r\n const missing = [...expectedSet].filter((c) => !actualSet.has(c))\r\n const extra = [...actualSet].filter((c) => !expectedSet.has(c))\r\n\r\n if (missing.length > 0 || extra.length > 0) {\r\n const parts: string[] = []\r\n if (missing.length > 0) parts.push(`Missing: ${missing.join(\", \")}`)\r\n if (extra.length > 0) parts.push(`Extra: ${extra.join(\", \")}`)\r\n throw new Error(`Class mismatch:\\n ${parts.join(\"\\n \")}`)\r\n }\r\n}\r\n\r\n// ─── Engine metrics matchers ──────────────────────────────────────────────────\r\n\r\nexport interface EngineMetricsSnapshot {\r\n totalFiles?: number\r\n uniqueClasses?: number\r\n buildTimeMs?: number\r\n cssBytes?: number\r\n cacheHits?: number\r\n cacheMisses?: number\r\n incrementalRuns?: number\r\n fullRescans?: number\r\n}\r\n\r\n/**\r\n * Assert engine metrics snapshot meets minimum thresholds.\r\n *\r\n * @example\r\n * const result = await engine.build()\r\n * expectEngineMetrics(metrics.snapshot(), {\r\n * totalFiles: 10,\r\n * buildTimeMs: 5000, // max\r\n * })\r\n */\r\nexport function expectEngineMetrics(\r\n metrics: EngineMetricsSnapshot,\r\n expectations: {\r\n minFiles?: number\r\n maxBuildTimeMs?: number\r\n minUniqueClasses?: number\r\n cacheHitRateMin?: number // 0–1\r\n }\r\n): void {\r\n if (expectations.minFiles !== undefined) {\r\n const actual = metrics.totalFiles ?? 0\r\n if (actual < expectations.minFiles) {\r\n throw new Error(\r\n `Engine metrics: expected at least ${expectations.minFiles} files, got ${actual}`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.maxBuildTimeMs !== undefined) {\r\n const actual = metrics.buildTimeMs ?? 0\r\n if (actual > expectations.maxBuildTimeMs) {\r\n throw new Error(\r\n `Engine metrics: build took ${actual}ms, expected ≤ ${expectations.maxBuildTimeMs}ms`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.minUniqueClasses !== undefined) {\r\n const actual = metrics.uniqueClasses ?? 0\r\n if (actual < expectations.minUniqueClasses) {\r\n throw new Error(\r\n `Engine metrics: expected at least ${expectations.minUniqueClasses} unique classes, got ${actual}`\r\n )\r\n }\r\n }\r\n\r\n if (expectations.cacheHitRateMin !== undefined) {\r\n const hits = metrics.cacheHits ?? 0\r\n const total = hits + (metrics.cacheMisses ?? 0)\r\n const rate = total > 0 ? hits / total : 0\r\n if (rate < expectations.cacheHitRateMin) {\r\n throw new Error(\r\n `Engine metrics: cache hit rate ${(rate * 100).toFixed(1)}%, expected ≥ ${(expectations.cacheHitRateMin * 100).toFixed(1)}%`\r\n )\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Custom Jest/Vitest matcher: toHaveEngineMetrics\r\n *\r\n * @example\r\n * expect(metrics).toHaveEngineMetrics({ minFiles: 1 })\r\n */\r\nexport function toHaveEngineMetrics(expectations: Parameters<typeof expectEngineMetrics>[1]) {\r\n return (metrics: EngineMetricsSnapshot) => {\r\n try {\r\n expectEngineMetrics(metrics, expectations)\r\n return { pass: true, message: () => \"engine metrics matched expectations\" }\r\n } catch (e: unknown) {\r\n const msg = e instanceof Error ? e.message : String(e)\r\n return { pass: false, message: () => msg }\r\n }\r\n }\r\n}\r\n\r\n/** All matchers including engine metrics */\r\nexport const tailwindMatchersWithMetrics = {\r\n toHaveClass,\r\n toHaveClasses,\r\n toNotHaveClass,\r\n toHaveEngineMetrics,\r\n}"]}
|
package/dist/testing.mjs
CHANGED
|
@@ -54,7 +54,15 @@ function expectNoClasses(element, classes) {
|
|
|
54
54
|
}
|
|
55
55
|
function getClassList(element) {
|
|
56
56
|
if (!element) return [];
|
|
57
|
-
|
|
57
|
+
try {
|
|
58
|
+
const list = Array.from(element.classList);
|
|
59
|
+
if (list.length > 0) return list.sort();
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
if (element.className && typeof element.className === "string") {
|
|
63
|
+
return element.className.trim().split(/\s+/).filter(Boolean).sort();
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
58
66
|
}
|
|
59
67
|
function snapshotVariants(render, variants) {
|
|
60
68
|
return variants.map((variant) => ({
|