import { postcss } from "opticss" ; import { BLOCK_EXPORT , CLASS_NAME_IDENT , DEFAULT_EXPORT , RESERVED_BLOCK_NAMES } from "../../BlockSyntax" ; import { Block } from "../../BlockTree" ; import { Configuration } from "../../configuration" ; import * as errors from "../../errors" ; import { sourceRange } from "../../SourceLocation" ; import { allDone } from "../../util" ; import { BlockExport , LocalBlockExport , builders , typeguards } from "../ast" ; import { BlockFactorySync } from "../BlockFactorySync" ; import { BlockFactory } from "../index" ; import { parseBlockNamesAST , stripQuotes } from "../utils" ; const FROM_EXPR = /\s+from\s+/ ; function parseExport ( atRule ) { let exports = atRule . params ; let [ exportList = "" , fromPath = "" ] = exports . split ( FROM_EXPR ) ; fromPath = stripQuotes ( fromPath ) ; if ( ! exportList ) { return null ; } let { defaultName , names } = parseBlockNamesAST ( exportList , ! ! fromPath ) ; names = names || [ ] ; if ( defaultName ) { names . unshift ( { name : DEFAULT_EXPORT , asName : defaultName } ) ; } if ( fromPath ) { return builders . blockExport ( fromPath , names ) ; } else { return builders . localBlockExport ( names ) ; } } param block return export async function exportBlocks ( block , factory , file ) { let root = block . stylesheet ; const exportPromises = [ ] ; const remoteNames = new Set ( ) ; if ( ! root ) { block . addError ( new errors . InvalidBlockSyntax ( `Error finding PostCSS root for block ${ block . name }` ) ) ; } else { block . addBlockExport ( DEFAULT_EXPORT , block ) ; root . walkAtRules ( BLOCK_EXPORT , ( atRule ) => { let exports = parseExport ( atRule ) ; if ( ! exports ) { block . addError ( new errors . InvalidBlockSyntax ( `Malformed block export: \`@export ${ atRule . params }\`` , sourceRange ( factory . configuration , block . stylesheet , file , atRule ) ) ) ; } else { block . blockAST ! . children . push ( exports ) ; let srcBlockPromise = Promise . resolve ( block ) ; if ( typeguards . isBlockExport ( exports ) ) { srcBlockPromise = Promise . resolve ( factory . getBlockRelative ( block . identifier , exports . fromPath ) ) ; } const exportPromise = srcBlockPromise . then ( ( srcBlock ) => { validateAndAddBlockExport ( block , factory . configuration , srcBlock , remoteNames , exports , file , atRule ) ; } , ( error ) => { block . addError ( new errors . CascadingError ( `Error in exported block "${ ( < BlockExport > exports ) . fromPath }"` , error , sourceRange ( factory . configuration , block . stylesheet , file , atRule ) ) ) ; } ) ; exportPromises . push ( exportPromise ) ; } } ) ; } await allDone ( exportPromises ) ; } param block return export function exportBlocksSync ( block , factory , file ) { let root = block . stylesheet ; const remoteNames = new Set ( ) ; if ( ! root ) { block . addError ( new errors . InvalidBlockSyntax ( `Error finding PostCSS root for block ${ block . name }` ) ) ; } else { block . addBlockExport ( DEFAULT_EXPORT , block ) ; root . walkAtRules ( BLOCK_EXPORT , ( atRule ) => { let exports = parseExport ( atRule ) ; if ( ! exports ) { block . addError ( new errors . InvalidBlockSyntax ( `Malformed block export: \`@export ${ atRule . params }\`` , sourceRange ( factory . configuration , block . stylesheet , file , atRule ) ) ) ; } else { block . blockAST ! . children . push ( exports ) ; let srcBlock ; if ( typeguards . isBlockExport ( exports ) ) { try { srcBlock = factory . getBlockRelative ( block . identifier , exports . fromPath ) ; validateAndAddBlockExport ( block , factory . configuration , srcBlock , remoteNames , exports , file , atRule ) ; } catch ( error ) { block . addError ( new errors . CascadingError ( `Error in exported block "${ exports . fromPath }"` , error , sourceRange ( factory . configuration , block . stylesheet , file , atRule ) ) ) ; } } } } ) ; } } function validateAndAddBlockExport ( block , configuration , srcBlock , remoteNames , exports , file , atRule ) { if ( typeguards . isBlockExport ( exports ) ) { block . blockReferencePaths . set ( exports . fromPath , srcBlock ) ; } for ( let { name : localName , asName : remoteName } of exports ?. exports ! ) { remoteName = remoteName || localName ; if ( remoteNames . has ( remoteName ) ) { block . addError ( new errors . InvalidBlockSyntax ( `Cannot have duplicate Block export of same name: "${ remoteName }".` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } else { remoteNames . add ( remoteName ) ; if ( ! CLASS_NAME_IDENT . test ( localName ) ) { block . addError ( new errors . InvalidBlockSyntax ( `Illegal block name in export. "${ localName }" is not a legal CSS identifier.` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } else if ( ! CLASS_NAME_IDENT . test ( remoteName ) ) { block . addError ( new errors . InvalidBlockSyntax ( `Illegal block name in import. "${ remoteName }" is not a legal CSS identifier.` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } else if ( localName === DEFAULT_EXPORT && remoteName === DEFAULT_EXPORT ) { block . addError ( new errors . InvalidBlockSyntax ( `Unnecessary re-export of default Block.` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } else if ( RESERVED_BLOCK_NAMES . has ( remoteName ) ) { block . addError ( new errors . InvalidBlockSyntax ( `Cannot export "${ localName }" as reserved word "${ remoteName }"` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } let referencedBlock = srcBlock . getReferencedBlock ( localName ) ; if ( ! referencedBlock ) { block . addError ( new errors . InvalidBlockSyntax ( `Cannot export Block "${ localName }". No Block named "${ localName }" in "${ file }".` , sourceRange ( configuration , block . stylesheet , file , atRule ) ) ) ; } else { block . addBlockExport ( remoteName , referencedBlock ) ; } } } }