feat(testing): add migration for moving test target defaults (#19993)
This commit is contained in:
parent
95ae6fd2a3
commit
b0d179904d
@ -29,6 +29,11 @@
|
||||
"version": "16.5.0-beta.2",
|
||||
"description": "Add test-setup.ts to ignored files in production input",
|
||||
"implementation": "./src/migrations/update-16-5-0/add-test-setup-to-inputs-ignore"
|
||||
},
|
||||
"move-options-to-target-defaults": {
|
||||
"version": "17.1.0-beta.2",
|
||||
"description": "Move jest executor options to nx.json targetDefaults",
|
||||
"implementation": "./src/migrations/update-17-1-0/move-options-to-target-defaults"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -2,7 +2,6 @@ import {
|
||||
addDependenciesToPackageJson,
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
removeDependenciesFromPackageJson,
|
||||
runTasksInSerial,
|
||||
@ -27,7 +26,6 @@ import {
|
||||
typesNodeVersion,
|
||||
} from '../../utils/versions';
|
||||
import { JestInitSchema } from './schema';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
interface NormalizedSchema extends ReturnType<typeof normalizeOptions> {}
|
||||
|
||||
|
||||
@ -0,0 +1,562 @@
|
||||
import { createTree } from '@nx/devkit/testing';
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual<any>('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||
return projectGraph;
|
||||
}),
|
||||
}));
|
||||
|
||||
import {
|
||||
addProjectConfiguration as _addProjectConfiguration,
|
||||
ProjectGraph,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
function addProjectConfiguration(tree, name, project) {
|
||||
_addProjectConfiguration(tree, name, project);
|
||||
projectGraph.nodes[name] = {
|
||||
name: name,
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: project.root,
|
||||
targets: project.targets,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
import update from './move-options-to-target-defaults';
|
||||
|
||||
describe('move-options-to-target-defaults migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTree();
|
||||
|
||||
writeJson(tree, 'nx.json', {
|
||||
namedInputs: {
|
||||
production: ['default'],
|
||||
},
|
||||
targetDefaults: {},
|
||||
});
|
||||
|
||||
projectGraph = {
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
externalNodes: {},
|
||||
};
|
||||
});
|
||||
|
||||
it('should add config to nx.json and remove it from projects', async () => {
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj2', {
|
||||
root: 'proj2',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets.test).toEqual({
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
},
|
||||
});
|
||||
expect(readProjectConfiguration(tree, 'proj2').targets.test).toEqual({
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should use test target defaults if all jest targets are test', async () => {
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.targetDefaults['test'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets.test).toEqual({
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: false,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
watch: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not remove config which does not match', async () => {
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
passWithNoTests: true,
|
||||
watch: false,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets.test).toEqual({
|
||||
executor: '@nx/jest:jest',
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
watch: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not remove defaults if target uses other executors', async () => {
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj2', {
|
||||
root: 'proj2',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/vite:vitest',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets.test).toEqual({
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.js',
|
||||
},
|
||||
});
|
||||
expect(readProjectConfiguration(tree, 'proj2').targets.test).toEqual({
|
||||
executor: '@nx/vite:vitest',
|
||||
options: {},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle when jest and vite are used for test and jest and cypress are used for e2e', async () => {
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.targetDefaults['test'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: true,
|
||||
},
|
||||
};
|
||||
nxJson.targetDefaults['e2e'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
e2e: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj2', {
|
||||
root: 'proj2',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/vite:vitest',
|
||||
options: {},
|
||||
},
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets).toEqual({
|
||||
e2e: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj2').targets).toEqual({
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
test: {
|
||||
executor: '@nx/vite:vitest',
|
||||
options: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
e2e: {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
},
|
||||
test: {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not assign things that had a default already', async () => {
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.targetDefaults['test'] = {
|
||||
cache: true,
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets).toEqual({
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove target defaults which are not used anymore', async () => {
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.targetDefaults['@nx/vite:test'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production'],
|
||||
};
|
||||
nxJson.targetDefaults['test'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: true,
|
||||
},
|
||||
};
|
||||
nxJson.targetDefaults['e2e'] = {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
e2e: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj2', {
|
||||
root: 'proj2',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/vite:test',
|
||||
options: {},
|
||||
},
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj1').targets).toEqual({
|
||||
e2e: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
test: {
|
||||
executor: '@nx/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'jest.config.ts',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj2').targets).toEqual({
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
test: {
|
||||
executor: '@nx/vite:test',
|
||||
options: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/jest:jest': {
|
||||
cache: true,
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
codeCoverage: true,
|
||||
},
|
||||
},
|
||||
inputs: ['default', '^production'],
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
'@nx/vite:test': {
|
||||
cache: false,
|
||||
inputs: ['default', '^production'],
|
||||
},
|
||||
e2e: {
|
||||
cache: false,
|
||||
inputs: ['default', '^production', '{workspaceRoot}/other-file.txt'],
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,185 @@
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
ProjectConfiguration,
|
||||
ProjectGraphProjectNode,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
TargetDefaults,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import {
|
||||
forEachExecutorOptions,
|
||||
forEachExecutorOptionsInGraph,
|
||||
} from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
// Don't override anything if there are already target defaults for jest
|
||||
if (nxJson.targetDefaults?.['@nx/jest:jest']) {
|
||||
return;
|
||||
}
|
||||
|
||||
nxJson.targetDefaults ??= {};
|
||||
|
||||
/**
|
||||
* A set of targets which does not use any other executors
|
||||
*/
|
||||
const jestTargets = new Set<string>();
|
||||
|
||||
const graph = await createProjectGraphAsync();
|
||||
|
||||
forEachExecutorOptionsInGraph(
|
||||
graph,
|
||||
'@nx/jest:jest',
|
||||
(value, proj, targetName) => {
|
||||
jestTargets.add(targetName);
|
||||
}
|
||||
);
|
||||
|
||||
// Workspace does not use jest?
|
||||
if (jestTargets.size === 0) {
|
||||
return;
|
||||
}
|
||||
// Use the project graph so targets which are inferred are considered
|
||||
const projects = graph.nodes;
|
||||
const projectMap = getProjects(tree);
|
||||
|
||||
const jestDefaults: TargetConfiguration<Partial<JestExecutorOptions>> =
|
||||
(nxJson.targetDefaults['@nx/jest:jest'] = {});
|
||||
|
||||
// All jest targets have the same name
|
||||
if (jestTargets.size === 1) {
|
||||
const targetName = Array.from(jestTargets)[0];
|
||||
if (nxJson.targetDefaults[targetName]) {
|
||||
Object.assign(jestDefaults, nxJson.targetDefaults[targetName]);
|
||||
}
|
||||
}
|
||||
|
||||
jestDefaults.cache ??= true;
|
||||
|
||||
const inputs = ['default'];
|
||||
inputs.push(nxJson.namedInputs?.production ? '^production' : '^default');
|
||||
if (tree.exists('jest.preset.js')) {
|
||||
inputs.push('{workspaceRoot}/jest.preset.js');
|
||||
}
|
||||
jestDefaults.inputs ??= inputs;
|
||||
|
||||
// Remember if there were already defaults so we don't assume the executor default
|
||||
const passWithNoTestsPreviouslyInDefaults =
|
||||
jestDefaults.options?.passWithNoTests !== undefined;
|
||||
const ciCiPreviouslyInDefaults =
|
||||
jestDefaults.configurations?.ci?.ci !== undefined;
|
||||
const ciCodeCoveragePreviouslyInDefaults =
|
||||
jestDefaults.configurations?.ci?.codeCoverage !== undefined;
|
||||
|
||||
jestDefaults.options ??= {};
|
||||
jestDefaults.options.passWithNoTests ??= true;
|
||||
jestDefaults.configurations ??= {};
|
||||
jestDefaults.configurations.ci ??= {};
|
||||
jestDefaults.configurations.ci.ci ??= true;
|
||||
jestDefaults.configurations.ci.codeCoverage ??= true;
|
||||
|
||||
// Cleanup old target defaults
|
||||
for (const [targetDefaultKey, targetDefault] of Object.entries(
|
||||
nxJson.targetDefaults
|
||||
)) {
|
||||
if (
|
||||
!isTargetDefaultUsed(
|
||||
targetDefault,
|
||||
nxJson.targetDefaults,
|
||||
projects,
|
||||
projectMap
|
||||
)
|
||||
) {
|
||||
delete nxJson.targetDefaults[targetDefaultKey];
|
||||
}
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nx/jest:jest',
|
||||
(value, proj, targetName, configuration) => {
|
||||
const projConfig = projectMap.get(proj);
|
||||
|
||||
if (!configuration) {
|
||||
// Options
|
||||
if (value.passWithNoTests === jestDefaults.options.passWithNoTests) {
|
||||
delete projConfig.targets[targetName].options.passWithNoTests;
|
||||
} else if (!passWithNoTestsPreviouslyInDefaults) {
|
||||
projConfig.targets[targetName].options.passWithNoTests ??= false;
|
||||
}
|
||||
|
||||
if (Object.keys(projConfig.targets[targetName].options).length === 0) {
|
||||
delete projConfig.targets[targetName].options;
|
||||
}
|
||||
} else if (configuration === 'ci') {
|
||||
// CI Config
|
||||
if (value.ci === jestDefaults.configurations.ci.ci) {
|
||||
delete projConfig.targets[targetName].configurations.ci.ci;
|
||||
} else if (ciCiPreviouslyInDefaults) {
|
||||
projConfig.targets[targetName].configurations.ci.ci ??= false;
|
||||
}
|
||||
if (
|
||||
value.codeCoverage === jestDefaults.configurations.ci.codeCoverage
|
||||
) {
|
||||
delete projConfig.targets[targetName].configurations.ci.codeCoverage;
|
||||
} else if (ciCodeCoveragePreviouslyInDefaults) {
|
||||
projConfig.targets[targetName].configurations.ci.codeCoverage ??=
|
||||
false;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.keys(projConfig.targets[targetName].configurations.ci)
|
||||
.length === 0
|
||||
) {
|
||||
delete projConfig.targets[targetName].configurations.ci;
|
||||
}
|
||||
if (
|
||||
Object.keys(projConfig.targets[targetName].configurations).length ===
|
||||
0
|
||||
) {
|
||||
delete projConfig.targets[targetName].configurations;
|
||||
}
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, proj, projConfig);
|
||||
}
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks every target on every project to see if one of them uses the target default
|
||||
*/
|
||||
function isTargetDefaultUsed(
|
||||
targetDefault: Partial<TargetConfiguration>,
|
||||
targetDefaults: TargetDefaults,
|
||||
projects: Record<string, ProjectGraphProjectNode>,
|
||||
projectMap: Map<string, ProjectConfiguration>
|
||||
) {
|
||||
for (const p of Object.values(projects)) {
|
||||
for (const targetName in p.data?.targets ?? {}) {
|
||||
if (
|
||||
readTargetDefaultsForTarget(
|
||||
targetName,
|
||||
targetDefaults,
|
||||
// It might seem like we should use the graph here too but we don't want to pass an executor which was processed in the graph
|
||||
projectMap.get(p.name).targets?.[targetName]?.executor
|
||||
) === targetDefault
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -35,6 +35,11 @@
|
||||
"description": "Change vite-tsconfig-paths plugin for first party nx-vite-tsconfig-paths plugin",
|
||||
"cli": "nx",
|
||||
"implementation": "./src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin"
|
||||
},
|
||||
"move-target-defaults": {
|
||||
"version": "17.1.0-beta.2",
|
||||
"description": "Move target defaults",
|
||||
"implementation": "./src/migrations/update-17-1-0/move-target-defaults"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -81,7 +81,7 @@ describe('@nx/vite:init', () => {
|
||||
const productionNamedInputs = readJson(tree, 'nx.json').namedInputs
|
||||
.production;
|
||||
const vitestDefaults = readJson(tree, 'nx.json').targetDefaults[
|
||||
'@nx/vite:vitest'
|
||||
'@nx/vite:test'
|
||||
];
|
||||
|
||||
expect(productionNamedInputs).toContain(
|
||||
|
||||
@ -93,9 +93,9 @@ export function createVitestConfig(tree: Tree) {
|
||||
}
|
||||
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults['@nx/vite:vitest'] ??= {};
|
||||
nxJson.targetDefaults['@nx/vite:vitest'].cache ??= true;
|
||||
nxJson.targetDefaults['@nx/vite:vitest'].inputs ??= [
|
||||
nxJson.targetDefaults['@nx/vite:test'] ??= {};
|
||||
nxJson.targetDefaults['@nx/vite:test'].cache ??= true;
|
||||
nxJson.targetDefaults['@nx/vite:test'].inputs ??= [
|
||||
'default',
|
||||
productionFileSet ? '^production' : '^default',
|
||||
];
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
import { createTree } from '@nx/devkit/testing';
|
||||
import {
|
||||
addProjectConfiguration as _addProjectConfiguration,
|
||||
ProjectGraph,
|
||||
readNxJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import update from './move-target-defaults';
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual<any>('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||
return projectGraph;
|
||||
}),
|
||||
}));
|
||||
|
||||
function addProjectConfiguration(tree, name, project) {
|
||||
_addProjectConfiguration(tree, name, project);
|
||||
projectGraph.nodes[name] = {
|
||||
name: name,
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: project.root,
|
||||
targets: project.targets,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('move-target-defaults migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTree();
|
||||
writeJson(tree, 'nx.json', {
|
||||
namedInputs: {
|
||||
production: ['default'],
|
||||
},
|
||||
targetDefaults: {
|
||||
test: {
|
||||
cache: true,
|
||||
inputs: ['default', '^production'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
projectGraph = {
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
externalNodes: {},
|
||||
};
|
||||
});
|
||||
|
||||
it('should add options to nx.json target defaults and remove them from projects', async () => {
|
||||
addProjectConfiguration(tree, 'proj1', {
|
||||
root: 'proj1',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nx/vite:test',
|
||||
options: {
|
||||
passWithNoTests: true,
|
||||
reportsDirectory: '../../reports',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readNxJson(tree).targetDefaults).toEqual({
|
||||
'@nx/vite:test': {
|
||||
cache: true,
|
||||
inputs: ['default', '^production'],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,112 @@
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
ProjectConfiguration,
|
||||
ProjectGraphProjectNode,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
TargetDefaults,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptionsInGraph } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { VitestExecutorOptions } from '../../executors/test/schema';
|
||||
import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
// Don't override anything if there are already target defaults for vitest
|
||||
if (nxJson.targetDefaults?.['@nx/vite:test']) {
|
||||
return;
|
||||
}
|
||||
|
||||
nxJson.targetDefaults ??= {};
|
||||
|
||||
/**
|
||||
* A set of targets which does not use any other executors
|
||||
*/
|
||||
const vitestTargets = new Set<string>();
|
||||
const graph = await createProjectGraphAsync();
|
||||
const projectMap = getProjects(tree);
|
||||
|
||||
forEachExecutorOptionsInGraph(
|
||||
graph,
|
||||
'@nx/vite:test',
|
||||
(value, proj, targetName) => {
|
||||
vitestTargets.add(targetName);
|
||||
}
|
||||
);
|
||||
|
||||
// Workspace does not use vitest
|
||||
if (vitestTargets.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the project graph nodes so that targets which are inferred are considered
|
||||
const projects = graph.nodes;
|
||||
|
||||
const vitestDefaults: TargetConfiguration<Partial<VitestExecutorOptions>> =
|
||||
(nxJson.targetDefaults['@nx/vite:test'] = {});
|
||||
|
||||
// All vitest targets have the same name
|
||||
if (vitestTargets.size === 1) {
|
||||
const targetName = Array.from(vitestTargets)[0];
|
||||
if (nxJson.targetDefaults[targetName]) {
|
||||
Object.assign(vitestDefaults, nxJson.targetDefaults[targetName]);
|
||||
}
|
||||
}
|
||||
|
||||
vitestDefaults.cache ??= true;
|
||||
|
||||
const inputs = ['default'];
|
||||
inputs.push(nxJson.namedInputs?.production ? '^production' : '^default');
|
||||
vitestDefaults.inputs ??= inputs;
|
||||
|
||||
// Cleanup old target defaults
|
||||
for (const [targetDefaultKey, targetDefault] of Object.entries(
|
||||
nxJson.targetDefaults
|
||||
)) {
|
||||
if (
|
||||
!isTargetDefaultUsed(
|
||||
targetDefault,
|
||||
nxJson.targetDefaults,
|
||||
projects,
|
||||
projectMap
|
||||
)
|
||||
) {
|
||||
delete nxJson.targetDefaults[targetDefaultKey];
|
||||
}
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks every target on every project to see if one of them uses the target default
|
||||
*/
|
||||
function isTargetDefaultUsed(
|
||||
targetDefault: Partial<TargetConfiguration>,
|
||||
targetDefaults: TargetDefaults,
|
||||
projects: Record<string, ProjectGraphProjectNode>,
|
||||
projectMap: Map<string, ProjectConfiguration>
|
||||
) {
|
||||
for (const p of Object.values(projects)) {
|
||||
for (const targetName in p.data?.targets ?? {}) {
|
||||
if (
|
||||
readTargetDefaultsForTarget(
|
||||
targetName,
|
||||
targetDefaults,
|
||||
// It might seem like we should use the graph here too but we don't want to pass an executor which was processed in the graph
|
||||
projectMap.get(p.name).targets?.[targetName]?.executor
|
||||
) === targetDefault
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user