@tenphi/tasty 0.0.0-snapshot.08a6610
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/LICENSE +21 -0
- package/README.md +666 -0
- package/dist/_virtual/_rolldown/runtime.js +8 -0
- package/dist/chunks/cacheKey.js +70 -0
- package/dist/chunks/cacheKey.js.map +1 -0
- package/dist/chunks/definitions.d.ts +37 -0
- package/dist/chunks/definitions.js +259 -0
- package/dist/chunks/definitions.js.map +1 -0
- package/dist/chunks/renderChunk.js +61 -0
- package/dist/chunks/renderChunk.js.map +1 -0
- package/dist/config.d.ts +311 -0
- package/dist/config.js +458 -0
- package/dist/config.js.map +1 -0
- package/dist/core/index.d.ts +32 -0
- package/dist/core/index.js +26 -0
- package/dist/debug.d.ts +204 -0
- package/dist/debug.js +733 -0
- package/dist/debug.js.map +1 -0
- package/dist/hooks/useGlobalStyles.d.ts +30 -0
- package/dist/hooks/useGlobalStyles.js +83 -0
- package/dist/hooks/useGlobalStyles.js.map +1 -0
- package/dist/hooks/useKeyframes.d.ts +56 -0
- package/dist/hooks/useKeyframes.js +69 -0
- package/dist/hooks/useKeyframes.js.map +1 -0
- package/dist/hooks/useProperty.d.ts +79 -0
- package/dist/hooks/useProperty.js +114 -0
- package/dist/hooks/useProperty.js.map +1 -0
- package/dist/hooks/useRawCSS.d.ts +53 -0
- package/dist/hooks/useRawCSS.js +40 -0
- package/dist/hooks/useRawCSS.js.map +1 -0
- package/dist/hooks/useStyles.d.ts +45 -0
- package/dist/hooks/useStyles.js +248 -0
- package/dist/hooks/useStyles.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +33 -0
- package/dist/injector/index.d.ts +165 -0
- package/dist/injector/index.js +162 -0
- package/dist/injector/index.js.map +1 -0
- package/dist/injector/injector.d.ts +148 -0
- package/dist/injector/injector.js +430 -0
- package/dist/injector/injector.js.map +1 -0
- package/dist/injector/sheet-manager.d.ts +136 -0
- package/dist/injector/sheet-manager.js +729 -0
- package/dist/injector/sheet-manager.js.map +1 -0
- package/dist/injector/types.d.ts +144 -0
- package/dist/keyframes/index.js +206 -0
- package/dist/keyframes/index.js.map +1 -0
- package/dist/parser/classify.js +319 -0
- package/dist/parser/classify.js.map +1 -0
- package/dist/parser/const.js +33 -0
- package/dist/parser/const.js.map +1 -0
- package/dist/parser/lru.js +109 -0
- package/dist/parser/lru.js.map +1 -0
- package/dist/parser/parser.d.ts +25 -0
- package/dist/parser/parser.js +116 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/tokenizer.js +69 -0
- package/dist/parser/tokenizer.js.map +1 -0
- package/dist/parser/types.d.ts +51 -0
- package/dist/parser/types.js +46 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/pipeline/conditions.d.ts +134 -0
- package/dist/pipeline/conditions.js +406 -0
- package/dist/pipeline/conditions.js.map +1 -0
- package/dist/pipeline/exclusive.js +231 -0
- package/dist/pipeline/exclusive.js.map +1 -0
- package/dist/pipeline/index.d.ts +53 -0
- package/dist/pipeline/index.js +660 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/materialize.js +856 -0
- package/dist/pipeline/materialize.js.map +1 -0
- package/dist/pipeline/parseStateKey.d.ts +15 -0
- package/dist/pipeline/parseStateKey.js +451 -0
- package/dist/pipeline/parseStateKey.js.map +1 -0
- package/dist/pipeline/simplify.js +516 -0
- package/dist/pipeline/simplify.js.map +1 -0
- package/dist/pipeline/warnings.js +18 -0
- package/dist/pipeline/warnings.js.map +1 -0
- package/dist/plugins/okhsl-plugin.d.ts +35 -0
- package/dist/plugins/okhsl-plugin.js +371 -0
- package/dist/plugins/okhsl-plugin.js.map +1 -0
- package/dist/plugins/types.d.ts +76 -0
- package/dist/properties/index.js +236 -0
- package/dist/properties/index.js.map +1 -0
- package/dist/properties/property-type-resolver.d.ts +24 -0
- package/dist/properties/property-type-resolver.js +91 -0
- package/dist/properties/property-type-resolver.js.map +1 -0
- package/dist/ssr/astro.d.ts +29 -0
- package/dist/ssr/astro.js +65 -0
- package/dist/ssr/astro.js.map +1 -0
- package/dist/ssr/async-storage.d.ts +17 -0
- package/dist/ssr/async-storage.js +35 -0
- package/dist/ssr/async-storage.js.map +1 -0
- package/dist/ssr/collect-auto-properties.js +40 -0
- package/dist/ssr/collect-auto-properties.js.map +1 -0
- package/dist/ssr/collector.d.ts +85 -0
- package/dist/ssr/collector.js +183 -0
- package/dist/ssr/collector.js.map +1 -0
- package/dist/ssr/context.d.ts +8 -0
- package/dist/ssr/context.js +14 -0
- package/dist/ssr/context.js.map +1 -0
- package/dist/ssr/format-global-rules.js +22 -0
- package/dist/ssr/format-global-rules.js.map +1 -0
- package/dist/ssr/format-keyframes.js +70 -0
- package/dist/ssr/format-keyframes.js.map +1 -0
- package/dist/ssr/format-property.js +48 -0
- package/dist/ssr/format-property.js.map +1 -0
- package/dist/ssr/format-rules.js +70 -0
- package/dist/ssr/format-rules.js.map +1 -0
- package/dist/ssr/hydrate.d.ts +22 -0
- package/dist/ssr/hydrate.js +50 -0
- package/dist/ssr/hydrate.js.map +1 -0
- package/dist/ssr/index.d.ts +5 -0
- package/dist/ssr/index.js +12 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/ssr/next.d.ts +45 -0
- package/dist/ssr/next.js +71 -0
- package/dist/ssr/next.js.map +1 -0
- package/dist/ssr/ssr-collector-ref.js +12 -0
- package/dist/ssr/ssr-collector-ref.js.map +1 -0
- package/dist/states/index.d.ts +49 -0
- package/dist/states/index.js +416 -0
- package/dist/states/index.js.map +1 -0
- package/dist/static/index.d.ts +5 -0
- package/dist/static/index.js +5 -0
- package/dist/static/tastyStatic.d.ts +46 -0
- package/dist/static/tastyStatic.js +31 -0
- package/dist/static/tastyStatic.js.map +1 -0
- package/dist/static/types.d.ts +49 -0
- package/dist/static/types.js +24 -0
- package/dist/static/types.js.map +1 -0
- package/dist/styles/align.d.ts +15 -0
- package/dist/styles/align.js +14 -0
- package/dist/styles/align.js.map +1 -0
- package/dist/styles/border.d.ts +25 -0
- package/dist/styles/border.js +114 -0
- package/dist/styles/border.js.map +1 -0
- package/dist/styles/color.d.ts +14 -0
- package/dist/styles/color.js +23 -0
- package/dist/styles/color.js.map +1 -0
- package/dist/styles/createStyle.js +77 -0
- package/dist/styles/createStyle.js.map +1 -0
- package/dist/styles/dimension.js +97 -0
- package/dist/styles/dimension.js.map +1 -0
- package/dist/styles/display.d.ts +37 -0
- package/dist/styles/display.js +67 -0
- package/dist/styles/display.js.map +1 -0
- package/dist/styles/fade.d.ts +15 -0
- package/dist/styles/fade.js +58 -0
- package/dist/styles/fade.js.map +1 -0
- package/dist/styles/fill.d.ts +42 -0
- package/dist/styles/fill.js +52 -0
- package/dist/styles/fill.js.map +1 -0
- package/dist/styles/flow.d.ts +16 -0
- package/dist/styles/flow.js +12 -0
- package/dist/styles/flow.js.map +1 -0
- package/dist/styles/gap.d.ts +31 -0
- package/dist/styles/gap.js +37 -0
- package/dist/styles/gap.js.map +1 -0
- package/dist/styles/height.d.ts +17 -0
- package/dist/styles/height.js +20 -0
- package/dist/styles/height.js.map +1 -0
- package/dist/styles/index.d.ts +2 -0
- package/dist/styles/index.js +9 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/inset.d.ts +52 -0
- package/dist/styles/inset.js +150 -0
- package/dist/styles/inset.js.map +1 -0
- package/dist/styles/justify.d.ts +15 -0
- package/dist/styles/justify.js +14 -0
- package/dist/styles/justify.js.map +1 -0
- package/dist/styles/list.d.ts +16 -0
- package/dist/styles/list.js +98 -0
- package/dist/styles/list.js.map +1 -0
- package/dist/styles/margin.d.ts +24 -0
- package/dist/styles/margin.js +104 -0
- package/dist/styles/margin.js.map +1 -0
- package/dist/styles/outline.d.ts +29 -0
- package/dist/styles/outline.js +65 -0
- package/dist/styles/outline.js.map +1 -0
- package/dist/styles/padding.d.ts +24 -0
- package/dist/styles/padding.js +104 -0
- package/dist/styles/padding.js.map +1 -0
- package/dist/styles/predefined.d.ts +71 -0
- package/dist/styles/predefined.js +238 -0
- package/dist/styles/predefined.js.map +1 -0
- package/dist/styles/preset.d.ts +47 -0
- package/dist/styles/preset.js +126 -0
- package/dist/styles/preset.js.map +1 -0
- package/dist/styles/radius.d.ts +14 -0
- package/dist/styles/radius.js +51 -0
- package/dist/styles/radius.js.map +1 -0
- package/dist/styles/scrollbar.d.ts +25 -0
- package/dist/styles/scrollbar.js +48 -0
- package/dist/styles/scrollbar.js.map +1 -0
- package/dist/styles/shadow.d.ts +14 -0
- package/dist/styles/shadow.js +24 -0
- package/dist/styles/shadow.js.map +1 -0
- package/dist/styles/transition.d.ts +14 -0
- package/dist/styles/transition.js +158 -0
- package/dist/styles/transition.js.map +1 -0
- package/dist/styles/types.d.ts +508 -0
- package/dist/styles/width.d.ts +17 -0
- package/dist/styles/width.js +20 -0
- package/dist/styles/width.js.map +1 -0
- package/dist/tasty.d.ts +981 -0
- package/dist/tasty.js +206 -0
- package/dist/tasty.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/utils/cache-wrapper.js +26 -0
- package/dist/utils/cache-wrapper.js.map +1 -0
- package/dist/utils/case-converter.js +8 -0
- package/dist/utils/case-converter.js.map +1 -0
- package/dist/utils/colors.d.ts +5 -0
- package/dist/utils/colors.js +9 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/css-types.d.ts +7 -0
- package/dist/utils/dotize.d.ts +26 -0
- package/dist/utils/dotize.js +122 -0
- package/dist/utils/dotize.js.map +1 -0
- package/dist/utils/filter-base-props.d.ts +15 -0
- package/dist/utils/filter-base-props.js +45 -0
- package/dist/utils/filter-base-props.js.map +1 -0
- package/dist/utils/get-display-name.d.ts +7 -0
- package/dist/utils/get-display-name.js +10 -0
- package/dist/utils/get-display-name.js.map +1 -0
- package/dist/utils/hsl-to-rgb.js +38 -0
- package/dist/utils/hsl-to-rgb.js.map +1 -0
- package/dist/utils/is-dev-env.js +19 -0
- package/dist/utils/is-dev-env.js.map +1 -0
- package/dist/utils/is-valid-element-type.js +15 -0
- package/dist/utils/is-valid-element-type.js.map +1 -0
- package/dist/utils/merge-styles.d.ts +7 -0
- package/dist/utils/merge-styles.js +146 -0
- package/dist/utils/merge-styles.js.map +1 -0
- package/dist/utils/mod-attrs.d.ts +8 -0
- package/dist/utils/mod-attrs.js +21 -0
- package/dist/utils/mod-attrs.js.map +1 -0
- package/dist/utils/okhsl-to-rgb.js +296 -0
- package/dist/utils/okhsl-to-rgb.js.map +1 -0
- package/dist/utils/process-tokens.d.ts +31 -0
- package/dist/utils/process-tokens.js +171 -0
- package/dist/utils/process-tokens.js.map +1 -0
- package/dist/utils/resolve-recipes.d.ts +17 -0
- package/dist/utils/resolve-recipes.js +147 -0
- package/dist/utils/resolve-recipes.js.map +1 -0
- package/dist/utils/selector-transform.js +32 -0
- package/dist/utils/selector-transform.js.map +1 -0
- package/dist/utils/string.js +8 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/utils/styles.d.ts +177 -0
- package/dist/utils/styles.js +730 -0
- package/dist/utils/styles.js.map +1 -0
- package/dist/utils/typography.d.ts +47 -0
- package/dist/utils/typography.js +43 -0
- package/dist/utils/typography.js.map +1 -0
- package/dist/utils/warnings.d.ts +16 -0
- package/dist/utils/warnings.js +16 -0
- package/dist/utils/warnings.js.map +1 -0
- package/dist/zero/babel.d.ts +147 -0
- package/dist/zero/babel.js +331 -0
- package/dist/zero/babel.js.map +1 -0
- package/dist/zero/css-writer.d.ts +45 -0
- package/dist/zero/css-writer.js +74 -0
- package/dist/zero/css-writer.js.map +1 -0
- package/dist/zero/extractor.d.ts +24 -0
- package/dist/zero/extractor.js +215 -0
- package/dist/zero/extractor.js.map +1 -0
- package/dist/zero/index.d.ts +3 -0
- package/dist/zero/index.js +4 -0
- package/dist/zero/next.d.ts +74 -0
- package/dist/zero/next.js +129 -0
- package/dist/zero/next.js.map +1 -0
- package/docs/adoption.md +286 -0
- package/docs/comparison.md +413 -0
- package/docs/configuration.md +242 -0
- package/docs/debug.md +505 -0
- package/docs/design-system.md +401 -0
- package/docs/dsl.md +540 -0
- package/docs/getting-started.md +201 -0
- package/docs/injector.md +528 -0
- package/docs/methodology.md +501 -0
- package/docs/runtime.md +291 -0
- package/docs/ssr.md +382 -0
- package/docs/styles.md +574 -0
- package/docs/tasty-static.md +421 -0
- package/package.json +209 -0
- package/tasty.config.ts +14 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SSRCacheState, ServerStyleCollector } from "./collector.js";
|
|
2
|
+
import { hydrateTastyCache } from "./hydrate.js";
|
|
3
|
+
import { TastySSRContext } from "./context.js";
|
|
4
|
+
import { getSSRCollector, runWithCollector } from "./async-storage.js";
|
|
5
|
+
export { type SSRCacheState, ServerStyleCollector, TastySSRContext, getSSRCollector, hydrateTastyCache, runWithCollector };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TastySSRContext } from "./context.js";
|
|
2
|
+
import { registerSSRCollectorGetter } from "./ssr-collector-ref.js";
|
|
3
|
+
import { ServerStyleCollector } from "./collector.js";
|
|
4
|
+
import { getSSRCollector, runWithCollector } from "./async-storage.js";
|
|
5
|
+
import { hydrateTastyCache } from "./hydrate.js";
|
|
6
|
+
|
|
7
|
+
//#region src/ssr/index.ts
|
|
8
|
+
registerSSRCollectorGetter(getSSRCollector);
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { ServerStyleCollector, TastySSRContext, getSSRCollector, hydrateTastyCache, runWithCollector };
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/ssr/index.ts"],"sourcesContent":["/**\n * SSR entry point for @tenphi/tasty.\n *\n * Provides the core SSR infrastructure: ServerStyleCollector,\n * React context, AsyncLocalStorage integration, and cache hydration.\n *\n * Import from '@tenphi/tasty/ssr'.\n */\n\n// Core collector\nexport { ServerStyleCollector } from './collector';\nexport type { SSRCacheState } from './collector';\n\n// React context for Next.js streaming\nexport { TastySSRContext } from './context';\n\n// AsyncLocalStorage integration for Astro / generic frameworks\nexport { runWithCollector, getSSRCollector } from './async-storage';\n\n// Client-side cache hydration\nexport { hydrateTastyCache } from './hydrate';\n\n// Register the ALS getter so useStyles can find the collector\n// without importing 'node:async_hooks' in the browser bundle.\nimport { getSSRCollector } from './async-storage';\nimport { registerSSRCollectorGetter } from './ssr-collector-ref';\n\nregisterSSRCollectorGetter(getSSRCollector);\n"],"mappings":";;;;;;;AA2BA,2BAA2B,gBAAgB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ServerStyleCollector } from "./collector.js";
|
|
2
|
+
import * as react from "react";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/ssr/next.d.ts
|
|
6
|
+
interface TastyRegistryProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to embed the cache state script for client hydration.
|
|
10
|
+
* Set to false to skip cache transfer (useful when cache size
|
|
11
|
+
* exceeds the hydration benefit). Default: true.
|
|
12
|
+
*/
|
|
13
|
+
transferCache?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Next.js App Router registry for Tasty SSR.
|
|
17
|
+
*
|
|
18
|
+
* Wraps the component tree with a ServerStyleCollector and flushes
|
|
19
|
+
* collected CSS into the HTML stream via useServerInsertedHTML.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* // app/tasty-registry.tsx
|
|
24
|
+
* 'use client';
|
|
25
|
+
* import { TastyRegistry } from '@tenphi/tasty/ssr/next';
|
|
26
|
+
* export default function TastyStyleRegistry({ children }) {
|
|
27
|
+
* return <TastyRegistry>{children}</TastyRegistry>;
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // app/layout.tsx
|
|
31
|
+
* import TastyStyleRegistry from './tasty-registry';
|
|
32
|
+
* export default function RootLayout({ children }) {
|
|
33
|
+
* return <html><body>
|
|
34
|
+
* <TastyStyleRegistry>{children}</TastyStyleRegistry>
|
|
35
|
+
* </body></html>;
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function TastyRegistry({
|
|
40
|
+
children,
|
|
41
|
+
transferCache
|
|
42
|
+
}: TastyRegistryProps): react.FunctionComponentElement<react.ProviderProps<ServerStyleCollector | null>>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { TastyRegistry, TastyRegistryProps };
|
|
45
|
+
//# sourceMappingURL=next.d.ts.map
|
package/dist/ssr/next.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { getConfig } from "../config.js";
|
|
4
|
+
import { TastySSRContext } from "./context.js";
|
|
5
|
+
import { ServerStyleCollector } from "./collector.js";
|
|
6
|
+
import { hydrateTastyCache } from "./hydrate.js";
|
|
7
|
+
import { Fragment, createElement, useState } from "react";
|
|
8
|
+
import { useServerInsertedHTML } from "next/navigation";
|
|
9
|
+
|
|
10
|
+
//#region src/ssr/next.ts
|
|
11
|
+
/**
|
|
12
|
+
* Next.js integration for Tasty SSR.
|
|
13
|
+
*
|
|
14
|
+
* Provides TastyRegistry for App Router (streaming via useServerInsertedHTML)
|
|
15
|
+
* and createTastySSRDocument for Pages Router (non-streaming).
|
|
16
|
+
*
|
|
17
|
+
* Import from '@tenphi/tasty/ssr/next'.
|
|
18
|
+
*/
|
|
19
|
+
if (typeof window !== "undefined" && window.__TASTY_SSR_CACHE__) hydrateTastyCache(window.__TASTY_SSR_CACHE__);
|
|
20
|
+
/**
|
|
21
|
+
* Next.js App Router registry for Tasty SSR.
|
|
22
|
+
*
|
|
23
|
+
* Wraps the component tree with a ServerStyleCollector and flushes
|
|
24
|
+
* collected CSS into the HTML stream via useServerInsertedHTML.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* // app/tasty-registry.tsx
|
|
29
|
+
* 'use client';
|
|
30
|
+
* import { TastyRegistry } from '@tenphi/tasty/ssr/next';
|
|
31
|
+
* export default function TastyStyleRegistry({ children }) {
|
|
32
|
+
* return <TastyRegistry>{children}</TastyRegistry>;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // app/layout.tsx
|
|
36
|
+
* import TastyStyleRegistry from './tasty-registry';
|
|
37
|
+
* export default function RootLayout({ children }) {
|
|
38
|
+
* return <html><body>
|
|
39
|
+
* <TastyStyleRegistry>{children}</TastyStyleRegistry>
|
|
40
|
+
* </body></html>;
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function TastyRegistry({ children, transferCache = true }) {
|
|
45
|
+
const isClient = typeof window !== "undefined";
|
|
46
|
+
const [collector] = useState(() => isClient ? null : new ServerStyleCollector());
|
|
47
|
+
const nonce = getConfig().nonce;
|
|
48
|
+
useServerInsertedHTML(() => {
|
|
49
|
+
if (!collector) return null;
|
|
50
|
+
const css = collector.flushCSS();
|
|
51
|
+
const cacheState = collector.getCacheState();
|
|
52
|
+
if (!css) return null;
|
|
53
|
+
const styleEl = createElement("style", {
|
|
54
|
+
key: "tasty-ssr-styles",
|
|
55
|
+
"data-tasty-ssr": "",
|
|
56
|
+
nonce,
|
|
57
|
+
dangerouslySetInnerHTML: { __html: css }
|
|
58
|
+
});
|
|
59
|
+
if (!transferCache) return styleEl;
|
|
60
|
+
return createElement(Fragment, null, styleEl, createElement("script", {
|
|
61
|
+
key: "tasty-ssr-cache",
|
|
62
|
+
nonce,
|
|
63
|
+
dangerouslySetInnerHTML: { __html: `(window.__TASTY_SSR_CACHE__=window.__TASTY_SSR_CACHE__||{entries:{},classCounter:0});Object.assign(window.__TASTY_SSR_CACHE__.entries,${JSON.stringify(cacheState.entries)});window.__TASTY_SSR_CACHE__.classCounter=${cacheState.classCounter};` }
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
return createElement(TastySSRContext.Provider, { value: collector }, children);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { TastyRegistry };
|
|
71
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.js","names":[],"sources":["../../src/ssr/next.ts"],"sourcesContent":["/**\n * Next.js integration for Tasty SSR.\n *\n * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML)\n * and createTastySSRDocument for Pages Router (non-streaming).\n *\n * Import from '@tenphi/tasty/ssr/next'.\n */\n\n'use client';\n\n/// <reference path=\"./next-navigation.d.ts\" />\n\nimport { createElement, Fragment, useState, type ReactNode } from 'react';\nimport { useServerInsertedHTML } from 'next/navigation';\n\nimport { getConfig } from '../config';\nimport { ServerStyleCollector } from './collector';\nimport { TastySSRContext } from './context';\nimport { hydrateTastyCache } from './hydrate';\n\n// Auto-hydrate on module load (client only).\n// When this module is imported by the TastyRegistry client component,\n// the streaming cache scripts have already populated __TASTY_SSR_CACHE__.\nif (typeof window !== 'undefined' && window.__TASTY_SSR_CACHE__) {\n hydrateTastyCache(window.__TASTY_SSR_CACHE__);\n}\n\nexport interface TastyRegistryProps {\n children: ReactNode;\n /**\n * Whether to embed the cache state script for client hydration.\n * Set to false to skip cache transfer (useful when cache size\n * exceeds the hydration benefit). Default: true.\n */\n transferCache?: boolean;\n}\n\n/**\n * Next.js App Router registry for Tasty SSR.\n *\n * Wraps the component tree with a ServerStyleCollector and flushes\n * collected CSS into the HTML stream via useServerInsertedHTML.\n *\n * @example\n * ```tsx\n * // app/tasty-registry.tsx\n * 'use client';\n * import { TastyRegistry } from '@tenphi/tasty/ssr/next';\n * export default function TastyStyleRegistry({ children }) {\n * return <TastyRegistry>{children}</TastyRegistry>;\n * }\n *\n * // app/layout.tsx\n * import TastyStyleRegistry from './tasty-registry';\n * export default function RootLayout({ children }) {\n * return <html><body>\n * <TastyStyleRegistry>{children}</TastyStyleRegistry>\n * </body></html>;\n * }\n * ```\n */\nexport function TastyRegistry({\n children,\n transferCache = true,\n}: TastyRegistryProps) {\n const isClient = typeof window !== 'undefined';\n\n const [collector] = useState(() =>\n isClient ? null : new ServerStyleCollector(),\n );\n const nonce = getConfig().nonce;\n\n useServerInsertedHTML(() => {\n if (!collector) return null;\n\n const css = collector.flushCSS();\n const cacheState = collector.getCacheState();\n\n if (!css) return null;\n\n const styleEl = createElement('style', {\n key: 'tasty-ssr-styles',\n 'data-tasty-ssr': '',\n nonce,\n dangerouslySetInnerHTML: { __html: css },\n });\n\n if (!transferCache) return styleEl;\n\n const scriptEl = createElement('script', {\n key: 'tasty-ssr-cache',\n nonce,\n dangerouslySetInnerHTML: {\n __html:\n `(window.__TASTY_SSR_CACHE__=window.__TASTY_SSR_CACHE__||{entries:{},classCounter:0});` +\n `Object.assign(window.__TASTY_SSR_CACHE__.entries,${JSON.stringify(cacheState.entries)});` +\n `window.__TASTY_SSR_CACHE__.classCounter=${cacheState.classCounter};`,\n },\n });\n\n return createElement(Fragment, null, styleEl, scriptEl);\n });\n\n return createElement(\n TastySSRContext.Provider,\n { value: collector },\n children,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,IAAI,OAAO,WAAW,eAAe,OAAO,oBAC1C,mBAAkB,OAAO,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;AAqC/C,SAAgB,cAAc,EAC5B,UACA,gBAAgB,QACK;CACrB,MAAM,WAAW,OAAO,WAAW;CAEnC,MAAM,CAAC,aAAa,eAClB,WAAW,OAAO,IAAI,sBAAsB,CAC7C;CACD,MAAM,QAAQ,WAAW,CAAC;AAE1B,6BAA4B;AAC1B,MAAI,CAAC,UAAW,QAAO;EAEvB,MAAM,MAAM,UAAU,UAAU;EAChC,MAAM,aAAa,UAAU,eAAe;AAE5C,MAAI,CAAC,IAAK,QAAO;EAEjB,MAAM,UAAU,cAAc,SAAS;GACrC,KAAK;GACL,kBAAkB;GAClB;GACA,yBAAyB,EAAE,QAAQ,KAAK;GACzC,CAAC;AAEF,MAAI,CAAC,cAAe,QAAO;AAa3B,SAAO,cAAc,UAAU,MAAM,SAXpB,cAAc,UAAU;GACvC,KAAK;GACL;GACA,yBAAyB,EACvB,QACE,yIACoD,KAAK,UAAU,WAAW,QAAQ,CAAC,4CAC5C,WAAW,aAAa,IACtE;GACF,CAAC,CAEqD;GACvD;AAEF,QAAO,cACL,gBAAgB,UAChB,EAAE,OAAO,WAAW,EACpB,SACD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/ssr/ssr-collector-ref.ts
|
|
2
|
+
let _getSSRCollector = null;
|
|
3
|
+
function registerSSRCollectorGetter(fn) {
|
|
4
|
+
_getSSRCollector = fn;
|
|
5
|
+
}
|
|
6
|
+
function getRegisteredSSRCollector() {
|
|
7
|
+
return _getSSRCollector ? _getSSRCollector() : null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { getRegisteredSSRCollector, registerSSRCollectorGetter };
|
|
12
|
+
//# sourceMappingURL=ssr-collector-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-collector-ref.js","names":[],"sources":["../../src/ssr/ssr-collector-ref.ts"],"sourcesContent":["/**\n * Global reference to the SSR collector getter function.\n *\n * This indirection avoids importing 'node:async_hooks' in the browser bundle.\n * The SSR entry point sets this ref when loaded on the server. The useStyles\n * hook calls it if set; on the client it stays null and is never called.\n */\n\nimport type { ServerStyleCollector } from './collector';\n\ntype SSRCollectorGetter = () => ServerStyleCollector | null;\n\nlet _getSSRCollector: SSRCollectorGetter | null = null;\n\nexport function registerSSRCollectorGetter(fn: SSRCollectorGetter): void {\n _getSSRCollector = fn;\n}\n\nexport function getRegisteredSSRCollector(): ServerStyleCollector | null {\n return _getSSRCollector ? _getSSRCollector() : null;\n}\n"],"mappings":";AAYA,IAAI,mBAA8C;AAElD,SAAgB,2BAA2B,IAA8B;AACvE,oBAAmB;;AAGrB,SAAgB,4BAAyD;AACvE,QAAO,mBAAmB,kBAAkB,GAAG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Styles } from "../styles/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/states/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Parsed advanced state information
|
|
6
|
+
*/
|
|
7
|
+
interface ParsedAdvancedState {
|
|
8
|
+
type: 'media' | 'container' | 'root' | 'parent' | 'own' | 'starting' | 'predefined' | 'modifier';
|
|
9
|
+
condition: string;
|
|
10
|
+
containerName?: string;
|
|
11
|
+
raw: string;
|
|
12
|
+
mediaType?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Context for state parsing operations
|
|
16
|
+
*/
|
|
17
|
+
interface StateParserContext {
|
|
18
|
+
localPredefinedStates: Record<string, string>;
|
|
19
|
+
globalPredefinedStates: Record<string, string>;
|
|
20
|
+
isSubElement?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* At-rule context for CSS generation
|
|
24
|
+
*/
|
|
25
|
+
interface AtRuleContext {
|
|
26
|
+
media?: string[];
|
|
27
|
+
container?: {
|
|
28
|
+
name?: string;
|
|
29
|
+
condition: string;
|
|
30
|
+
}[];
|
|
31
|
+
startingStyle?: boolean;
|
|
32
|
+
rootStates?: string[];
|
|
33
|
+
negatedRootStates?: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Configure global predefined states
|
|
37
|
+
*/
|
|
38
|
+
declare function setGlobalPredefinedStates(states: Record<string, string>): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get global predefined states
|
|
41
|
+
*/
|
|
42
|
+
declare function getGlobalPredefinedStates(): Record<string, string>;
|
|
43
|
+
/**
|
|
44
|
+
* Create a state parser context from styles
|
|
45
|
+
*/
|
|
46
|
+
declare function createStateParserContext(styles?: Styles, isSubElement?: boolean): StateParserContext;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { AtRuleContext, ParsedAdvancedState, StateParserContext, createStateParserContext, getGlobalPredefinedStates, setGlobalPredefinedStates };
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { isDevEnv } from "../utils/is-dev-env.js";
|
|
2
|
+
import { hasStylesGenerated } from "../config.js";
|
|
3
|
+
|
|
4
|
+
//#region src/states/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Advanced State Mapping - Predefined States Management
|
|
7
|
+
*
|
|
8
|
+
* This module handles global and local predefined states for the Tasty styling system.
|
|
9
|
+
* See ADVANCED_STATE_MAPPING.md for full specification.
|
|
10
|
+
*/
|
|
11
|
+
const BUILTIN_STATES = new Set([
|
|
12
|
+
"@starting",
|
|
13
|
+
"@keyframes",
|
|
14
|
+
"@properties",
|
|
15
|
+
"@supports",
|
|
16
|
+
"@inherit"
|
|
17
|
+
]);
|
|
18
|
+
const RESERVED_PREFIXES = [
|
|
19
|
+
"@media",
|
|
20
|
+
"@root",
|
|
21
|
+
"@parent",
|
|
22
|
+
"@own",
|
|
23
|
+
"@(",
|
|
24
|
+
"@starting",
|
|
25
|
+
"@keyframes",
|
|
26
|
+
"@properties",
|
|
27
|
+
"@supports",
|
|
28
|
+
"@inherit"
|
|
29
|
+
];
|
|
30
|
+
let globalPredefinedStates = {};
|
|
31
|
+
const emittedWarnings = /* @__PURE__ */ new Set();
|
|
32
|
+
const devMode = isDevEnv();
|
|
33
|
+
/**
|
|
34
|
+
* Emit a warning only once
|
|
35
|
+
*/
|
|
36
|
+
function warnOnce(key, message) {
|
|
37
|
+
if (devMode && !emittedWarnings.has(key)) {
|
|
38
|
+
emittedWarnings.add(key);
|
|
39
|
+
console.warn(message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Configure global predefined states
|
|
44
|
+
*/
|
|
45
|
+
function setGlobalPredefinedStates(states) {
|
|
46
|
+
if (hasStylesGenerated()) {
|
|
47
|
+
const newStateNames = Object.keys(states).join(", ");
|
|
48
|
+
warnOnce(`dynamic-states:${newStateNames}`, `[Tasty] Cannot update predefined states after styles have been generated.\nThe new definition(s) for ${newStateNames} will be ignored.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const [name, value] of Object.entries(states)) {
|
|
52
|
+
if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(name)) {
|
|
53
|
+
warnOnce(`invalid-state-name:${name}`, `[Tasty] Invalid predefined state name '${name}'. Must start with '@' followed by a letter.`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (BUILTIN_STATES.has(name)) {
|
|
57
|
+
warnOnce(`reserved-state:${name}`, `[Tasty] Cannot define predefined state '${name}'. This name is reserved for built-in functionality.`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (name === "@media" || name === "@root" || name === "@parent" || name === "@own" || name.startsWith("@(")) {
|
|
61
|
+
warnOnce(`reserved-prefix:${name}`, `[Tasty] Cannot define predefined state '${name}'. This prefix is reserved for built-in functionality.`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const crossRefs = extractPredefinedStateRefs(value);
|
|
65
|
+
if (crossRefs.length > 0) {
|
|
66
|
+
warnOnce(`cross-ref:${name}`, `[Tasty] Predefined state '${name}' references another predefined state '${crossRefs[0]}'.\nPredefined states cannot reference each other. Use the full definition instead.`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (globalPredefinedStates[name] && globalPredefinedStates[name] !== value) warnOnce(`duplicate-state:${name}`, `[Tasty] Duplicate predefined state '${name}' in configure(). The last definition will be used.`);
|
|
70
|
+
globalPredefinedStates[name] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get global predefined states
|
|
75
|
+
*/
|
|
76
|
+
function getGlobalPredefinedStates() {
|
|
77
|
+
return globalPredefinedStates;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Regex to match predefined state references in a string
|
|
81
|
+
* Matches @name that is NOT followed by ( or : and is a complete word
|
|
82
|
+
* Uses word boundary and negative lookahead
|
|
83
|
+
*/
|
|
84
|
+
const PREDEFINED_STATE_PATTERN = /@([A-Za-z][A-Za-z0-9-]*)(?![A-Za-z0-9-:(])/g;
|
|
85
|
+
/**
|
|
86
|
+
* Extract predefined state references from a string
|
|
87
|
+
*/
|
|
88
|
+
function extractPredefinedStateRefs(value) {
|
|
89
|
+
const matches = value.matchAll(PREDEFINED_STATE_PATTERN);
|
|
90
|
+
const refs = [];
|
|
91
|
+
for (const match of matches) {
|
|
92
|
+
const stateName = "@" + match[1];
|
|
93
|
+
if (!BUILTIN_STATES.has(stateName) && !refs.includes(stateName)) refs.push(stateName);
|
|
94
|
+
}
|
|
95
|
+
return refs;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if a state key is a predefined state reference
|
|
99
|
+
*/
|
|
100
|
+
function isPredefinedStateRef(stateKey) {
|
|
101
|
+
if (!stateKey.startsWith("@")) return false;
|
|
102
|
+
if (BUILTIN_STATES.has(stateKey)) return false;
|
|
103
|
+
for (const prefix of RESERVED_PREFIXES) if (stateKey === prefix || stateKey.startsWith(prefix)) {
|
|
104
|
+
if (stateKey === "@media" || stateKey.startsWith("@media(") || stateKey.startsWith("@media:")) return false;
|
|
105
|
+
if (stateKey === "@root" || stateKey.startsWith("@root(")) return false;
|
|
106
|
+
if (stateKey === "@parent" || stateKey.startsWith("@parent(")) return false;
|
|
107
|
+
if (stateKey === "@own" || stateKey.startsWith("@own(")) return false;
|
|
108
|
+
if (stateKey.startsWith("@(")) return false;
|
|
109
|
+
}
|
|
110
|
+
return /^@[A-Za-z][A-Za-z0-9-]*$/.test(stateKey);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extract local predefined states from a styles object
|
|
114
|
+
* Local predefined states are top-level keys starting with @ that have string values
|
|
115
|
+
* and are valid predefined state names (not built-in like @media, @root, etc.)
|
|
116
|
+
*/
|
|
117
|
+
function extractLocalPredefinedStates(styles) {
|
|
118
|
+
const localStates = {};
|
|
119
|
+
if (!styles || typeof styles !== "object") return localStates;
|
|
120
|
+
for (const [key, value] of Object.entries(styles)) if (key.startsWith("@") && typeof value === "string") {
|
|
121
|
+
if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(key)) continue;
|
|
122
|
+
if (BUILTIN_STATES.has(key)) continue;
|
|
123
|
+
if (key === "@media" || key === "@root" || key === "@parent" || key === "@own" || key.startsWith("@(")) continue;
|
|
124
|
+
const crossRefs = extractPredefinedStateRefs(value);
|
|
125
|
+
if (crossRefs.length > 0) {
|
|
126
|
+
warnOnce(`local-cross-ref:${key}`, `[Tasty] Predefined state '${key}' references another predefined state '${crossRefs[0]}'.\nPredefined states cannot reference each other. Use the full definition instead.`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
localStates[key] = value;
|
|
130
|
+
}
|
|
131
|
+
return localStates;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a state parser context from styles
|
|
135
|
+
*/
|
|
136
|
+
function createStateParserContext(styles, isSubElement) {
|
|
137
|
+
return {
|
|
138
|
+
localPredefinedStates: styles ? extractLocalPredefinedStates(styles) : {},
|
|
139
|
+
globalPredefinedStates: getGlobalPredefinedStates(),
|
|
140
|
+
isSubElement
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Resolve a predefined state reference to its value
|
|
145
|
+
* Returns the resolved value or null if not found
|
|
146
|
+
*/
|
|
147
|
+
function resolvePredefinedState(stateKey, ctx) {
|
|
148
|
+
if (ctx.localPredefinedStates[stateKey]) return ctx.localPredefinedStates[stateKey];
|
|
149
|
+
if (ctx.globalPredefinedStates[stateKey]) return ctx.globalPredefinedStates[stateKey];
|
|
150
|
+
warnOnce(`undefined-state:${stateKey}`, `[Tasty] Undefined predefined state '${stateKey}'.\nDefine it in configure({ states: { '${stateKey}': '...' } }) or in the component's styles.`);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Expand dimension shorthands in a condition string
|
|
155
|
+
* w -> width, h -> height, is -> inline-size, bs -> block-size
|
|
156
|
+
*/
|
|
157
|
+
function expandDimensionShorthands(condition) {
|
|
158
|
+
let result = condition;
|
|
159
|
+
result = result.replace(/\bw\b/g, "width");
|
|
160
|
+
result = result.replace(/\bh\b/g, "height");
|
|
161
|
+
result = result.replace(/\bis\b/g, "inline-size");
|
|
162
|
+
result = result.replace(/\bbs\b/g, "block-size");
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Convert tasty units in a string (e.g., 40x -> calc(var(--gap) * 40))
|
|
167
|
+
*/
|
|
168
|
+
function expandTastyUnits(value) {
|
|
169
|
+
return value.replace(/(\d+(?:\.\d+)?)\s*x\b/g, (_, num) => {
|
|
170
|
+
return `calc(var(--gap) * ${num})`;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Parse an advanced state key and return its type and components
|
|
175
|
+
*/
|
|
176
|
+
function parseAdvancedState(stateKey, ctx) {
|
|
177
|
+
const raw = stateKey;
|
|
178
|
+
if (stateKey === "@starting") return {
|
|
179
|
+
type: "starting",
|
|
180
|
+
condition: "",
|
|
181
|
+
raw
|
|
182
|
+
};
|
|
183
|
+
if (stateKey.startsWith("@media:")) {
|
|
184
|
+
const mediaType = stateKey.slice(7);
|
|
185
|
+
if (![
|
|
186
|
+
"all",
|
|
187
|
+
"screen",
|
|
188
|
+
"print",
|
|
189
|
+
"speech"
|
|
190
|
+
].includes(mediaType)) warnOnce(`unknown-media-type:${mediaType}`, `[Tasty] Unknown media type '${mediaType}'. Valid types: all, screen, print, speech.`);
|
|
191
|
+
return {
|
|
192
|
+
type: "media",
|
|
193
|
+
condition: "",
|
|
194
|
+
mediaType,
|
|
195
|
+
raw
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (stateKey.startsWith("@media(")) {
|
|
199
|
+
const endParen = findMatchingParen(stateKey, 6);
|
|
200
|
+
if (endParen === -1) {
|
|
201
|
+
warnOnce(`unclosed-media:${stateKey}`, `[Tasty] Unclosed media query '${stateKey}'. Missing closing parenthesis.`);
|
|
202
|
+
return {
|
|
203
|
+
type: "modifier",
|
|
204
|
+
condition: stateKey,
|
|
205
|
+
raw
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
let condition = stateKey.slice(7, endParen);
|
|
209
|
+
if (!condition.trim()) {
|
|
210
|
+
warnOnce(`empty-media:${stateKey}`, `[Tasty] Empty media query condition '${stateKey}'.`);
|
|
211
|
+
return {
|
|
212
|
+
type: "modifier",
|
|
213
|
+
condition: stateKey,
|
|
214
|
+
raw
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
condition = expandDimensionShorthands(condition);
|
|
218
|
+
condition = expandTastyUnits(condition);
|
|
219
|
+
return {
|
|
220
|
+
type: "media",
|
|
221
|
+
condition,
|
|
222
|
+
raw
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (stateKey.startsWith("@root(")) {
|
|
226
|
+
const endParen = findMatchingParen(stateKey, 5);
|
|
227
|
+
if (endParen === -1) {
|
|
228
|
+
warnOnce(`unclosed-root:${stateKey}`, `[Tasty] Unclosed root state '${stateKey}'. Missing closing parenthesis.`);
|
|
229
|
+
return {
|
|
230
|
+
type: "modifier",
|
|
231
|
+
condition: stateKey,
|
|
232
|
+
raw
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const condition = stateKey.slice(6, endParen);
|
|
236
|
+
if (!condition.trim()) {
|
|
237
|
+
warnOnce(`empty-root:${stateKey}`, `[Tasty] Empty root state condition '${stateKey}'.`);
|
|
238
|
+
return {
|
|
239
|
+
type: "modifier",
|
|
240
|
+
condition: stateKey,
|
|
241
|
+
raw
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
type: "root",
|
|
246
|
+
condition,
|
|
247
|
+
raw
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (stateKey.startsWith("@parent(")) {
|
|
251
|
+
const endParen = findMatchingParen(stateKey, 7);
|
|
252
|
+
if (endParen === -1) {
|
|
253
|
+
warnOnce(`unclosed-parent:${stateKey}`, `[Tasty] Unclosed parent state '${stateKey}'. Missing closing parenthesis.`);
|
|
254
|
+
return {
|
|
255
|
+
type: "modifier",
|
|
256
|
+
condition: stateKey,
|
|
257
|
+
raw
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const condition = stateKey.slice(8, endParen);
|
|
261
|
+
if (!condition.trim()) {
|
|
262
|
+
warnOnce(`empty-parent:${stateKey}`, `[Tasty] Empty parent state condition '${stateKey}'.`);
|
|
263
|
+
return {
|
|
264
|
+
type: "modifier",
|
|
265
|
+
condition: stateKey,
|
|
266
|
+
raw
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
type: "parent",
|
|
271
|
+
condition,
|
|
272
|
+
raw
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (stateKey.startsWith("@own(")) {
|
|
276
|
+
const endParen = findMatchingParen(stateKey, 4);
|
|
277
|
+
if (endParen === -1) {
|
|
278
|
+
warnOnce(`unclosed-own:${stateKey}`, `[Tasty] Unclosed own state '${stateKey}'. Missing closing parenthesis.`);
|
|
279
|
+
return {
|
|
280
|
+
type: "modifier",
|
|
281
|
+
condition: stateKey,
|
|
282
|
+
raw
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const condition = stateKey.slice(5, endParen);
|
|
286
|
+
if (!condition.trim()) {
|
|
287
|
+
warnOnce(`empty-own:${stateKey}`, `[Tasty] Empty own state condition '${stateKey}'.`);
|
|
288
|
+
return {
|
|
289
|
+
type: "modifier",
|
|
290
|
+
condition: stateKey,
|
|
291
|
+
raw
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (!ctx.isSubElement) {
|
|
295
|
+
warnOnce(`own-outside-subelement:${stateKey}`, `[Tasty] @own(${condition}) used outside sub-element context.\n@own() is equivalent to '${condition}' at the root level. Did you mean to use it inside a sub-element?`);
|
|
296
|
+
return {
|
|
297
|
+
type: "modifier",
|
|
298
|
+
condition,
|
|
299
|
+
raw
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
type: "own",
|
|
304
|
+
condition,
|
|
305
|
+
raw
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (stateKey.startsWith("@(")) {
|
|
309
|
+
const endParen = findMatchingParen(stateKey, 1);
|
|
310
|
+
if (endParen === -1) {
|
|
311
|
+
warnOnce(`unclosed-container:${stateKey}`, `[Tasty] Unclosed container query '${stateKey}'. Missing closing parenthesis.`);
|
|
312
|
+
return {
|
|
313
|
+
type: "modifier",
|
|
314
|
+
condition: stateKey,
|
|
315
|
+
raw
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
const content = stateKey.slice(2, endParen);
|
|
319
|
+
if (!content.trim()) {
|
|
320
|
+
warnOnce(`empty-container:${stateKey}`, `[Tasty] Empty container query '${stateKey}'.`);
|
|
321
|
+
return {
|
|
322
|
+
type: "modifier",
|
|
323
|
+
condition: stateKey,
|
|
324
|
+
raw
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const commaIndex = findTopLevelComma(content);
|
|
328
|
+
let containerName;
|
|
329
|
+
let condition;
|
|
330
|
+
if (commaIndex !== -1) {
|
|
331
|
+
containerName = content.slice(0, commaIndex).trim();
|
|
332
|
+
condition = content.slice(commaIndex + 1).trim();
|
|
333
|
+
} else condition = content.trim();
|
|
334
|
+
if (condition.startsWith("$")) {
|
|
335
|
+
const styleCondition = parseStyleQuery(condition);
|
|
336
|
+
if (!styleCondition) {
|
|
337
|
+
warnOnce(`invalid-style-query:${stateKey}`, `[Tasty] Invalid style query '${condition}' in container query.`);
|
|
338
|
+
return {
|
|
339
|
+
type: "modifier",
|
|
340
|
+
condition: stateKey,
|
|
341
|
+
raw
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
condition = styleCondition;
|
|
345
|
+
} else if (/^[a-zA-Z][\w-]*\s*\(/.test(condition)) {} else {
|
|
346
|
+
condition = expandDimensionShorthands(condition);
|
|
347
|
+
condition = expandTastyUnits(condition);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
type: "container",
|
|
351
|
+
condition,
|
|
352
|
+
containerName,
|
|
353
|
+
raw
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (isPredefinedStateRef(stateKey)) {
|
|
357
|
+
const resolved = resolvePredefinedState(stateKey, ctx);
|
|
358
|
+
if (resolved) return {
|
|
359
|
+
...parseAdvancedState(resolved, ctx),
|
|
360
|
+
raw
|
|
361
|
+
};
|
|
362
|
+
return {
|
|
363
|
+
type: "modifier",
|
|
364
|
+
condition: stateKey,
|
|
365
|
+
raw
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
type: "modifier",
|
|
370
|
+
condition: stateKey,
|
|
371
|
+
raw
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Parse a style query condition (e.g., $compact, $variant=compact, $variant="very compact")
|
|
376
|
+
*/
|
|
377
|
+
function parseStyleQuery(condition) {
|
|
378
|
+
const query = condition.slice(1);
|
|
379
|
+
if (/[<>]/.test(query)) {
|
|
380
|
+
warnOnce(`style-query-comparison:${condition}`, `[Tasty] Style queries only support equality. '${condition}' is invalid. Use '${condition.split(/[<>]/)[0]}=...' instead.`);
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const eqIndex = query.indexOf("=");
|
|
384
|
+
if (eqIndex === -1) return `style(--${query})`;
|
|
385
|
+
const propName = query.slice(0, eqIndex).trim();
|
|
386
|
+
let value = query.slice(eqIndex + 1).trim();
|
|
387
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) {} else value = `"${value}"`;
|
|
388
|
+
value = expandTastyUnits(value);
|
|
389
|
+
return `style(--${propName}: ${value})`;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Find the index of the first comma at parentheses depth 0.
|
|
393
|
+
* Returns -1 if no top-level comma is found.
|
|
394
|
+
* This prevents splitting on commas inside function calls like scroll-state(a, b).
|
|
395
|
+
*/
|
|
396
|
+
function findTopLevelComma(s) {
|
|
397
|
+
let depth = 0;
|
|
398
|
+
for (let i = 0; i < s.length; i++) if (s[i] === "(") depth++;
|
|
399
|
+
else if (s[i] === ")") depth--;
|
|
400
|
+
else if (s[i] === "," && depth === 0) return i;
|
|
401
|
+
return -1;
|
|
402
|
+
}
|
|
403
|
+
function findMatchingParen(str, startIndex) {
|
|
404
|
+
let depth = 1;
|
|
405
|
+
let i = startIndex + 1;
|
|
406
|
+
while (i < str.length && depth > 0) {
|
|
407
|
+
if (str[i] === "(") depth++;
|
|
408
|
+
else if (str[i] === ")") depth--;
|
|
409
|
+
i++;
|
|
410
|
+
}
|
|
411
|
+
return depth === 0 ? i - 1 : -1;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
export { createStateParserContext, expandDimensionShorthands, expandTastyUnits, extractLocalPredefinedStates, extractPredefinedStateRefs, findTopLevelComma, getGlobalPredefinedStates, parseAdvancedState, resolvePredefinedState, setGlobalPredefinedStates };
|
|
416
|
+
//# sourceMappingURL=index.js.map
|