import lex from 'pug-lexer' ; import walk from 'pug-walk' ; import parser from 'pug-parser' ; import merge from 'lodash.merge' ; import wrap from 'pug-runtime/wrap' ; import generateCode from 'pug-code-gen' ; import generateSource from 'pug-source-gen' ; import replaceJs from './js' ; import replaceCss from './css' ; import selectorsLibrary from '../selectorsLibrary' ; import shouldTriggerAttribute , { Attr } from '../helpers/shouldTriggerAttribute' ; type EspreeOptions = { } ; export interface ReplacePugOptions { sourceFile ? : string ; espreeOptions ? : EspreeOptions ; triggerClassAttributes ? : string [ ] ; triggerIdAttributes ? : string [ ] ; } const replacePug = ( code , opts = { } ) : string => { const lexed = lex ( code ) ; const ast = parser ( lexed ) ; const defaultOptions = { espreeOptions : { } , triggerClassAttributes : [ ] , triggerIdAttributes : [ ] , } ; const options = merge ( { } , opts , defaultOptions ) ; walk ( ast , ( node ) => { if ( node . name === 'script' || node . name === 'style' ) { const modifiedBlockNodes = node . block . nodes . map ( ( block ) => { if ( block . type === 'Code' ) { block . type = 'Text' ; block . val = `#{${ block . val }}` ; } return block ; } ) ; const newCode = wrap ( ( generateCode ( ( { ... node . block , nodes : modifiedBlockNodes } ) ) ) ) ( ) ; const replacedCode = node . name === 'script' ? replaceJs ( newCode , merge ( { } , options . espreeOptions , { sourceFile : opts . sourceFile } ) ) : replaceCss ( newCode , { sourceFile : opts . sourceFile } ) ; const pugCode = `${ node . name }.\n${ replacedCode }` . replace ( /\n/g , '\n\t' ) ; const astReplaced = parser ( lex ( pugCode ) ) ; const scriptBlock = astReplaced . nodes [ 0 ] . block ; node . block . nodes = node . block . nodes . map ( ( n , i ) => { const { val , type } = scriptBlock . nodes [ i ] ; return { ... n , val , type } ; } ) ; } if ( Array . isArray ( node . attrs ) && node . attrs . length >= 0 ) { node . attrs . forEach ( ( attr ) => { let selectorType ; let shouldReplace = false ; if ( attr . name === 'class' || options . triggerClassAttributes . some ( ( item ) => ( shouldTriggerAttribute ( attr , item ) ) ) ) { selectorType = '.' ; shouldReplace = true ; } if ( attr . name === 'id' || options . triggerIdAttributes . some ( ( item ) => ( shouldTriggerAttribute ( attr , item ) ) ) ) { selectorType = '#' ; shouldReplace = true ; } if ( ! shouldReplace ) { return ; } const prefix = attr . val . charAt ( 0 ) ; const suffix = attr . val . charAt ( attr . val . length - 1 ) ; const val = attr . val . slice ( 1 , ( attr . val . length - 1 ) ) ; const replacedVal = val . split ( ' ' ) . map ( ( value ) => ( selectorsLibrary . get ( `${ selectorType }${ value }` , { source : { file : opts . sourceFile || '' , line : node . line , text : '' } , } ) . replace ( new RegExp ( `^\\${ selectorType }` ) , '' ) ) ) . join ( ' ' ) ; attr . val = prefix + replacedVal + suffix ; } ) ; } } ) ; return generateSource ( ast ) ; } ; export default replacePug ;