@@ -4,47 +4,140 @@ import {inferDataType} from "./utils/infer-data-type.ts";
44import { getTailwindTheme } from "./theme.ts" ;
55import { isColor } from "./utils/is-color.ts" ;
66import { decodeArbitraryValue } from "./utils/decodeArbitraryValue.ts" ;
7+ import { segment } from "./utils/segment.ts" ;
8+ import { PluginNotFoundException } from "./exceptions/plugin-not-found-exception.ts" ;
9+ import { colord } from "colord" ;
10+ import type { CustomThemeConfig } from "tailwindcss/types/config" ;
11+ import { StringBuilder } from "./utils/string-builder.ts" ;
712
813export type AstDeclaration = {
914 property : string
1015 value : string
1116 variants ?: Variant [ ]
12- modifiers ?: Modifier [ ] ,
17+ modifier ?: string ,
1318 important ?: boolean
1419 negative ?: boolean ,
1520}
1621
17- export const classname = ( ast : AstDeclaration ) : string => {
18- const theme = getTailwindTheme ( )
19- const important = ast . important ? "!" : ""
20- let variants = ast . variants ?. length ? buildVariants ( ast . variants ) : [ ]
22+ export const classname = ( ast : AstDeclaration , config ?: CustomThemeConfig ) : string => {
23+ const theme = getTailwindTheme ( config )
24+ const stringBuilder = new StringBuilder ( )
25+ let negative = ast . negative || false
26+ stringBuilder . appendVariants ( ...ast . variants || [ ] )
2127
22- /**
23- * output "sm:hover:lg:!" or "!" or ""
24- */
25- const prefix = `${ variants } ${ important } `
26- const [ namedPluginClassName , namedPluginClassPlugin ] = [ ...namedPlugins . entries ( ) ] . find ( ( [ , plugin ] ) => plugin . value === ast . value ) || [ ]
27- if ( namedPluginClassName ) {
28- return `${ prefix } ${ namedPluginClassName } `
28+ if ( ast . important ) {
29+ stringBuilder . makeImportant ( )
2930 }
3031
31- //color is special, we need to find if value is a color
32- if ( isColor ( ast . value , theme ) ) {
33- console . log ( decodeArbitraryValue ( ast . value ) )
34- return `${ prefix } text-[${ ast . value } ]`
32+ if ( ast . value [ 0 ] === "-" ) {
33+ ast . value = ast . value . slice ( 1 )
34+ negative = true
3535 }
3636
37- /*
38- const [functionalPluginClassName, matchedFunctionalPlugins] = [...functionalPlugins.entries()].find(([,plugin]) => plugin.value === ast.value) || []
39- if(key){
40- return `${prefix}${key}`
41- }*/
37+ const [ namedPluginClassName , namedPluginClassPlugin ] = [ ...namedPlugins . entries ( ) ] . find ( ( [ , plugin ] ) => plugin . value === ast . value ) || [ ]
38+ if ( namedPluginClassName ) {
39+ return stringBuilder . addRoot ( namedPluginClassName ) . toString ( )
40+ }
41+
42+ const [ root , possiblePlugins = [ ] ] = [ ...functionalPlugins . entries ( ) ] . find ( ( [ root , plugins ] ) => plugins . some ( o => o . ns === ast . property ) ) || [ ]
43+ if ( ! root ) {
44+ throw new PluginNotFoundException ( ast . property )
45+ }
46+
47+ stringBuilder . addRoot ( root )
48+ //color is special, we need to find if value is a color
49+ if ( isColor ( ast . value , theme ) ) {
50+ const matchedPlugin = possiblePlugins . find ( ( plugin ) => plugin . type === "color" )
51+ if ( ! matchedPlugin ) {
52+ throw new PluginNotFoundException ( ast . property )
53+ }
54+
55+ let tailwindThemeColor = ast . value . split ( '-' ) . reduce ( ( acc , val ) => acc [ val ] , theme [ matchedPlugin . scaleKey || "colors" ] as any )
56+ if ( tailwindThemeColor && typeof tailwindThemeColor !== "object" ) {
57+ //user entered a value like "red-500". we found equivalent tailwind theme color.
58+ //return TW class directly like "bg-red-500" with modifiers and variants
59+ return stringBuilder
60+ . appendModifier ( buildModifier ( ast . modifier , theme . opacity ) )
61+ . addValue ( ast . value )
62+ . toString ( )
63+ }
64+ //at this point we know user entered a value like "#ff0000", or just "red" maybe rgba, hsla, etc.
65+ //try to get hex color and check if tailwind has it.
66+ const color = calculateHex ( ast . value )
67+ return stringBuilder
68+ . appendModifier ( buildModifier ( color . alpha || ast . modifier , theme . opacity ) )
69+ . addValue ( findTailwindColorByHex ( color . hex , theme [ matchedPlugin . scaleKey || "colors" ] ) || StringBuilder . makeArbitrary ( color . hex ) )
70+ . toString ( )
71+ }
72+
73+ const matchedPlugin = possiblePlugins . find ( ( plugin ) => plugin . ns === ast . property )
74+ if ( ! matchedPlugin ) {
75+ throw new PluginNotFoundException ( ast . property )
76+ }
4277
43- return "dummy"
78+ const possibleValue = findTailwindValueByUnit ( ast . value , theme [ matchedPlugin . scaleKey ] )
79+ if ( possibleValue ) {
80+ stringBuilder . addValue ( possibleValue )
81+
82+ //for making the class negative, we are making sure matched TW Plugin supports negative
83+ if ( matchedPlugin . supportNegative && negative ) {
84+ stringBuilder . makeNegative ( )
85+ }
86+ }
87+
88+ return stringBuilder . toString ( )
89+ }
90+
91+ const calculateHex = ( input : string ) : { hex : string , alpha : string | undefined } => {
92+ const color = colord ( input )
93+ const alpha = color . alpha ( )
94+
95+ return {
96+ hex : color . alpha ( 1 ) . toHex ( ) ,
97+ alpha : alpha !== 1 ? alpha . toString ( ) : undefined
98+ }
4499}
45100
46- const buildVariants = ( variants : Variant [ ] ) : string => {
47- const variantOrder : Variant [ "type" ] [ ] = [ "media" , 'system' , "interaction" , "pseudo" , "content" , "form" , "state" , "misc" ]
48- const _sortedVariants = variants . sort ( ( a , b ) => variantOrder . indexOf ( a . type ) - variantOrder . indexOf ( b . type ) )
49- return _sortedVariants . length > 0 ? _sortedVariants . map ( x => x . value ) . join ( ':' ) + ':' : ''
101+ const buildModifier = ( modifier : string | undefined , opacityScale : any ) : string => {
102+ if ( ! modifier ) return ""
103+
104+ for ( let [ key , value ] of Object . entries ( opacityScale ) ) {
105+ if ( key === modifier || value === modifier ) {
106+ return key
107+ }
108+ }
109+
110+ return StringBuilder . makeArbitrary ( modifier )
111+ }
112+
113+ const findTailwindColorByHex = ( colorInput : string | undefined , colors : any ) => {
114+ if ( ! colorInput ) return false
115+
116+ for ( let [ key , twColors ] of Object . entries ( colors ) ) {
117+ for ( let [ shade , hex ] of Object . entries ( twColors as [ string , string ] ) ) {
118+ if ( hex === colorInput ) {
119+ return `${ key } -${ shade } `
120+ }
121+ }
122+ }
123+
124+ return false
125+ }
126+
127+ const findTailwindValueByUnit = ( unit : string | undefined , scale : any ) => {
128+ if ( ! unit ) {
129+ return undefined
130+ }
131+
132+ for ( let [ key , value ] of Object . entries ( scale ) ) {
133+ if (
134+ ( Array . isArray ( value ) && ( key === unit || value . includes ( unit ) ) ) ||
135+ ( key === unit || value === unit )
136+ ) {
137+ return key !== "DEFAULT" ? key : undefined
138+ }
139+ }
140+
141+ //if unit is not found in tailwind scales, it's probably an arbitrary unit
142+ return StringBuilder . makeArbitrary ( unit )
50143}
0 commit comments