import { Options } from "./plugin" ; import BigNumber from "bignumber.js" ; export type OperationFunction = ( ... args : unknown [ ] ) => number | string ; export interface ParsedOptions { [ unitName ] : RuntimeOperationFunction ; } export interface Operation { sub : OperationFunction ; add : OperationFunction ; mul : OperationFunction ; div : OperationFunction ; unit : OperationFunction ; } export type RuntimeOperationFunction = ( target : string ) => string ; export interface MethodParseResult { methodName : string ; arg : string ; } export interface CSSValue { value : number ; unit : string ; } export function optionsParser ( options ) { const { rules = { } } = options || { } ; const parsedOptions = { } ; for ( const unitName of Object . keys ( rules ) ) { parsedOptions [ unitName ] = operationParser ( unitName , rules [ unitName ] ) ; } return parsedOptions ; } export function extractCSSValue ( target ) { const [ value , unit ] = target . split ( /(?<=\d)(?=[a-zA-Z])/ ) ; return { value : + value , unit : unit } ; } function operationParser ( unitName , unitValue ) { const methods = unitValue . split ( /(?<=[^\d])\.(?=[^\d])/ ) ; const funcs = [ ] ; for ( const method of methods ) { const { methodName , arg } = methodParser ( method ) ; funcs . push ( ( target ) => operation [ methodName ] ( target , arg ) ) ; } return ( target ) => { const { value , unit : rawUnit } = extractCSSValue ( target ) ; if ( value === undefined || rawUnit === undefined || isNaN ( value ) || rawUnit === "" || unitName !== rawUnit ) { return target ; } const processResult = pipe ( value , funcs ) ; return typeof processResult === "string" ? processResult : processResult + unitName ; } ; } function methodParser ( methodString ) { const result = methodString . match ( /([a-zA-Z]+)\(([^()]+?)\)/ ) ; if ( ! result ) { parseError ( ) ; return ; } const methodName = result [ 1 ] ; const arg = result [ 2 ] ; if ( ! methodName || ! arg ) { parseError ( ) ; return ; } return { methodName , arg } ; } function parseError ( ) { throw new Error ( "Parse Error: Please check your rules" ) ; } function parseCSSError ( ) { throw new Error ( "Parse Error: Please check your cssom" ) ; } function pipe < T extends number | string > ( initValue , funcs ) { return funcs . reduce ( ( total , _ , i , funcs ) => funcs [ i ] ( typeof total === "string" ? extractCSSValue ( total ) . value : total ) , initValue ) ; } function optionArg2NumGuard ( target ) { const result = + target ; if ( isNaN ( result ) ) { return 0 ; } return result ; } const operation = { sub : ( target , opNum ) => + new BigNumber ( target ) . minus ( optionArg2NumGuard ( opNum ) ) , add : ( target , opNum ) => + new BigNumber ( target ) . plus ( optionArg2NumGuard ( opNum ) ) , div : ( target , opNum ) => + new BigNumber ( target ) . div ( optionArg2NumGuard ( opNum ) ) , mul : ( target , opNum ) => + new BigNumber ( target ) . multipliedBy ( optionArg2NumGuard ( opNum ) ) , unit : ( target , unit ) => target + unit } ;