import { Analysis , Block } from "@css-blocks/core" ; import { ObjectDictionary } from "@opticss/util" ; import { NodePath } from "babel-traverse" ; import { ClassDeclaration , Function , ImportDeclaration , VariableDeclaration , VariableDeclarator , isIdentifier , isImportDefaultSpecifier , isImportNamespaceSpecifier , isImportSpecifier , } from "babel-types" ; import * as debugGenerator from "debug" ; import * as fs from "fs" ; import * as path from "path" ; import { JSXTemplate , TEMPLATE_TYPE } from "../../Analyzer/Template" ; import { JSXOptionsReader } from "../../options" ; import { ErrorLocation , TemplateImportError } from "../../utils/Errors" ; import { isBlockFilename } from "../../utils/isBlockFilename" ; import { CSSBlocksJSXAnalyzer } from "../index" ; const debug = debugGenerator ( "css-blocks:jsx" ) ; const DEFAULT_IDENTIFIER = "default" ; const VALID_FILE_EXTENSIONS = { ".jsx" : 1 , ".tsx" : 1 , } ; param name param registry function throwIfRegistered ( name , blockRegistry , loc ) { if ( blockRegistry [ name ] ) { throw new TemplateImportError ( `Block identifier "${ name }" cannot be re-defined in any scope once imported.` , loc ) ; } } param file param analysis param blockFactory export function importVisitor ( file , analyzer , analysis , blockPromises , childTemplatePromises , options ) { let _localBlocks = { } ; let dirname = path . dirname ( file . identifier ) ; let aliases = options . aliases || { } ; return { ImportDeclaration ( nodePath ) { let filePath = nodePath . node . source . value ; let specifiers = nodePath . node . specifiers ; for ( let key in aliases ) { if ( filePath . indexOf ( key ) === 0 ) { filePath = filePath . replace ( key , aliases [ key ] ) ; break ; } } let absoluteFilePath = path . isAbsolute ( filePath ) ? filePath : path . resolve ( dirname , filePath ) ; let parsedPath = path . parse ( absoluteFilePath ) ; delete parsedPath . base ; if ( ! parsedPath . ext ) { for ( let key in VALID_FILE_EXTENSIONS ) { parsedPath . ext = key ; if ( fs . existsSync ( path . format ( parsedPath ) ) ) { break ; } delete parsedPath . ext ; } } absoluteFilePath = path . format ( parsedPath ) ; if ( fs . existsSync ( absoluteFilePath ) && VALID_FILE_EXTENSIONS [ parsedPath . ext ] ) { debug ( `Analyzing discovered dependency: ${ absoluteFilePath }` ) ; childTemplatePromises . push ( analyzer . parseFile ( absoluteFilePath ) ) ; return ; } if ( ! isBlockFilename ( filePath ) ) { return ; } let localName = "" ; let blockPath = path . resolve ( dirname , filePath ) ; specifiers . forEach ( ( specifier ) => { let isNamespace = isImportNamespaceSpecifier ( specifier ) ; if ( isImportDefaultSpecifier ( specifier ) || isNamespace || ( isImportSpecifier ( specifier ) && specifier . imported . name === DEFAULT_IDENTIFIER ) ) { localName = specifier . local . name ; _localBlocks [ localName ] = 1 ; } } ) ; let res = analyzer . blockPromises [ blockPath ] ; if ( ! res ) { res = analyzer . blockFactory . getBlockFromPath ( blockPath ) . catch ( ( err ) => { throw new TemplateImportError ( `Error parsing block import "${ filePath }". Failed with:\n\n"${ err . message }"\n\n` , { filename : file . identifier , line : nodePath . node . loc . start . line , column : nodePath . node . loc . start . column , } ) ; } ) ; analyzer . blockPromises . set ( blockPath , res ) ; } let blockPromise = res . then ( ( block ) : Block => { analysis . addBlock ( localName , block ) ; return block ; } ) ; blockPromises . push ( blockPromise ) ; } , VariableDeclaration ( path ) { path . node . declarations . forEach ( ( decl ) => { if ( ! isIdentifier ( decl . id ) ) { return ; } throwIfRegistered ( decl . id . name , _localBlocks , { filename : analysis . template . identifier , line : path . node . loc . start . line , column : path . node . loc . start . column , } ) ; } ) ; } , ClassDeclaration ( path ) { if ( ! isIdentifier ( path . node . id ) ) { return ; } throwIfRegistered ( path . node . id . name , _localBlocks , { filename : analysis . template . identifier , line : path . node . loc . start . line , column : path . node . loc . start . column , } ) ; } , Function ( path ) { let node = path . node ; if ( isIdentifier ( node . id ) ) { throwIfRegistered ( node . id . name , _localBlocks , { filename : analysis . template . identifier , line : path . node . loc . start . line , column : path . node . loc . start . column , } ) ; } node . params . forEach ( ( param ) => { if ( isIdentifier ( param ) ) { throwIfRegistered ( param . name , _localBlocks , { filename : analysis . template . identifier , line : path . node . loc . start . line , column : path . node . loc . start . column , } ) ; } } ) ; } , } ; }