11import * as fs from 'fs' ;
22import * as path from 'path' ;
3+ import * as jp from 'jsonpath' ;
4+ import * as chalk from 'chalk' ;
35
46const schemaPath = path . resolve ( process . env . CLI_ROOT , 'lib/config/schema.json' ) ;
57const schema = require ( schemaPath ) ;
@@ -29,7 +31,7 @@ export class CliConfig {
2931 if ( path ) {
3032 try {
3133 fs . accessSync ( path ) ;
32- this . _config = require ( path ) ;
34+ this . _config = JSON . parse ( fs . readFileSync ( path , 'utf8' ) ) ;
3335 } catch ( e ) {
3436 throw new Error ( `Config file does not exits.` ) ;
3537 }
@@ -46,116 +48,104 @@ export class CliConfig {
4648 fs . writeFileSync ( path , JSON . stringify ( this . _config , null , 2 ) , { encoding : 'utf-8' } ) ;
4749 }
4850
49- set ( jsonPath : string , value : any , force : boolean = false ) : boolean {
50- let method : any = null ;
51- let splittedPath = jsonPath . split ( '.' ) ;
52- if ( ARRAY_METHODS . indexOf ( splittedPath [ splittedPath . length - 1 ] ) != - 1 ) {
53- method = splittedPath [ splittedPath . length - 1 ] ;
54- splittedPath . splice ( splittedPath . length - 1 , 1 ) ;
55- jsonPath = splittedPath . join ( '.' ) ;
56- }
51+ checkValidSchemaPath ( jsonPath : Object ) : boolean {
52+ const parsed = jp . parse ( jsonPath ) ;
53+ const invalidMsg = `${ jsonPath } does not match schema.` ;
54+ let propertiesPath ;
5755
58- let { parent , name , remaining } = this . _findParent ( jsonPath ) ;
59- let properties : any ;
60- let additionalProperties : boolean ;
56+ parsed . forEach ( ( p , i ) => {
57+ let type = p . expression . type ;
58+ let value = p . expression . value ;
6159
62- const checkPath = jsonPath . split ( '.' ) . reduce ( ( o , i ) => {
63- if ( ! o || ! o . properties ) {
64- throw new Error ( `Invalid config path.` ) ;
60+ if ( i === parsed . length - 1 ) {
61+ return ;
6562 }
66- properties = o . properties ;
67- additionalProperties = o . additionalProperties ;
6863
69- return o . properties [ i ] ;
70- } , schema ) ;
71- const configPath = jsonPath . split ( '.' ) . reduce ( ( o , i ) => o [ i ] , this . _config ) ;
64+ if ( ! i ) {
65+ propertiesPath = `properties.${ value } ` ;
66+ } else {
67+ if ( type === 'numeric_literal' ) {
68+ let prop = propertiesPath . split ( '.' ) . reduce ( ( prev , curr ) => prev [ curr ] , schema ) ;
69+ if ( prop . type !== 'array' ) {
70+ throw new Error ( invalidMsg ) ;
71+ } else {
72+ propertiesPath += `.items` ;
73+ }
74+ } else {
75+ propertiesPath += `.properties.${ value } ` ;
76+ }
77+ }
78+ } ) ;
7279
73- if ( ! properties [ name ] && ! additionalProperties ) {
74- throw new Error ( ` ${ name } is not a known property.` ) ;
80+ if ( ! propertiesPath . split ( '.' ) . reduce ( ( prev , curr ) => prev [ curr ] , schema ) ) {
81+ throw new Error ( invalidMsg ) ;
7582 }
83+ }
7684
77- if ( method ) {
78- if ( Array . isArray ( configPath ) && checkPath . type === 'array' ) {
79- [ ] [ method ] . call ( configPath , value ) ;
80- return true ;
81- } else {
82- throw new Error ( `Trying to use array method on non-array property type.` ) ;
85+ set ( jsonPath : string , value : any , force : boolean = false ) : boolean {
86+ this . _validatePath ( jsonPath ) ;
87+ this . checkValidSchemaPath ( jsonPath ) ;
88+
89+ if ( value . slice ( 0 , 1 ) === '{' && value . slice ( - 1 ) === '}' ) {
90+ try {
91+ value = JSON . parse ( value . replace ( / \' / g, '\"' ) ) ;
92+ } catch ( e ) {
93+ throw new Error ( `Invalid JSON value ${ value } ` ) ;
8394 }
8495 }
8596
86- if ( typeof checkPath . type === 'string' && isNaN ( value ) ) {
87- parent [ name ] = value ;
88- return true ;
97+ if ( typeof value === 'object' ) {
98+ if ( prop . type !== 'object' ) {
99+
100+ }
89101 }
90102
91- if ( typeof checkPath . type === 'number' && ! isNaN ( value ) ) {
92- parent [ name ] = value ;
93- return true ;
103+ let prop = jsonPath . split ( '.' ) . reduce ( ( prev , curr ) => prev [ curr ] , this . _config ) ;
104+
105+ if ( ARRAY_METHODS . indexOf ( path . extname ( jsonPath ) . replace ( '.' , '' ) ) !== - 1 ) {
106+ let method = path . extname ( jsonPath ) ;
107+ let parentPath = jsonPath . replace ( path . extname ( jsonPath ) , '' ) ;
108+
109+ if ( typeof jp . query ( this . _config , `$.${ parentPath } ` ) [ 0 ] === 'string' ) {
110+ throw new Error ( `Cannot use array method on non-array type.` ) ;
111+ } else {
112+ [ ] [ method ] . call ( parent , value ) ;
113+ }
94114 }
95115
96- if ( typeof value != checkPath . type ) {
97- throw new Error ( `Invalid value type. Trying to set ${ typeof value } to ${ path . type } ` ) ;
116+ if ( ! prop ) {
117+ throw new Error ( `Property does not exists. ` ) ;
98118 }
119+
120+ jp . value ( this . _config , `$.${ jsonPath } ` , value ) ;
99121 }
100122
101123 get ( jsonPath : string ) : any {
102- let { parent, name, remaining } = this . _findParent ( jsonPath ) ;
103- if ( remaining || ! ( name in parent ) ) {
124+ let results = jp . query ( this . _config , `$.${ jsonPath } ` ) ;
125+ if ( ! results . length ) {
126+ let ext = path . extname ( jsonPath ) ;
127+ results = jp . query ( this . _config , `$..${ ext } ` ) ;
128+ if ( results . length ) {
129+ console . log ( chalk . yellow ( `
130+ We could not find value on the path you were requested.
131+ Did you mean: ${ results [ 0 ] } ?`
132+ ) ) ;
133+ }
134+
104135 return null ;
105136 } else {
106- return parent [ name ] ;
137+ return results [ 0 ] ;
107138 }
108139 }
109140
110141 private _validatePath ( jsonPath : string ) {
111- if ( ! jsonPath . match ( / ^ (?: [ - _ \w \d ] + (?: \[ \d + \] ) * \. ) * (?: [ - _ \w \d ] + (?: \[ \d + \] ) * ) $ / ) ) {
142+ try {
143+ jp . parse ( jsonPath ) ;
144+ } catch ( e ) {
112145 throw `Invalid JSON path: "${ jsonPath } "` ;
113146 }
114147 }
115148
116- private _findParent ( jsonPath : string ) : { parent : any , name : string | number , remaining ?: string } {
117- this . _validatePath ( jsonPath ) ;
118-
119- let parent : any = null ;
120- let current : any = this . _config ;
121-
122- const splitPath = jsonPath . split ( '.' ) ;
123- let name : string | number = '' ;
124-
125- while ( splitPath . length > 0 ) {
126- const m = splitPath . shift ( ) . match ( / ^ ( .* ?) (?: \[ ( \d + ) \] ) * $ / ) ;
127-
128- name = m [ 1 ] ;
129- const index : string = m [ 2 ] ;
130- parent = current ;
131- current = current [ name ] ;
132-
133- if ( current === null || current === undefined ) {
134- return {
135- parent,
136- name,
137- remaining : ( ! isNaN ( index ) ? `[${ index } ]` : '' ) + splitPath . join ( '.' )
138- } ;
139- }
140-
141- if ( ! isNaN ( index ) ) {
142- name = index ;
143- parent = current ;
144- current = current [ index ] ;
145-
146- if ( current === null || current === undefined ) {
147- return {
148- parent,
149- name,
150- remaining : splitPath . join ( '.' )
151- } ;
152- }
153- }
154- }
155-
156- return { parent, name } ;
157- }
158-
159149 private static _configFilePath ( projectPath ?: string ) : string {
160150 // Find the configuration, either where specified, in the angular-cli project
161151 // (if it's in node_modules) or from the current process.
@@ -166,6 +156,6 @@ export class CliConfig {
166156
167157 public static fromProject ( ) : any {
168158 const configPath = CliConfig . _configFilePath ( ) ;
169- return configPath ? require ( configPath ) : { } ;
159+ return configPath ? JSON . parse ( fs . readFileSync ( configPath , 'utf8' ) ) : { } ;
170160 }
171161}
0 commit comments