ioc-manifest 4.0.0 → 4.0.2
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 +26 -0
- package/dist/cli/ioc.js +8 -295
- package/dist/cli/ioc.js.map +1 -1
- package/dist/cli/runIocCli.d.ts +18 -0
- package/dist/cli/runIocCli.d.ts.map +1 -0
- package/dist/cli/runIocCli.js +317 -0
- package/dist/cli/runIocCli.js.map +1 -0
- package/dist/config/configModuleLoader.d.ts +43 -0
- package/dist/config/configModuleLoader.d.ts.map +1 -0
- package/dist/config/configModuleLoader.js +192 -0
- package/dist/config/configModuleLoader.js.map +1 -0
- package/dist/config/loadIocConfig.d.ts +2 -0
- package/dist/config/loadIocConfig.d.ts.map +1 -1
- package/dist/config/loadIocConfig.js +43 -8
- package/dist/config/loadIocConfig.js.map +1 -1
- package/dist/generator/formatGeneratedFile.d.ts +8 -0
- package/dist/generator/formatGeneratedFile.d.ts.map +1 -0
- package/dist/generator/formatGeneratedFile.js +110 -0
- package/dist/generator/formatGeneratedFile.js.map +1 -0
- package/dist/generator/generateManifest.d.ts.map +1 -1
- package/dist/generator/generateManifest.js +6 -32
- package/dist/generator/generateManifest.js.map +1 -1
- package/dist/generator/generatedReferenceForms.d.ts +1 -1
- package/dist/generator/generatedReferenceForms.js +1 -1
- package/dist/test-support/runIocCliInProcess.d.ts +14 -0
- package/dist/test-support/runIocCliInProcess.d.ts.map +1 -0
- package/dist/test-support/runIocCliInProcess.js +55 -0
- package/dist/test-support/runIocCliInProcess.js.map +1 -0
- package/dist/test-support/testLaneSeam.d.ts +26 -0
- package/dist/test-support/testLaneSeam.d.ts.map +1 -0
- package/dist/test-support/testLaneSeam.js +295 -0
- package/dist/test-support/testLaneSeam.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -59,6 +59,32 @@ Full documentation lives at **[reharik.github.io/ioc-manifest](https://reharik.g
|
|
|
59
59
|
- **[Quick start](https://reharik.github.io/ioc-manifest/guide/quick-start)** — units → config → generate → bootstrap, in a few minutes.
|
|
60
60
|
- **[Adopting on an existing codebase](https://reharik.github.io/ioc-manifest/guide/adopting)** — why the first run is red, and how to read it.
|
|
61
61
|
|
|
62
|
+
## Working on ioc-manifest
|
|
63
|
+
|
|
64
|
+
The test suite runs in two lanes.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm run test:fast # ~6s, 568 tests — everything that builds no TypeScript program
|
|
68
|
+
npm test # the whole suite; what CI runs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Iterate on `test:fast`; run the full suite before you report anything.** `test:fast` is the
|
|
72
|
+
non-`*.integration.test.ts` glob, and what earns a file the `.integration` suffix is mechanism, not
|
|
73
|
+
taste: a test that builds a TypeScript program, spawns a subprocess, or runs codegen belongs in the
|
|
74
|
+
slow lane, because every one of those costs seconds. `src/test-support/testLaneSeam.test.ts`
|
|
75
|
+
enforces that in both directions — it fails on a fast-lane file that can reach any of the three, and
|
|
76
|
+
on an `.integration` file that can reach none of them — so the suffix cannot quietly stop being
|
|
77
|
+
true. When it fails, the fix is a rename, and the message says which way.
|
|
78
|
+
|
|
79
|
+
The rest of the checks:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run typecheck # tsc --noEmit
|
|
83
|
+
npm run build # dist/
|
|
84
|
+
npm run gen:manifest && git diff --exit-code src/generated # generated-diff must be zero
|
|
85
|
+
npm run example:full # the multi-package example, end to end
|
|
86
|
+
```
|
|
87
|
+
|
|
62
88
|
## Contributing to the docs
|
|
63
89
|
|
|
64
90
|
The docs are a [VitePress](https://vitepress.dev/) site under `docs/`.
|
package/dist/cli/ioc.js
CHANGED
|
@@ -1,301 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @fileoverview `ioc`
|
|
3
|
+
* @fileoverview The `ioc` executable: argv in, exit code out.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the CLI runs under plain `node`, which cannot import a `.ts` file at all.
|
|
9
|
-
* - `ioc inspect --discovery` — re-runs source discovery (no manifest read) for drift analysis.
|
|
10
|
-
* - `ioc explain <key>` — one cradle key: what it resolves to, its lifetime and the chain that
|
|
11
|
-
* decided it, what it depends on, and who depends on it. Same two modes as `inspect`.
|
|
12
|
-
* - `ioc validate` — app mode only: cross-manifest composition checks without writing files (CI gate).
|
|
5
|
+
* Everything the CLI actually does is in `runIocCli.ts`. This file is the process boundary and
|
|
6
|
+
* nothing else — it is what `bin/ioc.cjs` spawns, and what the tests that care about the process
|
|
7
|
+
* contract spawn too.
|
|
13
8
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* `process.exitCode` rather than `process.exit`: the latter would truncate any output still queued
|
|
10
|
+
* on a pipe, which is exactly the situation the `--json` surfaces are used in.
|
|
16
11
|
*/
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
import { formatCommandMap, formatVerbHelp } from "./commandMap.js";
|
|
20
|
-
import { generateManifest } from "../generator/generateManifest.js";
|
|
21
|
-
import { resolveIocConfigPath, resolveProjectRootFromIocConfigPath, tryLoadIocConfig, } from "../config/loadIocConfig.js";
|
|
22
|
-
import { buildDiscoveryReport, buildInspectionReport, filterDiscoveryReportByContract, filterInspectionReportByContract, formatDiscoveryReport, formatDiscoveryReportJson, formatInspectionReport, formatInspectionReportJson, readPriorGroupMembers, } from "../inspection/index.js";
|
|
23
|
-
import { loadManifestForInspection, tryLoadManifestForInspection, } from "../inspection/loadManifestForInspection.js";
|
|
24
|
-
import { resolveDiscoveryManifestContext, runDiscoveryAnalysis, } from "../inspection/runDiscoveryAnalysis.js";
|
|
25
|
-
import { explainFromDiscovery, explainFromManifest, } from "../inspection/explain.js";
|
|
26
|
-
import { buildExplainComposedView, } from "../inspection/explainComposedView.js";
|
|
27
|
-
import { loadCompositionContext } from "../composition/compositionContext.js";
|
|
28
|
-
import { assessFreshness } from "../composition/freshnessPass.js";
|
|
29
|
-
import { isAppMode } from "../config/iocMode.js";
|
|
30
|
-
import { formatExplainReport, formatExplainReportJson, } from "../inspection/formatExplain.js";
|
|
31
|
-
import { printValidateResult, runValidate, } from "../validate/runValidate.js";
|
|
32
|
-
import { formatCaughtErrorForTerminal } from "../diagnostics/colorizeDiagnostic.js";
|
|
33
|
-
import { formatStalenessBanner, readGenerationRecord, readGenerationState, } from "../diagnostics/generationState.js";
|
|
34
|
-
import { currentInputsForConfigPath } from "../diagnostics/currentInputsHash.js";
|
|
35
|
-
import { formatFreshnessAdvisory, formatFreshnessBanner, isStale, isUnknown, judgeFreshness, } from "../diagnostics/freshness.js";
|
|
36
|
-
import { LOCAL_PACKAGE_IDENTIFIER } from "../config/packageIdentifier.js";
|
|
37
|
-
/**
|
|
38
|
-
* The composed picture `ioc explain` answers over, in app mode.
|
|
39
|
-
*
|
|
40
|
-
* The SAME loader the composition suite builds its picture from — `loadCompositionContext` — so an
|
|
41
|
-
* explanation and a `ioc validate` finding about the same key cannot be built on different readings
|
|
42
|
-
* of the same manifests. Library mode gets `undefined` and the local answer it has always had.
|
|
43
|
-
*
|
|
44
|
-
* Degradation is a note, never a failure. `explain` is a view: an app whose composed package cannot
|
|
45
|
-
* be resolved still deserves an answer about its own keys, and saying which half of the picture is
|
|
46
|
-
* missing is strictly better than refusing the question. The same stance every other part of
|
|
47
|
-
* inspection takes about an unreadable composed package.
|
|
48
|
-
*
|
|
49
|
-
* The freshness pass runs for the COMPOSED packages only. The local package is already bannered by
|
|
50
|
-
* {@link bannerIfNotFresh} in manifest mode and is read from source in discovery mode, so judging it
|
|
51
|
-
* again here would print the same warning twice.
|
|
52
|
-
*/
|
|
53
|
-
const loadExplainComposedView = async (cfgPath, knownConfig, json) => {
|
|
54
|
-
const config = knownConfig ?? (await tryLoadIocConfig(cfgPath));
|
|
55
|
-
if (config === undefined || !isAppMode(config)) {
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
const projectRoot = resolveProjectRootFromIocConfigPath(cfgPath);
|
|
59
|
-
const loaded = await loadCompositionContext({
|
|
60
|
-
projectRoot,
|
|
61
|
-
configPath: cfgPath,
|
|
62
|
-
config,
|
|
63
|
-
});
|
|
64
|
-
if (!loaded.ok) {
|
|
65
|
-
if (!json) {
|
|
66
|
-
console.error(`note: the composed picture could not be read (${loaded.message}) — this answer covers this package only.`);
|
|
67
|
-
console.error("");
|
|
68
|
-
}
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
const freshness = await assessFreshness({
|
|
72
|
-
projectRoot,
|
|
73
|
-
configPath: cfgPath,
|
|
74
|
-
config,
|
|
75
|
-
slices: loaded.context.slices,
|
|
76
|
-
includeLocal: false,
|
|
77
|
-
});
|
|
78
|
-
return buildExplainComposedView({ context: loaded.context, freshness });
|
|
79
|
-
};
|
|
80
|
-
const formatResolvedScanDir = (e) => {
|
|
81
|
-
if (e.scope !== undefined) {
|
|
82
|
-
return `${e.absPath} [scope=${e.scope}]`;
|
|
83
|
-
}
|
|
84
|
-
return e.absPath;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Prints the staleness banner ahead of an artifact-derived report, and returns the marker.
|
|
88
|
-
*
|
|
89
|
-
* On stderr, deliberately: the report itself goes to stdout and is routinely piped into a file or
|
|
90
|
-
* another tool. A caveat that vanished into that pipe would be a caveat nobody reads, and one that
|
|
91
|
-
* rode along inside the payload would corrupt it. stderr is where `logInspectContext` already puts
|
|
92
|
-
* the same kind of framing.
|
|
93
|
-
*
|
|
94
|
-
* `--json` gets the marker as data instead, so nothing is printed here.
|
|
95
|
-
*/
|
|
96
|
-
const bannerIfStale = (generatedDir, json) => {
|
|
97
|
-
const marker = readGenerationState(generatedDir);
|
|
98
|
-
if (marker !== undefined && !json) {
|
|
99
|
-
console.error(formatStalenessBanner(marker));
|
|
100
|
-
console.error("");
|
|
101
|
-
}
|
|
102
|
-
return marker;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Prints the freshness banner for THIS package ahead of a manifest-derived report.
|
|
106
|
-
*
|
|
107
|
-
* Banner only, and local only. `inspect` and `explain` describe one package's artifacts; there is
|
|
108
|
-
* no composed picture here to attribute a per-finding caveat to, and nothing in their reports is a
|
|
109
|
-
* "finding" in the sense the composition suite means. What a reader needs is the one fact that
|
|
110
|
-
* changes how to read everything below: the manifest being summarised may predate the sources.
|
|
111
|
-
*
|
|
112
|
-
* Manifest mode only. `--discovery` re-reads the source, so its answer is current by construction
|
|
113
|
-
* and a freshness caveat on it would be about a document it is not showing.
|
|
114
|
-
*
|
|
115
|
-
* Nothing is added to `--json`: these documents carry `staleness` because a failed generation is a
|
|
116
|
-
* fact about the artifacts they are reporting. Freshness here is advisory-only by ruling, and the
|
|
117
|
-
* verb that publishes it as data is `ioc validate`.
|
|
118
|
-
*/
|
|
119
|
-
const bannerIfNotFresh = async (generatedDir, cfgPath, json) => {
|
|
120
|
-
if (json) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const current = await currentInputsForConfigPath(cfgPath);
|
|
124
|
-
const freshness = judgeFreshness({
|
|
125
|
-
name: LOCAL_PACKAGE_IDENTIFIER,
|
|
126
|
-
sourceId: LOCAL_PACKAGE_IDENTIFIER,
|
|
127
|
-
record: readGenerationRecord(generatedDir),
|
|
128
|
-
currentHash: current.hash,
|
|
129
|
-
...(current.unknown !== undefined ? { currentUnknown: current.unknown } : {}),
|
|
130
|
-
});
|
|
131
|
-
if (isStale(freshness)) {
|
|
132
|
-
console.error(formatFreshnessBanner(freshness));
|
|
133
|
-
console.error("");
|
|
134
|
-
}
|
|
135
|
-
else if (isUnknown(freshness)) {
|
|
136
|
-
console.error(formatFreshnessAdvisory(freshness));
|
|
137
|
-
console.error("");
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
/** Adds the `staleness` field to a JSON report document, when there is one. Never renames. */
|
|
141
|
-
const withStalenessField = (json, marker) => {
|
|
142
|
-
if (marker === undefined) {
|
|
143
|
-
return json;
|
|
144
|
-
}
|
|
145
|
-
const parsed = JSON.parse(json);
|
|
146
|
-
return JSON.stringify({ ...parsed, staleness: marker }, null, 2);
|
|
147
|
-
};
|
|
148
|
-
const logInspectContext = (cfgPath, scanDirs) => {
|
|
149
|
-
console.error(`[ioc inspect] resolved config: ${cfgPath}`);
|
|
150
|
-
console.error(`[ioc inspect] resolved discovery scanDirs: ${scanDirs.map(formatResolvedScanDir).join("; ")}`);
|
|
151
|
-
};
|
|
152
|
-
const main = async () => {
|
|
153
|
-
const parsed = parseIocCliArgv(process.argv);
|
|
154
|
-
if (parsed.kind === "help") {
|
|
155
|
-
// `--json` is not a thing a help screen has; what a pipe gets is the same text without escapes,
|
|
156
|
-
// which `formatCommandMap` handles through the shared TTY/NO_COLOR check.
|
|
157
|
-
console.log(parsed.verb === undefined
|
|
158
|
-
? formatCommandMap()
|
|
159
|
-
: formatVerbHelp(parsed.verb));
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (parsed.kind === "generate") {
|
|
163
|
-
const cli = parsed.options;
|
|
164
|
-
await generateManifest({
|
|
165
|
-
iocConfigPath: cli.iocConfigPath,
|
|
166
|
-
paths: cli.projectDir !== undefined
|
|
167
|
-
? { projectRoot: path.resolve(cli.projectDir) }
|
|
168
|
-
: undefined,
|
|
169
|
-
});
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (parsed.kind === "validate") {
|
|
173
|
-
/**
|
|
174
|
-
* `validate` is separate from `generate` so dev codegen can tolerate transient sibling drift;
|
|
175
|
-
* validate is the pre-merge / pre-deploy gate that reports every composition issue at once.
|
|
176
|
-
* Run after `ioc generate`. Does not modify any files.
|
|
177
|
-
*/
|
|
178
|
-
const cli = parsed.options;
|
|
179
|
-
const searchStart = path.resolve(cli.projectDir ?? process.cwd());
|
|
180
|
-
const cfgPath = resolveIocConfigPath(searchStart, cli.iocConfigPath);
|
|
181
|
-
const config = await tryLoadIocConfig(cfgPath);
|
|
182
|
-
if (config === undefined) {
|
|
183
|
-
throw new Error(`No ioc config found at ${cfgPath}. Pass --config or run from a project with ioc.config.ts.`);
|
|
184
|
-
}
|
|
185
|
-
const projectRoot = resolveProjectRootFromIocConfigPath(cfgPath);
|
|
186
|
-
const result = await runValidate({
|
|
187
|
-
projectRoot,
|
|
188
|
-
configPath: cfgPath,
|
|
189
|
-
config,
|
|
190
|
-
json: cli.json,
|
|
191
|
-
});
|
|
192
|
-
const code = printValidateResult(result, cli.json);
|
|
193
|
-
if (code !== 0) {
|
|
194
|
-
process.exitCode = code;
|
|
195
|
-
}
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (parsed.kind === "explain") {
|
|
199
|
-
const cli = parsed.options;
|
|
200
|
-
const searchStart = path.resolve(cli.projectDir ?? process.cwd());
|
|
201
|
-
// Two modes, the same two sources `inspect` uses: the manifest on disk, or a fresh scan. The
|
|
202
|
-
// scan is the only one that can say WHY a lifetime is what it is; the report says so itself
|
|
203
|
-
// rather than leaving a reader to wonder why the chain is missing.
|
|
204
|
-
let staleness;
|
|
205
|
-
let report;
|
|
206
|
-
if (cli.discovery) {
|
|
207
|
-
const resolved = await resolveDiscoveryManifestContext({
|
|
208
|
-
iocConfigPath: cli.iocConfigPath,
|
|
209
|
-
searchStartDir: searchStart,
|
|
210
|
-
});
|
|
211
|
-
const analysis = await runDiscoveryAnalysis({ reuseResolution: resolved });
|
|
212
|
-
// Discovery mode reads SOURCE, so its own answer is never stale — but a reader who reaches
|
|
213
|
-
// for `--discovery` after a failing generation is reconciling two worlds, and saying that the
|
|
214
|
-
// artifacts beside it are stale is the whole point of the banner.
|
|
215
|
-
staleness = bannerIfStale(analysis.generatedDir, cli.json);
|
|
216
|
-
report = explainFromDiscovery(cli.key, analysis, await loadExplainComposedView(resolved.cfgPath, resolved.config, cli.json));
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
const manifest = await loadManifestForInspection(cli.iocConfigPath, searchStart);
|
|
220
|
-
staleness = bannerIfStale(manifest.generatedDir, cli.json);
|
|
221
|
-
await bannerIfNotFresh(manifest.generatedDir, manifest.cfgPath, cli.json);
|
|
222
|
-
report = explainFromManifest(cli.key, manifest, await loadExplainComposedView(manifest.cfgPath, undefined, cli.json));
|
|
223
|
-
}
|
|
224
|
-
console.log(cli.json
|
|
225
|
-
? withStalenessField(formatExplainReportJson(report), staleness)
|
|
226
|
-
: formatExplainReport(report));
|
|
227
|
-
// An unknown key is a failed question, not a healthy report — CI and shell scripts need to see
|
|
228
|
-
// that in the exit code.
|
|
229
|
-
if (report.resolution.kind === "unknown") {
|
|
230
|
-
process.exitCode = 1;
|
|
231
|
-
}
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const cli = parsed.options;
|
|
235
|
-
const searchStart = path.resolve(cli.projectDir ?? process.cwd());
|
|
236
|
-
if (cli.discovery) {
|
|
237
|
-
const resolved = await resolveDiscoveryManifestContext({
|
|
238
|
-
iocConfigPath: cli.iocConfigPath,
|
|
239
|
-
searchStartDir: searchStart,
|
|
240
|
-
});
|
|
241
|
-
logInspectContext(resolved.cfgPath, resolved.options.paths.scanDirs);
|
|
242
|
-
const staleness = bannerIfStale(resolved.options.paths.generatedDir, cli.json);
|
|
243
|
-
const analysis = await runDiscoveryAnalysis({
|
|
244
|
-
reuseResolution: resolved,
|
|
245
|
-
});
|
|
246
|
-
// The manifest is read here and nowhere deeper: `runDiscoveryAnalysis` answers "what does the
|
|
247
|
-
// source say", and must keep answering it without a generated file in sight. What the manifest
|
|
248
|
-
// adds is the BEFORE side — which contracts were group members last generation — and a report
|
|
249
|
-
// that cannot find one simply loses that one signal.
|
|
250
|
-
const prior = await tryLoadManifestForInspection(cli.iocConfigPath, searchStart);
|
|
251
|
-
const full = buildDiscoveryReport({
|
|
252
|
-
...analysis,
|
|
253
|
-
...(prior !== undefined
|
|
254
|
-
? { priorGroupMembers: readPriorGroupMembers(prior.groups) }
|
|
255
|
-
: {}),
|
|
256
|
-
});
|
|
257
|
-
const report = cli.contract !== undefined
|
|
258
|
-
? filterDiscoveryReportByContract(full, cli.contract)
|
|
259
|
-
: full;
|
|
260
|
-
console.log(cli.json
|
|
261
|
-
? withStalenessField(formatDiscoveryReportJson(report), staleness)
|
|
262
|
-
: formatDiscoveryReport(report, { verbose: cli.verbose }));
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const manifest = await loadManifestForInspection(cli.iocConfigPath, searchStart);
|
|
266
|
-
logInspectContext(manifest.cfgPath, manifest.scanDirs);
|
|
267
|
-
const staleness = bannerIfStale(manifest.generatedDir, cli.json);
|
|
268
|
-
await bannerIfNotFresh(manifest.generatedDir, manifest.cfgPath, cli.json);
|
|
269
|
-
const full = buildInspectionReport(manifest.contracts, {
|
|
270
|
-
groups: manifest.groups,
|
|
271
|
-
scopeRoots: manifest.scopeRoots,
|
|
272
|
-
});
|
|
273
|
-
const report = cli.contract !== undefined
|
|
274
|
-
? filterInspectionReportByContract(full, cli.contract)
|
|
275
|
-
: full;
|
|
276
|
-
console.log(cli.json
|
|
277
|
-
? withStalenessField(formatInspectionReportJson(report), staleness)
|
|
278
|
-
: formatInspectionReport(report, { verbose: cli.verbose }));
|
|
279
|
-
};
|
|
280
|
-
/**
|
|
281
|
-
* The one place a thrown diagnostic becomes terminal output.
|
|
282
|
-
*
|
|
283
|
-
* Generation is this tool's primary error surface — the demand model, the group law, scope-root
|
|
284
|
-
* verification and the whole composition suite all reach a developer as a thrown `Error.message` —
|
|
285
|
-
* so this is where those messages get their colour. It is applied HERE and nowhere upstream because
|
|
286
|
-
* `Error.message` itself must stay escape-free: it is serialized, matched and re-wrapped by things
|
|
287
|
-
* that are not terminals (see `diagnostics/colorizeDiagnostic.ts`).
|
|
288
|
-
*
|
|
289
|
-
* `IOC_DEBUG=1` prints the error object whole, stack included, and is left exactly as it was — it
|
|
290
|
-
* exists to show the raw thing, and tinting it would defeat that.
|
|
291
|
-
*/
|
|
292
|
-
main().catch((error) => {
|
|
293
|
-
if (process.env.IOC_DEBUG === "1") {
|
|
294
|
-
console.error(error);
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
console.error(formatCaughtErrorForTerminal(error));
|
|
298
|
-
}
|
|
299
|
-
process.exit(1);
|
|
300
|
-
});
|
|
12
|
+
import { runIocCli } from "./runIocCli.js";
|
|
13
|
+
process.exitCode = await runIocCli(process.argv);
|
|
301
14
|
//# sourceMappingURL=ioc.js.map
|
package/dist/cli/ioc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ioc.js","sourceRoot":"","sources":["../../src/cli/ioc.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"ioc.js","sourceRoot":"","sources":["../../src/cli/ioc.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,CAAC,QAAQ,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole CLI, including the one place a thrown diagnostic becomes terminal output.
|
|
3
|
+
*
|
|
4
|
+
* Generation is this tool's primary error surface — the demand model, the group law, scope-root
|
|
5
|
+
* verification and the whole composition suite all reach a developer as a thrown `Error.message` —
|
|
6
|
+
* so this is where those messages get their colour. It is applied HERE and nowhere upstream because
|
|
7
|
+
* `Error.message` itself must stay escape-free: it is serialized, matched and re-wrapped by things
|
|
8
|
+
* that are not terminals (see `diagnostics/colorizeDiagnostic.ts`).
|
|
9
|
+
*
|
|
10
|
+
* `IOC_DEBUG=1` prints the error object whole, stack included, and is left exactly as it was — it
|
|
11
|
+
* exists to show the raw thing, and tinting it would defeat that.
|
|
12
|
+
*
|
|
13
|
+
* The catch lives inside the exported function rather than around its call, so the error surface is
|
|
14
|
+
* part of what "running the CLI" means for every caller — a test that asks for an unknown verb gets
|
|
15
|
+
* the same message and the same 1 that a shell does.
|
|
16
|
+
*/
|
|
17
|
+
export declare const runIocCli: (argv: readonly string[]) => Promise<number>;
|
|
18
|
+
//# sourceMappingURL=runIocCli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runIocCli.d.ts","sourceRoot":"","sources":["../../src/cli/runIocCli.ts"],"names":[],"mappings":"AAqZA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAWvE,CAAC"}
|