import { parse } from 'postcss' ; import cssVariablesLibrary from '../cssVariablesLibrary' ; import arrayToRegex from '../helpers/arrayToRegex' ; import keyframesLibrary from '../keyframesLibrary' ; import selectorsLibrary from '../selectorsLibrary' ; import replaceRegex from './regex' ; const getAttributeSelector = ( match ) : string => { const re = new RegExp ( replaceRegex . commonAttributeSelectors ) ; const exec = re . exec ( match ) ; if ( ! exec ) { return match ; } const stringChar = exec [ 3 ] . charAt ( 0 ) ; const stringWithoutStringChars = exec [ 3 ] . slice ( 1 , exec [ 3 ] . length - 1 ) ; let result = match ; let newString = exec [ 3 ] ; const suffix = selectorsLibrary . getSuffix ( ) ; const prefix = selectorsLibrary . getPrefix ( ) ; if ( exec [ 2 ] === '$' ) { newString = stringChar + stringWithoutStringChars + suffix + stringChar ; } if ( exec [ 2 ] === '^' ) { newString = stringChar + prefix + stringWithoutStringChars + stringChar ; } result = result . replace ( replaceRegex . strings , newString ) ; return result ; } ; export interface ReplaceCssOptions { sourceFile ? : string ; triggerClassAttributes ? : string [ ] ; triggerIdAttributes ? : string [ ] ; } const replaceCss = ( css , opts = { } ) : string => { const cssAST = parse ( css ) ; cssAST . walk ( ( node ) => { const parentName = node . parent . name || '' ; const selectorLib = selectorsLibrary . getIdSelector ( ) ; const source = { file : opts . sourceFile || '' , line : node . source . start . line , text : '' } ; if ( node . selector && ! parentName . match ( /keyframes/ ) ) { const regex = selectorLib . getAll ( { regex : true , addSelectorType : true } ) ; if ( typeof regex !== 'string' && ! ( regex instanceof RegExp ) ) { return ; } node . selectors = node . selectors . map ( ( selector ) => selector . split ( '#' ) . map ( ( splittedSelector ) => { const splittedSelectorWithDot = `#${ splittedSelector }` ; const prefixFreeSelector = splittedSelectorWithDot . replace ( /\\/g , '' ) ; if ( ! prefixFreeSelector . match ( regex ) ) { return splittedSelector ; } return prefixFreeSelector . replace ( regex , ( match ) => ( selectorLib . get ( match , { addSelectorType : true , source , } ) ) ) . slice ( 1 ) ; } ) . join ( '#' ) ) ; } } ) ; cssAST . walk ( ( node ) => { const parentName = node . parent . name || '' ; const selectorLib = selectorsLibrary . getClassSelector ( ) ; const source = { file : opts . sourceFile || '' , line : node . source . start . line , text : '' } ; if ( node . selector && ! parentName . match ( /keyframes/ ) ) { const regex = selectorLib . getAll ( { regex : true , addSelectorType : true } ) ; if ( typeof regex !== 'string' && ! ( regex instanceof RegExp ) ) { return ; } node . selectors = node . selectors . map ( ( selector ) => selector . split ( '.' ) . map ( ( splittedSelector ) => { const splittedSelectorWithDot = `.${ splittedSelector }` ; const prefixFreeSelector = splittedSelectorWithDot . replace ( /\\/g , '' ) ; if ( ! prefixFreeSelector . match ( regex ) ) { return splittedSelector ; } return prefixFreeSelector . replace ( regex , ( match ) => ( selectorLib . get ( match , { addSelectorType : true , source , } ) ) ) . slice ( 1 ) ; } ) . join ( '.' ) ) ; } } ) ; cssAST . walkAtRules ( ( node ) => { if ( ! node . name . match ( /keyframes/ ) ) { return ; } const source = { file : opts . sourceFile || '' , line : node . source . start . line , text : '' } ; node . params = keyframesLibrary . get ( node . params , { countStats : false , source } ) ; } ) ; cssAST . walkDecls ( ( node ) => { const source = { file : opts . sourceFile || '' , line : node . source . start . line , text : '' } ; if ( node . value . match ( replaceRegex . cssVariables ) ) { const regex = arrayToRegex ( Object . keys ( cssVariablesLibrary . values ) , ( v ) => `--${ v }` ) ; if ( regex ) { node . value = node . value . replace ( regex , ( match ) => ( `--${ cssVariablesLibrary . get ( match . replace ( /^--/ , '' ) , { source } ) }` ) ) ; } } if ( node . type === 'decl' && node . prop . match ( '^--' ) ) { node . prop = cssVariablesLibrary . get ( node . prop , { countStats : false , source } ) ; } if ( node . prop . match ( /(animation|animation-name)/ ) ) { node . value = node . value . replace ( ',' , ' , ' ) . split ( ' ' ) . map ( ( value ) => ( keyframesLibrary . get ( value , { source } ) ) ) . join ( ' ' ) . replace ( ' , ' , ',' ) ; } } ) ; cssAST . walkRules ( ( node ) => { node . selector = node . selector . replace ( replaceRegex . commonAttributeSelectors , ( match ) => ( getAttributeSelector ( match ) ) ) ; } ) ; return cssAST . toResult ( ) . css ; } ; export default replaceCss ;