outfitter 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +34 -0
- package/dist/cli.js +1 -1
- package/dist/commands/add.d.ts +54 -0
- package/dist/commands/add.js +16 -0
- package/dist/commands/check.d.ts +91 -0
- package/dist/commands/check.js +14 -0
- package/dist/commands/demo.d.ts +21 -0
- package/dist/commands/demo.js +8 -0
- package/dist/commands/docs-module-loader.d.ts +2 -0
- package/dist/commands/docs-module-loader.js +8 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +13 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +31 -0
- package/dist/commands/migrate-kit.d.ts +2 -0
- package/dist/commands/migrate-kit.js +15 -0
- package/dist/commands/repo.d.ts +3 -0
- package/dist/commands/repo.js +9 -0
- package/dist/commands/scaffold.d.ts +4 -0
- package/dist/commands/scaffold.js +31 -0
- package/dist/commands/shared-deps.d.ts +36 -0
- package/dist/commands/shared-deps.js +10 -0
- package/dist/commands/upgrade-codemods.d.ts +42 -0
- package/dist/commands/upgrade-codemods.js +15 -0
- package/dist/commands/upgrade-planner.d.ts +58 -0
- package/dist/commands/upgrade-planner.js +8 -0
- package/dist/commands/upgrade-workspace.d.ts +76 -0
- package/dist/commands/upgrade-workspace.js +16 -0
- package/dist/commands/upgrade.d.ts +214 -0
- package/dist/commands/upgrade.js +25 -0
- package/dist/create/index.d.ts +5 -0
- package/dist/create/index.js +29 -0
- package/dist/create/planner.d.ts +3 -0
- package/dist/create/planner.js +21 -0
- package/dist/create/presets.d.ts +3 -0
- package/dist/create/presets.js +12 -0
- package/dist/create/types.d.ts +2 -0
- package/dist/create/types.js +1 -0
- package/dist/engine/blocks.d.ts +3 -0
- package/dist/engine/blocks.js +12 -0
- package/dist/engine/collector.d.ts +2 -0
- package/dist/engine/collector.js +8 -0
- package/dist/engine/config.d.ts +3 -0
- package/dist/engine/config.js +12 -0
- package/dist/engine/executor.d.ts +3 -0
- package/dist/engine/executor.js +16 -0
- package/dist/engine/index.d.ts +8 -0
- package/dist/engine/index.js +59 -0
- package/dist/engine/names.d.ts +2 -0
- package/dist/engine/names.js +16 -0
- package/dist/engine/post-scaffold.d.ts +3 -0
- package/dist/engine/post-scaffold.js +8 -0
- package/dist/engine/render-plan.d.ts +7 -0
- package/dist/engine/render-plan.js +9 -0
- package/dist/engine/template.d.ts +3 -0
- package/dist/engine/template.js +17 -0
- package/dist/engine/types.d.ts +2 -0
- package/dist/engine/types.js +8 -0
- package/dist/engine/workspace.d.ts +3 -0
- package/dist/engine/workspace.js +13 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +71 -0
- package/dist/manifest.js +16 -0
- package/dist/output-mode.d.ts +2 -0
- package/dist/output-mode.js +10 -0
- package/dist/shared/{chunk-tpwtpa74.js → chunk-k59f60cp.js} +858 -396
- package/dist/shared/outfitter-193jvzg4.d.ts +5 -0
- package/dist/shared/outfitter-1dd0k853.js +194 -0
- package/dist/shared/outfitter-1dvma85c.js +322 -0
- package/dist/shared/outfitter-1h7k8xxt.js +29 -0
- package/dist/shared/outfitter-2ngep1h2.d.ts +5 -0
- package/dist/shared/outfitter-2np85etz.js +95 -0
- package/dist/shared/outfitter-33w361tc.d.ts +18 -0
- package/dist/shared/outfitter-344t1r38.js +1 -0
- package/dist/shared/outfitter-3weh61w7.d.ts +25 -0
- package/dist/shared/outfitter-4s9meh3j.js +221 -0
- package/dist/shared/outfitter-66b25bj8.js +125 -0
- package/dist/shared/outfitter-6bkqjk86.d.ts +3 -0
- package/dist/shared/outfitter-79vfxt6y.js +269 -0
- package/dist/shared/outfitter-7ha7p61k.d.ts +6 -0
- package/dist/shared/outfitter-7r12fj7f.js +30 -0
- package/dist/shared/outfitter-8y2dfx6n.js +11 -0
- package/dist/shared/outfitter-9x1brcmq.js +184 -0
- package/dist/shared/outfitter-9zqc2njf.js +859 -0
- package/dist/shared/outfitter-a79xrm12.d.ts +17 -0
- package/dist/shared/outfitter-amc4jbs1.d.ts +50 -0
- package/dist/shared/outfitter-ara3djt0.js +73 -0
- package/dist/shared/outfitter-avhm5z6w.js +82 -0
- package/dist/shared/outfitter-bkwpbkr9.d.ts +63 -0
- package/dist/shared/outfitter-bn9c8p2e.js +204 -0
- package/dist/shared/outfitter-bpr28y54.js +70 -0
- package/dist/shared/outfitter-cwq39bv4.d.ts +48 -0
- package/dist/shared/outfitter-d7pq7d0k.js +196 -0
- package/dist/shared/outfitter-dd0btgec.d.ts +40 -0
- package/dist/shared/outfitter-e2zz5wv7.d.ts +51 -0
- package/dist/shared/outfitter-ehp18x1n.js +1 -0
- package/dist/shared/outfitter-gdvm5c0b.d.ts +4 -0
- package/dist/shared/outfitter-h1mnzzd1.d.ts +14 -0
- package/dist/shared/outfitter-hvsaxgcp.js +1 -0
- package/dist/shared/outfitter-hws10ze7.js +532 -0
- package/dist/shared/outfitter-j833sxws.js +61 -0
- package/dist/shared/outfitter-j8yc7294.d.ts +22 -0
- package/dist/shared/outfitter-k112c427.js +21 -0
- package/dist/shared/outfitter-k56rmt24.d.ts +30 -0
- package/dist/shared/outfitter-ksa1pp4t.d.ts +4 -0
- package/dist/shared/outfitter-ksyvwmb5.js +191 -0
- package/dist/shared/outfitter-mdt37hqm.js +4 -0
- package/dist/shared/outfitter-mtbpabf3.js +91 -0
- package/dist/shared/outfitter-mxz69pgy.js +713 -0
- package/dist/shared/outfitter-npemy7ta.d.ts +53 -0
- package/dist/shared/outfitter-npyfbdmc.d.ts +6 -0
- package/dist/shared/outfitter-pyy1zkfh.d.ts +133 -0
- package/dist/shared/outfitter-q9agarmb.js +42 -0
- package/dist/shared/outfitter-qfh36ddg.d.ts +66 -0
- package/dist/shared/outfitter-qn864k6h.js +581 -0
- package/dist/shared/outfitter-rdc5v5ms.js +746 -0
- package/dist/shared/outfitter-sgtq57qr.d.ts +5 -0
- package/dist/shared/outfitter-ttjr95y9.js +98 -0
- package/dist/shared/outfitter-vh4xgb93.js +35 -0
- package/dist/shared/outfitter-yvksv5qb.js +322 -0
- package/dist/shared/outfitter-zwyvewr1.js +36 -0
- package/dist/targets/index.d.ts +4 -0
- package/dist/targets/index.js +29 -0
- package/dist/targets/registry.d.ts +3 -0
- package/dist/targets/registry.js +28 -0
- package/dist/targets/types.d.ts +2 -0
- package/dist/targets/types.js +1 -0
- package/package.json +21 -14
|
@@ -212,12 +212,9 @@ async function printDoctorResults(result, options) {
|
|
|
212
212
|
await output(lines, { mode: "human" });
|
|
213
213
|
}
|
|
214
214
|
function doctorCommand(program) {
|
|
215
|
-
program.command("doctor").description("Validate environment and dependencies").
|
|
215
|
+
program.command("doctor").description("Validate environment and dependencies").action(async (_flags, command) => {
|
|
216
216
|
const resolvedFlags = command.optsWithGlobals();
|
|
217
217
|
const outputOptions = resolvedFlags.json ? { mode: "json" } : undefined;
|
|
218
|
-
if (resolvedFlags.json) {
|
|
219
|
-
process.env["OUTFITTER_JSON"] = "1";
|
|
220
|
-
}
|
|
221
218
|
const result = await runDoctor({ cwd: process.cwd() });
|
|
222
219
|
await printDoctorResults(result, outputOptions);
|
|
223
220
|
process.exit(result.exitCode);
|
|
@@ -2088,12 +2085,11 @@ function initCommand(program) {
|
|
|
2088
2085
|
};
|
|
2089
2086
|
const resolveOutputMode = (flags) => {
|
|
2090
2087
|
if (flags.json) {
|
|
2091
|
-
process.env["OUTFITTER_JSON"] = "1";
|
|
2092
2088
|
return "json";
|
|
2093
2089
|
}
|
|
2094
2090
|
return;
|
|
2095
2091
|
};
|
|
2096
|
-
const withCommonOptions = (command) => command.option("-n, --name <name>", "Package name (defaults to directory name)").option("-b, --bin <name>", "Binary name (defaults to project name)").option("-p, --preset <preset>", "Preset to use (minimal|cli|mcp|daemon)").option("-s, --structure <mode>", "Project structure (single|workspace)").option("--workspace-name <name>", "Workspace root package name").option("-f, --force", "Overwrite existing files", false).option("--local", "Use workspace:* for @outfitter dependencies").option("--workspace", "Alias for --local").option("--with <blocks>", "Comma-separated tooling blocks to add").option("--no-tooling", "Skip default tooling blocks").option("-y, --yes", "Skip prompts and use defaults", false).option("--dry-run", "Preview changes without writing files", false).option("--skip-install", "Skip bun install", false).option("--skip-git", "Skip git init and initial commit", false).option("--skip-commit", "Skip initial commit only", false).option("--install-timeout <ms>", "bun install timeout in ms")
|
|
2092
|
+
const withCommonOptions = (command) => command.option("-n, --name <name>", "Package name (defaults to directory name)").option("-b, --bin <name>", "Binary name (defaults to project name)").option("-p, --preset <preset>", "Preset to use (minimal|cli|mcp|daemon)").option("-s, --structure <mode>", "Project structure (single|workspace)").option("--workspace-name <name>", "Workspace root package name").option("-f, --force", "Overwrite existing files", false).option("--local", "Use workspace:* for @outfitter dependencies").option("--workspace", "Alias for --local").option("--with <blocks>", "Comma-separated tooling blocks to add").option("--no-tooling", "Skip default tooling blocks").option("-y, --yes", "Skip prompts and use defaults", false).option("--dry-run", "Preview changes without writing files", false).option("--skip-install", "Skip bun install", false).option("--skip-git", "Skip git init and initial commit", false).option("--skip-commit", "Skip initial commit only", false).option("--install-timeout <ms>", "bun install timeout in ms");
|
|
2097
2093
|
withCommonOptions(init.argument("[directory]").option("-t, --template <template>", "Template to use (deprecated)")).action(async (directory, flags, command) => {
|
|
2098
2094
|
const targetDir = directory ?? process.cwd();
|
|
2099
2095
|
const resolvedFlags = resolveFlags(flags, command);
|
|
@@ -2904,18 +2900,16 @@ function migrateKitCommand(program) {
|
|
|
2904
2900
|
if (migrate.commands.some((command) => command.name() === "kit")) {
|
|
2905
2901
|
return;
|
|
2906
2902
|
}
|
|
2907
|
-
migrate.command("kit [directory]").description("Migrate foundation imports and dependencies to @outfitter/kit").option("--dry-run", "Preview changes without writing files", false).
|
|
2908
|
-
|
|
2909
|
-
process.env["OUTFITTER_JSON"] = "1";
|
|
2910
|
-
}
|
|
2903
|
+
migrate.command("kit [directory]").description("Migrate foundation imports and dependencies to @outfitter/kit").option("--dry-run", "Preview changes without writing files", false).action(async (directory, _flags, command) => {
|
|
2904
|
+
const resolvedFlags = command.optsWithGlobals();
|
|
2911
2905
|
const result = await runMigrateKit({
|
|
2912
2906
|
...directory ? { targetDir: directory } : {},
|
|
2913
|
-
dryRun: Boolean(
|
|
2907
|
+
dryRun: Boolean(resolvedFlags.dryRun)
|
|
2914
2908
|
});
|
|
2915
2909
|
if (result.isErr()) {
|
|
2916
2910
|
throw result.error;
|
|
2917
2911
|
}
|
|
2918
|
-
await printMigrateKitResults(result.value,
|
|
2912
|
+
await printMigrateKitResults(result.value, resolvedFlags.json ? { mode: "json" } : undefined);
|
|
2919
2913
|
});
|
|
2920
2914
|
}
|
|
2921
2915
|
|
|
@@ -3395,20 +3389,21 @@ async function printScaffoldResults(result, options) {
|
|
|
3395
3389
|
await output6(lines, { mode: "human" });
|
|
3396
3390
|
}
|
|
3397
3391
|
function scaffoldCommand(program) {
|
|
3398
|
-
program.command("scaffold <target> [name]").description("Add a capability to an existing project").option("-f, --force", "Overwrite existing files", false).option("--skip-install", "Skip bun install", false).option("--dry-run", "Preview changes without executing", false).option("--with <blocks>", "Comma-separated tooling blocks to add").option("--no-tooling", "Skip default tooling blocks").option("--local", "Use workspace:* for @outfitter dependencies").option("--install-timeout <ms>", "bun install timeout in ms").
|
|
3399
|
-
const
|
|
3392
|
+
program.command("scaffold <target> [name]").description("Add a capability to an existing project").option("-f, --force", "Overwrite existing files", false).option("--skip-install", "Skip bun install", false).option("--dry-run", "Preview changes without executing", false).option("--with <blocks>", "Comma-separated tooling blocks to add").option("--no-tooling", "Skip default tooling blocks").option("--local", "Use workspace:* for @outfitter dependencies").option("--install-timeout <ms>", "bun install timeout in ms").action(async (target, name, _flags, command) => {
|
|
3393
|
+
const resolvedFlags = command.optsWithGlobals();
|
|
3394
|
+
const mode = resolvedFlags.json ? "json" : undefined;
|
|
3400
3395
|
const outputOptions = mode ? { mode } : undefined;
|
|
3401
3396
|
const result = await runScaffold({
|
|
3402
3397
|
target,
|
|
3403
3398
|
name,
|
|
3404
|
-
force: Boolean(
|
|
3405
|
-
skipInstall: Boolean(
|
|
3406
|
-
dryRun: Boolean(
|
|
3407
|
-
with:
|
|
3408
|
-
noTooling:
|
|
3409
|
-
local:
|
|
3399
|
+
force: Boolean(resolvedFlags.force),
|
|
3400
|
+
skipInstall: Boolean(resolvedFlags.skipInstall),
|
|
3401
|
+
dryRun: Boolean(resolvedFlags.dryRun),
|
|
3402
|
+
with: resolvedFlags.with,
|
|
3403
|
+
noTooling: resolvedFlags.noTooling,
|
|
3404
|
+
local: resolvedFlags.local,
|
|
3410
3405
|
cwd: process.cwd(),
|
|
3411
|
-
...
|
|
3406
|
+
...resolvedFlags.installTimeout !== undefined ? { installTimeout: resolvedFlags.installTimeout } : {}
|
|
3412
3407
|
});
|
|
3413
3408
|
if (result.isErr()) {
|
|
3414
3409
|
exitWithError2(result.error, outputOptions);
|
|
@@ -3419,14 +3414,22 @@ function scaffoldCommand(program) {
|
|
|
3419
3414
|
}
|
|
3420
3415
|
|
|
3421
3416
|
// src/actions.ts
|
|
3422
|
-
import { resolve as
|
|
3417
|
+
import { resolve as resolve11 } from "node:path";
|
|
3423
3418
|
import { output as output10 } from "@outfitter/cli";
|
|
3424
|
-
import {
|
|
3419
|
+
import { actionCliPresets } from "@outfitter/cli/actions";
|
|
3420
|
+
import {
|
|
3421
|
+
booleanFlagPreset,
|
|
3422
|
+
cwdPreset,
|
|
3423
|
+
dryRunPreset,
|
|
3424
|
+
forcePreset,
|
|
3425
|
+
interactionPreset,
|
|
3426
|
+
verbosePreset
|
|
3427
|
+
} from "@outfitter/cli/flags";
|
|
3425
3428
|
import {
|
|
3426
3429
|
createActionRegistry,
|
|
3427
3430
|
defineAction,
|
|
3428
|
-
InternalError as
|
|
3429
|
-
Result as
|
|
3431
|
+
InternalError as InternalError5,
|
|
3432
|
+
Result as Result17
|
|
3430
3433
|
} from "@outfitter/contracts";
|
|
3431
3434
|
import { z as z2 } from "zod";
|
|
3432
3435
|
|
|
@@ -3841,14 +3844,137 @@ async function runDemo(options) {
|
|
|
3841
3844
|
`));
|
|
3842
3845
|
}
|
|
3843
3846
|
|
|
3844
|
-
// src/commands/
|
|
3845
|
-
import { existsSync as
|
|
3846
|
-
import { join as
|
|
3847
|
+
// src/commands/upgrade.ts
|
|
3848
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3849
|
+
import { join as join14, resolve as resolve10 } from "node:path";
|
|
3847
3850
|
import { output as output9 } from "@outfitter/cli";
|
|
3848
|
-
import { InternalError as
|
|
3851
|
+
import { InternalError as InternalError4, Result as Result16 } from "@outfitter/contracts";
|
|
3849
3852
|
import { createTheme as createTheme3 } from "@outfitter/tui/render";
|
|
3850
3853
|
|
|
3851
|
-
// src/commands/
|
|
3854
|
+
// src/commands/upgrade-codemods.ts
|
|
3855
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
3856
|
+
import { isAbsolute, join as join12, relative as relative4, resolve as resolve8 } from "node:path";
|
|
3857
|
+
import { InternalError as InternalError2, Result as Result14 } from "@outfitter/contracts";
|
|
3858
|
+
var CODEMOD_PATHS = [
|
|
3859
|
+
"plugins/outfitter/shared/codemods",
|
|
3860
|
+
"node_modules/@outfitter/kit/shared/codemods"
|
|
3861
|
+
];
|
|
3862
|
+
function findCodemodsDir(cwd, binaryDir) {
|
|
3863
|
+
for (const relativePath of CODEMOD_PATHS) {
|
|
3864
|
+
const dir = join12(cwd, relativePath);
|
|
3865
|
+
if (existsSync11(dir))
|
|
3866
|
+
return dir;
|
|
3867
|
+
}
|
|
3868
|
+
let current = resolve8(cwd);
|
|
3869
|
+
const root = resolve8("/");
|
|
3870
|
+
while (current !== root) {
|
|
3871
|
+
const parent = resolve8(current, "..");
|
|
3872
|
+
if (parent === current)
|
|
3873
|
+
break;
|
|
3874
|
+
current = parent;
|
|
3875
|
+
for (const relativePath of CODEMOD_PATHS) {
|
|
3876
|
+
const dir = join12(current, relativePath);
|
|
3877
|
+
if (existsSync11(dir))
|
|
3878
|
+
return dir;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
const resolvedBinaryDir = binaryDir ?? resolve8(import.meta.dir, "../../../..");
|
|
3882
|
+
for (const relativePath of CODEMOD_PATHS) {
|
|
3883
|
+
const dir = join12(resolvedBinaryDir, relativePath);
|
|
3884
|
+
if (existsSync11(dir))
|
|
3885
|
+
return dir;
|
|
3886
|
+
}
|
|
3887
|
+
return null;
|
|
3888
|
+
}
|
|
3889
|
+
function discoverCodemods(migrationsDir, codemodsDir, shortName, fromVersion, toVersion) {
|
|
3890
|
+
const resolvedCodemodsDir = resolve8(codemodsDir);
|
|
3891
|
+
const docs = (() => {
|
|
3892
|
+
try {
|
|
3893
|
+
return readMigrationDocsWithMetadata(migrationsDir, shortName, fromVersion, toVersion);
|
|
3894
|
+
} catch {
|
|
3895
|
+
return [];
|
|
3896
|
+
}
|
|
3897
|
+
})();
|
|
3898
|
+
const seen = new Set;
|
|
3899
|
+
const codemods = [];
|
|
3900
|
+
for (const doc of docs) {
|
|
3901
|
+
if (!doc.frontmatter.changes)
|
|
3902
|
+
continue;
|
|
3903
|
+
for (const change of doc.frontmatter.changes) {
|
|
3904
|
+
if (!change.codemod)
|
|
3905
|
+
continue;
|
|
3906
|
+
if (seen.has(change.codemod))
|
|
3907
|
+
continue;
|
|
3908
|
+
seen.add(change.codemod);
|
|
3909
|
+
const absolutePath = resolveCodemodPath(resolvedCodemodsDir, change.codemod);
|
|
3910
|
+
if (absolutePath === null)
|
|
3911
|
+
continue;
|
|
3912
|
+
if (!existsSync11(absolutePath))
|
|
3913
|
+
continue;
|
|
3914
|
+
codemods.push({
|
|
3915
|
+
relativePath: change.codemod,
|
|
3916
|
+
absolutePath
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
return codemods;
|
|
3921
|
+
}
|
|
3922
|
+
function resolveCodemodPath(codemodsDir, relativePath) {
|
|
3923
|
+
if (relativePath.trim().length === 0) {
|
|
3924
|
+
return null;
|
|
3925
|
+
}
|
|
3926
|
+
if (isAbsolute(relativePath)) {
|
|
3927
|
+
return null;
|
|
3928
|
+
}
|
|
3929
|
+
const resolvedPath = resolve8(codemodsDir, relativePath);
|
|
3930
|
+
const relPath = relative4(codemodsDir, resolvedPath);
|
|
3931
|
+
if (relPath === "" || relPath.startsWith("..") || isAbsolute(relPath)) {
|
|
3932
|
+
return null;
|
|
3933
|
+
}
|
|
3934
|
+
return resolvedPath;
|
|
3935
|
+
}
|
|
3936
|
+
async function runCodemod(codemodPath, targetDir, dryRun) {
|
|
3937
|
+
let mod;
|
|
3938
|
+
try {
|
|
3939
|
+
mod = await import(codemodPath);
|
|
3940
|
+
} catch (error) {
|
|
3941
|
+
return Result14.err(InternalError2.create("Failed to load codemod", {
|
|
3942
|
+
codemodPath,
|
|
3943
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3944
|
+
}));
|
|
3945
|
+
}
|
|
3946
|
+
if (typeof mod["transform"] !== "function") {
|
|
3947
|
+
return Result14.err(InternalError2.create(`Codemod has no transform export: ${codemodPath}`, {
|
|
3948
|
+
codemodPath
|
|
3949
|
+
}));
|
|
3950
|
+
}
|
|
3951
|
+
const transform = mod["transform"];
|
|
3952
|
+
try {
|
|
3953
|
+
const result = await transform({ targetDir, dryRun });
|
|
3954
|
+
if (!isCodemodResult(result)) {
|
|
3955
|
+
return Result14.err(InternalError2.create("Codemod returned invalid result shape", {
|
|
3956
|
+
codemodPath
|
|
3957
|
+
}));
|
|
3958
|
+
}
|
|
3959
|
+
return Result14.ok(result);
|
|
3960
|
+
} catch (error) {
|
|
3961
|
+
return Result14.err(InternalError2.create("Codemod execution failed", {
|
|
3962
|
+
codemodPath,
|
|
3963
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3964
|
+
}));
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
function isCodemodResult(value) {
|
|
3968
|
+
if (typeof value !== "object" || value === null)
|
|
3969
|
+
return false;
|
|
3970
|
+
const candidate = value;
|
|
3971
|
+
return isStringArray(candidate["changedFiles"]) && isStringArray(candidate["skippedFiles"]) && isStringArray(candidate["errors"]);
|
|
3972
|
+
}
|
|
3973
|
+
function isStringArray(value) {
|
|
3974
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
// src/commands/upgrade-planner.ts
|
|
3852
3978
|
function getMajor(version) {
|
|
3853
3979
|
const parts = version.split(".");
|
|
3854
3980
|
return Number.parseInt(parts[0] ?? "0", 10);
|
|
@@ -3885,7 +4011,7 @@ function classify(currentVersion, latestVersion, breakingFlag) {
|
|
|
3885
4011
|
}
|
|
3886
4012
|
return "upgradableNonBreaking";
|
|
3887
4013
|
}
|
|
3888
|
-
function
|
|
4014
|
+
function analyzeUpgrades(installed, latest, migrationDocs) {
|
|
3889
4015
|
const packages = [];
|
|
3890
4016
|
for (const [name, currentVersion] of installed) {
|
|
3891
4017
|
const latestInfo = latest.get(name);
|
|
@@ -3916,24 +4042,24 @@ function analyzeUpdates(installed, latest, migrationDocs) {
|
|
|
3916
4042
|
return { packages, summary };
|
|
3917
4043
|
}
|
|
3918
4044
|
|
|
3919
|
-
// src/commands/
|
|
3920
|
-
import { existsSync as
|
|
3921
|
-
import { basename as basename5, dirname as dirname7, join as
|
|
3922
|
-
import { InternalError as
|
|
4045
|
+
// src/commands/upgrade-workspace.ts
|
|
4046
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
4047
|
+
import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve9 } from "node:path";
|
|
4048
|
+
import { InternalError as InternalError3, Result as Result15 } from "@outfitter/contracts";
|
|
3923
4049
|
function detectWorkspaceRoot2(cwd) {
|
|
3924
|
-
let current =
|
|
3925
|
-
const root =
|
|
4050
|
+
let current = resolve9(cwd);
|
|
4051
|
+
const root = resolve9("/");
|
|
3926
4052
|
while (true) {
|
|
3927
|
-
if (
|
|
3928
|
-
return
|
|
4053
|
+
if (existsSync12(join13(current, "pnpm-workspace.yaml"))) {
|
|
4054
|
+
return Result15.ok(current);
|
|
3929
4055
|
}
|
|
3930
|
-
const pkgPath =
|
|
3931
|
-
if (
|
|
4056
|
+
const pkgPath = join13(current, "package.json");
|
|
4057
|
+
if (existsSync12(pkgPath)) {
|
|
3932
4058
|
try {
|
|
3933
4059
|
const raw = readFileSync9(pkgPath, "utf-8");
|
|
3934
4060
|
const pkg = JSON.parse(raw);
|
|
3935
4061
|
if (hasWorkspacesField3(pkg)) {
|
|
3936
|
-
return
|
|
4062
|
+
return Result15.ok(current);
|
|
3937
4063
|
}
|
|
3938
4064
|
} catch {}
|
|
3939
4065
|
}
|
|
@@ -3946,7 +4072,7 @@ function detectWorkspaceRoot2(cwd) {
|
|
|
3946
4072
|
}
|
|
3947
4073
|
current = parent;
|
|
3948
4074
|
}
|
|
3949
|
-
return
|
|
4075
|
+
return Result15.ok(null);
|
|
3950
4076
|
}
|
|
3951
4077
|
function hasWorkspacesField3(pkg) {
|
|
3952
4078
|
const workspaces = pkg.workspaces;
|
|
@@ -3991,10 +4117,10 @@ function normalizeWorkspacePattern2(pattern) {
|
|
|
3991
4117
|
return `${value}/package.json`;
|
|
3992
4118
|
}
|
|
3993
4119
|
function collectWorkspaceManifests(rootDir) {
|
|
3994
|
-
const resolvedRoot =
|
|
3995
|
-
const rootPackageJson =
|
|
3996
|
-
if (!
|
|
3997
|
-
return
|
|
4120
|
+
const resolvedRoot = resolve9(rootDir);
|
|
4121
|
+
const rootPackageJson = join13(resolvedRoot, "package.json");
|
|
4122
|
+
if (!existsSync12(rootPackageJson)) {
|
|
4123
|
+
return Result15.err(InternalError3.create("No package.json found at workspace root", {
|
|
3998
4124
|
rootDir: resolvedRoot
|
|
3999
4125
|
}));
|
|
4000
4126
|
}
|
|
@@ -4003,7 +4129,7 @@ function collectWorkspaceManifests(rootDir) {
|
|
|
4003
4129
|
const raw = readFileSync9(rootPackageJson, "utf-8");
|
|
4004
4130
|
pkg = JSON.parse(raw);
|
|
4005
4131
|
} catch {
|
|
4006
|
-
return
|
|
4132
|
+
return Result15.err(InternalError3.create("Invalid JSON in root package.json", {
|
|
4007
4133
|
rootDir: resolvedRoot
|
|
4008
4134
|
}));
|
|
4009
4135
|
}
|
|
@@ -4014,13 +4140,13 @@ function collectWorkspaceManifests(rootDir) {
|
|
|
4014
4140
|
continue;
|
|
4015
4141
|
const glob = new Bun.Glob(pattern);
|
|
4016
4142
|
for (const entry of glob.scanSync({ cwd: resolvedRoot })) {
|
|
4017
|
-
const absolute =
|
|
4018
|
-
if (
|
|
4143
|
+
const absolute = resolve9(resolvedRoot, entry);
|
|
4144
|
+
if (existsSync12(absolute) && basename5(absolute) === "package.json") {
|
|
4019
4145
|
files.add(absolute);
|
|
4020
4146
|
}
|
|
4021
4147
|
}
|
|
4022
4148
|
}
|
|
4023
|
-
return
|
|
4149
|
+
return Result15.ok(Array.from(files).sort((a, b) => a.localeCompare(b)));
|
|
4024
4150
|
}
|
|
4025
4151
|
function extractOutfitterDeps(manifestPath2) {
|
|
4026
4152
|
let pkg;
|
|
@@ -4028,7 +4154,7 @@ function extractOutfitterDeps(manifestPath2) {
|
|
|
4028
4154
|
const raw = readFileSync9(manifestPath2, "utf-8");
|
|
4029
4155
|
pkg = JSON.parse(raw);
|
|
4030
4156
|
} catch {
|
|
4031
|
-
return
|
|
4157
|
+
return Result15.err(InternalError3.create("Failed to parse package.json", {
|
|
4032
4158
|
path: manifestPath2
|
|
4033
4159
|
}));
|
|
4034
4160
|
}
|
|
@@ -4064,10 +4190,10 @@ function extractOutfitterDeps(manifestPath2) {
|
|
|
4064
4190
|
}
|
|
4065
4191
|
packages.push({ name, version: cleaned });
|
|
4066
4192
|
}
|
|
4067
|
-
return
|
|
4193
|
+
return Result15.ok(packages);
|
|
4068
4194
|
}
|
|
4069
4195
|
function getInstalledPackagesFromWorkspace(rootDir) {
|
|
4070
|
-
const resolvedRoot =
|
|
4196
|
+
const resolvedRoot = resolve9(rootDir);
|
|
4071
4197
|
const wsRootResult = detectWorkspaceRoot2(resolvedRoot);
|
|
4072
4198
|
if (wsRootResult.isErr())
|
|
4073
4199
|
return wsRootResult;
|
|
@@ -4079,9 +4205,9 @@ function getInstalledPackagesFromWorkspace(rootDir) {
|
|
|
4079
4205
|
return manifestsResult;
|
|
4080
4206
|
manifestPaths = manifestsResult.value;
|
|
4081
4207
|
} else {
|
|
4082
|
-
const rootPkg =
|
|
4083
|
-
if (!
|
|
4084
|
-
return
|
|
4208
|
+
const rootPkg = join13(resolvedRoot, "package.json");
|
|
4209
|
+
if (!existsSync12(rootPkg)) {
|
|
4210
|
+
return Result15.err(InternalError3.create("No package.json found", { cwd: resolvedRoot }));
|
|
4085
4211
|
}
|
|
4086
4212
|
manifestPaths = [rootPkg];
|
|
4087
4213
|
}
|
|
@@ -4131,7 +4257,7 @@ function getInstalledPackagesFromWorkspace(rootDir) {
|
|
|
4131
4257
|
}
|
|
4132
4258
|
}
|
|
4133
4259
|
packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
4134
|
-
return
|
|
4260
|
+
return Result15.ok({
|
|
4135
4261
|
packages,
|
|
4136
4262
|
conflicts,
|
|
4137
4263
|
manifestsByPackage,
|
|
@@ -4160,7 +4286,7 @@ async function applyUpdatesToWorkspace(manifestPaths, manifestsByPackage, update
|
|
|
4160
4286
|
try {
|
|
4161
4287
|
raw = readFileSync9(manifestPath2, "utf-8");
|
|
4162
4288
|
} catch {
|
|
4163
|
-
return
|
|
4289
|
+
return Result15.err(InternalError3.create("Failed to read package.json for apply", {
|
|
4164
4290
|
path: manifestPath2
|
|
4165
4291
|
}));
|
|
4166
4292
|
}
|
|
@@ -4168,7 +4294,7 @@ async function applyUpdatesToWorkspace(manifestPaths, manifestsByPackage, update
|
|
|
4168
4294
|
try {
|
|
4169
4295
|
pkg = JSON.parse(raw);
|
|
4170
4296
|
} catch {
|
|
4171
|
-
return
|
|
4297
|
+
return Result15.err(InternalError3.create("Invalid JSON in package.json", {
|
|
4172
4298
|
path: manifestPath2
|
|
4173
4299
|
}));
|
|
4174
4300
|
}
|
|
@@ -4198,13 +4324,13 @@ async function applyUpdatesToWorkspace(manifestPaths, manifestsByPackage, update
|
|
|
4198
4324
|
`;
|
|
4199
4325
|
await Bun.write(manifestPath2, updated);
|
|
4200
4326
|
} catch {
|
|
4201
|
-
return
|
|
4327
|
+
return Result15.err(InternalError3.create("Failed to write updated package.json", {
|
|
4202
4328
|
path: manifestPath2
|
|
4203
4329
|
}));
|
|
4204
4330
|
}
|
|
4205
4331
|
}
|
|
4206
4332
|
}
|
|
4207
|
-
return
|
|
4333
|
+
return Result15.ok(undefined);
|
|
4208
4334
|
}
|
|
4209
4335
|
function getVersionPrefix(specifier) {
|
|
4210
4336
|
if (specifier.startsWith("workspace:")) {
|
|
@@ -4224,19 +4350,20 @@ async function runInstall(cwd) {
|
|
|
4224
4350
|
const exitCode = await proc.exited;
|
|
4225
4351
|
if (exitCode !== 0) {
|
|
4226
4352
|
const stderr = await new Response(proc.stderr).text();
|
|
4227
|
-
return
|
|
4353
|
+
return Result15.err(InternalError3.create("bun install failed", {
|
|
4228
4354
|
cwd,
|
|
4229
4355
|
exitCode,
|
|
4230
4356
|
stderr: stderr.trim()
|
|
4231
4357
|
}));
|
|
4232
4358
|
}
|
|
4233
4359
|
} catch {
|
|
4234
|
-
return
|
|
4360
|
+
return Result15.err(InternalError3.create("Failed to run bun install", { cwd }));
|
|
4235
4361
|
}
|
|
4236
|
-
return
|
|
4362
|
+
return Result15.ok(undefined);
|
|
4237
4363
|
}
|
|
4238
4364
|
|
|
4239
|
-
// src/commands/
|
|
4365
|
+
// src/commands/upgrade.ts
|
|
4366
|
+
var FRONTMATTER_BLOCK_REGEX = /^---\r?\n[\s\S]*?\r?\n---\r?\n*/;
|
|
4240
4367
|
async function getLatestVersion(name) {
|
|
4241
4368
|
try {
|
|
4242
4369
|
const proc = Bun.spawn(["npm", "view", name, "version"], {
|
|
@@ -4257,28 +4384,28 @@ var MIGRATION_DOC_PATHS = [
|
|
|
4257
4384
|
"node_modules/@outfitter/kit/shared/migrations"
|
|
4258
4385
|
];
|
|
4259
4386
|
function findMigrationDocsDir(cwd, binaryDir) {
|
|
4260
|
-
for (const
|
|
4261
|
-
const dir =
|
|
4262
|
-
if (
|
|
4387
|
+
for (const relative5 of MIGRATION_DOC_PATHS) {
|
|
4388
|
+
const dir = join14(cwd, relative5);
|
|
4389
|
+
if (existsSync13(dir))
|
|
4263
4390
|
return dir;
|
|
4264
4391
|
}
|
|
4265
|
-
let current =
|
|
4266
|
-
const root =
|
|
4392
|
+
let current = resolve10(cwd);
|
|
4393
|
+
const root = resolve10("/");
|
|
4267
4394
|
while (current !== root) {
|
|
4268
|
-
const parent =
|
|
4395
|
+
const parent = resolve10(current, "..");
|
|
4269
4396
|
if (parent === current)
|
|
4270
4397
|
break;
|
|
4271
4398
|
current = parent;
|
|
4272
|
-
for (const
|
|
4273
|
-
const dir =
|
|
4274
|
-
if (
|
|
4399
|
+
for (const relative5 of MIGRATION_DOC_PATHS) {
|
|
4400
|
+
const dir = join14(current, relative5);
|
|
4401
|
+
if (existsSync13(dir))
|
|
4275
4402
|
return dir;
|
|
4276
4403
|
}
|
|
4277
4404
|
}
|
|
4278
|
-
const resolvedBinaryDir = binaryDir ??
|
|
4279
|
-
for (const
|
|
4280
|
-
const dir =
|
|
4281
|
-
if (
|
|
4405
|
+
const resolvedBinaryDir = binaryDir ?? resolve10(import.meta.dir, "../../../..");
|
|
4406
|
+
for (const relative5 of MIGRATION_DOC_PATHS) {
|
|
4407
|
+
const dir = join14(resolvedBinaryDir, relative5);
|
|
4408
|
+
if (existsSync13(dir))
|
|
4282
4409
|
return dir;
|
|
4283
4410
|
}
|
|
4284
4411
|
return null;
|
|
@@ -4296,14 +4423,14 @@ function readMigrationDocs(migrationsDir, shortName, fromVersion, toVersion) {
|
|
|
4296
4423
|
continue;
|
|
4297
4424
|
if (Bun.semver.order(docVersion, toVersion) > 0)
|
|
4298
4425
|
continue;
|
|
4299
|
-
const filePath =
|
|
4426
|
+
const filePath = join14(migrationsDir, entry);
|
|
4300
4427
|
let content;
|
|
4301
4428
|
try {
|
|
4302
4429
|
content = readFileSync10(filePath, "utf-8");
|
|
4303
4430
|
} catch {
|
|
4304
4431
|
continue;
|
|
4305
4432
|
}
|
|
4306
|
-
const body = content.replace(
|
|
4433
|
+
const body = content.replace(FRONTMATTER_BLOCK_REGEX, "").trim();
|
|
4307
4434
|
if (body) {
|
|
4308
4435
|
docs.push({ version: docVersion, content: body });
|
|
4309
4436
|
}
|
|
@@ -4312,8 +4439,8 @@ function readMigrationDocs(migrationsDir, shortName, fromVersion, toVersion) {
|
|
|
4312
4439
|
return docs.map((d) => d.content);
|
|
4313
4440
|
}
|
|
4314
4441
|
function readMigrationBreakingFlag(migrationsDir, shortName, version) {
|
|
4315
|
-
const filePath =
|
|
4316
|
-
if (!
|
|
4442
|
+
const filePath = join14(migrationsDir, `outfitter-${shortName}-${version}.md`);
|
|
4443
|
+
if (!existsSync13(filePath)) {
|
|
4317
4444
|
return;
|
|
4318
4445
|
}
|
|
4319
4446
|
let content;
|
|
@@ -4337,23 +4464,176 @@ function readMigrationBreakingFlag(migrationsDir, shortName, version) {
|
|
|
4337
4464
|
return false;
|
|
4338
4465
|
return;
|
|
4339
4466
|
}
|
|
4467
|
+
var VALID_CHANGE_TYPES = new Set([
|
|
4468
|
+
"renamed",
|
|
4469
|
+
"removed",
|
|
4470
|
+
"signature-changed",
|
|
4471
|
+
"moved",
|
|
4472
|
+
"deprecated",
|
|
4473
|
+
"added"
|
|
4474
|
+
]);
|
|
4475
|
+
function parseYamlValue(raw) {
|
|
4476
|
+
const trimmed = raw.trim();
|
|
4477
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
4478
|
+
return trimmed.slice(1, -1);
|
|
4479
|
+
}
|
|
4480
|
+
return trimmed;
|
|
4481
|
+
}
|
|
4482
|
+
function parseMigrationFrontmatter(content) {
|
|
4483
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
4484
|
+
if (!fmMatch?.[1])
|
|
4485
|
+
return null;
|
|
4486
|
+
const fmBlock = fmMatch[1];
|
|
4487
|
+
const lines = fmBlock.split(/\r?\n/);
|
|
4488
|
+
let pkg;
|
|
4489
|
+
let version;
|
|
4490
|
+
let breaking;
|
|
4491
|
+
let changesStartIdx = -1;
|
|
4492
|
+
for (let i = 0;i < lines.length; i++) {
|
|
4493
|
+
const line = lines[i];
|
|
4494
|
+
if (line === undefined)
|
|
4495
|
+
continue;
|
|
4496
|
+
const trimmed = line.trimStart();
|
|
4497
|
+
if (trimmed.startsWith("package:")) {
|
|
4498
|
+
pkg = parseYamlValue(trimmed.slice("package:".length));
|
|
4499
|
+
} else if (trimmed.startsWith("version:")) {
|
|
4500
|
+
version = parseYamlValue(trimmed.slice("version:".length));
|
|
4501
|
+
} else if (trimmed.startsWith("breaking:")) {
|
|
4502
|
+
const val = parseYamlValue(trimmed.slice("breaking:".length));
|
|
4503
|
+
if (val === "true")
|
|
4504
|
+
breaking = true;
|
|
4505
|
+
else if (val === "false")
|
|
4506
|
+
breaking = false;
|
|
4507
|
+
} else if (trimmed.startsWith("changes:")) {
|
|
4508
|
+
changesStartIdx = i + 1;
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
if (pkg === undefined || version === undefined || breaking === undefined) {
|
|
4512
|
+
return null;
|
|
4513
|
+
}
|
|
4514
|
+
let changes;
|
|
4515
|
+
if (changesStartIdx >= 0) {
|
|
4516
|
+
changes = parseChangesArray(lines, changesStartIdx);
|
|
4517
|
+
}
|
|
4518
|
+
return {
|
|
4519
|
+
package: pkg,
|
|
4520
|
+
version,
|
|
4521
|
+
breaking,
|
|
4522
|
+
...changes !== undefined ? { changes } : {}
|
|
4523
|
+
};
|
|
4524
|
+
}
|
|
4525
|
+
function parseChangesArray(lines, startIdx) {
|
|
4526
|
+
const changes = [];
|
|
4527
|
+
let current = null;
|
|
4528
|
+
for (let i = startIdx;i < lines.length; i++) {
|
|
4529
|
+
const line = lines[i];
|
|
4530
|
+
if (line === undefined)
|
|
4531
|
+
continue;
|
|
4532
|
+
if (/^\s+-\s+/.test(line)) {
|
|
4533
|
+
if (current !== null) {
|
|
4534
|
+
const change = buildChange(current);
|
|
4535
|
+
if (change)
|
|
4536
|
+
changes.push(change);
|
|
4537
|
+
}
|
|
4538
|
+
current = {};
|
|
4539
|
+
const afterDash = line.replace(/^\s+-\s+/, "");
|
|
4540
|
+
const colonIdx = afterDash.indexOf(":");
|
|
4541
|
+
if (colonIdx >= 0) {
|
|
4542
|
+
const key = afterDash.slice(0, colonIdx).trim();
|
|
4543
|
+
const val = parseYamlValue(afterDash.slice(colonIdx + 1));
|
|
4544
|
+
current[key] = val;
|
|
4545
|
+
}
|
|
4546
|
+
} else if (current !== null && /^\s{4,}\S/.test(line)) {
|
|
4547
|
+
const trimmed = line.trim();
|
|
4548
|
+
const colonIdx = trimmed.indexOf(":");
|
|
4549
|
+
if (colonIdx >= 0) {
|
|
4550
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
4551
|
+
const val = parseYamlValue(trimmed.slice(colonIdx + 1));
|
|
4552
|
+
current[key] = val;
|
|
4553
|
+
}
|
|
4554
|
+
} else if (/^\S/.test(line)) {
|
|
4555
|
+
break;
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
if (current !== null) {
|
|
4559
|
+
const change = buildChange(current);
|
|
4560
|
+
if (change)
|
|
4561
|
+
changes.push(change);
|
|
4562
|
+
}
|
|
4563
|
+
return changes;
|
|
4564
|
+
}
|
|
4565
|
+
function buildChange(raw) {
|
|
4566
|
+
const type = raw["type"];
|
|
4567
|
+
if (!(type && VALID_CHANGE_TYPES.has(type))) {
|
|
4568
|
+
return null;
|
|
4569
|
+
}
|
|
4570
|
+
return {
|
|
4571
|
+
type,
|
|
4572
|
+
...raw["from"] ? { from: raw["from"] } : {},
|
|
4573
|
+
...raw["to"] ? { to: raw["to"] } : {},
|
|
4574
|
+
...raw["path"] ? { path: raw["path"] } : {},
|
|
4575
|
+
...raw["export"] ? { export: raw["export"] } : {},
|
|
4576
|
+
...raw["detail"] ? { detail: raw["detail"] } : {},
|
|
4577
|
+
...raw["codemod"] ? { codemod: raw["codemod"] } : {}
|
|
4578
|
+
};
|
|
4579
|
+
}
|
|
4580
|
+
function readMigrationDocsWithMetadata(migrationsDir, shortName, fromVersion, toVersion) {
|
|
4581
|
+
const glob = new Bun.Glob(`outfitter-${shortName}-*.md`);
|
|
4582
|
+
const versionPattern = new RegExp(`^outfitter-${shortName}-(\\d+\\.\\d+\\.\\d+)\\.md$`);
|
|
4583
|
+
const docs = [];
|
|
4584
|
+
for (const entry of glob.scanSync({ cwd: migrationsDir })) {
|
|
4585
|
+
const match = entry.match(versionPattern);
|
|
4586
|
+
if (!match?.[1])
|
|
4587
|
+
continue;
|
|
4588
|
+
const docVersion = match[1];
|
|
4589
|
+
if (Bun.semver.order(docVersion, fromVersion) <= 0)
|
|
4590
|
+
continue;
|
|
4591
|
+
if (Bun.semver.order(docVersion, toVersion) > 0)
|
|
4592
|
+
continue;
|
|
4593
|
+
const filePath = join14(migrationsDir, entry);
|
|
4594
|
+
let content;
|
|
4595
|
+
try {
|
|
4596
|
+
content = readFileSync10(filePath, "utf-8");
|
|
4597
|
+
} catch {
|
|
4598
|
+
continue;
|
|
4599
|
+
}
|
|
4600
|
+
const frontmatter = parseMigrationFrontmatter(content);
|
|
4601
|
+
if (!frontmatter)
|
|
4602
|
+
continue;
|
|
4603
|
+
const body = content.replace(FRONTMATTER_BLOCK_REGEX, "").trim();
|
|
4604
|
+
docs.push({ frontmatter, body, version: docVersion });
|
|
4605
|
+
}
|
|
4606
|
+
docs.sort((a, b) => Bun.semver.order(a.version, b.version));
|
|
4607
|
+
return docs;
|
|
4608
|
+
}
|
|
4340
4609
|
function buildMigrationGuides(packages, migrationsDir) {
|
|
4341
4610
|
const guides = [];
|
|
4342
4611
|
for (const pkg of packages) {
|
|
4343
4612
|
if (!pkg.updateAvailable || pkg.latest === null)
|
|
4344
4613
|
continue;
|
|
4345
4614
|
let steps = [];
|
|
4615
|
+
let allChanges;
|
|
4346
4616
|
if (migrationsDir !== null) {
|
|
4347
4617
|
const shortName = pkg.name.replace("@outfitter/", "");
|
|
4348
|
-
const
|
|
4349
|
-
steps =
|
|
4618
|
+
const metaDocs = readMigrationDocsWithMetadata(migrationsDir, shortName, pkg.current, pkg.latest);
|
|
4619
|
+
steps = metaDocs.map((doc) => doc.body);
|
|
4620
|
+
const changes = [];
|
|
4621
|
+
for (const doc of metaDocs) {
|
|
4622
|
+
if (doc.frontmatter.changes) {
|
|
4623
|
+
changes.push(...doc.frontmatter.changes);
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
if (changes.length > 0) {
|
|
4627
|
+
allChanges = changes;
|
|
4628
|
+
}
|
|
4350
4629
|
}
|
|
4351
4630
|
guides.push({
|
|
4352
4631
|
packageName: pkg.name,
|
|
4353
4632
|
fromVersion: pkg.current,
|
|
4354
4633
|
toVersion: pkg.latest,
|
|
4355
4634
|
breaking: pkg.breaking,
|
|
4356
|
-
steps
|
|
4635
|
+
steps,
|
|
4636
|
+
...allChanges !== undefined ? { changes: allChanges } : {}
|
|
4357
4637
|
});
|
|
4358
4638
|
}
|
|
4359
4639
|
return guides;
|
|
@@ -4367,18 +4647,18 @@ function getVersionPrefix2(specifier) {
|
|
|
4367
4647
|
return match?.[1] ?? "";
|
|
4368
4648
|
}
|
|
4369
4649
|
async function applyUpdates(cwd, updates) {
|
|
4370
|
-
const pkgPath =
|
|
4650
|
+
const pkgPath = join14(cwd, "package.json");
|
|
4371
4651
|
let raw;
|
|
4372
4652
|
try {
|
|
4373
4653
|
raw = readFileSync10(pkgPath, "utf-8");
|
|
4374
4654
|
} catch {
|
|
4375
|
-
return
|
|
4655
|
+
return Result16.err(InternalError4.create("Failed to read package.json for apply", { cwd }));
|
|
4376
4656
|
}
|
|
4377
4657
|
let pkg;
|
|
4378
4658
|
try {
|
|
4379
4659
|
pkg = JSON.parse(raw);
|
|
4380
4660
|
} catch {
|
|
4381
|
-
return
|
|
4661
|
+
return Result16.err(InternalError4.create("Invalid JSON in package.json", { cwd }));
|
|
4382
4662
|
}
|
|
4383
4663
|
const updateMap = new Map;
|
|
4384
4664
|
for (const u of updates) {
|
|
@@ -4404,7 +4684,7 @@ async function applyUpdates(cwd, updates) {
|
|
|
4404
4684
|
`;
|
|
4405
4685
|
await Bun.write(pkgPath, updated);
|
|
4406
4686
|
} catch {
|
|
4407
|
-
return
|
|
4687
|
+
return Result16.err(InternalError4.create("Failed to write updated package.json", { cwd }));
|
|
4408
4688
|
}
|
|
4409
4689
|
try {
|
|
4410
4690
|
const proc = Bun.spawn(["bun", "install"], {
|
|
@@ -4415,112 +4695,212 @@ async function applyUpdates(cwd, updates) {
|
|
|
4415
4695
|
const exitCode = await proc.exited;
|
|
4416
4696
|
if (exitCode !== 0) {
|
|
4417
4697
|
const stderr = await new Response(proc.stderr).text();
|
|
4418
|
-
return
|
|
4698
|
+
return Result16.err(InternalError4.create("bun install failed", {
|
|
4419
4699
|
cwd,
|
|
4420
4700
|
exitCode,
|
|
4421
4701
|
stderr: stderr.trim()
|
|
4422
4702
|
}));
|
|
4423
4703
|
}
|
|
4424
4704
|
} catch {
|
|
4425
|
-
return
|
|
4426
|
-
}
|
|
4427
|
-
return
|
|
4428
|
-
}
|
|
4429
|
-
async function
|
|
4430
|
-
const cwd =
|
|
4431
|
-
const
|
|
4432
|
-
|
|
4433
|
-
const
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4705
|
+
return Result16.err(InternalError4.create("Failed to run bun install", { cwd }));
|
|
4706
|
+
}
|
|
4707
|
+
return Result16.ok(undefined);
|
|
4708
|
+
}
|
|
4709
|
+
async function runUpgrade(options) {
|
|
4710
|
+
const cwd = resolve10(options.cwd);
|
|
4711
|
+
const startedAt = new Date;
|
|
4712
|
+
let workspaceRoot = null;
|
|
4713
|
+
const emptyResult = {
|
|
4714
|
+
packages: [],
|
|
4715
|
+
total: 0,
|
|
4716
|
+
updatesAvailable: 0,
|
|
4717
|
+
hasBreaking: false,
|
|
4718
|
+
applied: false,
|
|
4719
|
+
appliedPackages: [],
|
|
4720
|
+
skippedBreaking: []
|
|
4721
|
+
};
|
|
4722
|
+
const writeReport = (status, result, error) => {
|
|
4723
|
+
writeUpgradeReportSafely(cwd, result, {
|
|
4724
|
+
status,
|
|
4725
|
+
startedAt,
|
|
4726
|
+
workspaceRoot,
|
|
4727
|
+
options,
|
|
4728
|
+
...error !== undefined ? { error } : {}
|
|
4729
|
+
});
|
|
4730
|
+
};
|
|
4731
|
+
try {
|
|
4732
|
+
const migrationsDir = findMigrationDocsDir(cwd);
|
|
4733
|
+
const migrationFlagsDir = findMigrationDocsDir(cwd, cwd);
|
|
4734
|
+
const scanResult = getInstalledPackagesFromWorkspace(cwd);
|
|
4735
|
+
if (scanResult.isErr()) {
|
|
4736
|
+
writeReport("failed", emptyResult, scanResult.error);
|
|
4737
|
+
return scanResult;
|
|
4738
|
+
}
|
|
4739
|
+
const scan = scanResult.value;
|
|
4740
|
+
workspaceRoot = scan.workspaceRoot;
|
|
4741
|
+
const installed = scan.packages;
|
|
4742
|
+
const installRoot = scan.workspaceRoot ?? cwd;
|
|
4743
|
+
const codemodTargetDir = scan.workspaceRoot ?? cwd;
|
|
4744
|
+
if (installed.length === 0) {
|
|
4745
|
+
writeReport("no_updates", emptyResult);
|
|
4746
|
+
return Result16.ok(emptyResult);
|
|
4747
|
+
}
|
|
4748
|
+
const latestVersions = new Map;
|
|
4749
|
+
const installedMap = new Map;
|
|
4750
|
+
const npmFailures = new Set;
|
|
4751
|
+
await Promise.all(installed.map(async (pkg) => {
|
|
4752
|
+
installedMap.set(pkg.name, pkg.version);
|
|
4753
|
+
const latest = await getLatestVersion(pkg.name);
|
|
4754
|
+
if (latest !== null) {
|
|
4755
|
+
const shortName = pkg.name.replace("@outfitter/", "");
|
|
4756
|
+
const docBreaking = migrationFlagsDir !== null ? readMigrationBreakingFlag(migrationFlagsDir, shortName, latest) : undefined;
|
|
4757
|
+
latestVersions.set(pkg.name, {
|
|
4758
|
+
version: latest,
|
|
4759
|
+
...docBreaking !== undefined ? { breaking: docBreaking } : {}
|
|
4760
|
+
});
|
|
4761
|
+
} else {
|
|
4762
|
+
npmFailures.add(pkg.name);
|
|
4763
|
+
}
|
|
4764
|
+
}));
|
|
4765
|
+
const plan = analyzeUpgrades(installedMap, latestVersions);
|
|
4766
|
+
const packages = plan.packages.map((action) => ({
|
|
4767
|
+
name: action.name,
|
|
4768
|
+
current: action.currentVersion,
|
|
4769
|
+
latest: npmFailures.has(action.name) ? null : action.latestVersion,
|
|
4770
|
+
updateAvailable: action.classification === "upgradableNonBreaking" || action.classification === "upgradableBreaking",
|
|
4771
|
+
breaking: action.breaking
|
|
4772
|
+
}));
|
|
4773
|
+
const updatesAvailable = packages.filter((p) => p.updateAvailable).length;
|
|
4774
|
+
const hasBreaking = packages.some((p) => p.breaking);
|
|
4775
|
+
const nonBreakingUpgradable = plan.packages.filter((a) => a.classification === "upgradableNonBreaking");
|
|
4776
|
+
const breakingUpgradable = plan.packages.filter((a) => a.classification === "upgradableBreaking");
|
|
4777
|
+
const includeBreaking = options.all === true;
|
|
4778
|
+
const packagesToApply = includeBreaking ? [...nonBreakingUpgradable, ...breakingUpgradable] : nonBreakingUpgradable;
|
|
4779
|
+
const skippedBreaking = includeBreaking ? [] : breakingUpgradable.map((a) => a.name);
|
|
4780
|
+
let guidesData = options.guide === true ? buildMigrationGuides(packages, migrationsDir) : undefined;
|
|
4781
|
+
if (guidesData !== undefined && options.guidePackages !== undefined && options.guidePackages.length > 0) {
|
|
4782
|
+
const filterSet = new Set(options.guidePackages);
|
|
4783
|
+
guidesData = guidesData.filter((g) => filterSet.has(g.packageName));
|
|
4784
|
+
}
|
|
4785
|
+
const buildResult = (overrides = {}) => ({
|
|
4786
|
+
packages,
|
|
4787
|
+
total: packages.length,
|
|
4788
|
+
updatesAvailable,
|
|
4789
|
+
hasBreaking,
|
|
4445
4790
|
applied: false,
|
|
4446
4791
|
appliedPackages: [],
|
|
4447
|
-
skippedBreaking
|
|
4792
|
+
skippedBreaking,
|
|
4793
|
+
...guidesData !== undefined ? { guides: guidesData } : {},
|
|
4794
|
+
...overrides
|
|
4448
4795
|
});
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4796
|
+
if (options.dryRun) {
|
|
4797
|
+
const result = buildResult();
|
|
4798
|
+
writeReport("dry_run", result);
|
|
4799
|
+
return Result16.ok(result);
|
|
4800
|
+
}
|
|
4801
|
+
if (packagesToApply.length === 0) {
|
|
4802
|
+
const result = buildResult();
|
|
4803
|
+
writeReport("no_updates", result);
|
|
4804
|
+
return Result16.ok(result);
|
|
4805
|
+
}
|
|
4806
|
+
if (options.yes !== true && options.interactive !== false) {
|
|
4807
|
+
const { confirmDestructive } = await import("@outfitter/tui/confirm");
|
|
4808
|
+
const confirmed = await confirmDestructive({
|
|
4809
|
+
message: `Apply ${packagesToApply.length} upgrade(s)?`,
|
|
4810
|
+
itemCount: packagesToApply.length,
|
|
4811
|
+
bypassFlag: false
|
|
4462
4812
|
});
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
updateAvailable: action.classification === "upgradableNonBreaking" || action.classification === "upgradableBreaking",
|
|
4473
|
-
breaking: action.breaking
|
|
4474
|
-
}));
|
|
4475
|
-
const updatesAvailable = packages.filter((p) => p.updateAvailable).length;
|
|
4476
|
-
const hasBreaking = packages.some((p) => p.breaking);
|
|
4477
|
-
const nonBreakingUpgradable = plan.packages.filter((a) => a.classification === "upgradableNonBreaking");
|
|
4478
|
-
const breakingUpgradable = plan.packages.filter((a) => a.classification === "upgradableBreaking");
|
|
4479
|
-
const includeBreaking = options.apply === true && options.breaking === true;
|
|
4480
|
-
const packagesToApply = includeBreaking ? [...nonBreakingUpgradable, ...breakingUpgradable] : nonBreakingUpgradable;
|
|
4481
|
-
const skippedBreaking = includeBreaking ? [] : breakingUpgradable.map((a) => a.name);
|
|
4482
|
-
let applied = false;
|
|
4483
|
-
const appliedPackages = [];
|
|
4484
|
-
if (options.apply && packagesToApply.length > 0) {
|
|
4485
|
-
if (scan.workspaceRoot !== null) {
|
|
4486
|
-
const applyResult = await applyUpdatesToWorkspace(scan.manifestPaths, scan.manifestsByPackage, packagesToApply);
|
|
4487
|
-
if (applyResult.isErr())
|
|
4488
|
-
return applyResult;
|
|
4489
|
-
const installResult = await runInstall(installRoot);
|
|
4490
|
-
if (installResult.isErr())
|
|
4491
|
-
return installResult;
|
|
4492
|
-
} else {
|
|
4493
|
-
const applyResult = await applyUpdates(cwd, packagesToApply);
|
|
4494
|
-
if (applyResult.isErr())
|
|
4495
|
-
return applyResult;
|
|
4813
|
+
if (confirmed.isErr()) {
|
|
4814
|
+
const result = buildResult();
|
|
4815
|
+
writeReport("cancelled", result);
|
|
4816
|
+
return Result16.ok(result);
|
|
4817
|
+
}
|
|
4818
|
+
} else if (options.interactive === false && options.yes !== true) {
|
|
4819
|
+
const result = buildResult();
|
|
4820
|
+
writeReport("skipped_non_interactive", result);
|
|
4821
|
+
return Result16.ok(result);
|
|
4496
4822
|
}
|
|
4497
|
-
applied =
|
|
4498
|
-
appliedPackages
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4823
|
+
let applied = false;
|
|
4824
|
+
const appliedPackages = [];
|
|
4825
|
+
if (packagesToApply.length > 0) {
|
|
4826
|
+
if (scan.workspaceRoot !== null) {
|
|
4827
|
+
const applyResult = await applyUpdatesToWorkspace(scan.manifestPaths, scan.manifestsByPackage, packagesToApply);
|
|
4828
|
+
if (applyResult.isErr()) {
|
|
4829
|
+
const failureResult = buildResult();
|
|
4830
|
+
writeReport("failed", failureResult, applyResult.error);
|
|
4831
|
+
return applyResult;
|
|
4832
|
+
}
|
|
4833
|
+
const installResult = await runInstall(installRoot);
|
|
4834
|
+
if (installResult.isErr()) {
|
|
4835
|
+
const failureResult = buildResult();
|
|
4836
|
+
writeReport("failed", failureResult, installResult.error);
|
|
4837
|
+
return installResult;
|
|
4838
|
+
}
|
|
4839
|
+
} else {
|
|
4840
|
+
const applyResult = await applyUpdates(cwd, packagesToApply);
|
|
4841
|
+
if (applyResult.isErr()) {
|
|
4842
|
+
const failureResult = buildResult();
|
|
4843
|
+
writeReport("failed", failureResult, applyResult.error);
|
|
4844
|
+
return applyResult;
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
applied = true;
|
|
4848
|
+
appliedPackages.push(...packagesToApply.map((a) => a.name));
|
|
4849
|
+
}
|
|
4850
|
+
let codemodSummary;
|
|
4851
|
+
if (applied && options.noCodemods !== true && migrationsDir !== null) {
|
|
4852
|
+
const codemodsDir = findCodemodsDir(cwd);
|
|
4853
|
+
if (codemodsDir !== null) {
|
|
4854
|
+
const allChangedFiles = [];
|
|
4855
|
+
const allErrors = [];
|
|
4856
|
+
let codemodCount = 0;
|
|
4857
|
+
for (const pkg of packagesToApply) {
|
|
4858
|
+
const shortName = pkg.name.replace("@outfitter/", "");
|
|
4859
|
+
const codemods = discoverCodemods(migrationsDir, codemodsDir, shortName, installedMap.get(pkg.name) ?? "0.0.0", pkg.latestVersion);
|
|
4860
|
+
for (const codemod of codemods) {
|
|
4861
|
+
const codemodResult = await runCodemod(codemod.absolutePath, codemodTargetDir, false);
|
|
4862
|
+
codemodCount++;
|
|
4863
|
+
if (codemodResult.isOk()) {
|
|
4864
|
+
allChangedFiles.push(...codemodResult.value.changedFiles);
|
|
4865
|
+
allErrors.push(...codemodResult.value.errors);
|
|
4866
|
+
} else {
|
|
4867
|
+
allErrors.push(codemodResult.error.message);
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
if (codemodCount > 0) {
|
|
4872
|
+
codemodSummary = {
|
|
4873
|
+
codemodCount,
|
|
4874
|
+
changedFiles: allChangedFiles,
|
|
4875
|
+
errors: allErrors
|
|
4876
|
+
};
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
const finalResult = buildResult({
|
|
4881
|
+
applied,
|
|
4882
|
+
appliedPackages,
|
|
4883
|
+
...codemodSummary !== undefined ? { codemods: codemodSummary } : {}
|
|
4884
|
+
});
|
|
4885
|
+
writeReport("applied", finalResult);
|
|
4886
|
+
return Result16.ok(finalResult);
|
|
4887
|
+
} catch (error) {
|
|
4888
|
+
const normalizedError = error && typeof error === "object" && "category" in error && "message" in error ? error : InternalError4.create("Unexpected error in outfitter upgrade", {
|
|
4889
|
+
cwd,
|
|
4890
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4891
|
+
});
|
|
4892
|
+
writeReport("failed", emptyResult, normalizedError);
|
|
4893
|
+
return Result16.err(normalizedError);
|
|
4504
4894
|
}
|
|
4505
|
-
return Result15.ok({
|
|
4506
|
-
packages,
|
|
4507
|
-
total: packages.length,
|
|
4508
|
-
updatesAvailable,
|
|
4509
|
-
hasBreaking,
|
|
4510
|
-
applied,
|
|
4511
|
-
appliedPackages,
|
|
4512
|
-
skippedBreaking,
|
|
4513
|
-
...guidesData !== undefined ? { guides: guidesData } : {}
|
|
4514
|
-
});
|
|
4515
4895
|
}
|
|
4516
|
-
async function
|
|
4896
|
+
async function printUpgradeResults(result, options) {
|
|
4517
4897
|
const structuredMode = resolveStructuredOutputMode(options?.mode);
|
|
4518
4898
|
if (structuredMode) {
|
|
4519
4899
|
await output9(result, { mode: structuredMode });
|
|
4520
4900
|
return;
|
|
4521
4901
|
}
|
|
4522
4902
|
const theme = createTheme3();
|
|
4523
|
-
const lines = ["", "Outfitter
|
|
4903
|
+
const lines = ["", "Outfitter Upgrade", "", "=".repeat(60)];
|
|
4524
4904
|
if (result.packages.length === 0) {
|
|
4525
4905
|
lines.push("No @outfitter/* packages found in package.json.");
|
|
4526
4906
|
await output9(lines, { mode: "human" });
|
|
@@ -4549,39 +4929,60 @@ async function printUpdateResults(result, options) {
|
|
|
4549
4929
|
const breakingApplied = result.appliedPackages.filter((name) => result.packages.some((p) => p.name === name && p.breaking));
|
|
4550
4930
|
const nonBreakingApplied = result.appliedPackages.filter((name) => !result.packages.some((p) => p.name === name && p.breaking));
|
|
4551
4931
|
if (nonBreakingApplied.length > 0) {
|
|
4552
|
-
lines.push(theme.success(`Applied ${nonBreakingApplied.length} non-breaking
|
|
4932
|
+
lines.push(theme.success(`Applied ${nonBreakingApplied.length} non-breaking upgrade(s):`));
|
|
4553
4933
|
for (const name of nonBreakingApplied) {
|
|
4554
4934
|
lines.push(` - ${name}`);
|
|
4555
4935
|
}
|
|
4556
4936
|
lines.push("");
|
|
4557
4937
|
}
|
|
4558
4938
|
if (breakingApplied.length > 0) {
|
|
4559
|
-
lines.push(theme.error(`Applied ${breakingApplied.length} breaking
|
|
4939
|
+
lines.push(theme.error(`Applied ${breakingApplied.length} breaking upgrade(s):`));
|
|
4560
4940
|
for (const name of breakingApplied) {
|
|
4561
4941
|
const pkg = result.packages.find((p) => p.name === name);
|
|
4562
4942
|
lines.push(` - ${name} (${pkg?.current} -> ${pkg?.latest})`);
|
|
4563
4943
|
}
|
|
4564
|
-
lines.push("", theme.muted("Review migration guides: 'outfitter
|
|
4944
|
+
lines.push("", theme.muted("Review migration guides: 'outfitter upgrade --guide'"));
|
|
4565
4945
|
lines.push("");
|
|
4566
4946
|
}
|
|
4567
|
-
} else if (options?.applied !== undefined && options.applied === false) {
|
|
4568
|
-
if (result.updatesAvailable === 0) {
|
|
4569
|
-
lines.push(theme.success("All packages are up to date. Nothing to apply."));
|
|
4570
|
-
} else if (result.appliedPackages.length === 0 && result.skippedBreaking.length > 0) {
|
|
4571
|
-
lines.push(theme.muted("No non-breaking updates to apply. All available updates contain breaking changes."));
|
|
4572
|
-
}
|
|
4573
4947
|
}
|
|
4574
|
-
if (result.skippedBreaking.length > 0 &&
|
|
4575
|
-
|
|
4948
|
+
if (result.skippedBreaking.length > 0 && options?.all !== true) {
|
|
4949
|
+
if (result.applied) {
|
|
4950
|
+
lines.push(theme.error(`Skipped ${result.skippedBreaking.length} breaking upgrade(s):`));
|
|
4951
|
+
} else {
|
|
4952
|
+
lines.push(" Excluded (breaking):");
|
|
4953
|
+
}
|
|
4576
4954
|
for (const name of result.skippedBreaking) {
|
|
4577
|
-
|
|
4955
|
+
const pkg = result.packages.find((p) => p.name === name);
|
|
4956
|
+
const codemodHint = pkg?.breaking ? "(migration guide)" : "";
|
|
4957
|
+
lines.push(` ${name.padEnd(24)} ${(pkg?.current ?? "").padEnd(8)} -> ${(pkg?.latest ?? "").padEnd(8)} ${codemodHint}`.trimEnd());
|
|
4958
|
+
}
|
|
4959
|
+
lines.push("", theme.muted(" Use --all to include breaking changes"));
|
|
4960
|
+
lines.push("");
|
|
4961
|
+
}
|
|
4962
|
+
if (result.codemods !== undefined) {
|
|
4963
|
+
const uniqueChangedFiles = [
|
|
4964
|
+
...new Set(result.codemods.changedFiles)
|
|
4965
|
+
].sort();
|
|
4966
|
+
lines.push(theme.info(`Ran ${result.codemods.codemodCount} codemod(s).`));
|
|
4967
|
+
if (uniqueChangedFiles.length > 0) {
|
|
4968
|
+
lines.push(theme.success(`Codemods changed ${uniqueChangedFiles.length} file(s):`));
|
|
4969
|
+
for (const file of uniqueChangedFiles) {
|
|
4970
|
+
lines.push(` - ${file}`);
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
if (result.codemods.errors.length > 0) {
|
|
4974
|
+
lines.push(theme.error(`Codemod errors (${result.codemods.errors.length}):`));
|
|
4975
|
+
for (const error of result.codemods.errors) {
|
|
4976
|
+
lines.push(` - ${error}`);
|
|
4977
|
+
}
|
|
4578
4978
|
}
|
|
4579
|
-
lines.push("", theme.muted("Use 'outfitter update --apply --breaking' to include breaking updates."));
|
|
4580
4979
|
lines.push("");
|
|
4581
4980
|
}
|
|
4582
4981
|
if (!result.applied) {
|
|
4583
|
-
if (
|
|
4584
|
-
lines.push(theme.muted("
|
|
4982
|
+
if (options?.dryRun) {
|
|
4983
|
+
lines.push(theme.muted("Dry run — no changes applied."));
|
|
4984
|
+
} else if (result.updatesAvailable > 0) {
|
|
4985
|
+
lines.push(theme.muted("Run 'outfitter upgrade --guide' for migration instructions."));
|
|
4585
4986
|
} else {
|
|
4586
4987
|
lines.push(theme.success("All packages are up to date."));
|
|
4587
4988
|
}
|
|
@@ -4620,6 +5021,58 @@ async function printUpdateResults(result, options) {
|
|
|
4620
5021
|
}
|
|
4621
5022
|
await output9(lines, { mode: "human" });
|
|
4622
5023
|
}
|
|
5024
|
+
function writeUpgradeReport(cwd, result, meta) {
|
|
5025
|
+
const reportsDir = join14(cwd, ".outfitter", "reports");
|
|
5026
|
+
mkdirSync6(reportsDir, { recursive: true });
|
|
5027
|
+
const finishedAtIso = new Date().toISOString();
|
|
5028
|
+
const errorContext = meta.error !== undefined && "context" in meta.error && meta.error.context !== undefined && typeof meta.error.context === "object" ? meta.error.context : undefined;
|
|
5029
|
+
const report = {
|
|
5030
|
+
$schema: "https://outfitter.dev/reports/upgrade/v1",
|
|
5031
|
+
status: meta.status,
|
|
5032
|
+
checkedAt: finishedAtIso,
|
|
5033
|
+
startedAt: meta.startedAt.toISOString(),
|
|
5034
|
+
finishedAt: finishedAtIso,
|
|
5035
|
+
cwd,
|
|
5036
|
+
workspaceRoot: meta.workspaceRoot,
|
|
5037
|
+
flags: {
|
|
5038
|
+
dryRun: meta.options.dryRun === true,
|
|
5039
|
+
yes: meta.options.yes === true,
|
|
5040
|
+
interactive: meta.options.interactive !== false,
|
|
5041
|
+
all: meta.options.all === true,
|
|
5042
|
+
noCodemods: meta.options.noCodemods === true,
|
|
5043
|
+
outputMode: meta.options.outputMode ?? null
|
|
5044
|
+
},
|
|
5045
|
+
applied: result.applied,
|
|
5046
|
+
summary: {
|
|
5047
|
+
total: result.total,
|
|
5048
|
+
available: result.updatesAvailable,
|
|
5049
|
+
breaking: result.packages.filter((p) => p.breaking).length,
|
|
5050
|
+
applied: result.appliedPackages.length
|
|
5051
|
+
},
|
|
5052
|
+
packages: result.packages,
|
|
5053
|
+
excluded: {
|
|
5054
|
+
breaking: result.skippedBreaking
|
|
5055
|
+
},
|
|
5056
|
+
...result.codemods !== undefined ? { codemods: result.codemods } : {},
|
|
5057
|
+
...meta.error !== undefined ? {
|
|
5058
|
+
error: {
|
|
5059
|
+
message: meta.error.message,
|
|
5060
|
+
category: meta.error.category,
|
|
5061
|
+
...errorContext !== undefined ? { context: errorContext } : {}
|
|
5062
|
+
}
|
|
5063
|
+
} : {}
|
|
5064
|
+
};
|
|
5065
|
+
writeFileSync7(join14(reportsDir, "upgrade.json"), JSON.stringify(report, null, 2));
|
|
5066
|
+
}
|
|
5067
|
+
function writeUpgradeReportSafely(cwd, result, meta) {
|
|
5068
|
+
try {
|
|
5069
|
+
writeUpgradeReport(cwd, result, meta);
|
|
5070
|
+
} catch (error) {
|
|
5071
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5072
|
+
process.stderr.write(`[outfitter upgrade] Failed to write report: ${reason}
|
|
5073
|
+
`);
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
4623
5076
|
|
|
4624
5077
|
// src/actions.ts
|
|
4625
5078
|
var outputModeSchema = z2.enum(["human", "json", "jsonl"]).default("human");
|
|
@@ -4688,6 +5141,7 @@ function resolveLocalFlag(flags) {
|
|
|
4688
5141
|
}
|
|
4689
5142
|
function resolveInitOptions(context, presetOverride) {
|
|
4690
5143
|
const flags = context.flags;
|
|
5144
|
+
const { force, dryRun, yes } = initSharedFlags.resolve(context);
|
|
4691
5145
|
const targetDir = context.args[0] ?? process.cwd();
|
|
4692
5146
|
const name = resolveStringFlag(flags.name);
|
|
4693
5147
|
const bin = resolveStringFlag(flags.bin);
|
|
@@ -4696,11 +5150,8 @@ function resolveInitOptions(context, presetOverride) {
|
|
|
4696
5150
|
const structure = resolveStringFlag(flags.structure);
|
|
4697
5151
|
const workspaceName = resolveStringFlag(flags.workspaceName);
|
|
4698
5152
|
const local = resolveLocalFlag(flags);
|
|
4699
|
-
const force = Boolean(flags.force);
|
|
4700
5153
|
const withBlocks = resolveStringFlag(flags.with);
|
|
4701
5154
|
const noTooling = resolveNoToolingFlag(flags);
|
|
4702
|
-
const yes = Boolean(flags.yes);
|
|
4703
|
-
const dryRun = Boolean(flags.dryRun ?? context.flags["dry-run"]);
|
|
4704
5155
|
const skipInstall = Boolean(flags.skipInstall ?? context.flags["skip-install"]);
|
|
4705
5156
|
const skipGit = Boolean(flags.skipGit ?? context.flags["skip-git"]);
|
|
4706
5157
|
const skipCommit = Boolean(flags.skipCommit ?? context.flags["skip-commit"]);
|
|
@@ -4735,6 +5186,7 @@ function resolveInitOptions(context, presetOverride) {
|
|
|
4735
5186
|
}
|
|
4736
5187
|
function resolveScaffoldOptions(context) {
|
|
4737
5188
|
const flags = context.flags;
|
|
5189
|
+
const { force, dryRun } = scaffoldSharedFlags.resolve(context);
|
|
4738
5190
|
const outputMode = resolveOutputModeFromContext(context.flags);
|
|
4739
5191
|
const noTooling = resolveNoToolingFlag(flags);
|
|
4740
5192
|
const local = resolveLocalFlag(flags);
|
|
@@ -4748,9 +5200,9 @@ function resolveScaffoldOptions(context) {
|
|
|
4748
5200
|
return {
|
|
4749
5201
|
target: String(context.args[0] ?? ""),
|
|
4750
5202
|
name: resolveStringFlag(context.args[1]),
|
|
4751
|
-
force
|
|
5203
|
+
force,
|
|
4752
5204
|
skipInstall: Boolean(flags.skipInstall ?? context.flags["skip-install"]),
|
|
4753
|
-
dryRun
|
|
5205
|
+
dryRun,
|
|
4754
5206
|
...local !== undefined ? { local } : {},
|
|
4755
5207
|
with: resolveStringFlag(flags.with),
|
|
4756
5208
|
...noTooling !== undefined ? { noTooling } : {},
|
|
@@ -4760,11 +5212,11 @@ function resolveScaffoldOptions(context) {
|
|
|
4760
5212
|
};
|
|
4761
5213
|
}
|
|
4762
5214
|
function resolveMigrateKitOptions(context) {
|
|
4763
|
-
const
|
|
5215
|
+
const { dryRun } = migrateKitSharedFlags.resolve(context);
|
|
4764
5216
|
const outputMode = resolveOutputModeFromContext(context.flags);
|
|
4765
5217
|
return {
|
|
4766
5218
|
targetDir: context.args[0] ?? process.cwd(),
|
|
4767
|
-
dryRun
|
|
5219
|
+
dryRun,
|
|
4768
5220
|
outputMode
|
|
4769
5221
|
};
|
|
4770
5222
|
}
|
|
@@ -4777,11 +5229,6 @@ var commonInitOptions = [
|
|
|
4777
5229
|
flags: "-b, --bin <name>",
|
|
4778
5230
|
description: "Binary name (defaults to project name)"
|
|
4779
5231
|
},
|
|
4780
|
-
{
|
|
4781
|
-
flags: "-f, --force",
|
|
4782
|
-
description: "Overwrite existing files",
|
|
4783
|
-
defaultValue: false
|
|
4784
|
-
},
|
|
4785
5232
|
{
|
|
4786
5233
|
flags: "--local",
|
|
4787
5234
|
description: "Use workspace:* for @outfitter dependencies"
|
|
@@ -4803,12 +5250,19 @@ var templateOption = {
|
|
|
4803
5250
|
flags: "-t, --template <template>",
|
|
4804
5251
|
description: "Template to use (deprecated, use --preset)"
|
|
4805
5252
|
};
|
|
5253
|
+
var initSharedFlags = actionCliPresets(forcePreset(), dryRunPreset(), booleanFlagPreset({
|
|
5254
|
+
id: "initYes",
|
|
5255
|
+
key: "yes",
|
|
5256
|
+
flags: "-y, --yes",
|
|
5257
|
+
description: "Skip prompts and use defaults for missing values"
|
|
5258
|
+
}));
|
|
4806
5259
|
function createInitAction(options) {
|
|
4807
5260
|
const presetOption = {
|
|
4808
5261
|
flags: "-p, --preset <preset>",
|
|
4809
5262
|
description: "Preset to use (minimal, cli, mcp, daemon)"
|
|
4810
5263
|
};
|
|
4811
5264
|
const initOptions = [...commonInitOptions];
|
|
5265
|
+
initOptions.push(...initSharedFlags.options);
|
|
4812
5266
|
initOptions.push({
|
|
4813
5267
|
flags: "-s, --structure <mode>",
|
|
4814
5268
|
description: "Project structure (single|workspace)"
|
|
@@ -4817,16 +5271,6 @@ function createInitAction(options) {
|
|
|
4817
5271
|
flags: "--workspace-name <name>",
|
|
4818
5272
|
description: "Workspace root package name"
|
|
4819
5273
|
});
|
|
4820
|
-
initOptions.push({
|
|
4821
|
-
flags: "-y, --yes",
|
|
4822
|
-
description: "Skip prompts and use defaults for missing values",
|
|
4823
|
-
defaultValue: false
|
|
4824
|
-
});
|
|
4825
|
-
initOptions.push({
|
|
4826
|
-
flags: "--dry-run",
|
|
4827
|
-
description: "Preview changes without writing files",
|
|
4828
|
-
defaultValue: false
|
|
4829
|
-
});
|
|
4830
5274
|
initOptions.push({
|
|
4831
5275
|
flags: "--skip-install",
|
|
4832
5276
|
description: "Skip bun install",
|
|
@@ -4868,16 +5312,19 @@ function createInitAction(options) {
|
|
|
4868
5312
|
const { outputMode, ...initInput } = input;
|
|
4869
5313
|
const result = await runInit(initInput);
|
|
4870
5314
|
if (result.isErr()) {
|
|
4871
|
-
return
|
|
5315
|
+
return Result17.err(new InternalError5({
|
|
4872
5316
|
message: result.error.message,
|
|
4873
5317
|
context: { action: options.id }
|
|
4874
5318
|
}));
|
|
4875
5319
|
}
|
|
4876
5320
|
await printInitResults(result.value, { mode: outputMode });
|
|
4877
|
-
return
|
|
5321
|
+
return Result17.ok(result.value);
|
|
4878
5322
|
}
|
|
4879
5323
|
});
|
|
4880
5324
|
}
|
|
5325
|
+
var scaffoldSharedFlags = actionCliPresets(forcePreset(), dryRunPreset());
|
|
5326
|
+
var addSharedFlags = actionCliPresets(forcePreset(), dryRunPreset());
|
|
5327
|
+
var migrateKitSharedFlags = actionCliPresets(dryRunPreset());
|
|
4881
5328
|
var createAction = defineAction({
|
|
4882
5329
|
id: "create",
|
|
4883
5330
|
description: "Removed - use 'outfitter init' instead",
|
|
@@ -4889,7 +5336,7 @@ var createAction = defineAction({
|
|
|
4889
5336
|
options: [],
|
|
4890
5337
|
mapInput: () => ({})
|
|
4891
5338
|
},
|
|
4892
|
-
handler: async () =>
|
|
5339
|
+
handler: async () => Result17.err(new InternalError5({
|
|
4893
5340
|
message: [
|
|
4894
5341
|
"The 'create' command has been removed.",
|
|
4895
5342
|
"",
|
|
@@ -4914,21 +5361,12 @@ var scaffoldAction = defineAction({
|
|
|
4914
5361
|
command: "scaffold <target> [name]",
|
|
4915
5362
|
description: "Add a capability (cli, mcp, daemon, lib, ...) to an existing project",
|
|
4916
5363
|
options: [
|
|
4917
|
-
|
|
4918
|
-
flags: "-f, --force",
|
|
4919
|
-
description: "Overwrite existing files",
|
|
4920
|
-
defaultValue: false
|
|
4921
|
-
},
|
|
5364
|
+
...scaffoldSharedFlags.options,
|
|
4922
5365
|
{
|
|
4923
5366
|
flags: "--skip-install",
|
|
4924
5367
|
description: "Skip bun install",
|
|
4925
5368
|
defaultValue: false
|
|
4926
5369
|
},
|
|
4927
|
-
{
|
|
4928
|
-
flags: "--dry-run",
|
|
4929
|
-
description: "Preview changes without executing",
|
|
4930
|
-
defaultValue: false
|
|
4931
|
-
},
|
|
4932
5370
|
{
|
|
4933
5371
|
flags: "--with <blocks>",
|
|
4934
5372
|
description: "Comma-separated tooling blocks to add"
|
|
@@ -4952,13 +5390,13 @@ var scaffoldAction = defineAction({
|
|
|
4952
5390
|
const { outputMode, ...scaffoldInput } = input;
|
|
4953
5391
|
const result = await runScaffold(scaffoldInput);
|
|
4954
5392
|
if (result.isErr()) {
|
|
4955
|
-
return
|
|
5393
|
+
return Result17.err(new InternalError5({
|
|
4956
5394
|
message: result.error.message,
|
|
4957
5395
|
context: { action: "scaffold" }
|
|
4958
5396
|
}));
|
|
4959
5397
|
}
|
|
4960
5398
|
await printScaffoldResults(result.value, { mode: outputMode });
|
|
4961
|
-
return
|
|
5399
|
+
return Result17.ok(result.value);
|
|
4962
5400
|
}
|
|
4963
5401
|
});
|
|
4964
5402
|
var demoInputSchema = z2.object({
|
|
@@ -5004,9 +5442,9 @@ var demoAction = defineAction({
|
|
|
5004
5442
|
if (result.exitCode !== 0) {
|
|
5005
5443
|
process.exit(result.exitCode);
|
|
5006
5444
|
}
|
|
5007
|
-
return
|
|
5445
|
+
return Result17.ok(result);
|
|
5008
5446
|
} catch (error) {
|
|
5009
|
-
return
|
|
5447
|
+
return Result17.err(new InternalError5({
|
|
5010
5448
|
message: error instanceof Error ? error.message : "Failed to run demo",
|
|
5011
5449
|
context: { action: "demo" }
|
|
5012
5450
|
}));
|
|
@@ -5036,7 +5474,7 @@ var doctorAction = defineAction({
|
|
|
5036
5474
|
if (result.exitCode !== 0) {
|
|
5037
5475
|
process.exit(result.exitCode);
|
|
5038
5476
|
}
|
|
5039
|
-
return
|
|
5477
|
+
return Result17.ok(result);
|
|
5040
5478
|
}
|
|
5041
5479
|
});
|
|
5042
5480
|
var addInputSchema = z2.object({
|
|
@@ -5055,24 +5493,14 @@ var addAction = defineAction({
|
|
|
5055
5493
|
group: "add",
|
|
5056
5494
|
command: "<block>",
|
|
5057
5495
|
description: "Add a block from the registry (claude, biome, lefthook, bootstrap, scaffolding)",
|
|
5058
|
-
options: [
|
|
5059
|
-
{
|
|
5060
|
-
flags: "-f, --force",
|
|
5061
|
-
description: "Overwrite existing files",
|
|
5062
|
-
defaultValue: false
|
|
5063
|
-
},
|
|
5064
|
-
{
|
|
5065
|
-
flags: "--dry-run",
|
|
5066
|
-
description: "Show what would be added without making changes",
|
|
5067
|
-
defaultValue: false
|
|
5068
|
-
}
|
|
5069
|
-
],
|
|
5496
|
+
options: [...addSharedFlags.options],
|
|
5070
5497
|
mapInput: (context) => {
|
|
5071
5498
|
const outputMode = resolveOutputModeFromContext(context.flags);
|
|
5499
|
+
const { force, dryRun } = addSharedFlags.resolve(context);
|
|
5072
5500
|
return {
|
|
5073
5501
|
block: context.args[0],
|
|
5074
|
-
force
|
|
5075
|
-
dryRun
|
|
5502
|
+
force,
|
|
5503
|
+
dryRun,
|
|
5076
5504
|
cwd: process.cwd(),
|
|
5077
5505
|
outputMode
|
|
5078
5506
|
};
|
|
@@ -5082,13 +5510,13 @@ var addAction = defineAction({
|
|
|
5082
5510
|
const { outputMode, ...addInput } = input;
|
|
5083
5511
|
const result = await runAdd(addInput);
|
|
5084
5512
|
if (result.isErr()) {
|
|
5085
|
-
return
|
|
5513
|
+
return Result17.err(new InternalError5({
|
|
5086
5514
|
message: result.error.message,
|
|
5087
5515
|
context: { action: "add" }
|
|
5088
5516
|
}));
|
|
5089
5517
|
}
|
|
5090
5518
|
await printAddResults(result.value, addInput.dryRun, { mode: outputMode });
|
|
5091
|
-
return
|
|
5519
|
+
return Result17.ok(result.value);
|
|
5092
5520
|
}
|
|
5093
5521
|
});
|
|
5094
5522
|
var listBlocksAction = defineAction({
|
|
@@ -5110,7 +5538,7 @@ var listBlocksAction = defineAction({
|
|
|
5110
5538
|
handler: async (input) => {
|
|
5111
5539
|
const result = listBlocks();
|
|
5112
5540
|
if (result.isErr()) {
|
|
5113
|
-
return
|
|
5541
|
+
return Result17.err(new InternalError5({
|
|
5114
5542
|
message: result.error.message,
|
|
5115
5543
|
context: { action: "add.list" }
|
|
5116
5544
|
}));
|
|
@@ -5125,7 +5553,7 @@ var listBlocksAction = defineAction({
|
|
|
5125
5553
|
];
|
|
5126
5554
|
await output10(lines, { mode: "human" });
|
|
5127
5555
|
}
|
|
5128
|
-
return
|
|
5556
|
+
return Result17.ok({ blocks: result.value });
|
|
5129
5557
|
}
|
|
5130
5558
|
});
|
|
5131
5559
|
var checkInputSchema = z2.object({
|
|
@@ -5163,7 +5591,7 @@ var checkAction = defineAction({
|
|
|
5163
5591
|
const outputMode = resolveOutputModeFromContext(context.flags);
|
|
5164
5592
|
const { verbose } = checkVerbose.resolve(context.flags);
|
|
5165
5593
|
const { cwd: rawCwd } = checkCwd.resolve(context.flags);
|
|
5166
|
-
const cwd =
|
|
5594
|
+
const cwd = resolve11(process.cwd(), rawCwd);
|
|
5167
5595
|
const block = resolveStringFlag(context.flags["block"]);
|
|
5168
5596
|
return {
|
|
5169
5597
|
cwd,
|
|
@@ -5179,7 +5607,7 @@ var checkAction = defineAction({
|
|
|
5179
5607
|
const effectiveMode = ci ? "json" : outputMode;
|
|
5180
5608
|
const result = await runCheck(checkInput);
|
|
5181
5609
|
if (result.isErr()) {
|
|
5182
|
-
return
|
|
5610
|
+
return Result17.err(new InternalError5({
|
|
5183
5611
|
message: result.error.message,
|
|
5184
5612
|
context: { action: "check" }
|
|
5185
5613
|
}));
|
|
@@ -5191,80 +5619,100 @@ var checkAction = defineAction({
|
|
|
5191
5619
|
if (result.value.driftedCount > 0 || result.value.missingCount > 0) {
|
|
5192
5620
|
process.exit(1);
|
|
5193
5621
|
}
|
|
5194
|
-
return
|
|
5622
|
+
return Result17.ok(result.value);
|
|
5195
5623
|
}
|
|
5196
5624
|
});
|
|
5197
|
-
var
|
|
5625
|
+
var upgradeInputSchema = z2.object({
|
|
5198
5626
|
cwd: z2.string(),
|
|
5199
5627
|
guide: z2.boolean(),
|
|
5200
5628
|
guidePackages: z2.array(z2.string()).optional(),
|
|
5201
|
-
|
|
5202
|
-
|
|
5629
|
+
dryRun: z2.boolean(),
|
|
5630
|
+
yes: z2.boolean(),
|
|
5631
|
+
interactive: z2.boolean(),
|
|
5632
|
+
all: z2.boolean(),
|
|
5633
|
+
noCodemods: z2.boolean(),
|
|
5203
5634
|
outputMode: outputModeSchema
|
|
5204
5635
|
});
|
|
5205
|
-
var
|
|
5206
|
-
|
|
5636
|
+
var upgradeCwd = cwdPreset();
|
|
5637
|
+
var upgradeDryRun = dryRunPreset();
|
|
5638
|
+
var upgradeInteraction = interactionPreset();
|
|
5639
|
+
var upgradeAll = booleanFlagPreset({
|
|
5640
|
+
id: "upgradeAll",
|
|
5641
|
+
key: "all",
|
|
5642
|
+
flags: "--all",
|
|
5643
|
+
description: "Include breaking changes in the upgrade"
|
|
5644
|
+
});
|
|
5645
|
+
var upgradeNoCodemods = booleanFlagPreset({
|
|
5646
|
+
id: "upgradeNoCodemods",
|
|
5647
|
+
key: "noCodemods",
|
|
5648
|
+
flags: "--no-codemods",
|
|
5649
|
+
description: "Skip automatic codemod execution during upgrade",
|
|
5650
|
+
sources: ["noCodemods", "no-codemods"],
|
|
5651
|
+
negatedSources: ["codemods"]
|
|
5652
|
+
});
|
|
5653
|
+
var upgradeGuide = booleanFlagPreset({
|
|
5654
|
+
id: "upgradeGuide",
|
|
5655
|
+
key: "guide",
|
|
5656
|
+
flags: "--guide",
|
|
5657
|
+
description: "Show migration instructions for available updates. Pass package names to filter."
|
|
5658
|
+
});
|
|
5659
|
+
var upgradeFlags = actionCliPresets(upgradeCwd, upgradeDryRun, upgradeInteraction, upgradeAll, upgradeNoCodemods, upgradeGuide);
|
|
5660
|
+
var upgradeAction = defineAction({
|
|
5661
|
+
id: "upgrade",
|
|
5207
5662
|
description: "Check for @outfitter/* package updates and migration guidance",
|
|
5208
5663
|
surfaces: ["cli"],
|
|
5209
|
-
input:
|
|
5664
|
+
input: upgradeInputSchema,
|
|
5210
5665
|
cli: {
|
|
5211
|
-
command: "
|
|
5666
|
+
command: "upgrade [packages...]",
|
|
5212
5667
|
description: "Check for @outfitter/* package updates and migration guidance",
|
|
5213
|
-
options: [
|
|
5214
|
-
{
|
|
5215
|
-
flags: "--guide",
|
|
5216
|
-
description: "Show migration instructions for available updates. Pass package names to filter.",
|
|
5217
|
-
defaultValue: false
|
|
5218
|
-
},
|
|
5219
|
-
{
|
|
5220
|
-
flags: "--apply",
|
|
5221
|
-
description: "Apply non-breaking updates to package.json and run bun install",
|
|
5222
|
-
defaultValue: false
|
|
5223
|
-
},
|
|
5224
|
-
{
|
|
5225
|
-
flags: "--breaking",
|
|
5226
|
-
description: "Include breaking updates when used with --apply",
|
|
5227
|
-
defaultValue: false
|
|
5228
|
-
},
|
|
5229
|
-
{
|
|
5230
|
-
flags: "--cwd <path>",
|
|
5231
|
-
description: "Working directory (defaults to current directory)"
|
|
5232
|
-
}
|
|
5233
|
-
],
|
|
5668
|
+
options: [...upgradeFlags.options],
|
|
5234
5669
|
mapInput: (context) => {
|
|
5235
5670
|
const outputMode = resolveOutputModeFromContext(context.flags);
|
|
5236
|
-
const
|
|
5671
|
+
const {
|
|
5672
|
+
cwd: rawCwd,
|
|
5673
|
+
dryRun,
|
|
5674
|
+
interactive,
|
|
5675
|
+
yes,
|
|
5676
|
+
all,
|
|
5677
|
+
noCodemods,
|
|
5678
|
+
guide
|
|
5679
|
+
} = upgradeFlags.resolve(context);
|
|
5680
|
+
const cwd = resolve11(process.cwd(), rawCwd);
|
|
5237
5681
|
const guidePackages = context.args.length > 0 ? context.args : undefined;
|
|
5238
5682
|
return {
|
|
5239
5683
|
cwd,
|
|
5240
|
-
guide
|
|
5684
|
+
guide,
|
|
5241
5685
|
...guidePackages !== undefined ? { guidePackages } : {},
|
|
5242
|
-
|
|
5243
|
-
|
|
5686
|
+
dryRun,
|
|
5687
|
+
yes,
|
|
5688
|
+
interactive,
|
|
5689
|
+
all,
|
|
5690
|
+
noCodemods,
|
|
5244
5691
|
outputMode
|
|
5245
5692
|
};
|
|
5246
5693
|
}
|
|
5247
5694
|
},
|
|
5248
5695
|
handler: async (input) => {
|
|
5249
|
-
const { outputMode, guidePackages, ...
|
|
5250
|
-
const result = await
|
|
5251
|
-
...
|
|
5696
|
+
const { outputMode, guidePackages, ...upgradeInput } = input;
|
|
5697
|
+
const result = await runUpgrade({
|
|
5698
|
+
...upgradeInput,
|
|
5699
|
+
outputMode,
|
|
5252
5700
|
...guidePackages !== undefined ? { guidePackages } : {}
|
|
5253
5701
|
});
|
|
5254
5702
|
if (result.isErr()) {
|
|
5255
|
-
return
|
|
5703
|
+
return Result17.err(new InternalError5({
|
|
5256
5704
|
message: result.error.message,
|
|
5257
|
-
context: { action: "
|
|
5705
|
+
context: { action: "upgrade" }
|
|
5258
5706
|
}));
|
|
5259
5707
|
}
|
|
5260
|
-
await
|
|
5708
|
+
await printUpgradeResults(result.value, {
|
|
5261
5709
|
mode: outputMode,
|
|
5262
|
-
guide:
|
|
5263
|
-
cwd:
|
|
5264
|
-
|
|
5265
|
-
|
|
5710
|
+
guide: upgradeInput.guide,
|
|
5711
|
+
cwd: upgradeInput.cwd,
|
|
5712
|
+
dryRun: upgradeInput.dryRun,
|
|
5713
|
+
all: upgradeInput.all
|
|
5266
5714
|
});
|
|
5267
|
-
return
|
|
5715
|
+
return Result17.ok(result.value);
|
|
5268
5716
|
}
|
|
5269
5717
|
});
|
|
5270
5718
|
var migrateKitAction = defineAction({
|
|
@@ -5276,26 +5724,20 @@ var migrateKitAction = defineAction({
|
|
|
5276
5724
|
group: "migrate",
|
|
5277
5725
|
command: "kit [directory]",
|
|
5278
5726
|
description: "Migrate foundation imports and dependencies to @outfitter/kit",
|
|
5279
|
-
options: [
|
|
5280
|
-
{
|
|
5281
|
-
flags: "--dry-run",
|
|
5282
|
-
description: "Preview changes without writing files",
|
|
5283
|
-
defaultValue: false
|
|
5284
|
-
}
|
|
5285
|
-
],
|
|
5727
|
+
options: [...migrateKitSharedFlags.options],
|
|
5286
5728
|
mapInput: resolveMigrateKitOptions
|
|
5287
5729
|
},
|
|
5288
5730
|
handler: async (input) => {
|
|
5289
5731
|
const { outputMode, ...migrateInput } = input;
|
|
5290
5732
|
const result = await runMigrateKit(migrateInput);
|
|
5291
5733
|
if (result.isErr()) {
|
|
5292
|
-
return
|
|
5734
|
+
return Result17.err(new InternalError5({
|
|
5293
5735
|
message: result.error.message,
|
|
5294
5736
|
context: { action: "migrate.kit" }
|
|
5295
5737
|
}));
|
|
5296
5738
|
}
|
|
5297
5739
|
await printMigrateKitResults(result.value, { mode: outputMode });
|
|
5298
|
-
return
|
|
5740
|
+
return Result17.ok(result.value);
|
|
5299
5741
|
}
|
|
5300
5742
|
});
|
|
5301
5743
|
var outfitterActions = createActionRegistry().add(createAction).add(scaffoldAction).add(createInitAction({
|
|
@@ -5319,29 +5761,40 @@ var outfitterActions = createActionRegistry().add(createAction).add(scaffoldActi
|
|
|
5319
5761
|
description: "Create a new daemon project",
|
|
5320
5762
|
command: "daemon [directory]",
|
|
5321
5763
|
presetOverride: "daemon"
|
|
5322
|
-
})).add(demoAction).add(doctorAction).add(addAction).add(listBlocksAction).add(checkAction).add(migrateKitAction).add(
|
|
5764
|
+
})).add(demoAction).add(doctorAction).add(addAction).add(listBlocksAction).add(checkAction).add(migrateKitAction).add(upgradeAction);
|
|
5323
5765
|
|
|
5324
5766
|
// src/commands/repo.ts
|
|
5325
|
-
import { existsSync as
|
|
5767
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
5326
5768
|
import { createRequire as createRequire3 } from "node:module";
|
|
5327
|
-
import { dirname as dirname9, join as
|
|
5769
|
+
import { dirname as dirname9, join as join16 } from "node:path";
|
|
5770
|
+
import {
|
|
5771
|
+
booleanFlagPreset as booleanFlagPreset2,
|
|
5772
|
+
composePresets,
|
|
5773
|
+
cwdPreset as cwdPreset2,
|
|
5774
|
+
stringListFlagPreset
|
|
5775
|
+
} from "@outfitter/cli/flags";
|
|
5776
|
+
import {
|
|
5777
|
+
resolveDocsCliOptions,
|
|
5778
|
+
withDocsCommonOptions,
|
|
5779
|
+
withDocsExportOptions
|
|
5780
|
+
} from "@outfitter/docs";
|
|
5328
5781
|
import { Command } from "commander";
|
|
5329
5782
|
|
|
5330
5783
|
// src/commands/docs-module-loader.ts
|
|
5331
|
-
import { existsSync as
|
|
5784
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
5332
5785
|
import { createRequire as createRequire2 } from "node:module";
|
|
5333
|
-
import { dirname as dirname8, join as
|
|
5786
|
+
import { dirname as dirname8, join as join15 } from "node:path";
|
|
5334
5787
|
import { pathToFileURL } from "node:url";
|
|
5335
5788
|
var require2 = createRequire2(import.meta.url);
|
|
5336
5789
|
function resolveDocsEntrypoint() {
|
|
5337
5790
|
const packageJsonPath = require2.resolve("@outfitter/docs/package.json");
|
|
5338
5791
|
const packageRoot = dirname8(packageJsonPath);
|
|
5339
|
-
const srcEntrypoint =
|
|
5340
|
-
if (
|
|
5792
|
+
const srcEntrypoint = join15(packageRoot, "src", "index.ts");
|
|
5793
|
+
if (existsSync14(srcEntrypoint)) {
|
|
5341
5794
|
return srcEntrypoint;
|
|
5342
5795
|
}
|
|
5343
|
-
const distEntrypoint =
|
|
5344
|
-
if (
|
|
5796
|
+
const distEntrypoint = join15(packageRoot, "dist", "index.js");
|
|
5797
|
+
if (existsSync14(distEntrypoint)) {
|
|
5345
5798
|
return distEntrypoint;
|
|
5346
5799
|
}
|
|
5347
5800
|
throw new Error("Unable to resolve @outfitter/docs entrypoint (expected src/index.ts or dist/index.js).");
|
|
@@ -5371,21 +5824,15 @@ function getIo(options) {
|
|
|
5371
5824
|
`))
|
|
5372
5825
|
};
|
|
5373
5826
|
}
|
|
5374
|
-
function resolveCwdOption(inputCwd) {
|
|
5375
|
-
if (typeof inputCwd !== "string" || inputCwd.length === 0) {
|
|
5376
|
-
return process.cwd();
|
|
5377
|
-
}
|
|
5378
|
-
return resolve11(process.cwd(), inputCwd);
|
|
5379
|
-
}
|
|
5380
5827
|
function resolveToolingCliEntrypoint() {
|
|
5381
5828
|
const packageJsonPath = require3.resolve("@outfitter/tooling/package.json");
|
|
5382
5829
|
const packageRoot = dirname9(packageJsonPath);
|
|
5383
|
-
const srcEntrypoint =
|
|
5384
|
-
if (
|
|
5830
|
+
const srcEntrypoint = join16(packageRoot, "src", "cli", "index.ts");
|
|
5831
|
+
if (existsSync15(srcEntrypoint)) {
|
|
5385
5832
|
return srcEntrypoint;
|
|
5386
5833
|
}
|
|
5387
|
-
const distEntrypoint =
|
|
5388
|
-
if (
|
|
5834
|
+
const distEntrypoint = join16(packageRoot, "dist", "cli", "index.js");
|
|
5835
|
+
if (existsSync15(distEntrypoint)) {
|
|
5389
5836
|
return distEntrypoint;
|
|
5390
5837
|
}
|
|
5391
5838
|
throw new Error("Unable to resolve @outfitter/tooling CLI entrypoint (expected dist/cli/index.js or src/cli/index.ts).");
|
|
@@ -5417,97 +5864,112 @@ function applyExitCode(code) {
|
|
|
5417
5864
|
process.exitCode = code;
|
|
5418
5865
|
}
|
|
5419
5866
|
}
|
|
5867
|
+
function applyPresetOptions(command, preset) {
|
|
5868
|
+
for (const option of preset.options) {
|
|
5869
|
+
command.option(option.flags, option.description, option.defaultValue);
|
|
5870
|
+
}
|
|
5871
|
+
return command;
|
|
5872
|
+
}
|
|
5420
5873
|
function addDocsCheckSubcommand(command, options) {
|
|
5421
|
-
command.command("docs").description("Check whether assembled package docs are in sync")
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
...cmdOptions.cwd ? { cwd: resolveCwdOption(cmdOptions.cwd) } : {}
|
|
5425
|
-
}, options.io);
|
|
5874
|
+
const docsCheckCommand = command.command("docs").description("Check whether assembled package docs are in sync");
|
|
5875
|
+
withDocsCommonOptions(docsCheckCommand).action(async (cmdOptions) => {
|
|
5876
|
+
const code = await options.runDocsCheck(resolveDocsCliOptions(cmdOptions), options.io);
|
|
5426
5877
|
applyExitCode(code);
|
|
5427
5878
|
});
|
|
5428
5879
|
}
|
|
5429
5880
|
function addDocsSyncSubcommand(command, options) {
|
|
5430
|
-
command.command("docs").description("Assemble package docs into docs/packages")
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
...cmdOptions.cwd ? { cwd: resolveCwdOption(cmdOptions.cwd) } : {}
|
|
5434
|
-
}, options.io);
|
|
5881
|
+
const docsSyncCommand = command.command("docs").description("Assemble package docs into docs/packages");
|
|
5882
|
+
withDocsCommonOptions(docsSyncCommand).action(async (cmdOptions) => {
|
|
5883
|
+
const code = await options.runDocsSync(resolveDocsCliOptions(cmdOptions), options.io);
|
|
5435
5884
|
applyExitCode(code);
|
|
5436
5885
|
});
|
|
5437
5886
|
}
|
|
5438
5887
|
function addDocsExportSubcommand(command, options) {
|
|
5439
|
-
command.command("docs").description("Export docs artifacts for packages and LLM targets")
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
...cmdOptions.cwd ? { cwd: resolveCwdOption(cmdOptions.cwd) } : {}
|
|
5443
|
-
}, options.io);
|
|
5888
|
+
const docsExportCommand = command.command("docs").description("Export docs artifacts for packages and LLM targets");
|
|
5889
|
+
withDocsExportOptions(docsExportCommand).action(async (cmdOptions) => {
|
|
5890
|
+
const code = await options.runDocsExport(resolveDocsCliOptions(cmdOptions), options.io);
|
|
5444
5891
|
applyExitCode(code);
|
|
5445
5892
|
});
|
|
5446
5893
|
}
|
|
5447
5894
|
function addToolingCheckSubcommands(command, runToolingCommand) {
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
command: "check-exports",
|
|
5455
|
-
args,
|
|
5456
|
-
cwd: resolveCwdOption(cmdOptions.cwd)
|
|
5457
|
-
});
|
|
5458
|
-
applyExitCode(code);
|
|
5895
|
+
const cwdFlag = cwdPreset2();
|
|
5896
|
+
const jsonFlag = booleanFlagPreset2({
|
|
5897
|
+
id: "repo-check-json",
|
|
5898
|
+
key: "json",
|
|
5899
|
+
flags: "--json",
|
|
5900
|
+
description: "Output results as JSON"
|
|
5459
5901
|
});
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
const code = await runToolingCommand({
|
|
5466
|
-
command: "check-readme-imports",
|
|
5467
|
-
args,
|
|
5468
|
-
cwd: resolveCwdOption(cmdOptions.cwd)
|
|
5469
|
-
});
|
|
5470
|
-
applyExitCode(code);
|
|
5902
|
+
const skipFlag = booleanFlagPreset2({
|
|
5903
|
+
id: "repo-check-skip",
|
|
5904
|
+
key: "skip",
|
|
5905
|
+
flags: "-s, --skip",
|
|
5906
|
+
description: "Skip changeset check"
|
|
5471
5907
|
});
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
});
|
|
5478
|
-
applyExitCode(code);
|
|
5908
|
+
const pathsFlag = stringListFlagPreset({
|
|
5909
|
+
id: "repo-check-paths",
|
|
5910
|
+
key: "paths",
|
|
5911
|
+
flags: "--paths <paths...>",
|
|
5912
|
+
description: "Limit check to specific paths"
|
|
5479
5913
|
});
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
const
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5914
|
+
const toolingWithCwd = composePresets(cwdFlag);
|
|
5915
|
+
const toolingWithJsonAndCwd = composePresets(jsonFlag, cwdFlag);
|
|
5916
|
+
const toolingWithSkipAndCwd = composePresets(skipFlag, cwdFlag);
|
|
5917
|
+
const toolingWithPathsAndCwd = composePresets(pathsFlag, cwdFlag);
|
|
5918
|
+
function registerToolingCheckSubcommand(config) {
|
|
5919
|
+
const subcommand = command.command(config.name).description(config.description);
|
|
5920
|
+
applyPresetOptions(subcommand, config.preset);
|
|
5921
|
+
subcommand.action(async (cmdOptions) => {
|
|
5922
|
+
const resolved = config.preset.resolve(cmdOptions);
|
|
5923
|
+
const cwd = resolveDocsCliOptions({ cwd: resolved.cwd }).cwd || process.cwd();
|
|
5924
|
+
const code = await runToolingCommand({
|
|
5925
|
+
command: config.toolingCommand,
|
|
5926
|
+
args: config.buildArgs(resolved),
|
|
5927
|
+
cwd
|
|
5928
|
+
});
|
|
5929
|
+
applyExitCode(code);
|
|
5489
5930
|
});
|
|
5490
|
-
|
|
5931
|
+
}
|
|
5932
|
+
registerToolingCheckSubcommand({
|
|
5933
|
+
name: "exports",
|
|
5934
|
+
description: "Validate package.json exports match source entry points",
|
|
5935
|
+
toolingCommand: "check-exports",
|
|
5936
|
+
preset: toolingWithJsonAndCwd,
|
|
5937
|
+
buildArgs: (resolved) => resolved["json"] ? ["--json"] : []
|
|
5491
5938
|
});
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
command: "check-clean-tree",
|
|
5499
|
-
args,
|
|
5500
|
-
cwd: resolveCwdOption(cmdOptions.cwd)
|
|
5501
|
-
});
|
|
5502
|
-
applyExitCode(code);
|
|
5939
|
+
registerToolingCheckSubcommand({
|
|
5940
|
+
name: "readme",
|
|
5941
|
+
description: "Validate README import examples match package exports",
|
|
5942
|
+
toolingCommand: "check-readme-imports",
|
|
5943
|
+
preset: toolingWithJsonAndCwd,
|
|
5944
|
+
buildArgs: (resolved) => resolved["json"] ? ["--json"] : []
|
|
5503
5945
|
});
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5946
|
+
registerToolingCheckSubcommand({
|
|
5947
|
+
name: "registry",
|
|
5948
|
+
description: "Validate packages with bunup --filter are registered in bunup.config.ts",
|
|
5949
|
+
toolingCommand: "check-bunup-registry",
|
|
5950
|
+
preset: toolingWithCwd,
|
|
5951
|
+
buildArgs: () => []
|
|
5952
|
+
});
|
|
5953
|
+
registerToolingCheckSubcommand({
|
|
5954
|
+
name: "changeset",
|
|
5955
|
+
description: "Validate PRs touching package source include a changeset",
|
|
5956
|
+
toolingCommand: "check-changeset",
|
|
5957
|
+
preset: toolingWithSkipAndCwd,
|
|
5958
|
+
buildArgs: (resolved) => resolved["skip"] ? ["--skip"] : []
|
|
5959
|
+
});
|
|
5960
|
+
registerToolingCheckSubcommand({
|
|
5961
|
+
name: "tree",
|
|
5962
|
+
description: "Assert working tree is clean (no modified or untracked files)",
|
|
5963
|
+
toolingCommand: "check-clean-tree",
|
|
5964
|
+
preset: toolingWithPathsAndCwd,
|
|
5965
|
+
buildArgs: (resolved) => Array.isArray(resolved["paths"]) && resolved["paths"].length > 0 ? ["--paths", ...resolved["paths"]] : []
|
|
5966
|
+
});
|
|
5967
|
+
registerToolingCheckSubcommand({
|
|
5968
|
+
name: "boundary-invocations",
|
|
5969
|
+
description: "Validate root/app scripts do not execute packages/*/src entrypoints directly",
|
|
5970
|
+
toolingCommand: "check-boundary-invocations",
|
|
5971
|
+
preset: toolingWithCwd,
|
|
5972
|
+
buildArgs: () => []
|
|
5511
5973
|
});
|
|
5512
5974
|
}
|
|
5513
5975
|
function createRepoCommand(options) {
|