cleanup(core): extract "core" folder from "command-line"
This commit is contained in:
parent
b794aa68a9
commit
8749c18f91
@ -4,8 +4,8 @@ import { callRule, runSchematic } from '../utils/testing';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { updateJsonInTree } from '@nrwl/workspace/src/utils/ast-utils';
|
||||
import { updateWorkspace } from '@nrwl/workspace/src/utils/workspace';
|
||||
import { NxJson } from '@nrwl/workspace/src/command-line/shared';
|
||||
import { rulesNodeJSSha, rulesNodeJSVersion } from '../utils/versions';
|
||||
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
|
||||
|
||||
describe('@nrwl/bazel:sync', () => {
|
||||
let tree: Tree;
|
||||
|
||||
@ -21,7 +21,7 @@ import { join, normalize } from '@angular-devkit/core';
|
||||
import {
|
||||
ProjectGraph,
|
||||
ProjectGraphNode
|
||||
} from '@nrwl/workspace/src/command-line/project-graph';
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import { rulesNodeJSSha, rulesNodeJSVersion } from '../utils/versions';
|
||||
import { TargetDefinition } from '@angular-devkit/core/src/workspace';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// we can import from '@nrwl/workspace' because it will require typescript
|
||||
import { output } from '@nrwl/workspace/src/command-line/output';
|
||||
import { output } from '@nrwl/workspace/src/utils/output';
|
||||
import { execSync } from 'child_process';
|
||||
import { writeFileSync } from 'fs';
|
||||
import * as inquirer from 'inquirer';
|
||||
|
||||
@ -1,8 +1,3 @@
|
||||
import {
|
||||
readWorkspaceJson,
|
||||
readNxJson,
|
||||
normalizedProjectRoot
|
||||
} from '@nrwl/workspace/src/command-line/shared';
|
||||
import { appRootPath } from '@nrwl/workspace/src/utils/app-root';
|
||||
import {
|
||||
DepConstraint,
|
||||
@ -24,7 +19,12 @@ import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectType
|
||||
} from '@nrwl/workspace/src/command-line/project-graph';
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import {
|
||||
normalizedProjectRoot,
|
||||
readNxJson,
|
||||
readWorkspaceJson
|
||||
} from '@nrwl/workspace/src/core/file-utils';
|
||||
|
||||
type Options = [
|
||||
{
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
DependencyType,
|
||||
ProjectGraph,
|
||||
ProjectType
|
||||
} from '@nrwl/workspace/src/command-line/project-graph';
|
||||
} from '@nrwl/workspace/src/core/project-graph';
|
||||
import { extname } from 'path';
|
||||
|
||||
describe('Enforce Module Boundaries', () => {
|
||||
|
||||
@ -10,8 +10,8 @@ import {
|
||||
DefaultTasksRunnerOptions
|
||||
} from '@nrwl/workspace/src/tasks-runner/default-tasks-runner';
|
||||
import * as fs from 'fs';
|
||||
import { TasksMap } from '@nrwl/workspace/src/command-line/run-tasks/run-command';
|
||||
import { ProjectGraph } from '@nrwl/workspace/src/command-line/project-graph';
|
||||
import { TasksMap } from '@nrwl/workspace/src/tasks-runner/run-command';
|
||||
import { ProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||
const axios = require('axios');
|
||||
|
||||
interface InsightsTaskRunnerOptions extends DefaultTasksRunnerOptions {
|
||||
|
||||
@ -19,16 +19,13 @@ export {
|
||||
ExistingPrettierConfig,
|
||||
resolveUserExistingPrettierConfig
|
||||
} from './src/utils/common';
|
||||
export { output } from './src/command-line/output';
|
||||
export { output } from './src/utils/output';
|
||||
export {
|
||||
commandsObject,
|
||||
supportedNxCommands
|
||||
} from './src/command-line/nx-commands';
|
||||
export {
|
||||
readWorkspaceJson,
|
||||
readNxJson,
|
||||
NxJson
|
||||
} from './src/command-line/shared';
|
||||
export { readWorkspaceJson, readNxJson } from './src/core/file-utils';
|
||||
export { NxJson } from './src/core/shared-interfaces';
|
||||
export {
|
||||
readJsonInTree,
|
||||
updateJsonInTree,
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
import { JsonObject } from '@angular-devkit/core';
|
||||
import { exec } from 'child_process';
|
||||
import { Observable } from 'rxjs';
|
||||
import { TEN_MEGABYTES } from '../../command-line/shared';
|
||||
import { TEN_MEGABYTES } from '@nrwl/workspace/src/core/file-utils';
|
||||
|
||||
try {
|
||||
require('dotenv').config();
|
||||
|
||||
145
packages/workspace/src/command-line/affected.ts
Normal file
145
packages/workspace/src/command-line/affected.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import * as yargs from 'yargs';
|
||||
import { generateGraph } from './dep-graph';
|
||||
import { output } from '../utils/output';
|
||||
import { parseFiles, printArgsWarning } from './shared';
|
||||
import { runCommand } from '../tasks-runner/run-command';
|
||||
import { NxArgs, splitArgsIntoNxArgsAndTargetArgs } from './utils';
|
||||
import { filterAffected } from '../core/affected-project-graph';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraphNode,
|
||||
ProjectType,
|
||||
withDeps
|
||||
} from '../core/project-graph';
|
||||
import { calculateFileChanges, readEnvironment } from '../core/file-utils';
|
||||
import { printAffected } from './print-affected';
|
||||
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
||||
|
||||
export function affected(command: string, parsedArgs: yargs.Arguments): void {
|
||||
const { nxArgs, targetArgs } = splitArgsIntoNxArgsAndTargetArgs(parsedArgs);
|
||||
|
||||
const env = readEnvironment(nxArgs.target);
|
||||
const projectGraph = createProjectGraph();
|
||||
const fileChanges = readFileChanges(nxArgs);
|
||||
let affectedGraph = filterAffected(projectGraph, fileChanges);
|
||||
if (parsedArgs.withDeps) {
|
||||
affectedGraph = withDeps(projectGraph, Object.values(affectedGraph.nodes));
|
||||
}
|
||||
const affectedProjects = Object.values(
|
||||
parsedArgs.all ? projectGraph.nodes : affectedGraph.nodes
|
||||
)
|
||||
.filter(n => !parsedArgs.exclude.includes(n.name))
|
||||
.filter(n => !parsedArgs.onlyFailed || !env.workspace.getResult(n.name));
|
||||
|
||||
try {
|
||||
switch (command) {
|
||||
case 'apps':
|
||||
const apps = affectedProjects
|
||||
.filter(p => p.type === ProjectType.app)
|
||||
.map(p => p.name);
|
||||
if (parsedArgs.plain) {
|
||||
console.log(apps.join(' '));
|
||||
} else {
|
||||
printArgsWarning(parsedArgs);
|
||||
if (apps.length) {
|
||||
output.log({
|
||||
title: 'Affected apps:',
|
||||
bodyLines: apps.map(app => `${output.colors.gray('-')} ${app}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'libs':
|
||||
const libs = affectedProjects
|
||||
.filter(p => p.type === ProjectType.lib)
|
||||
.map(p => p.name);
|
||||
if (parsedArgs.plain) {
|
||||
console.log(libs.join(' '));
|
||||
} else {
|
||||
printArgsWarning(parsedArgs);
|
||||
if (libs.length) {
|
||||
output.log({
|
||||
title: 'Affected libs:',
|
||||
bodyLines: libs.map(lib => `${output.colors.gray('-')} ${lib}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'dep-graph':
|
||||
const projectNames = affectedProjects.map(p => p.name);
|
||||
printArgsWarning(parsedArgs);
|
||||
generateGraph(parsedArgs as any, projectNames);
|
||||
break;
|
||||
|
||||
case 'print-affected':
|
||||
if (nxArgs.target) {
|
||||
const projectWithTargetAndConfig = allProjectsWithTargetAndConfiguration(
|
||||
affectedProjects,
|
||||
nxArgs
|
||||
);
|
||||
printAffected(
|
||||
projectWithTargetAndConfig,
|
||||
affectedProjects,
|
||||
projectGraph,
|
||||
nxArgs,
|
||||
targetArgs
|
||||
);
|
||||
} else {
|
||||
printAffected([], affectedProjects, projectGraph, nxArgs, targetArgs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'affected': {
|
||||
const projectWithTargetAndConfig = allProjectsWithTargetAndConfiguration(
|
||||
affectedProjects,
|
||||
nxArgs
|
||||
);
|
||||
printArgsWarning(parsedArgs);
|
||||
runCommand(
|
||||
projectWithTargetAndConfig,
|
||||
projectGraph,
|
||||
env,
|
||||
nxArgs,
|
||||
targetArgs
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printError(e, parsedArgs.verbose);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function readFileChanges(nxArgs: NxArgs) {
|
||||
// Do we still need this `--all` option?
|
||||
if (nxArgs.all) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = parseFiles(nxArgs).files;
|
||||
return calculateFileChanges(files, nxArgs.base, nxArgs.head);
|
||||
}
|
||||
|
||||
function allProjectsWithTargetAndConfiguration(
|
||||
projects: ProjectGraphNode[],
|
||||
nxArgs: NxArgs
|
||||
) {
|
||||
return projects.filter(p =>
|
||||
projectHasTargetAndConfiguration(p, nxArgs.target, nxArgs.configuration)
|
||||
);
|
||||
}
|
||||
|
||||
function printError(e: any, verbose?: boolean) {
|
||||
const bodyLines = [e.message];
|
||||
if (verbose && e.stack) {
|
||||
bodyLines.push('');
|
||||
bodyLines.push(e.stack);
|
||||
}
|
||||
output.error({
|
||||
title: 'There was a critical error when running your command',
|
||||
bodyLines
|
||||
});
|
||||
}
|
||||
@ -5,8 +5,8 @@ import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectGraphNode
|
||||
} from './project-graph';
|
||||
import { output } from './output';
|
||||
} from '../core/project-graph';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export function generateGraph(
|
||||
args: { file?: string; filter?: string[]; exclude?: string[] },
|
||||
|
||||
@ -2,16 +2,13 @@ import { execSync } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as resolve from 'resolve';
|
||||
import { getProjectRoots, parseFiles, printArgsWarning } from './shared';
|
||||
import { YargsAffectedOptions } from './run-tasks/affected';
|
||||
import { fileExists } from '../utils/fileutils';
|
||||
import { output } from './output';
|
||||
import { createProjectGraph } from './project-graph';
|
||||
import { filterAffected } from './affected-project-graph';
|
||||
import { calculateFileChanges } from './file-utils';
|
||||
|
||||
export interface YargsFormatOptions extends YargsAffectedOptions {
|
||||
libsAndApps?: boolean;
|
||||
}
|
||||
import { output } from '../utils/output';
|
||||
import { createProjectGraph } from '../core/project-graph';
|
||||
import { filterAffected } from '../core/affected-project-graph';
|
||||
import { calculateFileChanges } from '../core/file-utils';
|
||||
import * as yargs from 'yargs';
|
||||
import { NxArgs } from './utils';
|
||||
|
||||
const PRETTIER_EXTENSIONS = [
|
||||
'ts',
|
||||
@ -26,11 +23,11 @@ const PRETTIER_EXTENSIONS = [
|
||||
'md'
|
||||
];
|
||||
|
||||
export function format(command: 'check' | 'write', args: YargsFormatOptions) {
|
||||
export function format(command: 'check' | 'write', args: yargs.Arguments) {
|
||||
let patterns: string[];
|
||||
|
||||
try {
|
||||
patterns = getPatterns(args);
|
||||
patterns = getPatterns(args as any);
|
||||
} catch (e) {
|
||||
output.error({
|
||||
title: e.message,
|
||||
@ -60,7 +57,7 @@ export function format(command: 'check' | 'write', args: YargsFormatOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
function getPatterns(args: YargsAffectedOptions) {
|
||||
function getPatterns(args: NxArgs & { libsAndApps: boolean; _: string[] }) {
|
||||
const allFilesPattern = [`"**/*.{${PRETTIER_EXTENSIONS.join(',')}}"`];
|
||||
|
||||
try {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { createProjectGraph } from './project-graph';
|
||||
import { createProjectGraph } from '../core/project-graph';
|
||||
import { WorkspaceIntegrityChecks } from './workspace-integrity-checks';
|
||||
import * as path from 'path';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { allFilesInDir } from '../command-line/file-utils';
|
||||
import { output } from './output';
|
||||
import { allFilesInDir } from '../core/file-utils';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export function workspaceLint() {
|
||||
const graph = createProjectGraph();
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
readCapabilitiesFromNodeModules
|
||||
} from '../utils/plugin-utils';
|
||||
import { approvedPlugins } from '../utils/plugins';
|
||||
import { output } from './output';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export interface YargsListArgs extends yargs.Arguments, ListArgs {}
|
||||
|
||||
|
||||
@ -3,13 +3,14 @@ import { execSync } from 'child_process';
|
||||
import { platform } from 'os';
|
||||
import * as yargs from 'yargs';
|
||||
import { nxVersion } from '../utils/versions';
|
||||
import { affected, runMany } from './run-tasks';
|
||||
import { generateGraph } from './dep-graph';
|
||||
import { format } from './format';
|
||||
import { workspaceLint } from './lint';
|
||||
import { list } from './list';
|
||||
import { report } from './report';
|
||||
import { workspaceSchematic } from './workspace-schematic';
|
||||
import { affected } from './affected';
|
||||
import { runMany } from '@nrwl/workspace/src/command-line/run-many';
|
||||
|
||||
const noop = (yargs: yargs.Argv): yargs.Argv => yargs;
|
||||
|
||||
@ -133,7 +134,7 @@ export const commandsObject = yargs
|
||||
.command(
|
||||
'print-affected',
|
||||
'Graph execution plan',
|
||||
yargs => withAffectedOptions(yargs),
|
||||
yargs => withAffectedOptions(withPrintAffectedOptions(yargs)),
|
||||
args =>
|
||||
affected('print-affected', {
|
||||
...args
|
||||
@ -225,6 +226,10 @@ function withFormatOptions(yargs: yargs.Argv): yargs.Argv {
|
||||
});
|
||||
}
|
||||
|
||||
function withPrintAffectedOptions(yargs: yargs.Argv): yargs.Argv {
|
||||
return yargs.option('select', { type: 'string' });
|
||||
}
|
||||
|
||||
function withAffectedOptions(yargs: yargs.Argv): yargs.Argv {
|
||||
return yargs
|
||||
.option('files', {
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { readWorkspaceConfigPath, updateJsonFile } from '../utils/fileutils';
|
||||
|
||||
type Migration = { description: string; run(): void };
|
||||
type MigrationName = { name: string; migration: Migration };
|
||||
|
||||
const allMigrations = fs
|
||||
.readdirSync(path.join(__dirname, '/../../migrations'))
|
||||
.filter(f => f.endsWith('.js') && !f.endsWith('.d.js'))
|
||||
.map(file => ({
|
||||
migration: require(`../../migrations/${file}`).default,
|
||||
name: path.parse(file).name
|
||||
}));
|
||||
|
||||
const latestMigration = readLatestMigration();
|
||||
const migrationsToRun = calculateMigrationsToRun(
|
||||
allMigrations,
|
||||
latestMigration
|
||||
);
|
||||
|
||||
if (migrationsToRun.length === 0) {
|
||||
console.log('No migrations to run');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
printMigrationsNames(latestMigration, migrationsToRun);
|
||||
runMigrations(migrationsToRun);
|
||||
updateLatestMigration();
|
||||
|
||||
console.log('All migrations run successfully');
|
||||
|
||||
function readLatestMigration(): string {
|
||||
const angularCli = readWorkspaceConfigPath();
|
||||
return angularCli.project.latestMigration;
|
||||
}
|
||||
|
||||
function calculateMigrationsToRun(
|
||||
migrations: MigrationName[],
|
||||
latestMigration: string
|
||||
) {
|
||||
const startingWith = latestMigration
|
||||
? migrations.findIndex(item => item.name === latestMigration) + 1
|
||||
: 0;
|
||||
return migrations.slice(startingWith);
|
||||
}
|
||||
|
||||
function printMigrationsNames(
|
||||
latestMigration: string,
|
||||
migrations: MigrationName[]
|
||||
): void {
|
||||
console.log(
|
||||
`Nx will run the following migrations (after ${latestMigration}):`
|
||||
);
|
||||
migrations.forEach(m => {
|
||||
console.log(`- ${m.name}`);
|
||||
});
|
||||
console.log('---------------------------------------------');
|
||||
}
|
||||
|
||||
function runMigrations(migrations: MigrationName[]): void {
|
||||
migrations.forEach(m => {
|
||||
try {
|
||||
console.log(`Running ${m.name}`);
|
||||
console.log(m.migration.description);
|
||||
m.migration.run();
|
||||
console.log('---------------------------------------------');
|
||||
} catch (e) {
|
||||
console.error(`Migration ${m.name} failed`);
|
||||
console.error(e);
|
||||
console.error(`Please run 'git checkout .'`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateLatestMigration(): void {
|
||||
// we must reread .angular-cli.json because some of the migrations could have modified it
|
||||
updateJsonFile('.angular-cli.json', angularCliJson => {
|
||||
angularCliJson.project.latestMigration =
|
||||
migrationsToRun[migrationsToRun.length - 1].name;
|
||||
});
|
||||
}
|
||||
77
packages/workspace/src/command-line/print-affected.ts
Normal file
77
packages/workspace/src/command-line/print-affected.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { ProjectGraph, ProjectGraphNode } from '../core/project-graph';
|
||||
import { Task } from '../tasks-runner/tasks-runner';
|
||||
import { createTask } from '../tasks-runner/run-command';
|
||||
import { basename } from 'path';
|
||||
import { getCommand, getOutputs } from '../tasks-runner/utils';
|
||||
import * as yargs from 'yargs';
|
||||
import { NxArgs } from './utils';
|
||||
import { cliCommand } from '@nrwl/workspace/src/core/file-utils';
|
||||
|
||||
export function printAffected(
|
||||
affectedProjectsWithTargetAndConfig: ProjectGraphNode[],
|
||||
affectedProjects: ProjectGraphNode[],
|
||||
projectGraph: ProjectGraph,
|
||||
nxArgs: NxArgs,
|
||||
targetArgs: yargs.Arguments
|
||||
) {
|
||||
const projectNames = affectedProjects.map(p => p.name);
|
||||
const tasksJson = createTasks(
|
||||
affectedProjectsWithTargetAndConfig,
|
||||
projectGraph,
|
||||
nxArgs,
|
||||
targetArgs
|
||||
);
|
||||
const result = {
|
||||
tasks: tasksJson,
|
||||
projects: projectNames,
|
||||
projectGraph: serializeProjectGraph(projectGraph)
|
||||
};
|
||||
console.log(JSON.stringify(selectPrintAffected(result, null), null, 2));
|
||||
}
|
||||
|
||||
function createTasks(
|
||||
affectedProjectsWithTargetAndConfig: ProjectGraphNode[],
|
||||
projectGraph: ProjectGraph,
|
||||
nxArgs: NxArgs,
|
||||
targetArgs: yargs.Arguments
|
||||
) {
|
||||
const tasks: Task[] = affectedProjectsWithTargetAndConfig.map(
|
||||
affectedProject =>
|
||||
createTask({
|
||||
project: affectedProject,
|
||||
target: nxArgs.target,
|
||||
configuration: nxArgs.configuration,
|
||||
overrides: targetArgs
|
||||
})
|
||||
);
|
||||
const cli = cliCommand();
|
||||
const isYarn = basename(process.env.npm_execpath || 'npm').startsWith('yarn');
|
||||
return tasks.map(task => ({
|
||||
id: task.id,
|
||||
overrides: targetArgs,
|
||||
target: task.target,
|
||||
command: `${isYarn ? 'yarn' : 'npm run'} ${getCommand(cli, isYarn, task)}`,
|
||||
outputs: getOutputs(projectGraph.nodes, task)
|
||||
}));
|
||||
}
|
||||
|
||||
function serializeProjectGraph(projectGraph: ProjectGraph) {
|
||||
const nodes = Object.values(projectGraph.nodes).map(n => n.name);
|
||||
return { nodes, dependencies: projectGraph.dependencies };
|
||||
}
|
||||
|
||||
function selectPrintAffected(result: any, select: string) {
|
||||
if (!select) return result;
|
||||
const parts = select.indexOf('.') > -1 ? select.split('.') : [select];
|
||||
return parts
|
||||
.reduce((m, c) => {
|
||||
if (m[c]) {
|
||||
return m[c];
|
||||
} else {
|
||||
throw new Error(
|
||||
`Cannot select '${select}' in the results of print-affected.`
|
||||
);
|
||||
}
|
||||
}, result)
|
||||
.join(', ');
|
||||
}
|
||||
@ -2,7 +2,7 @@ import { terminal } from '@angular-devkit/core';
|
||||
import { readFileSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { output } from './output';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export const packagesWeCareAbout = [
|
||||
'@nrwl/angular',
|
||||
|
||||
86
packages/workspace/src/command-line/run-many.ts
Normal file
86
packages/workspace/src/command-line/run-many.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import * as yargs from 'yargs';
|
||||
import { runCommand } from '../tasks-runner/run-command';
|
||||
import { splitArgsIntoNxArgsAndTargetArgs, NxArgs } from './utils';
|
||||
import { output } from '../utils/output';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectGraphNode,
|
||||
withDeps
|
||||
} from '../core/project-graph';
|
||||
import { readEnvironment } from '../core/file-utils';
|
||||
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
||||
|
||||
export function runMany(parsedArgs: yargs.Arguments): void {
|
||||
const { nxArgs, targetArgs } = splitArgsIntoNxArgsAndTargetArgs(parsedArgs);
|
||||
const env = readEnvironment(nxArgs.target);
|
||||
const projectGraph = createProjectGraph();
|
||||
const projects = projectsToRun(nxArgs, projectGraph);
|
||||
runCommand(projects, projectGraph, env, nxArgs, targetArgs);
|
||||
}
|
||||
|
||||
function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
|
||||
const allProjects = Object.values(projectGraph.nodes);
|
||||
if (nxArgs.all) {
|
||||
return runnableForTargetAndConfiguration(
|
||||
allProjects,
|
||||
nxArgs.target,
|
||||
nxArgs.configuration
|
||||
);
|
||||
} else {
|
||||
checkForInvalidProjects(nxArgs, allProjects);
|
||||
let selectedProjects = allProjects.filter(
|
||||
p => nxArgs.projects.indexOf(p.name) > -1
|
||||
);
|
||||
if (nxArgs.withDeps) {
|
||||
selectedProjects = Object.values(
|
||||
withDeps(projectGraph, selectedProjects).nodes
|
||||
);
|
||||
}
|
||||
return runnableForTargetAndConfiguration(
|
||||
selectedProjects,
|
||||
nxArgs.target,
|
||||
nxArgs.configuration,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function checkForInvalidProjects(
|
||||
nxArgs: NxArgs,
|
||||
allProjects: ProjectGraphNode[]
|
||||
) {
|
||||
const invalid = nxArgs.projects.filter(
|
||||
name => !allProjects.find(p => p.name === name)
|
||||
);
|
||||
if (invalid.length !== 0) {
|
||||
throw new Error(`Invalid projects: ${invalid.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
function runnableForTargetAndConfiguration(
|
||||
projects: ProjectGraphNode[],
|
||||
target: string,
|
||||
configuration?: string,
|
||||
strict = false
|
||||
): ProjectGraphNode[] {
|
||||
const notRunnable = [];
|
||||
const runnable = [];
|
||||
|
||||
for (let project of projects) {
|
||||
if (projectHasTargetAndConfiguration(project, target, configuration)) {
|
||||
runnable.push(project);
|
||||
} else {
|
||||
notRunnable.push(project);
|
||||
}
|
||||
}
|
||||
|
||||
if (strict && notRunnable.length) {
|
||||
output.warn({
|
||||
title: `the following do not have configuration for "${target}"`,
|
||||
bodyLines: notRunnable.map(p => '- ' + p)
|
||||
});
|
||||
}
|
||||
|
||||
return runnable;
|
||||
}
|
||||
@ -1,275 +0,0 @@
|
||||
import { basename } from 'path';
|
||||
import * as yargs from 'yargs';
|
||||
import { Task } from '../../tasks-runner/tasks-runner';
|
||||
import { generateGraph } from '../dep-graph';
|
||||
import { output } from '../output';
|
||||
import { cliCommand, parseFiles, printArgsWarning } from '../shared';
|
||||
import { getCommand, getOutputs } from '../../tasks-runner/utils';
|
||||
import { createTask, runCommand } from './run-command';
|
||||
import {
|
||||
Arguments,
|
||||
projectHasTargetAndConfiguration,
|
||||
readEnvironment,
|
||||
splitArgs
|
||||
} from './utils';
|
||||
import { filterAffected } from '../affected-project-graph';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectGraphNode,
|
||||
ProjectType,
|
||||
reverse,
|
||||
withDeps
|
||||
} from '../project-graph';
|
||||
import { calculateFileChanges } from '../file-utils';
|
||||
|
||||
export interface YargsAffectedOptions
|
||||
extends yargs.Arguments,
|
||||
AffectedOptions {}
|
||||
|
||||
export interface AffectedOptions {
|
||||
target?: string;
|
||||
configuration?: string;
|
||||
runner?: string;
|
||||
parallel?: boolean;
|
||||
maxParallel?: number;
|
||||
untracked?: boolean;
|
||||
uncommitted?: boolean;
|
||||
all?: boolean;
|
||||
base?: string;
|
||||
head?: string;
|
||||
exclude?: string[];
|
||||
files?: string[];
|
||||
onlyFailed?: boolean;
|
||||
'only-failed'?: boolean;
|
||||
'max-parallel'?: boolean;
|
||||
verbose?: boolean;
|
||||
help?: boolean;
|
||||
version?: boolean;
|
||||
quiet?: boolean;
|
||||
plain?: boolean;
|
||||
withDeps?: boolean;
|
||||
}
|
||||
|
||||
export function affected(
|
||||
command: string,
|
||||
parsedArgs: YargsAffectedOptions
|
||||
): void {
|
||||
const env = readEnvironment(parsedArgs.target);
|
||||
const projectGraph = createProjectGraph();
|
||||
|
||||
const fileChanges = readFileChanges(parsedArgs);
|
||||
let affectedGraph = filterAffected(projectGraph, fileChanges);
|
||||
if (parsedArgs.withDeps) {
|
||||
affectedGraph = withDeps(projectGraph, Object.values(affectedGraph.nodes));
|
||||
}
|
||||
|
||||
const projects = Object.values(
|
||||
parsedArgs.all ? projectGraph.nodes : affectedGraph.nodes
|
||||
)
|
||||
.filter(n => !parsedArgs.exclude.includes(n.name))
|
||||
.filter(n => !parsedArgs.onlyFailed || !env.workspace.getResult(n.name));
|
||||
|
||||
try {
|
||||
switch (command) {
|
||||
case 'apps':
|
||||
const apps = projects
|
||||
.filter(p => p.type === ProjectType.app)
|
||||
.map(p => p.name);
|
||||
if (parsedArgs.plain) {
|
||||
console.log(apps.join(' '));
|
||||
} else {
|
||||
printArgsWarning(parsedArgs);
|
||||
if (apps.length) {
|
||||
output.log({
|
||||
title: 'Affected apps:',
|
||||
bodyLines: apps.map(app => `${output.colors.gray('-')} ${app}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'libs':
|
||||
const libs = projects
|
||||
.filter(p => p.type === ProjectType.lib)
|
||||
.map(p => p.name);
|
||||
if (parsedArgs.plain) {
|
||||
console.log(libs.join(' '));
|
||||
} else {
|
||||
printArgsWarning(parsedArgs);
|
||||
if (libs.length) {
|
||||
output.log({
|
||||
title: 'Affected libs:',
|
||||
bodyLines: libs.map(lib => `${output.colors.gray('-')} ${lib}`)
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'print-affected':
|
||||
if (parsedArgs && parsedArgs.target) {
|
||||
const {
|
||||
args,
|
||||
projectWithTargetAndConfig
|
||||
} = allProjectsWithTargetAndConfiguration(projects, parsedArgs);
|
||||
printAffectedWithTasks(
|
||||
projectWithTargetAndConfig,
|
||||
projects,
|
||||
projectGraph,
|
||||
args
|
||||
);
|
||||
} else {
|
||||
printAffectedWithoutTasks(projects, projectGraph);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'dep-graph': {
|
||||
const projectNames = projects.map(p => p.name);
|
||||
printArgsWarning(parsedArgs);
|
||||
generateGraph(parsedArgs as any, projectNames);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'affected': {
|
||||
const {
|
||||
args,
|
||||
projectWithTargetAndConfig
|
||||
} = allProjectsWithTargetAndConfiguration(projects, parsedArgs);
|
||||
printArgsWarning(parsedArgs);
|
||||
|
||||
runCommand(projectWithTargetAndConfig, projectGraph, args, env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printError(e, parsedArgs.verbose);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function readFileChanges(parsedArgs: YargsAffectedOptions) {
|
||||
// Do we still need this `--all` option?
|
||||
if (parsedArgs.all) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = parseFiles(parsedArgs).files;
|
||||
return calculateFileChanges(files, parsedArgs.base, parsedArgs.head);
|
||||
}
|
||||
|
||||
function allProjectsWithTargetAndConfiguration(
|
||||
projects: ProjectGraphNode[],
|
||||
parsedArgs: YargsAffectedOptions
|
||||
) {
|
||||
const args = splitArgs(parsedArgs, nxSpecificFlags);
|
||||
const projectWithTargetAndConfig = projects.filter(p =>
|
||||
projectHasTargetAndConfiguration(
|
||||
p,
|
||||
args.nxArgs.target,
|
||||
args.nxArgs.configuration
|
||||
)
|
||||
);
|
||||
return { args, projectWithTargetAndConfig };
|
||||
}
|
||||
|
||||
function printError(e: any, verbose?: boolean) {
|
||||
const bodyLines = [e.message];
|
||||
if (verbose && e.stack) {
|
||||
bodyLines.push('');
|
||||
bodyLines.push(e.stack);
|
||||
}
|
||||
output.error({
|
||||
title: 'There was a critical error when running your command',
|
||||
bodyLines
|
||||
});
|
||||
}
|
||||
|
||||
function printAffectedWithTasks(
|
||||
affectedProjectsWithTargetAndConfig: ProjectGraphNode[],
|
||||
affectedProjects: ProjectGraphNode[],
|
||||
projectGraph: ProjectGraph,
|
||||
args: Arguments<YargsAffectedOptions>
|
||||
) {
|
||||
const tasks: Task[] = affectedProjectsWithTargetAndConfig.map(
|
||||
affectedProject =>
|
||||
createTask({
|
||||
project: affectedProject,
|
||||
target: args.nxArgs.target,
|
||||
configuration: args.nxArgs.configuration,
|
||||
overrides: args.overrides
|
||||
})
|
||||
);
|
||||
const cli = cliCommand();
|
||||
const isYarn = basename(process.env.npm_execpath || 'npm').startsWith('yarn');
|
||||
const projectNames = affectedProjects.map(p => p.name);
|
||||
const tasksJson = tasks.map(task => ({
|
||||
id: task.id,
|
||||
overrides: task.overrides,
|
||||
target: task.target,
|
||||
command: `${isYarn ? 'yarn' : 'npm run'} ${getCommand(cli, isYarn, task)}`,
|
||||
outputs: getOutputs(projectGraph.nodes, task)
|
||||
}));
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
tasks: tasksJson,
|
||||
projects: projectNames,
|
||||
projectGraph: serializeProjectGraph(projectGraph)
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function printAffectedWithoutTasks(
|
||||
affectedProjects: ProjectGraphNode[],
|
||||
projectGraph: ProjectGraph
|
||||
) {
|
||||
const projectNames = affectedProjects.map(p => p.name);
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
tasks: [],
|
||||
projects: projectNames,
|
||||
projectGraph: serializeProjectGraph(projectGraph)
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function serializeProjectGraph(projectGraph: ProjectGraph) {
|
||||
const nodes = Object.values(projectGraph.nodes).map(n => n.name);
|
||||
return { nodes, dependencies: projectGraph.dependencies };
|
||||
}
|
||||
|
||||
/**
|
||||
* These options are only for getting an array with properties of AffectedOptions.
|
||||
*
|
||||
* @remark They are not defaults or useful for anything else
|
||||
*/
|
||||
const dummyOptions: AffectedOptions = {
|
||||
target: '',
|
||||
configuration: '',
|
||||
onlyFailed: false,
|
||||
'only-failed': false,
|
||||
untracked: false,
|
||||
uncommitted: false,
|
||||
runner: '',
|
||||
help: false,
|
||||
version: false,
|
||||
quiet: false,
|
||||
all: false,
|
||||
base: 'base',
|
||||
head: 'head',
|
||||
exclude: ['exclude'],
|
||||
files: [''],
|
||||
verbose: false,
|
||||
plain: false
|
||||
};
|
||||
|
||||
const nxSpecificFlags = Object.keys(dummyOptions);
|
||||
@ -1,2 +0,0 @@
|
||||
export * from './run-many';
|
||||
export * from './affected';
|
||||
@ -1,128 +0,0 @@
|
||||
import * as yargs from 'yargs';
|
||||
import { runCommand } from './run-command';
|
||||
import {
|
||||
readEnvironment,
|
||||
splitArgs,
|
||||
projectHasTargetAndConfiguration
|
||||
} from './utils';
|
||||
import { output } from '../output';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectGraphNode,
|
||||
withDeps
|
||||
} from '../project-graph';
|
||||
|
||||
export type YargsRunManyOptions = yargs.Arguments & RunManyOptions;
|
||||
|
||||
export interface RunManyOptions {
|
||||
target: string;
|
||||
projects: string[];
|
||||
all: boolean;
|
||||
configuration?: string;
|
||||
runner?: string;
|
||||
parallel?: boolean;
|
||||
maxParallel?: number;
|
||||
onlyFailed?: boolean;
|
||||
'only-failed'?: boolean;
|
||||
'max-parallel'?: boolean;
|
||||
verbose?: boolean;
|
||||
help?: boolean;
|
||||
version?: boolean;
|
||||
quiet?: boolean;
|
||||
withDeps?: boolean;
|
||||
'with-deps'?: boolean;
|
||||
}
|
||||
|
||||
export function runMany(parsedArgs: yargs.Arguments): void {
|
||||
const args = splitArgs(normalize(parsedArgs) as YargsRunManyOptions, flags);
|
||||
const env = readEnvironment(args.nxArgs.target);
|
||||
const projectGraph = createProjectGraph();
|
||||
const projects = projectsToRun(args.nxArgs, projectGraph);
|
||||
runCommand(projects, projectGraph, args, env);
|
||||
}
|
||||
|
||||
function projectsToRun(nxArgs: RunManyOptions, projectGraph: ProjectGraph) {
|
||||
const allProjects = Object.values(projectGraph.nodes);
|
||||
if (nxArgs.all) {
|
||||
return runnableForTargetAndConfiguration(
|
||||
allProjects,
|
||||
nxArgs.target,
|
||||
nxArgs.configuration
|
||||
);
|
||||
} else {
|
||||
let selectedProjects = allProjects.filter(
|
||||
p => nxArgs.projects.indexOf(p.name) > -1
|
||||
);
|
||||
if (nxArgs.withDeps) {
|
||||
selectedProjects = Object.values(
|
||||
withDeps(projectGraph, selectedProjects).nodes
|
||||
);
|
||||
}
|
||||
return runnableForTargetAndConfiguration(
|
||||
selectedProjects,
|
||||
nxArgs.target,
|
||||
nxArgs.configuration,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(args: yargs.Arguments): yargs.Arguments {
|
||||
if (!args.all) {
|
||||
args.all = false;
|
||||
}
|
||||
|
||||
if (!args.projects) {
|
||||
args.projects = [];
|
||||
} else {
|
||||
args.projects = args.projects.split(',').map((p: string) => p.trim());
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function runnableForTargetAndConfiguration(
|
||||
projects: ProjectGraphNode[],
|
||||
target: string,
|
||||
configuration?: string,
|
||||
strict = false
|
||||
): ProjectGraphNode[] {
|
||||
const notRunnable = [];
|
||||
const runnable = [];
|
||||
|
||||
for (let project of projects) {
|
||||
if (projectHasTargetAndConfiguration(project, target, configuration)) {
|
||||
runnable.push(project);
|
||||
} else {
|
||||
notRunnable.push(project);
|
||||
}
|
||||
}
|
||||
|
||||
if (strict && notRunnable.length) {
|
||||
output.warn({
|
||||
title: `the following do not have configuration for "${target}"`,
|
||||
bodyLines: notRunnable.map(p => '- ' + p)
|
||||
});
|
||||
}
|
||||
|
||||
return runnable;
|
||||
}
|
||||
|
||||
const dummyOptions: RunManyOptions = {
|
||||
target: '',
|
||||
projects: [],
|
||||
all: false,
|
||||
configuration: '',
|
||||
onlyFailed: false,
|
||||
'only-failed': false,
|
||||
runner: '',
|
||||
help: false,
|
||||
version: false,
|
||||
quiet: false,
|
||||
verbose: false,
|
||||
withDeps: false,
|
||||
'with-deps': false
|
||||
};
|
||||
|
||||
const flags = Object.keys(dummyOptions);
|
||||
@ -1,51 +0,0 @@
|
||||
import { splitArgs } from './utils';
|
||||
|
||||
describe('splitArgs', () => {
|
||||
it('should split nx specific arguments into nxArgs', () => {
|
||||
expect(
|
||||
splitArgs(
|
||||
{
|
||||
files: [''],
|
||||
notNxArg: true,
|
||||
_: ['--override'],
|
||||
$0: ''
|
||||
},
|
||||
['files']
|
||||
).nxArgs
|
||||
).toEqual({
|
||||
files: ['']
|
||||
});
|
||||
});
|
||||
|
||||
it('should split non nx specific arguments into target args', () => {
|
||||
expect(
|
||||
splitArgs(
|
||||
{
|
||||
files: [''],
|
||||
notNxArg: true,
|
||||
_: ['--override'],
|
||||
$0: ''
|
||||
},
|
||||
['files']
|
||||
).targetArgs
|
||||
).toEqual({
|
||||
notNxArg: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should split delimited args into task overrides', () => {
|
||||
expect(
|
||||
splitArgs(
|
||||
{
|
||||
files: [''],
|
||||
notNxArg: true,
|
||||
_: ['', '--override'],
|
||||
$0: ''
|
||||
},
|
||||
['files']
|
||||
).overrides
|
||||
).toEqual({
|
||||
override: true
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,75 +0,0 @@
|
||||
import * as yargsParser from 'yargs-parser';
|
||||
import * as yargs from 'yargs';
|
||||
import { NxJson, readNxJson, readWorkspaceJson } from '../shared';
|
||||
import { WorkspaceResults } from '../workspace-results';
|
||||
import { ProjectGraphNode } from '../project-graph';
|
||||
|
||||
export interface Arguments<T extends yargs.Arguments> {
|
||||
nxArgs: T;
|
||||
targetArgs: T;
|
||||
overrides: any;
|
||||
}
|
||||
|
||||
const ignoreArgs = ['$0', '_'];
|
||||
export function splitArgs<T extends yargs.Arguments>(
|
||||
args: T,
|
||||
nxSpecific: (keyof T)[]
|
||||
): Arguments<T> {
|
||||
const nx: any = {};
|
||||
const targetArgs: any = {};
|
||||
|
||||
const overrides = yargsParser(args._.slice(1));
|
||||
delete overrides._;
|
||||
|
||||
Object.entries(args).forEach(([key, value]) => {
|
||||
if (nxSpecific.includes(key as any)) {
|
||||
nx[key] = value;
|
||||
} else if (!ignoreArgs.includes(key)) {
|
||||
targetArgs[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return { nxArgs: nx, targetArgs, overrides };
|
||||
}
|
||||
export interface TaskArgs {
|
||||
projects: string[];
|
||||
target: string;
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
export function projectHasTargetAndConfiguration(
|
||||
project: ProjectGraphNode,
|
||||
target: string,
|
||||
configuration?: string
|
||||
) {
|
||||
if (
|
||||
!project.data ||
|
||||
!project.data.architect ||
|
||||
!project.data.architect[target]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!configuration) {
|
||||
return !!project.data.architect[target];
|
||||
} else {
|
||||
return (
|
||||
project.data.architect[target].configurations &&
|
||||
project.data.architect[target].configurations[configuration]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
nxJson: NxJson;
|
||||
workspaceJson: any;
|
||||
workspace: any;
|
||||
}
|
||||
|
||||
export function readEnvironment(target: string): Environment {
|
||||
const nxJson = readNxJson();
|
||||
const workspaceJson = readWorkspaceJson();
|
||||
const workspace = new WorkspaceResults(target);
|
||||
|
||||
return { nxJson, workspaceJson, workspace };
|
||||
}
|
||||
@ -1,44 +1,12 @@
|
||||
import { execSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { YargsAffectedOptions } from './run-tasks/affected';
|
||||
import { output } from './output';
|
||||
import { allFilesInDir, FileData } from './file-utils';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraphNode,
|
||||
ProjectType
|
||||
} from './project-graph';
|
||||
import { output } from '../utils/output';
|
||||
import { createProjectGraph, ProjectGraphNode } from '../core/project-graph';
|
||||
import { NxArgs } from './utils';
|
||||
import { NxJson } from '../core/shared-interfaces';
|
||||
import { readWorkspaceJson, TEN_MEGABYTES } from '../core/file-utils';
|
||||
|
||||
export const TEN_MEGABYTES = 1024 * 10000;
|
||||
export type ImplicitDependencyEntry = { [key: string]: '*' | string[] };
|
||||
export type NormalizedImplicitDependencyEntry = { [key: string]: string[] };
|
||||
export type ImplicitDependencies = {
|
||||
files: NormalizedImplicitDependencyEntry;
|
||||
projects: NormalizedImplicitDependencyEntry;
|
||||
};
|
||||
|
||||
export interface NxJson {
|
||||
implicitDependencies?: ImplicitDependencyEntry;
|
||||
npmScope: string;
|
||||
projects: {
|
||||
[projectName: string]: NxJsonProjectConfig;
|
||||
};
|
||||
tasksRunnerOptions?: {
|
||||
[tasksRunnerName: string]: {
|
||||
runner: string;
|
||||
options?: unknown;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface NxJsonProjectConfig {
|
||||
implicitDependencies?: string[];
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export function printArgsWarning(options: YargsAffectedOptions) {
|
||||
export function printArgsWarning(options: NxArgs) {
|
||||
const { files, uncommitted, untracked, base, head, all } = options;
|
||||
|
||||
if (
|
||||
@ -72,7 +40,7 @@ export function printArgsWarning(options: YargsAffectedOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseFiles(options: YargsAffectedOptions): { files: string[] } {
|
||||
export function parseFiles(options: NxArgs): { files: string[] } {
|
||||
const { files, uncommitted, untracked, base, head } = options;
|
||||
|
||||
if (files) {
|
||||
@ -147,146 +115,7 @@ function parseGitOutput(command: string): string[] {
|
||||
.filter(a => a.length > 0);
|
||||
}
|
||||
|
||||
function detectAndSetInvalidProjectValues(
|
||||
map: Map<string, string[]>,
|
||||
sourceName: string,
|
||||
desiredProjectNames: string[],
|
||||
validProjects: any
|
||||
) {
|
||||
const invalidProjects = desiredProjectNames.filter(
|
||||
projectName => !validProjects[projectName]
|
||||
);
|
||||
if (invalidProjects.length > 0) {
|
||||
map.set(sourceName, invalidProjects);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertWorkspaceValidity(workspaceJson, nxJson) {
|
||||
const workspaceJsonProjects = Object.keys(workspaceJson.projects);
|
||||
const nxJsonProjects = Object.keys(nxJson.projects);
|
||||
|
||||
if (minus(workspaceJsonProjects, nxJsonProjects).length > 0) {
|
||||
throw new Error(
|
||||
`${workspaceFileName()} and nx.json are out of sync. The following projects are missing in nx.json: ${minus(
|
||||
workspaceJsonProjects,
|
||||
nxJsonProjects
|
||||
).join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
if (minus(nxJsonProjects, workspaceJsonProjects).length > 0) {
|
||||
throw new Error(
|
||||
`${workspaceFileName()} and nx.json are out of sync. The following projects are missing in ${workspaceFileName()}: ${minus(
|
||||
nxJsonProjects,
|
||||
workspaceJsonProjects
|
||||
).join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const projects = {
|
||||
...workspaceJson.projects,
|
||||
...nxJson.projects
|
||||
};
|
||||
|
||||
const invalidImplicitDependencies = new Map<string, string[]>();
|
||||
|
||||
Object.entries<'*' | string[]>(nxJson.implicitDependencies || {})
|
||||
.filter(([_, val]) => val !== '*') // These are valid since it is calculated
|
||||
.reduce((map, [filename, projectNames]: [string, string[]]) => {
|
||||
detectAndSetInvalidProjectValues(map, filename, projectNames, projects);
|
||||
return map;
|
||||
}, invalidImplicitDependencies);
|
||||
|
||||
nxJsonProjects
|
||||
.filter(nxJsonProjectName => {
|
||||
const project = nxJson.projects[nxJsonProjectName];
|
||||
return !!project.implicitDependencies;
|
||||
})
|
||||
.reduce((map, nxJsonProjectName) => {
|
||||
const project = nxJson.projects[nxJsonProjectName];
|
||||
detectAndSetInvalidProjectValues(
|
||||
map,
|
||||
nxJsonProjectName,
|
||||
project.implicitDependencies,
|
||||
projects
|
||||
);
|
||||
return map;
|
||||
}, invalidImplicitDependencies);
|
||||
|
||||
if (invalidImplicitDependencies.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = `The following implicitDependencies specified in nx.json are invalid:
|
||||
`;
|
||||
invalidImplicitDependencies.forEach((projectNames, key) => {
|
||||
const str = ` ${key}
|
||||
${projectNames.map(projectName => ` ${projectName}`).join('\n')}`;
|
||||
message += str;
|
||||
});
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function minus(a: string[], b: string[]): string[] {
|
||||
const res = [];
|
||||
a.forEach(aa => {
|
||||
if (!b.find(bb => bb === aa)) {
|
||||
res.push(aa);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
export function cliCommand() {
|
||||
return workspaceFileName() === 'angular.json' ? 'ng' : 'nx';
|
||||
}
|
||||
|
||||
export function readWorkspaceJson(): any {
|
||||
return readJsonFile(`${appRootPath}/${workspaceFileName()}`);
|
||||
}
|
||||
|
||||
export function workspaceFileName() {
|
||||
const packageJson = readPackageJson();
|
||||
if (
|
||||
packageJson.devDependencies['@angular/cli'] ||
|
||||
packageJson.dependencies['@angular/cli']
|
||||
) {
|
||||
return 'angular.json';
|
||||
} else {
|
||||
return 'workspace.json';
|
||||
}
|
||||
}
|
||||
|
||||
export function readPackageJson(): any {
|
||||
return readJsonFile(`${appRootPath}/package.json`);
|
||||
}
|
||||
|
||||
export function readNxJson(): NxJson {
|
||||
const config = readJsonFile<NxJson>(`${appRootPath}/nx.json`);
|
||||
if (!config.npmScope) {
|
||||
throw new Error(`nx.json must define the npmScope property.`);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function readWorkspaceFiles(): FileData[] {
|
||||
const workspaceJson = readWorkspaceJson();
|
||||
const files = [];
|
||||
|
||||
// Add known workspace files and directories
|
||||
files.push(...allFilesInDir(appRootPath, false));
|
||||
files.push(...allFilesInDir(`${appRootPath}/tools`));
|
||||
|
||||
// Add files for workspace projects
|
||||
Object.keys(workspaceJson.projects).forEach(projectName => {
|
||||
const project = workspaceJson.projects[projectName];
|
||||
files.push(...allFilesInDir(`${appRootPath}/${project.root}`));
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
// TODO: remove it in Nx 10
|
||||
export function getProjectNodes(
|
||||
workspaceJson: any,
|
||||
nxJson: NxJson
|
||||
@ -295,30 +124,7 @@ export function getProjectNodes(
|
||||
return Object.values(graph.nodes);
|
||||
}
|
||||
|
||||
export function normalizedProjectRoot(p: ProjectGraphNode): string {
|
||||
if (p.data && p.data.root) {
|
||||
return p.data.root
|
||||
.split('/')
|
||||
.filter(v => !!v)
|
||||
.slice(1)
|
||||
.join('/');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function getProjectRoots(projectNames: string[]): string[] {
|
||||
const { projects } = readWorkspaceJson();
|
||||
return projectNames.map(name => projects[name].root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time when file was last modified
|
||||
* Returns -Infinity for a non-existent file
|
||||
*/
|
||||
export function mtime(filePath: string): number {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return -Infinity;
|
||||
}
|
||||
return fs.statSync(filePath).mtimeMs;
|
||||
}
|
||||
|
||||
31
packages/workspace/src/command-line/utils.spec.ts
Normal file
31
packages/workspace/src/command-line/utils.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { splitArgsIntoNxArgsAndTargetArgs } from './utils';
|
||||
|
||||
describe('splitArgs', () => {
|
||||
it('should split nx specific arguments into nxArgs', () => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndTargetArgs({
|
||||
files: [''],
|
||||
notNxArg: true,
|
||||
_: ['--override'],
|
||||
$0: ''
|
||||
}).nxArgs
|
||||
).toEqual({
|
||||
projects: [],
|
||||
files: ['']
|
||||
});
|
||||
});
|
||||
|
||||
it('should split non nx specific arguments into target args', () => {
|
||||
expect(
|
||||
splitArgsIntoNxArgsAndTargetArgs({
|
||||
files: [''],
|
||||
notNxArg: true,
|
||||
_: ['--override'],
|
||||
$0: ''
|
||||
}).targetArgs
|
||||
).toEqual({
|
||||
notNxArg: true,
|
||||
override: true
|
||||
});
|
||||
});
|
||||
});
|
||||
92
packages/workspace/src/command-line/utils.ts
Normal file
92
packages/workspace/src/command-line/utils.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import * as yargsParser from 'yargs-parser';
|
||||
import * as yargs from 'yargs';
|
||||
import { WorkspaceResults } from './workspace-results';
|
||||
import { ProjectGraphNode } from '../core/project-graph';
|
||||
import { readWorkspaceJson, readNxJson } from '../core/file-utils';
|
||||
import { NxJson } from '../core/shared-interfaces';
|
||||
|
||||
/**
|
||||
* These options are only for getting an array with properties of AffectedOptions.
|
||||
*
|
||||
* @remark They are not defaults or useful for anything else
|
||||
*/
|
||||
const dummyOptions: NxArgs = {
|
||||
target: '',
|
||||
configuration: '',
|
||||
runner: '',
|
||||
parallel: false,
|
||||
maxParallel: 0,
|
||||
'max-parallel': 0,
|
||||
untracked: false,
|
||||
uncommitted: false,
|
||||
all: false,
|
||||
base: 'base',
|
||||
head: 'head',
|
||||
exclude: ['exclude'],
|
||||
files: [''],
|
||||
onlyFailed: false,
|
||||
'only-failed': false,
|
||||
verbose: false,
|
||||
help: false,
|
||||
version: false,
|
||||
quiet: false,
|
||||
plain: false,
|
||||
withDeps: false,
|
||||
'with-deps': false,
|
||||
projects: []
|
||||
} as any;
|
||||
|
||||
const nxSpecific = Object.keys(dummyOptions);
|
||||
|
||||
export interface NxArgs {
|
||||
target?: string;
|
||||
configuration?: string;
|
||||
runner?: string;
|
||||
parallel?: boolean;
|
||||
maxParallel?: number;
|
||||
'max-parallel'?: number;
|
||||
untracked?: boolean;
|
||||
uncommitted?: boolean;
|
||||
all?: boolean;
|
||||
base?: string;
|
||||
head?: string;
|
||||
exclude?: string[];
|
||||
files?: string[];
|
||||
onlyFailed?: boolean;
|
||||
'only-failed'?: boolean;
|
||||
verbose?: boolean;
|
||||
help?: boolean;
|
||||
version?: boolean;
|
||||
quiet?: boolean;
|
||||
plain?: boolean;
|
||||
withDeps?: boolean;
|
||||
'with-deps'?: boolean;
|
||||
projects?: string[];
|
||||
_: string[];
|
||||
}
|
||||
|
||||
const ignoreArgs = ['$0', '_'];
|
||||
|
||||
export function splitArgsIntoNxArgsAndTargetArgs(
|
||||
args: yargs.Arguments
|
||||
): { nxArgs: NxArgs; targetArgs: yargs.Arguments } {
|
||||
const nxArgs: any = {};
|
||||
const targetArgs = yargsParser(args._);
|
||||
delete targetArgs._;
|
||||
|
||||
Object.entries(args).forEach(([key, value]) => {
|
||||
if (nxSpecific.includes(key as any)) {
|
||||
nxArgs[key] = value;
|
||||
} else if (!ignoreArgs.includes(key)) {
|
||||
targetArgs[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (!nxArgs.projects) {
|
||||
nxArgs.projects = [];
|
||||
} else {
|
||||
nxArgs.projects = args.projects.split(',').map((p: string) => p.trim());
|
||||
}
|
||||
|
||||
return { nxArgs, targetArgs };
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { WorkspaceIntegrityChecks } from './workspace-integrity-checks';
|
||||
import chalk from 'chalk';
|
||||
import { ProjectType, ProjectGraph } from './project-graph';
|
||||
import { ProjectType, ProjectGraph } from '../core/project-graph';
|
||||
import { extname } from 'path';
|
||||
|
||||
describe('WorkspaceIntegrityChecks', () => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { output, CLIErrorMessageConfig } from './output';
|
||||
import { workspaceFileName } from './shared';
|
||||
import { ProjectGraph } from './project-graph';
|
||||
import { output, CLIErrorMessageConfig } from '../utils/output';
|
||||
import { ProjectGraph } from '../core/project-graph';
|
||||
import { workspaceFileName } from '@nrwl/workspace/src/core/file-utils';
|
||||
|
||||
export class WorkspaceIntegrityChecks {
|
||||
constructor(private projectGraph: ProjectGraph, private files: string[]) {}
|
||||
|
||||
@ -3,7 +3,7 @@ import * as fs from 'fs';
|
||||
import { WorkspaceResults } from './workspace-results';
|
||||
import { serializeJson } from '../utils/fileutils';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { output } from './output';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
describe('WorkspacesResults', () => {
|
||||
let results: WorkspaceResults;
|
||||
|
||||
@ -29,7 +29,7 @@ import * as yargsParser from 'yargs-parser';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { detectPackageManager } from '../utils/detect-package-manager';
|
||||
import { fileExists, readJsonFile } from '../utils/fileutils';
|
||||
import { output } from './output';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
const rootDirectory = appRootPath;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { NxJson } from '../shared';
|
||||
import { NxJson } from '../shared-interfaces';
|
||||
|
||||
export interface AffectedProjectGraphContext {
|
||||
workspaceJson: any;
|
||||
@ -4,7 +4,7 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { createProjectGraph } from '../project-graph';
|
||||
import { filterAffected } from './affected-project-graph';
|
||||
import { FileData } from '../file-utils';
|
||||
import { NxJson } from '../shared';
|
||||
import { NxJson } from '../shared-interfaces';
|
||||
|
||||
jest.mock('fs', () => require('memfs').fs);
|
||||
jest.mock('../../utils/app-root', () => ({ appRootPath: '/root' }));
|
||||
@ -1,7 +1,7 @@
|
||||
import { ProjectGraph } from '../project-graph';
|
||||
import { NxJson, readNxJson, readWorkspaceJson } from '../shared';
|
||||
import { FileChange } from '../file-utils';
|
||||
import { FileChange, readNxJson, readWorkspaceJson } from '../file-utils';
|
||||
import { filterAffectedProjects } from './filter-affected-projects';
|
||||
import { NxJson } from '../shared-interfaces';
|
||||
|
||||
export function filterAffected(
|
||||
graph: ProjectGraph,
|
||||
@ -1,4 +1,4 @@
|
||||
import { assertWorkspaceValidity } from './shared';
|
||||
import { assertWorkspaceValidity } from './assert-workspace-validity';
|
||||
|
||||
describe('assertWorkspaceValidity', () => {
|
||||
let mockNxJson: any;
|
||||
92
packages/workspace/src/core/assert-workspace-validity.ts
Normal file
92
packages/workspace/src/core/assert-workspace-validity.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { workspaceFileName } from './file-utils';
|
||||
|
||||
export function assertWorkspaceValidity(workspaceJson, nxJson) {
|
||||
const workspaceJsonProjects = Object.keys(workspaceJson.projects);
|
||||
const nxJsonProjects = Object.keys(nxJson.projects);
|
||||
|
||||
if (minus(workspaceJsonProjects, nxJsonProjects).length > 0) {
|
||||
throw new Error(
|
||||
`${workspaceFileName()} and nx.json are out of sync. The following projects are missing in nx.json: ${minus(
|
||||
workspaceJsonProjects,
|
||||
nxJsonProjects
|
||||
).join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
if (minus(nxJsonProjects, workspaceJsonProjects).length > 0) {
|
||||
throw new Error(
|
||||
`${workspaceFileName()} and nx.json are out of sync. The following projects are missing in ${workspaceFileName()}: ${minus(
|
||||
nxJsonProjects,
|
||||
workspaceJsonProjects
|
||||
).join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const projects = {
|
||||
...workspaceJson.projects,
|
||||
...nxJson.projects
|
||||
};
|
||||
|
||||
const invalidImplicitDependencies = new Map<string, string[]>();
|
||||
|
||||
Object.entries<'*' | string[]>(nxJson.implicitDependencies || {})
|
||||
.filter(([_, val]) => val !== '*') // These are valid since it is calculated
|
||||
.reduce((map, [filename, projectNames]: [string, string[]]) => {
|
||||
detectAndSetInvalidProjectValues(map, filename, projectNames, projects);
|
||||
return map;
|
||||
}, invalidImplicitDependencies);
|
||||
|
||||
nxJsonProjects
|
||||
.filter(nxJsonProjectName => {
|
||||
const project = nxJson.projects[nxJsonProjectName];
|
||||
return !!project.implicitDependencies;
|
||||
})
|
||||
.reduce((map, nxJsonProjectName) => {
|
||||
const project = nxJson.projects[nxJsonProjectName];
|
||||
detectAndSetInvalidProjectValues(
|
||||
map,
|
||||
nxJsonProjectName,
|
||||
project.implicitDependencies,
|
||||
projects
|
||||
);
|
||||
return map;
|
||||
}, invalidImplicitDependencies);
|
||||
|
||||
if (invalidImplicitDependencies.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = `The following implicitDependencies specified in nx.json are invalid:
|
||||
`;
|
||||
invalidImplicitDependencies.forEach((projectNames, key) => {
|
||||
const str = ` ${key}
|
||||
${projectNames.map(projectName => ` ${projectName}`).join('\n')}`;
|
||||
message += str;
|
||||
});
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function detectAndSetInvalidProjectValues(
|
||||
map: Map<string, string[]>,
|
||||
sourceName: string,
|
||||
desiredProjectNames: string[],
|
||||
validProjects: any
|
||||
) {
|
||||
const invalidProjects = desiredProjectNames.filter(
|
||||
projectName => !validProjects[projectName]
|
||||
);
|
||||
if (invalidProjects.length > 0) {
|
||||
map.set(sourceName, invalidProjects);
|
||||
}
|
||||
}
|
||||
|
||||
function minus(a: string[], b: string[]): string[] {
|
||||
const res = [];
|
||||
a.forEach(aa => {
|
||||
if (!b.find(bb => bb === aa)) {
|
||||
res.push(aa);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@ -2,10 +2,14 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { extname } from 'path';
|
||||
import { mtime } from './shared';
|
||||
import { jsonDiff } from '../utils/json-diff';
|
||||
import { readFileSync } from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { Environment, NxJson } from './shared-interfaces';
|
||||
import { ProjectGraphNode } from './project-graph';
|
||||
import { WorkspaceResults } from '../command-line/workspace-results';
|
||||
|
||||
const ignore = require('ignore');
|
||||
|
||||
export interface FileData {
|
||||
@ -59,7 +63,7 @@ export function calculateFileChanges(
|
||||
});
|
||||
}
|
||||
|
||||
const TEN_MEGABYTES = 1024 * 10000;
|
||||
export const TEN_MEGABYTES = 1024 * 10000;
|
||||
|
||||
function defaultReadFileAtRevision(
|
||||
file: string,
|
||||
@ -126,3 +130,83 @@ function getIgnoredGlobs() {
|
||||
function readFileIfExisting(path: string) {
|
||||
return fs.existsSync(path) ? fs.readFileSync(path, 'UTF-8').toString() : '';
|
||||
}
|
||||
|
||||
export function readWorkspaceJson(): any {
|
||||
return readJsonFile(`${appRootPath}/${workspaceFileName()}`);
|
||||
}
|
||||
|
||||
export function cliCommand() {
|
||||
return workspaceFileName() === 'angular.json' ? 'ng' : 'nx';
|
||||
}
|
||||
|
||||
export function workspaceFileName() {
|
||||
const packageJson = readPackageJson();
|
||||
if (
|
||||
packageJson.devDependencies['@angular/cli'] ||
|
||||
packageJson.dependencies['@angular/cli']
|
||||
) {
|
||||
return 'angular.json';
|
||||
} else {
|
||||
return 'workspace.json';
|
||||
}
|
||||
}
|
||||
|
||||
export function readPackageJson(): any {
|
||||
return readJsonFile(`${appRootPath}/package.json`);
|
||||
}
|
||||
|
||||
export function readNxJson(): NxJson {
|
||||
const config = readJsonFile<NxJson>(`${appRootPath}/nx.json`);
|
||||
if (!config.npmScope) {
|
||||
throw new Error(`nx.json must define the npmScope property.`);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function readWorkspaceFiles(): FileData[] {
|
||||
const workspaceJson = readWorkspaceJson();
|
||||
const files = [];
|
||||
|
||||
// Add known workspace files and directories
|
||||
files.push(...allFilesInDir(appRootPath, false));
|
||||
files.push(...allFilesInDir(`${appRootPath}/tools`));
|
||||
|
||||
// Add files for workspace projects
|
||||
Object.keys(workspaceJson.projects).forEach(projectName => {
|
||||
const project = workspaceJson.projects[projectName];
|
||||
files.push(...allFilesInDir(`${appRootPath}/${project.root}`));
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export function readEnvironment(target: string): Environment {
|
||||
const nxJson = readNxJson();
|
||||
const workspaceJson = readWorkspaceJson();
|
||||
const workspace = new WorkspaceResults(target);
|
||||
|
||||
return { nxJson, workspaceJson, workspace };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time when file was last modified
|
||||
* Returns -Infinity for a non-existent file
|
||||
*/
|
||||
export function mtime(filePath: string): number {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return -Infinity;
|
||||
}
|
||||
return fs.statSync(filePath).mtimeMs;
|
||||
}
|
||||
|
||||
export function normalizedProjectRoot(p: ProjectGraphNode): string {
|
||||
if (p.data && p.data.root) {
|
||||
return p.data.root
|
||||
.split('/')
|
||||
.filter(v => !!v)
|
||||
.slice(1)
|
||||
.join('/');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import {
|
||||
ProjectGraphNodeRecords
|
||||
} from '../project-graph-models';
|
||||
import { TypeScriptImportLocator } from './typescript-import-locator';
|
||||
import { normalizedProjectRoot } from '../../shared';
|
||||
import { normalizedProjectRoot } from '../../file-utils';
|
||||
|
||||
export function buildExplicitTypeScriptDependencies(
|
||||
ctx: ProjectGraphContext,
|
||||
@ -1,6 +1,6 @@
|
||||
import { NxJson } from '../shared';
|
||||
import { FileMap } from '../file-graph';
|
||||
import { FileData } from '../file-utils';
|
||||
import { NxJson } from '../shared-interfaces';
|
||||
|
||||
export interface ProjectGraph {
|
||||
nodes: Record<string, ProjectGraphNode>;
|
||||
@ -4,7 +4,7 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { createProjectGraph } from './project-graph';
|
||||
import { DependencyType } from './project-graph-models';
|
||||
import { FileData } from '../file-utils';
|
||||
import { NxJson } from '../shared';
|
||||
import { NxJson } from '../shared-interfaces';
|
||||
|
||||
jest.mock('fs', () => require('memfs').fs);
|
||||
jest.mock('../../utils/app-root', () => ({ appRootPath: '/root' }));
|
||||
@ -1,13 +1,6 @@
|
||||
import { mkdirSync, readFileSync } from 'fs';
|
||||
import { ProjectGraph, ProjectGraphContext } from './project-graph-models';
|
||||
import { ProjectGraphBuilder } from './project-graph-builder';
|
||||
import {
|
||||
assertWorkspaceValidity,
|
||||
mtime,
|
||||
readNxJson,
|
||||
readWorkspaceFiles,
|
||||
readWorkspaceJson
|
||||
} from '../shared';
|
||||
import { appRootPath } from '../../utils/app-root';
|
||||
import {
|
||||
directoryExists,
|
||||
@ -15,7 +8,13 @@ import {
|
||||
readJsonFile,
|
||||
writeJsonFile
|
||||
} from '../../utils/fileutils';
|
||||
import { FileData } from '../file-utils';
|
||||
import {
|
||||
FileData,
|
||||
mtime,
|
||||
readNxJson,
|
||||
readWorkspaceFiles,
|
||||
readWorkspaceJson
|
||||
} from '../file-utils';
|
||||
import { createFileMap, FileMap } from '../file-graph';
|
||||
import { BuildNodes, buildWorkspaceProjectNodes } from './build-nodes';
|
||||
import {
|
||||
@ -23,6 +22,7 @@ import {
|
||||
buildExplicitTypeScriptDependencies,
|
||||
buildImplicitProjectDependencies
|
||||
} from './build-dependencies';
|
||||
import { assertWorkspaceValidity } from '../assert-workspace-validity';
|
||||
|
||||
export function createProjectGraph(
|
||||
workspaceJson = readWorkspaceJson(),
|
||||
26
packages/workspace/src/core/shared-interfaces.ts
Normal file
26
packages/workspace/src/core/shared-interfaces.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export type ImplicitDependencyEntry = { [key: string]: '*' | string[] };
|
||||
|
||||
export interface NxJson {
|
||||
implicitDependencies?: ImplicitDependencyEntry;
|
||||
npmScope: string;
|
||||
projects: {
|
||||
[projectName: string]: NxJsonProjectConfig;
|
||||
};
|
||||
tasksRunnerOptions?: {
|
||||
[tasksRunnerName: string]: {
|
||||
runner: string;
|
||||
options?: unknown;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface NxJsonProjectConfig {
|
||||
implicitDependencies?: string[];
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
nxJson: NxJson;
|
||||
workspaceJson: any;
|
||||
workspace: any;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { output } from '../output';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export interface ReporterArgs {
|
||||
target?: string;
|
||||
@ -2,7 +2,7 @@ import defaultTaskRunner from './default-tasks-runner';
|
||||
import { AffectedEventType, Task } from './tasks-runner';
|
||||
jest.mock('npm-run-all', () => jest.fn());
|
||||
import * as runAll from 'npm-run-all';
|
||||
jest.mock('../command-line/shared', () => ({
|
||||
jest.mock('../core/file-utils', () => ({
|
||||
cliCommand: () => 'nx'
|
||||
}));
|
||||
jest.mock('../utils/fileutils', () => ({
|
||||
|
||||
@ -7,10 +7,10 @@ import {
|
||||
TaskCompleteEvent,
|
||||
TasksRunner
|
||||
} from './tasks-runner';
|
||||
import { cliCommand } from '../command-line/shared';
|
||||
import { output } from '../command-line/output';
|
||||
import { output } from '../utils/output';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { getCommand } from './utils';
|
||||
import { cliCommand } from '../core/file-utils';
|
||||
|
||||
export interface DefaultTasksRunnerOptions {
|
||||
parallel?: boolean;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { NxJson } from '../shared';
|
||||
import { TasksRunner } from '../../tasks-runner/tasks-runner';
|
||||
import defaultTasksRunner from '../../tasks-runner/default-tasks-runner';
|
||||
import { TasksRunner } from './tasks-runner';
|
||||
import defaultTasksRunner from './default-tasks-runner';
|
||||
import { getRunner } from './run-command';
|
||||
import { NxJson } from '../core/shared-interfaces';
|
||||
|
||||
describe('getRunner', () => {
|
||||
let nxJson: NxJson;
|
||||
@ -1,22 +1,18 @@
|
||||
import { NxJson } from '../shared';
|
||||
import {
|
||||
AffectedEventType,
|
||||
Task,
|
||||
TaskCompleteEvent,
|
||||
TasksRunner
|
||||
} from '../../tasks-runner/tasks-runner';
|
||||
import { defaultTasksRunner } from '../../tasks-runner/default-tasks-runner';
|
||||
import { isRelativePath } from '../../utils/fileutils';
|
||||
} from './tasks-runner';
|
||||
import { defaultTasksRunner } from './default-tasks-runner';
|
||||
import { isRelativePath } from '../utils/fileutils';
|
||||
import { join } from 'path';
|
||||
import { appRootPath } from '../../utils/app-root';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import { DefaultReporter, ReporterArgs } from './default-reporter';
|
||||
import {
|
||||
Arguments,
|
||||
Environment,
|
||||
projectHasTargetAndConfiguration
|
||||
} from './utils';
|
||||
import * as yargs from 'yargs';
|
||||
import { ProjectGraph, ProjectGraphNode } from '../project-graph';
|
||||
import { ProjectGraph, ProjectGraphNode } from '../core/project-graph';
|
||||
import { Environment, NxJson } from '../core/shared-interfaces';
|
||||
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
||||
|
||||
export interface TasksMap {
|
||||
[projectName: string]: { [targetName: string]: Task };
|
||||
@ -27,17 +23,18 @@ type RunArgs = yargs.Arguments & ReporterArgs;
|
||||
export function runCommand<T extends RunArgs>(
|
||||
projectsToRun: ProjectGraphNode[],
|
||||
projectGraph: ProjectGraph,
|
||||
{ nxArgs, overrides, targetArgs }: Arguments<T>,
|
||||
{ nxJson, workspace }: Environment
|
||||
{ nxJson, workspace }: Environment,
|
||||
nxArgs: any,
|
||||
targetArgs: any
|
||||
) {
|
||||
const reporter = new DefaultReporter();
|
||||
reporter.beforeRun(projectsToRun.map(p => p.name), nxArgs, overrides);
|
||||
reporter.beforeRun(projectsToRun.map(p => p.name), nxArgs, targetArgs);
|
||||
const tasks: Task[] = projectsToRun.map(project =>
|
||||
createTask({
|
||||
project,
|
||||
target: nxArgs.target,
|
||||
configuration: nxArgs.configuration,
|
||||
overrides: overrides
|
||||
overrides: targetArgs
|
||||
})
|
||||
);
|
||||
|
||||
@ -54,7 +51,7 @@ export function runCommand<T extends RunArgs>(
|
||||
project: project,
|
||||
target: nxArgs.target,
|
||||
configuration: nxArgs.configuration,
|
||||
overrides: overrides
|
||||
overrides: targetArgs
|
||||
})
|
||||
};
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Target } from '@angular-devkit/architect';
|
||||
|
||||
import { ProjectGraph } from '../command-line/project-graph';
|
||||
import { ProjectGraph } from '../core/project-graph';
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Task } from './tasks-runner';
|
||||
import { ProjectGraphNode } from '../command-line/project-graph';
|
||||
import { ProjectGraphNode } from '../core/project-graph';
|
||||
|
||||
const commonCommands = ['build', 'test', 'lint', 'e2e', 'deploy'];
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@ import * as ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { Rule } from './nxEnforceModuleBoundariesRule';
|
||||
import { DependencyType, ProjectGraph } from '../command-line/project-graph';
|
||||
import { ProjectType } from '../command-line/project-graph';
|
||||
import { DependencyType, ProjectGraph } from '../core/project-graph';
|
||||
import { ProjectType } from '../core/project-graph';
|
||||
import { extname } from 'path';
|
||||
|
||||
describe('Enforce Module Boundaries', () => {
|
||||
|
||||
@ -5,12 +5,7 @@ import {
|
||||
createProjectGraph,
|
||||
ProjectGraph,
|
||||
ProjectGraphNode
|
||||
} from '../command-line/project-graph';
|
||||
import {
|
||||
normalizedProjectRoot,
|
||||
readNxJson,
|
||||
readWorkspaceJson
|
||||
} from '../command-line/shared';
|
||||
} from '../core/project-graph';
|
||||
import { appRootPath } from '../utils/app-root';
|
||||
import {
|
||||
DepConstraint,
|
||||
@ -27,7 +22,12 @@ import {
|
||||
onlyLoadChildren
|
||||
} from '../utils/runtime-lint-utils';
|
||||
import { normalize } from '@angular-devkit/core';
|
||||
import { ProjectType } from '../command-line/project-graph';
|
||||
import { ProjectType } from '../core/project-graph';
|
||||
import {
|
||||
normalizedProjectRoot,
|
||||
readNxJson,
|
||||
readWorkspaceJson
|
||||
} from '@nrwl/workspace/src/core/file-utils';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
constructor(
|
||||
|
||||
@ -11,13 +11,10 @@ import * as stripJsonComments from 'strip-json-comments';
|
||||
import { serializeJson } from './fileutils';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { getWorkspacePath } from './cli-config-utils';
|
||||
import {
|
||||
createProjectGraph,
|
||||
ProjectGraph
|
||||
} from '../command-line/project-graph';
|
||||
import { NxJson } from '../command-line/shared';
|
||||
import { FileData } from '../command-line/file-utils';
|
||||
import { createProjectGraph, ProjectGraph } from '../core/project-graph';
|
||||
import { FileData } from '../core/file-utils';
|
||||
import { extname, join, normalize, Path } from '@angular-devkit/core';
|
||||
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
|
||||
|
||||
function nodesByPosition(first: ts.Node, second: ts.Node): number {
|
||||
return first.getStart() - second.getStart();
|
||||
@ -42,7 +39,7 @@ function insertAfterLastOccurrence(
|
||||
}
|
||||
if (!lastItem && fallbackPos == undefined) {
|
||||
throw new Error(
|
||||
`tried to insert ${toInsert} as first occurence with no fallback position`
|
||||
`tried to insert ${toInsert} as first occurrence with no fallback position`
|
||||
);
|
||||
}
|
||||
const lastItemPosition: number = lastItem ? lastItem.getEnd() : fallbackPos;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import { readJsonInTree } from './ast-utils';
|
||||
import { NxJson } from '../command-line/shared';
|
||||
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
|
||||
|
||||
export function getWorkspacePath(host: Tree) {
|
||||
const possibleFiles = ['/workspace.json', '/angular.json', '/.angular.json'];
|
||||
|
||||
@ -12,11 +12,6 @@ export interface CLIWarnMessageConfig {
|
||||
slug?: string;
|
||||
}
|
||||
|
||||
export interface CLILogMessageConfig {
|
||||
title: string;
|
||||
bodyLines?: string[];
|
||||
}
|
||||
|
||||
export interface CLINoteMessageConfig {
|
||||
title: string;
|
||||
bodyLines?: string[];
|
||||
@ -0,0 +1,24 @@
|
||||
import { ProjectGraphNode } from '@nrwl/workspace/src/core/project-graph';
|
||||
|
||||
export function projectHasTargetAndConfiguration(
|
||||
project: ProjectGraphNode,
|
||||
target: string,
|
||||
configuration?: string
|
||||
) {
|
||||
if (
|
||||
!project.data ||
|
||||
!project.data.architect ||
|
||||
!project.data.architect[target]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!configuration) {
|
||||
return !!project.data.architect[target];
|
||||
} else {
|
||||
return (
|
||||
project.data.architect[target].configurations &&
|
||||
project.data.architect[target].configurations[configuration]
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
import * as path from 'path';
|
||||
import { normalizedProjectRoot } from '../command-line/shared';
|
||||
import {
|
||||
DependencyType,
|
||||
ProjectGraphNode,
|
||||
ProjectGraphDependency,
|
||||
ProjectGraph
|
||||
} from '../command-line/project-graph';
|
||||
} from '../core/project-graph';
|
||||
import { normalize } from '@angular-devkit/core';
|
||||
import { FileData } from '../command-line/file-utils';
|
||||
import { FileData, normalizedProjectRoot } from '../core/file-utils';
|
||||
|
||||
export type Deps = { [projectName: string]: ProjectGraphDependency[] };
|
||||
export type DepConstraint = {
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
import {
|
||||
Tree,
|
||||
MergeStrategy,
|
||||
DirEntry,
|
||||
FileEntry,
|
||||
FilePredicate,
|
||||
UpdateRecorder,
|
||||
Action
|
||||
} from '@angular-devkit/schematics';
|
||||
import { NxJson } from '../command-line/shared';
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
import {
|
||||
_test_addWorkspaceFile,
|
||||
WorkspaceFormat
|
||||
} from '@angular-devkit/core/src/workspace/core';
|
||||
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
|
||||
|
||||
export function getFileContent(tree: Tree, path: string): string {
|
||||
const fileEntry = tree.get(path);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user