tailwind-styled-v4 5.0.11 → 5.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{analyzeWorkspace-DDOQdzzI.d.ts → analyzeWorkspace-CopJNGmi.d.ts} +2 -0
- package/dist/{analyzeWorkspace-BS5O4rhC.d.mts → analyzeWorkspace-DpVPccjz.d.mts} +2 -0
- package/dist/analyzer.d.mts +4 -4
- package/dist/analyzer.d.ts +4 -4
- package/dist/analyzer.js +34 -69
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzer.mjs +33 -68
- package/dist/analyzer.mjs.map +1 -1
- package/dist/animate.js +11 -11
- package/dist/animate.js.map +1 -1
- package/dist/animate.mjs +11 -11
- package/dist/animate.mjs.map +1 -1
- package/dist/atomic.js +16 -7
- package/dist/atomic.js.map +1 -1
- package/dist/atomic.mjs +16 -7
- package/dist/atomic.mjs.map +1 -1
- package/dist/cli.js +262 -190
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +259 -187
- package/dist/cli.mjs.map +1 -1
- package/dist/compiler.d.mts +2543 -109
- package/dist/compiler.d.ts +2543 -109
- package/dist/compiler.js +1962 -435
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.mjs +1816 -382
- package/dist/compiler.mjs.map +1 -1
- package/dist/devtools.js +17 -4
- package/dist/devtools.js.map +1 -1
- package/dist/devtools.mjs +17 -4
- package/dist/devtools.mjs.map +1 -1
- package/dist/engine.d.mts +11 -470
- package/dist/engine.d.ts +11 -470
- package/dist/engine.js +1442 -417
- package/dist/engine.js.map +1 -1
- package/dist/engine.mjs +1442 -417
- package/dist/engine.mjs.map +1 -1
- package/dist/index-BDQw13kn.d.ts +464 -0
- package/dist/index-DJv28Uzq.d.mts +464 -0
- package/dist/index.browser.mjs +143 -255
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +23 -39
- package/dist/index.d.ts +23 -39
- package/dist/index.js +6000 -1463
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5995 -1458
- package/dist/index.mjs.map +1 -1
- package/dist/next.d.mts +44 -1
- package/dist/next.d.ts +44 -1
- package/dist/next.js +3197 -1128
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +3196 -1129
- package/dist/next.mjs.map +1 -1
- package/dist/rspack.d.mts +9 -0
- package/dist/rspack.d.ts +9 -0
- package/dist/rspack.js +99 -61
- package/dist/rspack.js.map +1 -1
- package/dist/rspack.mjs +99 -61
- package/dist/rspack.mjs.map +1 -1
- package/dist/runtime-css.d.mts +8 -0
- package/dist/runtime-css.d.ts +8 -0
- package/dist/runtime-css.js +23 -7
- package/dist/runtime-css.js.map +1 -1
- package/dist/runtime-css.mjs +23 -7
- package/dist/runtime-css.mjs.map +1 -1
- package/dist/scanner.js +16 -37
- package/dist/scanner.js.map +1 -1
- package/dist/scanner.mjs +15 -36
- package/dist/scanner.mjs.map +1 -1
- package/dist/shared.d.mts +107 -1
- package/dist/shared.d.ts +107 -1
- package/dist/shared.js +1627 -376
- package/dist/shared.js.map +1 -1
- package/dist/shared.mjs +1620 -354
- package/dist/shared.mjs.map +1 -1
- package/dist/svelte.js +39 -35
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +38 -34
- package/dist/svelte.mjs.map +1 -1
- package/dist/theme.js +85 -76
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +83 -74
- package/dist/theme.mjs.map +1 -1
- package/dist/turbopackLoader.js +943 -76
- package/dist/turbopackLoader.js.map +1 -1
- package/dist/turbopackLoader.mjs +943 -76
- package/dist/turbopackLoader.mjs.map +1 -1
- package/dist/tw.js +262 -190
- package/dist/tw.js.map +1 -1
- package/dist/tw.mjs +259 -187
- package/dist/tw.mjs.map +1 -1
- package/dist/vite.js +1336 -296
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +1336 -296
- package/dist/vite.mjs.map +1 -1
- package/dist/vue.js +39 -35
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +38 -34
- package/dist/vue.mjs.map +1 -1
- package/dist/webpackLoader.js +140 -34
- package/dist/webpackLoader.js.map +1 -1
- package/dist/webpackLoader.mjs +140 -34
- package/dist/webpackLoader.mjs.map +1 -1
- package/native/index.node +0 -0
- package/native/tailwind-styled-native.node +0 -0
- package/native/tailwind-styled-native.win32-x64-msvc.node +0 -0
- package/package.json +9 -4
- package/CHANGELOG.md +0 -285
package/dist/runtime-css.mjs
CHANGED
|
@@ -17,6 +17,24 @@ function getStyleElement() {
|
|
|
17
17
|
document.head.appendChild(_state.styleEl);
|
|
18
18
|
return _state.styleEl;
|
|
19
19
|
}
|
|
20
|
+
function insertRuleToSheet(cssRule) {
|
|
21
|
+
const trimmed = cssRule.trim();
|
|
22
|
+
if (!trimmed) return;
|
|
23
|
+
const el = getStyleElement();
|
|
24
|
+
const sheet = el.sheet;
|
|
25
|
+
if (sheet) {
|
|
26
|
+
try {
|
|
27
|
+
sheet.insertRule(trimmed, sheet.cssRules.length);
|
|
28
|
+
return;
|
|
29
|
+
} catch {
|
|
30
|
+
if (process.env.NODE_ENV === "development") {
|
|
31
|
+
console.warn("[tw] insertRule failed, falling back to textContent append for rule:", trimmed.slice(0, 80));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
el.textContent = (el.textContent ?? "") + `
|
|
36
|
+
${trimmed}`;
|
|
37
|
+
}
|
|
20
38
|
function batchedInject(cssRule) {
|
|
21
39
|
if (typeof window === "undefined") return;
|
|
22
40
|
if (!cssRule || injected.has(cssRule)) return;
|
|
@@ -29,18 +47,16 @@ function batchedInject(cssRule) {
|
|
|
29
47
|
function flushBatchedCss() {
|
|
30
48
|
_state.rafHandle = null;
|
|
31
49
|
if (pending.length === 0 || typeof document === "undefined") return;
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
${css}`;
|
|
50
|
+
const rules = pending.splice(0);
|
|
51
|
+
for (const rule of rules) {
|
|
52
|
+
insertRuleToSheet(rule);
|
|
53
|
+
}
|
|
37
54
|
}
|
|
38
55
|
function syncInject(cssRule) {
|
|
39
56
|
if (typeof document === "undefined") return;
|
|
40
57
|
if (!cssRule || injected.has(cssRule)) return;
|
|
41
58
|
injected.add(cssRule);
|
|
42
|
-
|
|
43
|
-
${cssRule}`;
|
|
59
|
+
insertRuleToSheet(cssRule);
|
|
44
60
|
}
|
|
45
61
|
function isInjected(cssRule) {
|
|
46
62
|
return injected.has(cssRule);
|
package/dist/runtime-css.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/domain/runtime-css/src/batchedInjector.ts","../packages/domain/runtime-css/src/CssInjector.tsx"],"names":[],"mappings":"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","file":"runtime-css.mjs","sourcesContent":["\"use client\"\r\n\r\n/**\r\n * tailwind-styled-v5 — Batched CSS Injector (Client Runtime)\r\n *\r\n * Menggantikan pattern inject-per-komponen yang menyebabkan banyak\r\n * style recalculation saat banyak komponen mount bersamaan.\r\n *\r\n * Cara kerja:\r\n * - Semua CSS rules dari render cycle yang sama dikumpulkan\r\n * - Satu requestAnimationFrame = satu DOM style update\r\n * - Deduplication via Set<string> — rule yang sama tidak diinjeksi dua kali\r\n * - Fallback synchronous untuk SSR / server context\r\n *\r\n * Usage (internal, dipakai oleh stateEngine dan containerQuery):\r\n * import { batchedInject, flushBatchedCss } from \"./batchedInjector\"\r\n *\r\n * // Queue a rule\r\n * batchedInject(\".tw-s-abc123[data-active=\\\"true\\\"]{opacity:0.5}\")\r\n *\r\n * // Force flush (biasanya tidak perlu — RAF melakukan ini otomatis)\r\n * flushBatchedCss()\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Singleton state\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** All injected rules (deduplication registry) */\r\nconst injected = new Set<string>()\r\n\r\n/** Pending rules for current RAF batch */\r\nconst pending: string[] = []\r\n\r\n/** RAF handle and style element state */\r\nconst _state = {\r\n rafHandle: null as ReturnType<typeof requestAnimationFrame> | null,\r\n styleEl: null as HTMLStyleElement | null,\r\n}\r\n\r\nfunction getStyleElement(): HTMLStyleElement {\r\n if (_state.styleEl && document.head.contains(_state.styleEl)) return _state.styleEl\r\n\r\n _state.styleEl = document.createElement(\"style\")\r\n _state.styleEl.id = \"__tw-runtime-css\"\r\n _state.styleEl.setAttribute(\"data-tw-batched\", \"true\")\r\n document.head.appendChild(_state.styleEl)\r\n return _state.styleEl\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Core API\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Queue a CSS rule for batched injection.\r\n * Multiple rules accumulated during one event loop tick are flushed together\r\n * in one requestAnimationFrame → one style recalculation.\r\n */\r\nexport function batchedInject(cssRule: string): void {\r\n if (typeof window === \"undefined\") return // SSR — no-op\r\n if (!cssRule || injected.has(cssRule)) return\r\n\r\n injected.add(cssRule)\r\n pending.push(cssRule)\r\n\r\n if (_state.rafHandle === null) {\r\n _state.rafHandle = requestAnimationFrame(flushBatchedCss)\r\n }\r\n}\r\n\r\n/**\r\n * Immediately flush all pending CSS rules to the DOM.\r\n * Called automatically by RAF each frame. Can also be called manually\r\n * after synchronous component setup where RAF timing is too late.\r\n */\r\nexport function flushBatchedCss(): void {\r\n _state.rafHandle = null\r\n\r\n if (pending.length === 0 || typeof document === \"undefined\") return\r\n\r\n const el = getStyleElement()\r\n const css = pending.join(\"\\n\")\r\n pending.length = 0\r\n\r\n // Append rather than replace — preserves previously injected rules\r\n el.textContent += `\\n${css}`\r\n}\r\n\r\n/**\r\n * Synchronous inject — skips batching.\r\n * Use for SSR / critical path where RAF is not available.\r\n */\r\nexport function syncInject(cssRule: string): void {\r\n if (typeof document === \"undefined\") return\r\n if (!cssRule || injected.has(cssRule)) return\r\n\r\n injected.add(cssRule)\r\n getStyleElement().textContent += `\\n${cssRule}`\r\n}\r\n\r\n/**\r\n * Check if a rule has already been injected (deduplication check).\r\n */\r\nexport function isInjected(cssRule: string): boolean {\r\n return injected.has(cssRule)\r\n}\r\n\r\n/**\r\n * Clear all injected rules and remove the style element.\r\n * Useful for testing / SSR resets. Not for production use.\r\n */\r\nexport function resetBatchedCss(): void {\r\n injected.clear()\r\n pending.length = 0\r\n\r\n if (_state.rafHandle !== null) {\r\n cancelAnimationFrame(_state.rafHandle)\r\n _state.rafHandle = null\r\n }\r\n\r\n // Guard: document tidak tersedia di SSR / Node test environment\r\n if (typeof document !== \"undefined\" && _state.styleEl && document.head.contains(_state.styleEl)) {\r\n document.head.removeChild(_state.styleEl)\r\n _state.styleEl = null\r\n } else {\r\n _state.styleEl = null\r\n }\r\n}\r\n\r\n/**\r\n * Get stats about the current injection state (for devtools).\r\n */\r\nexport function getBatchedCssStats(): {\r\n totalInjected: number\r\n pendingCount: number\r\n hasBatchScheduled: boolean\r\n} {\r\n return {\r\n totalInjected: injected.size,\r\n pendingCount: pending.length,\r\n hasBatchScheduled: _state.rafHandle !== null,\r\n }\r\n}","/**\r\n * tailwind-styled-v4 — TwCssInjector\r\n *\r\n * React Server Component — inject route-specific CSS ke <head>.\r\n * Zero client JS, no hydration overhead, streaming-friendly.\r\n *\r\n * Pipeline:\r\n * 1. withTailwindStyled webpack plugin emit CSS manifest ke .next/static/css/tw/\r\n * 2. TwCssInjector baca manifest → inject <style> inline per route\r\n *\r\n * Usage:\r\n * // app/layout.tsx\r\n * import { TwCssInjector } from \"tailwind-styled-v4/runtime-css\"\r\n *\r\n * export default function Layout({ children }) {\r\n * return (\r\n * <html>\r\n * <head><TwCssInjector /></head>\r\n * <body>{children}</body>\r\n * </html>\r\n * )\r\n * }\r\n */\r\n\r\nimport React from \"react\"\r\n\r\ninterface CssInjectorProps {\r\n /** Route spesifik. Default: auto-detect */\r\n route?: string\r\n /** Inject global CSS juga. Default: true */\r\n includeGlobal?: boolean\r\n /** Minify inline CSS. Default: true */\r\n minify?: boolean\r\n /** CSS directory. Default: .next/static/css/tw */\r\n cssDir?: string\r\n}\r\n\r\n/**\r\n * Server Component — inject CSS per route ke <head>.\r\n * Baca dari manifest yang di-emit oleh TwCssManifestPlugin.\r\n */\r\nexport async function TwCssInjector(props: CssInjectorProps = {}): Promise<React.ReactElement> {\r\n const { route, includeGlobal = true, minify = true, cssDir } = props\r\n\r\n // Dynamic import fs — hanya jalan di server\r\n let fs: typeof import(\"node:fs\") | null = null\r\n let path: typeof import(\"node:path\") | null = null\r\n try {\r\n fs = await import(\"node:fs\")\r\n path = await import(\"node:path\")\r\n } catch {\r\n return React.createElement(React.Fragment, null)\r\n }\r\n\r\n const resolvedDir = cssDir\r\n ?? path.join(process.cwd(), \".next\", \"static\", \"css\", \"tw\")\r\n\r\n const manifestPath = path.join(resolvedDir, \"css-manifest.json\")\r\n\r\n // Baca manifest\r\n let manifest: { routes?: Record<string, string> } = {}\r\n try {\r\n if (fs.existsSync(manifestPath)) {\r\n manifest = JSON.parse(fs.readFileSync(manifestPath, \"utf-8\"))\r\n }\r\n } catch {\r\n // Manifest tidak ada — mungkin belum build atau native binding belum ready\r\n return React.createElement(React.Fragment, null)\r\n }\r\n\r\n const cssChunks: string[] = []\r\n\r\n // Global CSS (_global.css)\r\n if (includeGlobal && manifest.routes?.[\"__global\"]) {\r\n const globalPath = path.join(resolvedDir, manifest.routes[\"__global\"])\r\n const css = readFile(fs, globalPath)\r\n if (css) cssChunks.push(css)\r\n }\r\n\r\n // Route-specific CSS\r\n const targetRoute = route ?? \"/\"\r\n if (manifest.routes?.[targetRoute]) {\r\n const routePath = path.join(resolvedDir, manifest.routes[targetRoute])\r\n const css = readFile(fs, routePath)\r\n if (css) cssChunks.push(css)\r\n }\r\n\r\n if (cssChunks.length === 0) return React.createElement(React.Fragment, null)\r\n\r\n const combined = cssChunks.join(\"\\n\")\r\n const final = minify ? minifyCss(combined) : combined\r\n\r\n return React.createElement(\"style\", {\r\n dangerouslySetInnerHTML: { __html: final },\r\n \"data-tw-route\": targetRoute,\r\n \"data-tw-injector\": \"true\",\r\n })\r\n}\r\n\r\n/**\r\n * Hook untuk client components — CSS sudah di-handle TwCssInjector di server.\r\n */\r\nexport function useTwClasses(classes: string): string {\r\n return classes\r\n}\r\n\r\n// Helpers\r\nfunction readFile(fs: typeof import(\"node:fs\"), filepath: string): string | null {\r\n try {\r\n if (fs.existsSync(filepath)) return fs.readFileSync(filepath, \"utf-8\")\r\n } catch {}\r\n return null\r\n}\r\n\r\nfunction minifyCss(css: string): string {\r\n return css\r\n .replace(/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//g, \"\")\r\n .replace(/\\s+/g, \" \")\r\n .replace(/\\s*{\\s*/g, \"{\")\r\n .replace(/\\s*}\\s*/g, \"}\")\r\n .replace(/\\s*;\\s*/g, \";\")\r\n .trim()\r\n}"]}
|
|
1
|
+
{"version":3,"sources":["../packages/domain/runtime-css/src/batchedInjector.ts","../packages/domain/runtime-css/src/CssInjector.tsx"],"names":[],"mappings":"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","file":"runtime-css.mjs","sourcesContent":["\"use client\"\r\n\r\n/**\r\n * tailwind-styled-v5 — Batched CSS Injector (Client Runtime)\r\n *\r\n * Menggantikan pattern inject-per-komponen yang menyebabkan banyak\r\n * style recalculation saat banyak komponen mount bersamaan.\r\n *\r\n * Cara kerja:\r\n * - Semua CSS rules dari render cycle yang sama dikumpulkan\r\n * - Satu requestAnimationFrame = satu DOM style update\r\n * - Deduplication via Set<string> — rule yang sama tidak diinjeksi dua kali\r\n * - Injection via CSSStyleSheet.insertRule() — TIDAK menghapus rules yang ada\r\n * - Fallback ke textContent += jika CSSOM API tidak tersedia\r\n *\r\n * PENTING — Kenapa insertRule() bukan textContent +=:\r\n * `el.textContent += css` terlihat additive tapi sebenarnya:\r\n * 1. Browser baca semua textContent yang ada\r\n * 2. HAPUS semua rules dari stylesheet\r\n * 3. Concat string baru\r\n * 4. Parse ulang dan re-add semua rules\r\n * Di step 2-3, ada window singkat dimana SEMUA styles hilang → FLICKER.\r\n *\r\n * `sheet.insertRule()` benar-benar additive — hanya menambah rule baru\r\n * tanpa menyentuh rules yang sudah ada. Zero flicker.\r\n *\r\n * Usage (internal, dipakai oleh stateEngine dan containerQuery):\r\n * import { batchedInject, flushBatchedCss } from \"./batchedInjector\"\r\n *\r\n * // Queue a rule\r\n * batchedInject(\".tw-s-abc123[data-active=\\\"true\\\"]{opacity:0.5}\")\r\n *\r\n * // Force flush (biasanya tidak perlu — RAF melakukan ini otomatis)\r\n * flushBatchedCss()\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Singleton state\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** All injected rules (deduplication registry) */\r\nconst injected = new Set<string>()\r\n\r\n/** Pending rules for current RAF batch */\r\nconst pending: string[] = []\r\n\r\n/** RAF handle and style element state */\r\nconst _state = {\r\n rafHandle: null as ReturnType<typeof requestAnimationFrame> | null,\r\n styleEl: null as HTMLStyleElement | null,\r\n}\r\n\r\nfunction getStyleElement(): HTMLStyleElement {\r\n if (_state.styleEl && document.head.contains(_state.styleEl)) return _state.styleEl\r\n\r\n _state.styleEl = document.createElement(\"style\")\r\n _state.styleEl.id = \"__tw-runtime-css\"\r\n _state.styleEl.setAttribute(\"data-tw-batched\", \"true\")\r\n // Harus diappend ke DOM sebelum .sheet bisa diakses\r\n document.head.appendChild(_state.styleEl)\r\n return _state.styleEl\r\n}\r\n\r\n/**\r\n * Inject satu CSS rule via CSSStyleSheet.insertRule().\r\n *\r\n * Ini benar-benar additive — tidak menyentuh rules yang sudah ada.\r\n * Fallback ke textContent append jika sheet belum siap (edge case).\r\n *\r\n * @internal\r\n */\r\nfunction insertRuleToSheet(cssRule: string): void {\r\n const trimmed = cssRule.trim()\r\n if (!trimmed) return\r\n\r\n const el = getStyleElement()\r\n const sheet = el.sheet\r\n\r\n if (sheet) {\r\n try {\r\n // insertRule() di akhir — urutan tidak kritis untuk utility CSS\r\n sheet.insertRule(trimmed, sheet.cssRules.length)\r\n return\r\n } catch {\r\n // insertRule() bisa throw jika:\r\n // 1. Rule syntax tidak valid\r\n // 2. Multi-rule string (insertRule() hanya terima satu rule)\r\n // Fallback ke textContent append untuk kasus ini.\r\n if (process.env.NODE_ENV === \"development\") {\r\n console.warn(\"[tw] insertRule failed, falling back to textContent append for rule:\", trimmed.slice(0, 80))\r\n }\r\n }\r\n }\r\n\r\n // Fallback: textContent append.\r\n // Lebih jarang terjadi sekarang karena stateEngine sudah loop per-rule,\r\n // tapi tetap perlu sebagai safety net.\r\n el.textContent = (el.textContent ?? \"\") + `\\n${trimmed}`\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Core API\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Queue a CSS rule for batched injection.\r\n * Multiple rules accumulated during one event loop tick are flushed together\r\n * in one requestAnimationFrame → one style recalculation.\r\n *\r\n * Rules diinjeksi via insertRule() — zero flicker, truly additive.\r\n */\r\nexport function batchedInject(cssRule: string): void {\r\n if (typeof window === \"undefined\") return // SSR — no-op\r\n if (!cssRule || injected.has(cssRule)) return\r\n\r\n injected.add(cssRule)\r\n pending.push(cssRule)\r\n\r\n if (_state.rafHandle === null) {\r\n _state.rafHandle = requestAnimationFrame(flushBatchedCss)\r\n }\r\n}\r\n\r\n/**\r\n * Immediately flush all pending CSS rules to the DOM.\r\n * Called automatically by RAF each frame. Can also be called manually\r\n * after synchronous component setup where RAF timing is too late.\r\n *\r\n * Setiap rule diinjeksi secara individual via insertRule() — tidak ada\r\n * window dimana existing rules hilang.\r\n */\r\nexport function flushBatchedCss(): void {\r\n _state.rafHandle = null\r\n\r\n if (pending.length === 0 || typeof document === \"undefined\") return\r\n\r\n // Flush semua rules sekaligus — satu per satu via insertRule()\r\n // insertRule() atomic per-rule: rule lama tidak pernah dihapus\r\n const rules = pending.splice(0)\r\n for (const rule of rules) {\r\n insertRuleToSheet(rule)\r\n }\r\n}\r\n\r\n/**\r\n * Synchronous inject — skips batching.\r\n * Use for SSR / critical path where RAF is not available.\r\n *\r\n * Menggunakan insertRule() — tidak ada flicker meskipun dipanggil\r\n * di tengah render cycle.\r\n */\r\nexport function syncInject(cssRule: string): void {\r\n if (typeof document === \"undefined\") return\r\n if (!cssRule || injected.has(cssRule)) return\r\n\r\n injected.add(cssRule)\r\n insertRuleToSheet(cssRule)\r\n}\r\n\r\n/**\r\n * Check if a rule has already been injected (deduplication check).\r\n */\r\nexport function isInjected(cssRule: string): boolean {\r\n return injected.has(cssRule)\r\n}\r\n\r\n/**\r\n * Clear all injected rules and remove the style element.\r\n * Useful for testing / SSR resets. Not for production use.\r\n */\r\nexport function resetBatchedCss(): void {\r\n injected.clear()\r\n pending.length = 0\r\n\r\n if (_state.rafHandle !== null) {\r\n cancelAnimationFrame(_state.rafHandle)\r\n _state.rafHandle = null\r\n }\r\n\r\n // Guard: document tidak tersedia di SSR / Node test environment\r\n if (typeof document !== \"undefined\" && _state.styleEl && document.head.contains(_state.styleEl)) {\r\n document.head.removeChild(_state.styleEl)\r\n _state.styleEl = null\r\n } else {\r\n _state.styleEl = null\r\n }\r\n}\r\n\r\n/**\r\n * Get stats about the current injection state (for devtools).\r\n */\r\nexport function getBatchedCssStats(): {\r\n totalInjected: number\r\n pendingCount: number\r\n hasBatchScheduled: boolean\r\n} {\r\n return {\r\n totalInjected: injected.size,\r\n pendingCount: pending.length,\r\n hasBatchScheduled: _state.rafHandle !== null,\r\n }\r\n}","/**\r\n * tailwind-styled-v4 — TwCssInjector\r\n *\r\n * React Server Component — inject route-specific CSS ke <head>.\r\n * Zero client JS, no hydration overhead, streaming-friendly.\r\n *\r\n * Pipeline:\r\n * 1. withTailwindStyled webpack plugin emit CSS manifest ke .next/static/css/tw/\r\n * 2. TwCssInjector baca manifest → inject <style> inline per route\r\n *\r\n * Usage:\r\n * // app/layout.tsx\r\n * import { TwCssInjector } from \"tailwind-styled-v4/runtime-css\"\r\n *\r\n * export default function Layout({ children }) {\r\n * return (\r\n * <html>\r\n * <head><TwCssInjector /></head>\r\n * <body>{children}</body>\r\n * </html>\r\n * )\r\n * }\r\n */\r\n\r\nimport React from \"react\"\r\n\r\ninterface CssInjectorProps {\r\n /** Route spesifik. Default: auto-detect */\r\n route?: string\r\n /** Inject global CSS juga. Default: true */\r\n includeGlobal?: boolean\r\n /** Minify inline CSS. Default: true */\r\n minify?: boolean\r\n /** CSS directory. Default: .next/static/css/tw */\r\n cssDir?: string\r\n}\r\n\r\n/**\r\n * Server Component — inject CSS per route ke <head>.\r\n * Baca dari manifest yang di-emit oleh TwCssManifestPlugin.\r\n */\r\nexport async function TwCssInjector(props: CssInjectorProps = {}): Promise<React.ReactElement> {\r\n const { route, includeGlobal = true, minify = true, cssDir } = props\r\n\r\n // Dynamic import fs — hanya jalan di server\r\n let fs: typeof import(\"node:fs\") | null = null\r\n let path: typeof import(\"node:path\") | null = null\r\n try {\r\n fs = await import(\"node:fs\")\r\n path = await import(\"node:path\")\r\n } catch {\r\n return React.createElement(React.Fragment, null)\r\n }\r\n\r\n const resolvedDir = cssDir\r\n ?? path.join(process.cwd(), \".next\", \"static\", \"css\", \"tw\")\r\n\r\n const manifestPath = path.join(resolvedDir, \"css-manifest.json\")\r\n\r\n // Baca manifest\r\n let manifest: { routes?: Record<string, string> } = {}\r\n try {\r\n if (fs.existsSync(manifestPath)) {\r\n manifest = JSON.parse(fs.readFileSync(manifestPath, \"utf-8\"))\r\n }\r\n } catch {\r\n // Manifest tidak ada — mungkin belum build atau native binding belum ready\r\n return React.createElement(React.Fragment, null)\r\n }\r\n\r\n const cssChunks: string[] = []\r\n\r\n // Global CSS (_global.css)\r\n if (includeGlobal && manifest.routes?.[\"__global\"]) {\r\n const globalPath = path.join(resolvedDir, manifest.routes[\"__global\"])\r\n const css = readFile(fs, globalPath)\r\n if (css) cssChunks.push(css)\r\n }\r\n\r\n // Route-specific CSS\r\n const targetRoute = route ?? \"/\"\r\n if (manifest.routes?.[targetRoute]) {\r\n const routePath = path.join(resolvedDir, manifest.routes[targetRoute])\r\n const css = readFile(fs, routePath)\r\n if (css) cssChunks.push(css)\r\n }\r\n\r\n if (cssChunks.length === 0) return React.createElement(React.Fragment, null)\r\n\r\n const combined = cssChunks.join(\"\\n\")\r\n const final = minify ? minifyCss(combined) : combined\r\n\r\n return React.createElement(\"style\", {\r\n dangerouslySetInnerHTML: { __html: final },\r\n \"data-tw-route\": targetRoute,\r\n \"data-tw-injector\": \"true\",\r\n })\r\n}\r\n\r\n/**\r\n * Hook untuk client components — CSS sudah di-handle TwCssInjector di server.\r\n */\r\nexport function useTwClasses(classes: string): string {\r\n return classes\r\n}\r\n\r\n// Helpers\r\nfunction readFile(fs: typeof import(\"node:fs\"), filepath: string): string | null {\r\n try {\r\n if (fs.existsSync(filepath)) return fs.readFileSync(filepath, \"utf-8\")\r\n } catch {}\r\n return null\r\n}\r\n\r\nfunction minifyCss(css: string): string {\r\n return css\r\n .replace(/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//g, \"\")\r\n .replace(/\\s+/g, \" \")\r\n .replace(/\\s*{\\s*/g, \"{\")\r\n .replace(/\\s*}\\s*/g, \"}\")\r\n .replace(/\\s*;\\s*/g, \";\")\r\n .trim()\r\n}"]}
|
package/dist/scanner.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
require('crypto');
|
|
4
|
-
var
|
|
4
|
+
var fs3 = require('fs');
|
|
5
5
|
var path5 = require('path');
|
|
6
6
|
var url = require('url');
|
|
7
7
|
var module$1 = require('module');
|
|
@@ -12,7 +12,7 @@ var zod = require('zod');
|
|
|
12
12
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
13
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
14
|
|
|
15
|
-
var
|
|
15
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
16
16
|
var path5__default = /*#__PURE__*/_interopDefault(path5);
|
|
17
17
|
|
|
18
18
|
/* tailwind-styled-v4 v5.0.4 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
@@ -77,7 +77,7 @@ function loadNativeBinding(options) {
|
|
|
77
77
|
for (const candidate of candidates) {
|
|
78
78
|
const candidatePath = path5__default.default.resolve(runtimeDir, candidate);
|
|
79
79
|
try {
|
|
80
|
-
if (!
|
|
80
|
+
if (!fs3__default.default.existsSync(candidatePath) && !fs3__default.default.existsSync(candidatePath + ".node")) {
|
|
81
81
|
continue;
|
|
82
82
|
}
|
|
83
83
|
const mod = requireNativeModule(candidatePath);
|
|
@@ -109,9 +109,9 @@ function resolveNativeBindingCandidates(options) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
if (!includeDefaultCandidates) return candidates;
|
|
112
|
-
if (
|
|
112
|
+
if (fs3__default.default.existsSync(runtimeDir)) {
|
|
113
113
|
try {
|
|
114
|
-
for (const entry of
|
|
114
|
+
for (const entry of fs3__default.default.readdirSync(runtimeDir)) {
|
|
115
115
|
if (entry.endsWith(".node")) candidates.push(entry);
|
|
116
116
|
}
|
|
117
117
|
} catch {
|
|
@@ -525,7 +525,7 @@ function defaultCachePath(rootDir, cacheDir) {
|
|
|
525
525
|
}
|
|
526
526
|
function readCache(rootDir, cacheDir) {
|
|
527
527
|
const cachePath = defaultCachePath(rootDir, cacheDir);
|
|
528
|
-
|
|
528
|
+
fs3__default.default.mkdirSync(path5__default.default.dirname(cachePath), { recursive: true });
|
|
529
529
|
const result = cacheReadNative(cachePath);
|
|
530
530
|
if (!result) return [];
|
|
531
531
|
return result.entries.map((e) => ({
|
|
@@ -540,7 +540,7 @@ function readCache(rootDir, cacheDir) {
|
|
|
540
540
|
}
|
|
541
541
|
function writeCache(rootDir, entries, cacheDir) {
|
|
542
542
|
const cachePath = defaultCachePath(rootDir, cacheDir);
|
|
543
|
-
|
|
543
|
+
fs3__default.default.mkdirSync(path5__default.default.dirname(cachePath), { recursive: true });
|
|
544
544
|
const success = cacheWriteNative(cachePath, entries);
|
|
545
545
|
if (!success) {
|
|
546
546
|
throw new Error(
|
|
@@ -568,27 +568,7 @@ var DEFAULT_CHUNK_SIZE = 150;
|
|
|
568
568
|
function collectFiles(rootDir, extensions, ignoreDirs) {
|
|
569
569
|
const native = collectFilesNative(rootDir, extensions, ignoreDirs);
|
|
570
570
|
if (native !== null) return native;
|
|
571
|
-
|
|
572
|
-
function walk(dir) {
|
|
573
|
-
let entries;
|
|
574
|
-
try {
|
|
575
|
-
entries = fs4__default.default.readdirSync(dir, { withFileTypes: true });
|
|
576
|
-
} catch {
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
for (const entry of entries) {
|
|
580
|
-
const fullPath = path5__default.default.join(dir, entry.name);
|
|
581
|
-
const rel = path5__default.default.relative(rootDir, fullPath);
|
|
582
|
-
if (entry.isDirectory()) {
|
|
583
|
-
const ignored = ignoreDirs.some((d) => entry.name === d || rel.startsWith(d + path5__default.default.sep));
|
|
584
|
-
if (!ignored) walk(fullPath);
|
|
585
|
-
} else if (isScannableFile(entry.name, extensions)) {
|
|
586
|
-
files.push(fullPath);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
walk(rootDir);
|
|
591
|
-
return files;
|
|
571
|
+
throw new Error("FATAL: Native binding 'collectFiles' is required but not available.");
|
|
592
572
|
}
|
|
593
573
|
function mergeResults(batchResults) {
|
|
594
574
|
const files = batchResults.map((r) => ({
|
|
@@ -598,8 +578,7 @@ function mergeResults(batchResults) {
|
|
|
598
578
|
}));
|
|
599
579
|
const native = rebuildWorkspaceResultNative(files);
|
|
600
580
|
if (native) return native;
|
|
601
|
-
|
|
602
|
-
return { files, totalFiles: files.length, uniqueClasses: Array.from(unique).sort() };
|
|
581
|
+
throw new Error("FATAL: Native binding 'rebuildWorkspaceResult' is required but not available.");
|
|
603
582
|
}
|
|
604
583
|
if (!worker_threads.isMainThread && worker_threads.parentPort) {
|
|
605
584
|
const { filePaths } = worker_threads.workerData;
|
|
@@ -783,7 +762,7 @@ var createNativeParserLoader = () => {
|
|
|
783
762
|
)
|
|
784
763
|
];
|
|
785
764
|
for (const fullPath of candidates) {
|
|
786
|
-
if (!
|
|
765
|
+
if (!fs3__default.default.existsSync(fullPath)) continue;
|
|
787
766
|
try {
|
|
788
767
|
const required = req(fullPath);
|
|
789
768
|
if (required && (typeof required.extractClassesFromSource === "function" || typeof required.parseClasses === "function" || typeof required.parse_classes === "function")) {
|
|
@@ -829,7 +808,7 @@ function resolveScannerWorkerModulePath() {
|
|
|
829
808
|
path5__default.default.resolve(runtimeDir, "worker.ts")
|
|
830
809
|
];
|
|
831
810
|
for (const candidate of candidates) {
|
|
832
|
-
if (
|
|
811
|
+
if (fs3__default.default.existsSync(candidate)) return candidate;
|
|
833
812
|
}
|
|
834
813
|
return null;
|
|
835
814
|
}
|
|
@@ -886,7 +865,7 @@ function collectCandidates(rootDir, ignoreDirectories, extensionSet) {
|
|
|
886
865
|
if (!currentDir) continue;
|
|
887
866
|
const entries = (() => {
|
|
888
867
|
try {
|
|
889
|
-
return
|
|
868
|
+
return fs3__default.default.readdirSync(currentDir, { withFileTypes: true });
|
|
890
869
|
} catch {
|
|
891
870
|
return [];
|
|
892
871
|
}
|
|
@@ -924,7 +903,7 @@ function scanSource(source) {
|
|
|
924
903
|
"FATAL: Native parser binding is required but not available.\nThis package requires native Rust bindings.\n\nResolution steps:\n1. Build the native Rust module: npm run build:rust"
|
|
925
904
|
);
|
|
926
905
|
}
|
|
927
|
-
function
|
|
906
|
+
function isScannableFile2(filePath, includeExtensions = DEFAULT_EXTENSIONS) {
|
|
928
907
|
return includeExtensions.includes(path5__default.default.extname(filePath));
|
|
929
908
|
}
|
|
930
909
|
function scanFile(filePath) {
|
|
@@ -985,7 +964,7 @@ function scanWorkspace(rootDir, options = {}) {
|
|
|
985
964
|
for (const filePath of candidates) {
|
|
986
965
|
const stat = (() => {
|
|
987
966
|
try {
|
|
988
|
-
return
|
|
967
|
+
return fs3__default.default.statSync(filePath);
|
|
989
968
|
} catch {
|
|
990
969
|
return null;
|
|
991
970
|
}
|
|
@@ -1011,7 +990,7 @@ function scanWorkspace(rootDir, options = {}) {
|
|
|
1011
990
|
for (const { filePath, stat, size, cached } of ranked) {
|
|
1012
991
|
const content = (() => {
|
|
1013
992
|
try {
|
|
1014
|
-
return
|
|
993
|
+
return fs3__default.default.readFileSync(filePath, "utf8");
|
|
1015
994
|
} catch {
|
|
1016
995
|
return null;
|
|
1017
996
|
}
|
|
@@ -1097,7 +1076,7 @@ exports.DEFAULT_EXTENSIONS = DEFAULT_EXTENSIONS;
|
|
|
1097
1076
|
exports.DEFAULT_IGNORES = DEFAULT_IGNORES;
|
|
1098
1077
|
exports.batchExtractClassesNative = batchExtractClassesNative;
|
|
1099
1078
|
exports.extractClassesNative = extractClassesNative;
|
|
1100
|
-
exports.isScannableFile =
|
|
1079
|
+
exports.isScannableFile = isScannableFile2;
|
|
1101
1080
|
exports.parseScanWorkspaceOptions = parseScanWorkspaceOptions;
|
|
1102
1081
|
exports.parseScanWorkspaceResult = parseScanWorkspaceResult;
|
|
1103
1082
|
exports.parseScannerWorkerMessage = parseScannerWorkerMessage;
|