feat(core): outputs should be able to interpolate project properties
This commit is contained in:
parent
70942802fe
commit
876d4d8a0e
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user