Skip to content

Commit 093e4ea

Browse files
hanslBrocco
authored andcommitted
feat(@angular/cli): add the add command
1 parent 88fc93f commit 093e4ea

File tree

15 files changed

+266
-115
lines changed

15 files changed

+266
-115
lines changed

package-lock.json

Lines changed: 43 additions & 111 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@angular/cli/commands/add.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import chalk from 'chalk';
2+
import { Command, CommandScope, Option } from '../models/command';
3+
import { parseOptions } from '../models/command-runner';
4+
import { CliConfig } from '../models/config';
5+
import { SchematicAvailableOptions } from '../tasks/schematic-get-options';
6+
7+
const SilentError = require('silent-error');
8+
9+
10+
export default class AddCommand extends Command {
11+
readonly name = 'add';
12+
readonly description = 'Add support for a library to your project.';
13+
scope = CommandScope.inProject;
14+
arguments = ['collection'];
15+
options: Option[] = [];
16+
17+
private async _parseSchematicOptions(collectionName: string): Promise<any> {
18+
const SchematicGetOptionsTask = require('../tasks/schematic-get-options').default;
19+
20+
const getOptionsTask = new SchematicGetOptionsTask({
21+
ui: this.ui,
22+
project: this.project
23+
});
24+
25+
const availableOptions: SchematicAvailableOptions[] = await getOptionsTask.run({
26+
schematicName: 'ng-add',
27+
collectionName,
28+
});
29+
30+
const options = this.options.concat(availableOptions || []);
31+
32+
return parseOptions(this._rawArgs, options, []);
33+
}
34+
35+
validate(options: any) {
36+
const collectionName = options.collection;
37+
38+
if (!collectionName) {
39+
throw new SilentError(
40+
`The "ng ${this.name}" command requires a name argument to be specified eg. `
41+
+ `${chalk.yellow('ng add [name] ')}. For more details, use "ng help".`
42+
);
43+
}
44+
45+
return true;
46+
}
47+
48+
async run(commandOptions: any) {
49+
const collectionName = commandOptions.collection;
50+
51+
if (!collectionName) {
52+
throw new SilentError(
53+
`The "ng ${this.name}" command requires a name argument to be specified eg. `
54+
+ `${chalk.yellow('ng add [name] ')}. For more details, use "ng help".`
55+
);
56+
}
57+
58+
const packageManager = CliConfig.fromGlobal().get('packageManager');
59+
60+
const NpmInstall = require('../tasks/npm-install').default;
61+
const SchematicRunTask = require('../tasks/schematic-run').default;
62+
63+
const packageName = collectionName.startsWith('@')
64+
? collectionName.split('/', 2).join('/')
65+
: collectionName.split('/', 1)[0];
66+
67+
// We don't actually add the package to package.json, that would be the work of the package
68+
// itself.
69+
let npmInstall = new NpmInstall({
70+
ui: this.ui,
71+
project: this.project,
72+
packageManager,
73+
packageName,
74+
save: false,
75+
});
76+
77+
const schematicRunTask = new SchematicRunTask({
78+
ui: this.ui,
79+
project: this.project
80+
});
81+
82+
await npmInstall.run();
83+
84+
// Reparse the options with the new schematic accessible.
85+
commandOptions = await this._parseSchematicOptions(collectionName);
86+
87+
const runOptions = {
88+
taskOptions: commandOptions,
89+
workingDir: this.project.root,
90+
collectionName,
91+
schematicName: 'ng-add',
92+
allowPrivate: true,
93+
};
94+
95+
await schematicRunTask.run(runOptions);
96+
}
97+
}

packages/@angular/cli/lib/cli/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const UI = require('../../ember-cli/lib/ui');
1313

1414
function loadCommands() {
1515
return {
16+
'add': require('../../commands/add').default,
1617
'build': require('../../commands/build').default,
1718
'serve': require('../../commands/serve').default,
1819
'eject': require('../../commands/eject').default,

packages/@angular/cli/models/command.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export enum CommandScope {
1414
}
1515

1616
export abstract class Command {
17+
protected _rawArgs: string[];
18+
1719
constructor(context: CommandContext, logger: logging.Logger) {
1820
this.logger = logger;
1921
if (context) {
@@ -22,7 +24,8 @@ export abstract class Command {
2224
}
2325
}
2426

25-
async initializeRaw(args: any): Promise<any> {
27+
async initializeRaw(args: string[]): Promise<any> {
28+
this._rawArgs = args;
2629
return args;
2730
}
2831
async initialize(_options: any): Promise<void> {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { ModuleNotFoundException, resolve } from '@angular-devkit/core/node';
2+
3+
const Task = require('../ember-cli/lib/models/task');
4+
import chalk from 'chalk';
5+
import { spawn } from 'child_process';
6+
7+
8+
export default Task.extend({
9+
run: async function () {
10+
const ui = this.ui;
11+
let packageManager = this.packageManager;
12+
if (packageManager === 'default') {
13+
packageManager = 'npm';
14+
}
15+
16+
ui.writeLine(chalk.green(`Installing packages for tooling via ${packageManager}.`));
17+
18+
const installArgs = ['install'];
19+
if (packageManager === 'npm') {
20+
installArgs.push('--quiet');
21+
}
22+
if (this.packageName) {
23+
try {
24+
// Verify if we need to install the package (it might already be there).
25+
// If it's available and we shouldn't save, simply return. Nothing to be done.
26+
resolve(this.packageName, { checkLocal: true, basedir: this.project.root });
27+
28+
if (!this.save) {
29+
return;
30+
}
31+
} catch (e) {
32+
if (!(e instanceof ModuleNotFoundException)) {
33+
throw e;
34+
}
35+
}
36+
installArgs.push(this.packageName);
37+
}
38+
39+
if (!this.save) {
40+
installArgs.push('--no-save');
41+
}
42+
const installOptions = {
43+
stdio: 'inherit',
44+
shell: true
45+
};
46+
47+
await new Promise((resolve, reject) => {
48+
spawn(packageManager, installArgs, installOptions)
49+
.on('close', (code: number) => {
50+
if (code === 0) {
51+
ui.writeLine(chalk.green(`Installed packages for tooling via ${packageManager}.`));
52+
resolve();
53+
} else {
54+
const message = 'Package install failed, see above.';
55+
ui.writeLine(chalk.red(message));
56+
reject(message);
57+
}
58+
});
59+
});
60+
}
61+
});

packages/@angular/cli/tasks/schematic-run.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface SchematicRunOptions {
2828
emptyHost: boolean;
2929
collectionName: string;
3030
schematicName: string;
31+
allowPrivate?: boolean;
3132
}
3233

3334
export interface SchematicOptions {
@@ -73,7 +74,7 @@ export default Task.extend({
7374
);
7475

7576
const collection = getCollection(collectionName);
76-
const schematic = getSchematic(collection, schematicName);
77+
const schematic = getSchematic(collection, schematicName, options.allowPrivate);
7778

7879
const projectRoot = !!this.project ? this.project.root : workingDir;
7980

packages/@angular/cli/utilities/schematics.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function getCollection(collectionName: string): Collection<any, any> {
6363
}
6464

6565
export function getSchematic(collection: Collection<any, any>,
66-
schematicName: string): Schematic<any, any> {
67-
return collection.createSchematic(schematicName);
66+
schematicName: string,
67+
allowPrivate?: boolean): Schematic<any, any> {
68+
return collection.createSchematic(schematicName, allowPrivate);
6869
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@angular-devkit-tests/ng-add-simple",
3+
"schematics": {
4+
"ng-add": {
5+
"factory": "./index.js",
6+
"description": "Create an empty application"
7+
}
8+
}
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports.default = () => tree => tree.create('/ng-add-test', 'hello world');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "@angular-devkit-tests/ng-add-simple",
3+
"version": "1.0.0",
4+
"schematics": "./collection.json"
5+
}

0 commit comments

Comments
 (0)