feat(core): support specific project dependency in dependsOn with string syntax (#16674)

This commit is contained in:
Craigory Coppola 2023-05-02 12:12:30 -04:00 committed by GitHub
parent 4be6f7401c
commit e164d50f92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 18 deletions

View File

@ -1,4 +1,5 @@
import { import {
expandDependencyConfigSyntaxSugar,
getOutputsForTargetAndConfiguration, getOutputsForTargetAndConfiguration,
transformLegacyOutputs, transformLegacyOutputs,
validateOutputs, validateOutputs,
@ -397,4 +398,57 @@ describe('utils', () => {
expect(result).toEqual(['{projectRoot}/dist']); expect(result).toEqual(['{projectRoot}/dist']);
} }
}); });
describe('expandDependencyConfigSyntaxSugar', () => {
it('should expand syntax for simple target names', () => {
const result = expandDependencyConfigSyntaxSugar('build', {
dependencies: {},
nodes: {},
});
expect(result).toEqual({
target: 'build',
});
});
it('should expand syntax for simple target names targetting dependencies', () => {
const result = expandDependencyConfigSyntaxSugar('^build', {
dependencies: {},
nodes: {},
});
expect(result).toEqual({
target: 'build',
dependencies: true,
});
});
it('should expand syntax for strings like project:target if project is a valid project', () => {
const result = expandDependencyConfigSyntaxSugar('project:build', {
dependencies: {},
nodes: {
project: {
name: 'project',
type: 'app',
data: {
root: 'libs/project',
files: [],
},
},
},
});
expect(result).toEqual({
target: 'build',
projects: ['project'],
});
});
it('should expand syntax for strings like target:with:colons', () => {
const result = expandDependencyConfigSyntaxSugar('target:with:colons', {
dependencies: {},
nodes: {},
});
expect(result).toEqual({
target: 'target:with:colons',
});
});
});
}); });

View File

@ -15,6 +15,7 @@ import { NxJsonConfiguration } from '../config/nx-json';
import { joinPathFragments } from '../utils/path'; import { joinPathFragments } from '../utils/path';
import { isRelativePath } from '../utils/fileutils'; import { isRelativePath } from '../utils/fileutils';
import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides-into-command-line'; import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides-into-command-line';
import { splitByColons, splitTarget } from '../utils/split-target';
export function getCommandAsString(execCommand: string, task: Task) { export function getCommandAsString(execCommand: string, task: Task) {
const args = getPrintableCommandArgsForTask(task); const args = getPrintableCommandArgsForTask(task);
@ -26,10 +27,14 @@ export function getDependencyConfigs(
defaultDependencyConfigs: Record<string, (TargetDependencyConfig | string)[]>, defaultDependencyConfigs: Record<string, (TargetDependencyConfig | string)[]>,
projectGraph: ProjectGraph projectGraph: ProjectGraph
): TargetDependencyConfig[] | undefined { ): TargetDependencyConfig[] | undefined {
const dependencyConfigs = expandDependencyConfigSyntaxSugar( const dependencyConfigs = (
projectGraph.nodes[project].data?.targets[target]?.dependsOn ?? projectGraph.nodes[project].data?.targets[target]?.dependsOn ??
defaultDependencyConfigs[target] ?? defaultDependencyConfigs[target] ??
[] []
).map((config) =>
typeof config === 'string'
? expandDependencyConfigSyntaxSugar(config, projectGraph)
: config
); );
for (const dependencyConfig of dependencyConfigs) { for (const dependencyConfig of dependencyConfigs) {
const specifiers = const specifiers =
@ -64,20 +69,32 @@ export function getDependencyConfigs(
return dependencyConfigs; return dependencyConfigs;
} }
function expandDependencyConfigSyntaxSugar( export function expandDependencyConfigSyntaxSugar(
deps: (TargetDependencyConfig | string)[] dependencyConfigString: string,
): TargetDependencyConfig[] { graph: ProjectGraph
return deps.map((d) => { ): TargetDependencyConfig {
if (typeof d === 'string') { const [dependencies, targetString] = dependencyConfigString.startsWith('^')
if (d.startsWith('^')) { ? [true, dependencyConfigString.substring(1)]
return { dependencies: true, target: d.substring(1) }; : [false, dependencyConfigString];
} else {
return { target: d }; // Support for `project:target` syntax doesn't make sense for
} // dependencies, so we only support `target` syntax for dependencies.
} else { if (dependencies) {
return d; return {
} target: targetString,
}); dependencies: true,
};
}
// Support for both `project:target` and `target:with:colons` syntax
const [maybeProject, ...segments] = splitByColons(targetString);
return {
// Only the first segment could be a project. If it is, the rest is a target.
// If its not, then the whole targetString was a target with colons in its name.
target: maybeProject in graph.nodes ? segments.join(':') : targetString,
// If the first segment is a project, then we have a specific project. Otherwise, we don't.
projects: maybeProject in graph.nodes ? [maybeProject] : undefined,
};
} }
export function getOutputs( export function getOutputs(

View File

@ -37,7 +37,7 @@ function groupJointSegments(segments: string[], validTargetNames: Set<string>) {
return segments; return segments;
} }
function splitByColons(s: string) { export function splitByColons(s: string) {
const parts = [] as string[]; const parts = [] as string[];
let currentPart = ''; let currentPart = '';
for (let i = 0; i < s.length; ++i) { for (let i = 0; i < s.length; ++i) {