import DependencyAnalyzer from "@amiller-gh/glimmer-analyzer" ; import { ResolvedConfiguration } from "@css-blocks/core" ; import { ResolverConfiguration } from "@glimmer/resolver" ; import * as debugGenerator from "debug" ; import * as fs from "fs-extra" ; import * as path from "path" ; import { ResolvedFile } from "./Template" ; const DEBUG = debugGenerator ( "css-blocks:glimmer:resolver" ) ; param base param templatePath param ext return function toClassicPath ( base , templatePath , ext ) { let classic = path . parse ( templatePath . replace ( "templates/" , "styles/" ) ) ; delete classic . base ; classic . ext = `.block.${ ext }` ; return path . join ( base , path . format ( classic ) ) ; } param base param templatePath param ext return function toPodsPath ( base , templatePath , ext ) { let pods = path . parse ( templatePath ) ; delete pods . base ; pods . name = "stylesheet" ; pods . ext = `.block.${ ext }` ; return path . join ( base , path . format ( pods ) ) ; } /**
 * The Glimmer CSS Blocks Resolver currently supports three
 * kinds of project structure modes:
 *  - Component Names as Paths (Module Config)
 *  - Relative Template Paths (Classic Ember)
 *  - Relative Template Paths (Ember Pods)
 *
 * It provides abstractions for interacting with the three project
 * structures, so the rest of the Glimmer analyzer code can operate
 * independently of the filesystem structure.
 *
*/ export class Resolver { private depAnalyzers : Map < string , DependencyAnalyzer > = new Map ( ) ; private moduleConfig ? : ResolverConfiguration ; private fileEndings : Set < string > ; param cssBlocksConfig param moduleConfig constructor ( cssBlocksConfig , moduleConfig ? ) { if ( moduleConfig ) { this . moduleConfig = moduleConfig ; } this . fileEndings = new Set ( [ "css" , ... Object . keys ( cssBlocksConfig . preprocessors ) ] ) ; DEBUG ( `Discovering all Block files that end with ("${ [ ... this . fileEndings ] . join ( `|` ) }")` ) ; } param base return private dependencyAnalyzerFor ( base ) { DEBUG ( "Base directory for dependency analysis is %s" , base ) ; if ( ! this . moduleConfig ) { return undefined ; } if ( this . depAnalyzers . has ( base ) ) { return this . depAnalyzers . get ( base ) ! ; } let src = this . moduleConfig . app && this . moduleConfig . app . mainPath || "src" ; let depAnalyzer = new DependencyAnalyzer ( base , { config : { moduleConfiguration : this . moduleConfig } , paths : { src , } , } ) ; this . depAnalyzers . set ( base , depAnalyzer ) ; return depAnalyzer ; } param base param template return private async tmplPathToStylesheetPath ( base , template ) { let triedPaths = [ ] ; for ( let ext of this . fileEndings ) { let classic = toClassicPath ( base , template , ext ) ; if ( fs . pathExistsSync ( classic ) ) { DEBUG ( `Discovered classic Block for template ${ template }: ${ classic }` ) ; return classic ; } let podsPath = toPodsPath ( base , template , ext ) ; if ( fs . pathExistsSync ( podsPath ) ) { DEBUG ( `Discovered pods Block for template ${ template }: ${ podsPath }` ) ; return podsPath ; } triedPaths . push ( path . relative ( base , classic ) , path . relative ( base , podsPath ) ) ; } DEBUG ( `No Block discovered for template ${ template }. Attempted at:${ triedPaths . join ( `\n  - ` ) }` ) ; return undefined ; } param base param identifier return recursiveDependenciesForTemplate ( base , identifier ) { let depAnalyzer = this . dependencyAnalyzerFor ( base ) ; if ( ! depAnalyzer ) { return [ identifier ] ; } let results = depAnalyzer . recursiveDependenciesForTemplate ( identifier ) . components ; DEBUG ( "Recursively discovered components: %s" , results ) ; return results ; } param base param identifier return async stylesheetFor ( base , identifier ) { let depAnalyzer = this . dependencyAnalyzerFor ( base ) ; if ( ! depAnalyzer ) { let stylesheet = await this . tmplPathToStylesheetPath ( base , identifier ) ; if ( ! stylesheet ) { return undefined ; } return new ResolvedFile ( ( fs . readFileSync ( stylesheet ) ) . toString ( ) , stylesheet , stylesheet ) ; } identifier = depAnalyzer . project . resolver . identify ( `stylesheet:${ identifier }` ) ; if ( ! identifier ) { return undefined ; } let file = depAnalyzer . project . resolver . retrieve ( identifier ) ; if ( ! file ) { return undefined ; } file = path . join ( base , depAnalyzer . project . paths . src , file ) ; if ( ! fs . existsSync ( file ) ) { return undefined ; } let content = ( fs . readFileSync ( file ) ) . toString ( ) ; return new ResolvedFile ( content , identifier , file ) ; } param base param identifier return async templateFor ( base , identifier ) { let depAnalyzer = this . dependencyAnalyzerFor ( base ) ; if ( ! depAnalyzer ) { let template = path . join ( base , identifier ) ; return new ResolvedFile ( ( fs . readFileSync ( template ) ) . toString ( ) , identifier , identifier ) ; } let template = depAnalyzer . project . templateFor ( identifier ) ; return new ResolvedFile ( template . string , template . specifier , template . path ) ; } }