import type { IconifyJSON } from '@iconify/types' ; import type { UnoGenerator } from '@unocss/core' ; import { createGenerator } from '@unocss/core' ; import UnocssIcons from '@unocss/preset-icons' ; import fg from 'fast-glob' ; import { readFileSync } from 'fs' ; import { parse , preprocess } from 'svelte/compiler' ; import type { PreprocessorGroup , Processed , } from 'svelte/types/compiler/preprocess' ; import { Processor } from 'windicss/lib' ; import { CSSParser } from 'windicss/utils/parser' ; import { StyleSheet } from 'windicss/utils/style' ; import { globalStyleSheet , Magician , SetObject } from './utils' ; import { loadConfig } from 'unconfig' ; import { FullConfig } from 'windicss/types/interfaces' ; let entryFileName = '' ; interface generatorObject { data : SetObject ; updatedAt : number ; writtenAt ? : number ; } const raw = new Map < string , generatorObject > ( [ [ '__GLOBAL' , { data : { inlineClasses : new Set ( ) , inlineDirectives : new Set ( ) , inlineExpressions : new Set ( ) , inlineIcons : new Set ( ) , inlineAttributify : new Map ( ) , } , updatedAt : Date . now ( ) , } , ] , ] ) ; async function generateCSS ( key , attributes ) { const t = raw . get ( key ) ; const defaultStyleSheet = generatorWindi . interpret ( Array . from ( new Set ( [ ... ( t ?. data . inlineClasses || new Set ( ) ) , ... ( t ?. data . inlineDirectives || new Set ( ) ) , ... ( t ?. data . inlineExpressions || new Set ( ) ) , ] ) ) . join ( ' ' ) ) . styleSheet ; let defaultStyles = '' ; if ( attributes [ 'windi:global' ] || attributes [ 'windi-inline-global' ] ) { defaultStyles = globalStyleSheet ( defaultStyleSheet ) . build ( ) ; } else { defaultStyles = defaultStyleSheet . build ( ) ; } const nObj = { } ; t ?. data . inlineAttributify . forEach ( ( v , k ) => { nObj [ k ] = Array . from ( v ) ; } ) ; const attributifyStyleSheet = generatorWindi . attributify ( nObj ) . styleSheet ; let attributifyStyles = '' ; if ( attributes [ 'windi:global' ] || attributes [ 'windi-inline-global' ] ) { attributifyStyles = globalStyleSheet ( attributifyStyleSheet ) . build ( ) ; } else { attributifyStyles = attributifyStyleSheet . build ( ) ; } let iconStyles = '' ; if ( generatorUno && configuration . experimental ?. icons != undefined ) { iconStyles = await generatorUno . generate ( t ?. data . inlineIcons || '' ) . then ( resolve => resolve . css ) ; } return { defaultStyles , attributifyStyles , iconStyles } ; } function agent ( ) { let result ; return { async markup ( { content , filename } ) { if ( ! filename ) return { code : content } ; let worker = new Magician ( content , filename , configuration ) ; worker = worker . prepare ( ) ; worker = worker . setInject ( ) ; worker = worker . extract ( ) ; result = worker . getSets ( ) ; raw . set ( filename , { data : result , updatedAt : Date . now ( ) , } ) ; return { code : worker . getContent ( ) , } ; } , } ; } function main ( ) { if ( configuration . experimental ?. scan !== undefined ) { const files = fg . sync ( [ 'src/**/*.svelte' ] , { } ) ; for ( const file of files ) { const content = readFileSync ( file ) . toString ( ) ; const filename = file ; const ast = parse ( content , { filename } ) ; const hasGlobalInline = ast . css . attributes . some ( el => el . name == 'windi-inline-global' ) ; console . log ( filename , hasGlobalInline ) ; let worker = new Magician ( content , filename , configuration ) ; worker = worker . prepare ( ) ; worker = worker . extract ( ) ; const result = worker . getSets ( ) ; if ( hasGlobalInline ) { const global = raw . get ( '__GLOBAL' ) ! . data ; raw . set ( '__GLOBAL' , { data : { inlineClasses : new Set ( [ ... global . inlineClasses , ... result . inlineClasses , ] ) , inlineDirectives : new Set ( [ ... global . inlineDirectives , ... result . inlineDirectives , ] ) , inlineExpressions : new Set ( [ ... global . inlineExpressions , ... result . inlineExpressions , ] ) , inlineIcons : new Set ( [ ... global . inlineIcons , ... result . inlineIcons , ] ) , inlineAttributify : new Map ( [ ... global . inlineAttributify , ... result . inlineAttributify , ] ) , } , updatedAt : Date . now ( ) , } ) ; raw . set ( filename , { data : { inlineClasses : new Set ( ) , inlineDirectives : new Set ( ) , inlineExpressions : new Set ( ) , inlineIcons : new Set ( ) , inlineAttributify : new Map ( ) , } , updatedAt : Date . now ( ) , } ) ; } else { raw . set ( filename , { data : result , updatedAt : Date . now ( ) , } ) ; } } } return { async style ( { content , attributes , filename } ) { if ( ! filename ) return { code : content } ; let preflightStyleSheet = new StyleSheet ( ) ; if ( attributes [ 'windi:preflights:global' ] || attributes [ 'windi-preflights-global' ] ) { preflightStyleSheet = globalStyleSheet ( generatorWindi . preflight ( ) ) ; } const preflightStyles = preflightStyleSheet . build ( ) ; let safelistStyleSheet = new StyleSheet ( ) ; let safelistIconsStyleSheet = new StyleSheet ( ) ; if ( attributes [ 'windi:safelist:global' ] || attributes [ 'windi-safelist-global' ] ) { safelistStyleSheet = globalStyleSheet ( generatorWindi . interpret ( configuration . safeList || '' ) . styleSheet ) ; if ( generatorUno && configuration . experimental ?. icons != undefined ) { const { css } = await generatorUno . generate ( configuration . safeList || '' ) ; safelistIconsStyleSheet = globalStyleSheet ( new CSSParser ( css ) . parse ( ) ) ; } } let safelistStyles = safelistStyleSheet . build ( ) ; safelistStyles += safelistIconsStyleSheet . build ( ) ; let defaultStyles , attributifyStyles , iconStyles ; if ( entryFileName . length > 0 ) { const result = await generateCSS ( filename , attributes ) ; defaultStyles = result . defaultStyles ; attributifyStyles = result . attributifyStyles ; iconStyles = result . iconStyles ; } else { entryFileName = filename ; const resultGlobal = await generateCSS ( '__GLOBAL' , { 'windi-inline-global' : true , } ) ; defaultStyles = resultGlobal . defaultStyles ; attributifyStyles = resultGlobal . attributifyStyles ; iconStyles = resultGlobal . iconStyles ; const result = await generateCSS ( filename , attributes ) ; defaultStyles += result . defaultStyles ; attributifyStyles += result . attributifyStyles ; iconStyles += result . iconStyles ; } const cssStyleSheet = new CSSParser ( content , generatorWindi ) . parse ( ) || new StyleSheet ( ) ; let cssStyles = '' ; if ( attributes [ 'global' ] ) { cssStyles = globalStyleSheet ( cssStyleSheet ) . build ( ) ; } else { cssStyles = cssStyleSheet . build ( ) ; } let newCode = '' ; if ( preflightStyles . length > 0 ) newCode += '\n' + preflightStyles ; if ( safelistStyles . length > 0 ) newCode += '\n' + safelistStyles ; if ( defaultStyles . length > 0 ) newCode += '\n' + defaultStyles ; if ( attributifyStyles . length > 0 ) newCode += '\n' + attributifyStyles ; if ( iconStyles . length > 0 ) newCode += '\n' + iconStyles ; if ( cssStyles . length > 0 ) newCode += '\n' + cssStyles ; newCode += '\n' ; return { code : newCode , } ; } , } ; } export interface BaseConfig { silent ? : boolean ; mode ? : 'development' | 'production' ; configPath ? : string ; disableFormat ? : boolean ; devTools ? : { enabled : boolean ; completions ? : boolean ; } ; safeList ? : string ; preflights ? : boolean ; experimental ? : { icons ? : { prefix ? : string ; collections ? : Record < string , IconifyJSON > ; extraProperties ? : Record < string , string > ; } ; scan ? : boolean ; } ; } export type DefaultConfig = BaseConfig ; export type UserConfig = BaseConfig ; const defaultConfig = { silent : false , disableFormat : false , devTools : { enabled : false , } , safeList : undefined , preflights : true , } ; let configuration ; let generatorWindi ; let generatorUno ; let windiConfiguration ; export function windi ( userConfig = { } ) { configuration = { ... defaultConfig , ... userConfig } ; generatorWindi = new Processor ( ) ; const steps = [ ] ; if ( configuration . experimental ?. icons != undefined ) { generatorUno = createGenerator ( { presets : [ UnocssIcons ( { ... configuration . experimental . icons , } ) , ] , } , { } ) ; } if ( configuration . experimental ?. scan == undefined ) { steps . push ( agent ( ) ) ; } steps . push ( main ( ) ) ; return { async markup ( { content , filename } ) { if ( ! windiConfiguration ) { const { config } = await loadConfig < FullConfig > ( { merge : false , sources : [ { files : 'windi.config' , extensions : [ 'ts' , 'mts' , 'cts' , 'js' , 'mjs' , 'cjs' , 'json' , '' ] , } , ] , } ) ; windiConfiguration = config ; if ( typeof config ?. safelist == 'string' ) { configuration . safeList = config . safelist ; } else if ( config ?. safelist ) { configuration . safeList = [ ... new Set ( config . safelist . flat ( Infinity ) ) , ] . join ( ' ' ) ; } generatorWindi . loadConfig ( config ) ; } let currentContent = content ; for ( const step of steps ) { const code = ( await preprocess ( currentContent , step , { filename } ) ) . code ; currentContent = code ; } return { code : currentContent , } ; } , } ; } export default windi ;