Skip to content

Commit 0633551

Browse files
clydinfilipesilva
authored andcommitted
fix(@angular/cli): avoid exceptions for expected errors in architect commands
Errors caused by invalid options or workspace configuration will now be presented as fatal console messages and the CLI will exit gracefully with an exit code of 1.
1 parent af1cc2d commit 0633551

File tree

3 files changed

+29
-20
lines changed

3 files changed

+29
-20
lines changed

packages/angular/cli/commands/deploy-impl.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@ export class DeployCommand extends ArchitectCommand<DeployCommandSchema> {
2828
public readonly target = 'deploy';
2929
public readonly missingTargetError = BuilderMissing;
3030

31-
public async run(options: ArchitectCommandOptions & Arguments) {
32-
return this.runArchitectTarget(options);
33-
}
34-
35-
public async initialize(options: DeployCommandSchema & Arguments): Promise<void> {
31+
public async initialize(options: DeployCommandSchema & Arguments): Promise<number | void> {
3632
if (!options.help) {
3733
return super.initialize(options);
3834
}

packages/angular/cli/models/architect-command.ts

+22-10
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ export abstract class ArchitectCommand<
3535
target: string | undefined;
3636
missingTargetError: string | undefined;
3737

38-
public async initialize(options: T & Arguments): Promise<void> {
39-
await super.initialize(options);
40-
38+
public async initialize(options: T & Arguments): Promise<number | void> {
4139
this._registry = new json.schema.CoreSchemaRegistry();
4240
this._registry.addPostTransform(json.schema.transforms.addUndefinedDefaults);
4341
this._registry.useXDeprecatedProvider(msg => this.logger.warn(msg));
4442

4543
if (!this.workspace) {
46-
throw new Error('A workspace is required for an architect command.');
44+
this.logger.fatal('A workspace is required for this command.');
45+
46+
return 1;
4747
}
4848

4949
this._architectHost = new WorkspaceNodeModulesArchitectHost(this.workspace, this.workspace.basePath);
@@ -57,15 +57,19 @@ export abstract class ArchitectCommand<
5757

5858
const specifier = this._makeTargetSpecifier(options);
5959
if (!specifier.project || !specifier.target) {
60-
throw new Error('Cannot determine project or target for command.');
60+
this.logger.fatal('Cannot determine project or target for command.');
61+
62+
return 1;
6163
}
6264

6365
return;
6466
}
6567

6668
let projectName = options.project;
6769
if (projectName && !this.workspace.projects.has(projectName)) {
68-
throw new Error(`Project '${projectName}' does not exist.`);
70+
this.logger.fatal(`Project '${projectName}' does not exist.`);
71+
72+
return 1;
6973
}
7074

7175
const commandLeftovers = options['--'];
@@ -77,12 +81,16 @@ export abstract class ArchitectCommand<
7781
}
7882

7983
if (targetProjectNames.length === 0) {
80-
throw new Error(this.missingTargetError || `No projects support the '${this.target}' target.`);
84+
this.logger.fatal(this.missingTargetError || `No projects support the '${this.target}' target.`);
85+
86+
return 1;
8187
}
8288

8389
if (projectName && !targetProjectNames.includes(projectName)) {
84-
throw new Error(this.missingTargetError ||
90+
this.logger.fatal(this.missingTargetError ||
8591
`Project '${projectName}' does not support the '${this.target}' target.`);
92+
93+
return 1;
8694
}
8795

8896
if (!projectName && commandLeftovers && commandLeftovers.length > 0) {
@@ -141,11 +149,13 @@ export abstract class ArchitectCommand<
141149
}
142150

143151
if (!projectName && this.multiTarget && builderNames.size > 1) {
144-
throw new Error(tags.oneLine`
152+
this.logger.fatal(tags.oneLine`
145153
Architect commands with command line overrides cannot target different builders. The
146154
'${this.target}' target would run on projects ${targetProjectNames.join()} which have the
147155
following builders: ${'\n ' + [...builderNames].join('\n ')}
148156
`);
157+
158+
return 1;
149159
}
150160
}
151161

@@ -159,7 +169,9 @@ export abstract class ArchitectCommand<
159169
// This is a special case where we just return.
160170
return;
161171
} else {
162-
throw new Error(this.missingTargetError || 'Cannot determine project or target for command.');
172+
this.logger.fatal(this.missingTargetError || 'Cannot determine project or target for command.');
173+
174+
return 1;
163175
}
164176
}
165177

packages/angular/cli/models/command.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ export abstract class Command<T extends BaseCommandOptions = BaseCommandOptions>
4242
this.analytics = context.analytics || new analytics.NoopAnalytics();
4343
}
4444

45-
async initialize(options: T & Arguments): Promise<void> {
46-
return;
47-
}
45+
async initialize(options: T & Arguments): Promise<number | void> {}
4846

4947
async printHelp(): Promise<number> {
5048
await this.printHelpUsage();
@@ -169,7 +167,10 @@ export abstract class Command<T extends BaseCommandOptions = BaseCommandOptions>
169167
if (!(options.help === true || options.help === 'json' || options.help === 'JSON')) {
170168
await this.validateScope();
171169
}
172-
await this.initialize(options);
170+
let result = await this.initialize(options);
171+
if (typeof result === 'number' && result !== 0) {
172+
return result;
173+
}
173174

174175
if (options.help === true) {
175176
return this.printHelp();
@@ -180,7 +181,7 @@ export abstract class Command<T extends BaseCommandOptions = BaseCommandOptions>
180181
if (this.useReportAnalytics) {
181182
await this.reportAnalytics([this.description.name], options);
182183
}
183-
const result = await this.run(options);
184+
result = await this.run(options);
184185
const endTime = +new Date();
185186

186187
this.analytics.timing(this.description.name, 'duration', endTime - startTime);

0 commit comments

Comments
 (0)