import { applyPlugins } from './PluginsRegistry' ; import { getRule } from './RulesRegistry' ; import isNonEmptyString from './utils/isNonEmptyString' ; import isPlainObject from './utils/isPlainObject' ; import { Indexable , Nullable } from './utils/types' ; export type Rule = { id : string ; definitions : Definition [ ] ; dependencies : string [ ] ; } ; export type Definition = { style : StyleObject ; selector : string ; media : Nullable < string > ; } ; export interface RuleModifier { param selector param media param style addDefinition ( selector , media , style ) : void ; param ruleId addDependency ( ruleId ) : void ; } export type StyleObject = Indexable < any > ; param id param style export function createRule ( id , style ) { const definitions = [ ] ; const dependencies = [ ] ; let currentSelector = '&' ; let currentMedia = null ; function createDefinition ( modifier , style , index ) { applyPlugins ( modifier , style ) ; if ( Object . keys ( style ) . length ) { definitions . splice ( index , 0 , { style , media : currentMedia , selector : currentSelector } ) ; } } const ruleModifiders = { addDefinition ( selector , media , style ) { if ( ! isPlainObject ( style ) ) { throw new Error ( 'Expected style description to be a plain object' ) ; } if ( selector && typeof selector !== 'string' ) { throw new Error ( 'Expected selector to be a string' ) ; } if ( media && typeof media !== 'string' ) { throw new Error ( 'Expected selector to be a string' ) ; } if ( currentMedia && media ) { throw new Error ( 'Media query nesting is not allowed' ) ; } if ( ! media && ! selector ) { throw new Error ( 'Expected definition to have a selector or a media' ) ; } const index = definitions . length ; const previousSelector = currentSelector ; const previousMedia = currentMedia ; currentSelector = selector ? selector . replace ( '&' , currentSelector ) : currentSelector ; currentMedia = media || currentMedia ; createDefinition ( ruleModifiders , style , index ) ; currentSelector = previousSelector ; currentMedia = previousMedia ; } , addDependency ( ruleId ) { if ( ! isNonEmptyString ( ruleId ) || ! getRule ( ruleId ) ) { throw new Error ( 'the dependency should be an id returned by the RulesRegistry' ) ; } if ( currentMedia || currentSelector !== '&' ) { throw new Error ( 'dependencies can only be defined on top level rules' ) ; } dependencies . push ( ruleId ) ; } , } ; createDefinition ( ruleModifiders , style , 0 ) ; return { id , definitions , dependencies } ; }