11import { Config } from 'codify-schemas' ;
2- import * as fs from 'node:fs/promises' ;
3- import path from 'node:path' ;
4- import { validate } from 'uuid'
52
63import { InternalError } from '../common/errors.js' ;
74import { ConfigBlock } from '../entities/config.js' ;
85import { Project } from '../entities/project.js' ;
9- import { FileUtils } from '../utils/file.js' ;
10- import { CloudParser } from './cloud/cloud-parser.js' ;
116import { ConfigFactory } from './config-factory.js' ;
127import { FileType , InMemoryFile , ParsedConfig } from './entities.js' ;
8+ import { MultipleFilesError , NoCodifyFileError } from './errors.js' ;
139import { JsonParser } from './json/json-parser.js' ;
1410import { Json5Parser } from './json5/json-parser.js' ;
1511import { JsoncParser } from './jsonc/json-parser.js' ;
16- import { CloudReader } from './reader/cloud-reader .js' ;
17- import { FileReader } from './reader/file-reader .js' ;
12+ import { RemoteParser } from './remote/remote-parser .js' ;
13+ import { CodifyResolver , ResolverResult , ResolverType } from './resolvers .js' ;
1814import { SourceMapCache } from './source-maps.js' ;
1915import { YamlParser } from './yaml/yaml-parser.js' ;
20- import { MultipleFilesError } from './errors.js' ;
21- import { CodifyResolver , ResolverType } from './resolvers.js' ;
2216
2317export const CODIFY_FILE_REGEX = / ^ ( .* ) ? c o d i f y ( .* ) ? ( .j s o n | .y a m l | .j s o n 5 | .j s o n c ) $ / ;
2418
@@ -28,11 +22,17 @@ export interface ParserArgs {
2822 path ?: string ;
2923 transformProject ?: ( project : Project ) => Project | Promise < Project > ;
3024 rawConfigs ?: Config [ ] ; // Raw configs are provided directly
25+ resolverType ?: ResolverType ;
3126}
3227
33- interface FilePointer {
34- location : string ;
35- type : FileType ;
28+ interface ParseResult {
29+ configs : ParsedConfig [ ] ;
30+ file : InMemoryFile ;
31+ }
32+
33+ interface ConfigResult {
34+ configs : ConfigBlock [ ] ;
35+ file : InMemoryFile ;
3636}
3737
3838class Parser {
@@ -41,7 +41,7 @@ class Parser {
4141 [ FileType . YAML ] : new YamlParser ( ) ,
4242 [ FileType . JSON5 ] : new Json5Parser ( ) ,
4343 [ FileType . JSONC ] : new JsoncParser ( ) ,
44- [ FileType . REMOTE ] : new CloudParser ( ) ,
44+ [ FileType . REMOTE ] : new RemoteParser ( ) ,
4545 }
4646
4747 /**
@@ -57,16 +57,15 @@ class Parser {
5757 * @param location
5858 * @param args
5959 */
60- async parse ( location : string , args ?: ParserArgs ) : Promise < Project > {
60+ async parse ( location : string , args ?: ParserArgs , isLoggedIn = false ) : Promise < Project > {
6161 const sourceMaps = new SourceMapCache ( )
6262
63- const configs = this . resolveFiles ( args )
64- . then ( ( result ) => this . throwIfMultipleFiles ( result ) )
65- . then ( ( path ) => this . readFiles ( codifyFiles ) )
63+ const { configs, file } = await this . resolveFiles ( location , args , isLoggedIn )
64+ . then ( ( result ) => this . validateResolver ( result ) )
6665 . then ( ( files ) => this . parseContents ( files , sourceMaps ) )
6766 . then ( ( config ) => this . createConfigBlocks ( config , sourceMaps ) )
6867
69- return Project . create ( configs , codifyFiles [ 0 ] , sourceMaps ) ;
68+ return Project . create ( configs , file . path , sourceMaps ) ;
7069 }
7170
7271 async parseJson ( configs : Config [ ] ) : Promise < Project > {
@@ -77,83 +76,51 @@ class Parser {
7776 sourceMaps
7877 )
7978
80- return Project . create ( configBlocks , undefined , sourceMaps ) ;
79+ return Project . create ( configBlocks . configs , undefined , sourceMaps ) ;
8180 }
8281
83- private async getFilePaths ( dirOrFile : string ) : Promise < string [ ] > {
84- // A cloud file is represented as an uuid. Skip file checks if it's a cloud file;
85- if ( validate ( dirOrFile ) ) {
86- return [ dirOrFile ] ;
82+ private async resolveFiles ( location : string , args ?: ParserArgs , isLoggedIn = false ) : Promise < ResolverResult > {
83+ if ( args ?. resolverType ) {
84+ return CodifyResolver . runResolver ( location , args . resolverType ) ;
8785 }
8886
89- const absolutePath = path . resolve ( dirOrFile ) ;
90- const isDirectory = ( await fs . lstat ( absolutePath ) ) . isDirectory ( ) ;
91-
92- // A single file was passed in. We need to test if the file satisfies the codify file regex
93- if ( ! isDirectory ) {
94- const fileName = path . basename ( absolutePath ) ;
95- if ( ! CODIFY_FILE_REGEX . test ( fileName ) ) {
96- throw new Error ( `Invalid file path provided ${ absolutePath } ${ fileName } . Expected the file to be *.codify.jsonc, *.codify.json5, *.codify.json, or *.codify.yaml ` )
97- }
98-
99- return [ absolutePath ] ;
87+ if ( args ?. path ) {
88+ return CodifyResolver . resolveLocal ( args ?. path )
10089 }
10190
102- const filesInDir = await fs . readdir ( absolutePath ) ;
103-
104- return filesInDir
105- . filter ( ( name ) => CODIFY_FILE_REGEX . test ( name ) )
106- . map ( ( name ) => path . join ( absolutePath , name ) )
107- }
108-
109- private async resolveFiles ( location : string , args ?: ParserArgs , isLoggedIn = false ) : Promise < string [ ] > {
110- return CodifyResolver . runUntilResolves ( location , [
111- ( args ?. path ) ? ResolverType . EXPLICIT_PATH : null ,
112- ResolverType . FILE_OR_DIRECTORY ,
91+ return CodifyResolver . run ( location , [
92+ ResolverType . LOCAL ,
11393 ( isLoggedIn ) ? ResolverType . REMOTE_DOCUMENT_ID : null ,
114- ( isLoggedIn ) ? ResolverType . REMOTE_FILE : null ,
115- ResolverType . TEMPLATE ,
94+ ( isLoggedIn ) ? ResolverType . REMOTE_DOCUMENT : null ,
95+ ( args ?. allowTemplates ) ? ResolverType . TEMPLATE : null ,
11696 ] ) ;
117-
11897 }
11998
120- private async throwIfMultipleFiles ( result : string [ ] ) : Promise < string > {
121- if ( result . length > 1 ) {
122- throw new MultipleFilesError ( result ) ;
99+ private async validateResolver ( result : ResolverResult ) : Promise < InMemoryFile > {
100+ if ( result . files . length === 0 ) {
101+ throw new NoCodifyFileError ( result ) ;
123102 }
124103
125- return result [ 0 ] ;
126- }
127-
128- private readFiles ( filePaths : string [ ] ) : Promise < InMemoryFile [ ] > {
129- const cloudReader = new CloudReader ( ) ;
130- const fileReader = new FileReader ( ) ;
131-
132- return Promise . all ( filePaths . map (
133- async ( p ) => {
134- // If path is a uuid and doesn't exist as a file, it's a cloud file
135- if ( validate ( p ) && ! ( await FileUtils . fileExists ( p ) ) ) {
136- return cloudReader . read ( p )
137- }
104+ if ( result . files . length > 1 ) {
105+ throw new MultipleFilesError ( result ) ;
106+ }
138107
139- return fileReader . read ( p )
140- }
141- ) )
108+ return result . files [ 0 ] ;
142109 }
143110
144- private parseContents ( files : InMemoryFile [ ] , sourceMaps : SourceMapCache ) : ParsedConfig [ ] {
145- return files . flatMap ( ( file ) => {
146- const parser = this . languageSpecificParsers [ file . fileType ] ;
147- if ( ! parser ) {
148- throw new InternalError ( `Unable to find a language specific parser for type ${ file . fileType } for file ${ file . path } ` )
149- }
111+ private parseContents ( file : InMemoryFile , sourceMaps : SourceMapCache ) : ParseResult {
112+ const parser = this . languageSpecificParsers [ file . fileType ] ;
113+ if ( ! parser ) {
114+ throw new InternalError ( `Unable to find a language specific parser for type ${ file . fileType } for file ${ file . path } ` )
115+ }
150116
151- return parser . parse ( file , sourceMaps ) ;
152- } ) ;
117+ const configs = parser . parse ( file , sourceMaps ) ;
118+ return { configs , file } ;
153119 }
154120
155- private createConfigBlocks ( parsedConfig : ParsedConfig [ ] , sourceMaps : SourceMapCache ) : ConfigBlock [ ] {
156- return parsedConfig . map ( ( config ) => ConfigFactory . create ( config , sourceMaps ) )
121+ private createConfigBlocks ( parseResult : ParseResult , sourceMaps : SourceMapCache ) : ConfigResult {
122+ const configs = parseResult . configs . map ( ( config ) => ConfigFactory . create ( config , sourceMaps ) ) ;
123+ return { configs, file : parseResult . file } ;
157124 }
158125}
159126
0 commit comments