UNPKG

@pandabox/panda-plugins

Version:

- `missing-css-warnings` - Logs a warning message when a CSS rule was used at runtime but couldn't be statically extracted - `strict-tokens-scope` - Enforce `strictTokens` only for a set of `TokenCategory` or style props - `strict-tokens-runtime` - Enfo

108 lines (90 loc) 3.7 kB
import type { CodegenPrepareHookArgs, CssProperties, LoggerInterface, PandaPlugin, TokenCategory, } from '@pandacss/types' import type { PandaContext } from '@pandacss/node' export interface StrictTokensScopeOptions { categories?: TokenCategory[] props?: Array<keyof CssProperties | (string & {})> } /** * Enforce `strictTokens` only for a set of `TokenCategory` or style props * - `categories`: Every properties bound to those token categories will be restricted with `strictTokens` * - `props`: Explicit list of props that will be restricted with `strictTokens` * * @see https://panda-css.com/docs/concepts/writing-styles#type-safety */ export const pluginStrictTokensScope = (options: StrictTokensScopeOptions): PandaPlugin => { let logger: LoggerInterface let ctx: PandaContext return { name: 'strict-tokens-scope', hooks: { 'context:created': (context) => { logger = context.logger // @ts-expect-error ctx = context.ctx.processor.context }, 'codegen:prepare': (args) => { return transformPropTypes(args, options, ctx, logger) }, }, } } export const transformPropTypes = ( args: CodegenPrepareHookArgs, options: StrictTokensScopeOptions, ctx: PandaContext, logger?: LoggerInterface, ) => { const { categories = [], props = [] } = options if (!categories.length && !props.length) return args.artifacts const artifact = args.artifacts.find((x) => x.id === 'types-styles') const content = artifact?.files.find((x) => x.file.includes('style-props')) if (!content?.code) return args.artifacts const shorthandsByProp = new Map<string, string[]>() ctx.utility.shorthands.forEach((longhand, shorthand) => { shorthandsByProp.set(longhand, (shorthandsByProp.get(longhand) ?? []).concat(shorthand)) }) const types = ctx.utility.getTypes() const categoryByProp = new Map<string, TokenCategory>() types.forEach((values, prop) => { const categoryType = values.find((type) => type.includes('Tokens[')) if (!categoryType) return const tokenCategory = categoryType.replace('Tokens["', '').replace('"]', '') as TokenCategory if (!categories.includes(tokenCategory)) { return } categoryByProp.set(prop, tokenCategory) const shorthands = shorthandsByProp.get(prop) if (!shorthands) return shorthands.forEach((shorthand) => { categoryByProp.set(shorthand, tokenCategory) }) }) const strictTokenProps = props.concat(Array.from(categoryByProp.keys())) if (!strictTokenProps.length) return args.artifacts if (logger) { logger.debug('plugin:restrict-strict-tokens', `🐼 Strict token props: ${strictTokenProps.join(', ')}`) } // const regex = new RegExp(`(${strictTokenProps.join('|')})\?: ConditionalValue<WithEscapeHatch<(.+)>>`, 'g') const regex = /(\w+)\?: ConditionalValue<WithEscapeHatch<(.+)>>/g const transformed = content.code.replace(regex, (match, prop, value) => { // console.log({ match, prop, value, shouldBeStrict: strictTokenProps.includes(prop) }) if (strictTokenProps.includes(prop)) return match const longhand = ctx.utility.shorthands.get(prop) // console.log({ prop, longhand, isStrict: strictTokenProps.includes(prop) }) if (value.includes('CssProperties')) return match return `${prop}?: ConditionalValue<${value} | CssProperties["${longhand || prop}"]>` }) + [ `\n\ntype StrictTokenProps = ${strictTokenProps.map((t) => `"${t}"`).join(' | ')}`, `type Restrict<Key, Value, Fallback> = Key extends StrictTokenProps ? Value : Value | Fallback`, ].join('\n\n') content.code = transformed return args.artifacts }