feat(core): merge more target options from targetDefaults (#12435)
Fixes https://github.com/nrwl/nx/issues/12433
This commit is contained in:
parent
66a26583da
commit
c783ac5e9a
@ -113,6 +113,13 @@ In this case Nx will use the right `production` input for each project.
|
||||
|
||||
### Target Defaults
|
||||
|
||||
Target defaults provide ways to set common options for a particular target in your workspace. When building your project's configuration, we merge it with up to 1 default from this map. For a given target, we look at its name and its executor. We then check target defaults for any of the following combinations:
|
||||
|
||||
- `` `${executor}` ``
|
||||
- `` `${targetName}` ``
|
||||
|
||||
Whichever of these we find first, we use as the base for that target's configuration. Some common scenarios for this follow.
|
||||
|
||||
Targets can depend on other targets. A common scenario is having to build dependencies of a project first before
|
||||
building the project. The `dependsOn` property in `project.json` can be used to define the list of dependencies of an
|
||||
individual target.
|
||||
@ -149,6 +156,35 @@ Another target default you can configure is `outputs`:
|
||||
}
|
||||
```
|
||||
|
||||
When defining any options or configurations inside of a target default, you may use the `{workspaceRoot}` and `{projectRoot}` tokens. This is useful for defining things like the outputPath or tsconfig for many build targets.
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
"targetDefaults": {
|
||||
"@nrwl/js:tsc": {
|
||||
"options": {
|
||||
"main": "{projectRoot}/src/index.ts"
|
||||
},
|
||||
"configurations": {
|
||||
"prod": {
|
||||
"tsconfig": "{projectRoot}/tsconfig.prod.json"
|
||||
}
|
||||
},
|
||||
"inputs": ["prod"],
|
||||
"outputs": ["{workspaceRoot}/{projectRoot}"]
|
||||
},
|
||||
"build": {
|
||||
"inputs": ["prod"],
|
||||
"outputs": ["{workspaceRoot}/{projectRoot}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% callout type="note" title="Target Default Priority" %}
|
||||
Note that the inputs and outputs are respecified on the @nrwl/js:tsc default configuration. This is **required**, as when reading target defaults Nx will only ever look at one key. If there is a default configuration based on the executor used, it will be read first. If not, Nx will fall back to looking at the configuration based on target name. For instance, running `nx build project` will read the options from `targetDefaults[@nrwl/js:tsc]` if the target configuration for build uses the @nrwl/js:tsc executor. It **would not** read any of the configuration from the `build` target default configuration unless the executor does not match.
|
||||
{% /callout %}
|
||||
|
||||
### Generators
|
||||
|
||||
Default generator options are configured in `nx.json` as well. For instance, the following tells Nx to always
|
||||
@ -174,7 +210,7 @@ named "default" is used by default. Specify a different one like this `nx run-ma
|
||||
Tasks runners can accept different options. The following are the options supported
|
||||
by `"nx/tasks-runners/default"` and `"@nrwl/nx-cloud"`.
|
||||
|
||||
| Property | Descrtipion |
|
||||
| Property | Description |
|
||||
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| cacheableOperations | defines the list of targets/operations that are cached by Nx |
|
||||
| parallel | defines the max number of targets ran in parallel (in older versions of Nx you had to pass `--parallel --maxParallel=3` instead of `--parallel=3`) |
|
||||
|
||||
@ -254,6 +254,69 @@ describe('Nx Running Tests', () => {
|
||||
);
|
||||
}, 10000);
|
||||
|
||||
describe('target defaults + executor specifications', () => {
|
||||
it('should be able to run targets with unspecified executor given an appropriate targetDefaults entry', () => {
|
||||
const target = uniq('target');
|
||||
const lib = uniq('lib');
|
||||
|
||||
updateJson('nx.json', (nxJson) => {
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults[target] = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
command: `echo Hello from ${target}`,
|
||||
},
|
||||
};
|
||||
return nxJson;
|
||||
});
|
||||
|
||||
updateFile(
|
||||
`libs/${lib}/project.json`,
|
||||
JSON.stringify({
|
||||
name: lib,
|
||||
targets: {
|
||||
[target]: {},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(runCLI(`${target} ${lib} --verbose`)).toContain(
|
||||
`Hello from ${target}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to pull options from targetDefaults based on executor', () => {
|
||||
const target = uniq('target');
|
||||
const lib = uniq('lib');
|
||||
|
||||
updateJson('nx.json', (nxJson) => {
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults[`nx:run-commands`] = {
|
||||
options: {
|
||||
command: `echo Hello from ${target}`,
|
||||
},
|
||||
};
|
||||
return nxJson;
|
||||
});
|
||||
|
||||
updateFile(
|
||||
`libs/${lib}/project.json`,
|
||||
JSON.stringify({
|
||||
name: lib,
|
||||
targets: {
|
||||
[target]: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(runCLI(`${target} ${lib} --verbose`)).toContain(
|
||||
`Hello from ${target}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('target dependencies', () => {
|
||||
let myapp;
|
||||
let mylib1;
|
||||
|
||||
@ -209,6 +209,26 @@
|
||||
"type": "object",
|
||||
"description": "Target defaults",
|
||||
"properties": {
|
||||
"executor": {
|
||||
"description": "The function that Nx will invoke when you run this target",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"type": "object",
|
||||
"description": "provides extra sets of values that will be merged into the options map",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"inputs": {
|
||||
"$ref": "#/definitions/inputs"
|
||||
},
|
||||
@ -243,12 +263,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { PackageManager } from '../utils/package-manager';
|
||||
import {
|
||||
InputDefinition,
|
||||
TargetConfiguration,
|
||||
TargetDependencyConfig,
|
||||
} from './workspace-json-project-json';
|
||||
|
||||
@ -19,14 +20,7 @@ export interface NxAffectedConfig {
|
||||
defaultBase?: string;
|
||||
}
|
||||
|
||||
export type TargetDefaults = Record<
|
||||
string,
|
||||
{
|
||||
outputs?: string[];
|
||||
dependsOn?: (TargetDependencyConfig | string)[];
|
||||
inputs?: (InputDefinition | string)[];
|
||||
}
|
||||
>;
|
||||
export type TargetDefaults = Record<string, Partial<TargetConfiguration>>;
|
||||
|
||||
export type TargetDependencies = Record<
|
||||
string,
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
import { toProjectName, Workspaces } from './workspaces';
|
||||
import {
|
||||
mergeTargetConfigurations,
|
||||
readTargetDefaultsForTarget,
|
||||
toProjectName,
|
||||
Workspaces,
|
||||
} from './workspaces';
|
||||
import { NxJsonConfiguration } from './nx-json';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import * as fastGlob from 'fast-glob';
|
||||
import { TargetConfiguration } from './workspace-json-project-json';
|
||||
|
||||
jest.mock('fs', () => require('memfs').fs);
|
||||
|
||||
@ -138,4 +144,316 @@ describe('Workspaces', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('target defaults', () => {
|
||||
const targetDefaults = {
|
||||
'nx:run-commands': {
|
||||
options: {
|
||||
key: 'default-value-for-executor',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
options: {
|
||||
key: 'default-value-for-targetname',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should prefer executor key', () => {
|
||||
expect(
|
||||
readTargetDefaultsForTarget(
|
||||
'other-target',
|
||||
targetDefaults,
|
||||
'nx:run-commands'
|
||||
).options['key']
|
||||
).toEqual('default-value-for-executor');
|
||||
});
|
||||
|
||||
it('should fallback to target key', () => {
|
||||
expect(
|
||||
readTargetDefaultsForTarget('build', targetDefaults, 'other-executor')
|
||||
.options['key']
|
||||
).toEqual('default-value-for-targetname');
|
||||
});
|
||||
|
||||
it('should return undefined if not found', () => {
|
||||
expect(
|
||||
readTargetDefaultsForTarget(
|
||||
'other-target',
|
||||
targetDefaults,
|
||||
'other-executor'
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
it('should merge if executor matches', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'target',
|
||||
options: {
|
||||
a: 'project-value-a',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
options: {
|
||||
a: 'default-value-a',
|
||||
b: 'default-value-b',
|
||||
},
|
||||
}
|
||||
).options
|
||||
).toEqual({ a: 'project-value-a', b: 'default-value-b' });
|
||||
});
|
||||
|
||||
it('should merge if executor is only provided on the project', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'target',
|
||||
options: {
|
||||
a: 'project-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
options: {
|
||||
a: 'default-value',
|
||||
b: 'default-value',
|
||||
},
|
||||
}
|
||||
).options
|
||||
).toEqual({ a: 'project-value', b: 'default-value' });
|
||||
});
|
||||
|
||||
it('should merge if executor is only provided in the defaults', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
options: {
|
||||
a: 'project-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
options: {
|
||||
a: 'default-value',
|
||||
b: 'default-value',
|
||||
},
|
||||
}
|
||||
).options
|
||||
).toEqual({ a: 'project-value', b: 'default-value' });
|
||||
});
|
||||
|
||||
it('should not merge if executor is different', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'other',
|
||||
options: {
|
||||
a: 'project-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'default-executor',
|
||||
options: {
|
||||
b: 'default-value',
|
||||
},
|
||||
}
|
||||
).options
|
||||
).toEqual({ a: 'project-value' });
|
||||
});
|
||||
|
||||
it('should resolve workspaceRoot and projectRoot tokens', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: 'my/project',
|
||||
targets: {
|
||||
build: {
|
||||
options: {
|
||||
a: '{workspaceRoot}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
options: {
|
||||
b: '{workspaceRoot}/dist/{projectRoot}',
|
||||
},
|
||||
}
|
||||
).options
|
||||
).toEqual({ a: '{workspaceRoot}', b: 'dist/my/project' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('configurations', () => {
|
||||
const projectConfigurations: TargetConfiguration['configurations'] = {
|
||||
dev: {
|
||||
foo: 'project-value-foo',
|
||||
},
|
||||
prod: {
|
||||
bar: 'project-value-bar',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultConfigurations: TargetConfiguration['configurations'] = {
|
||||
dev: {
|
||||
foo: 'default-value-foo',
|
||||
other: 'default-value-other',
|
||||
},
|
||||
baz: {
|
||||
x: 'default-value-x',
|
||||
},
|
||||
};
|
||||
|
||||
const merged: TargetConfiguration['configurations'] = {
|
||||
dev: {
|
||||
foo: projectConfigurations.dev.foo,
|
||||
other: defaultConfigurations.dev.other,
|
||||
},
|
||||
prod: { bar: projectConfigurations.prod.bar },
|
||||
baz: { x: defaultConfigurations.baz.x },
|
||||
};
|
||||
|
||||
it('should merge configurations if executor matches', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'target',
|
||||
configurations: projectConfigurations,
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
configurations: defaultConfigurations,
|
||||
}
|
||||
).configurations
|
||||
).toEqual(merged);
|
||||
});
|
||||
|
||||
it('should merge if executor is only provided on the project', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'target',
|
||||
configurations: projectConfigurations,
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
configurations: defaultConfigurations,
|
||||
}
|
||||
).configurations
|
||||
).toEqual(merged);
|
||||
});
|
||||
|
||||
it('should merge if executor is only provided in the defaults', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '.',
|
||||
targets: {
|
||||
build: {
|
||||
configurations: projectConfigurations,
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
configurations: defaultConfigurations,
|
||||
}
|
||||
).configurations
|
||||
).toEqual(merged);
|
||||
});
|
||||
|
||||
it('should not merge if executor doesnt match', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: '',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'other',
|
||||
configurations: projectConfigurations,
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
configurations: defaultConfigurations,
|
||||
}
|
||||
).configurations
|
||||
).toEqual(projectConfigurations);
|
||||
});
|
||||
|
||||
it('should resolve workspaceRoot and projectRoot tokens', () => {
|
||||
expect(
|
||||
mergeTargetConfigurations(
|
||||
{
|
||||
root: 'my/project',
|
||||
targets: {
|
||||
build: {
|
||||
configurations: {
|
||||
dev: {
|
||||
a: '{workspaceRoot}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'build',
|
||||
{
|
||||
executor: 'target',
|
||||
configurations: {
|
||||
prod: {
|
||||
a: '{workspaceRoot}/dist/{projectRoot}',
|
||||
},
|
||||
},
|
||||
}
|
||||
).configurations
|
||||
).toEqual({
|
||||
dev: { a: '{workspaceRoot}' },
|
||||
prod: { a: 'dist/my/project' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -7,14 +7,15 @@ import { performance } from 'perf_hooks';
|
||||
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { logger, NX_PREFIX } from '../utils/logger';
|
||||
import { logger, NX_PREFIX, stripIndent } from '../utils/logger';
|
||||
import { loadNxPlugins, readPluginPackageJson } from '../utils/nx-plugin';
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
import type { NxJsonConfiguration } from './nx-json';
|
||||
import type { NxJsonConfiguration, TargetDefaults } from './nx-json';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
ProjectsConfigurations,
|
||||
TargetConfiguration,
|
||||
} from './workspace-json-project-json';
|
||||
import {
|
||||
CustomHasher,
|
||||
@ -140,16 +141,19 @@ export class Workspaces {
|
||||
for (const proj of Object.values(config.projects)) {
|
||||
if (proj.targets) {
|
||||
for (const targetName of Object.keys(proj.targets)) {
|
||||
if (nxJson.targetDefaults[targetName]) {
|
||||
const projectTargetDefinition = proj.targets[targetName];
|
||||
if (!projectTargetDefinition.outputs) {
|
||||
projectTargetDefinition.outputs =
|
||||
nxJson.targetDefaults[targetName].outputs;
|
||||
}
|
||||
if (!projectTargetDefinition.dependsOn) {
|
||||
projectTargetDefinition.dependsOn =
|
||||
nxJson.targetDefaults[targetName].dependsOn;
|
||||
}
|
||||
const projectTargetDefinition = proj.targets[targetName];
|
||||
const defaults = readTargetDefaultsForTarget(
|
||||
targetName,
|
||||
nxJson.targetDefaults,
|
||||
projectTargetDefinition.executor
|
||||
);
|
||||
|
||||
if (defaults) {
|
||||
proj.targets[targetName] = mergeTargetConfigurations(
|
||||
proj,
|
||||
targetName,
|
||||
defaults
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -928,6 +932,125 @@ export function buildWorkspaceConfigurationFromGlobs(
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeTargetConfigurations(
|
||||
projectConfiguration: ProjectConfiguration,
|
||||
target: string,
|
||||
targetDefaults: TargetDefaults[string]
|
||||
): TargetConfiguration {
|
||||
const { targets, root } = projectConfiguration;
|
||||
const targetConfiguration = targets?.[target];
|
||||
|
||||
if (!targetConfiguration) {
|
||||
throw new Error(
|
||||
`Attempted to merge targetDefaults for ${projectConfiguration.name}.${target}, which doesn't exist.`
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
configurations: defaultConfigurations,
|
||||
options: defaultOptions,
|
||||
...defaults
|
||||
} = targetDefaults;
|
||||
const result = {
|
||||
...defaults,
|
||||
...targetConfiguration,
|
||||
};
|
||||
|
||||
// Target is "compatible", e.g. executor is defined only once or is the same
|
||||
// in both places. This means that it is likely safe to merge options
|
||||
if (
|
||||
!targetDefaults.executor ||
|
||||
!targetConfiguration.executor ||
|
||||
targetDefaults.executor === targetConfiguration.executor
|
||||
) {
|
||||
result.options = mergeOptions(
|
||||
defaultOptions,
|
||||
targetConfiguration.options ?? {},
|
||||
root,
|
||||
target
|
||||
);
|
||||
result.configurations = mergeConfigurations(
|
||||
defaultConfigurations,
|
||||
targetConfiguration.configurations,
|
||||
root,
|
||||
target
|
||||
);
|
||||
}
|
||||
return result as TargetConfiguration;
|
||||
}
|
||||
|
||||
function mergeOptions<T extends Object>(
|
||||
defaults: T,
|
||||
options: T,
|
||||
projectRoot: string,
|
||||
key: string
|
||||
): T {
|
||||
return {
|
||||
...resolvePathTokensInOptions(defaults, projectRoot, key),
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeConfigurations<T extends Object>(
|
||||
defaultConfigurations: Record<string, T>,
|
||||
projectDefinedConfigurations: Record<string, T>,
|
||||
projectRoot: string,
|
||||
targetName: string
|
||||
): Record<string, T> {
|
||||
const configurations: Record<string, T> = { ...projectDefinedConfigurations };
|
||||
for (const configuration in defaultConfigurations) {
|
||||
configurations[configuration] = mergeOptions(
|
||||
defaultConfigurations[configuration],
|
||||
configurations[configuration],
|
||||
projectRoot,
|
||||
`${targetName}.${configuration}`
|
||||
);
|
||||
}
|
||||
return configurations;
|
||||
}
|
||||
|
||||
function resolvePathTokensInOptions<T extends Object>(
|
||||
object: T,
|
||||
projectRoot: string,
|
||||
key: string
|
||||
): T {
|
||||
for (let [opt, value] of Object.entries(object ?? {})) {
|
||||
if (typeof value === 'string') {
|
||||
if (value.startsWith('{workspaceRoot}/')) {
|
||||
value = value.replace(/^\{workspaceRoot\}\//, '');
|
||||
}
|
||||
if (value.includes('{workspaceRoot}')) {
|
||||
throw new Error(
|
||||
`${NX_PREFIX} The {workspaceRoot} token is only valid at the beginning of an option. (${key})`
|
||||
);
|
||||
}
|
||||
object[opt] = value.replace('{projectRoot}', projectRoot);
|
||||
} else if (typeof value === 'object' && value) {
|
||||
resolvePathTokensInOptions(value, projectRoot, [key, opt].join('.'));
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
export function readTargetDefaultsForTarget(
|
||||
targetName: string,
|
||||
targetDefaults: TargetDefaults,
|
||||
executor?: string
|
||||
): TargetDefaults[string] {
|
||||
if (executor) {
|
||||
// If an executor is defined in project.json, defaults should be read
|
||||
// from the most specific key that matches that executor.
|
||||
// e.g. If executor === run-commands, and the target is named build:
|
||||
// Use, use nx:run-commands if it is present
|
||||
// If not, use build if it is present.
|
||||
const key = [executor, targetName].find((x) => targetDefaults?.[x]);
|
||||
return key ? targetDefaults?.[key] : null;
|
||||
} else {
|
||||
// If the executor is not defined, the only key we have is the target name.
|
||||
return targetDefaults?.[targetName];
|
||||
}
|
||||
}
|
||||
|
||||
// we have to do it this way to preserve the order of properties
|
||||
// not to screw up the formatting
|
||||
export function renamePropertyWithStableKeys(
|
||||
|
||||
@ -11,12 +11,13 @@ import { ProjectGraphBuilder } from '../project-graph-builder';
|
||||
import { PackageJson } from '../../utils/package-json';
|
||||
import { readJsonFile } from '../../utils/fileutils';
|
||||
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
} from '../../config/workspace-json-project-json';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import { findMatchingProjects } from '../../utils/find-matching-projects';
|
||||
import { NX_PREFIX } from '../../utils/logger';
|
||||
import {
|
||||
mergeTargetConfigurations,
|
||||
readTargetDefaultsForTarget,
|
||||
} from '../../config/workspaces';
|
||||
|
||||
export function buildWorkspaceProjectNodes(
|
||||
ctx: ProjectGraphProcessorContext,
|
||||
@ -55,7 +56,6 @@ export function buildWorkspaceProjectNodes(
|
||||
}
|
||||
}
|
||||
|
||||
p.targets = normalizeProjectTargets(p.targets, nxJson.targetDefaults, key);
|
||||
p.implicitDependencies = normalizeImplicitDependencies(
|
||||
key,
|
||||
p.implicitDependencies,
|
||||
@ -69,6 +69,8 @@ export function buildWorkspaceProjectNodes(
|
||||
loadNxPlugins(ctx.workspace.plugins)
|
||||
);
|
||||
|
||||
p.targets = normalizeProjectTargets(p, nxJson.targetDefaults, key);
|
||||
|
||||
// TODO: remove in v16
|
||||
const projectType =
|
||||
p.projectType === 'application'
|
||||
@ -109,26 +111,27 @@ export function buildWorkspaceProjectNodes(
|
||||
* Apply target defaults and normalization
|
||||
*/
|
||||
function normalizeProjectTargets(
|
||||
targets: Record<string, TargetConfiguration>,
|
||||
defaultTargets: NxJsonConfiguration['targetDefaults'],
|
||||
project: ProjectConfiguration,
|
||||
targetDefaults: NxJsonConfiguration['targetDefaults'],
|
||||
projectName: string
|
||||
) {
|
||||
for (const targetName in defaultTargets) {
|
||||
const target = targets?.[targetName];
|
||||
if (!target) {
|
||||
continue;
|
||||
}
|
||||
if (defaultTargets[targetName].inputs && !target.inputs) {
|
||||
target.inputs = defaultTargets[targetName].inputs;
|
||||
}
|
||||
if (defaultTargets[targetName].dependsOn && !target.dependsOn) {
|
||||
target.dependsOn = defaultTargets[targetName].dependsOn;
|
||||
}
|
||||
if (defaultTargets[targetName].outputs && !target.outputs) {
|
||||
target.outputs = defaultTargets[targetName].outputs;
|
||||
}
|
||||
}
|
||||
const targets = project.targets;
|
||||
for (const target in targets) {
|
||||
const executor =
|
||||
targets[target].executor ?? targets[target].command
|
||||
? 'nx:run-commands'
|
||||
: null;
|
||||
|
||||
const defaults = readTargetDefaultsForTarget(
|
||||
target,
|
||||
targetDefaults,
|
||||
executor
|
||||
);
|
||||
|
||||
if (defaults) {
|
||||
targets[target] = mergeTargetConfigurations(project, target, defaults);
|
||||
}
|
||||
|
||||
const config = targets[target];
|
||||
if (config.command) {
|
||||
if (config.executor) {
|
||||
@ -138,12 +141,13 @@ function normalizeProjectTargets(
|
||||
} else {
|
||||
targets[target] = {
|
||||
...targets[target],
|
||||
executor: 'nx:run-commands',
|
||||
executor,
|
||||
options: {
|
||||
...config.options,
|
||||
command: config.command,
|
||||
},
|
||||
};
|
||||
delete config.command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user