298 lines
8.4 KiB
TypeScript
298 lines
8.4 KiB
TypeScript
import {
|
|
addDependenciesToPackageJson,
|
|
addProjectConfiguration,
|
|
formatFiles,
|
|
generateFiles,
|
|
GeneratorCallback,
|
|
getProjects,
|
|
joinPathFragments,
|
|
logger,
|
|
offsetFromRoot,
|
|
ProjectConfiguration,
|
|
readProjectConfiguration,
|
|
runTasksInSerial,
|
|
stripIndents,
|
|
toJS,
|
|
Tree,
|
|
updateJson,
|
|
} from '@nx/devkit';
|
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
|
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
|
|
import {
|
|
getRelativePathToRootTsConfig,
|
|
initGenerator as jsInitGenerator,
|
|
} from '@nx/js';
|
|
import { Linter } from '@nx/eslint';
|
|
import { join } from 'path';
|
|
import { major } from 'semver';
|
|
import { addLinterToCyProject } from '../../utils/add-linter';
|
|
import { installedCypressVersion } from '../../utils/cypress-version';
|
|
import {
|
|
cypressVersion,
|
|
typesNodeVersion,
|
|
viteVersion,
|
|
} from '../../utils/versions';
|
|
import { cypressInitGenerator } from '../init/init';
|
|
import { Schema } from './schema';
|
|
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
|
|
|
export interface CypressProjectSchema extends Schema {
|
|
projectName: string;
|
|
projectRoot: string;
|
|
rootProject: boolean;
|
|
}
|
|
|
|
function createFiles(tree: Tree, options: CypressProjectSchema) {
|
|
// if not installed or >v10 use v10 folder
|
|
// else use v9 folder
|
|
const cypressVersion = installedCypressVersion();
|
|
const cypressFiles =
|
|
cypressVersion && cypressVersion < 10 ? 'v9-and-under' : 'v10-and-after';
|
|
|
|
generateFiles(
|
|
tree,
|
|
join(__dirname, './files', cypressFiles),
|
|
options.projectRoot,
|
|
{
|
|
tmpl: '',
|
|
...options,
|
|
project: options.project || 'Project',
|
|
ext: options.js ? 'js' : 'ts',
|
|
offsetFromRoot: offsetFromRoot(options.projectRoot),
|
|
rootTsConfigPath: getRelativePathToRootTsConfig(
|
|
tree,
|
|
options.projectRoot
|
|
),
|
|
bundler: options.bundler,
|
|
}
|
|
);
|
|
|
|
if (cypressVersion && cypressVersion < 7) {
|
|
updateJson(tree, join(options.projectRoot, 'cypress.json'), (json) => {
|
|
json.pluginsFile = './src/plugins/index';
|
|
return json;
|
|
});
|
|
} else if (cypressVersion < 10) {
|
|
const pluginPath = join(options.projectRoot, 'src/plugins/index.js');
|
|
if (tree.exists(pluginPath)) {
|
|
tree.delete(pluginPath);
|
|
}
|
|
}
|
|
|
|
if (options.js) {
|
|
toJS(tree);
|
|
}
|
|
}
|
|
|
|
function addProject(tree: Tree, options: CypressProjectSchema) {
|
|
let e2eProjectConfig: ProjectConfiguration;
|
|
|
|
const detectedCypressVersion =
|
|
installedCypressVersion() ??
|
|
major(checkAndCleanWithSemver('cypress', cypressVersion));
|
|
|
|
const cypressConfig =
|
|
detectedCypressVersion < 10 ? 'cypress.json' : 'cypress.config.ts';
|
|
|
|
if (options.baseUrl) {
|
|
e2eProjectConfig = {
|
|
root: options.projectRoot,
|
|
sourceRoot: joinPathFragments(options.projectRoot, 'src'),
|
|
projectType: 'application',
|
|
targets: {
|
|
e2e: {
|
|
executor: '@nx/cypress:cypress',
|
|
options: {
|
|
cypressConfig: joinPathFragments(
|
|
options.projectRoot,
|
|
cypressConfig
|
|
),
|
|
baseUrl: options.baseUrl,
|
|
testingType: 'e2e',
|
|
},
|
|
},
|
|
},
|
|
tags: [],
|
|
implicitDependencies: options.project ? [options.project] : undefined,
|
|
};
|
|
} else if (options.project) {
|
|
const project = readProjectConfiguration(tree, options.project);
|
|
|
|
if (!project.targets) {
|
|
logger.warn(stripIndents`
|
|
NOTE: Project, "${options.project}", does not have any targets defined and a baseUrl was not provided. Nx will use
|
|
"${options.project}:serve" as the devServerTarget. But you may need to define this target within the project, "${options.project}".
|
|
`);
|
|
}
|
|
const devServerTarget =
|
|
project.targets?.serve && project.targets?.serve?.defaultConfiguration
|
|
? `${options.project}:serve:${project.targets.serve.defaultConfiguration}`
|
|
: `${options.project}:serve`;
|
|
e2eProjectConfig = {
|
|
root: options.projectRoot,
|
|
sourceRoot: joinPathFragments(options.projectRoot, 'src'),
|
|
projectType: 'application',
|
|
targets: {
|
|
e2e: {
|
|
executor: '@nx/cypress:cypress',
|
|
options: {
|
|
cypressConfig: joinPathFragments(
|
|
options.projectRoot,
|
|
cypressConfig
|
|
),
|
|
devServerTarget,
|
|
testingType: 'e2e',
|
|
},
|
|
configurations: {
|
|
production: {
|
|
devServerTarget: `${options.project}:serve:production`,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
tags: [],
|
|
implicitDependencies: options.project ? [options.project] : undefined,
|
|
};
|
|
if (project.targets?.['serve-static']) {
|
|
e2eProjectConfig.targets.e2e.configurations.ci = {
|
|
devServerTarget: `${options.project}:serve-static`,
|
|
};
|
|
}
|
|
} else {
|
|
throw new Error(`Either project or baseUrl should be specified.`);
|
|
}
|
|
|
|
if (detectedCypressVersion < 7) {
|
|
e2eProjectConfig.targets.e2e.options.tsConfig = joinPathFragments(
|
|
options.projectRoot,
|
|
'tsconfig.json'
|
|
);
|
|
}
|
|
addProjectConfiguration(tree, options.projectName, e2eProjectConfig);
|
|
}
|
|
|
|
/**
|
|
* @deprecated use cypressE2EConfigurationGenerator instead
|
|
**/
|
|
export async function cypressProjectGenerator(host: Tree, schema: Schema) {
|
|
return await cypressProjectGeneratorInternal(host, {
|
|
projectNameAndRootFormat: 'derived',
|
|
addPlugin: false,
|
|
...schema,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @deprecated use cypressE2EConfigurationGenerator instead
|
|
**/
|
|
export async function cypressProjectGeneratorInternal(
|
|
host: Tree,
|
|
schema: Schema
|
|
) {
|
|
const options = await normalizeOptions(host, schema);
|
|
const tasks: GeneratorCallback[] = [];
|
|
const cypressVersion = installedCypressVersion();
|
|
// if there is an installed cypress version, then we don't call
|
|
// init since we want to keep the existing version that is installed
|
|
if (!cypressVersion) {
|
|
tasks.push(await jsInitGenerator(host, { ...options, skipFormat: true }));
|
|
tasks.push(
|
|
await cypressInitGenerator(host, {
|
|
...options,
|
|
skipFormat: true,
|
|
addPlugin: options.addPlugin,
|
|
})
|
|
);
|
|
}
|
|
|
|
createFiles(host, options);
|
|
addProject(host, options);
|
|
const installTask = await addLinterToCyProject(host, {
|
|
...options,
|
|
cypressDir: 'src',
|
|
linter: schema.linter,
|
|
project: options.projectName,
|
|
overwriteExisting: true,
|
|
});
|
|
tasks.push(installTask);
|
|
|
|
if (!options.skipPackageJson) {
|
|
tasks.push(ensureDependencies(host, options));
|
|
}
|
|
|
|
if (!options.skipFormat) {
|
|
await formatFiles(host);
|
|
}
|
|
return runTasksInSerial(...tasks);
|
|
}
|
|
|
|
function ensureDependencies(tree: Tree, options: CypressProjectSchema) {
|
|
const devDependencies: Record<string, string> = {
|
|
'@types/node': typesNodeVersion,
|
|
};
|
|
|
|
if (options.bundler === 'vite') {
|
|
devDependencies['vite'] = viteVersion;
|
|
}
|
|
|
|
return runTasksInSerial(
|
|
...[
|
|
addDependenciesToPackageJson(tree, {}, devDependencies),
|
|
() => {
|
|
logShowProjectCommand(options.projectName);
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
async function normalizeOptions(
|
|
host: Tree,
|
|
options: Schema
|
|
): Promise<CypressProjectSchema> {
|
|
let maybeRootProject: ProjectConfiguration;
|
|
let isRootProject = false;
|
|
|
|
const projects = getProjects(host);
|
|
// nx will set the project option for generators when ran within a project.
|
|
// since the root project will always be set for standalone projects we can just check it here.
|
|
if (options.project) {
|
|
maybeRootProject = projects.get(options.project);
|
|
}
|
|
|
|
if (
|
|
maybeRootProject?.root === '.' ||
|
|
// should still check to see if we are in a standalone based workspace
|
|
(!maybeRootProject &&
|
|
Array.from(projects.values()).some((config) => config.root === '.'))
|
|
) {
|
|
isRootProject = true;
|
|
}
|
|
|
|
let { projectName, projectRoot } = await determineProjectNameAndRootOptions(
|
|
host,
|
|
{
|
|
name: options.name,
|
|
projectType: 'application',
|
|
directory: isRootProject ? options.name : options.directory,
|
|
projectNameAndRootFormat: isRootProject
|
|
? 'as-provided'
|
|
: options.projectNameAndRootFormat,
|
|
callingGenerator: '@nx/cypress:cypress-project',
|
|
}
|
|
);
|
|
|
|
options.linter = options.linter || Linter.EsLint;
|
|
options.bundler = options.bundler || 'webpack';
|
|
options.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false';
|
|
|
|
return {
|
|
...options,
|
|
// other generators depend on the rootProject flag down stream
|
|
rootProject: isRootProject,
|
|
projectName,
|
|
projectRoot,
|
|
};
|
|
}
|
|
|
|
export default cypressProjectGenerator;
|