Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/angular/cli/commands/add-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {
}
}

const packageManager = await getPackageManager(this.workspace.root);
const packageManager = await getPackageManager(this.context.root);
const usingYarn = packageManager === PackageManager.Yarn;

if (packageIdentifier.type === 'tag' && !packageIdentifier.rawSpec) {
Expand Down Expand Up @@ -210,7 +210,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {

private isPackageInstalled(name: string): boolean {
try {
require.resolve(join(name, 'package.json'), { paths: [this.workspace.root] });
require.resolve(join(name, 'package.json'), { paths: [this.context.root] });

return true;
} catch (e) {
Expand Down Expand Up @@ -254,7 +254,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {
let installedPackage;
try {
installedPackage = require.resolve(join(name, 'package.json'), {
paths: [this.workspace.root],
paths: [this.context.root],
});
} catch {}

Expand All @@ -268,7 +268,7 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {

let projectManifest;
try {
projectManifest = await fetchPackageManifest(this.workspace.root, this.logger);
projectManifest = await fetchPackageManifest(this.context.root, this.logger);
} catch {}

if (projectManifest) {
Expand Down
22 changes: 11 additions & 11 deletions packages/angular/cli/commands/update-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
private packageManager = PackageManager.Npm;

async initialize() {
this.packageManager = await getPackageManager(this.workspace.root);
this.packageManager = await getPackageManager(this.context.root);
this.workflow = new NodeWorkflow(
new virtualFs.ScopedHost(new NodeJsSyncHost(), normalize(this.workspace.root)),
new virtualFs.ScopedHost(new NodeJsSyncHost(), normalize(this.context.root)),
{
packageManager: this.packageManager,
root: normalize(this.workspace.root),
root: normalize(this.context.root),
// __dirname -> favor @schematics/update from this package
// Otherwise, use packages from the active workspace (migrations)
resolvePaths: [__dirname, this.workspace.root],
resolvePaths: [__dirname, this.context.root],
},
);
this.workflow.engineHost.registerOptionsTransform(
Expand Down Expand Up @@ -274,7 +274,7 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
// This works around issues with packages containing migrations that cannot directly depend on the package
// This check can be removed once the schematic runtime handles this situation
try {
require.resolve('@angular-devkit/schematics', { paths: [this.workspace.root] });
require.resolve('@angular-devkit/schematics', { paths: [this.context.root] });
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
this.logger.fatal(
Expand Down Expand Up @@ -386,16 +386,16 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
options.from === undefined &&
packages.length === 1 &&
packages[0].name === '@angular/cli' &&
this.workspace.configFile &&
oldConfigFileNames.includes(this.workspace.configFile)
this.workspace &&
oldConfigFileNames.includes(path.basename(this.workspace.filePath))
) {
options.migrateOnly = true;
options.from = '1.0.0';
}

this.logger.info('Collecting installed dependencies...');

const rootDependencies = await getProjectDependencies(this.workspace.root);
const rootDependencies = await getProjectDependencies(this.context.root);

this.logger.info(`Found ${rootDependencies.size} dependencies.`);

Expand Down Expand Up @@ -441,7 +441,7 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
// Allow running migrations on transitively installed dependencies
// There can technically be nested multiple versions
// TODO: If multiple, this should find all versions and ask which one to use
const packageJson = findPackageJson(this.workspace.root, packageName);
const packageJson = findPackageJson(this.context.root, packageName);
if (packageJson) {
packagePath = path.dirname(packageJson);
packageNode = await readPackageJson(packagePath);
Expand Down Expand Up @@ -679,7 +679,7 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
migration.package,
// Resolve the collection from the workspace root, as otherwise it will be resolved from the temp
// installed CLI version.
require.resolve(migration.collection, { paths: [this.workspace.root] }),
require.resolve(migration.collection, { paths: [this.context.root] }),
new semver.Range('>' + migration.from + ' <=' + migration.to),
options.createCommits,
);
Expand Down Expand Up @@ -754,7 +754,7 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
// Only files inside the workspace root are relevant
for (const entry of result.split('\n')) {
const relativeEntry = path.relative(
path.resolve(this.workspace.root),
path.resolve(this.context.root),
path.resolve(topLevel.trim(), entry.slice(3).trim()),
);

Expand Down
6 changes: 3 additions & 3 deletions packages/angular/cli/commands/version-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class VersionCommand extends Command<VersionCommandSchema> {
const cliPackage: PartialPackageInfo = require('../package.json');
let workspacePackage: PartialPackageInfo | undefined;
try {
workspacePackage = require(path.resolve(this.workspace.root, 'package.json'));
workspacePackage = require(path.resolve(this.context.root, 'package.json'));
} catch {}

const patterns = [
Expand Down Expand Up @@ -144,7 +144,7 @@ export class VersionCommand extends Command<VersionCommandSchema> {

// Try to find the package in the workspace
try {
packagePath = require.resolve(`${moduleName}/package.json`, { paths: [ this.workspace.root ]});
packagePath = require.resolve(`${moduleName}/package.json`, { paths: [ this.context.root ]});
} catch {}

// If not found, try to find within the CLI
Expand All @@ -169,7 +169,7 @@ export class VersionCommand extends Command<VersionCommandSchema> {

private getIvyWorkspace(): string {
try {
const content = fs.readFileSync(path.resolve(this.workspace.root, 'tsconfig.json'), 'utf-8');
const content = fs.readFileSync(path.resolve(this.context.root, 'tsconfig.json'), 'utf-8');
const tsConfig = parseJson(content, JsonParseMode.Loose);
if (!isJsonObject(tsConfig)) {
return '<error>';
Expand Down
19 changes: 13 additions & 6 deletions packages/angular/cli/lib/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { createConsoleLogger } from '@angular-devkit/core/node';
import { format } from 'util';
import { runCommand } from '../../models/command-runner';
import { colors, removeColor } from '../../utilities/color';
import { getWorkspaceRaw } from '../../utilities/config';
import { AngularWorkspace, getWorkspaceRaw } from '../../utilities/config';
import { writeErrorToLogFile } from '../../utilities/log-file';
import { getWorkspaceDetails } from '../../utilities/project';
import { findWorkspaceFile } from '../../utilities/project';

const debugEnv = process.env['NG_DEBUG'];
const isDebug =
Expand Down Expand Up @@ -67,8 +67,9 @@ export default async function(options: { testing?: boolean; cliArgs: string[] })
logger.error(format(...args));
};

let projectDetails = getWorkspaceDetails();
if (projectDetails === null) {
let workspace;
const workspaceFile = findWorkspaceFile();
if (workspaceFile === null) {
const [, localPath] = getWorkspaceRaw('local');
if (localPath !== null) {
logger.fatal(
Expand All @@ -78,12 +79,18 @@ export default async function(options: { testing?: boolean; cliArgs: string[] })

return 1;
}
} else {
try {
workspace = await AngularWorkspace.load(workspaceFile);
} catch (e) {
logger.fatal(`Unable to read workspace file '${workspaceFile}': ${e.message}`);

projectDetails = { root: process.cwd() };
return 1;
}
}

try {
const maybeExitCode = await runCommand(options.cliArgs, logger, projectDetails);
const maybeExitCode = await runCommand(options.cliArgs, logger, workspace);
if (typeof maybeExitCode === 'number') {
console.assert(Number.isInteger(maybeExitCode));

Expand Down
26 changes: 12 additions & 14 deletions packages/angular/cli/models/architect-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
*/
import { Architect, Target } from '@angular-devkit/architect';
import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
import { json, schema, tags, workspaces } from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { json, schema, tags } from '@angular-devkit/core';
import { parseJsonSchemaToOptions } from '../utilities/json-schema';
import { isPackageNameSafeForAnalytics } from './analytics';
import { BaseCommandOptions, Command } from './command';
Expand All @@ -27,7 +26,6 @@ export abstract class ArchitectCommand<
> extends Command<T> {
protected _architect!: Architect;
protected _architectHost!: WorkspaceNodeModulesArchitectHost;
protected _workspace!: workspaces.WorkspaceDefinition;
protected _registry!: json.schema.SchemaRegistry;

// If this command supports running multiple targets.
Expand All @@ -43,13 +41,11 @@ export abstract class ArchitectCommand<
this._registry.addPostTransform(json.schema.transforms.addUndefinedDefaults);
this._registry.useXDeprecatedProvider(msg => this.logger.warn(msg));

const { workspace } = await workspaces.readWorkspace(
this.workspace.root,
workspaces.createWorkspaceHost(new NodeJsSyncHost()),
);
this._workspace = workspace;
if (!this.workspace) {
throw new Error('A workspace is required for an architect command.');
}

this._architectHost = new WorkspaceNodeModulesArchitectHost(workspace, this.workspace.root);
this._architectHost = new WorkspaceNodeModulesArchitectHost(this.workspace, this.workspace.basePath);
this._architect = new Architect(this._architectHost, this._registry);

if (!this.target) {
Expand All @@ -67,13 +63,13 @@ export abstract class ArchitectCommand<
}

let projectName = options.project;
if (projectName && !this._workspace.projects.has(projectName)) {
if (projectName && !this.workspace.projects.has(projectName)) {
throw new Error(`Project '${projectName}' does not exist.`);
}

const commandLeftovers = options['--'];
const targetProjectNames: string[] = [];
for (const [name, project] of this._workspace.projects) {
for (const [name, project] of this.workspace.projects) {
if (project.targets.has(this.target)) {
targetProjectNames.push(name);
}
Expand Down Expand Up @@ -153,7 +149,7 @@ export abstract class ArchitectCommand<
}

if (!projectName && !this.multiTarget) {
const defaultProjectName = this._workspace.extensions['defaultProject'] as string;
const defaultProjectName = this.workspace.extensions['defaultProject'] as string;
if (targetProjectNames.length === 1) {
projectName = targetProjectNames[0];
} else if (defaultProjectName && targetProjectNames.includes(defaultProjectName)) {
Expand Down Expand Up @@ -286,7 +282,8 @@ export abstract class ArchitectCommand<

private getProjectNamesByTarget(targetName: string): string[] {
const allProjectsForTargetName: string[] = [];
for (const [name, project] of this._workspace.projects) {
// tslint:disable-next-line: no-non-null-assertion
for (const [name, project] of this.workspace!.projects) {
if (project.targets.has(targetName)) {
allProjectsForTargetName.push(name);
}
Expand All @@ -298,7 +295,8 @@ export abstract class ArchitectCommand<
} else {
// For single target commands, we try the default project first,
// then the full list if it has a single project, then error out.
const maybeDefaultProject = this._workspace.extensions['defaultProject'] as string;
// tslint:disable-next-line: no-non-null-assertion
const maybeDefaultProject = this.workspace!.extensions['defaultProject'] as string;
if (maybeDefaultProject && allProjectsForTargetName.includes(maybeDefaultProject)) {
return [maybeDefaultProject];
}
Expand Down
16 changes: 11 additions & 5 deletions packages/angular/cli/models/command-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@angular-devkit/core';
import { readFileSync } from 'fs';
import { join, resolve } from 'path';
import { AngularWorkspace } from '../utilities/config';
import { parseJsonSchemaToCommandDescription } from '../utilities/json-schema';
import {
getGlobalAnalytics,
Expand All @@ -26,7 +27,7 @@ import {
promptProjectAnalytics,
} from './analytics';
import { Command } from './command';
import { CommandDescription, CommandWorkspace } from './interface';
import { CommandDescription } from './interface';
import * as parser from './parser';

// NOTE: Update commands.json if changing this. It's still deep imported in one CI validation
Expand Down Expand Up @@ -116,9 +117,9 @@ async function loadCommandDescription(
export async function runCommand(
args: string[],
logger: logging.Logger,
workspace: CommandWorkspace,
workspace: AngularWorkspace | undefined,
commands: CommandMapOptions = standardCommands,
options: { analytics?: analytics.Analytics } = {},
options: { analytics?: analytics.Analytics; currentDirectory: string } = { currentDirectory: process.cwd() },
): Promise<number | void> {
// This registry is exclusively used for flattening schemas, and not for validating.
const registry = new schema.CoreSchemaRegistry([]);
Expand Down Expand Up @@ -233,8 +234,13 @@ export async function runCommand(

const analytics =
options.analytics ||
(await _createAnalytics(!!workspace.configFile, description.name === 'update'));
const context = { workspace, analytics };
(await _createAnalytics(!!workspace, description.name === 'update'));
const context = {
workspace,
analytics,
currentDirectory: options.currentDirectory,
root: workspace?.basePath ?? options.currentDirectory,
};
const command = new description.impl(context, description, logger);

// Flush on an interval (if the event loop is waiting).
Expand Down
19 changes: 7 additions & 12 deletions packages/angular/cli/models/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import { analytics, logging, strings, tags } from '@angular-devkit/core';
import * as path from 'path';
import { colors } from '../utilities/color';
import { getWorkspace } from '../utilities/config';
import { AngularWorkspace } from '../utilities/config';
import {
Arguments,
CommandContext,
CommandDescription,
CommandDescriptionMap,
CommandScope,
CommandWorkspace,
Option,
SubCommandDescription,
} from './interface';
Expand All @@ -26,16 +24,16 @@ export interface BaseCommandOptions {

export abstract class Command<T extends BaseCommandOptions = BaseCommandOptions> {
public allowMissingWorkspace = false;
public workspace: CommandWorkspace;
public analytics: analytics.Analytics;
readonly workspace?: AngularWorkspace;
readonly analytics: analytics.Analytics;

protected static commandMap: () => Promise<CommandDescriptionMap>;
static setCommandMap(map: () => Promise<CommandDescriptionMap>) {
this.commandMap = map;
}

constructor(
context: CommandContext,
protected readonly context: CommandContext,
public readonly description: CommandDescription,
protected readonly logger: logging.Logger,
) {
Expand Down Expand Up @@ -120,19 +118,16 @@ export abstract class Command<T extends BaseCommandOptions = BaseCommandOptions>
async validateScope(scope?: CommandScope): Promise<void> {
switch (scope === undefined ? this.description.scope : scope) {
case CommandScope.OutProject:
if (this.workspace.configFile) {
if (this.workspace) {
this.logger.fatal(tags.oneLine`
The ${this.description.name} command requires to be run outside of a project, but a
project definition was found at "${path.join(
this.workspace.root,
this.workspace.configFile,
)}".
project definition was found at "${this.workspace.filePath}".
`);
throw 1;
}
break;
case CommandScope.InProject:
if (!this.workspace.configFile || (await getWorkspace('local')) === null) {
if (!this.workspace) {
this.logger.fatal(tags.oneLine`
The ${this.description.name} command requires to be run in an Angular project, but a
project definition could not be found.
Expand Down
Loading