UNPKG

nx

Version:

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

136 lines (135 loc) 6.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTargetDefaultsResults = createTargetDefaultsResults; exports.readTargetDefaultsForTarget = readTargetDefaultsForTarget; const minimatch_1 = require("minimatch"); const globs_1 = require("../../../utils/globs"); const target_merging_1 = require("./target-merging"); const utils_1 = require("./utils"); /** * Builds a synthetic plugin result from nx.json's `targetDefaults`, layered * between specified-plugin and default-plugin results during merging. */ function createTargetDefaultsResults(specifiedPluginRootMap, defaultPluginRootMap, nxJsonConfiguration) { const targetDefaultsConfig = nxJsonConfiguration.targetDefaults; if (!targetDefaultsConfig) { return []; } const syntheticProjects = {}; const allRoots = new Set([ ...Object.keys(specifiedPluginRootMap), ...Object.keys(defaultPluginRootMap), ]); for (const root of allRoots) { const specifiedTargets = specifiedPluginRootMap[root]?.targets ?? {}; const defaultTargets = defaultPluginRootMap[root]?.targets ?? {}; for (const targetName of (0, utils_1.uniqueKeysInObjects)(specifiedTargets, defaultTargets)) { const syntheticTarget = buildSyntheticTargetForRoot(targetName, root, specifiedTargets[targetName], defaultTargets[targetName], targetDefaultsConfig); if (!syntheticTarget) continue; syntheticProjects[root] ??= { root, targets: {} }; syntheticProjects[root].targets[targetName] = syntheticTarget; } } if (Object.keys(syntheticProjects).length === 0) { return []; } return [ [ 'nx/target-defaults', 'nx.json', { projects: syntheticProjects, }, ], ]; } // Returns the synthetic defaults target to insert for `targetName` at // `root`, or undefined if no defaults apply. // Layering: specified plugins < target defaults < default plugins. function buildSyntheticTargetForRoot(targetName, root, specifiedTarget, defaultTarget, targetDefaultsConfig) { const resolvedSpecified = specifiedTarget ? (0, target_merging_1.resolveCommandSyntacticSugar)(specifiedTarget, root) : undefined; const resolvedDefault = defaultTarget ? (0, target_merging_1.resolveCommandSyntacticSugar)(defaultTarget, root) : undefined; // Specified-only: layer defaults on top, but only when the resulting // synthetic is compatible with the specified target. An incompatible // synthetic (e.g. `targetDefaults['test-native'] = { executor: // '@monodon/rust:test' }` while a polyglot plugin infers `test-native` // with `nx:run-commands`) would otherwise replace the specified target // wholesale during the downstream merge. if (resolvedSpecified && !resolvedDefault) { const targetDefaults = readAndPrepareTargetDefaults(targetName, resolvedSpecified.executor, root, targetDefaultsConfig); if (targetDefaults && !(0, target_merging_1.isCompatibleTarget)(resolvedSpecified, targetDefaults)) { return undefined; } return targetDefaults; } // Default-only. if (resolvedDefault && !resolvedSpecified) { return readAndPrepareTargetDefaults(targetName, resolvedDefault.executor, root, targetDefaultsConfig); } if (!resolvedSpecified || !resolvedDefault) return undefined; // Both compatible: use the default plugin's executor for the lookup. // The same incompatibility check applies — a project.json `{}` entry // asks defaults to fill in the target, but the lookup can still fall // back to a target-name keyed default with a foreign executor that // would replace the inferred target. Skip the synthetic in that case. if ((0, target_merging_1.isCompatibleTarget)(resolvedSpecified, resolvedDefault)) { const targetDefaults = readAndPrepareTargetDefaults(targetName, resolvedDefault.executor || resolvedSpecified.executor, root, targetDefaultsConfig); if (targetDefaults && !(0, target_merging_1.isCompatibleTarget)(resolvedSpecified, targetDefaults)) { return undefined; } return targetDefaults; } // Incompatible: default plugin will replace specified; only defaults // matching the default plugin's executor are useful. const targetDefaults = readAndPrepareTargetDefaults(targetName, resolvedDefault.executor, root, targetDefaultsConfig); if (targetDefaults && (0, target_merging_1.isCompatibleTarget)(resolvedDefault, targetDefaults)) { // Stamp executor/command so the default layer merges cleanly on top. return { ...targetDefaults, executor: resolvedDefault.executor, command: resolvedDefault.command, }; } return undefined; } function readAndPrepareTargetDefaults(targetName, executor, root, targetDefaultsConfig) { const rawTargetDefaults = readTargetDefaultsForTarget(targetName, targetDefaultsConfig, executor); if (!rawTargetDefaults) return undefined; return (0, target_merging_1.resolveCommandSyntacticSugar)((0, target_merging_1.deepClone)(rawTargetDefaults), root); } function readTargetDefaultsForTarget(targetName, targetDefaults, executor) { if (executor && targetDefaults?.[executor]) { // If an executor is defined in project.json, defaults should be read // from the most specific key that matches that executor. // e.g. If executor === run-commands, and the target is named build: // Use, use nx:run-commands if it is present // If not, use build if it is present. return targetDefaults?.[executor]; } else if (targetDefaults?.[targetName]) { // If the executor is not defined, the only key we have is the target name. return targetDefaults?.[targetName]; } let matchingTargetDefaultKey = null; for (const key in targetDefaults ?? {}) { if ((0, globs_1.isGlobPattern)(key) && (0, minimatch_1.minimatch)(targetName, key)) { if (!matchingTargetDefaultKey || matchingTargetDefaultKey.length < key.length) { matchingTargetDefaultKey = key; } } } if (matchingTargetDefaultKey) { return targetDefaults[matchingTargetDefaultKey]; } return null; }