fix(nextjs): default to build target for incremental serve

feat(core): support exclude with run-many
This commit is contained in:
vsavkin 2021-04-28 09:40:20 -04:00 committed by Victor Savkin
parent ffa879c8bb
commit d767261fea
17 changed files with 181 additions and 132 deletions

View File

@ -212,7 +212,11 @@ export function runCommandAsync(
command,
{
cwd: tmpProjPath(),
env: { ...(opts.env || process.env), FORCE_COLOR: 'false' },
env: {
...(opts.env || process.env),
FORCE_COLOR: 'false',
NX_INVOKED_BY_RUNNER: undefined,
},
},
(err, stdout, stderr) => {
if (!opts.silenceError && err) {
@ -231,7 +235,11 @@ export function runCommandUntil(
const pm = getPackageManagerCommand();
const p = exec(`${pm.runNx} ${command}`, {
cwd: tmpProjPath(),
env: { ...process.env, FORCE_COLOR: 'false' },
env: {
...process.env,
FORCE_COLOR: 'false',
NX_INVOKED_BY_RUNNER: undefined,
},
});
return new Promise((res, rej) => {
let output = '';
@ -286,7 +294,7 @@ export function runNgAdd(
`./node_modules/.bin/ng g @nrwl/workspace:ng-add ${command}`,
{
cwd: tmpProjPath(),
env: opts.env || process.env,
env: { ...(opts.env || process.env), NX_INVOKED_BY_RUNNER: undefined },
}
)
.toString()
@ -315,7 +323,7 @@ export function runCLI(
const pm = getPackageManagerCommand();
let r = execSync(`${pm.runNx} ${command}`, {
cwd: opts.cwd || tmpProjPath(),
env: opts.env || process.env,
env: { ...(opts.env || process.env), NX_INVOKED_BY_RUNNER: undefined },
}).toString();
r = r.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
@ -353,7 +361,11 @@ export function runCommand(command: string): string {
const r = execSync(command, {
cwd: tmpProjPath(),
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, FORCE_COLOR: 'false' },
env: {
...process.env,
FORCE_COLOR: 'false',
NX_INVOKED_BY_RUNNER: undefined,
},
}).toString();
if (process.env.VERBOSE_OUTPUT) {
console.log(r);

View File

@ -53,10 +53,11 @@ if (localNx === require.resolve('@nrwl/cli/bin/nx.js')) {
output.warn({
title: `Running global Nx CLI with PNPM may have issues.`,
bodyLines: [
`Prefer to use "pnpm run" (https://pnpm.js.org/en/cli/run') to execute commands in this workspace.`,
`Prefer to use "pnpm run" (https://pnpm.js.org/en/cli/run) to execute commands in this workspace.`,
`${chalk.reset.inverse.bold.cyan(
' TIP '
)} create a shortcut such as: ${chalk.bold.white(tip)}`,
``,
],
});
}

View File

@ -29,7 +29,7 @@ export default async function exportExecutor(
projGraph,
context.root,
context.projectName,
context.targetName,
'build', // this should be generalized
context.configurationName
);

View File

@ -51,7 +51,7 @@ export default async function* serveExecutor(
projGraph,
context.root,
context.projectName,
context.targetName,
'build', // should be generalized
context.configurationName
);

View File

@ -1,72 +0,0 @@
import * as normalizeUtils from '../../utils/normalize';
import { WebBuildBuilderOptions } from '../build/build.impl';
import webDevServerImpl, { WebDevServerOptions } from './dev-server.impl';
jest.mock('@nrwl/devkit');
import { readTargetOptions, ExecutorContext } from '@nrwl/devkit';
jest.mock('../../utils/devserver.config', () => ({
getDevServerConfig: jest.fn().mockReturnValue({}),
}));
describe('Web Server Builder', () => {
let context: ExecutorContext;
let options: WebDevServerOptions;
beforeEach(async () => {
jest.clearAllMocks();
context = {
root: '/root',
cwd: '/root',
projectName: 'proj',
targetName: 'serve',
workspace: {
version: 2,
projects: {
proj: {
root: 'proj',
sourceRoot: 'proj/src',
targets: {
serve: {
executor: '@nrwl/web:dev-server',
options: {
buildTarget: 'proj:build',
},
},
build: {
executor: 'build',
options: {},
},
},
},
},
},
isVerbose: false,
};
options = {
buildTarget: 'proj:build',
port: 4200,
} as WebDevServerOptions;
(readTargetOptions as any).mockImplementation(() => {});
jest
.spyOn(normalizeUtils, 'normalizeWebBuildOptions')
.mockReturnValue({} as WebBuildBuilderOptions);
});
it('should pass `baseHref` to build', async () => {
const baseHref = '/my-domain';
await webDevServerImpl({ ...options, baseHref }, context);
expect(normalizeUtils.normalizeWebBuildOptions).toHaveBeenCalledWith(
expect.objectContaining({
baseHref,
}),
'/root',
'proj/src'
);
});
});

View File

@ -2,6 +2,7 @@ import {
ExecutorContext,
parseTargetString,
readTargetOptions,
joinPathFragments,
} from '@nrwl/devkit';
import { Configuration } from 'webpack';
@ -17,6 +18,11 @@ import {
import { normalizeWebBuildOptions } from '../../utils/normalize';
import { WebBuildBuilderOptions } from '../build/build.impl';
import { getDevServerConfig } from '../../utils/devserver.config';
import {
calculateProjectDependencies,
createTmpTsConfig,
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
export interface WebDevServerOptions {
host: string;
@ -59,6 +65,23 @@ export default function devServerExecutor(
});
}
if (!buildOptions.buildLibsFromSource) {
const projGraph = createProjectGraph();
const { target, dependencies } = calculateProjectDependencies(
projGraph,
context.root,
context.projectName,
'build', // should be generalized
context.configurationName
);
buildOptions.tsConfig = createTmpTsConfig(
joinPathFragments(context.root, buildOptions.tsConfig),
context.root,
target.data.root,
dependencies
);
}
return eachValueFrom(
runWebpackDevServer(webpackConfig, webpack, WebpackDevServer).pipe(
tap(({ stats }) => {

View File

@ -4,6 +4,7 @@ import { calculateFileChanges, readEnvironment } from '../core/file-utils';
import {
createProjectGraph,
onlyWorkspaceProjects,
ProjectGraph,
ProjectGraphNode,
ProjectType,
withDeps,
@ -18,6 +19,8 @@ import { connectToNxCloudUsingScan } from './connect-to-nx-cloud';
import { parseFiles } from './shared';
import { NxArgs, RawNxArgs, splitArgsIntoNxArgsAndOverrides } from './utils';
import { performance } from 'perf_hooks';
import { Environment } from '@nrwl/workspace/src/core/shared-interfaces';
import { EmptyReporter } from '@nrwl/workspace/src/tasks-runner/empty-reporter';
export async function affected(
command: 'apps' | 'libs' | 'dep-graph' | 'print-affected' | 'affected',
@ -35,33 +38,15 @@ export async function affected(
await connectToNxCloudUsingScan(nxArgs.scan);
const projectGraph = createProjectGraph();
let affectedGraph = nxArgs.all
? projectGraph
: filterAffected(
projectGraph,
calculateFileChanges(parseFiles(nxArgs).files, nxArgs)
);
if (parsedArgs.withDeps) {
affectedGraph = onlyWorkspaceProjects(
withDeps(projectGraph, Object.values(affectedGraph.nodes))
);
}
const projects = parsedArgs.all ? projectGraph.nodes : affectedGraph.nodes;
const projectsNotExcluded = Object.keys(projects)
.filter((key) => !parsedArgs.exclude.includes(key))
.reduce((p, key) => {
p[key] = projects[key];
return p;
}, {} as Record<string, ProjectGraphNode>);
const projects = projectsToRun(nxArgs, projectGraph);
const projectsNotExcluded = applyExclude(projects, nxArgs);
const env = readEnvironment(nxArgs.target, projectsNotExcluded);
const affectedProjects = Object.values(projectsNotExcluded).filter(
(n) => !parsedArgs.onlyFailed || !env.workspaceResults.getResult(n.name)
);
const filteredProjects = applyOnlyFailed(projectsNotExcluded, nxArgs, env);
try {
switch (command) {
case 'apps':
const apps = affectedProjects
const apps = filteredProjects
.filter((p) => p.type === ProjectType.app)
.map((p) => p.name);
if (parsedArgs.plain) {
@ -77,7 +62,7 @@ export async function affected(
break;
case 'libs':
const libs = affectedProjects
const libs = filteredProjects
.filter((p) => p.type === ProjectType.lib)
.map((p) => p.name);
if (parsedArgs.plain) {
@ -93,19 +78,19 @@ export async function affected(
break;
case 'dep-graph':
const projectNames = affectedProjects.map((p) => p.name);
const projectNames = filteredProjects.map((p) => p.name);
generateGraph(parsedArgs as any, projectNames);
break;
case 'print-affected':
if (nxArgs.target) {
const projectsWithTarget = allProjectsWithTarget(
affectedProjects,
filteredProjects,
nxArgs
);
printAffected(
projectsWithTarget,
affectedProjects,
filteredProjects,
projectGraph,
env,
nxArgs,
@ -114,7 +99,7 @@ export async function affected(
} else {
printAffected(
[],
affectedProjects,
filteredProjects,
projectGraph,
env,
nxArgs,
@ -125,7 +110,7 @@ export async function affected(
case 'affected': {
const projectsWithTarget = allProjectsWithTarget(
affectedProjects,
filteredProjects,
nxArgs
);
runCommand(
@ -134,7 +119,7 @@ export async function affected(
env,
nxArgs,
overrides,
new DefaultReporter(),
nxArgs.hideCachedOutput ? new EmptyReporter() : new DefaultReporter(),
null
);
break;
@ -146,6 +131,45 @@ export async function affected(
}
}
function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
if (nxArgs.all) return projectGraph.nodes;
let affectedGraph = nxArgs.all
? projectGraph
: filterAffected(
projectGraph,
calculateFileChanges(parseFiles(nxArgs).files, nxArgs)
);
if (nxArgs.withDeps) {
affectedGraph = onlyWorkspaceProjects(
withDeps(projectGraph, Object.values(affectedGraph.nodes))
);
}
return affectedGraph.nodes;
}
function applyExclude(
projects: Record<string, ProjectGraphNode<any>>,
nxArgs: NxArgs
) {
return Object.keys(projects)
.filter((key) => !(nxArgs.exclude || []).includes(key))
.reduce((p, key) => {
p[key] = projects[key];
return p;
}, {} as Record<string, ProjectGraphNode>);
}
function applyOnlyFailed(
projectsNotExcluded: Record<string, ProjectGraphNode<any>>,
nxArgs: NxArgs,
env: Environment
) {
return Object.values(projectsNotExcluded).filter(
(n) => !nxArgs.onlyFailed || !env.workspaceResults.getResult(n.name)
);
}
function allProjectsWithTarget(projects: ProjectGraphNode[], nxArgs: NxArgs) {
return projects.filter((p) => projectHasTarget(p, nxArgs.target));
}

View File

@ -356,6 +356,12 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv {
type: 'boolean',
default: false,
})
.option('exclude', {
describe: 'Exclude certain projects from being processed',
type: 'array',
coerce: parseCSV,
default: [],
})
.option('verbose', {
describe: 'Print additional error stack trace on failure',
})

View File

@ -1,6 +1,6 @@
import * as yargs from 'yargs';
import { runCommand } from '../tasks-runner/run-command';
import { NxArgs, splitArgsIntoNxArgsAndOverrides } from './utils';
import { NxArgs, RawNxArgs, splitArgsIntoNxArgsAndOverrides } from './utils';
import {
createProjectGraph,
isWorkspaceProject,
@ -10,12 +10,14 @@ import {
} from '../core/project-graph';
import { readEnvironment } from '../core/file-utils';
import { DefaultReporter } from '../tasks-runner/default-reporter';
import { EmptyReporter } from '../tasks-runner/empty-reporter';
import { projectHasTarget } from '../utilities/project-graph-utils';
import { output } from '../utilities/output';
import { connectToNxCloudUsingScan } from './connect-to-nx-cloud';
import { performance } from 'perf_hooks';
import { Environment } from '../core/shared-interfaces';
export async function runMany(parsedArgs: yargs.Arguments) {
export async function runMany(parsedArgs: yargs.Arguments & RawNxArgs) {
performance.mark('command-execution-begins');
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(
parsedArgs,
@ -26,21 +28,17 @@ export async function runMany(parsedArgs: yargs.Arguments) {
const projectGraph = createProjectGraph();
const projects = projectsToRun(nxArgs, projectGraph);
const projectMap: Record<string, ProjectGraphNode> = {};
projects.forEach((proj) => {
projectMap[proj.name] = proj;
});
const env = readEnvironment(nxArgs.target, projectMap);
const filteredProjects = Object.values(projects).filter(
(n) => !parsedArgs.onlyFailed || !env.workspaceResults.getResult(n.name)
);
const projectsNotExcluded = applyExclude(projects, nxArgs);
const env = readEnvironment(nxArgs.target, projectsNotExcluded);
const filteredProjects = applyOnlyFailed(projectsNotExcluded, nxArgs, env);
runCommand(
filteredProjects,
projectGraph,
env,
nxArgs,
overrides,
new DefaultReporter(),
nxArgs.hideCachedOutput ? new EmptyReporter() : new DefaultReporter(),
null
);
}
@ -48,7 +46,10 @@ export async function runMany(parsedArgs: yargs.Arguments) {
function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
const allProjects = Object.values(projectGraph.nodes);
if (nxArgs.all) {
return runnableForTarget(allProjects, nxArgs.target);
return runnableForTarget(allProjects, nxArgs.target).reduce(
(m, c) => ((m[c.name] = c), m),
{}
);
} else {
checkForInvalidProjects(nxArgs, allProjects);
let selectedProjects = allProjects.filter(
@ -59,10 +60,35 @@ function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
withDeps(projectGraph, selectedProjects).nodes
);
}
return runnableForTarget(selectedProjects, nxArgs.target, true);
return runnableForTarget(selectedProjects, nxArgs.target, true).reduce(
(m, c) => ((m[c.name] = c), m),
{}
);
}
}
function applyExclude(
projects: Record<string, ProjectGraphNode>,
nxArgs: NxArgs
) {
return Object.keys(projects)
.filter((key) => !(nxArgs.exclude || []).includes(key))
.reduce((p, key) => {
p[key] = projects[key];
return p;
}, {} as Record<string, ProjectGraphNode>);
}
function applyOnlyFailed(
projectsNotExcluded: Record<string, ProjectGraphNode<any>>,
nxArgs: NxArgs,
env: Environment
) {
return Object.values(projectsNotExcluded).filter(
(n) => !nxArgs.onlyFailed || !env.workspaceResults.getResult(n.name)
);
}
function checkForInvalidProjects(
nxArgs: NxArgs,
allProjects: ProjectGraphNode[]

View File

@ -18,6 +18,7 @@ const runOne = [
'with-deps',
'skip-nx-cache',
'scan',
'hide-cached-output',
];
const runMany = [...runOne, 'projects', 'quiet', 'all', 'verbose'];
@ -67,6 +68,8 @@ export interface NxArgs {
select?: string;
skipNxCache?: boolean;
'skip-nx-cache'?: boolean;
'hide-cached-output'?: boolean;
hideCachedOutput?: boolean;
scan?: boolean;
}

View File

@ -50,6 +50,7 @@ export const defaultTasksRunner: TasksRunner<DefaultTasksRunnerOptions> = (
initiatingProject?: string;
projectGraph: ProjectGraph;
nxJson: NxJson;
hideCachedOutput?: boolean;
}
): Observable<TaskCompleteEvent> => {
if (!options.lifeCycle) {
@ -79,6 +80,7 @@ async function runAllTasks(
initiatingProject?: string;
projectGraph: ProjectGraph;
nxJson: NxJson;
hideCachedOutput?: boolean;
}
): Promise<Array<{ task: Task; type: any; success: boolean }>> {
const defaultTargetDependencies = getDefaultDependencyConfigs(
@ -93,7 +95,8 @@ async function runAllTasks(
const orchestrator = new TaskOrchestrator(
context.initiatingProject,
context.projectGraph,
options
options,
context.hideCachedOutput
);
const res = [];

View File

@ -0,0 +1,20 @@
import { Task } from './tasks-runner';
import { Reporter, ReporterArgs } from './reporter';
export class EmptyReporter implements Reporter {
beforeRun(
projectNames: string[],
tasks: Task[],
args: ReporterArgs,
taskOverrides: any
) {}
printResults(
args: ReporterArgs,
failedProjectNames: string[],
startedWithFailedProjects: boolean,
tasks: Task[],
failedTasks: Task[],
cachedTasks: Task[]
) {}
}

View File

@ -65,6 +65,7 @@ export async function runCommand<T extends RunArgs>(
target: nxArgs.target,
projectGraph,
nxJson,
hideCachedOutput: nxArgs.hideCachedOutput,
}).subscribe({
next: (event: any) => {
switch (event.type) {

View File

@ -12,8 +12,7 @@ export class RunOneReporter implements Reporter {
args: ReporterArgs,
taskOverrides: any
) {
// Silent for a single task
if (tasks.length === 1 && process.env.NX_INVOKED_BY_RUNNER) {
if (process.env.NX_INVOKED_BY_RUNNER) {
return;
}
this.projectNames = projectNames;
@ -43,7 +42,7 @@ export class RunOneReporter implements Reporter {
cachedTasks: Task[]
) {
// Silent for a single task
if (tasks.length === 1 && process.env.NX_INVOKED_BY_RUNNER) {
if (process.env.NX_INVOKED_BY_RUNNER) {
return;
}
output.addNewline();

View File

@ -20,7 +20,8 @@ export class TaskOrchestrator {
constructor(
private readonly initiatingProject: string | undefined,
private readonly projectGraph: ProjectGraph,
private readonly options: DefaultTasksRunnerOptions
private readonly options: DefaultTasksRunnerOptions,
private readonly hideCachedOutput: boolean
) {
this.setupOnProcessExitListener();
}
@ -125,8 +126,9 @@ export class TaskOrchestrator {
}
if (
!this.initiatingProject ||
this.initiatingProject === t.task.target.project
(!this.initiatingProject ||
this.initiatingProject === t.task.target.project) &&
!this.hideCachedOutput
) {
const args = this.getCommandArgs(t.task);
output.logCommand(

View File

@ -40,5 +40,6 @@ export type TasksRunner<T = unknown> = (
initiatingProject?: string | null;
projectGraph: ProjectGraph;
nxJson: NxJson;
hideCachedOutput?: boolean;
}
) => Observable<AffectedEvent>;

View File

@ -114,10 +114,10 @@ function readTsConfigWithRemappedPaths(
if (process.env.NX_VERBOSE_LOGGING === 'true') {
output.log({
title: 'TypeScript path mappings have been rewritten.',
bodyLines: [
JSON.stringify(generatedTsConfig.compilerOptions.paths, null, 2),
],
});
console.log(
JSON.stringify(generatedTsConfig.compilerOptions.paths, null, 2)
);
}
return generatedTsConfig;
}