import * as debugGenerator from "debug" ; import { postcss } from "opticss" ; import { Block } from "../BlockTree" ; import { Options , ResolvedConfiguration , resolveConfiguration } from "../configuration" ; import { CssBlockError } from "../errors" ; import { FileIdentifier } from "../importing" ; import { builders } from "./ast" ; import { addPresetSelectors } from "./features/add-preset-selectors" ; import { assertForeignGlobalAttribute } from "./features/assert-foreign-global-attribute" ; import { composeBlock } from "./features/composes-block" ; import { constructBlock } from "./features/construct-block" ; import { disallowDefinitionRules } from "./features/disallow-dfn-rules" ; import { disallowImportant } from "./features/disallow-important" ; import { discoverGuid } from "./features/discover-guid" ; import { discoverName } from "./features/discover-name" ; import { exportBlocks , exportBlocksSync } from "./features/export-blocks" ; import { extendBlock } from "./features/extend-block" ; import { globalAttributes } from "./features/global-attributes" ; import { implementBlock } from "./features/implement-block" ; import { importBlocks , importBlocksSync } from "./features/import-blocks" ; import { processDebugStatements } from "./features/process-debug-statements" ; import { BlockFactory } from "./index" ; import { BlockFactorySync } from "./index" ; import { Syntax } from "./preprocessing" ; import { gen_guid } from "./utils/genGuid" ; const debug = debugGenerator ( "css-blocks:BlockParser" ) ; export interface ParsedSource { identifier : FileIdentifier ; defaultName : string ; originalSource : string ; originalSyntax : Syntax ; parseResult : postcss . Root ; dependencies : string [ ] ; } /**
 * Parser that, given a PostCSS AST will return a `Block` object. Main public
 * interface is `BlockParser.parse`.
 */ export class BlockParser { private config : ResolvedConfiguration ; private factory : BlockFactory | BlockFactorySync ; constructor ( opts , factory ) { this . config = resolveConfiguration ( opts ) ; this . factory = factory ; } public async parseSource ( source ) { let root = source . parseResult ; let block = await this . parse ( root , source . identifier , source . defaultName ) ; for ( let dependency of source . dependencies ) { block . addDependency ( dependency ) ; } return block ; } public parseSourceSync ( source ) { let root = source . parseResult ; let block = this . parseSync ( root , source . identifier , source . defaultName ) ; for ( let dependency of source . dependencies ) { block . addDependency ( dependency ) ; } return block ; } public async parseDefinitionSource ( root , identifier , expectedId , defaultName ) { return await this . parse ( root , identifier , defaultName , true , expectedId ) ; } public parseDefinitionSourceSync ( root , identifier , expectedId , defaultName ) { return this . parseSync ( root , identifier , defaultName , true , expectedId ) ; } param root param sourceFile param name param isDfnFile param expectedGuid public async parse ( root , identifier , name , isDfnFile = false , expectedGuid ? ) { let importer = this . config . importer ; let debugIdent = importer . debugIdentifier ( identifier , this . config ) ; let sourceFile = importer . filesystemPath ( identifier , this . config ) || debugIdent ; let configuration = this . factory . configuration ; debug ( `Begin parse: "${ debugIdent }" which ${ isDfnFile ? "is" : "is not" } a definition file.` ) ; let nameDiscoveryError ; try { name = discoverName ( configuration , root , sourceFile , isDfnFile , name ) ; } catch ( e ) { nameDiscoveryError = e ; } let guid ; let guidDiscoveryError ; try { guid = discoverGuid ( configuration , root , sourceFile , isDfnFile , expectedGuid ) || gen_guid ( identifier , configuration . guidAutogenCharacters ) ; } catch ( e ) { guidDiscoveryError = e ; guid = gen_guid ( identifier , configuration . guidAutogenCharacters ) ; } let block = new Block ( name , identifier , guid , root ) ; block . blockAST = builders . root ( ) ; if ( nameDiscoveryError ) { block . addError ( nameDiscoveryError ) ; } if ( guidDiscoveryError ) { block . addError ( guidDiscoveryError ) ; } if ( ! isDfnFile ) { debug ( " - Disallow Definition-Only Declarations" ) ; disallowDefinitionRules ( block , configuration , root , sourceFile ) ; } disallowImportant ( configuration , root , block , sourceFile ) ; await importBlocks ( block , this . factory , sourceFile ) ; await exportBlocks ( block , this . factory , sourceFile ) ; globalAttributes ( configuration , root , block , sourceFile ) ; constructBlock ( configuration , root , block , debugIdent ) ; assertForeignGlobalAttribute ( configuration , root , block , debugIdent ) ; extendBlock ( configuration , root , block , debugIdent ) ; implementBlock ( configuration , root , block , debugIdent ) ; composeBlock ( configuration , root , block , debugIdent ) ; processDebugStatements ( root , block , debugIdent , this . config ) ; if ( isDfnFile ) { debug ( " - Process Preset Block Classes" ) ; addPresetSelectors ( configuration , root , block , debugIdent ) ; } debug ( ` - Complete` ) ; return block ; } param root param sourceFile param name param isDfnFile param expectedGuid public parseSync ( root , identifier , name , isDfnFile = false , expectedGuid ? ) { let importer = this . config . importer ; let debugIdent = importer . debugIdentifier ( identifier , this . config ) ; let sourceFile = importer . filesystemPath ( identifier , this . config ) || debugIdent ; let configuration = this . factory . configuration ; if ( ! this . factory . isSync ) { throw new CssBlockError ( "Configuration Error: an async factory was provided for a synchronous method call." ) ; } debug ( `Begin parse: "${ debugIdent }" which ${ isDfnFile ? "is" : "is not" } a definition file.` ) ; let nameDiscoveryError ; try { name = discoverName ( configuration , root , sourceFile , isDfnFile , name ) ; } catch ( e ) { nameDiscoveryError = e ; } let guid ; let guidDiscoveryError ; try { guid = discoverGuid ( configuration , root , sourceFile , isDfnFile , expectedGuid ) || gen_guid ( identifier , configuration . guidAutogenCharacters ) ; } catch ( e ) { guidDiscoveryError = e ; guid = gen_guid ( identifier , configuration . guidAutogenCharacters ) ; } let block = new Block ( name , identifier , guid , root ) ; block . blockAST = builders . root ( ) ; if ( nameDiscoveryError ) { block . addError ( nameDiscoveryError ) ; } if ( guidDiscoveryError ) { block . addError ( guidDiscoveryError ) ; } if ( ! isDfnFile ) { disallowDefinitionRules ( block , configuration , root , sourceFile ) ; } disallowImportant ( configuration , root , block , sourceFile ) ; importBlocksSync ( block , this . factory , sourceFile ) ; exportBlocksSync ( block , this . factory , sourceFile ) ; globalAttributes ( configuration , root , block , sourceFile ) ; constructBlock ( configuration , root , block , debugIdent ) ; assertForeignGlobalAttribute ( configuration , root , block , debugIdent ) ; extendBlock ( configuration , root , block , debugIdent ) ; implementBlock ( configuration , root , block , debugIdent ) ; composeBlock ( configuration , root , block , debugIdent ) ; processDebugStatements ( root , block , debugIdent , this . config ) ; if ( isDfnFile ) { addPresetSelectors ( configuration , root , block , debugIdent ) ; } return block ; } }