nx
Version:
178 lines (177 loc) • 8.07 kB
JavaScript
"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;
}