UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

178 lines (177 loc) • 8.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateProject = validateProject; exports.normalizeTarget = normalizeTarget; exports.validateAndNormalizeProjectRootMap = validateAndNormalizeProjectRootMap; const executor_utils_1 = require("../../../command-line/run/executor-utils"); const fileutils_1 = require("../../../utils/fileutils"); const to_project_name_1 = require("../../../config/to-project-name"); const error_types_1 = require("../../error-types"); const target_merging_1 = require("./target-merging"); const node_fs_1 = require("node:fs"); const path_1 = require("path"); function validateProject(project, // name -> project knownProjects) { if (!project.name) { try { const { name } = (0, fileutils_1.readJsonFile)((0, path_1.join)(project.root, 'package.json')); if (!name) { throw new Error(`Project at ${project.root} has no name provided.`); } project.name = name; } catch { throw new error_types_1.ProjectWithNoNameError(project.root); } } else if (knownProjects[project.name] && knownProjects[project.name].root !== project.root) { throw new error_types_1.ProjectWithExistingNameError(project.name, project.root); } } /** * Expand's `command` syntactic sugar, replaces tokens in options, and adds information from executor schema. * @param target The target to normalize * @param project The project that the target belongs to * @returns The normalized target configuration */ function normalizeTarget(target, project, workspaceRoot, projectsMap, errorMsgKey) { target = { ...target, configurations: { ...target.configurations, }, }; target = (0, target_merging_1.resolveCommandSyntacticSugar)(target, project.root); target.options = (0, target_merging_1.resolveNxTokensInOptions)(target.options, project, errorMsgKey); for (const configuration in target.configurations) { target.configurations[configuration] = (0, target_merging_1.resolveNxTokensInOptions)(target.configurations[configuration], project, `${project.root}:${target}:${configuration}`); } target.parallelism ??= true; if (target.executor && !('continuous' in target)) { try { const [executorNodeModule, executorName] = (0, executor_utils_1.parseExecutor)(target.executor); const { schema } = (0, executor_utils_1.getExecutorInformation)(executorNodeModule, executorName, workspaceRoot, projectsMap); if (schema.continuous) { target.continuous ??= schema.continuous; } } catch (e) { // If the executor is not found, we assume that it is not a valid executor. // This means that we should not set the continuous property. // We could throw an error here, but it would be better to just ignore it. } } return target; } function normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, /** * Project configurations keyed by project name */ projects) { const targetErrorMessage = []; for (const targetName in project.targets) { project.targets[targetName] = normalizeTarget(project.targets[targetName], project, workspaceRoot, projects, [project.root, targetName].join(':')); const projectSourceMaps = sourceMaps[project.root]; const targetConfig = project.targets[targetName]; const targetDefaults = (0, target_merging_1.deepClone)((0, target_merging_1.readTargetDefaultsForTarget)(targetName, nxJsonConfiguration.targetDefaults, targetConfig.executor)); // We only apply defaults if they exist if (targetDefaults && (0, target_merging_1.isCompatibleTarget)(targetConfig, targetDefaults)) { project.targets[targetName] = (0, target_merging_1.mergeTargetDefaultWithTargetDefinition)(targetName, project, normalizeTarget(targetDefaults, project, workspaceRoot, projects, ['nx.json[targetDefaults]', targetName].join(':')), projectSourceMaps); } const target = project.targets[targetName]; if ( // If the target has no executor or command, it doesn't do anything !target.executor && !target.command) { // But it may have dependencies that do something if (target.dependsOn && target.dependsOn.length > 0) { target.executor = 'nx:noop'; } else { // If it does nothing, and has no depenencies, // we can remove it. delete project.targets[targetName]; } } if (target.cache && target.continuous) { targetErrorMessage.push(`- "${targetName}" has both "cache" and "continuous" set to true. Continuous targets cannot be cached. Please remove the "cache" property.`); } } if (targetErrorMessage.length > 0) { targetErrorMessage.unshift(`Errors detected in targets of project "${project.name}":`); throw new error_types_1.WorkspaceValidityError(targetErrorMessage.join('\n')); } } function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJsonConfiguration, sourceMaps = {}) { // Name -> Project, used to validate that all projects have unique names const projects = {}; // If there are projects that have the same name, that is an error. // This object tracks name -> (all roots of projects with that name) // to provide better error messaging. const conflicts = new Map(); const projectRootsWithNoName = []; const validityErrors = []; for (const root in projectRootMap) { const project = projectRootMap[root]; // We're setting `// targets` as a comment `targets` is empty due to Project Crystal. // Strip it before returning configuration for usage. if (project['// targets']) delete project['// targets']; // We initially did this in the project.json plugin, but // that resulted in project.json files without names causing // the resulting project to change names from earlier plugins... if (!project.name && (0, node_fs_1.existsSync)((0, path_1.join)(workspaceRoot, project.root, 'project.json'))) { project.name = (0, to_project_name_1.toProjectName)((0, path_1.join)(root, 'project.json')); } try { validateProject(project, projects); projects[project.name] = project; } catch (e) { if ((0, error_types_1.isProjectWithNoNameError)(e)) { projectRootsWithNoName.push(e.projectRoot); } else if ((0, error_types_1.isProjectWithExistingNameError)(e)) { const rootErrors = conflicts.get(e.projectName) ?? [ projects[e.projectName].root, ]; rootErrors.push(e.projectRoot); conflicts.set(e.projectName, rootErrors); } else { throw e; } } } for (const root in projectRootMap) { const project = projectRootMap[root]; try { normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, projects); } catch (e) { if (e instanceof error_types_1.WorkspaceValidityError) { validityErrors.push(e); } else { throw e; } } } const errors = []; if (conflicts.size > 0) { errors.push(new error_types_1.MultipleProjectsWithSameNameError(conflicts, projects)); } if (projectRootsWithNoName.length > 0) { errors.push(new error_types_1.ProjectsWithNoNameError(projectRootsWithNoName, projects)); } if (validityErrors.length > 0) { errors.push(...validityErrors); } if (errors.length > 0) { throw new AggregateError(errors); } return projectRootMap; }