tailwind-styled-v4 4.0.0 → 5.0.1
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 +398 -0
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/dist/analyzer.d.mts +114 -0
- package/dist/analyzer.d.ts +114 -0
- package/dist/analyzer.js +1555 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/analyzer.mjs +1544 -0
- package/dist/analyzer.mjs.map +1 -0
- package/dist/animate.d.mts +46 -0
- package/dist/animate.d.ts +41 -112
- package/dist/animate.js +792 -235
- package/dist/animate.js.map +1 -1
- package/dist/animate.mjs +782 -0
- package/dist/animate.mjs.map +1 -0
- package/dist/atomic.d.mts +18 -0
- package/dist/atomic.d.ts +18 -0
- package/dist/atomic.js +191 -0
- package/dist/atomic.js.map +1 -0
- package/dist/atomic.mjs +185 -0
- package/dist/atomic.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +6063 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +6053 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/{compiler.d.cts → compiler.d.mts} +503 -210
- package/dist/compiler.d.ts +503 -210
- package/dist/compiler.js +1549 -566
- package/dist/compiler.js.map +1 -1
- package/dist/{compiler.cjs → compiler.mjs} +1476 -627
- package/dist/compiler.mjs.map +1 -0
- package/dist/dashboard.d.mts +272 -0
- package/dist/dashboard.d.ts +272 -0
- package/dist/dashboard.js +249 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/dashboard.mjs +239 -0
- package/dist/dashboard.mjs.map +1 -0
- package/dist/devtools.js +336 -211
- package/dist/devtools.js.map +1 -1
- package/dist/{devtools.cjs → devtools.mjs} +331 -220
- package/dist/devtools.mjs.map +1 -0
- package/dist/engine.d.mts +84 -0
- package/dist/engine.d.ts +84 -0
- package/dist/engine.js +3014 -0
- package/dist/engine.js.map +1 -0
- package/dist/engine.mjs +3005 -0
- package/dist/engine.mjs.map +1 -0
- package/dist/{index.d.cts → index.d.mts} +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +1341 -149
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2162 -0
- package/dist/index.mjs.map +1 -0
- package/dist/liveTokenEngine-DYN3Zale.d.mts +34 -0
- package/dist/liveTokenEngine-DYN3Zale.d.ts +34 -0
- package/dist/next.d.mts +55 -0
- package/dist/next.d.ts +30 -20
- package/dist/next.js +6947 -149
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +7050 -0
- package/dist/next.mjs.map +1 -0
- package/dist/plugin.d.mts +90 -0
- package/dist/plugin.d.ts +90 -0
- package/dist/plugin.js +185 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.mjs +174 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/pluginRegistry.d.mts +83 -0
- package/dist/pluginRegistry.d.ts +83 -0
- package/dist/pluginRegistry.js +303 -0
- package/dist/pluginRegistry.js.map +1 -0
- package/dist/pluginRegistry.mjs +298 -0
- package/dist/pluginRegistry.mjs.map +1 -0
- package/dist/{preset.d.cts → preset.d.mts} +29 -2
- package/dist/preset.d.ts +29 -2
- package/dist/preset.js +318 -21
- package/dist/preset.js.map +1 -1
- package/dist/preset.mjs +414 -0
- package/dist/preset.mjs.map +1 -0
- package/dist/rspack.d.mts +33 -0
- package/dist/rspack.d.ts +33 -0
- package/dist/rspack.js +55 -0
- package/dist/rspack.js.map +1 -0
- package/dist/rspack.mjs +45 -0
- package/dist/rspack.mjs.map +1 -0
- package/dist/runtime.d.mts +62 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +207 -0
- package/dist/runtime.js.map +1 -0
- package/dist/runtime.mjs +188 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/runtimeCss.d.mts +65 -0
- package/dist/runtimeCss.d.ts +65 -0
- package/dist/runtimeCss.js +188 -0
- package/dist/runtimeCss.js.map +1 -0
- package/dist/runtimeCss.mjs +173 -0
- package/dist/runtimeCss.mjs.map +1 -0
- package/dist/scanner.d.mts +25 -0
- package/dist/scanner.d.ts +25 -0
- package/dist/scanner.js +717 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanner.mjs +703 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/shared.d.mts +85 -0
- package/dist/shared.d.ts +85 -0
- package/dist/shared.js +255 -0
- package/dist/shared.js.map +1 -0
- package/dist/shared.mjs +233 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/storybookAddon.d.mts +108 -0
- package/dist/storybookAddon.d.ts +108 -0
- package/dist/storybookAddon.js +95 -0
- package/dist/storybookAddon.js.map +1 -0
- package/dist/storybookAddon.mjs +88 -0
- package/dist/storybookAddon.mjs.map +1 -0
- package/dist/svelte.d.mts +114 -0
- package/dist/svelte.d.ts +114 -0
- package/dist/svelte.js +67 -0
- package/dist/svelte.js.map +1 -0
- package/dist/svelte.mjs +59 -0
- package/dist/svelte.mjs.map +1 -0
- package/dist/testing.d.mts +185 -0
- package/dist/testing.d.ts +185 -0
- package/dist/testing.js +173 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +158 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/{theme.d.cts → theme.d.mts} +18 -11
- package/dist/theme.d.ts +18 -11
- package/dist/theme.js +205 -19
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +311 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/types-DXr2PmGP.d.mts +31 -0
- package/dist/types-DXr2PmGP.d.ts +31 -0
- package/dist/vite.d.mts +51 -0
- package/dist/vite.d.ts +35 -6
- package/dist/vite.js +4254 -57
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +4281 -0
- package/dist/vite.mjs.map +1 -0
- package/dist/vue.d.mts +89 -0
- package/dist/vue.d.ts +89 -0
- package/dist/vue.js +104 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +96 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +173 -67
- package/dist/animate.cjs +0 -252
- package/dist/animate.cjs.map +0 -1
- package/dist/animate.d.cts +0 -117
- package/dist/astTransform-ua-eapqs.d.cts +0 -41
- package/dist/astTransform-ua-eapqs.d.ts +0 -41
- package/dist/compiler.cjs.map +0 -1
- package/dist/css.cjs +0 -71
- package/dist/css.cjs.map +0 -1
- package/dist/css.d.cts +0 -45
- package/dist/css.d.ts +0 -45
- package/dist/css.js +0 -62
- package/dist/css.js.map +0 -1
- package/dist/devtools.cjs.map +0 -1
- package/dist/index.cjs +0 -1058
- package/dist/index.cjs.map +0 -1
- package/dist/next.cjs +0 -268
- package/dist/next.cjs.map +0 -1
- package/dist/next.d.cts +0 -45
- package/dist/plugins.cjs +0 -396
- package/dist/plugins.cjs.map +0 -1
- package/dist/plugins.d.cts +0 -231
- package/dist/plugins.d.ts +0 -231
- package/dist/plugins.js +0 -381
- package/dist/plugins.js.map +0 -1
- package/dist/preset.cjs +0 -129
- package/dist/preset.cjs.map +0 -1
- package/dist/theme.cjs +0 -154
- package/dist/theme.cjs.map +0 -1
- package/dist/turbopackLoader.cjs +0 -2689
- package/dist/turbopackLoader.cjs.map +0 -1
- package/dist/turbopackLoader.d.cts +0 -22
- package/dist/turbopackLoader.d.ts +0 -22
- package/dist/turbopackLoader.js +0 -2681
- package/dist/turbopackLoader.js.map +0 -1
- package/dist/vite.cjs +0 -105
- package/dist/vite.cjs.map +0 -1
- package/dist/vite.d.cts +0 -22
- package/dist/webpackLoader.cjs +0 -2670
- package/dist/webpackLoader.cjs.map +0 -1
- package/dist/webpackLoader.d.cts +0 -24
- package/dist/webpackLoader.d.ts +0 -24
- package/dist/webpackLoader.js +0 -2662
- package/dist/webpackLoader.js.map +0 -1
- /package/dist/{devtools.d.cts → devtools.d.mts} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface CssInjectorProps {
|
|
4
|
+
/** Override CSS directory. Default: .next/static/css/tw */
|
|
5
|
+
cssDir?: string;
|
|
6
|
+
/** Specific route to inject. Default: auto-detect dari headers */
|
|
7
|
+
route?: string;
|
|
8
|
+
/** Inject global CSS juga. Default: true */
|
|
9
|
+
includeGlobal?: boolean;
|
|
10
|
+
/** Minify inline CSS. Default: true */
|
|
11
|
+
minify?: boolean;
|
|
12
|
+
/** Add <link> tag instead of inline <style> untuk cached CSS */
|
|
13
|
+
asLink?: boolean;
|
|
14
|
+
/** Override manifest path for dev mode */
|
|
15
|
+
manifestPath?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Server Component — inject route-specific CSS into <head>.
|
|
19
|
+
* No client JS, no hydration overhead.
|
|
20
|
+
*/
|
|
21
|
+
declare function TwCssInjector(props?: CssInjectorProps): Promise<React.ReactElement>;
|
|
22
|
+
/**
|
|
23
|
+
* Lightweight hook to get current route's CSS classes.
|
|
24
|
+
* Useful for dynamic class injection in client components.
|
|
25
|
+
*
|
|
26
|
+
* Returns empty string on server (SSR) — CSS already injected by TwCssInjector.
|
|
27
|
+
*/
|
|
28
|
+
declare function useTwClasses(classes: string): string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Queue a CSS rule for batched injection.
|
|
32
|
+
* Multiple rules accumulated during one event loop tick are flushed together
|
|
33
|
+
* in one requestAnimationFrame → one style recalculation.
|
|
34
|
+
*/
|
|
35
|
+
declare function batchedInject(cssRule: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Immediately flush all pending CSS rules to the DOM.
|
|
38
|
+
* Called automatically by RAF each frame. Can also be called manually
|
|
39
|
+
* after synchronous component setup where RAF timing is too late.
|
|
40
|
+
*/
|
|
41
|
+
declare function flushBatchedCss(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Synchronous inject — skips batching.
|
|
44
|
+
* Use for SSR / critical path where RAF is not available.
|
|
45
|
+
*/
|
|
46
|
+
declare function syncInject(cssRule: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a rule has already been injected (deduplication check).
|
|
49
|
+
*/
|
|
50
|
+
declare function isInjected(cssRule: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Clear all injected rules and remove the style element.
|
|
53
|
+
* Useful for testing / SSR resets. Not for production use.
|
|
54
|
+
*/
|
|
55
|
+
declare function resetBatchedCss(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get stats about the current injection state (for devtools).
|
|
58
|
+
*/
|
|
59
|
+
declare function getBatchedCssStats(): {
|
|
60
|
+
totalInjected: number;
|
|
61
|
+
pendingCount: number;
|
|
62
|
+
hasBatchScheduled: boolean;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { TwCssInjector, batchedInject, flushBatchedCss, getBatchedCssStats, isInjected, resetBatchedCss, syncInject, useTwClasses };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
10
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
12
|
+
|
|
13
|
+
/* tailwind-styled-v4 v5.0.1 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
14
|
+
|
|
15
|
+
var defaultProps = {
|
|
16
|
+
cssDir: void 0,
|
|
17
|
+
route: void 0,
|
|
18
|
+
includeGlobal: true,
|
|
19
|
+
minify: true,
|
|
20
|
+
asLink: false,
|
|
21
|
+
manifestPath: void 0
|
|
22
|
+
};
|
|
23
|
+
async function TwCssInjector(props) {
|
|
24
|
+
const { cssDir, route, includeGlobal, minify, asLink, manifestPath } = {
|
|
25
|
+
...defaultProps,
|
|
26
|
+
...props
|
|
27
|
+
};
|
|
28
|
+
const resolvedDir = cssDir ?? path__default.default.join(process.cwd(), ".next", "static", "css", "tw");
|
|
29
|
+
const cssChunks = [];
|
|
30
|
+
if (manifestPath) {
|
|
31
|
+
const manifestCss = loadCssFromManifest(manifestPath, route ?? "/");
|
|
32
|
+
if (manifestCss) {
|
|
33
|
+
cssChunks.push(manifestCss);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
const detectedManifest = detectManifestPath(resolvedDir);
|
|
37
|
+
if (detectedManifest) {
|
|
38
|
+
const manifestCss = loadCssFromManifest(detectedManifest, route ?? "/");
|
|
39
|
+
if (manifestCss) {
|
|
40
|
+
cssChunks.push(manifestCss);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (includeGlobal) {
|
|
45
|
+
const globalCss = loadCssFile(path__default.default.join(resolvedDir, "_global.css"));
|
|
46
|
+
if (globalCss) cssChunks.push(globalCss);
|
|
47
|
+
}
|
|
48
|
+
const targetRoute = route ?? "/";
|
|
49
|
+
const routeFile = routeToFilename(targetRoute);
|
|
50
|
+
const routeCss = loadCssFile(path__default.default.join(resolvedDir, routeFile));
|
|
51
|
+
if (routeCss) cssChunks.push(routeCss);
|
|
52
|
+
if (cssChunks.length === 0) return React__default.default.createElement(React__default.default.Fragment, null);
|
|
53
|
+
const combined = cssChunks.join("\n");
|
|
54
|
+
const final = minify ? minifyCss(combined) : combined;
|
|
55
|
+
if (asLink) {
|
|
56
|
+
return React__default.default.createElement("link", {
|
|
57
|
+
rel: "stylesheet",
|
|
58
|
+
href: `/_next/static/css/tw/${routeFile}`,
|
|
59
|
+
crossOrigin: "anonymous"
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return React__default.default.createElement("style", {
|
|
63
|
+
dangerouslySetInnerHTML: { __html: final },
|
|
64
|
+
"data-tw-route": targetRoute
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function useTwClasses(classes) {
|
|
68
|
+
return classes;
|
|
69
|
+
}
|
|
70
|
+
function loadCssFile(filepath) {
|
|
71
|
+
try {
|
|
72
|
+
if (fs__default.default.existsSync(filepath)) {
|
|
73
|
+
return fs__default.default.readFileSync(filepath, "utf-8");
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function routeToFilename(route) {
|
|
80
|
+
if (route === "/") return "index.css";
|
|
81
|
+
if (route === "__global") return "_global.css";
|
|
82
|
+
return `${route.replace(/^\//, "").replace(/\//g, "_")}.css`;
|
|
83
|
+
}
|
|
84
|
+
function detectManifestPath(cssDir) {
|
|
85
|
+
const candidates = [
|
|
86
|
+
path__default.default.join(cssDir, "css-manifest.json"),
|
|
87
|
+
path__default.default.join(process.cwd(), "public", "__tw", "css-manifest.json"),
|
|
88
|
+
path__default.default.join(process.cwd(), "artifacts", "route-css", "css-manifest.json")
|
|
89
|
+
];
|
|
90
|
+
for (const candidate of candidates) {
|
|
91
|
+
if (fs__default.default.existsSync(candidate)) {
|
|
92
|
+
return candidate;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function loadCssFromManifest(manifestPath, route) {
|
|
98
|
+
try {
|
|
99
|
+
if (!fs__default.default.existsSync(manifestPath)) return null;
|
|
100
|
+
const content = fs__default.default.readFileSync(manifestPath, "utf-8");
|
|
101
|
+
const manifest = JSON.parse(content);
|
|
102
|
+
if (manifest.routes?.[route]) {
|
|
103
|
+
const routeCssPath = path__default.default.join(path__default.default.dirname(manifestPath), manifest.routes[route]);
|
|
104
|
+
return loadCssFile(routeCssPath);
|
|
105
|
+
}
|
|
106
|
+
if (manifest.global) {
|
|
107
|
+
const globalCssPath = path__default.default.join(path__default.default.dirname(manifestPath), manifest.global);
|
|
108
|
+
return loadCssFile(globalCssPath);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function minifyCss(css) {
|
|
115
|
+
return css.replace(/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g, "").replace(/\s+/g, " ").replace(/\s*{\s*/g, "{").replace(/\s*}\s*/g, "}").replace(/\s*;\s*/g, ";").trim();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// packages/runtime-css/src/batchedInjector.ts
|
|
119
|
+
var injected = /* @__PURE__ */ new Set();
|
|
120
|
+
var pending = [];
|
|
121
|
+
var rafHandle = null;
|
|
122
|
+
var styleEl = null;
|
|
123
|
+
function getStyleElement() {
|
|
124
|
+
if (styleEl && document.head.contains(styleEl)) return styleEl;
|
|
125
|
+
styleEl = document.createElement("style");
|
|
126
|
+
styleEl.id = "__tw-runtime-css";
|
|
127
|
+
styleEl.setAttribute("data-tw-batched", "true");
|
|
128
|
+
document.head.appendChild(styleEl);
|
|
129
|
+
return styleEl;
|
|
130
|
+
}
|
|
131
|
+
function batchedInject(cssRule) {
|
|
132
|
+
if (typeof window === "undefined") return;
|
|
133
|
+
if (!cssRule || injected.has(cssRule)) return;
|
|
134
|
+
injected.add(cssRule);
|
|
135
|
+
pending.push(cssRule);
|
|
136
|
+
if (rafHandle === null) {
|
|
137
|
+
rafHandle = requestAnimationFrame(flushBatchedCss);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function flushBatchedCss() {
|
|
141
|
+
rafHandle = null;
|
|
142
|
+
if (pending.length === 0 || typeof document === "undefined") return;
|
|
143
|
+
const el = getStyleElement();
|
|
144
|
+
const css = pending.join("\n");
|
|
145
|
+
pending.length = 0;
|
|
146
|
+
el.textContent += `
|
|
147
|
+
${css}`;
|
|
148
|
+
}
|
|
149
|
+
function syncInject(cssRule) {
|
|
150
|
+
if (typeof document === "undefined") return;
|
|
151
|
+
if (!cssRule || injected.has(cssRule)) return;
|
|
152
|
+
injected.add(cssRule);
|
|
153
|
+
getStyleElement().textContent += `
|
|
154
|
+
${cssRule}`;
|
|
155
|
+
}
|
|
156
|
+
function isInjected(cssRule) {
|
|
157
|
+
return injected.has(cssRule);
|
|
158
|
+
}
|
|
159
|
+
function resetBatchedCss() {
|
|
160
|
+
injected.clear();
|
|
161
|
+
pending.length = 0;
|
|
162
|
+
if (rafHandle !== null) {
|
|
163
|
+
cancelAnimationFrame(rafHandle);
|
|
164
|
+
rafHandle = null;
|
|
165
|
+
}
|
|
166
|
+
if (styleEl && document.head.contains(styleEl)) {
|
|
167
|
+
document.head.removeChild(styleEl);
|
|
168
|
+
styleEl = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function getBatchedCssStats() {
|
|
172
|
+
return {
|
|
173
|
+
totalInjected: injected.size,
|
|
174
|
+
pendingCount: pending.length,
|
|
175
|
+
hasBatchScheduled: rafHandle !== null
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
exports.TwCssInjector = TwCssInjector;
|
|
180
|
+
exports.batchedInject = batchedInject;
|
|
181
|
+
exports.flushBatchedCss = flushBatchedCss;
|
|
182
|
+
exports.getBatchedCssStats = getBatchedCssStats;
|
|
183
|
+
exports.isInjected = isInjected;
|
|
184
|
+
exports.resetBatchedCss = resetBatchedCss;
|
|
185
|
+
exports.syncInject = syncInject;
|
|
186
|
+
exports.useTwClasses = useTwClasses;
|
|
187
|
+
//# sourceMappingURL=runtimeCss.js.map
|
|
188
|
+
//# sourceMappingURL=runtimeCss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/runtime-css/src/CssInjector.tsx","../packages/runtime-css/src/batchedInjector.ts"],"names":["path","React","fs"],"mappings":"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","file":"runtimeCss.js","sourcesContent":["\"use server\"\n\n/**\n * tailwind-styled-v5 — CSS Injector (React Server Component)\n *\n * Inject CSS yang sudah di-generate per-route langsung ke <head>.\n * Dipakai di Next.js App Router layout atau page.\n *\n * Di server component — inject inline CSS, zero client JS.\n * Streaming friendly — CSS di-emit bersamaan dengan HTML.\n *\n * Usage:\n * // app/layout.tsx\n * import { TwCssInjector } from \"tailwind-styled-v4/css\"\n * export default function Layout({ children }) {\n * return <html><head><TwCssInjector/></head><body>{children}</body></html>\n * }\n */\n\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport React from \"react\"\n\ninterface CssInjectorProps {\n /** Override CSS directory. Default: .next/static/css/tw */\n cssDir?: string\n /** Specific route to inject. Default: auto-detect dari headers */\n route?: string\n /** Inject global CSS juga. Default: true */\n includeGlobal?: boolean\n /** Minify inline CSS. Default: true */\n minify?: boolean\n /** Add <link> tag instead of inline <style> untuk cached CSS */\n asLink?: boolean\n /** Override manifest path for dev mode */\n manifestPath?: string\n}\n\nconst defaultProps: CssInjectorProps = {\n cssDir: undefined,\n route: undefined,\n includeGlobal: true,\n minify: true,\n asLink: false,\n manifestPath: undefined,\n}\n\n/**\n * Server Component — inject route-specific CSS into <head>.\n * No client JS, no hydration overhead.\n */\nexport async function TwCssInjector(props?: CssInjectorProps): Promise<React.ReactElement> {\n const { cssDir, route, includeGlobal, minify, asLink, manifestPath } = {\n ...defaultProps,\n ...props,\n }\n const resolvedDir = cssDir ?? path.join(process.cwd(), \".next\", \"static\", \"css\", \"tw\")\n\n const cssChunks: string[] = []\n\n // Try manifest-based loading first (for dev mode)\n if (manifestPath) {\n const manifestCss = loadCssFromManifest(manifestPath, route ?? \"/\")\n if (manifestCss) {\n cssChunks.push(manifestCss)\n }\n } else {\n // Fallback: legacy file-based loading with path detection\n const detectedManifest = detectManifestPath(resolvedDir)\n if (detectedManifest) {\n const manifestCss = loadCssFromManifest(detectedManifest, route ?? \"/\")\n if (manifestCss) {\n cssChunks.push(manifestCss)\n }\n }\n }\n\n // 1. Global CSS (base styles, reset)\n if (includeGlobal) {\n const globalCss = loadCssFile(path.join(resolvedDir, \"_global.css\"))\n if (globalCss) cssChunks.push(globalCss)\n }\n\n // 2. Route-specific CSS\n const targetRoute = route ?? \"/\"\n const routeFile = routeToFilename(targetRoute)\n const routeCss = loadCssFile(path.join(resolvedDir, routeFile))\n if (routeCss) cssChunks.push(routeCss)\n\n if (cssChunks.length === 0) return React.createElement(React.Fragment, null)\n\n const combined = cssChunks.join(\"\\n\")\n const final = minify ? minifyCss(combined) : combined\n\n if (asLink) {\n // Return <link> tag — CSS cached by browser\n return React.createElement(\"link\", {\n rel: \"stylesheet\",\n href: `/_next/static/css/tw/${routeFile}`,\n crossOrigin: \"anonymous\",\n })\n }\n\n // Inline <style> — zero network request, fastest FCP\n return React.createElement(\"style\", {\n dangerouslySetInnerHTML: { __html: final },\n \"data-tw-route\": targetRoute,\n })\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Hook for client components\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Lightweight hook to get current route's CSS classes.\n * Useful for dynamic class injection in client components.\n *\n * Returns empty string on server (SSR) — CSS already injected by TwCssInjector.\n */\nexport function useTwClasses(classes: string): string {\n // In client environment, return classes as-is\n // CSS is already handled by TwCssInjector at server level\n return classes\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction loadCssFile(filepath: string): string | null {\n try {\n if (fs.existsSync(filepath)) {\n return fs.readFileSync(filepath, \"utf-8\")\n }\n } catch {\n // file not found or unreadable\n }\n return null\n}\n\nfunction routeToFilename(route: string): string {\n if (route === \"/\") return \"index.css\"\n if (route === \"__global\") return \"_global.css\"\n return `${route.replace(/^\\//, \"\").replace(/\\//g, \"_\")}.css`\n}\n\nfunction detectManifestPath(cssDir: string): string | null {\n const candidates = [\n path.join(cssDir, \"css-manifest.json\"),\n path.join(process.cwd(), \"public\", \"__tw\", \"css-manifest.json\"),\n path.join(process.cwd(), \"artifacts\", \"route-css\", \"css-manifest.json\"),\n ]\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n\ninterface CssManifest {\n routes?: Record<string, string>\n global?: string\n [key: string]: unknown\n}\n\nfunction loadCssFromManifest(manifestPath: string, route: string): string | null {\n try {\n if (!fs.existsSync(manifestPath)) return null\n\n const content = fs.readFileSync(manifestPath, \"utf-8\")\n const manifest: CssManifest = JSON.parse(content)\n\n // Try route-specific CSS\n if (manifest.routes?.[route]) {\n const routeCssPath = path.join(path.dirname(manifestPath), manifest.routes[route])\n return loadCssFile(routeCssPath)\n }\n\n // Fallback: global CSS\n if (manifest.global) {\n const globalCssPath = path.join(path.dirname(manifestPath), manifest.global)\n return loadCssFile(globalCssPath)\n }\n } catch {\n // manifest not found or invalid\n }\n return null\n}\n\nfunction minifyCss(css: string): string {\n return css\n .replace(/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//g, \"\")\n .replace(/\\s+/g, \" \")\n .replace(/\\s*{\\s*/g, \"{\")\n .replace(/\\s*}\\s*/g, \"}\")\n .replace(/\\s*;\\s*/g, \";\")\n .trim()\n}\n","\"use client\"\n\n/**\n * tailwind-styled-v5 — Batched CSS Injector (Client Runtime)\n *\n * Menggantikan pattern inject-per-komponen yang menyebabkan banyak\n * style recalculation saat banyak komponen mount bersamaan.\n *\n * Cara kerja:\n * - Semua CSS rules dari render cycle yang sama dikumpulkan\n * - Satu requestAnimationFrame = satu DOM style update\n * - Deduplication via Set<string> — rule yang sama tidak diinjeksi dua kali\n * - Fallback synchronous untuk SSR / server context\n *\n * Usage (internal, dipakai oleh stateEngine dan containerQuery):\n * import { batchedInject, flushBatchedCss } from \"./batchedInjector\"\n *\n * // Queue a rule\n * batchedInject(\".tw-s-abc123[data-active=\\\"true\\\"]{opacity:0.5}\")\n *\n * // Force flush (biasanya tidak perlu — RAF melakukan ini otomatis)\n * flushBatchedCss()\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Singleton state\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** All injected rules (deduplication registry) */\nconst injected = new Set<string>()\n\n/** Pending rules for current RAF batch */\nconst pending: string[] = []\n\n/** RAF handle — null means no batch is scheduled */\nlet rafHandle: ReturnType<typeof requestAnimationFrame> | null = null\n\n/** The single shared <style> element for all batched runtime CSS */\nlet styleEl: HTMLStyleElement | null = null\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleEl && document.head.contains(styleEl)) return styleEl\n\n styleEl = document.createElement(\"style\")\n styleEl.id = \"__tw-runtime-css\"\n styleEl.setAttribute(\"data-tw-batched\", \"true\")\n document.head.appendChild(styleEl)\n return styleEl\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Core API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Queue a CSS rule for batched injection.\n * Multiple rules accumulated during one event loop tick are flushed together\n * in one requestAnimationFrame → one style recalculation.\n */\nexport function batchedInject(cssRule: string): void {\n if (typeof window === \"undefined\") return // SSR — no-op\n if (!cssRule || injected.has(cssRule)) return\n\n injected.add(cssRule)\n pending.push(cssRule)\n\n if (rafHandle === null) {\n rafHandle = requestAnimationFrame(flushBatchedCss)\n }\n}\n\n/**\n * Immediately flush all pending CSS rules to the DOM.\n * Called automatically by RAF each frame. Can also be called manually\n * after synchronous component setup where RAF timing is too late.\n */\nexport function flushBatchedCss(): void {\n rafHandle = null\n\n if (pending.length === 0 || typeof document === \"undefined\") return\n\n const el = getStyleElement()\n const css = pending.join(\"\\n\")\n pending.length = 0\n\n // Append rather than replace — preserves previously injected rules\n el.textContent += `\\n${css}`\n}\n\n/**\n * Synchronous inject — skips batching.\n * Use for SSR / critical path where RAF is not available.\n */\nexport function syncInject(cssRule: string): void {\n if (typeof document === \"undefined\") return\n if (!cssRule || injected.has(cssRule)) return\n\n injected.add(cssRule)\n getStyleElement().textContent += `\\n${cssRule}`\n}\n\n/**\n * Check if a rule has already been injected (deduplication check).\n */\nexport function isInjected(cssRule: string): boolean {\n return injected.has(cssRule)\n}\n\n/**\n * Clear all injected rules and remove the style element.\n * Useful for testing / SSR resets. Not for production use.\n */\nexport function resetBatchedCss(): void {\n injected.clear()\n pending.length = 0\n\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle)\n rafHandle = null\n }\n\n if (styleEl && document.head.contains(styleEl)) {\n document.head.removeChild(styleEl)\n styleEl = null\n }\n}\n\n/**\n * Get stats about the current injection state (for devtools).\n */\nexport function getBatchedCssStats(): {\n totalInjected: number\n pendingCount: number\n hasBatchScheduled: boolean\n} {\n return {\n totalInjected: injected.size,\n pendingCount: pending.length,\n hasBatchScheduled: rafHandle !== null,\n }\n}\n"]}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
/* tailwind-styled-v4 v5.0.1 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
6
|
+
|
|
7
|
+
var defaultProps = {
|
|
8
|
+
cssDir: void 0,
|
|
9
|
+
route: void 0,
|
|
10
|
+
includeGlobal: true,
|
|
11
|
+
minify: true,
|
|
12
|
+
asLink: false,
|
|
13
|
+
manifestPath: void 0
|
|
14
|
+
};
|
|
15
|
+
async function TwCssInjector(props) {
|
|
16
|
+
const { cssDir, route, includeGlobal, minify, asLink, manifestPath } = {
|
|
17
|
+
...defaultProps,
|
|
18
|
+
...props
|
|
19
|
+
};
|
|
20
|
+
const resolvedDir = cssDir ?? path.join(process.cwd(), ".next", "static", "css", "tw");
|
|
21
|
+
const cssChunks = [];
|
|
22
|
+
if (manifestPath) {
|
|
23
|
+
const manifestCss = loadCssFromManifest(manifestPath, route ?? "/");
|
|
24
|
+
if (manifestCss) {
|
|
25
|
+
cssChunks.push(manifestCss);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
const detectedManifest = detectManifestPath(resolvedDir);
|
|
29
|
+
if (detectedManifest) {
|
|
30
|
+
const manifestCss = loadCssFromManifest(detectedManifest, route ?? "/");
|
|
31
|
+
if (manifestCss) {
|
|
32
|
+
cssChunks.push(manifestCss);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (includeGlobal) {
|
|
37
|
+
const globalCss = loadCssFile(path.join(resolvedDir, "_global.css"));
|
|
38
|
+
if (globalCss) cssChunks.push(globalCss);
|
|
39
|
+
}
|
|
40
|
+
const targetRoute = route ?? "/";
|
|
41
|
+
const routeFile = routeToFilename(targetRoute);
|
|
42
|
+
const routeCss = loadCssFile(path.join(resolvedDir, routeFile));
|
|
43
|
+
if (routeCss) cssChunks.push(routeCss);
|
|
44
|
+
if (cssChunks.length === 0) return React.createElement(React.Fragment, null);
|
|
45
|
+
const combined = cssChunks.join("\n");
|
|
46
|
+
const final = minify ? minifyCss(combined) : combined;
|
|
47
|
+
if (asLink) {
|
|
48
|
+
return React.createElement("link", {
|
|
49
|
+
rel: "stylesheet",
|
|
50
|
+
href: `/_next/static/css/tw/${routeFile}`,
|
|
51
|
+
crossOrigin: "anonymous"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return React.createElement("style", {
|
|
55
|
+
dangerouslySetInnerHTML: { __html: final },
|
|
56
|
+
"data-tw-route": targetRoute
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function useTwClasses(classes) {
|
|
60
|
+
return classes;
|
|
61
|
+
}
|
|
62
|
+
function loadCssFile(filepath) {
|
|
63
|
+
try {
|
|
64
|
+
if (fs.existsSync(filepath)) {
|
|
65
|
+
return fs.readFileSync(filepath, "utf-8");
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function routeToFilename(route) {
|
|
72
|
+
if (route === "/") return "index.css";
|
|
73
|
+
if (route === "__global") return "_global.css";
|
|
74
|
+
return `${route.replace(/^\//, "").replace(/\//g, "_")}.css`;
|
|
75
|
+
}
|
|
76
|
+
function detectManifestPath(cssDir) {
|
|
77
|
+
const candidates = [
|
|
78
|
+
path.join(cssDir, "css-manifest.json"),
|
|
79
|
+
path.join(process.cwd(), "public", "__tw", "css-manifest.json"),
|
|
80
|
+
path.join(process.cwd(), "artifacts", "route-css", "css-manifest.json")
|
|
81
|
+
];
|
|
82
|
+
for (const candidate of candidates) {
|
|
83
|
+
if (fs.existsSync(candidate)) {
|
|
84
|
+
return candidate;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function loadCssFromManifest(manifestPath, route) {
|
|
90
|
+
try {
|
|
91
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
92
|
+
const content = fs.readFileSync(manifestPath, "utf-8");
|
|
93
|
+
const manifest = JSON.parse(content);
|
|
94
|
+
if (manifest.routes?.[route]) {
|
|
95
|
+
const routeCssPath = path.join(path.dirname(manifestPath), manifest.routes[route]);
|
|
96
|
+
return loadCssFile(routeCssPath);
|
|
97
|
+
}
|
|
98
|
+
if (manifest.global) {
|
|
99
|
+
const globalCssPath = path.join(path.dirname(manifestPath), manifest.global);
|
|
100
|
+
return loadCssFile(globalCssPath);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function minifyCss(css) {
|
|
107
|
+
return css.replace(/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g, "").replace(/\s+/g, " ").replace(/\s*{\s*/g, "{").replace(/\s*}\s*/g, "}").replace(/\s*;\s*/g, ";").trim();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// packages/runtime-css/src/batchedInjector.ts
|
|
111
|
+
var injected = /* @__PURE__ */ new Set();
|
|
112
|
+
var pending = [];
|
|
113
|
+
var rafHandle = null;
|
|
114
|
+
var styleEl = null;
|
|
115
|
+
function getStyleElement() {
|
|
116
|
+
if (styleEl && document.head.contains(styleEl)) return styleEl;
|
|
117
|
+
styleEl = document.createElement("style");
|
|
118
|
+
styleEl.id = "__tw-runtime-css";
|
|
119
|
+
styleEl.setAttribute("data-tw-batched", "true");
|
|
120
|
+
document.head.appendChild(styleEl);
|
|
121
|
+
return styleEl;
|
|
122
|
+
}
|
|
123
|
+
function batchedInject(cssRule) {
|
|
124
|
+
if (typeof window === "undefined") return;
|
|
125
|
+
if (!cssRule || injected.has(cssRule)) return;
|
|
126
|
+
injected.add(cssRule);
|
|
127
|
+
pending.push(cssRule);
|
|
128
|
+
if (rafHandle === null) {
|
|
129
|
+
rafHandle = requestAnimationFrame(flushBatchedCss);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function flushBatchedCss() {
|
|
133
|
+
rafHandle = null;
|
|
134
|
+
if (pending.length === 0 || typeof document === "undefined") return;
|
|
135
|
+
const el = getStyleElement();
|
|
136
|
+
const css = pending.join("\n");
|
|
137
|
+
pending.length = 0;
|
|
138
|
+
el.textContent += `
|
|
139
|
+
${css}`;
|
|
140
|
+
}
|
|
141
|
+
function syncInject(cssRule) {
|
|
142
|
+
if (typeof document === "undefined") return;
|
|
143
|
+
if (!cssRule || injected.has(cssRule)) return;
|
|
144
|
+
injected.add(cssRule);
|
|
145
|
+
getStyleElement().textContent += `
|
|
146
|
+
${cssRule}`;
|
|
147
|
+
}
|
|
148
|
+
function isInjected(cssRule) {
|
|
149
|
+
return injected.has(cssRule);
|
|
150
|
+
}
|
|
151
|
+
function resetBatchedCss() {
|
|
152
|
+
injected.clear();
|
|
153
|
+
pending.length = 0;
|
|
154
|
+
if (rafHandle !== null) {
|
|
155
|
+
cancelAnimationFrame(rafHandle);
|
|
156
|
+
rafHandle = null;
|
|
157
|
+
}
|
|
158
|
+
if (styleEl && document.head.contains(styleEl)) {
|
|
159
|
+
document.head.removeChild(styleEl);
|
|
160
|
+
styleEl = null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function getBatchedCssStats() {
|
|
164
|
+
return {
|
|
165
|
+
totalInjected: injected.size,
|
|
166
|
+
pendingCount: pending.length,
|
|
167
|
+
hasBatchScheduled: rafHandle !== null
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { TwCssInjector, batchedInject, flushBatchedCss, getBatchedCssStats, isInjected, resetBatchedCss, syncInject, useTwClasses };
|
|
172
|
+
//# sourceMappingURL=runtimeCss.mjs.map
|
|
173
|
+
//# sourceMappingURL=runtimeCss.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/runtime-css/src/CssInjector.tsx","../packages/runtime-css/src/batchedInjector.ts"],"names":[],"mappings":"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","file":"runtimeCss.mjs","sourcesContent":["\"use server\"\n\n/**\n * tailwind-styled-v5 — CSS Injector (React Server Component)\n *\n * Inject CSS yang sudah di-generate per-route langsung ke <head>.\n * Dipakai di Next.js App Router layout atau page.\n *\n * Di server component — inject inline CSS, zero client JS.\n * Streaming friendly — CSS di-emit bersamaan dengan HTML.\n *\n * Usage:\n * // app/layout.tsx\n * import { TwCssInjector } from \"tailwind-styled-v4/css\"\n * export default function Layout({ children }) {\n * return <html><head><TwCssInjector/></head><body>{children}</body></html>\n * }\n */\n\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport React from \"react\"\n\ninterface CssInjectorProps {\n /** Override CSS directory. Default: .next/static/css/tw */\n cssDir?: string\n /** Specific route to inject. Default: auto-detect dari headers */\n route?: string\n /** Inject global CSS juga. Default: true */\n includeGlobal?: boolean\n /** Minify inline CSS. Default: true */\n minify?: boolean\n /** Add <link> tag instead of inline <style> untuk cached CSS */\n asLink?: boolean\n /** Override manifest path for dev mode */\n manifestPath?: string\n}\n\nconst defaultProps: CssInjectorProps = {\n cssDir: undefined,\n route: undefined,\n includeGlobal: true,\n minify: true,\n asLink: false,\n manifestPath: undefined,\n}\n\n/**\n * Server Component — inject route-specific CSS into <head>.\n * No client JS, no hydration overhead.\n */\nexport async function TwCssInjector(props?: CssInjectorProps): Promise<React.ReactElement> {\n const { cssDir, route, includeGlobal, minify, asLink, manifestPath } = {\n ...defaultProps,\n ...props,\n }\n const resolvedDir = cssDir ?? path.join(process.cwd(), \".next\", \"static\", \"css\", \"tw\")\n\n const cssChunks: string[] = []\n\n // Try manifest-based loading first (for dev mode)\n if (manifestPath) {\n const manifestCss = loadCssFromManifest(manifestPath, route ?? \"/\")\n if (manifestCss) {\n cssChunks.push(manifestCss)\n }\n } else {\n // Fallback: legacy file-based loading with path detection\n const detectedManifest = detectManifestPath(resolvedDir)\n if (detectedManifest) {\n const manifestCss = loadCssFromManifest(detectedManifest, route ?? \"/\")\n if (manifestCss) {\n cssChunks.push(manifestCss)\n }\n }\n }\n\n // 1. Global CSS (base styles, reset)\n if (includeGlobal) {\n const globalCss = loadCssFile(path.join(resolvedDir, \"_global.css\"))\n if (globalCss) cssChunks.push(globalCss)\n }\n\n // 2. Route-specific CSS\n const targetRoute = route ?? \"/\"\n const routeFile = routeToFilename(targetRoute)\n const routeCss = loadCssFile(path.join(resolvedDir, routeFile))\n if (routeCss) cssChunks.push(routeCss)\n\n if (cssChunks.length === 0) return React.createElement(React.Fragment, null)\n\n const combined = cssChunks.join(\"\\n\")\n const final = minify ? minifyCss(combined) : combined\n\n if (asLink) {\n // Return <link> tag — CSS cached by browser\n return React.createElement(\"link\", {\n rel: \"stylesheet\",\n href: `/_next/static/css/tw/${routeFile}`,\n crossOrigin: \"anonymous\",\n })\n }\n\n // Inline <style> — zero network request, fastest FCP\n return React.createElement(\"style\", {\n dangerouslySetInnerHTML: { __html: final },\n \"data-tw-route\": targetRoute,\n })\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Hook for client components\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Lightweight hook to get current route's CSS classes.\n * Useful for dynamic class injection in client components.\n *\n * Returns empty string on server (SSR) — CSS already injected by TwCssInjector.\n */\nexport function useTwClasses(classes: string): string {\n // In client environment, return classes as-is\n // CSS is already handled by TwCssInjector at server level\n return classes\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction loadCssFile(filepath: string): string | null {\n try {\n if (fs.existsSync(filepath)) {\n return fs.readFileSync(filepath, \"utf-8\")\n }\n } catch {\n // file not found or unreadable\n }\n return null\n}\n\nfunction routeToFilename(route: string): string {\n if (route === \"/\") return \"index.css\"\n if (route === \"__global\") return \"_global.css\"\n return `${route.replace(/^\\//, \"\").replace(/\\//g, \"_\")}.css`\n}\n\nfunction detectManifestPath(cssDir: string): string | null {\n const candidates = [\n path.join(cssDir, \"css-manifest.json\"),\n path.join(process.cwd(), \"public\", \"__tw\", \"css-manifest.json\"),\n path.join(process.cwd(), \"artifacts\", \"route-css\", \"css-manifest.json\"),\n ]\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n\ninterface CssManifest {\n routes?: Record<string, string>\n global?: string\n [key: string]: unknown\n}\n\nfunction loadCssFromManifest(manifestPath: string, route: string): string | null {\n try {\n if (!fs.existsSync(manifestPath)) return null\n\n const content = fs.readFileSync(manifestPath, \"utf-8\")\n const manifest: CssManifest = JSON.parse(content)\n\n // Try route-specific CSS\n if (manifest.routes?.[route]) {\n const routeCssPath = path.join(path.dirname(manifestPath), manifest.routes[route])\n return loadCssFile(routeCssPath)\n }\n\n // Fallback: global CSS\n if (manifest.global) {\n const globalCssPath = path.join(path.dirname(manifestPath), manifest.global)\n return loadCssFile(globalCssPath)\n }\n } catch {\n // manifest not found or invalid\n }\n return null\n}\n\nfunction minifyCss(css: string): string {\n return css\n .replace(/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//g, \"\")\n .replace(/\\s+/g, \" \")\n .replace(/\\s*{\\s*/g, \"{\")\n .replace(/\\s*}\\s*/g, \"}\")\n .replace(/\\s*;\\s*/g, \";\")\n .trim()\n}\n","\"use client\"\n\n/**\n * tailwind-styled-v5 — Batched CSS Injector (Client Runtime)\n *\n * Menggantikan pattern inject-per-komponen yang menyebabkan banyak\n * style recalculation saat banyak komponen mount bersamaan.\n *\n * Cara kerja:\n * - Semua CSS rules dari render cycle yang sama dikumpulkan\n * - Satu requestAnimationFrame = satu DOM style update\n * - Deduplication via Set<string> — rule yang sama tidak diinjeksi dua kali\n * - Fallback synchronous untuk SSR / server context\n *\n * Usage (internal, dipakai oleh stateEngine dan containerQuery):\n * import { batchedInject, flushBatchedCss } from \"./batchedInjector\"\n *\n * // Queue a rule\n * batchedInject(\".tw-s-abc123[data-active=\\\"true\\\"]{opacity:0.5}\")\n *\n * // Force flush (biasanya tidak perlu — RAF melakukan ini otomatis)\n * flushBatchedCss()\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Singleton state\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** All injected rules (deduplication registry) */\nconst injected = new Set<string>()\n\n/** Pending rules for current RAF batch */\nconst pending: string[] = []\n\n/** RAF handle — null means no batch is scheduled */\nlet rafHandle: ReturnType<typeof requestAnimationFrame> | null = null\n\n/** The single shared <style> element for all batched runtime CSS */\nlet styleEl: HTMLStyleElement | null = null\n\nfunction getStyleElement(): HTMLStyleElement {\n if (styleEl && document.head.contains(styleEl)) return styleEl\n\n styleEl = document.createElement(\"style\")\n styleEl.id = \"__tw-runtime-css\"\n styleEl.setAttribute(\"data-tw-batched\", \"true\")\n document.head.appendChild(styleEl)\n return styleEl\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Core API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Queue a CSS rule for batched injection.\n * Multiple rules accumulated during one event loop tick are flushed together\n * in one requestAnimationFrame → one style recalculation.\n */\nexport function batchedInject(cssRule: string): void {\n if (typeof window === \"undefined\") return // SSR — no-op\n if (!cssRule || injected.has(cssRule)) return\n\n injected.add(cssRule)\n pending.push(cssRule)\n\n if (rafHandle === null) {\n rafHandle = requestAnimationFrame(flushBatchedCss)\n }\n}\n\n/**\n * Immediately flush all pending CSS rules to the DOM.\n * Called automatically by RAF each frame. Can also be called manually\n * after synchronous component setup where RAF timing is too late.\n */\nexport function flushBatchedCss(): void {\n rafHandle = null\n\n if (pending.length === 0 || typeof document === \"undefined\") return\n\n const el = getStyleElement()\n const css = pending.join(\"\\n\")\n pending.length = 0\n\n // Append rather than replace — preserves previously injected rules\n el.textContent += `\\n${css}`\n}\n\n/**\n * Synchronous inject — skips batching.\n * Use for SSR / critical path where RAF is not available.\n */\nexport function syncInject(cssRule: string): void {\n if (typeof document === \"undefined\") return\n if (!cssRule || injected.has(cssRule)) return\n\n injected.add(cssRule)\n getStyleElement().textContent += `\\n${cssRule}`\n}\n\n/**\n * Check if a rule has already been injected (deduplication check).\n */\nexport function isInjected(cssRule: string): boolean {\n return injected.has(cssRule)\n}\n\n/**\n * Clear all injected rules and remove the style element.\n * Useful for testing / SSR resets. Not for production use.\n */\nexport function resetBatchedCss(): void {\n injected.clear()\n pending.length = 0\n\n if (rafHandle !== null) {\n cancelAnimationFrame(rafHandle)\n rafHandle = null\n }\n\n if (styleEl && document.head.contains(styleEl)) {\n document.head.removeChild(styleEl)\n styleEl = null\n }\n}\n\n/**\n * Get stats about the current injection state (for devtools).\n */\nexport function getBatchedCssStats(): {\n totalInjected: number\n pendingCount: number\n hasBatchScheduled: boolean\n} {\n return {\n totalInjected: injected.size,\n pendingCount: pending.length,\n hasBatchScheduled: rafHandle !== null,\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface ScanWorkspaceOptions {
|
|
2
|
+
includeExtensions?: string[];
|
|
3
|
+
ignoreDirectories?: string[];
|
|
4
|
+
useCache?: boolean;
|
|
5
|
+
cacheDir?: string;
|
|
6
|
+
smartInvalidation?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface ScanFileResult {
|
|
9
|
+
file: string;
|
|
10
|
+
classes: string[];
|
|
11
|
+
}
|
|
12
|
+
interface ScanWorkspaceResult {
|
|
13
|
+
files: ScanFileResult[];
|
|
14
|
+
totalFiles: number;
|
|
15
|
+
uniqueClasses: string[];
|
|
16
|
+
}
|
|
17
|
+
declare const DEFAULT_EXTENSIONS: string[];
|
|
18
|
+
declare const DEFAULT_IGNORES: string[];
|
|
19
|
+
declare function scanSource(source: string): string[];
|
|
20
|
+
declare function isScannableFile(filePath: string, includeExtensions?: string[]): boolean;
|
|
21
|
+
declare function scanFile(filePath: string): ScanFileResult;
|
|
22
|
+
declare function scanWorkspace(rootDir: string, options?: ScanWorkspaceOptions): ScanWorkspaceResult;
|
|
23
|
+
declare function scanWorkspaceAsync(rootDir: string, options?: ScanWorkspaceOptions): Promise<ScanWorkspaceResult>;
|
|
24
|
+
|
|
25
|
+
export { DEFAULT_EXTENSIONS, DEFAULT_IGNORES, type ScanFileResult, type ScanWorkspaceOptions, type ScanWorkspaceResult, isScannableFile, scanFile, scanSource, scanWorkspace, scanWorkspaceAsync };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface ScanWorkspaceOptions {
|
|
2
|
+
includeExtensions?: string[];
|
|
3
|
+
ignoreDirectories?: string[];
|
|
4
|
+
useCache?: boolean;
|
|
5
|
+
cacheDir?: string;
|
|
6
|
+
smartInvalidation?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface ScanFileResult {
|
|
9
|
+
file: string;
|
|
10
|
+
classes: string[];
|
|
11
|
+
}
|
|
12
|
+
interface ScanWorkspaceResult {
|
|
13
|
+
files: ScanFileResult[];
|
|
14
|
+
totalFiles: number;
|
|
15
|
+
uniqueClasses: string[];
|
|
16
|
+
}
|
|
17
|
+
declare const DEFAULT_EXTENSIONS: string[];
|
|
18
|
+
declare const DEFAULT_IGNORES: string[];
|
|
19
|
+
declare function scanSource(source: string): string[];
|
|
20
|
+
declare function isScannableFile(filePath: string, includeExtensions?: string[]): boolean;
|
|
21
|
+
declare function scanFile(filePath: string): ScanFileResult;
|
|
22
|
+
declare function scanWorkspace(rootDir: string, options?: ScanWorkspaceOptions): ScanWorkspaceResult;
|
|
23
|
+
declare function scanWorkspaceAsync(rootDir: string, options?: ScanWorkspaceOptions): Promise<ScanWorkspaceResult>;
|
|
24
|
+
|
|
25
|
+
export { DEFAULT_EXTENSIONS, DEFAULT_IGNORES, type ScanFileResult, type ScanWorkspaceOptions, type ScanWorkspaceResult, isScannableFile, scanFile, scanSource, scanWorkspace, scanWorkspaceAsync };
|