nx
Version:
136 lines (135 loc) • 6.48 kB
JavaScript
;
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;
}