feat(core): add metadata to targets (#22655)
This commit is contained in:
parent
fc8d5ba828
commit
21f90cae85
@ -8,7 +8,7 @@ Project configuration
|
|||||||
|
|
||||||
- [generators](../../devkit/documents/ProjectConfiguration#generators): Object
|
- [generators](../../devkit/documents/ProjectConfiguration#generators): Object
|
||||||
- [implicitDependencies](../../devkit/documents/ProjectConfiguration#implicitdependencies): string[]
|
- [implicitDependencies](../../devkit/documents/ProjectConfiguration#implicitdependencies): string[]
|
||||||
- [metadata](../../devkit/documents/ProjectConfiguration#metadata): Object
|
- [metadata](../../devkit/documents/ProjectConfiguration#metadata): ProjectMetadata
|
||||||
- [name](../../devkit/documents/ProjectConfiguration#name): string
|
- [name](../../devkit/documents/ProjectConfiguration#name): string
|
||||||
- [namedInputs](../../devkit/documents/ProjectConfiguration#namedinputs): Object
|
- [namedInputs](../../devkit/documents/ProjectConfiguration#namedinputs): Object
|
||||||
- [projectType](../../devkit/documents/ProjectConfiguration#projecttype): ProjectType
|
- [projectType](../../devkit/documents/ProjectConfiguration#projecttype): ProjectType
|
||||||
@ -56,14 +56,9 @@ List of projects which are added as a dependency
|
|||||||
|
|
||||||
### metadata
|
### metadata
|
||||||
|
|
||||||
• `Optional` **metadata**: `Object`
|
• `Optional` **metadata**: `ProjectMetadata`
|
||||||
|
|
||||||
#### Type declaration
|
Metadata about the project
|
||||||
|
|
||||||
| Name | Type |
|
|
||||||
| :-------------- | :------------------------------- |
|
|
||||||
| `targetGroups?` | `Record`\<`string`, `string`[]\> |
|
|
||||||
| `technologies?` | `string`[] |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ Target's configuration
|
|||||||
- [dependsOn](../../devkit/documents/TargetConfiguration#dependson): (string | TargetDependencyConfig)[]
|
- [dependsOn](../../devkit/documents/TargetConfiguration#dependson): (string | TargetDependencyConfig)[]
|
||||||
- [executor](../../devkit/documents/TargetConfiguration#executor): string
|
- [executor](../../devkit/documents/TargetConfiguration#executor): string
|
||||||
- [inputs](../../devkit/documents/TargetConfiguration#inputs): (string | InputDefinition)[]
|
- [inputs](../../devkit/documents/TargetConfiguration#inputs): (string | InputDefinition)[]
|
||||||
|
- [metadata](../../devkit/documents/TargetConfiguration#metadata): TargetMetadata
|
||||||
- [options](../../devkit/documents/TargetConfiguration#options): T
|
- [options](../../devkit/documents/TargetConfiguration#options): T
|
||||||
- [outputs](../../devkit/documents/TargetConfiguration#outputs): string[]
|
- [outputs](../../devkit/documents/TargetConfiguration#outputs): string[]
|
||||||
|
|
||||||
@ -86,6 +87,14 @@ This describes filesets, runtime dependencies and other inputs that a target dep
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### metadata
|
||||||
|
|
||||||
|
• `Optional` **metadata**: `TargetMetadata`
|
||||||
|
|
||||||
|
Metadata about the target
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### options
|
### options
|
||||||
|
|
||||||
• `Optional` **options**: `T`
|
• `Optional` **options**: `T`
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { defineConfig } from 'cypress';
|
|||||||
|
|
||||||
import { createNodes } from './plugin';
|
import { createNodes } from './plugin';
|
||||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||||
|
import { resetWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { nxE2EPreset } from '../../plugins/cypress-preset';
|
import { nxE2EPreset } from '../../plugins/cypress-preset';
|
||||||
|
|
||||||
@ -16,9 +17,9 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
|
|
||||||
await tempFs.createFiles({
|
await tempFs.createFiles({
|
||||||
'package.json': '{}',
|
'package.json': '{}',
|
||||||
|
'cypress.config.js': '',
|
||||||
'src/test.cy.ts': '',
|
'src/test.cy.ts': '',
|
||||||
});
|
});
|
||||||
process.chdir(tempFs.tempDir);
|
|
||||||
context = {
|
context = {
|
||||||
nxJsonConfiguration: {
|
nxJsonConfiguration: {
|
||||||
// These defaults should be overridden by plugin
|
// These defaults should be overridden by plugin
|
||||||
@ -41,6 +42,11 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
tempFs.cleanup();
|
tempFs.cleanup();
|
||||||
|
tempFs = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetWorkspaceContext();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a target for e2e', async () => {
|
it('should add a target for e2e', async () => {
|
||||||
@ -70,11 +76,7 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
".": {
|
".": {
|
||||||
"metadata": {
|
"metadata": undefined,
|
||||||
"technologies": [
|
|
||||||
"cypress",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"targets": {
|
"targets": {
|
||||||
"e2e": {
|
"e2e": {
|
||||||
@ -94,6 +96,12 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
@ -104,6 +112,12 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
},
|
},
|
||||||
"open-cypress": {
|
"open-cypress": {
|
||||||
"command": "cypress open",
|
"command": "cypress open",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Opens Cypress",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
@ -140,11 +154,7 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
".": {
|
".": {
|
||||||
"metadata": {
|
"metadata": undefined,
|
||||||
"technologies": [
|
|
||||||
"cypress",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"targets": {
|
"targets": {
|
||||||
"component-test": {
|
"component-test": {
|
||||||
@ -159,6 +169,12 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Component Tests",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
@ -169,6 +185,12 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
},
|
},
|
||||||
"open-cypress": {
|
"open-cypress": {
|
||||||
"command": "cypress open",
|
"command": "cypress open",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Opens Cypress",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
@ -184,16 +206,16 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
mockCypressConfig(
|
mockCypressConfig(
|
||||||
defineConfig({
|
defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
specPattern: '**/*.cy.ts',
|
...nxE2EPreset(join(tempFs.tempDir, 'cypress.config.js'), {
|
||||||
videosFolder: './dist/videos',
|
|
||||||
screenshotsFolder: './dist/screenshots',
|
|
||||||
...nxE2EPreset('.', {
|
|
||||||
webServerCommands: {
|
webServerCommands: {
|
||||||
default: 'my-app:serve',
|
default: 'my-app:serve',
|
||||||
production: 'my-app:serve:production',
|
production: 'my-app:serve:production',
|
||||||
},
|
},
|
||||||
ciWebServerCommand: 'my-app:serve-static',
|
ciWebServerCommand: 'my-app:serve-static',
|
||||||
}),
|
}),
|
||||||
|
specPattern: '**/*.cy.ts',
|
||||||
|
videosFolder: './dist/videos',
|
||||||
|
screenshotsFolder: './dist/screenshots',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -216,9 +238,6 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
"e2e-ci",
|
"e2e-ci",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"technologies": [
|
|
||||||
"cypress",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"targets": {
|
"targets": {
|
||||||
@ -239,12 +258,18 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"{projectRoot}/dist/cypress/videos",
|
"{projectRoot}/dist/videos",
|
||||||
"{projectRoot}/dist/cypress/screenshots",
|
"{projectRoot}/dist/screenshots",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"e2e-ci": {
|
"e2e-ci": {
|
||||||
@ -266,9 +291,15 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in CI",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"{projectRoot}/dist/cypress/videos",
|
"{projectRoot}/dist/videos",
|
||||||
"{projectRoot}/dist/cypress/screenshots",
|
"{projectRoot}/dist/screenshots",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"e2e-ci--src/test.cy.ts": {
|
"e2e-ci--src/test.cy.ts": {
|
||||||
@ -283,16 +314,28 @@ describe('@nx/cypress/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Cypress Tests in src/test.cy.ts in CI",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"{projectRoot}/dist/cypress/videos",
|
"{projectRoot}/dist/videos",
|
||||||
"{projectRoot}/dist/cypress/screenshots",
|
"{projectRoot}/dist/screenshots",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"open-cypress": {
|
"open-cypress": {
|
||||||
"command": "cypress open",
|
"command": "cypress open",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Opens Cypress",
|
||||||
|
"technologies": [
|
||||||
|
"cypress",
|
||||||
|
],
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": ".",
|
"cwd": ".",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -67,7 +67,7 @@ export const createNodes: CreateNodes<CypressPluginOptions> = [
|
|||||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { targets, targetGroups } = targetsCache[hash]
|
const { targets, metadata } = targetsCache[hash]
|
||||||
? targetsCache[hash]
|
? targetsCache[hash]
|
||||||
: await buildCypressTargets(
|
: await buildCypressTargets(
|
||||||
configFilePath,
|
configFilePath,
|
||||||
@ -76,20 +76,14 @@ export const createNodes: CreateNodes<CypressPluginOptions> = [
|
|||||||
context
|
context
|
||||||
);
|
);
|
||||||
|
|
||||||
calculatedTargets[hash] = { targets, targetGroups };
|
calculatedTargets[hash] = { targets, metadata };
|
||||||
|
|
||||||
const project: Omit<ProjectConfiguration, 'root'> = {
|
const project: Omit<ProjectConfiguration, 'root'> = {
|
||||||
projectType: 'application',
|
projectType: 'application',
|
||||||
targets,
|
targets,
|
||||||
metadata: {
|
metadata,
|
||||||
technologies: ['cypress'],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (targetGroups) {
|
|
||||||
project.metadata.targetGroups = targetGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projects: {
|
projects: {
|
||||||
[projectRoot]: project,
|
[projectRoot]: project,
|
||||||
@ -146,10 +140,7 @@ function getOutputs(
|
|||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CypressTargets {
|
type CypressTargets = Pick<ProjectConfiguration, 'targets' | 'metadata'>;
|
||||||
targets: Record<string, TargetConfiguration>;
|
|
||||||
targetGroups: Record<string, string[]> | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildCypressTargets(
|
async function buildCypressTargets(
|
||||||
configFilePath: string,
|
configFilePath: string,
|
||||||
@ -173,7 +164,7 @@ async function buildCypressTargets(
|
|||||||
const namedInputs = getNamedInputs(projectRoot, context);
|
const namedInputs = getNamedInputs(projectRoot, context);
|
||||||
|
|
||||||
const targets: Record<string, TargetConfiguration> = {};
|
const targets: Record<string, TargetConfiguration> = {};
|
||||||
let targetGroups: Record<string, string[]>;
|
let metadata: ProjectConfiguration['metadata'];
|
||||||
|
|
||||||
if ('e2e' in cypressConfig) {
|
if ('e2e' in cypressConfig) {
|
||||||
targets[options.targetName] = {
|
targets[options.targetName] = {
|
||||||
@ -182,6 +173,10 @@ async function buildCypressTargets(
|
|||||||
cache: true,
|
cache: true,
|
||||||
inputs: getInputs(namedInputs),
|
inputs: getInputs(namedInputs),
|
||||||
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
|
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
|
||||||
|
metadata: {
|
||||||
|
technologies: ['cypress'],
|
||||||
|
description: 'Runs Cypress Tests',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (webServerCommands?.default) {
|
if (webServerCommands?.default) {
|
||||||
@ -222,8 +217,8 @@ async function buildCypressTargets(
|
|||||||
const inputs = getInputs(namedInputs);
|
const inputs = getInputs(namedInputs);
|
||||||
|
|
||||||
const groupName = 'E2E (CI)';
|
const groupName = 'E2E (CI)';
|
||||||
targetGroups = { [groupName]: [] };
|
metadata = { targetGroups: { [groupName]: [] } };
|
||||||
const ciTargetGroup = targetGroups[groupName];
|
const ciTargetGroup = metadata.targetGroups[groupName];
|
||||||
for (const file of specFiles) {
|
for (const file of specFiles) {
|
||||||
const relativeSpecFilePath = normalizePath(relative(projectRoot, file));
|
const relativeSpecFilePath = normalizePath(relative(projectRoot, file));
|
||||||
const targetName = options.ciTargetName + '--' + relativeSpecFilePath;
|
const targetName = options.ciTargetName + '--' + relativeSpecFilePath;
|
||||||
@ -237,6 +232,10 @@ async function buildCypressTargets(
|
|||||||
options: {
|
options: {
|
||||||
cwd: projectRoot,
|
cwd: projectRoot,
|
||||||
},
|
},
|
||||||
|
metadata: {
|
||||||
|
technologies: ['cypress'],
|
||||||
|
description: `Runs Cypress Tests in ${relativeSpecFilePath} in CI`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
dependsOn.push({
|
dependsOn.push({
|
||||||
target: targetName,
|
target: targetName,
|
||||||
@ -251,10 +250,12 @@ async function buildCypressTargets(
|
|||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
dependsOn,
|
dependsOn,
|
||||||
|
metadata: {
|
||||||
|
technologies: ['cypress'],
|
||||||
|
description: 'Runs Cypress Tests in CI',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ciTargetGroup.push(options.ciTargetName);
|
ciTargetGroup.push(options.ciTargetName);
|
||||||
} else {
|
|
||||||
targetGroups = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,15 +267,23 @@ async function buildCypressTargets(
|
|||||||
cache: true,
|
cache: true,
|
||||||
inputs: getInputs(namedInputs),
|
inputs: getInputs(namedInputs),
|
||||||
outputs: getOutputs(projectRoot, cypressConfig, 'component'),
|
outputs: getOutputs(projectRoot, cypressConfig, 'component'),
|
||||||
|
metadata: {
|
||||||
|
technologies: ['cypress'],
|
||||||
|
description: 'Runs Cypress Component Tests',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
targets[options.openTargetName] = {
|
targets[options.openTargetName] = {
|
||||||
command: `cypress open`,
|
command: `cypress open`,
|
||||||
options: { cwd: projectRoot },
|
options: { cwd: projectRoot },
|
||||||
|
metadata: {
|
||||||
|
technologies: ['cypress'],
|
||||||
|
description: 'Opens Cypress',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return { targets, targetGroups };
|
return { targets, metadata };
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeOptions(options: CypressPluginOptions): CypressPluginOptions {
|
function normalizeOptions(options: CypressPluginOptions): CypressPluginOptions {
|
||||||
|
|||||||
@ -111,10 +111,21 @@ export interface ProjectConfiguration {
|
|||||||
'generator' | 'generatorOptions'
|
'generator' | 'generatorOptions'
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
metadata?: {
|
|
||||||
technologies?: string[];
|
/**
|
||||||
targetGroups?: Record<string, string[]>;
|
* Metadata about the project
|
||||||
};
|
*/
|
||||||
|
metadata?: ProjectMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectMetadata {
|
||||||
|
technologies?: string[];
|
||||||
|
targetGroups?: Record<string, string[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TargetMetadata {
|
||||||
|
description?: string;
|
||||||
|
technologies?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TargetDependencyConfig {
|
export interface TargetDependencyConfig {
|
||||||
@ -203,4 +214,9 @@ export interface TargetConfiguration<T = any> {
|
|||||||
* Determines if Nx is able to cache a given target.
|
* Determines if Nx is able to cache a given target.
|
||||||
*/
|
*/
|
||||||
cache?: boolean;
|
cache?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about the target
|
||||||
|
*/
|
||||||
|
metadata?: TargetMetadata;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -444,6 +444,101 @@ describe('project-configuration-utils', () => {
|
|||||||
expect(result.cache).not.toBeDefined();
|
expect(result.cache).not.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('metadata', () => {
|
||||||
|
it('should be added', () => {
|
||||||
|
const rootMap = new RootMapBuilder()
|
||||||
|
.addProject({
|
||||||
|
root: 'libs/lib-a',
|
||||||
|
name: 'lib-a',
|
||||||
|
})
|
||||||
|
.getRootMap();
|
||||||
|
const sourceMap: ConfigurationSourceMaps = {
|
||||||
|
'libs/lib-a': {},
|
||||||
|
};
|
||||||
|
mergeProjectConfigurationIntoRootMap(
|
||||||
|
rootMap,
|
||||||
|
{
|
||||||
|
root: 'libs/lib-a',
|
||||||
|
name: 'lib-a',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
metadata: {
|
||||||
|
description: 'do stuff',
|
||||||
|
technologies: ['tech'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourceMap,
|
||||||
|
['dummy', 'dummy.ts']
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(rootMap.get('libs/lib-a').targets.build.metadata).toEqual({
|
||||||
|
description: 'do stuff',
|
||||||
|
technologies: ['tech'],
|
||||||
|
});
|
||||||
|
expect(sourceMap['libs/lib-a']).toMatchObject({
|
||||||
|
'targets.build.metadata.description': ['dummy', 'dummy.ts'],
|
||||||
|
'targets.build.metadata.technologies': ['dummy', 'dummy.ts'],
|
||||||
|
'targets.build.metadata.technologies.0': ['dummy', 'dummy.ts'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be merged', () => {
|
||||||
|
const rootMap = new RootMapBuilder()
|
||||||
|
.addProject({
|
||||||
|
root: 'libs/lib-a',
|
||||||
|
name: 'lib-a',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
metadata: {
|
||||||
|
description: 'do stuff',
|
||||||
|
technologies: ['tech'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.getRootMap();
|
||||||
|
const sourceMap: ConfigurationSourceMaps = {
|
||||||
|
'libs/lib-a': {
|
||||||
|
'targets.build.metadata.technologies': ['existing', 'existing.ts'],
|
||||||
|
'targets.build.metadata.technologies.0': [
|
||||||
|
'existing',
|
||||||
|
'existing.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mergeProjectConfigurationIntoRootMap(
|
||||||
|
rootMap,
|
||||||
|
{
|
||||||
|
root: 'libs/lib-a',
|
||||||
|
name: 'lib-a',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
metadata: {
|
||||||
|
description: 'do cool stuff',
|
||||||
|
technologies: ['tech2'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourceMap,
|
||||||
|
['dummy', 'dummy.ts']
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(rootMap.get('libs/lib-a').targets.build.metadata).toEqual({
|
||||||
|
description: 'do cool stuff',
|
||||||
|
technologies: ['tech', 'tech2'],
|
||||||
|
});
|
||||||
|
expect(sourceMap['libs/lib-a']).toMatchObject({
|
||||||
|
'targets.build.metadata.description': ['dummy', 'dummy.ts'],
|
||||||
|
'targets.build.metadata.technologies': ['existing', 'existing.ts'],
|
||||||
|
'targets.build.metadata.technologies.0': ['existing', 'existing.ts'],
|
||||||
|
'targets.build.metadata.technologies.1': ['dummy', 'dummy.ts'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mergeProjectConfigurationIntoRootMap', () => {
|
describe('mergeProjectConfigurationIntoRootMap', () => {
|
||||||
|
|||||||
@ -2,7 +2,9 @@ import { NxJsonConfiguration, TargetDefaults } from '../../config/nx-json';
|
|||||||
import { ProjectGraphExternalNode } from '../../config/project-graph';
|
import { ProjectGraphExternalNode } from '../../config/project-graph';
|
||||||
import {
|
import {
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
|
ProjectMetadata,
|
||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
|
TargetMetadata,
|
||||||
} from '../../config/workspace-json-project-json';
|
} from '../../config/workspace-json-project-json';
|
||||||
import { NX_PREFIX } from '../../utils/logger';
|
import { NX_PREFIX } from '../../utils/logger';
|
||||||
import { CreateNodesResult, LoadedNxPlugin } from '../../utils/nx-plugin';
|
import { CreateNodesResult, LoadedNxPlugin } from '../../utils/nx-plugin';
|
||||||
@ -149,6 +151,16 @@ export function mergeProjectConfigurationIntoRootMap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (project.metadata) {
|
||||||
|
updatedProjectConfiguration.metadata = mergeMetadata(
|
||||||
|
sourceMap,
|
||||||
|
sourceInformation,
|
||||||
|
'metadata',
|
||||||
|
project.metadata,
|
||||||
|
matchingProject.metadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (project.targets) {
|
if (project.targets) {
|
||||||
// We merge the targets with special handling, so clear this back to the
|
// We merge the targets with special handling, so clear this back to the
|
||||||
// targets as defined originally before merging.
|
// targets as defined originally before merging.
|
||||||
@ -195,85 +207,85 @@ export function mergeProjectConfigurationIntoRootMap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.metadata) {
|
projectRootMap.set(
|
||||||
if (sourceMap) {
|
updatedProjectConfiguration.root,
|
||||||
sourceMap['targets'] ??= sourceInformation;
|
updatedProjectConfiguration
|
||||||
}
|
);
|
||||||
for (const [metadataKey, value] of Object.entries({
|
}
|
||||||
...project.metadata,
|
|
||||||
})) {
|
|
||||||
const existingValue = matchingProject.metadata?.[metadataKey];
|
|
||||||
|
|
||||||
if (Array.isArray(value) && Array.isArray(existingValue)) {
|
function mergeMetadata<T = ProjectMetadata | TargetMetadata>(
|
||||||
for (const item of [...value]) {
|
sourceMap: Record<string, [file: string, plugin: string]>,
|
||||||
const newLength =
|
sourceInformation: [file: string, plugin: string],
|
||||||
updatedProjectConfiguration.metadata[metadataKey].push(item);
|
baseSourceMapPath: string,
|
||||||
|
metadata: T,
|
||||||
|
matchingMetadata?: T
|
||||||
|
): T {
|
||||||
|
const result: T = {
|
||||||
|
...(matchingMetadata ?? ({} as T)),
|
||||||
|
};
|
||||||
|
for (const [metadataKey, value] of Object.entries(metadata)) {
|
||||||
|
const existingValue = matchingMetadata?.[metadataKey];
|
||||||
|
|
||||||
|
if (Array.isArray(value) && Array.isArray(existingValue)) {
|
||||||
|
for (const item of [...value]) {
|
||||||
|
const newLength = result[metadataKey].push(item);
|
||||||
|
if (sourceMap) {
|
||||||
|
sourceMap[`${baseSourceMapPath}.${metadataKey}.${newLength - 1}`] =
|
||||||
|
sourceInformation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(value) && existingValue === undefined) {
|
||||||
|
result[metadataKey] ??= value;
|
||||||
|
if (sourceMap) {
|
||||||
|
sourceMap[`${baseSourceMapPath}.${metadataKey}`] = sourceInformation;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
if (sourceMap) {
|
||||||
|
sourceMap[`${baseSourceMapPath}.${metadataKey}.${i}`] =
|
||||||
|
sourceInformation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'object' && typeof existingValue === 'object') {
|
||||||
|
for (const key in value) {
|
||||||
|
const existingValue = matchingMetadata?.[metadataKey]?.[key];
|
||||||
|
|
||||||
|
if (Array.isArray(value[key]) && Array.isArray(existingValue)) {
|
||||||
|
for (const item of value[key]) {
|
||||||
|
const i = result[metadataKey][key].push(item);
|
||||||
|
if (sourceMap) {
|
||||||
|
sourceMap[`${baseSourceMapPath}.${metadataKey}.${key}.${i - 1}`] =
|
||||||
|
sourceInformation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result[metadataKey] = value;
|
||||||
if (sourceMap) {
|
if (sourceMap) {
|
||||||
sourceMap[`metadata.${metadataKey}.${newLength - 1}`] =
|
sourceMap[`${baseSourceMapPath}.${metadataKey}`] =
|
||||||
sourceInformation;
|
sourceInformation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(value) && existingValue === undefined) {
|
}
|
||||||
updatedProjectConfiguration.metadata ??= {};
|
} else {
|
||||||
updatedProjectConfiguration.metadata[metadataKey] ??= value;
|
result[metadataKey] = value;
|
||||||
if (sourceMap) {
|
if (sourceMap) {
|
||||||
sourceMap[`metadata.${metadataKey}`] = sourceInformation;
|
sourceMap[`${baseSourceMapPath}.${metadataKey}`] = sourceInformation;
|
||||||
}
|
|
||||||
for (let i = 0; i < value.length; i++) {
|
|
||||||
if (sourceMap) {
|
|
||||||
sourceMap[`metadata.${metadataKey}.${i}`] = sourceInformation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
typeof value === 'object' &&
|
|
||||||
typeof existingValue === 'object'
|
|
||||||
) {
|
|
||||||
for (const key in value) {
|
|
||||||
const existingValue = matchingProject.metadata?.[metadataKey]?.[key];
|
|
||||||
|
|
||||||
if (Array.isArray(value[key]) && Array.isArray(existingValue)) {
|
if (typeof value === 'object') {
|
||||||
for (const item of value[key]) {
|
for (const k in value) {
|
||||||
const i =
|
sourceMap[`${baseSourceMapPath}.${metadataKey}.${k}`] =
|
||||||
updatedProjectConfiguration.metadata[metadataKey][key].push(
|
sourceInformation;
|
||||||
item
|
if (Array.isArray(value[k])) {
|
||||||
);
|
for (let i = 0; i < value[k].length; i++) {
|
||||||
if (sourceMap) {
|
sourceMap[`${baseSourceMapPath}.${metadataKey}.${k}.${i}`] =
|
||||||
sourceMap[`metadata.${metadataKey}.${key}.${i - 1}`] =
|
|
||||||
sourceInformation;
|
sourceInformation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
updatedProjectConfiguration.metadata[metadataKey] = value;
|
|
||||||
if (sourceMap) {
|
|
||||||
sourceMap[`metadata.${metadataKey}`] = sourceInformation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updatedProjectConfiguration.metadata[metadataKey] = value;
|
|
||||||
if (sourceMap) {
|
|
||||||
sourceMap[`metadata.${metadataKey}`] = sourceInformation;
|
|
||||||
|
|
||||||
if (typeof value === 'object') {
|
|
||||||
for (const k in value) {
|
|
||||||
sourceMap[`metadata.${metadataKey}.${k}`] = sourceInformation;
|
|
||||||
if (Array.isArray(value[k])) {
|
|
||||||
for (let i = 0; i < value[k].length; i++) {
|
|
||||||
sourceMap[`metadata.${metadataKey}.${k}.${i}`] =
|
|
||||||
sourceInformation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
projectRootMap.set(
|
|
||||||
updatedProjectConfiguration.root,
|
|
||||||
updatedProjectConfiguration
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConfigurationResult = {
|
export type ConfigurationResult = {
|
||||||
@ -689,6 +701,17 @@ export function mergeTargetConfigurations(
|
|||||||
targetIdentifier
|
targetIdentifier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.metadata) {
|
||||||
|
result.metadata = mergeMetadata(
|
||||||
|
projectConfigSourceMap,
|
||||||
|
sourceInformation,
|
||||||
|
`${targetIdentifier}.metadata`,
|
||||||
|
target.metadata,
|
||||||
|
baseTarget?.metadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return result as TargetConfiguration;
|
return result as TargetConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user