Skip to content

Commit 1ecd72d

Browse files
authored
refactor(config): refactor the config object. (angular#1809)
This also will load angular-cli.json in the HOME directory as a fallback, supports more stuff from the JSON Schema (like default values) than the old one, and actually verify that what you inputs is the right thing. This will be its own NPM package at some point, as other people will probably be interested in having a JSON Schema loader that gives type safety and provides fallbacks and metadata. Closesangular#1763
1 parent 0d914b2 commit 1ecd72d

24 files changed

+1092
-234
lines changed

‎addon/ng2/commands/get.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ const GetCommand = Command.extend({
1212

1313
run: function(commandOptions,rawArgs): Promise<void>{
1414
returnnewPromise(resolve=>{
15-
constvalue=newCliConfig().get(rawArgs[0]);
15+
constconfig=CliConfig.fromProject();
16+
constvalue=config.get(rawArgs[0]);
17+
1618
if(value===null){
1719
console.error(chalk.red('Value cannot be found.'));
1820
}elseif(typeofvalue=='object'){

‎addon/ng2/commands/github-pages-deploy.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ module.exports = Command.extend({
2727
type: String,
2828
default: 'new gh-pages version',
2929
description: 'The commit message to include with the build, must be wrapped in quotes.'
30-
},{
30+
},{
3131
name: 'target',
3232
type: String,
33-
default: 'production',
33+
default: 'production',
3434
aliases: ['t',{'dev': 'development'},{'prod': 'production'}]
35-
},{
35+
},{
3636
name: 'environment',
3737
type: String,
3838
default: '',
@@ -72,12 +72,12 @@ module.exports = Command.extend({
7272
}
7373
if(options.target==='production'){
7474
options.environment='prod';
75-
}
75+
}
7676
}
7777

7878
varprojectName=this.project.pkg.name;
7979

80-
constoutDir=CliConfig.fromProject().apps[0].outDir;
80+
constoutDir=CliConfig.fromProject().config.apps[0].outDir;
8181

8282
letghPagesBranch='gh-pages';
8383
letdestinationBranch=options.userPage ? 'master' : ghPagesBranch;

‎addon/ng2/commands/set.ts‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import*asSilentErrorfrom'silent-error';
12
import*asCommandfrom'ember-cli/lib/models/command';
23
import{CliConfig}from'../models/config';
34

@@ -11,10 +12,38 @@ const SetCommand = Command.extend({
1112
{name: 'global',type: Boolean,default: false,aliases: ['g']},
1213
],
1314

15+
asBoolean: function(raw: string): boolean{
16+
if(raw=='true'||raw=='1'){
17+
returntrue;
18+
}elseif(raw=='false'||raw==''||raw=='0'){
19+
returnfalse;
20+
}else{
21+
thrownewSilentError(`Invalid boolean value: "${raw}"`);
22+
}
23+
},
24+
asNumber: function(raw: string): number{
25+
if(Number.isNaN(+raw)){
26+
thrownewSilentError(`Invalid number value: "${raw}"`);
27+
}
28+
return+raw;
29+
},
30+
1431
run: function(commandOptions,rawArgs): Promise<void>{
1532
returnnewPromise(resolve=>{
16-
constconfig=newCliConfig();
17-
config.set(rawArgs[0],rawArgs[1],commandOptions.force);
33+
const[jsonPath,rawValue]=rawArgs;
34+
constconfig=CliConfig.fromProject();
35+
consttype=config.typeOf(jsonPath);
36+
letvalue: any=rawValue;
37+
38+
switch(type){
39+
case'boolean': value=this.asBoolean(rawValue);break;
40+
case'number': value=this.asNumber(rawValue);break;
41+
case'string': value=rawValue;break;
42+
43+
default: value=JSON.parse(rawValue);
44+
}
45+
46+
config.set(jsonPath,value);
1847
config.save();
1948
resolve();
2049
});

‎addon/ng2/commands/test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import*asTestCommandfrom'ember-cli/lib/commands/test';
2-
import*asconfigfrom'../models/config';
32
import*asTestTaskfrom'../tasks/test';
3+
import{CliConfig}from'../models/config';
44

55
module.exports=TestCommand.extend({
66
availableOptions: [
@@ -14,7 +14,7 @@ module.exports = TestCommand.extend({
1414
],
1515

1616
run: function(commandOptions){
17-
this.project.ngConfig=this.project.ngConfig||config.CliConfig.fromProject();
17+
this.project.ngConfig=this.project.ngConfig||CliConfig.fromProject();
1818

1919
vartestTask=newTestTask({
2020
ui: this.ui,

‎addon/ng2/index.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports ={
77
name: 'ng2',
88

99
config: function(){
10-
this.project.ngConfig=this.project.ngConfig||config.CliConfig.fromProject();
10+
this.project.ngConfig=this.project.ngConfig||config.CliConfig.fromProject().config;
1111
},
1212

1313
includedCommands: function(){

‎addon/ng2/models/config.ts‎

Lines changed: 21 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import{CliConfigasCliConfigBase}from'./config/config';
2+
import{CliConfigasConfigInterface}from'../../../lib/config/schema';
3+
import*aschalkfrom'chalk';
14
import*asfsfrom'fs';
25
import*aspathfrom'path';
3-
import*aschalkfrom'chalk';
46

57
constschemaPath=path.resolve(process.env.CLI_ROOT,'lib/config/schema.json');
68
constschema=require(schemaPath);
79

810
exportconstCLI_CONFIG_FILE_NAME='angular-cli.json';
9-
exportconstARRAY_METHODS=['push','splice','sort','reverse','pop','shift'];
11+
1012

1113
function_findUp(name: string,from: string){
1214
letcurrentDir=from;
@@ -16,175 +18,46 @@ function _findUp(name: string, from: string){
1618
returnp;
1719
}
1820

19-
currentDir=path.resolve(currentDir,'..');
21+
currentDir=path.dirname(currentDir);
2022
}
2123

2224
returnnull;
2325
}
2426

2527

26-
exportclassCliConfig{
27-
private_config: any;
28-
29-
constructor(path?: string){
30-
if(path){
31-
try{
32-
fs.accessSync(path);
33-
this._config=require(path);
34-
}catch(e){
35-
thrownewError(`Config file does not exits.`);
36-
}
37-
}else{
38-
this._config=CliConfig.fromProject();
39-
}
40-
}
41-
42-
save(path: string=CliConfig._configFilePath()){
43-
if(!path){
44-
thrownewError('Could not find config path.');
45-
}
46-
47-
fs.writeFileSync(path,JSON.stringify(this._config,null,2),{encoding: 'utf-8'});
48-
}
49-
50-
set(jsonPath: string,value: any,force: boolean=false): boolean{
51-
letmethod: any=null;
52-
letsplittedPath=jsonPath.split('.');
53-
if(ARRAY_METHODS.indexOf(splittedPath[splittedPath.length-1])!=-1){
54-
method=splittedPath[splittedPath.length-1];
55-
splittedPath.splice(splittedPath.length-1,1);
56-
jsonPath=splittedPath.join('.');
57-
}
58-
59-
let{ parent, name, remaining }=this._findParent(jsonPath);
60-
letproperties: any;
61-
letadditionalProperties: boolean;
62-
63-
constcheckPath=jsonPath.split('.').reduce((o,i)=>{
64-
if(!o||!o.properties){
65-
thrownewError(`Invalid config path.`);
66-
}
67-
properties=o.properties;
68-
additionalProperties=o.additionalProperties;
69-
70-
returno.properties[i];
71-
},schema);
72-
constconfigPath=jsonPath.split('.').reduce((o,i)=>o[i],this._config);
73-
74-
if(!properties[name]&&!additionalProperties){
75-
thrownewError(`${name} is not a known property.`);
76-
}
77-
78-
if(method){
79-
if(Array.isArray(configPath)&&checkPath.type==='array'){
80-
[][method].call(configPath,value);
81-
returntrue;
82-
}else{
83-
thrownewError(`Trying to use array method on non-array property type.`);
84-
}
85-
}
86-
87-
if(typeofcheckPath.type==='string'&&isNaN(value)){
88-
parent[name]=value;
89-
returntrue;
90-
}
91-
92-
if(typeofcheckPath.type==='number'&&!isNaN(value)){
93-
parent[name]=value;
94-
returntrue;
95-
}
96-
97-
if(typeofvalue!=checkPath.type){
98-
thrownewError(`Invalid value type. Trying to set ${typeofvalue} to ${path.type}`);
99-
}
100-
}
101-
102-
get(jsonPath: string): any{
103-
let{ parent, name, remaining }=this._findParent(jsonPath);
104-
if(remaining||!(nameinparent)){
105-
returnnull;
106-
}else{
107-
returnparent[name];
108-
}
109-
}
110-
111-
private_validatePath(jsonPath: string){
112-
if(!jsonPath.match(/^(?:[-_\w\d]+(?:\[\d+\])*\.)*(?:[-_\w\d]+(?:\[\d+\])*)$/)){
113-
throw`Invalid JSON path: "${jsonPath}"`;
114-
}
115-
}
116-
117-
private_findParent(jsonPath: string): {parent: any,name: string|number,remaining?: string}{
118-
this._validatePath(jsonPath);
119-
120-
letparent: any=null;
121-
letcurrent: any=this._config;
122-
123-
constsplitPath=jsonPath.split('.');
124-
letname: string|number='';
125-
126-
while(splitPath.length>0){
127-
constm=splitPath.shift().match(/^(.*?)(?:\[(\d+)\])*$/);
128-
129-
name=m[1];
130-
constindex: string=m[2];
131-
parent=current;
132-
current=current[name];
133-
134-
if(current===null||current===undefined){
135-
return{
136-
parent,
137-
name,
138-
remaining: (!isNaN(index) ? `[${index}]` : '')+splitPath.join('.')
139-
};
140-
}
141-
142-
if(!isNaN(index)){
143-
name=index;
144-
parent=current;
145-
current=current[index];
146-
147-
if(current===null||current===undefined){
148-
return{
149-
parent,
150-
name,
151-
remaining: splitPath.join('.')
152-
};
153-
}
154-
}
155-
}
28+
functiongetUserHome(){
29+
returnprocess.env[(process.platform=='win32') ? 'USERPROFILE' : 'HOME'];
30+
}
15631

157-
return{ parent, name };
158-
}
15932

33+
exportclassCliConfigextendsCliConfigBase<ConfigInterface>{
16034
privatestatic_configFilePath(projectPath?: string): string{
16135
// Find the configuration, either where specified, in the angular-cli project
16236
// (if it's in node_modules) or from the current process.
16337
return(projectPath&&_findUp(CLI_CONFIG_FILE_NAME,projectPath))
164-
||_findUp(CLI_CONFIG_FILE_NAME,__dirname)
165-
||_findUp(CLI_CONFIG_FILE_NAME,process.cwd());
38+
||_findUp(CLI_CONFIG_FILE_NAME,process.cwd())
39+
||_findUp(CLI_CONFIG_FILE_NAME,__dirname);
16640
}
16741

168-
publicstaticfromProject(): any{
169-
constconfigPath=CliConfig._configFilePath();
42+
43+
staticfromProject(): CliConfig{
44+
constconfigPath=this._configFilePath();
45+
constglobalConfigPath=path.join(getUserHome(),CLI_CONFIG_FILE_NAME);
17046

17147
if(!configPath){
17248
return{};
17349
}
17450

175-
letconfig=require(configPath);
176-
177-
if(config.defaults.sourceDir||config.defaults.prefix){
178-
config.apps[0].root=config.apps[0].root||config.defaults.sourceDir;
179-
config.apps[0].prefix=config.apps[0].prefix||config.defaults.prefix;
180-
51+
constcliConfig=CliConfigBase.fromConfigPath(CliConfig._configFilePath(),[globalConfigPath]);
52+
if(cliConfig.alias('apps.0.root','defaults.sourceDir')
53+
+cliConfig.alias('apps.0.prefix','defaults.prefix')){
18154
console.error(chalk.yellow(
182-
'The "defaults.prefix" and "defaults.sourceDir" properties of angular-cli.json'
55+
'The "defaults.prefix" and "defaults.sourceDir" properties of angular-cli.json\n'
18356
+'are deprecated in favor of "apps[0].root" and "apps[0].prefix".\n'
18457
+'Please update in order to avoid errors in future versions of angular-cli.'
18558
));
18659
}
187-
188-
returnconfig;
60+
61+
returncliConfigasCliConfig;
18962
}
19063
}

0 commit comments

Comments
(0)