@tenphi/tasty 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.js +8 -0
- package/dist/zero/{babel.d.mts → babel.d.ts} +6 -6
- package/dist/zero/{babel.mjs → babel.js} +6 -6
- package/dist/zero/babel.js.map +1 -0
- package/dist/{css-writer.d.mts → zero/css-writer.d.ts} +1 -1
- package/dist/{css-writer.mjs → zero/css-writer.js} +1 -1
- package/dist/zero/css-writer.js.map +1 -0
- package/dist/{extractor.d.mts → zero/extractor.d.ts} +3 -2
- package/dist/{extractor.mjs → zero/extractor.js} +6 -6
- package/dist/zero/extractor.js.map +1 -0
- package/dist/zero/index.d.ts +3 -0
- package/dist/zero/{index.mjs → index.js} +2 -2
- package/dist/zero/{next.d.mts → next.d.ts} +1 -1
- package/dist/zero/{next.mjs → next.js} +2 -2
- package/dist/zero/next.js.map +1 -0
- package/package.json +16 -23
- package/dist/_virtual/_rolldown/runtime.mjs +0 -7
- package/dist/chunks/cacheKey.mjs +0 -70
- package/dist/chunks/cacheKey.mjs.map +0 -1
- package/dist/chunks/definitions.mjs +0 -260
- package/dist/chunks/definitions.mjs.map +0 -1
- package/dist/chunks/renderChunk.mjs +0 -61
- package/dist/chunks/renderChunk.mjs.map +0 -1
- package/dist/config.mjs +0 -231
- package/dist/config.mjs.map +0 -1
- package/dist/css-writer.mjs.map +0 -1
- package/dist/extractor.mjs.map +0 -1
- package/dist/injector/injector.mjs +0 -404
- package/dist/injector/injector.mjs.map +0 -1
- package/dist/injector/sheet-manager.mjs +0 -714
- package/dist/injector/sheet-manager.mjs.map +0 -1
- package/dist/injector/types.d.mts +0 -18
- package/dist/keyframes/index.mjs +0 -156
- package/dist/keyframes/index.mjs.map +0 -1
- package/dist/parser/classify.mjs +0 -319
- package/dist/parser/classify.mjs.map +0 -1
- package/dist/parser/const.mjs +0 -33
- package/dist/parser/const.mjs.map +0 -1
- package/dist/parser/lru.mjs +0 -109
- package/dist/parser/lru.mjs.map +0 -1
- package/dist/parser/parser.mjs +0 -116
- package/dist/parser/parser.mjs.map +0 -1
- package/dist/parser/tokenizer.mjs +0 -69
- package/dist/parser/tokenizer.mjs.map +0 -1
- package/dist/parser/types.d.mts +0 -37
- package/dist/parser/types.mjs +0 -46
- package/dist/parser/types.mjs.map +0 -1
- package/dist/pipeline/conditions.mjs +0 -377
- package/dist/pipeline/conditions.mjs.map +0 -1
- package/dist/pipeline/exclusive.mjs +0 -231
- package/dist/pipeline/exclusive.mjs.map +0 -1
- package/dist/pipeline/index.mjs +0 -635
- package/dist/pipeline/index.mjs.map +0 -1
- package/dist/pipeline/materialize.mjs +0 -821
- package/dist/pipeline/materialize.mjs.map +0 -1
- package/dist/pipeline/parseStateKey.mjs +0 -418
- package/dist/pipeline/parseStateKey.mjs.map +0 -1
- package/dist/pipeline/simplify.mjs +0 -557
- package/dist/pipeline/simplify.mjs.map +0 -1
- package/dist/plugins/okhsl-plugin.mjs +0 -345
- package/dist/plugins/okhsl-plugin.mjs.map +0 -1
- package/dist/plugins/types.d.mts +0 -49
- package/dist/properties/index.mjs +0 -141
- package/dist/properties/index.mjs.map +0 -1
- package/dist/states/index.mjs +0 -161
- package/dist/states/index.mjs.map +0 -1
- package/dist/styles/align.mjs +0 -14
- package/dist/styles/align.mjs.map +0 -1
- package/dist/styles/border.mjs +0 -114
- package/dist/styles/border.mjs.map +0 -1
- package/dist/styles/color.mjs +0 -23
- package/dist/styles/color.mjs.map +0 -1
- package/dist/styles/createStyle.mjs +0 -77
- package/dist/styles/createStyle.mjs.map +0 -1
- package/dist/styles/dimension.mjs +0 -97
- package/dist/styles/dimension.mjs.map +0 -1
- package/dist/styles/display.mjs +0 -67
- package/dist/styles/display.mjs.map +0 -1
- package/dist/styles/fade.mjs +0 -58
- package/dist/styles/fade.mjs.map +0 -1
- package/dist/styles/fill.mjs +0 -51
- package/dist/styles/fill.mjs.map +0 -1
- package/dist/styles/flow.mjs +0 -12
- package/dist/styles/flow.mjs.map +0 -1
- package/dist/styles/gap.mjs +0 -37
- package/dist/styles/gap.mjs.map +0 -1
- package/dist/styles/height.mjs +0 -20
- package/dist/styles/height.mjs.map +0 -1
- package/dist/styles/index.mjs +0 -9
- package/dist/styles/index.mjs.map +0 -1
- package/dist/styles/inset.mjs +0 -142
- package/dist/styles/inset.mjs.map +0 -1
- package/dist/styles/justify.mjs +0 -14
- package/dist/styles/justify.mjs.map +0 -1
- package/dist/styles/margin.mjs +0 -96
- package/dist/styles/margin.mjs.map +0 -1
- package/dist/styles/outline.mjs +0 -65
- package/dist/styles/outline.mjs.map +0 -1
- package/dist/styles/padding.mjs +0 -96
- package/dist/styles/padding.mjs.map +0 -1
- package/dist/styles/predefined.mjs +0 -232
- package/dist/styles/predefined.mjs.map +0 -1
- package/dist/styles/preset.mjs +0 -126
- package/dist/styles/preset.mjs.map +0 -1
- package/dist/styles/radius.mjs +0 -51
- package/dist/styles/radius.mjs.map +0 -1
- package/dist/styles/scrollbar.mjs +0 -105
- package/dist/styles/scrollbar.mjs.map +0 -1
- package/dist/styles/shadow.mjs +0 -24
- package/dist/styles/shadow.mjs.map +0 -1
- package/dist/styles/styledScrollbar.mjs +0 -38
- package/dist/styles/styledScrollbar.mjs.map +0 -1
- package/dist/styles/transition.mjs +0 -138
- package/dist/styles/transition.mjs.map +0 -1
- package/dist/styles/types.d.mts +0 -492
- package/dist/styles/width.mjs +0 -20
- package/dist/styles/width.mjs.map +0 -1
- package/dist/utils/cache-wrapper.mjs +0 -26
- package/dist/utils/cache-wrapper.mjs.map +0 -1
- package/dist/utils/case-converter.mjs +0 -8
- package/dist/utils/case-converter.mjs.map +0 -1
- package/dist/utils/hsl-to-rgb.mjs +0 -38
- package/dist/utils/hsl-to-rgb.mjs.map +0 -1
- package/dist/utils/is-dev-env.mjs +0 -19
- package/dist/utils/is-dev-env.mjs.map +0 -1
- package/dist/utils/merge-styles.mjs +0 -146
- package/dist/utils/merge-styles.mjs.map +0 -1
- package/dist/utils/okhsl-to-rgb.mjs +0 -296
- package/dist/utils/okhsl-to-rgb.mjs.map +0 -1
- package/dist/utils/process-tokens.mjs +0 -28
- package/dist/utils/process-tokens.mjs.map +0 -1
- package/dist/utils/resolve-recipes.mjs +0 -143
- package/dist/utils/resolve-recipes.mjs.map +0 -1
- package/dist/utils/string.mjs +0 -8
- package/dist/utils/string.mjs.map +0 -1
- package/dist/utils/styles.d.mts +0 -18
- package/dist/utils/styles.mjs +0 -346
- package/dist/utils/styles.mjs.map +0 -1
- package/dist/zero/babel.mjs.map +0 -1
- package/dist/zero/index.d.mts +0 -3
- package/dist/zero/next.mjs.map +0 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
|
|
3
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
4
|
+
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { __require };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { RecipeStyles } from "../styles/types.
|
|
5
|
-
import { TastyPlugin } from "../plugins/types.
|
|
1
|
+
import { KeyframesSteps } from "../injector/types.js";
|
|
2
|
+
import { StyleDetails, UnitHandler } from "../parser/types.js";
|
|
3
|
+
import { StyleHandlerDefinition } from "../utils/styles.js";
|
|
4
|
+
import { RecipeStyles } from "../styles/types.js";
|
|
5
|
+
import { TastyPlugin } from "../plugins/types.js";
|
|
6
6
|
import * as _babel_core0 from "@babel/core";
|
|
7
7
|
import { PluginPass } from "@babel/core";
|
|
8
8
|
|
|
@@ -105,4 +105,4 @@ interface TastyZeroBabelOptions {
|
|
|
105
105
|
declare const _default: (api: object, options: TastyZeroBabelOptions | null | undefined, dirname: string) => _babel_core0.PluginObj<PluginPass>;
|
|
106
106
|
//#endregion
|
|
107
107
|
export { TastyZeroBabelOptions, TastyZeroConfig, _default as default };
|
|
108
|
-
//# sourceMappingURL=babel.d.
|
|
108
|
+
//# sourceMappingURL=babel.d.ts.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { configure } from "../config.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { configure } from "../config.js";
|
|
2
|
+
import { mergeStyles } from "../utils/merge-styles.js";
|
|
3
|
+
import { resolveRecipes } from "../utils/resolve-recipes.js";
|
|
4
|
+
import { extractKeyframesFromStyles, extractStylesForSelector, extractStylesWithChunks } from "./extractor.js";
|
|
5
|
+
import { CSSWriter } from "./css-writer.js";
|
|
6
6
|
import { declare } from "@babel/helper-plugin-utils";
|
|
7
7
|
import * as t from "@babel/types";
|
|
8
8
|
|
|
@@ -279,4 +279,4 @@ function escapeRegex(str) {
|
|
|
279
279
|
|
|
280
280
|
//#endregion
|
|
281
281
|
export { babel_default as default };
|
|
282
|
-
//# sourceMappingURL=babel.
|
|
282
|
+
//# sourceMappingURL=babel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"babel.js","names":[],"sources":["../../src/zero/babel.ts"],"sourcesContent":["/**\n * Babel plugin for zero-runtime tasty static site generation.\n *\n * Transforms:\n * - `tastyStatic(styles)` → StaticStyle object { className, styles, toString() }\n * - `tastyStatic(base, styles)` → StaticStyle object with merged styles\n * - `tastyStatic(selector, styles)` → removed entirely\n *\n * Usage:\n * ```javascript\n * // babel.config.js\n * module.exports = {\n * plugins: [\n * ['@tenphi/tasty/babel-plugin', { output: 'public/tasty.css' }]\n * ]\n * };\n * ```\n */\n\nimport { declare } from '@babel/helper-plugin-utils';\nimport * as t from '@babel/types';\n\nimport { configure } from '../config';\nimport type { RecipeStyles, Styles } from '../styles/types';\nimport { mergeStyles } from '../utils/merge-styles';\nimport { resolveRecipes } from '../utils/resolve-recipes';\nimport type { StyleHandlerDefinition } from '../utils/styles';\n\nimport { CSSWriter } from './css-writer';\nimport {\n extractKeyframesFromStyles,\n extractStylesForSelector,\n extractStylesWithChunks,\n} from './extractor';\n\nimport type { NodePath, PluginPass } from '@babel/core';\nimport type { KeyframesSteps } from '../injector/types';\nimport type { StyleDetails, UnitHandler } from '../parser/types';\nimport type { TastyPlugin } from '../plugins/types';\n\n/**\n * Build-time configuration for zero-runtime mode.\n * Subset of TastyConfig that applies at build time.\n */\nexport interface TastyZeroConfig {\n /**\n * Global predefined states for advanced state mapping.\n * Example: { '@mobile': '@media(w < 768px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Enable development mode features: source comments in generated CSS.\n * Default: false\n */\n devMode?: boolean;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for builds with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, states, handlers, or tokens.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * // babel.config.js\n * module.exports = {\n * plugins: [\n * ['@tenphi/tasty/babel-plugin', {\n * config: { plugins: [okhslPlugin()] }\n * }]\n * ]\n * };\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions for static extraction.\n * Keys are animation names, values are keyframes step definitions.\n * @example { fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } } }\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * handlers: {\n * fill: ({ fill }) => fill ? { 'background-color': fill } : undefined,\n * elevation: ({ elevation }) => ({\n * 'box-shadow': `0 ${elevation}px ${elevation * 2}px rgba(0,0,0,0.1)`,\n * }),\n * }\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Use `$name` for custom properties and `#name` for color tokens.\n * @example { $spacing: '2x', '#accent': '#purple' }\n */\n tokens?: Record<`$${string}` | `#${string}`, string | number>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys).\n * @example\n * ```ts\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * }\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\nexport interface TastyZeroBabelOptions {\n /** Output path for generated CSS (default: 'tasty.css') */\n output?: string;\n /** Tasty configuration for build-time processing */\n config?: TastyZeroConfig;\n}\n\n/**\n * Registry to track StaticStyle objects by their variable names.\n * Used to resolve base styles when extending.\n */\ntype StaticStyleRegistry = Record<string, {\n styles: Styles;\n className: string;\n }>;\n\ninterface PluginState extends PluginPass {\n staticStyleRegistry: StaticStyleRegistry;\n /** Current source file path (for devMode source comments) */\n sourceFile?: string;\n}\n\n// @ts-expect-error PluginState vs PluginPass type mismatch in @babel/helper-plugin-utils\nexport default declare<TastyZeroBabelOptions>((api, options) => {\n api.assertVersion(7);\n\n const outputPath = options.output || 'tasty.css';\n const config = options.config || {};\n const devMode = config.devMode ?? false;\n\n // Apply configuration using the shared configure() function\n // This handles plugin merging, states, units, funcs, handlers, tokens, etc.\n configure(config);\n\n const cssWriter = new CSSWriter(outputPath, { devMode });\n\n // Global registry for cross-file references (same build)\n const globalRegistry: StaticStyleRegistry = {};\n\n return {\n name: 'tasty-zero',\n\n pre(this: PluginState) {\n // Initialize per-file registry\n this.staticStyleRegistry = {};\n // Extract source filename for devMode comments\n if (devMode && this.filename) {\n // Get relative path or just filename\n this.sourceFile = this.filename.split('/').pop() || this.filename;\n }\n },\n\n visitor: {\n // Remove import statements for tasty/static\n ImportDeclaration(path: NodePath<t.ImportDeclaration>) {\n const source = path.node.source.value;\n\n if (\n source === '@tenphi/tasty/static' ||\n source.endsWith('/tasty/static')\n ) {\n path.remove();\n }\n },\n\n // Transform tastyStatic() calls\n CallExpression(path: NodePath<t.CallExpression>, state: PluginState) {\n const callee = path.node.callee;\n\n // Match tastyStatic(...) calls\n if (!t.isIdentifier(callee, { name: 'tastyStatic' })) {\n return;\n }\n\n const args = path.node.arguments;\n\n if (args.length === 0) {\n throw path.buildCodeFrameError(\n 'tastyStatic() requires at least one argument',\n );\n }\n\n const firstArg = args[0];\n\n if (t.isStringLiteral(firstArg)) {\n // Selector mode: tastyStatic(selector, styles)\n handleSelectorMode(\n path,\n args,\n cssWriter,\n state.sourceFile,\n config.keyframes,\n );\n } else if (t.isObjectExpression(firstArg)) {\n // Styles mode: tastyStatic(styles)\n handleStylesMode(\n path,\n args,\n cssWriter,\n state,\n globalRegistry,\n config.keyframes,\n );\n } else if (t.isIdentifier(firstArg)) {\n // Extension mode: tastyStatic(base, styles)\n handleExtensionMode(\n path,\n args,\n cssWriter,\n state,\n globalRegistry,\n config.keyframes,\n );\n } else {\n throw path.buildCodeFrameError(\n 'tastyStatic() first argument must be an object (styles), ' +\n 'identifier (base StaticStyle), or string (selector)',\n );\n }\n },\n\n // Track variable declarations to register StaticStyle objects\n VariableDeclarator(\n path: NodePath<t.VariableDeclarator>,\n state: PluginState,\n ) {\n const init = path.node.init;\n const id = path.node.id;\n\n // Check if this is a StaticStyle object (has className and styles properties)\n if (\n t.isIdentifier(id) &&\n t.isObjectExpression(init) &&\n isStaticStyleObject(init)\n ) {\n const variableName = id.name;\n const styles = extractStylesFromStaticStyleObject(init, path);\n const className = extractClassNameFromStaticStyleObject(init);\n\n if (styles && className) {\n state.staticStyleRegistry[variableName] = { styles, className };\n globalRegistry[variableName] = { styles, className };\n }\n }\n },\n },\n\n post() {\n // Write all collected CSS at the end of the build\n if (cssWriter.size > 0) {\n cssWriter.write();\n }\n },\n };\n});\n\n/**\n * Check if an object expression looks like a StaticStyle object\n */\nfunction isStaticStyleObject(node: t.ObjectExpression): boolean {\n const hasClassName = node.properties.some(\n (p) =>\n t.isObjectProperty(p) && t.isIdentifier(p.key, { name: 'className' }),\n );\n const hasStyles = node.properties.some(\n (p) => t.isObjectProperty(p) && t.isIdentifier(p.key, { name: 'styles' }),\n );\n return hasClassName && hasStyles;\n}\n\n/**\n * Extract styles object from a StaticStyle object expression\n */\nfunction extractStylesFromStaticStyleObject(\n node: t.ObjectExpression,\n path: NodePath,\n): Styles | null {\n for (const prop of node.properties) {\n if (\n t.isObjectProperty(prop) &&\n t.isIdentifier(prop.key, { name: 'styles' }) &&\n t.isObjectExpression(prop.value)\n ) {\n return evaluateObjectExpression(prop.value, path) as Styles;\n }\n }\n return null;\n}\n\n/**\n * Extract className from a StaticStyle object expression\n */\nfunction extractClassNameFromStaticStyleObject(\n node: t.ObjectExpression,\n): string | null {\n for (const prop of node.properties) {\n if (\n t.isObjectProperty(prop) &&\n t.isIdentifier(prop.key, { name: 'className' }) &&\n t.isStringLiteral(prop.value)\n ) {\n return prop.value.value;\n }\n }\n return null;\n}\n\n/**\n * Handle tastyStatic(styles) - returns StaticStyle object\n */\nfunction handleStylesMode(\n path: NodePath<t.CallExpression>,\n args: t.CallExpression['arguments'],\n cssWriter: CSSWriter,\n state: PluginState,\n globalRegistry: StaticStyleRegistry,\n globalKeyframes?: Record<string, KeyframesSteps>,\n): void {\n const stylesArg = args[0];\n\n if (!t.isObjectExpression(stylesArg)) {\n throw path.buildCodeFrameError(\n 'tastyStatic(styles) argument must be a static object literal',\n );\n }\n\n // Evaluate styles object at build time\n const rawStyles = evaluateObjectExpression(stylesArg, path) as Styles;\n\n // Resolve recipes before extraction\n const styles = resolveRecipes(rawStyles);\n\n // Extract keyframes (deduplicated by content)\n const { keyframes, nameMap } = extractKeyframesFromStyles(\n styles,\n globalKeyframes,\n );\n\n // Add keyframes CSS\n for (const kf of keyframes) {\n cssWriter.add(kf.css, kf.css, state.sourceFile);\n }\n\n // Extract styles with chunking\n const chunks = extractStylesWithChunks(styles);\n\n // Add CSS to writer, replacing animation names if needed\n for (const chunk of chunks) {\n const css =\n nameMap.size > 0\n ? replaceAnimationNamesInCSS(chunk.css, nameMap)\n : chunk.css;\n cssWriter.add(chunk.className, css, state.sourceFile);\n }\n\n // Generate className\n const className =\n chunks.length > 0 ? chunks.map((c) => c.className).join(' ') : '';\n\n // Replace call with StaticStyle object\n const staticStyleObject = createStaticStyleAST(className, styles);\n path.replaceWith(staticStyleObject);\n\n // Register if this is being assigned to a variable\n registerIfVariableDeclaration(path, className, styles, state, globalRegistry);\n}\n\n/**\n * Handle tastyStatic(base, styles) - extends base with additional styles\n */\nfunction handleExtensionMode(\n path: NodePath<t.CallExpression>,\n args: t.CallExpression['arguments'],\n cssWriter: CSSWriter,\n state: PluginState,\n globalRegistry: StaticStyleRegistry,\n globalKeyframes?: Record<string, KeyframesSteps>,\n): void {\n if (args.length < 2) {\n throw path.buildCodeFrameError(\n 'tastyStatic(base, styles) requires two arguments',\n );\n }\n\n const baseArg = args[0];\n const stylesArg = args[1];\n\n if (!t.isIdentifier(baseArg)) {\n throw path.buildCodeFrameError(\n 'tastyStatic(base, styles) first argument must be an identifier',\n );\n }\n\n if (!t.isObjectExpression(stylesArg)) {\n throw path.buildCodeFrameError(\n 'tastyStatic(base, styles) second argument must be a static object literal',\n );\n }\n\n const baseName = baseArg.name;\n\n // Look up base styles in registry\n const baseEntry =\n state.staticStyleRegistry[baseName] || globalRegistry[baseName];\n\n if (!baseEntry) {\n throw path.buildCodeFrameError(\n `Cannot find base StaticStyle '${baseName}'. ` +\n 'Make sure it is defined before being extended.',\n );\n }\n\n // Evaluate override styles\n const overrideStyles = evaluateObjectExpression(stylesArg, path) as Styles;\n\n // Merge styles using mergeStyles, then resolve recipes\n const mergedStyles = resolveRecipes(\n mergeStyles(baseEntry.styles, overrideStyles),\n );\n\n // Extract keyframes (deduplicated by content)\n const { keyframes, nameMap } = extractKeyframesFromStyles(\n mergedStyles,\n globalKeyframes,\n );\n\n // Add keyframes CSS\n for (const kf of keyframes) {\n cssWriter.add(kf.css, kf.css, state.sourceFile);\n }\n\n // Extract styles with chunking\n const chunks = extractStylesWithChunks(mergedStyles);\n\n // Add CSS to writer, replacing animation names if needed\n for (const chunk of chunks) {\n const css =\n nameMap.size > 0\n ? replaceAnimationNamesInCSS(chunk.css, nameMap)\n : chunk.css;\n cssWriter.add(chunk.className, css, state.sourceFile);\n }\n\n // Generate className\n const className =\n chunks.length > 0 ? chunks.map((c) => c.className).join(' ') : '';\n\n // Replace call with StaticStyle object\n const staticStyleObject = createStaticStyleAST(className, mergedStyles);\n path.replaceWith(staticStyleObject);\n\n // Register if this is being assigned to a variable\n registerIfVariableDeclaration(\n path,\n className,\n mergedStyles,\n state,\n globalRegistry,\n );\n}\n\n/**\n * Handle tastyStatic(selector, styles) - removes the call entirely\n */\nfunction handleSelectorMode(\n path: NodePath<t.CallExpression>,\n args: t.CallExpression['arguments'],\n cssWriter: CSSWriter,\n sourceFile?: string,\n globalKeyframes?: Record<string, KeyframesSteps>,\n): void {\n if (args.length < 2) {\n throw path.buildCodeFrameError(\n 'tastyStatic(selector, styles) requires two arguments',\n );\n }\n\n const selectorArg = args[0];\n const stylesArg = args[1];\n\n if (!t.isStringLiteral(selectorArg)) {\n throw path.buildCodeFrameError(\n 'tastyStatic(selector, styles) first argument must be a string literal',\n );\n }\n\n if (!t.isObjectExpression(stylesArg)) {\n throw path.buildCodeFrameError(\n 'tastyStatic(selector, styles) second argument must be a static object literal',\n );\n }\n\n const selector = selectorArg.value;\n const rawStyles = evaluateObjectExpression(stylesArg, path) as Styles;\n\n // Resolve recipes before extraction\n const styles = resolveRecipes(rawStyles);\n\n // Extract keyframes (deduplicated by content)\n const { keyframes, nameMap } = extractKeyframesFromStyles(\n styles,\n globalKeyframes,\n );\n\n // Add keyframes CSS\n for (const kf of keyframes) {\n cssWriter.add(kf.css, kf.css, sourceFile);\n }\n\n // Extract styles for selector\n const result = extractStylesForSelector(selector, styles);\n\n // Replace animation names if needed and add CSS\n const css =\n nameMap.size > 0\n ? replaceAnimationNamesInCSS(result.css, nameMap)\n : result.css;\n cssWriter.add(selector, css, sourceFile);\n\n // Remove the entire statement\n const parent = path.parentPath;\n if (parent && t.isExpressionStatement(parent.node)) {\n parent.remove();\n } else {\n // If used in an expression context (which would be incorrect usage),\n // replace with undefined\n path.replaceWith(t.identifier('undefined'));\n }\n}\n\n/**\n * Create a StaticStyle object AST node\n */\nfunction createStaticStyleAST(\n className: string,\n styles: Styles,\n): t.ObjectExpression {\n return t.objectExpression([\n t.objectProperty(t.identifier('className'), t.stringLiteral(className)),\n t.objectProperty(t.identifier('styles'), valueToAST(styles)),\n t.objectMethod(\n 'method',\n t.identifier('toString'),\n [],\n t.blockStatement([\n t.returnStatement(\n t.memberExpression(t.thisExpression(), t.identifier('className')),\n ),\n ]),\n ),\n ]);\n}\n\n/**\n * Register a StaticStyle in the registry if it's being assigned to a variable\n */\nfunction registerIfVariableDeclaration(\n path: NodePath,\n className: string,\n styles: Styles,\n state: PluginState,\n globalRegistry: StaticStyleRegistry,\n): void {\n const parent = path.parentPath;\n if (parent && t.isVariableDeclarator(parent.node)) {\n const id = parent.node.id;\n if (t.isIdentifier(id)) {\n const variableName = id.name;\n state.staticStyleRegistry[variableName] = { styles, className };\n globalRegistry[variableName] = { styles, className };\n }\n }\n}\n\n/**\n * Convert a JavaScript value to an AST node\n */\nfunction valueToAST(value: unknown): t.Expression {\n if (value === null) {\n return t.nullLiteral();\n }\n if (value === undefined) {\n return t.identifier('undefined');\n }\n if (typeof value === 'string') {\n return t.stringLiteral(value);\n }\n if (typeof value === 'number') {\n return t.numericLiteral(value);\n }\n if (typeof value === 'boolean') {\n return t.booleanLiteral(value);\n }\n if (Array.isArray(value)) {\n return t.arrayExpression(value.map(valueToAST));\n }\n if (typeof value === 'object') {\n const properties = Object.entries(value).map(([key, val]) =>\n t.objectProperty(\n /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)\n ? t.identifier(key)\n : t.stringLiteral(key),\n valueToAST(val),\n ),\n );\n return t.objectExpression(properties);\n }\n // Fallback for unsupported types\n return t.identifier('undefined');\n}\n\n/**\n * Evaluate an ObjectExpression to a plain JavaScript object.\n * Only supports static values that can be determined at build time.\n */\nfunction evaluateObjectExpression(\n node: t.ObjectExpression,\n path: NodePath,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const prop of node.properties) {\n if (t.isSpreadElement(prop)) {\n throw path.buildCodeFrameError(\n 'Spread elements are not supported in tastyStatic() - styles must be fully static',\n );\n }\n\n if (!t.isObjectProperty(prop)) {\n throw path.buildCodeFrameError(\n 'Only object properties are supported in tastyStatic()',\n );\n }\n\n // Get key\n let key: string;\n if (t.isIdentifier(prop.key)) {\n key = prop.key.name;\n } else if (t.isStringLiteral(prop.key)) {\n key = prop.key.value;\n } else {\n throw path.buildCodeFrameError(\n 'Dynamic property keys are not supported in tastyStatic()',\n );\n }\n\n // Get value\n const value = evaluateExpression(prop.value, path);\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Evaluate an expression to a JavaScript value.\n */\nfunction evaluateExpression(node: t.Node, path: NodePath): unknown {\n if (t.isStringLiteral(node)) {\n return node.value;\n }\n\n if (t.isNumericLiteral(node)) {\n return node.value;\n }\n\n if (t.isBooleanLiteral(node)) {\n return node.value;\n }\n\n if (t.isNullLiteral(node)) {\n return null;\n }\n\n if (t.isIdentifier(node, { name: 'undefined' })) {\n return undefined;\n }\n\n if (t.isArrayExpression(node)) {\n return node.elements.map((el) => {\n if (el === null) return null;\n if (t.isSpreadElement(el)) {\n throw path.buildCodeFrameError(\n 'Spread elements are not supported in tastyStatic()',\n );\n }\n return evaluateExpression(el, path);\n });\n }\n\n if (t.isObjectExpression(node)) {\n return evaluateObjectExpression(node, path);\n }\n\n if (t.isTemplateLiteral(node)) {\n // Only support template literals without expressions\n if (node.expressions.length > 0) {\n throw path.buildCodeFrameError(\n 'Template literals with expressions are not supported in tastyStatic()',\n );\n }\n return node.quasis.map((q) => q.value.cooked).join('');\n }\n\n if (t.isUnaryExpression(node, { operator: '-' })) {\n const arg = evaluateExpression(node.argument, path);\n if (typeof arg === 'number') {\n return -arg;\n }\n }\n\n throw path.buildCodeFrameError(\n `Dynamic expressions are not supported in tastyStatic() - got ${node.type}. ` +\n 'All values must be static literals.',\n );\n}\n\n/**\n * Replace animation names in CSS string.\n * Wraps the keyframes replaceAnimationNames to work on full CSS blocks.\n */\nfunction replaceAnimationNamesInCSS(\n css: string,\n nameMap: Map<string, string>,\n): string {\n if (nameMap.size === 0) return css;\n\n // The CSS contains full rules like \".class { animation: name 1s; }\"\n // We need to replace animation names within declaration blocks\n return css.replace(\n /(animation(?:-name)?)\\s*:\\s*([^;}]+)/gi,\n (match, prop, value) => {\n let newValue = value;\n for (const [original, replacement] of nameMap) {\n // Word boundary replacement\n const pattern = new RegExp(`\\\\b${escapeRegex(original)}\\\\b`, 'g');\n newValue = newValue.replace(pattern, replacement);\n }\n return `${prop}: ${newValue}`;\n },\n );\n}\n\n/**\n * Escape special regex characters.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAyJA,oBAAe,SAAgC,KAAK,YAAY;AAC9D,KAAI,cAAc,EAAE;CAEpB,MAAM,aAAa,QAAQ,UAAU;CACrC,MAAM,SAAS,QAAQ,UAAU,EAAE;CACnC,MAAM,UAAU,OAAO,WAAW;AAIlC,WAAU,OAAO;CAEjB,MAAM,YAAY,IAAI,UAAU,YAAY,EAAE,SAAS,CAAC;CAGxD,MAAM,iBAAsC,EAAE;AAE9C,QAAO;EACL,MAAM;EAEN,MAAuB;AAErB,QAAK,sBAAsB,EAAE;AAE7B,OAAI,WAAW,KAAK,SAElB,MAAK,aAAa,KAAK,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,KAAK;;EAI7D,SAAS;GAEP,kBAAkB,MAAqC;IACrD,MAAM,SAAS,KAAK,KAAK,OAAO;AAEhC,QACE,WAAW,0BACX,OAAO,SAAS,gBAAgB,CAEhC,MAAK,QAAQ;;GAKjB,eAAe,MAAkC,OAAoB;IACnE,MAAM,SAAS,KAAK,KAAK;AAGzB,QAAI,CAAC,EAAE,aAAa,QAAQ,EAAE,MAAM,eAAe,CAAC,CAClD;IAGF,MAAM,OAAO,KAAK,KAAK;AAEvB,QAAI,KAAK,WAAW,EAClB,OAAM,KAAK,oBACT,+CACD;IAGH,MAAM,WAAW,KAAK;AAEtB,QAAI,EAAE,gBAAgB,SAAS,CAE7B,oBACE,MACA,MACA,WACA,MAAM,YACN,OAAO,UACR;aACQ,EAAE,mBAAmB,SAAS,CAEvC,kBACE,MACA,MACA,WACA,OACA,gBACA,OAAO,UACR;aACQ,EAAE,aAAa,SAAS,CAEjC,qBACE,MACA,MACA,WACA,OACA,gBACA,OAAO,UACR;QAED,OAAM,KAAK,oBACT,+GAED;;GAKL,mBACE,MACA,OACA;IACA,MAAM,OAAO,KAAK,KAAK;IACvB,MAAM,KAAK,KAAK,KAAK;AAGrB,QACE,EAAE,aAAa,GAAG,IAClB,EAAE,mBAAmB,KAAK,IAC1B,oBAAoB,KAAK,EACzB;KACA,MAAM,eAAe,GAAG;KACxB,MAAM,SAAS,mCAAmC,MAAM,KAAK;KAC7D,MAAM,YAAY,sCAAsC,KAAK;AAE7D,SAAI,UAAU,WAAW;AACvB,YAAM,oBAAoB,gBAAgB;OAAE;OAAQ;OAAW;AAC/D,qBAAe,gBAAgB;OAAE;OAAQ;OAAW;;;;GAI3D;EAED,OAAO;AAEL,OAAI,UAAU,OAAO,EACnB,WAAU,OAAO;;EAGtB;EACD;;;;AAKF,SAAS,oBAAoB,MAAmC;CAC9D,MAAM,eAAe,KAAK,WAAW,MAClC,MACC,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC,CACxE;CACD,MAAM,YAAY,KAAK,WAAW,MAC/B,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,CAC1E;AACD,QAAO,gBAAgB;;;;;AAMzB,SAAS,mCACP,MACA,MACe;AACf,MAAK,MAAM,QAAQ,KAAK,WACtB,KACE,EAAE,iBAAiB,KAAK,IACxB,EAAE,aAAa,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC,IAC5C,EAAE,mBAAmB,KAAK,MAAM,CAEhC,QAAO,yBAAyB,KAAK,OAAO,KAAK;AAGrD,QAAO;;;;;AAMT,SAAS,sCACP,MACe;AACf,MAAK,MAAM,QAAQ,KAAK,WACtB,KACE,EAAE,iBAAiB,KAAK,IACxB,EAAE,aAAa,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC,IAC/C,EAAE,gBAAgB,KAAK,MAAM,CAE7B,QAAO,KAAK,MAAM;AAGtB,QAAO;;;;;AAMT,SAAS,iBACP,MACA,MACA,WACA,OACA,gBACA,iBACM;CACN,MAAM,YAAY,KAAK;AAEvB,KAAI,CAAC,EAAE,mBAAmB,UAAU,CAClC,OAAM,KAAK,oBACT,+DACD;CAOH,MAAM,SAAS,eAHG,yBAAyB,WAAW,KAAK,CAGnB;CAGxC,MAAM,EAAE,WAAW,YAAY,2BAC7B,QACA,gBACD;AAGD,MAAK,MAAM,MAAM,UACf,WAAU,IAAI,GAAG,KAAK,GAAG,KAAK,MAAM,WAAW;CAIjD,MAAM,SAAS,wBAAwB,OAAO;AAG9C,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MACJ,QAAQ,OAAO,IACX,2BAA2B,MAAM,KAAK,QAAQ,GAC9C,MAAM;AACZ,YAAU,IAAI,MAAM,WAAW,KAAK,MAAM,WAAW;;CAIvD,MAAM,YACJ,OAAO,SAAS,IAAI,OAAO,KAAK,MAAM,EAAE,UAAU,CAAC,KAAK,IAAI,GAAG;CAGjE,MAAM,oBAAoB,qBAAqB,WAAW,OAAO;AACjE,MAAK,YAAY,kBAAkB;AAGnC,+BAA8B,MAAM,WAAW,QAAQ,OAAO,eAAe;;;;;AAM/E,SAAS,oBACP,MACA,MACA,WACA,OACA,gBACA,iBACM;AACN,KAAI,KAAK,SAAS,EAChB,OAAM,KAAK,oBACT,mDACD;CAGH,MAAM,UAAU,KAAK;CACrB,MAAM,YAAY,KAAK;AAEvB,KAAI,CAAC,EAAE,aAAa,QAAQ,CAC1B,OAAM,KAAK,oBACT,iEACD;AAGH,KAAI,CAAC,EAAE,mBAAmB,UAAU,CAClC,OAAM,KAAK,oBACT,4EACD;CAGH,MAAM,WAAW,QAAQ;CAGzB,MAAM,YACJ,MAAM,oBAAoB,aAAa,eAAe;AAExD,KAAI,CAAC,UACH,OAAM,KAAK,oBACT,iCAAiC,SAAS,mDAE3C;CAIH,MAAM,iBAAiB,yBAAyB,WAAW,KAAK;CAGhE,MAAM,eAAe,eACnB,YAAY,UAAU,QAAQ,eAAe,CAC9C;CAGD,MAAM,EAAE,WAAW,YAAY,2BAC7B,cACA,gBACD;AAGD,MAAK,MAAM,MAAM,UACf,WAAU,IAAI,GAAG,KAAK,GAAG,KAAK,MAAM,WAAW;CAIjD,MAAM,SAAS,wBAAwB,aAAa;AAGpD,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MACJ,QAAQ,OAAO,IACX,2BAA2B,MAAM,KAAK,QAAQ,GAC9C,MAAM;AACZ,YAAU,IAAI,MAAM,WAAW,KAAK,MAAM,WAAW;;CAIvD,MAAM,YACJ,OAAO,SAAS,IAAI,OAAO,KAAK,MAAM,EAAE,UAAU,CAAC,KAAK,IAAI,GAAG;CAGjE,MAAM,oBAAoB,qBAAqB,WAAW,aAAa;AACvE,MAAK,YAAY,kBAAkB;AAGnC,+BACE,MACA,WACA,cACA,OACA,eACD;;;;;AAMH,SAAS,mBACP,MACA,MACA,WACA,YACA,iBACM;AACN,KAAI,KAAK,SAAS,EAChB,OAAM,KAAK,oBACT,uDACD;CAGH,MAAM,cAAc,KAAK;CACzB,MAAM,YAAY,KAAK;AAEvB,KAAI,CAAC,EAAE,gBAAgB,YAAY,CACjC,OAAM,KAAK,oBACT,wEACD;AAGH,KAAI,CAAC,EAAE,mBAAmB,UAAU,CAClC,OAAM,KAAK,oBACT,gFACD;CAGH,MAAM,WAAW,YAAY;CAI7B,MAAM,SAAS,eAHG,yBAAyB,WAAW,KAAK,CAGnB;CAGxC,MAAM,EAAE,WAAW,YAAY,2BAC7B,QACA,gBACD;AAGD,MAAK,MAAM,MAAM,UACf,WAAU,IAAI,GAAG,KAAK,GAAG,KAAK,WAAW;CAI3C,MAAM,SAAS,yBAAyB,UAAU,OAAO;CAGzD,MAAM,MACJ,QAAQ,OAAO,IACX,2BAA2B,OAAO,KAAK,QAAQ,GAC/C,OAAO;AACb,WAAU,IAAI,UAAU,KAAK,WAAW;CAGxC,MAAM,SAAS,KAAK;AACpB,KAAI,UAAU,EAAE,sBAAsB,OAAO,KAAK,CAChD,QAAO,QAAQ;KAIf,MAAK,YAAY,EAAE,WAAW,YAAY,CAAC;;;;;AAO/C,SAAS,qBACP,WACA,QACoB;AACpB,QAAO,EAAE,iBAAiB;EACxB,EAAE,eAAe,EAAE,WAAW,YAAY,EAAE,EAAE,cAAc,UAAU,CAAC;EACvE,EAAE,eAAe,EAAE,WAAW,SAAS,EAAE,WAAW,OAAO,CAAC;EAC5D,EAAE,aACA,UACA,EAAE,WAAW,WAAW,EACxB,EAAE,EACF,EAAE,eAAe,CACf,EAAE,gBACA,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,EAAE,WAAW,YAAY,CAAC,CAClE,CACF,CAAC,CACH;EACF,CAAC;;;;;AAMJ,SAAS,8BACP,MACA,WACA,QACA,OACA,gBACM;CACN,MAAM,SAAS,KAAK;AACpB,KAAI,UAAU,EAAE,qBAAqB,OAAO,KAAK,EAAE;EACjD,MAAM,KAAK,OAAO,KAAK;AACvB,MAAI,EAAE,aAAa,GAAG,EAAE;GACtB,MAAM,eAAe,GAAG;AACxB,SAAM,oBAAoB,gBAAgB;IAAE;IAAQ;IAAW;AAC/D,kBAAe,gBAAgB;IAAE;IAAQ;IAAW;;;;;;;AAQ1D,SAAS,WAAW,OAA8B;AAChD,KAAI,UAAU,KACZ,QAAO,EAAE,aAAa;AAExB,KAAI,UAAU,OACZ,QAAO,EAAE,WAAW,YAAY;AAElC,KAAI,OAAO,UAAU,SACnB,QAAO,EAAE,cAAc,MAAM;AAE/B,KAAI,OAAO,UAAU,SACnB,QAAO,EAAE,eAAe,MAAM;AAEhC,KAAI,OAAO,UAAU,UACnB,QAAO,EAAE,eAAe,MAAM;AAEhC,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,EAAE,gBAAgB,MAAM,IAAI,WAAW,CAAC;AAEjD,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,aAAa,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,SAClD,EAAE,eACA,6BAA6B,KAAK,IAAI,GAClC,EAAE,WAAW,IAAI,GACjB,EAAE,cAAc,IAAI,EACxB,WAAW,IAAI,CAChB,CACF;AACD,SAAO,EAAE,iBAAiB,WAAW;;AAGvC,QAAO,EAAE,WAAW,YAAY;;;;;;AAOlC,SAAS,yBACP,MACA,MACyB;CACzB,MAAM,SAAkC,EAAE;AAE1C,MAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,MAAI,EAAE,gBAAgB,KAAK,CACzB,OAAM,KAAK,oBACT,mFACD;AAGH,MAAI,CAAC,EAAE,iBAAiB,KAAK,CAC3B,OAAM,KAAK,oBACT,wDACD;EAIH,IAAI;AACJ,MAAI,EAAE,aAAa,KAAK,IAAI,CAC1B,OAAM,KAAK,IAAI;WACN,EAAE,gBAAgB,KAAK,IAAI,CACpC,OAAM,KAAK,IAAI;MAEf,OAAM,KAAK,oBACT,2DACD;AAKH,SAAO,OADO,mBAAmB,KAAK,OAAO,KAAK;;AAIpD,QAAO;;;;;AAMT,SAAS,mBAAmB,MAAc,MAAyB;AACjE,KAAI,EAAE,gBAAgB,KAAK,CACzB,QAAO,KAAK;AAGd,KAAI,EAAE,iBAAiB,KAAK,CAC1B,QAAO,KAAK;AAGd,KAAI,EAAE,iBAAiB,KAAK,CAC1B,QAAO,KAAK;AAGd,KAAI,EAAE,cAAc,KAAK,CACvB,QAAO;AAGT,KAAI,EAAE,aAAa,MAAM,EAAE,MAAM,aAAa,CAAC,CAC7C;AAGF,KAAI,EAAE,kBAAkB,KAAK,CAC3B,QAAO,KAAK,SAAS,KAAK,OAAO;AAC/B,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,EAAE,gBAAgB,GAAG,CACvB,OAAM,KAAK,oBACT,qDACD;AAEH,SAAO,mBAAmB,IAAI,KAAK;GACnC;AAGJ,KAAI,EAAE,mBAAmB,KAAK,CAC5B,QAAO,yBAAyB,MAAM,KAAK;AAG7C,KAAI,EAAE,kBAAkB,KAAK,EAAE;AAE7B,MAAI,KAAK,YAAY,SAAS,EAC5B,OAAM,KAAK,oBACT,wEACD;AAEH,SAAO,KAAK,OAAO,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC,KAAK,GAAG;;AAGxD,KAAI,EAAE,kBAAkB,MAAM,EAAE,UAAU,KAAK,CAAC,EAAE;EAChD,MAAM,MAAM,mBAAmB,KAAK,UAAU,KAAK;AACnD,MAAI,OAAO,QAAQ,SACjB,QAAO,CAAC;;AAIZ,OAAM,KAAK,oBACT,gEAAgE,KAAK,KAAK,uCAE3E;;;;;;AAOH,SAAS,2BACP,KACA,SACQ;AACR,KAAI,QAAQ,SAAS,EAAG,QAAO;AAI/B,QAAO,IAAI,QACT,2CACC,OAAO,MAAM,UAAU;EACtB,IAAI,WAAW;AACf,OAAK,MAAM,CAAC,UAAU,gBAAgB,SAAS;GAE7C,MAAM,UAAU,IAAI,OAAO,MAAM,YAAY,SAAS,CAAC,MAAM,IAAI;AACjE,cAAW,SAAS,QAAQ,SAAS,YAAY;;AAEnD,SAAO,GAAG,KAAK,IAAI;GAEtB;;;;;AAMH,SAAS,YAAY,KAAqB;AACxC,QAAO,IAAI,QAAQ,uBAAuB,OAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-writer.js","names":[],"sources":["../../src/zero/css-writer.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface CSSWriterOptions {\n /** Enable source comments in output (e.g., \"from: Button.tsx\") */\n devMode?: boolean;\n}\n\ninterface CSSBlock {\n css: string;\n source?: string;\n}\n\nexport class CSSWriter {\n private cssBlocks = new Map<string, CSSBlock>();\n private outputPath: string;\n private devMode: boolean;\n\n constructor(outputPath: string, options: CSSWriterOptions = {}) {\n this.outputPath = outputPath;\n this.devMode = options.devMode ?? false;\n }\n\n /**\n * Add CSS block with deduplication key\n * @param key - Unique key for deduplication\n * @param css - CSS content\n * @param source - Optional source file path (used in devMode)\n */\n add(key: string, css: string, source?: string): void {\n this.cssBlocks.set(key, { css, source });\n }\n\n /**\n * Check if a key already exists\n */\n has(key: string): boolean {\n return this.cssBlocks.has(key);\n }\n\n /**\n * Get the number of CSS blocks\n */\n get size(): number {\n return this.cssBlocks.size;\n }\n\n /**\n * Write all collected CSS to the output file\n */\n write(): void {\n const outputDir = path.dirname(this.outputPath);\n\n // Ensure directory exists\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n // Combine all CSS blocks with optional source comments\n const cssBlocks: string[] = [];\n for (const block of this.cssBlocks.values()) {\n if (this.devMode && block.source) {\n cssBlocks.push(`/* from: ${block.source} */\\n${block.css}`);\n } else {\n cssBlocks.push(block.css);\n }\n }\n const css = cssBlocks.join('\\n\\n');\n\n // Add header comment\n const header = `/* Generated by @tenphi/tasty/zero - DO NOT EDIT */\\n\\n`;\n\n fs.writeFileSync(this.outputPath, header + css);\n }\n\n /**\n * Get all CSS as string (for testing or in-memory use)\n */\n getCSS(): string {\n const cssBlocks: string[] = [];\n for (const block of this.cssBlocks.values()) {\n if (this.devMode && block.source) {\n cssBlocks.push(`/* from: ${block.source} */\\n${block.css}`);\n } else {\n cssBlocks.push(block.css);\n }\n }\n return cssBlocks.join('\\n\\n');\n }\n\n /**\n * Clear all collected CSS\n */\n clear(): void {\n this.cssBlocks.clear();\n }\n\n /**\n * Get the output path\n */\n getOutputPath(): string {\n return this.outputPath;\n }\n}\n"],"mappings":";;;;AAaA,IAAa,YAAb,MAAuB;CACrB,AAAQ,4BAAY,IAAI,KAAuB;CAC/C,AAAQ;CACR,AAAQ;CAER,YAAY,YAAoB,UAA4B,EAAE,EAAE;AAC9D,OAAK,aAAa;AAClB,OAAK,UAAU,QAAQ,WAAW;;;;;;;;CASpC,IAAI,KAAa,KAAa,QAAuB;AACnD,OAAK,UAAU,IAAI,KAAK;GAAE;GAAK;GAAQ,CAAC;;;;;CAM1C,IAAI,KAAsB;AACxB,SAAO,KAAK,UAAU,IAAI,IAAI;;;;;CAMhC,IAAI,OAAe;AACjB,SAAO,KAAK,UAAU;;;;;CAMxB,QAAc;EACZ,MAAM,YAAY,KAAK,QAAQ,KAAK,WAAW;AAG/C,MAAI,CAAC,GAAG,WAAW,UAAU,CAC3B,IAAG,UAAU,WAAW,EAAE,WAAW,MAAM,CAAC;EAI9C,MAAM,YAAsB,EAAE;AAC9B,OAAK,MAAM,SAAS,KAAK,UAAU,QAAQ,CACzC,KAAI,KAAK,WAAW,MAAM,OACxB,WAAU,KAAK,YAAY,MAAM,OAAO,OAAO,MAAM,MAAM;MAE3D,WAAU,KAAK,MAAM,IAAI;EAG7B,MAAM,MAAM,UAAU,KAAK,OAAO;AAKlC,KAAG,cAAc,KAAK,YAFP,4DAE4B,IAAI;;;;;CAMjD,SAAiB;EACf,MAAM,YAAsB,EAAE;AAC9B,OAAK,MAAM,SAAS,KAAK,UAAU,QAAQ,CACzC,KAAI,KAAK,WAAW,MAAM,OACxB,WAAU,KAAK,YAAY,MAAM,OAAO,OAAO,MAAM,MAAM;MAE3D,WAAU,KAAK,MAAM,IAAI;AAG7B,SAAO,UAAU,KAAK,OAAO;;;;;CAM/B,QAAc;AACZ,OAAK,UAAU,OAAO;;;;;CAMxB,gBAAwB;AACtB,SAAO,KAAK"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../injector/types.js";
|
|
2
|
+
import { Styles } from "../styles/types.js";
|
|
2
3
|
|
|
3
4
|
//#region src/zero/extractor.d.ts
|
|
4
5
|
interface ExtractedChunk {
|
|
@@ -21,4 +22,4 @@ declare function extractStylesWithChunks(styles: Styles): ExtractedChunk[];
|
|
|
21
22
|
declare function extractStylesForSelector(selector: string, styles: Styles): ExtractedSelector;
|
|
22
23
|
//#endregion
|
|
23
24
|
export { ExtractedChunk, ExtractedSelector, extractStylesForSelector, extractStylesWithChunks };
|
|
24
|
-
//# sourceMappingURL=extractor.d.
|
|
25
|
+
//# sourceMappingURL=extractor.d.ts.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { renderStyles } from "
|
|
2
|
-
import { categorizeStyleKeys } from "
|
|
3
|
-
import { generateChunkCacheKey } from "
|
|
4
|
-
import { renderStylesForChunk } from "
|
|
5
|
-
import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes } from "
|
|
1
|
+
import { renderStyles } from "../pipeline/index.js";
|
|
2
|
+
import { categorizeStyleKeys } from "../chunks/definitions.js";
|
|
3
|
+
import { generateChunkCacheKey } from "../chunks/cacheKey.js";
|
|
4
|
+
import { renderStylesForChunk } from "../chunks/renderChunk.js";
|
|
5
|
+
import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes } from "../keyframes/index.js";
|
|
6
6
|
import { createHash } from "crypto";
|
|
7
7
|
|
|
8
8
|
//#region src/zero/extractor.ts
|
|
@@ -147,4 +147,4 @@ function camelToKebab(str) {
|
|
|
147
147
|
|
|
148
148
|
//#endregion
|
|
149
149
|
export { extractKeyframesFromStyles, extractStylesForSelector, extractStylesWithChunks };
|
|
150
|
-
//# sourceMappingURL=extractor.
|
|
150
|
+
//# sourceMappingURL=extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.js","names":[],"sources":["../../src/zero/extractor.ts"],"sourcesContent":["import { createHash } from 'crypto';\n\nimport {\n categorizeStyleKeys,\n generateChunkCacheKey,\n renderStylesForChunk,\n} from '../chunks';\nimport type { KeyframesSteps } from '../injector/types';\nimport {\n extractAnimationNamesFromStyles,\n extractLocalKeyframes,\n filterUsedKeyframes,\n hasLocalKeyframes,\n mergeKeyframes,\n} from '../keyframes';\nimport type { StyleResult } from '../pipeline';\nimport { renderStyles } from '../pipeline';\nimport type { Styles } from '../styles/types';\n\nexport interface ExtractedChunk {\n className: string;\n css: string;\n}\n\nexport interface ExtractedSelector {\n selector: string;\n css: string;\n}\n\nexport interface ExtractedKeyframes {\n name: string;\n css: string;\n}\n\nexport interface KeyframesExtractionResult {\n /** Keyframes to inject (deduplicated by content) */\n keyframes: ExtractedKeyframes[];\n /** Map from original animation name to canonical name (for replacement) */\n nameMap: Map<string, string>;\n}\n\n/**\n * Generate a deterministic className from a cache key using content hash.\n * This ensures the same styles always produce the same className,\n * regardless of build order or incremental compilation.\n */\nfunction generateClassName(cacheKey: string): string {\n const hash = createHash('md5').update(cacheKey).digest('hex').slice(0, 6);\n return `ts${hash}`; // 'ts' prefix for \"tasty-static\" to distinguish from runtime 't' classes\n}\n\n/**\n * Extract styles using chunking (for className mode).\n * Returns multiple classes, one per chunk.\n */\nexport function extractStylesWithChunks(styles: Styles): ExtractedChunk[] {\n const chunks: ExtractedChunk[] = [];\n\n // Categorize style keys into chunks\n const chunkMap = categorizeStyleKeys(styles as Record<string, unknown>);\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n if (chunkStyleKeys.length === 0) continue;\n\n // Generate cache key for this chunk (used for className hash)\n const cacheKey = generateChunkCacheKey(styles, chunkName, chunkStyleKeys);\n\n // Render styles for this chunk\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n chunkStyleKeys,\n );\n\n if (renderResult.rules.length === 0) continue;\n\n // Generate deterministic className from content hash\n const className = generateClassName(cacheKey);\n const selector = `.${className}.${className}`;\n\n // Format CSS\n const css = formatRulesToCSS(renderResult.rules, selector);\n\n chunks.push({ className, css });\n }\n\n return chunks;\n}\n\n/**\n * Extract styles for a specific selector (for global/selector mode).\n * Returns a single CSS block.\n */\nexport function extractStylesForSelector(\n selector: string,\n styles: Styles,\n): ExtractedSelector {\n // renderStyles with selector returns StyleResult[] with selectors already applied\n const rules = renderStyles(styles, selector);\n // Format without re-prefixing - rules already have the full selector\n const css = formatRulesDirectly(rules);\n\n return { selector, css };\n}\n\n/**\n * Format StyleResult[] to CSS string.\n * Prefixes each rule's selector with the base selector.\n * Used for chunked styles where rules have relative selectors.\n */\nfunction formatRulesToCSS(rules: StyleResult[], baseSelector: string): string {\n return rules\n .map((rule) => {\n // Handle selector as array (OR conditions) or string\n // Note: renderStyles without className joins array selectors with '|||' placeholder\n const selectorParts = Array.isArray(rule.selector)\n ? rule.selector\n : rule.selector\n ? rule.selector.split('|||')\n : [''];\n\n // Prefix each selector part with the base selector\n const fullSelector = selectorParts\n .map((part) => {\n // Build selector: [rootPrefix] baseSelector[part]\n let selector: string;\n\n // If part is empty, just use base selector\n if (!part) {\n selector = baseSelector;\n } else if (part.startsWith(':') || part.startsWith('[')) {\n // If part starts with a pseudo-class or pseudo-element, append to base\n selector = `${baseSelector}${part}`;\n } else if (\n part.startsWith('>') ||\n part.startsWith('+') ||\n part.startsWith('~')\n ) {\n // If part starts with >, +, ~ combinator, append with space\n selector = `${baseSelector}${part}`;\n } else {\n // Otherwise, combine base with part\n selector = `${baseSelector}${part}`;\n }\n\n // Prepend rootPrefix if present (for @root() states)\n if (rule.rootPrefix) {\n selector = `${rule.rootPrefix} ${selector}`;\n }\n\n return selector;\n })\n .join(', ');\n\n let css = `${fullSelector} { ${rule.declarations} }`;\n\n // Wrap in at-rules (in reverse order for proper nesting)\n if (rule.atRules && rule.atRules.length > 0) {\n for (const atRule of [...rule.atRules].reverse()) {\n css = `${atRule} {\\n ${css}\\n}`;\n }\n }\n\n return css;\n })\n .join('\\n\\n');\n}\n\n/**\n * Format StyleResult[] to CSS string directly without prefixing.\n * Used for global styles where rules already have the full selector.\n */\nfunction formatRulesDirectly(rules: StyleResult[]): string {\n return rules\n .map((rule) => {\n // Prepend rootPrefix if present (for @root() states)\n const selector = rule.rootPrefix\n ? `${rule.rootPrefix} ${rule.selector}`\n : rule.selector;\n\n let css = `${selector} { ${rule.declarations} }`;\n\n // Wrap in at-rules (in reverse order for proper nesting)\n if (rule.atRules && rule.atRules.length > 0) {\n for (const atRule of [...rule.atRules].reverse()) {\n css = `${atRule} {\\n ${css}\\n}`;\n }\n }\n\n return css;\n })\n .join('\\n\\n');\n}\n\n// Note: With hash-based className generation, counter management functions\n// are no longer needed. ClassNames are deterministic based on content.\n\n/**\n * Generate a deterministic keyframes name from content hash.\n * This ensures the same keyframes content always produces the same name,\n * enabling automatic deduplication across elements and files.\n */\nfunction generateKeyframesName(steps: KeyframesSteps): string {\n const content = JSON.stringify(steps);\n const hash = createHash('md5').update(content).digest('hex').slice(0, 6);\n return `kf${hash}`; // 'kf' prefix for \"keyframes\"\n}\n\n/**\n * Extract keyframes that are used in styles.\n * Merges local @keyframes with global keyframes, filters to only used ones.\n * Generates hash-based names from content for automatic deduplication.\n *\n * @param styles - The styles object (may contain @keyframes and animation properties)\n * @param globalKeyframes - Optional global keyframes from config\n * @returns Keyframes to inject and name mapping for replacement\n */\nexport function extractKeyframesFromStyles(\n styles: Styles,\n globalKeyframes?: Record<string, KeyframesSteps> | null,\n): KeyframesExtractionResult {\n const emptyResult: KeyframesExtractionResult = {\n keyframes: [],\n nameMap: new Map(),\n };\n\n // Extract animation names from styles\n const usedNames = extractAnimationNamesFromStyles(styles);\n if (usedNames.size === 0) return emptyResult;\n\n // Merge local and global keyframes\n const local = hasLocalKeyframes(styles)\n ? extractLocalKeyframes(styles)\n : null;\n const allKeyframes = mergeKeyframes(local, globalKeyframes ?? null);\n\n // Filter to only used keyframes\n const usedKeyframes = filterUsedKeyframes(allKeyframes, usedNames);\n if (!usedKeyframes) return emptyResult;\n\n // Generate hash-based names and collect unique keyframes\n const seenHashes = new Set<string>();\n const nameMap = new Map<string, string>();\n const keyframesToEmit: ExtractedKeyframes[] = [];\n\n for (const [originalName, steps] of Object.entries(usedKeyframes)) {\n const hashedName = generateKeyframesName(steps);\n\n // Always map original name to hashed name (for CSS replacement)\n nameMap.set(originalName, hashedName);\n\n // Only emit each unique keyframe once\n if (!seenHashes.has(hashedName)) {\n seenHashes.add(hashedName);\n const css = keyframesToCSS(hashedName, steps);\n keyframesToEmit.push({ name: hashedName, css });\n }\n }\n\n return { keyframes: keyframesToEmit, nameMap };\n}\n\n/**\n * Convert keyframes steps to CSS string.\n */\nfunction keyframesToCSS(name: string, steps: KeyframesSteps): string {\n const stepRules: string[] = [];\n\n for (const [key, value] of Object.entries(steps)) {\n if (typeof value === 'string') {\n // Raw CSS string\n stepRules.push(`${key} { ${value.trim()} }`);\n } else if (value && typeof value === 'object') {\n // Style map - convert to CSS declarations\n const declarations = Object.entries(value)\n .map(([prop, val]) => {\n const cssProperty = camelToKebab(prop);\n return `${cssProperty}: ${val}`;\n })\n .join('; ');\n stepRules.push(`${key} { ${declarations} }`);\n }\n }\n\n return `@keyframes ${name} { ${stepRules.join(' ')} }`;\n}\n\n/**\n * Convert camelCase to kebab-case.\n */\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ExtractedChunk, ExtractedSelector, extractStylesForSelector, extractStylesWithChunks } from "./extractor.js";
|
|
2
|
+
import { CSSWriter } from "./css-writer.js";
|
|
3
|
+
export { CSSWriter, type ExtractedChunk, type ExtractedSelector, extractStylesForSelector, extractStylesWithChunks };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractStylesForSelector, extractStylesWithChunks } from "
|
|
2
|
-
import { CSSWriter } from "
|
|
1
|
+
import { extractStylesForSelector, extractStylesWithChunks } from "./extractor.js";
|
|
2
|
+
import { CSSWriter } from "./css-writer.js";
|
|
3
3
|
|
|
4
4
|
export { CSSWriter, extractStylesForSelector, extractStylesWithChunks };
|
|
@@ -57,4 +57,4 @@ interface TastyZeroNextOptions {
|
|
|
57
57
|
declare function withTastyZero(options?: TastyZeroNextOptions): (nextConfig?: NextConfig) => NextConfig;
|
|
58
58
|
//#endregion
|
|
59
59
|
export { TastyZeroNextOptions, withTastyZero };
|
|
60
|
-
//# sourceMappingURL=next.d.
|
|
60
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __require } from "../_virtual/_rolldown/runtime.
|
|
1
|
+
import { __require } from "../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
|
|
4
4
|
//#region src/zero/next.ts
|
|
@@ -75,4 +75,4 @@ function withTastyZero(options = {}) {
|
|
|
75
75
|
|
|
76
76
|
//#endregion
|
|
77
77
|
export { withTastyZero };
|
|
78
|
-
//# sourceMappingURL=next.
|
|
78
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.js","names":[],"sources":["../../src/zero/next.ts"],"sourcesContent":["/**\n * Next.js configuration wrapper for tasty-zero.\n *\n * Provides a convenient way to configure the Babel plugin for Next.js projects.\n *\n * @example\n * ```javascript\n * // next.config.js\n * const { withTastyZero } = require('@tenphi/tasty/next');\n *\n * module.exports = withTastyZero({\n * output: 'public/tasty.css',\n * })({\n * // your Next.js config\n * });\n * ```\n */\n\nimport * as path from 'path';\n\n// Next.js types (inline to avoid requiring next as a dependency)\ninterface WebpackConfigContext {\n isServer: boolean;\n dev: boolean;\n buildId: string;\n dir: string;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- webpack config types are complex */\ninterface NextConfig {\n webpack?: (config: any, context: WebpackConfigContext) => any;\n [key: string]: unknown;\n}\n\nexport interface TastyZeroNextOptions {\n /**\n * Output path for CSS relative to project root.\n * @default 'public/tasty.css'\n */\n output?: string;\n\n /**\n * Whether to enable the plugin.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Next.js configuration wrapper for tasty-zero.\n *\n * @param options - Configuration options\n * @returns A function that wraps the Next.js config\n *\n * @example\n * ```javascript\n * // next.config.js\n * const { withTastyZero } = require('@tenphi/tasty/next');\n *\n * module.exports = withTastyZero()({\n * reactStrictMode: true,\n * });\n * ```\n */\nexport function withTastyZero(options: TastyZeroNextOptions = {}) {\n const { output = 'public/tasty.css', enabled = true } = options;\n\n return (nextConfig: NextConfig = {}): NextConfig => {\n if (!enabled) {\n return nextConfig;\n }\n\n return {\n ...nextConfig,\n\n webpack(config: any, context: WebpackConfigContext) {\n const { isServer, dir } = context;\n\n // Only process on client build to avoid duplicate CSS generation\n if (!isServer) {\n // Get absolute output path\n const absoluteOutput = path.resolve(dir || process.cwd(), output);\n\n // Find existing babel-loader rule or add new one\n const babelPluginConfig = [\n require.resolve('./babel'),\n { output: absoluteOutput },\n ];\n\n // Add our plugin to the existing babel config or create new rule\n const existingRule = config.module?.rules?.find(\n (rule: any) =>\n rule.use?.loader === 'babel-loader' ||\n rule.use?.some?.((u: any) => u.loader === 'babel-loader'),\n );\n\n if (existingRule) {\n // Add to existing babel-loader\n const babelUse = Array.isArray(existingRule.use)\n ? existingRule.use.find((u: any) => u.loader === 'babel-loader')\n : existingRule.use;\n\n if (babelUse?.options) {\n babelUse.options.plugins = babelUse.options.plugins || [];\n babelUse.options.plugins.push(babelPluginConfig);\n }\n } else {\n // Add new rule for our plugin\n config.module = config.module || {};\n config.module.rules = config.module.rules || [];\n config.module.rules.push({\n test: /\\.(tsx?|jsx?)$/,\n exclude: /node_modules/,\n use: [\n {\n loader: require.resolve('babel-loader'),\n options: {\n plugins: [babelPluginConfig],\n },\n },\n ],\n });\n }\n }\n\n // Chain with existing webpack config\n if (typeof nextConfig.webpack === 'function') {\n return nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,cAAc,UAAgC,EAAE,EAAE;CAChE,MAAM,EAAE,SAAS,oBAAoB,UAAU,SAAS;AAExD,SAAQ,aAAyB,EAAE,KAAiB;AAClD,MAAI,CAAC,QACH,QAAO;AAGT,SAAO;GACL,GAAG;GAEH,QAAQ,QAAa,SAA+B;IAClD,MAAM,EAAE,UAAU,QAAQ;AAG1B,QAAI,CAAC,UAAU;KAEb,MAAM,iBAAiB,KAAK,QAAQ,OAAO,QAAQ,KAAK,EAAE,OAAO;KAGjE,MAAM,oBAAoB,WAChB,QAAQ,UAAU,EAC1B,EAAE,QAAQ,gBAAgB,CAC3B;KAGD,MAAM,eAAe,OAAO,QAAQ,OAAO,MACxC,SACC,KAAK,KAAK,WAAW,kBACrB,KAAK,KAAK,QAAQ,MAAW,EAAE,WAAW,eAAe,CAC5D;AAED,SAAI,cAAc;MAEhB,MAAM,WAAW,MAAM,QAAQ,aAAa,IAAI,GAC5C,aAAa,IAAI,MAAM,MAAW,EAAE,WAAW,eAAe,GAC9D,aAAa;AAEjB,UAAI,UAAU,SAAS;AACrB,gBAAS,QAAQ,UAAU,SAAS,QAAQ,WAAW,EAAE;AACzD,gBAAS,QAAQ,QAAQ,KAAK,kBAAkB;;YAE7C;AAEL,aAAO,SAAS,OAAO,UAAU,EAAE;AACnC,aAAO,OAAO,QAAQ,OAAO,OAAO,SAAS,EAAE;AAC/C,aAAO,OAAO,MAAM,KAAK;OACvB,MAAM;OACN,SAAS;OACT,KAAK,CACH;QACE,kBAAgB,QAAQ,eAAe;QACvC,SAAS,EACP,SAAS,CAAC,kBAAkB,EAC7B;QACF,CACF;OACF,CAAC;;;AAKN,QAAI,OAAO,WAAW,YAAY,WAChC,QAAO,WAAW,QAAQ,QAAQ,QAAQ;AAG5C,WAAO;;GAEV"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/tasty",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A design-system-integrated styling system and DSL for concise, state-aware UI styling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -42,21 +42,6 @@
|
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=20"
|
|
44
44
|
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"build": "tsdown",
|
|
47
|
-
"test": "vitest run",
|
|
48
|
-
"test:watch": "vitest",
|
|
49
|
-
"test:coverage": "vitest run --coverage",
|
|
50
|
-
"typecheck": "tsc --noEmit",
|
|
51
|
-
"lint": "eslint src",
|
|
52
|
-
"lint:fix": "eslint src --fix",
|
|
53
|
-
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
54
|
-
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
|
|
55
|
-
"prepublishOnly": "pnpm run build",
|
|
56
|
-
"changeset": "changeset",
|
|
57
|
-
"version": "changeset version",
|
|
58
|
-
"release": "pnpm run build && changeset publish"
|
|
59
|
-
},
|
|
60
45
|
"repository": {
|
|
61
46
|
"type": "git",
|
|
62
47
|
"url": "git+https://github.com/tenphi/tasty.git"
|
|
@@ -118,10 +103,18 @@
|
|
|
118
103
|
"typescript-eslint": "^8.56.0",
|
|
119
104
|
"vitest": "^4.0.18"
|
|
120
105
|
},
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
106
|
+
"scripts": {
|
|
107
|
+
"build": "tsdown",
|
|
108
|
+
"test": "vitest run",
|
|
109
|
+
"test:watch": "vitest",
|
|
110
|
+
"test:coverage": "vitest run --coverage",
|
|
111
|
+
"typecheck": "tsc --noEmit",
|
|
112
|
+
"lint": "eslint src",
|
|
113
|
+
"lint:fix": "eslint src --fix",
|
|
114
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
115
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
|
|
116
|
+
"changeset": "changeset",
|
|
117
|
+
"version": "changeset version",
|
|
118
|
+
"release": "changeset publish"
|
|
119
|
+
}
|
|
120
|
+
}
|
package/dist/chunks/cacheKey.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { extractLocalPredefinedStates, extractPredefinedStateRefs } from "../states/index.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/chunks/cacheKey.ts
|
|
4
|
-
/**
|
|
5
|
-
* Chunk-specific cache key generation.
|
|
6
|
-
*
|
|
7
|
-
* Generates cache keys that only include styles relevant to a specific chunk,
|
|
8
|
-
* enabling more granular caching and reuse.
|
|
9
|
-
*
|
|
10
|
-
* Enhanced to support predefined states:
|
|
11
|
-
* - Global predefined states don't affect cache keys (constant across app)
|
|
12
|
-
* - Local predefined states only affect cache keys if referenced in the chunk
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Recursively serialize a value with sorted keys for stable output.
|
|
16
|
-
* This ensures that {a: 1, b: 2} and {b: 2, a: 1} produce the same string.
|
|
17
|
-
*/
|
|
18
|
-
function stableStringify(value) {
|
|
19
|
-
if (value === null) return "null";
|
|
20
|
-
if (value === void 0) return "undefined";
|
|
21
|
-
if (typeof value !== "object") return JSON.stringify(value);
|
|
22
|
-
if (Array.isArray(value)) return "[" + value.map(stableStringify).join(",") + "]";
|
|
23
|
-
const obj = value;
|
|
24
|
-
const sortedKeys = Object.keys(obj).sort();
|
|
25
|
-
const parts = [];
|
|
26
|
-
for (const key of sortedKeys) if (obj[key] !== void 0) parts.push(`${JSON.stringify(key)}:${stableStringify(obj[key])}`);
|
|
27
|
-
return "{" + parts.join(",") + "}";
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Generate a cache key for a specific chunk.
|
|
31
|
-
*
|
|
32
|
-
* Only includes the styles that belong to this chunk, allowing
|
|
33
|
-
* chunks to be cached independently.
|
|
34
|
-
*
|
|
35
|
-
* Also includes relevant local predefined states that are referenced
|
|
36
|
-
* by this chunk's styles.
|
|
37
|
-
*
|
|
38
|
-
* @param styles - The full styles object
|
|
39
|
-
* @param chunkName - Name of the chunk
|
|
40
|
-
* @param styleKeys - Keys of styles belonging to this chunk
|
|
41
|
-
* @returns A stable cache key string
|
|
42
|
-
*/
|
|
43
|
-
function generateChunkCacheKey(styles, chunkName, styleKeys) {
|
|
44
|
-
const parts = [chunkName];
|
|
45
|
-
const sortedKeys = styleKeys.slice().sort();
|
|
46
|
-
let chunkStylesStr = "";
|
|
47
|
-
for (const key of sortedKeys) {
|
|
48
|
-
const value = styles[key];
|
|
49
|
-
if (value !== void 0) {
|
|
50
|
-
const serialized = stableStringify(value);
|
|
51
|
-
parts.push(`${key}:${serialized}`);
|
|
52
|
-
chunkStylesStr += serialized;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const localStates = extractLocalPredefinedStates(styles);
|
|
56
|
-
if (Object.keys(localStates).length > 0) {
|
|
57
|
-
const referencedStates = extractPredefinedStateRefs(chunkStylesStr);
|
|
58
|
-
const relevantLocalStates = [];
|
|
59
|
-
for (const stateName of referencedStates) if (localStates[stateName]) relevantLocalStates.push(`${stateName}=${localStates[stateName]}`);
|
|
60
|
-
if (relevantLocalStates.length > 0) {
|
|
61
|
-
relevantLocalStates.sort();
|
|
62
|
-
parts.unshift(`[states:${relevantLocalStates.join("|")}]`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return parts.join("\0");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
//#endregion
|
|
69
|
-
export { generateChunkCacheKey };
|
|
70
|
-
//# sourceMappingURL=cacheKey.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cacheKey.mjs","names":[],"sources":["../../src/chunks/cacheKey.ts"],"sourcesContent":["/**\n * Chunk-specific cache key generation.\n *\n * Generates cache keys that only include styles relevant to a specific chunk,\n * enabling more granular caching and reuse.\n *\n * Enhanced to support predefined states:\n * - Global predefined states don't affect cache keys (constant across app)\n * - Local predefined states only affect cache keys if referenced in the chunk\n */\n\nimport {\n extractLocalPredefinedStates,\n extractPredefinedStateRefs,\n} from '../states';\nimport type { Styles } from '../styles/types';\n\n/**\n * Recursively serialize a value with sorted keys for stable output.\n * This ensures that {a: 1, b: 2} and {b: 2, a: 1} produce the same string.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value !== 'object') {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n // Object: sort keys for stable order\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const key of sortedKeys) {\n if (obj[key] !== undefined) {\n parts.push(`${JSON.stringify(key)}:${stableStringify(obj[key])}`);\n }\n }\n return '{' + parts.join(',') + '}';\n}\n\n/**\n * Generate a cache key for a specific chunk.\n *\n * Only includes the styles that belong to this chunk, allowing\n * chunks to be cached independently.\n *\n * Also includes relevant local predefined states that are referenced\n * by this chunk's styles.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns A stable cache key string\n */\nexport function generateChunkCacheKey(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): string {\n // Start with chunk name for namespace separation\n const parts: string[] = [chunkName];\n\n // Sort keys for stable ordering\n const sortedKeys = styleKeys.slice().sort();\n\n // Build the chunk-specific styles string for predefined state detection\n let chunkStylesStr = '';\n\n for (const key of sortedKeys) {\n const value = styles[key];\n if (value !== undefined) {\n // Use stable stringify for consistent serialization regardless of key order\n const serialized = stableStringify(value);\n parts.push(`${key}:${serialized}`);\n chunkStylesStr += serialized;\n }\n }\n\n // Extract local predefined states from the full styles object\n const localStates = extractLocalPredefinedStates(styles);\n\n // Only include local predefined states that are actually referenced in this chunk\n if (Object.keys(localStates).length > 0) {\n const referencedStates = extractPredefinedStateRefs(chunkStylesStr);\n const relevantLocalStates: string[] = [];\n\n for (const stateName of referencedStates) {\n if (localStates[stateName]) {\n relevantLocalStates.push(`${stateName}=${localStates[stateName]}`);\n }\n }\n\n // Add relevant local states to the cache key (sorted for stability)\n if (relevantLocalStates.length > 0) {\n relevantLocalStates.sort();\n parts.unshift(`[states:${relevantLocalStates.join('|')}]`);\n }\n }\n\n // Use null character as separator (safe, not in JSON output)\n return parts.join('\\0');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqBA,SAAS,gBAAgB,OAAwB;AAC/C,KAAI,UAAU,KACZ,QAAO;AAET,KAAI,UAAU,OACZ,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,KAAK,UAAU,MAAM;AAE9B,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,MAAM,IAAI,gBAAgB,CAAC,KAAK,IAAI,GAAG;CAGtD,MAAM,MAAM;CACZ,MAAM,aAAa,OAAO,KAAK,IAAI,CAAC,MAAM;CAC1C,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,WAChB,KAAI,IAAI,SAAS,OACf,OAAM,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,gBAAgB,IAAI,KAAK,GAAG;AAGrE,QAAO,MAAM,MAAM,KAAK,IAAI,GAAG;;;;;;;;;;;;;;;;AAiBjC,SAAgB,sBACd,QACA,WACA,WACQ;CAER,MAAM,QAAkB,CAAC,UAAU;CAGnC,MAAM,aAAa,UAAU,OAAO,CAAC,MAAM;CAG3C,IAAI,iBAAiB;AAErB,MAAK,MAAM,OAAO,YAAY;EAC5B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,QAAW;GAEvB,MAAM,aAAa,gBAAgB,MAAM;AACzC,SAAM,KAAK,GAAG,IAAI,GAAG,aAAa;AAClC,qBAAkB;;;CAKtB,MAAM,cAAc,6BAA6B,OAAO;AAGxD,KAAI,OAAO,KAAK,YAAY,CAAC,SAAS,GAAG;EACvC,MAAM,mBAAmB,2BAA2B,eAAe;EACnE,MAAM,sBAAgC,EAAE;AAExC,OAAK,MAAM,aAAa,iBACtB,KAAI,YAAY,WACd,qBAAoB,KAAK,GAAG,UAAU,GAAG,YAAY,aAAa;AAKtE,MAAI,oBAAoB,SAAS,GAAG;AAClC,uBAAoB,MAAM;AAC1B,SAAM,QAAQ,WAAW,oBAAoB,KAAK,IAAI,CAAC,GAAG;;;AAK9D,QAAO,MAAM,KAAK,KAAK"}
|