Skip to content

Commit 4bea408

Browse files
committed
dependsOn back to work
1 parent 2e34dcd commit 4bea408

File tree

1 file changed

+66
-37
lines changed

1 file changed

+66
-37
lines changed

packages/sv/lib/cli/add/index.ts

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,20 @@ export async function promptAddonQuestions({
418418

419419
// run setup if we have access to workspace
420420
// prepare addons (both official and non-official)
421-
// When no addons are selected, use official addons for setup
422-
const officialAddonsList = Array.from(allAddons.values()).filter((addon) =>
423-
officialAddons.some((o) => o.id === addon.id)
424-
);
425-
let setups = selectedAddons.length ? selectedAddons : officialAddonsList;
426-
let addonSetupResults = setupAddons(setups, workspace);
421+
let addonSetupResults: Record<string, AddonSetupResult> = {};
422+
423+
// If we have selected addons, run setup on them (regardless of official status)
424+
if (selectedAddons.length > 0) {
425+
addonSetupResults = setupAddons(selectedAddons, workspace);
426+
}
427427

428428
// prompt which addons to apply (only when no addons were specified)
429429
// Only show selection prompt if no addons were specified at all
430430
if (selectedAddonIds.length === 0) {
431+
// For the prompt, we only show official addons
432+
const officialAddonsList = Array.from(allAddons.values()).filter((addon) =>
433+
officialAddons.some((o) => o.id === addon.id)
434+
);
431435
const allSetupResults = setupAddons(officialAddonsList, workspace);
432436
const addonOptions = officialAddonsList
433437
// only display supported addons relative to the current environment
@@ -456,45 +460,70 @@ export async function promptAddonQuestions({
456460
}
457461
}
458462

459-
// Re-run setup for the newly selected addons
460-
setups = selectedAddons;
461-
addonSetupResults = setupAddons(setups, workspace);
463+
// Re-run setup for all selected addons (including any that were added via CLI options)
464+
addonSetupResults = setupAddons(selectedAddons, workspace);
465+
}
466+
467+
// Ensure all selected addons have setup results
468+
// This should always be the case, but we add a safeguard
469+
const missingSetupResults = selectedAddons.filter((addon) => !addonSetupResults[addon.id]);
470+
if (missingSetupResults.length > 0) {
471+
const additionalSetupResults = setupAddons(missingSetupResults, workspace);
472+
Object.assign(addonSetupResults, additionalSetupResults);
462473
}
463474

464475
// add inter-addon dependencies
465-
for (const addon of selectedAddons) {
466-
const setupResult = addonSetupResults[addon.id];
467-
const missingDependencies = setupResult.dependsOn.filter(
468-
(depId) => !selectedAddons.some((a) => a.id === depId)
469-
);
476+
// We need to iterate until no new dependencies are added (to handle transitive dependencies)
477+
let hasNewDependencies = true;
478+
while (hasNewDependencies) {
479+
hasNewDependencies = false;
480+
const addonsToProcess = [...selectedAddons]; // Work with a snapshot to avoid infinite loops
481+
482+
for (const addon of addonsToProcess) {
483+
const setupResult = addonSetupResults[addon.id];
484+
if (!setupResult) {
485+
common.errorAndExit(`Setup result missing for addon: ${addon.id}`);
486+
}
487+
const missingDependencies = setupResult.dependsOn.filter(
488+
(depId) => !selectedAddons.some((a) => a.id === depId)
489+
);
470490

471-
for (const depId of missingDependencies) {
472-
// Dependencies are always official addons
473-
const depAddon = allAddons.get(depId);
474-
if (!depAddon) {
475-
// If not in resolved addons, try to get it (dependencies are always official)
476-
const officialDep = officialAddons.find((a) => a.id === depId);
477-
if (!officialDep) {
478-
throw new Error(`'${addon.id}' depends on an invalid add-on: '${depId}'`);
491+
for (const depId of missingDependencies) {
492+
hasNewDependencies = true;
493+
// Dependencies are always official addons
494+
const depAddon = allAddons.get(depId);
495+
if (!depAddon) {
496+
// If not in resolved addons, try to get it (dependencies are always official)
497+
const officialDep = officialAddons.find((a) => a.id === depId);
498+
if (!officialDep) {
499+
throw new Error(`'${addon.id}' depends on an invalid add-on: '${depId}'`);
500+
}
501+
// Add official dependency to the map and use it
502+
const officialAddonDetails = getAddonDetails(depId);
503+
allAddons.set(depId, officialAddonDetails);
504+
selectedAddons.push(officialAddonDetails);
505+
answers[depId] = {};
506+
continue;
479507
}
480-
// Add official dependency to the map and use it
481-
const officialAddonDetails = getAddonDetails(depId);
482-
allAddons.set(depId, officialAddonDetails);
483-
selectedAddons.push(officialAddonDetails);
508+
509+
// prompt to install the dependent
510+
const install = await p.confirm({
511+
message: `The ${pc.bold(pc.cyan(addon.id))} add-on requires ${pc.bold(pc.cyan(depId))} to also be setup. ${pc.green('Include it?')}`
512+
});
513+
if (install !== true) {
514+
p.cancel('Operation cancelled.');
515+
process.exit(1);
516+
}
517+
selectedAddons.push(depAddon);
484518
answers[depId] = {};
485-
continue;
486519
}
520+
}
487521

488-
// prompt to install the dependent
489-
const install = await p.confirm({
490-
message: `The ${pc.bold(pc.cyan(addon.id))} add-on requires ${pc.bold(pc.cyan(depId))} to also be setup. ${pc.green('Include it?')}`
491-
});
492-
if (install !== true) {
493-
p.cancel('Operation cancelled.');
494-
process.exit(1);
495-
}
496-
selectedAddons.push(depAddon);
497-
answers[depId] = {};
522+
// Run setup for any newly added dependencies
523+
const newlyAddedAddons = selectedAddons.filter((addon) => !addonSetupResults[addon.id]);
524+
if (newlyAddedAddons.length > 0) {
525+
const newSetupResults = setupAddons(newlyAddedAddons, workspace);
526+
Object.assign(addonSetupResults, newSetupResults);
498527
}
499528
}
500529

0 commit comments

Comments
 (0)