Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generated/packages/cypress/executors/cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"default": false
},
"tsConfig": {
"x-deprecated": "This option no longer has any effect. Cypress supports typescript out of the box. Add any options directly to <projectRoot>/tsconfig.json or <projectRoot>/cypress/tsconfig.json",
"type": "string",
"description": "The path of the Cypress tsconfig configuration json file.",
"x-completion-type": "file",
Expand Down
6 changes: 6 additions & 0 deletions packages/cypress/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
"version": "16.2.0-beta.0",
"description": "Normalize tsconfig.cy.json files to be located at '<projectRoot>/cypress/tsconfig.json'",
"implementation": "./src/migrations/update-16-2-0/update-cy-tsconfig"
},
"update-16-3-0-remove-old-tsconfigs": {
"cli": "nx",
"version": "16.4.0-beta.10",
"description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.",
"implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps"
}
},
"packageJsonUpdates": {
Expand Down
6 changes: 0 additions & 6 deletions packages/cypress/src/executors/cypress/cypress.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export type Json = { [k: string]: any };
export interface CypressExecutorOptions extends Json {
cypressConfig: string;
watch?: boolean;
tsConfig?: string;
devServerTarget?: string;
headed?: boolean;
/**
Expand Down Expand Up @@ -91,11 +90,6 @@ function normalizeOptions(
context: ExecutorContext
): NormalizedCypressExecutorOptions {
options.env = options.env || {};
if (options.tsConfig) {
const tsConfigPath = join(context.root, options.tsConfig);
options.env.tsConfig = tsConfigPath;
process.env.TS_NODE_PROJECT = tsConfigPath;
}
if (options.testingType === 'component') {
const project = context?.projectGraph?.nodes?.[context.projectName];
if (project?.data?.root) {
Expand Down
1 change: 1 addition & 0 deletions packages/cypress/src/executors/cypress/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"default": false
},
"tsConfig": {
"x-deprecated": "This option no longer has any effect. Cypress supports typescript out of the box. Add any options directly to <projectRoot>/tsconfig.json or <projectRoot>/cypress/tsconfig.json",
"type": "string",
"description": "The path of the Cypress tsconfig configuration json file.",
"x-completion-type": "file",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import {
Tree,
addProjectConfiguration,
readJson,
readProjectConfiguration,
updateJson,
updateProjectConfiguration,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
import { fixLegacyCypressTsconfig } from './tsconfig-sourcemaps';

describe('Cypress Migration: tsconfig-sourcemaps', () => {
let tree: Tree;

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});

it('should remove tsconfig.e2e.json and update tsconfig.json', async () => {
addLegacyProject(tree);

await fixLegacyCypressTsconfig(tree);
expect(readProjectConfiguration(tree, 'legacy-e2e').targets.e2e)
.toMatchInlineSnapshot(`
{
"configurations": {
"production": {
"devServerTarget": "legacy-e2e:serve:production",
},
},
"executor": "@nx/cypress:cypress",
"options": {
"cypressConfig": "apps/legacy-e2e/cypress.config.ts",
"devServerTarget": "legacy-e2e:serve",
"testingType": "e2e",
},
}
`);
expect(readJson(tree, 'apps/legacy-e2e/tsconfig.json'))
.toMatchInlineSnapshot(`
{
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"resolveJsonModule": true,
"sourceMap": false,
"strict": true,
"types": [
"cypress",
"node",
],
},
"exclude": [],
"extends": "../../tsconfig.base.json",
"files": [],
"include": [
"cypress.ci.1.config.ts",
"cypress.ci.2.config.ts",
"cypress.ci.3.config.ts",
"cypress.ci.4.config.ts",
"src/**/*.ts",
"cypress.config.ts",
],
"references": [],
}
`);
expect(tree.exists('apps/legacy-e2e/tsconfig.e2e.json')).toBeFalsy();
expect(tree.exists('apps/legacy-e2e/tsconfig.e2e.prod.json')).toBeFalsy();
});

it('should do nothing if tsconfig option is not used', async () => {
addLegacyProject(tree);

tree.delete('apps/legacy-e2e/tsconfig.e2e.json');
tree.delete('apps/legacy-e2e/tsconfig.e2e.prod.json');
const pc = readProjectConfiguration(tree, 'legacy-e2e');

delete pc.targets.e2e.options.tsConfig;
delete pc.targets.e2e.configurations;

const existingProjectConfig = pc.targets.e2e;

updateProjectConfiguration(tree, 'legacy-e2e', pc);

updateJson(tree, 'apps/legacy-e2e/tsconfig.json', (json) => {
json.references = [];
return json;
});

const existingTsConfig = readJson(tree, 'apps/legacy-e2e/tsconfig.json');

await fixLegacyCypressTsconfig(tree);

expect(readProjectConfiguration(tree, 'legacy-e2e').targets.e2e).toEqual(
existingProjectConfig
);
expect(readJson(tree, 'apps/legacy-e2e/tsconfig.json')).toEqual(
existingTsConfig
);
});
});

function addLegacyProject(tree: Tree) {
addProjectConfiguration(tree, 'legacy-e2e', {
root: 'apps/legacy-e2e',
sourceRoot: 'apps/legacy-e2e/src',
targets: {
e2e: {
executor: '@nx/cypress:cypress',
options: {
cypressConfig: 'apps/legacy-e2e/cypress.config.ts',
tsConfig: 'apps/legacy-e2e/tsconfig.e2e.json',
devServerTarget: 'legacy-e2e:serve',
testingType: 'e2e',
},
configurations: {
production: {
devServerTarget: 'legacy-e2e:serve:production',
tsConfig: 'apps/legacy-e2e/tsconfig.e2e.prod.json',
},
},
},
},
});

tree.write(
'apps/legacy-e2e/tsconfig.e2e.json',
JSON.stringify({
extends: './tsconfig.json',
compilerOptions: {
sourceMap: false,
outDir: '../../dist/out-tsc',
types: ['cypress', 'node'],
},
include: ['src/**/*.ts', 'cypress.config.ts'],
})
);
tree.write(
'apps/legacy-e2e/tsconfig.e2e.prod.json',
JSON.stringify({
extends: './tsconfig.e2e.json',
compilerOptions: {
sourceMap: false,
outDir: '../../dist/out-tsc',
types: ['cypress', 'node'],
strict: true,
},
include: ['src/**/*.ts', 'cypress.config.ts'],
})
);
tree.write(
'apps/legacy-e2e/tsconfig.json',
JSON.stringify({
extends: '../../tsconfig.base.json',
compilerOptions: {
types: ['cypress', 'node'],
resolveJsonModule: true,
},
include: [
'cypress.ci.1.config.ts',
'cypress.ci.2.config.ts',
'cypress.ci.3.config.ts',
'cypress.ci.4.config.ts',
],
files: [],
references: [
{
path: './tsconfig.e2e.json',
},
{
path: './tsconfig.e2e.prod.json',
},
],
})
);
}
102 changes: 102 additions & 0 deletions packages/cypress/src/migrations/update-16-4-0/tsconfig-sourcemaps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
import {
updateJson,
Tree,
getProjects,
joinPathFragments,
readJson,
updateProjectConfiguration,
formatFiles,
} from '@nx/devkit';
import { posix } from 'path';

export async function fixLegacyCypressTsconfig(tree: Tree) {
const projects = getProjects(tree);
forEachExecutorOptions<CypressExecutorOptions>(
tree,
'@nx/cypress:cypress',
(options, projectName, targetName, configName) => {
const projectConfig = projects.get(projectName);

if (
options.testingType !== 'e2e' &&
projectConfig.targets[targetName]?.options?.testingType !== 'e2e'
) {
return;
}

const tsconfigToRemove =
options.tsConfig ??
joinPathFragments(projectConfig.root, 'tsconfig.e2e.json');

const projectLevelConfigPath = joinPathFragments(
projectConfig.root,
'tsconfig.json'
);

if (
!tree.exists(projectLevelConfigPath) ||
!tree.exists(tsconfigToRemove)
) {
return;
}

if (tsconfigToRemove === projectLevelConfigPath) {
updateJson(tree, projectLevelConfigPath, (json) => {
json.compilerOptions = {
sourceMap: false,
...json.compilerOptions,
};
return json;
});
} else {
const e2eConfig = readJson(tree, tsconfigToRemove);

updateJson(tree, projectLevelConfigPath, (json) => {
json.compilerOptions = {
sourceMap: false,
...json.compilerOptions,
...e2eConfig.compilerOptions,
};
json.files = Array.from(
new Set([...(json.files ?? []), ...(e2eConfig.files ?? [])])
);
json.include = Array.from(
new Set([...(json.include ?? []), ...(e2eConfig.include ?? [])])
);
json.exclude = Array.from(
new Set([...(json.exclude ?? []), ...(e2eConfig.exclude ?? [])])
);

// these paths will always be 'unix style'
// and on windows relative will not work on these paths
const tsConfigFromProjRoot = posix.relative(
projectConfig.root,
tsconfigToRemove
);

json.references = (json.references ?? []).filter(
({ path }) => !path.includes(tsConfigFromProjRoot)
);
return json;
});

tree.delete(tsconfigToRemove);
}

if (configName) {
delete projectConfig.targets[targetName].configurations[configName]
.tsConfig;
} else {
delete projectConfig.targets[targetName].options.tsConfig;
}

updateProjectConfiguration(tree, projectName, projectConfig);
}
);

await formatFiles(tree);
}

export default fixLegacyCypressTsconfig;