outfitter 0.3.2 → 0.3.3
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 +33 -5
- package/dist/actions/add.d.ts +18 -0
- package/dist/actions/add.js +14 -0
- package/dist/actions/check-automation.d.ts +20 -0
- package/dist/actions/check-automation.js +27 -0
- package/dist/actions/check.d.ts +34 -0
- package/dist/actions/check.js +19 -0
- package/dist/actions/demo.d.ts +12 -0
- package/dist/actions/demo.js +11 -0
- package/dist/actions/docs-output-mode.d.ts +4 -0
- package/dist/actions/docs-output-mode.js +8 -0
- package/dist/actions/docs.d.ts +25 -0
- package/dist/actions/docs.js +31 -0
- package/dist/actions/doctor.d.ts +10 -0
- package/dist/actions/doctor.js +15 -0
- package/dist/actions/init.d.ts +28 -0
- package/dist/actions/init.js +31 -0
- package/dist/actions/scaffold.d.ts +19 -0
- package/dist/actions/scaffold.js +21 -0
- package/dist/actions/shared.d.ts +61 -0
- package/dist/actions/shared.js +30 -0
- package/dist/actions/upgrade.d.ts +17 -0
- package/dist/actions/upgrade.js +21 -0
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +66 -0
- package/dist/cli.js +66 -4
- package/dist/commands/add.d.ts +54 -0
- package/dist/commands/add.js +16 -0
- package/dist/commands/check-action-ceremony.d.ts +55 -0
- package/dist/commands/check-action-ceremony.js +15 -0
- package/dist/commands/check-docs-sentinel.d.ts +27 -0
- package/dist/commands/check-docs-sentinel.js +18 -0
- package/dist/commands/check-orchestrator.d.ts +2 -0
- package/dist/commands/check-orchestrator.js +17 -0
- package/dist/commands/check-preset-versions.d.ts +20 -0
- package/dist/commands/check-preset-versions.js +15 -0
- package/dist/commands/check-publish-guardrails.d.ts +38 -0
- package/dist/commands/check-publish-guardrails.js +19 -0
- package/dist/commands/check-surface-map-format.d.ts +29 -0
- package/dist/commands/check-surface-map-format.js +19 -0
- package/dist/commands/check-surface-map.d.ts +20 -0
- package/dist/commands/check-surface-map.js +15 -0
- package/dist/commands/check-tsdoc.d.ts +3 -0
- package/dist/commands/check-tsdoc.js +9 -0
- package/dist/commands/check.d.ts +93 -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-api.d.ts +4 -0
- package/dist/commands/docs-api.js +13 -0
- package/dist/commands/docs-export.d.ts +4 -0
- package/dist/commands/docs-export.js +12 -0
- package/dist/commands/docs-list.d.ts +3 -0
- package/dist/commands/docs-list.js +13 -0
- package/dist/commands/docs-module-loader.d.ts +2 -0
- package/dist/commands/docs-module-loader.js +8 -0
- package/dist/commands/docs-search.d.ts +3 -0
- package/dist/commands/docs-search.js +13 -0
- package/dist/commands/docs-show.d.ts +3 -0
- package/dist/commands/docs-show.js +13 -0
- package/dist/commands/docs-types.d.ts +21 -0
- package/dist/commands/docs-types.js +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +17 -0
- package/dist/commands/init-execution.d.ts +8 -0
- package/dist/commands/init-execution.js +11 -0
- package/dist/commands/init-option-resolution.d.ts +5 -0
- package/dist/commands/init-option-resolution.js +14 -0
- package/dist/commands/init-output.d.ts +9 -0
- package/dist/commands/init-output.js +11 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +24 -0
- package/dist/commands/jq-utils.d.ts +17 -0
- package/dist/commands/jq-utils.js +8 -0
- package/dist/commands/repo.d.ts +3 -0
- package/dist/commands/repo.js +13 -0
- package/dist/commands/scaffold-output.d.ts +4 -0
- package/dist/commands/scaffold-output.js +11 -0
- package/dist/commands/scaffold-planning.d.ts +65 -0
- package/dist/commands/scaffold-planning.js +20 -0
- package/dist/commands/scaffold.d.ts +4 -0
- package/dist/commands/scaffold.js +26 -0
- package/dist/commands/shared-deps.d.ts +22 -0
- package/dist/commands/shared-deps.js +11 -0
- package/dist/commands/upgrade-apply.d.ts +14 -0
- package/dist/commands/upgrade-apply.js +8 -0
- package/dist/commands/upgrade-codemods.d.ts +47 -0
- package/dist/commands/upgrade-codemods.js +14 -0
- package/dist/commands/upgrade-latest-version.d.ts +8 -0
- package/dist/commands/upgrade-latest-version.js +8 -0
- package/dist/commands/upgrade-migration-docs.d.ts +3 -0
- package/dist/commands/upgrade-migration-docs.js +15 -0
- package/dist/commands/upgrade-migration-frontmatter.d.ts +2 -0
- package/dist/commands/upgrade-migration-frontmatter.js +10 -0
- package/dist/commands/upgrade-migration-guides.d.ts +5 -0
- package/dist/commands/upgrade-migration-guides.js +10 -0
- package/dist/commands/upgrade-output.d.ts +5 -0
- package/dist/commands/upgrade-output.js +11 -0
- package/dist/commands/upgrade-planner.d.ts +58 -0
- package/dist/commands/upgrade-planner.js +8 -0
- package/dist/commands/upgrade-report.d.ts +5 -0
- package/dist/commands/upgrade-report.js +8 -0
- package/dist/commands/upgrade-workspace.d.ts +2 -0
- package/dist/commands/upgrade-workspace.js +16 -0
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.js +37 -0
- package/dist/create/index.d.ts +5 -0
- package/dist/create/index.js +2 -0
- package/dist/create/planner.d.ts +3 -0
- package/dist/create/planner.js +85 -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 +15 -0
- package/dist/engine/dependency-versions.d.ts +17 -0
- package/dist/engine/dependency-versions.js +12 -0
- package/dist/engine/executor.d.ts +3 -0
- package/dist/engine/executor.js +156 -0
- package/dist/engine/index.d.ts +10 -0
- package/dist/engine/index.js +54 -0
- package/dist/engine/names.d.ts +2 -0
- package/dist/engine/names.js +24 -0
- package/dist/engine/post-scaffold.d.ts +3 -0
- package/dist/engine/post-scaffold.js +8 -0
- package/dist/engine/preset.d.ts +3 -0
- package/dist/engine/preset.js +17 -0
- package/dist/engine/render-plan.d.ts +7 -0
- package/dist/engine/render-plan.js +9 -0
- package/dist/engine/template.d.ts +4 -0
- package/dist/engine/template.js +34 -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 +20 -0
- package/dist/index.d.ts +17 -397
- package/dist/index.js +7 -165
- 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/outfitter-109s75x0.d.ts +76 -0
- package/dist/shared/outfitter-1fy7byz5.js +170 -0
- package/dist/shared/outfitter-1h7k8xxt.js +29 -0
- package/dist/shared/outfitter-1tfa9hke.d.ts +55 -0
- package/dist/shared/outfitter-2nx0k4b3.d.ts +4 -0
- package/dist/shared/outfitter-2ysjerp6.d.ts +44 -0
- package/dist/shared/outfitter-2z61gp5w.js +29 -0
- package/dist/shared/outfitter-34vg353f.d.ts +82 -0
- package/dist/shared/outfitter-3dq4r10s.d.ts +24 -0
- package/dist/shared/outfitter-3rcrvva8.js +103 -0
- package/dist/shared/outfitter-3tx3adgj.js +278 -0
- package/dist/shared/outfitter-4q1zfmvc.js +154 -0
- package/dist/shared/outfitter-4s9meh3j.js +221 -0
- package/dist/shared/outfitter-507ra35w.js +285 -0
- package/dist/shared/outfitter-56jq0rh2.d.ts +42 -0
- package/dist/shared/outfitter-58rn1sj1.d.ts +30 -0
- package/dist/shared/outfitter-5d9wbzhh.d.ts +19 -0
- package/dist/shared/outfitter-5j7zee11.d.ts +180 -0
- package/dist/shared/outfitter-5r6q2749.d.ts +18 -0
- package/dist/shared/outfitter-5vx1bp7h.js +41 -0
- package/dist/shared/outfitter-6ddf91vh.js +190 -0
- package/dist/shared/outfitter-6mpkh3zn.js +432 -0
- package/dist/shared/outfitter-6rtcemk7.d.ts +18 -0
- package/dist/shared/outfitter-6t7xeyg1.js +159 -0
- package/dist/shared/outfitter-738z4c37.js +262 -0
- package/dist/shared/outfitter-76k25svs.js +322 -0
- package/dist/shared/outfitter-7n7vsz95.js +101 -0
- package/dist/shared/outfitter-7q9fnbwa.js +60 -0
- package/dist/shared/outfitter-7r12fj7f.js +30 -0
- package/dist/shared/outfitter-84chvazx.js +480 -0
- package/dist/shared/outfitter-8ggmja91.js +301 -0
- package/dist/shared/outfitter-8kmak0wc.d.ts +4 -0
- package/dist/shared/outfitter-8y2dfx6n.js +11 -0
- package/dist/shared/outfitter-940h0x7b.js +71 -0
- package/dist/shared/outfitter-954y4mzx.d.ts +5 -0
- package/dist/shared/outfitter-a79xrm12.d.ts +17 -0
- package/dist/shared/outfitter-b9cpnr7e.js +110 -0
- package/dist/shared/outfitter-bpr28y54.js +70 -0
- package/dist/shared/outfitter-c7sbs7es.js +92 -0
- package/dist/shared/outfitter-cyhzstz0.js +93 -0
- package/dist/shared/outfitter-cyvr4r8d.d.ts +67 -0
- package/dist/shared/outfitter-d0kqashd.d.ts +98 -0
- package/dist/shared/outfitter-dx4hn4ta.js +325 -0
- package/dist/shared/outfitter-e84cr97g.js +232 -0
- package/dist/shared/outfitter-ec83h4v2.js +17 -0
- package/dist/shared/outfitter-eepj7rf7.js +4 -0
- package/dist/shared/outfitter-ekb6t1zz.js +35 -0
- package/dist/shared/outfitter-ex8gn945.js +51 -0
- package/dist/shared/outfitter-f3a70135.js +75 -0
- package/dist/shared/outfitter-fbvfd5zq.d.ts +13 -0
- package/dist/shared/outfitter-fj2v5ffz.js +165 -0
- package/dist/shared/outfitter-fx1m251y.js +122 -0
- package/dist/shared/outfitter-fxry5n58.js +254 -0
- package/dist/shared/outfitter-g3hvjshg.js +1 -0
- package/dist/shared/outfitter-gdc7b7de.d.ts +5 -0
- package/dist/shared/outfitter-gyayfx5r.js +156 -0
- package/dist/shared/outfitter-h0wmtxw8.d.ts +23 -0
- package/dist/shared/outfitter-hcexcvxe.d.ts +25 -0
- package/dist/shared/outfitter-hf5bj2gq.js +117 -0
- package/dist/shared/outfitter-hsp8vy5m.d.ts +146 -0
- package/dist/shared/outfitter-htx4asgr.d.ts +52 -0
- package/dist/shared/outfitter-jkct38dh.js +53 -0
- package/dist/shared/outfitter-jwxggvz4.js +42 -0
- package/dist/shared/outfitter-k6zyvg2n.js +306 -0
- package/dist/shared/outfitter-ksyvwmb5.js +191 -0
- package/dist/shared/outfitter-m3ehh37q.d.ts +22 -0
- package/dist/shared/outfitter-mstr60zz.js +215 -0
- package/dist/shared/outfitter-n0ed012k.js +101 -0
- package/dist/shared/outfitter-n13pqaft.js +19 -0
- package/dist/shared/outfitter-nxvjxrmw.d.ts +48 -0
- package/dist/shared/outfitter-p2wn07b7.js +160 -0
- package/dist/shared/outfitter-px5sv5gn.js +321 -0
- package/dist/shared/outfitter-q1g58t85.js +8 -0
- package/dist/shared/outfitter-qsd5638j.js +378 -0
- package/dist/shared/outfitter-qsrx7m4w.js +72 -0
- package/dist/shared/outfitter-r2awqszh.d.ts +52 -0
- package/dist/shared/outfitter-rdpw2sbp.d.ts +77 -0
- package/dist/shared/outfitter-rp89dafm.js +109 -0
- package/dist/shared/outfitter-s1c0whzj.js +121 -0
- package/dist/shared/outfitter-ssrtakh3.js +342 -0
- package/dist/shared/outfitter-ssynegbs.js +167 -0
- package/dist/shared/outfitter-svts4wk2.js +36 -0
- package/dist/shared/outfitter-tavatb5p.js +166 -0
- package/dist/shared/outfitter-tqznjgbm.js +44 -0
- package/dist/shared/outfitter-ttjr95y9.js +98 -0
- package/dist/shared/outfitter-wcrp7d7m.d.ts +5 -0
- package/dist/shared/outfitter-wkt0a0ra.js +67 -0
- package/dist/shared/outfitter-wrcqq29p.js +132 -0
- package/dist/shared/outfitter-wyg1tpp5.d.ts +43 -0
- package/dist/shared/outfitter-x0vpb7tj.js +126 -0
- package/dist/shared/outfitter-x39awx8g.js +146 -0
- package/dist/shared/outfitter-x4cc5xsq.js +168 -0
- package/dist/shared/outfitter-x8w5sjnd.d.ts +39 -0
- package/dist/shared/outfitter-xr6g13nz.d.ts +50 -0
- package/dist/shared/outfitter-xs94pkfe.js +106 -0
- package/dist/shared/outfitter-y37yfehn.d.ts +37 -0
- package/dist/shared/outfitter-y6ee0k45.d.ts +18 -0
- package/dist/shared/outfitter-ydw7x6bh.js +61 -0
- package/dist/shared/outfitter-yhb23pjc.js +89 -0
- package/dist/shared/outfitter-ypcvwg1s.js +91 -0
- package/dist/shared/outfitter-znbqe5zy.d.ts +45 -0
- package/dist/shared/outfitter-zng6w0t9.d.ts +4 -0
- package/dist/targets/index.d.ts +4 -0
- package/dist/targets/index.js +28 -0
- package/dist/targets/registry.d.ts +3 -0
- package/dist/targets/registry.js +221 -0
- package/dist/targets/types.d.ts +2 -0
- package/dist/targets/types.js +1 -0
- package/package.json +194 -35
- package/dist/shared/chunk-3pwh8ys4.js +0 -6461
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
readManifest
|
|
4
|
+
} from "./outfitter-ypcvwg1s.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveStructuredOutputMode
|
|
7
|
+
} from "./outfitter-7r12fj7f.js";
|
|
8
|
+
|
|
9
|
+
// apps/outfitter/src/commands/check.ts
|
|
10
|
+
import { existsSync, readFileSync } from "fs";
|
|
11
|
+
import { dirname, join, resolve } from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { output } from "@outfitter/cli";
|
|
14
|
+
import { Result } from "@outfitter/contracts";
|
|
15
|
+
import { createTheme } from "@outfitter/tui/render";
|
|
16
|
+
class CheckError extends Error {
|
|
17
|
+
_tag = "CheckError";
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "CheckError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getRegistryPath() {
|
|
24
|
+
let currentDir = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
for (let i = 0;i < 10; i++) {
|
|
26
|
+
const registryPath = join(currentDir, "node_modules/@outfitter/tooling/registry/registry.json");
|
|
27
|
+
if (existsSync(registryPath)) {
|
|
28
|
+
return registryPath;
|
|
29
|
+
}
|
|
30
|
+
const monoRepoPath = join(currentDir, "packages/tooling/registry/registry.json");
|
|
31
|
+
if (existsSync(monoRepoPath)) {
|
|
32
|
+
return monoRepoPath;
|
|
33
|
+
}
|
|
34
|
+
currentDir = dirname(currentDir);
|
|
35
|
+
}
|
|
36
|
+
throw new CheckError("Could not find registry.json. Ensure @outfitter/tooling is installed.");
|
|
37
|
+
}
|
|
38
|
+
function readToolingVersion(registryPath) {
|
|
39
|
+
try {
|
|
40
|
+
const toolingRoot = dirname(dirname(registryPath));
|
|
41
|
+
const pkgPath = join(toolingRoot, "package.json");
|
|
42
|
+
const content = readFileSync(pkgPath, "utf-8");
|
|
43
|
+
const pkg = JSON.parse(content);
|
|
44
|
+
return pkg.version ?? "unknown";
|
|
45
|
+
} catch {
|
|
46
|
+
return "unknown";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function isRegistryLike(value) {
|
|
50
|
+
if (!value || typeof value !== "object") {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const record = value;
|
|
54
|
+
return !!record["blocks"] && typeof record["blocks"] === "object";
|
|
55
|
+
}
|
|
56
|
+
function loadRegistry() {
|
|
57
|
+
try {
|
|
58
|
+
const registryPath = getRegistryPath();
|
|
59
|
+
const content = readFileSync(registryPath, "utf-8");
|
|
60
|
+
const parsed = JSON.parse(content);
|
|
61
|
+
if (!isRegistryLike(parsed)) {
|
|
62
|
+
return Result.err(new CheckError("Failed to load registry: invalid registry shape"));
|
|
63
|
+
}
|
|
64
|
+
const registry = parsed;
|
|
65
|
+
const toolingVersion = readToolingVersion(registryPath);
|
|
66
|
+
return Result.ok({ registry, toolingVersion });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
69
|
+
return Result.err(new CheckError(`Failed to load registry: ${message}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function resolveBlockFiles(registry, blockName, visited = new Set) {
|
|
73
|
+
if (visited.has(blockName)) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
visited.add(blockName);
|
|
77
|
+
const block = registry.blocks[blockName];
|
|
78
|
+
if (!block) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const files = [];
|
|
82
|
+
if (block.extends && block.extends.length > 0) {
|
|
83
|
+
for (const extendedName of block.extends) {
|
|
84
|
+
files.push(...resolveBlockFiles(registry, extendedName, visited));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (block.files) {
|
|
88
|
+
files.push(...block.files);
|
|
89
|
+
}
|
|
90
|
+
return files;
|
|
91
|
+
}
|
|
92
|
+
function deepEqual(a, b) {
|
|
93
|
+
if (a === b)
|
|
94
|
+
return true;
|
|
95
|
+
if (a === null || b === null)
|
|
96
|
+
return a === b;
|
|
97
|
+
if (typeof a !== typeof b)
|
|
98
|
+
return false;
|
|
99
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
100
|
+
if (a.length !== b.length)
|
|
101
|
+
return false;
|
|
102
|
+
for (let i = 0;i < a.length; i++) {
|
|
103
|
+
if (!deepEqual(a[i], b[i]))
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
109
|
+
const aObj = a;
|
|
110
|
+
const bObj = b;
|
|
111
|
+
const aKeys = Object.keys(aObj);
|
|
112
|
+
const bKeys = Object.keys(bObj);
|
|
113
|
+
if (aKeys.length !== bKeys.length)
|
|
114
|
+
return false;
|
|
115
|
+
for (const key of aKeys) {
|
|
116
|
+
if (!Object.hasOwn(bObj, key))
|
|
117
|
+
return false;
|
|
118
|
+
if (!deepEqual(aObj[key], bObj[key]))
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
function compareFileContent(filePath, localContent, registryContent) {
|
|
126
|
+
if (filePath.endsWith(".json") || filePath.endsWith(".jsonc")) {
|
|
127
|
+
try {
|
|
128
|
+
const cleanLocal = filePath.endsWith(".jsonc") ? stripJsoncComments(localContent) : localContent;
|
|
129
|
+
const cleanRegistry = filePath.endsWith(".jsonc") ? stripJsoncComments(registryContent) : registryContent;
|
|
130
|
+
const localParsed = JSON.parse(cleanLocal);
|
|
131
|
+
const registryParsed = JSON.parse(cleanRegistry);
|
|
132
|
+
return deepEqual(localParsed, registryParsed);
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
return localContent === registryContent;
|
|
136
|
+
}
|
|
137
|
+
function stripJsoncComments(content) {
|
|
138
|
+
return content.split(`
|
|
139
|
+
`).map((line) => {
|
|
140
|
+
let inString = false;
|
|
141
|
+
for (let i = 0;i < line.length; i++) {
|
|
142
|
+
const char = line[i];
|
|
143
|
+
if (char === '"' && (i === 0 || line[i - 1] !== "\\")) {
|
|
144
|
+
inString = !inString;
|
|
145
|
+
}
|
|
146
|
+
if (!inString && char === "/" && line[i + 1] === "/") {
|
|
147
|
+
return line.slice(0, i).trimEnd();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return line;
|
|
151
|
+
}).filter((line) => line.trim() !== "").join(`
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
var BLOCK_FILE_MARKERS = {
|
|
155
|
+
linter: [".oxlintrc.json", ".oxfmtrc.jsonc"],
|
|
156
|
+
lefthook: [".lefthook.yml"],
|
|
157
|
+
claude: [".claude/settings.json"],
|
|
158
|
+
markdownlint: [".markdownlint-cli2.jsonc"],
|
|
159
|
+
bootstrap: ["scripts/bootstrap.sh"]
|
|
160
|
+
};
|
|
161
|
+
function detectBlocksByFilePresence(cwd) {
|
|
162
|
+
const detected = [];
|
|
163
|
+
for (const [blockName, files] of Object.entries(BLOCK_FILE_MARKERS)) {
|
|
164
|
+
const hasAnyFile = files.some((filePath) => existsSync(join(cwd, filePath)));
|
|
165
|
+
if (hasAnyFile) {
|
|
166
|
+
detected.push(blockName);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return detected;
|
|
170
|
+
}
|
|
171
|
+
function checkBlock(cwd, blockName, registry, toolingVersion, installedFrom, verbose) {
|
|
172
|
+
const versionFields = {
|
|
173
|
+
...installedFrom !== undefined ? { installedFrom } : {},
|
|
174
|
+
currentToolingVersion: toolingVersion
|
|
175
|
+
};
|
|
176
|
+
const files = resolveBlockFiles(registry, blockName);
|
|
177
|
+
if (files.length === 0 && !registry.blocks[blockName]) {
|
|
178
|
+
return {
|
|
179
|
+
name: blockName,
|
|
180
|
+
status: "missing",
|
|
181
|
+
...versionFields
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (files.length === 0) {
|
|
185
|
+
return {
|
|
186
|
+
name: blockName,
|
|
187
|
+
status: "current",
|
|
188
|
+
...versionFields
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
let allMissing = true;
|
|
192
|
+
let anyDrifted = false;
|
|
193
|
+
const driftedFiles = [];
|
|
194
|
+
for (const file of files) {
|
|
195
|
+
const localPath = join(cwd, file.path);
|
|
196
|
+
if (!existsSync(localPath)) {
|
|
197
|
+
anyDrifted = true;
|
|
198
|
+
if (verbose) {
|
|
199
|
+
driftedFiles.push({ path: file.path, reason: "missing" });
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
allMissing = false;
|
|
204
|
+
const localContent = readFileSync(localPath, "utf-8");
|
|
205
|
+
if (!compareFileContent(file.path, localContent, file.content)) {
|
|
206
|
+
anyDrifted = true;
|
|
207
|
+
if (verbose) {
|
|
208
|
+
driftedFiles.push({ path: file.path, reason: "modified" });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (allMissing) {
|
|
213
|
+
return {
|
|
214
|
+
name: blockName,
|
|
215
|
+
status: "missing",
|
|
216
|
+
...versionFields,
|
|
217
|
+
...verbose && driftedFiles.length > 0 ? { driftedFiles } : {}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (anyDrifted) {
|
|
221
|
+
return {
|
|
222
|
+
name: blockName,
|
|
223
|
+
status: "drifted",
|
|
224
|
+
...versionFields,
|
|
225
|
+
...verbose && driftedFiles.length > 0 ? { driftedFiles } : {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
name: blockName,
|
|
230
|
+
status: "current",
|
|
231
|
+
...versionFields
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function runCheck(options) {
|
|
235
|
+
const {
|
|
236
|
+
cwd: rawCwd,
|
|
237
|
+
verbose = false,
|
|
238
|
+
block: blockFilter,
|
|
239
|
+
manifestOnly = false
|
|
240
|
+
} = options;
|
|
241
|
+
const cwd = resolve(rawCwd);
|
|
242
|
+
const registryResult = loadRegistry();
|
|
243
|
+
if (registryResult.isErr()) {
|
|
244
|
+
return registryResult;
|
|
245
|
+
}
|
|
246
|
+
const { registry, toolingVersion } = registryResult.value;
|
|
247
|
+
const manifestResult = await readManifest(cwd);
|
|
248
|
+
if (manifestResult.isErr()) {
|
|
249
|
+
return Result.err(new CheckError(`Failed to read manifest: ${manifestResult.error.message}`));
|
|
250
|
+
}
|
|
251
|
+
const manifest = manifestResult.value;
|
|
252
|
+
let blocksToCheck;
|
|
253
|
+
if (manifest) {
|
|
254
|
+
blocksToCheck = Object.entries(manifest.blocks).map(([name, entry]) => ({
|
|
255
|
+
name,
|
|
256
|
+
installedFrom: entry.installedFrom
|
|
257
|
+
}));
|
|
258
|
+
} else if (manifestOnly) {
|
|
259
|
+
blocksToCheck = [];
|
|
260
|
+
} else {
|
|
261
|
+
const detected = detectBlocksByFilePresence(cwd);
|
|
262
|
+
blocksToCheck = detected.map((name) => ({
|
|
263
|
+
name,
|
|
264
|
+
installedFrom: undefined
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
if (blockFilter) {
|
|
268
|
+
blocksToCheck = blocksToCheck.filter((b) => b.name === blockFilter);
|
|
269
|
+
}
|
|
270
|
+
const blocks = [];
|
|
271
|
+
for (const { name, installedFrom } of blocksToCheck) {
|
|
272
|
+
blocks.push(checkBlock(cwd, name, registry, toolingVersion, installedFrom, verbose));
|
|
273
|
+
}
|
|
274
|
+
const currentCount = blocks.filter((b) => b.status === "current").length;
|
|
275
|
+
const driftedCount = blocks.filter((b) => b.status === "drifted").length;
|
|
276
|
+
const missingCount = blocks.filter((b) => b.status === "missing").length;
|
|
277
|
+
return Result.ok({
|
|
278
|
+
blocks,
|
|
279
|
+
totalChecked: blocks.length,
|
|
280
|
+
currentCount,
|
|
281
|
+
driftedCount,
|
|
282
|
+
missingCount
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async function printCheckResults(result, options) {
|
|
286
|
+
const structuredMode = resolveStructuredOutputMode(options?.mode);
|
|
287
|
+
if (structuredMode) {
|
|
288
|
+
await output(result, { mode: structuredMode });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const theme = createTheme();
|
|
292
|
+
const lines = [];
|
|
293
|
+
lines.push("");
|
|
294
|
+
lines.push("Outfitter Check");
|
|
295
|
+
lines.push("=".repeat(50));
|
|
296
|
+
lines.push("");
|
|
297
|
+
for (const block of result.blocks) {
|
|
298
|
+
let statusIcon;
|
|
299
|
+
switch (block.status) {
|
|
300
|
+
case "current":
|
|
301
|
+
statusIcon = theme.success("[PASS]");
|
|
302
|
+
break;
|
|
303
|
+
case "drifted":
|
|
304
|
+
statusIcon = theme.error("[DRIFT]");
|
|
305
|
+
break;
|
|
306
|
+
case "missing":
|
|
307
|
+
statusIcon = theme.warning("[MISSING]");
|
|
308
|
+
break;
|
|
309
|
+
default:
|
|
310
|
+
statusIcon = "[?]";
|
|
311
|
+
}
|
|
312
|
+
const versionInfo = block.installedFrom ? ` (installed from ${block.installedFrom})` : "";
|
|
313
|
+
lines.push(`${statusIcon} ${block.name}${versionInfo}`);
|
|
314
|
+
if (options?.verbose && block.driftedFiles) {
|
|
315
|
+
for (const file of block.driftedFiles) {
|
|
316
|
+
const reason = file.reason === "missing" ? "file missing" : "modified";
|
|
317
|
+
lines.push(` ${theme.muted(`${file.path}: ${reason}`)}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
lines.push("");
|
|
322
|
+
lines.push("=".repeat(50));
|
|
323
|
+
const summaryColor = result.driftedCount === 0 && result.missingCount === 0 ? theme.success : theme.error;
|
|
324
|
+
lines.push(summaryColor(`${result.currentCount}/${result.totalChecked} blocks current`));
|
|
325
|
+
if (result.driftedCount > 0) {
|
|
326
|
+
lines.push(theme.muted(`${result.driftedCount} block(s) have drifted`));
|
|
327
|
+
}
|
|
328
|
+
if (result.missingCount > 0) {
|
|
329
|
+
lines.push(theme.muted(`${result.missingCount} block(s) have missing files`));
|
|
330
|
+
}
|
|
331
|
+
if (result.driftedCount > 0 || result.missingCount > 0) {
|
|
332
|
+
lines.push("");
|
|
333
|
+
lines.push(theme.muted("Run 'outfitter add <block> --force' to restore registry defaults."));
|
|
334
|
+
}
|
|
335
|
+
if (result.totalChecked === 0) {
|
|
336
|
+
lines.push("");
|
|
337
|
+
lines.push(theme.muted("No blocks found. If this is a workspace root, run 'outfitter check' from an app or package directory."));
|
|
338
|
+
}
|
|
339
|
+
await output(lines, { mode: "human" });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export { CheckError, runCheck, printCheckResults };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
resolveStructuredOutputMode
|
|
4
|
+
} from "./outfitter-7r12fj7f.js";
|
|
5
|
+
|
|
6
|
+
// apps/outfitter/src/commands/check-publish-guardrails.ts
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
8
|
+
import { join, resolve } from "path";
|
|
9
|
+
import { Result } from "@outfitter/contracts";
|
|
10
|
+
var REQUIRED_PREPUBLISH_ONLY = "bun ../../scripts/check-publish-manifest.ts";
|
|
11
|
+
var WORKSPACE_DIRS = ["packages", "apps", "plugins"];
|
|
12
|
+
|
|
13
|
+
class CheckPublishGuardrailsError extends Error {
|
|
14
|
+
_tag = "CheckPublishGuardrailsError";
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "CheckPublishGuardrailsError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function readManifest(path) {
|
|
21
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
function listWorkspacePackageManifests(cwd) {
|
|
24
|
+
const manifests = [];
|
|
25
|
+
for (const workspaceDirName of WORKSPACE_DIRS) {
|
|
26
|
+
const workspaceDir = join(cwd, workspaceDirName);
|
|
27
|
+
if (!existsSync(workspaceDir)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
|
|
31
|
+
if (!entry.isDirectory()) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const manifestPath = join(workspaceDir, entry.name, "package.json");
|
|
35
|
+
if (!existsSync(manifestPath)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
manifests.push({
|
|
39
|
+
path: manifestPath,
|
|
40
|
+
manifest: readManifest(manifestPath)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return manifests;
|
|
45
|
+
}
|
|
46
|
+
function isPublishablePackage(manifest) {
|
|
47
|
+
return manifest.private !== true;
|
|
48
|
+
}
|
|
49
|
+
function findPublishGuardrailViolations(packages) {
|
|
50
|
+
const violations = [];
|
|
51
|
+
for (const pkg of packages) {
|
|
52
|
+
if (!isPublishablePackage(pkg.manifest)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const actual = pkg.manifest.scripts?.["prepublishOnly"];
|
|
56
|
+
if (actual === REQUIRED_PREPUBLISH_ONLY) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
violations.push({
|
|
60
|
+
packageName: pkg.manifest.name ?? pkg.path,
|
|
61
|
+
path: pkg.path,
|
|
62
|
+
expected: REQUIRED_PREPUBLISH_ONLY,
|
|
63
|
+
actual
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return violations;
|
|
67
|
+
}
|
|
68
|
+
function formatActual(actual) {
|
|
69
|
+
return typeof actual === "string" ? actual : "<missing>";
|
|
70
|
+
}
|
|
71
|
+
async function runCheckPublishGuardrails(options) {
|
|
72
|
+
try {
|
|
73
|
+
const workspaceRoot = resolve(options.cwd);
|
|
74
|
+
const packages = listWorkspacePackageManifests(workspaceRoot);
|
|
75
|
+
const violations = findPublishGuardrailViolations(packages);
|
|
76
|
+
return Result.ok({
|
|
77
|
+
workspaceRoot,
|
|
78
|
+
checkedManifestCount: packages.length,
|
|
79
|
+
violations,
|
|
80
|
+
ok: violations.length === 0
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : "Failed to check publish guardrails";
|
|
84
|
+
return Result.err(new CheckPublishGuardrailsError(message));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function printCheckPublishGuardrailsResult(result, options) {
|
|
88
|
+
const structuredMode = resolveStructuredOutputMode(options?.mode);
|
|
89
|
+
if (structuredMode) {
|
|
90
|
+
const serialized = structuredMode === "json" ? JSON.stringify(result, null, 2) : JSON.stringify(result);
|
|
91
|
+
process.stdout.write(`${serialized}
|
|
92
|
+
`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (result.ok) {
|
|
96
|
+
process.stdout.write(`[publish-guardrails] checked ${result.checkedManifestCount} workspace manifests; all publishable packages enforce prepublishOnly guard
|
|
97
|
+
`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const details = result.violations.map((violation) => `- ${violation.packageName} (${violation.path}): prepublishOnly=${formatActual(violation.actual)}; expected=${violation.expected}`).join(`
|
|
101
|
+
`);
|
|
102
|
+
process.stderr.write([
|
|
103
|
+
`[publish-guardrails] found ${result.violations.length} publish guardrail violation(s)`,
|
|
104
|
+
details,
|
|
105
|
+
"",
|
|
106
|
+
"Every non-private package must set scripts.prepublishOnly to run check-publish-manifest."
|
|
107
|
+
].join(`
|
|
108
|
+
`));
|
|
109
|
+
process.stderr.write(`
|
|
110
|
+
`);
|
|
111
|
+
}
|
|
112
|
+
function parseCliArgs(argv) {
|
|
113
|
+
let cwd = process.cwd();
|
|
114
|
+
let outputMode = "human";
|
|
115
|
+
for (let index = 0;index < argv.length; index++) {
|
|
116
|
+
const arg = argv[index];
|
|
117
|
+
if (arg === "--cwd") {
|
|
118
|
+
const value = argv[index + 1];
|
|
119
|
+
if (!value) {
|
|
120
|
+
throw new CheckPublishGuardrailsError("Missing value for --cwd");
|
|
121
|
+
}
|
|
122
|
+
cwd = value;
|
|
123
|
+
index += 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (arg === "--json") {
|
|
127
|
+
outputMode = "json";
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg === "--jsonl") {
|
|
131
|
+
outputMode = "jsonl";
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
cwd: resolve(cwd),
|
|
137
|
+
outputMode
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function runCheckPublishGuardrailsFromArgv(argv) {
|
|
141
|
+
let parsed;
|
|
142
|
+
try {
|
|
143
|
+
parsed = parseCliArgs(argv);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const message = error instanceof Error ? error.message : "Invalid command arguments";
|
|
146
|
+
process.stderr.write(`${message}
|
|
147
|
+
`);
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
const result = await runCheckPublishGuardrails({ cwd: parsed.cwd });
|
|
151
|
+
if (result.isErr()) {
|
|
152
|
+
process.stderr.write(`${result.error.message}
|
|
153
|
+
`);
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
await printCheckPublishGuardrailsResult(result.value, {
|
|
157
|
+
mode: parsed.outputMode
|
|
158
|
+
});
|
|
159
|
+
return result.value.ok ? 0 : 1;
|
|
160
|
+
}
|
|
161
|
+
if (import.meta.main) {
|
|
162
|
+
runCheckPublishGuardrailsFromArgv(process.argv.slice(2)).then((exitCode) => {
|
|
163
|
+
process.exit(exitCode);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { REQUIRED_PREPUBLISH_ONLY, CheckPublishGuardrailsError, findPublishGuardrailViolations, runCheckPublishGuardrails, printCheckPublishGuardrailsResult, runCheckPublishGuardrailsFromArgv };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// apps/outfitter/src/commands/docs-module-loader.ts
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
var require2 = createRequire(import.meta.url);
|
|
8
|
+
function resolveDocsEntrypoint() {
|
|
9
|
+
const packageJsonPath = require2.resolve("@outfitter/docs/package.json");
|
|
10
|
+
const packageRoot = dirname(packageJsonPath);
|
|
11
|
+
const srcEntrypoint = join(packageRoot, "src", "index.ts");
|
|
12
|
+
if (existsSync(srcEntrypoint)) {
|
|
13
|
+
return srcEntrypoint;
|
|
14
|
+
}
|
|
15
|
+
const distEntrypoint = join(packageRoot, "dist", "index.js");
|
|
16
|
+
if (existsSync(distEntrypoint)) {
|
|
17
|
+
return distEntrypoint;
|
|
18
|
+
}
|
|
19
|
+
throw new Error("Unable to resolve @outfitter/docs entrypoint (expected src/index.ts or dist/index.js).");
|
|
20
|
+
}
|
|
21
|
+
var docsModulePromise;
|
|
22
|
+
async function loadDocsModule() {
|
|
23
|
+
if (!docsModulePromise) {
|
|
24
|
+
docsModulePromise = (async () => {
|
|
25
|
+
const entrypoint = resolveDocsEntrypoint();
|
|
26
|
+
const module = await import(pathToFileURL(entrypoint).href);
|
|
27
|
+
if (typeof module.createDocsCommand !== "function" || typeof module.executeCheckCommand !== "function" || typeof module.executeSyncCommand !== "function" || typeof module.executeExportCommand !== "function" || typeof module.generateDocsMap !== "function" || typeof module.generatePackageListSection !== "function" || typeof module.replaceSentinelSection !== "function") {
|
|
28
|
+
throw new Error("Resolved @outfitter/docs entrypoint does not export required docs functions.");
|
|
29
|
+
}
|
|
30
|
+
return module;
|
|
31
|
+
})();
|
|
32
|
+
}
|
|
33
|
+
return await docsModulePromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { loadDocsModule };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
deriveBinName,
|
|
4
|
+
deriveProjectName,
|
|
5
|
+
executePlan,
|
|
6
|
+
isPathWithin,
|
|
7
|
+
resolveAuthor,
|
|
8
|
+
resolveYear,
|
|
9
|
+
scaffoldWorkspaceRoot,
|
|
10
|
+
validateProjectDirectoryName
|
|
11
|
+
} from "./outfitter-q1g58t85.js";
|
|
12
|
+
import {
|
|
13
|
+
runPostScaffold
|
|
14
|
+
} from "./outfitter-4s9meh3j.js";
|
|
15
|
+
import {
|
|
16
|
+
OperationCollector
|
|
17
|
+
} from "./outfitter-1h7k8xxt.js";
|
|
18
|
+
|
|
19
|
+
// apps/outfitter/src/commands/init-execution.ts
|
|
20
|
+
import { existsSync } from "fs";
|
|
21
|
+
import { basename, join, resolve } from "path";
|
|
22
|
+
import { Result } from "@outfitter/contracts";
|
|
23
|
+
function toExecutionErrorMessage(error) {
|
|
24
|
+
if (error instanceof Error) {
|
|
25
|
+
return error.message;
|
|
26
|
+
}
|
|
27
|
+
return "Unknown init error";
|
|
28
|
+
}
|
|
29
|
+
function buildInitPlan(target, input, projectDir, resolvedBinName) {
|
|
30
|
+
const blocks = input.includeTooling ? input.blocksOverride ?? [...target.defaultBlocks] : [];
|
|
31
|
+
return {
|
|
32
|
+
values: {
|
|
33
|
+
name: deriveProjectName(input.packageName),
|
|
34
|
+
projectName: deriveProjectName(input.packageName),
|
|
35
|
+
packageName: input.packageName,
|
|
36
|
+
binName: resolvedBinName,
|
|
37
|
+
version: "0.1.0",
|
|
38
|
+
description: "A new project created with Outfitter",
|
|
39
|
+
author: resolveAuthor(),
|
|
40
|
+
year: resolveYear()
|
|
41
|
+
},
|
|
42
|
+
changes: [
|
|
43
|
+
{
|
|
44
|
+
type: "copy-preset",
|
|
45
|
+
preset: target.presetDir,
|
|
46
|
+
targetDir: projectDir,
|
|
47
|
+
includeTooling: input.includeTooling,
|
|
48
|
+
overlayBaseTemplate: true
|
|
49
|
+
},
|
|
50
|
+
{ type: "inject-shared-config" },
|
|
51
|
+
...input.local ? [{ type: "rewrite-local-dependencies", mode: "workspace" }] : [],
|
|
52
|
+
...blocks.length > 0 ? [{ type: "add-blocks", blocks }] : []
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function executeInitPipeline(input, target, options) {
|
|
57
|
+
const projectName = deriveProjectName(input.packageName);
|
|
58
|
+
if (input.structure === "workspace") {
|
|
59
|
+
const invalidProjectName = validateProjectDirectoryName(projectName);
|
|
60
|
+
if (invalidProjectName) {
|
|
61
|
+
return Result.err(`Invalid workspace project name '${projectName}': ${invalidProjectName}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const collector = options.dryRun ? new OperationCollector : undefined;
|
|
65
|
+
const projectBaseDir = resolve(input.rootDir, target.placement);
|
|
66
|
+
const resolvedProjectDir = resolve(projectBaseDir, projectName);
|
|
67
|
+
if (input.structure === "workspace" && !isPathWithin(projectBaseDir, resolvedProjectDir)) {
|
|
68
|
+
return Result.err(`Invalid workspace project name '${projectName}': path escapes '${projectBaseDir}'`);
|
|
69
|
+
}
|
|
70
|
+
const projectDir = input.structure === "workspace" ? resolvedProjectDir : input.rootDir;
|
|
71
|
+
if (input.structure === "single") {
|
|
72
|
+
if (existsSync(join(input.rootDir, "package.json")) && !options.force) {
|
|
73
|
+
return Result.err(`Directory '${input.rootDir}' already has a package.json. Use --force to overwrite, or use 'outfitter add' for existing projects.`);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
const workspaceName = input.workspaceName ?? basename(input.rootDir);
|
|
77
|
+
const workspacePackageJsonPath = join(input.rootDir, "package.json");
|
|
78
|
+
if (options.dryRun) {
|
|
79
|
+
if (existsSync(workspacePackageJsonPath) && !options.force) {
|
|
80
|
+
return Result.err(`Directory '${input.rootDir}' already has a package.json. Use --force to overwrite.`);
|
|
81
|
+
}
|
|
82
|
+
collector?.add({
|
|
83
|
+
type: "dir-create",
|
|
84
|
+
path: join(input.rootDir, "apps")
|
|
85
|
+
});
|
|
86
|
+
collector?.add({
|
|
87
|
+
type: "dir-create",
|
|
88
|
+
path: join(input.rootDir, "packages")
|
|
89
|
+
});
|
|
90
|
+
collector?.add(existsSync(workspacePackageJsonPath) ? {
|
|
91
|
+
type: "file-overwrite",
|
|
92
|
+
path: workspacePackageJsonPath,
|
|
93
|
+
source: "generated"
|
|
94
|
+
} : {
|
|
95
|
+
type: "file-create",
|
|
96
|
+
path: workspacePackageJsonPath,
|
|
97
|
+
source: "generated"
|
|
98
|
+
});
|
|
99
|
+
const readmePath = join(input.rootDir, "README.md");
|
|
100
|
+
if (options.force || !existsSync(readmePath)) {
|
|
101
|
+
collector?.add(existsSync(readmePath) ? {
|
|
102
|
+
type: "file-overwrite",
|
|
103
|
+
path: readmePath,
|
|
104
|
+
source: "generated"
|
|
105
|
+
} : {
|
|
106
|
+
type: "file-create",
|
|
107
|
+
path: readmePath,
|
|
108
|
+
source: "generated"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const gitignorePath = join(input.rootDir, ".gitignore");
|
|
112
|
+
if (options.force || !existsSync(gitignorePath)) {
|
|
113
|
+
collector?.add(existsSync(gitignorePath) ? {
|
|
114
|
+
type: "file-overwrite",
|
|
115
|
+
path: gitignorePath,
|
|
116
|
+
source: "generated"
|
|
117
|
+
} : {
|
|
118
|
+
type: "file-create",
|
|
119
|
+
path: gitignorePath,
|
|
120
|
+
source: "generated"
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
const workspaceResult = scaffoldWorkspaceRoot(input.rootDir, workspaceName, options.force);
|
|
125
|
+
if (workspaceResult.isErr()) {
|
|
126
|
+
return Result.err(workspaceResult.error.message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const resolvedBinName = input.binName ?? deriveBinName(deriveProjectName(input.packageName));
|
|
131
|
+
const plan = buildInitPlan(target, input, projectDir, resolvedBinName);
|
|
132
|
+
const executeResult = await executePlan(plan, {
|
|
133
|
+
force: options.force,
|
|
134
|
+
...collector ? { collector } : {}
|
|
135
|
+
});
|
|
136
|
+
if (executeResult.isErr()) {
|
|
137
|
+
return Result.err(toExecutionErrorMessage(executeResult.error));
|
|
138
|
+
}
|
|
139
|
+
const postScaffoldResult = await runPostScaffold({
|
|
140
|
+
rootDir: input.rootDir,
|
|
141
|
+
projectDir,
|
|
142
|
+
origin: "init",
|
|
143
|
+
target: input.preset,
|
|
144
|
+
structure: input.structure,
|
|
145
|
+
skipInstall: options.skipInstall,
|
|
146
|
+
skipGit: options.skipGit,
|
|
147
|
+
skipCommit: options.skipCommit,
|
|
148
|
+
dryRun: options.dryRun,
|
|
149
|
+
installTimeoutMs: options.installTimeout
|
|
150
|
+
}, collector);
|
|
151
|
+
if (postScaffoldResult.isErr()) {
|
|
152
|
+
return Result.err("Post-scaffold step failed");
|
|
153
|
+
}
|
|
154
|
+
return Result.ok({
|
|
155
|
+
structure: input.structure,
|
|
156
|
+
rootDir: input.rootDir,
|
|
157
|
+
projectDir,
|
|
158
|
+
preset: input.preset,
|
|
159
|
+
packageName: input.packageName,
|
|
160
|
+
blocksAdded: executeResult.value.blocksAdded,
|
|
161
|
+
postScaffold: postScaffoldResult.value,
|
|
162
|
+
...collector ? { dryRunPlan: collector.toJSON() } : {}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export { executeInitPipeline };
|