1
1
import * as fs from 'fs' ;
2
2
import * as path from 'path' ;
3
+ import * as jp from 'jsonpath' ;
4
+ import * as chalk from 'chalk' ;
3
5
4
6
const schemaPath = path . resolve ( process . env . CLI_ROOT , 'lib/config/schema.json' ) ;
5
7
const schema = require ( schemaPath ) ;
@@ -29,7 +31,7 @@ export class CliConfig {
29
31
if ( path ) {
30
32
try {
31
33
fs . accessSync ( path ) ;
32
- this . _config = require ( path ) ;
34
+ this . _config = JSON . parse ( fs . readFileSync ( path , 'utf8' ) ) ;
33
35
} catch ( e ) {
34
36
throw new Error ( `Config file does not exits.` ) ;
35
37
}
@@ -46,116 +48,104 @@ export class CliConfig {
46
48
fs . writeFileSync ( path , JSON . stringify ( this . _config , null , 2 ) , { encoding : 'utf-8' } ) ;
47
49
}
48
50
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 ;
57
55
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 ;
61
59
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 ;
65
62
}
66
- properties = o . properties ;
67
- additionalProperties = o . additionalProperties ;
68
63
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
+ } ) ;
72
79
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 ) ;
75
82
}
83
+ }
76
84
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 } ` ) ;
83
94
}
84
95
}
85
96
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
+ }
89
101
}
90
102
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
+ }
94
114
}
95
115
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. ` ) ;
98
118
}
119
+
120
+ jp . value ( this . _config , `$.${ jsonPath } ` , value ) ;
99
121
}
100
122
101
123
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
+
104
135
return null ;
105
136
} else {
106
- return parent [ name ] ;
137
+ return results [ 0 ] ;
107
138
}
108
139
}
109
140
110
141
private _validatePath ( jsonPath : string ) {
111
- if ( ! jsonPath . match ( / ^ (?: [ - _ \w \d ] + (?: \[ \d + \] ) * \. ) * (?: [ - _ \w \d ] + (?: \[ \d + \] ) * ) $ / ) ) {
142
+ try {
143
+ jp . parse ( jsonPath ) ;
144
+ } catch ( e ) {
112
145
throw `Invalid JSON path: "${ jsonPath } "` ;
113
146
}
114
147
}
115
148
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
-
159
149
private static _configFilePath ( projectPath ?: string ) : string {
160
150
// Find the configuration, either where specified, in the angular-cli project
161
151
// (if it's in node_modules) or from the current process.
@@ -166,6 +156,6 @@ export class CliConfig {
166
156
167
157
public static fromProject ( ) : any {
168
158
const configPath = CliConfig . _configFilePath ( ) ;
169
- return configPath ? require ( configPath ) : { } ;
159
+ return configPath ? JSON . parse ( fs . readFileSync ( configPath , 'utf8' ) ) : { } ;
170
160
}
171
161
}
0 commit comments