feat(core): outputs should be able to interpolate project properties

This commit is contained in:
Victor Savkin 2022-06-02 16:09:59 -04:00
parent 70942802fe
commit 876d4d8a0e
6 changed files with 89 additions and 22 deletions

View File

@ -17,6 +17,12 @@
"version": "14.2.0-beta.0", "version": "14.2.0-beta.0",
"description": "Remove default collection from configuration to switch to prompts for collection", "description": "Remove default collection from configuration to switch to prompts for collection",
"factory": "./src/migrations/update-14-2-0/remove-default-collection" "factory": "./src/migrations/update-14-2-0/remove-default-collection"
},
"14-2-0-replace-relative-outputs-with-absolute": {
"cli": "nx",
"version": "14.2.0-beta.5",
"description": "Replace all ./ and ../ in outputs with absolute paths",
"factory": "./src/migrations/update-14-2-0/replace-all-relative-outputs-with-absolute"
} }
} }
} }

View File

@ -50,12 +50,14 @@ function projectsToRun(
const allProjects = Object.values(projectGraph.nodes); const allProjects = Object.values(projectGraph.nodes);
const excludedProjects = new Set(nxArgs.exclude ?? []); const excludedProjects = new Set(nxArgs.exclude ?? []);
if (nxArgs.all) { if (nxArgs.all) {
return runnableForTarget(allProjects, nxArgs.target).filter( const res = runnableForTarget(allProjects, nxArgs.target).filter(
(proj) => !excludedProjects.has(proj.name) (proj) => !excludedProjects.has(proj.name)
); );
res.sort((a, b) => a.name.localeCompare(b.name));
return res;
} }
checkForInvalidProjects(nxArgs, allProjects); checkForInvalidProjects(nxArgs, allProjects);
let selectedProjects = nxArgs.projects.map((name) => const selectedProjects = nxArgs.projects.map((name) =>
allProjects.find((project) => project.name === name) allProjects.find((project) => project.name === name)
); );
return runnableForTarget(selectedProjects, nxArgs.target, true).filter( return runnableForTarget(selectedProjects, nxArgs.target, true).filter(

View File

@ -0,0 +1,20 @@
import { Tree } from '../../generators/tree';
import {
getProjects,
updateProjectConfiguration,
} from '../../generators/utils/project-configuration';
import { isRelativePath } from 'nx/src/utils/fileutils';
import { joinPathFragments } from 'nx/src/utils/path';
export default async function (tree: Tree) {
for (const [name, value] of getProjects(tree).entries()) {
for (const t of Object.values(value.targets)) {
if (t.outputs) {
t.outputs = t.outputs.map((o) =>
isRelativePath(o) ? joinPathFragments(value.root, o) : o
);
}
}
updateProjectConfiguration(tree, name, value);
}
}

View File

@ -1,6 +1,6 @@
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph'; import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
import { TargetDependencyConfig } from '../config/workspace-json-project-json'; import { TargetDependencyConfig } from '../config/workspace-json-project-json';
import { getDependencyConfigs } from './utils'; import { getDependencyConfigs, interpolate } from './utils';
import { import {
projectHasTarget, projectHasTarget,
projectHasTargetAndConfiguration, projectHasTargetAndConfiguration,
@ -34,7 +34,6 @@ export class ProcessTasks {
configuration configuration
); );
const id = this.getId(projectName, target, resolvedConfiguration); const id = this.getId(projectName, target, resolvedConfiguration);
debugger;
const task = this.createTask( const task = this.createTask(
id, id,
this.projectGraph.nodes[projectName], this.projectGraph.nodes[projectName],
@ -219,23 +218,14 @@ export function createTaskGraph(
function interpolateOverrides<T = any>( function interpolateOverrides<T = any>(
args: T, args: T,
projectName: string, projectName: string,
projectMetadata: any project: any
): T { ): T {
const interpolatedArgs: T = { ...args }; const interpolatedArgs: T = { ...args };
Object.entries(interpolatedArgs).forEach(([name, value]) => { Object.entries(interpolatedArgs).forEach(([name, value]) => {
if (typeof value === 'string') { interpolatedArgs[name] =
const regex = /{project\.([^}]+)}/g; typeof value === 'string'
interpolatedArgs[name] = value.replace(regex, (_, group: string) => { ? interpolate(value, { project: { ...project, name: projectName } })
if (group.includes('.')) { : value;
throw new Error('Only top-level properties can be interpolated');
}
if (group === 'name') {
return projectName;
}
return projectMetadata[group];
});
}
}); });
return interpolatedArgs; return interpolatedArgs;
} }

View File

@ -17,6 +17,7 @@ describe('utils', () => {
name: 'myapp', name: 'myapp',
type: 'app', type: 'app',
data: { data: {
root: '/myapp',
targets: { targets: {
build: { ...build, executor: '' }, build: { ...build, executor: '' },
}, },
@ -37,6 +38,17 @@ describe('utils', () => {
).toEqual(['one', 'two']); ).toEqual(['one', 'two']);
}); });
it('should return them', () => {
expect(
getOutputsForTargetAndConfiguration(
task,
getNode({
outputs: ['one', 'two'],
})
)
).toEqual(['one', 'two']);
});
it('should support interpolation based on options', () => { it('should support interpolation based on options', () => {
expect( expect(
getOutputsForTargetAndConfiguration( getOutputsForTargetAndConfiguration(
@ -51,6 +63,34 @@ describe('utils', () => {
).toEqual(['path/one', 'two']); ).toEqual(['path/one', 'two']);
}); });
it('should support interpolating root', () => {
expect(
getOutputsForTargetAndConfiguration(
task,
getNode({
outputs: ['{project.root}/sub', 'two'],
options: {
myVar: 'one',
},
})
)
).toEqual(['/myapp/sub', 'two']);
});
it('should support relative paths', () => {
expect(
getOutputsForTargetAndConfiguration(
task,
getNode({
outputs: ['./sub', 'two'],
options: {
myVar: 'one',
},
})
)
).toEqual(['/myapp/sub', 'two']);
});
it('should support nested interpolation based on options', () => { it('should support nested interpolation based on options', () => {
expect( expect(
getOutputsForTargetAndConfiguration( getOutputsForTargetAndConfiguration(

View File

@ -13,6 +13,8 @@ import { getPackageManagerCommand } from '../utils/package-manager';
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph'; import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
import { TargetDependencyConfig } from '../config/workspace-json-project-json'; import { TargetDependencyConfig } from '../config/workspace-json-project-json';
import { workspaceRoot } from '../utils/workspace-root'; import { workspaceRoot } from '../utils/workspace-root';
import { isRelativePath } from 'nx/src/utils/fileutils';
import { joinPathFragments } from 'nx/src/utils/path';
export function getCommandAsString(task: Task) { export function getCommandAsString(task: Task) {
const execCommand = getPackageManagerCommand().exec; const execCommand = getPackageManagerCommand().exec;
@ -88,7 +90,15 @@ export function getOutputsForTargetAndConfiguration(
if (targets?.outputs) { if (targets?.outputs) {
return targets.outputs return targets.outputs
.map((output: string) => interpolateOutputs(output, options)) .map((output: string) => {
const interpolated = interpolate(output, {
options,
project: { ...node.data, name: node.name },
});
return isRelativePath(interpolated)
? joinPathFragments(node.data.root, interpolated)
: interpolated;
})
.filter((output) => !!output); .filter((output) => !!output);
} }
@ -150,17 +160,16 @@ function stringShouldBeWrappedIntoQuotes(str: string) {
return str.includes(' ') || str.includes('{') || str.includes('"'); return str.includes(' ') || str.includes('{') || str.includes('"');
} }
function interpolateOutputs(template: string, data: any): string { export function interpolate(template: string, data: any): string {
return template.replace(/{([\s\S]+?)}/g, (match: string) => { return template.replace(/{([\s\S]+?)}/g, (match: string) => {
let value = data; let value = data;
let path = match.slice(1, -1).trim().split('.').slice(1); let path = match.slice(1, -1).trim().split('.');
for (let idx = 0; idx < path.length; idx++) { for (let idx = 0; idx < path.length; idx++) {
if (!value[path[idx]]) { if (!value[path[idx]]) {
return; return;
} }
value = value[path[idx]]; value = value[path[idx]];
} }
return value; return value;
}); });
} }