import type { Root } from 'postcss' ; import postcss from 'postcss' ; import * as path from 'path' ; import * as fs from 'fs' ; import { isFunction } from '../../utils/common' ; interface INode { name : string ; value : string ; node : Root ; } /**
 * walker 差异化配置项
 */ const config = { less : { walker : 'walkAtRules' , node : { name : 'name' , nameGetter : ( str ) => str , value : 'params' } } , sass : { walker : 'walkDecls' , node : { name : 'prop' , nameGetter : ( str ) => str . slice ( 1 ) , value : 'value' } } } ; /**
 * 获取 postcss 解析后的 ast 节点的某些值
 */ const getNode = ( type , node ) : INode => { const { name , nameGetter , value } = config [ type ] . node ; return { name : nameGetter ( node [ name ] ) , value : node [ value ] , node : config [ type ] . node } ; } ; /**
 * 重新封装后的 postcss 遍历函数 (sync)
 */ export const walker = ( type , root , cb ) => { return root [ config [ type ] . walker ] ( e => cb ( getNode ( type , e ) ) ) ; } ; param { String } type param { Root } root param { Function } cb return { Boolean } export const walkerSome = ( type , root , cb ) : boolean => { let result = false ; walker ( type , root , e => { const value = cb ( e ) ; if ( value ) { result = true ; return false ; } } ) ; return result ; } ; param { String } type param { Root } root param { Function } cb return { T } export const walkerFind = < T extends any > ( type , root , cb ) : T => { let result ; walker ( type , root , e => { const value = cb ( e ) ; if ( value ) { result = value ; return false ; } } ) ; return result ; } ; param { Root } root param { string } type param { Function } cb return { Array } export const walkDeps = ( root , type , cb = ( ) => { } ) => { const deps = new Map < string , Root > ( ) ; const blacklist = [ '/reset' , 'mixin' , '_' ] ; /**
     * 不处理存在于黑名单内单词的路径
     */ const isBlack = ( list , id ) : boolean => { return list . some ( word => id . includes ( word ) ) ; } ; /**
     * 去掉字符左右引号
     */ const getValue = ( str ) => { const value = str . trim ( ) ; return value . slice ( 1 , value . length - 1 ) ; } ; const resolve = ( node , url ? ) => { const baseUrl = url ?? node . source . input . file ; const baseDir = path . dirname ( baseUrl ) ; deps . set ( baseUrl , node ) ; cb ( node ) ; node . walkAtRules ( decl => { const isImport = decl . name === 'import' ; if ( isImport ) { let id = getValue ( decl . params ) ; if ( id . endsWith ( '.css' ) ) return ; const ext = type === 'less' ? '.less' : '.scss' ; id = id . endsWith ( ext ) ? id : `${ id }${ ext }` ; if ( isBlack ( blacklist , id ) ) return ; let pathUrl = path . resolve ( baseDir , id ) ; if ( ! fs . existsSync ( url ) ) { pathUrl = path . resolve ( `node_modules/${ id }` ) ; } if ( deps . has ( pathUrl ) ) return ; const file = fs . readFileSync ( pathUrl , 'utf8' ) ; const parser = type === 'less' ? require ( 'postcss-less' ) : require ( 'postcss-scss' ) ; const rt = postcss ( ) . process ( file , { parser , from : pathUrl } ) . root ; resolve ( rt , pathUrl ) ; } } ) ; } ; resolve ( root ) ; const list = [ ] ; deps . forEach ( value => { list . push ( value ) ; } ) ; return list . reverse ( ) ; } ; /**
 * 获取该 postcss node 树下的全部变量
 */ export const getAllVars = ( node , type , cb ) => { walker ( type , node , ( { name , value } ) => { if ( isFunction ( value ) || name === 'import' ) return ; const varName = type === 'less' ? name . slice ( 0 , name . length - 1 ) : name ; cb ( varName , value ) ; } ) ; } ;