@descent-vtt/spec-brief 0.4.0 → 0.5.0
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/CHANGELOG.md +47 -0
- package/README.md +1 -0
- package/dist/archive.js +31 -18
- package/dist/archive.js.map +1 -1
- package/dist/brief.js +8 -5
- package/dist/brief.js.map +1 -1
- package/dist/cli.js +21 -13
- package/dist/cli.js.map +1 -1
- package/dist/collisions.d.ts +9 -0
- package/dist/collisions.js +50 -40
- package/dist/collisions.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.js +17 -6
- package/dist/config.js.map +1 -1
- package/dist/corpus.js +21 -15
- package/dist/corpus.js.map +1 -1
- package/dist/engine.d.ts +2 -1
- package/dist/engine.js +24 -15
- package/dist/engine.js.map +1 -1
- package/dist/frontmatter.js +1 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/fs.js +9 -5
- package/dist/fs.js.map +1 -1
- package/dist/git.js +7 -4
- package/dist/git.js.map +1 -1
- package/dist/glob.d.ts +8 -0
- package/dist/glob.js +74 -0
- package/dist/glob.js.map +1 -1
- package/dist/links.js +2 -1
- package/dist/links.js.map +1 -1
- package/dist/lint.js +3 -4
- package/dist/lint.js.map +1 -1
- package/dist/plugins.js +10 -6
- package/dist/plugins.js.map +1 -1
- package/dist/report.d.ts +8 -1
- package/dist/report.js +22 -20
- package/dist/report.js.map +1 -1
- package/dist/rules.js +10 -6
- package/dist/rules.js.map +1 -1
- package/dist/scaffold.js +2 -2
- package/dist/scaffold.js.map +1 -1
- package/dist/schedule.js +10 -20
- package/dist/schedule.js.map +1 -1
- package/dist/schema.js +9 -13
- package/dist/schema.js.map +1 -1
- package/dist/scope.js +2 -2
- package/dist/scope.js.map +1 -1
- package/dist/text.d.ts +4 -1
- package/dist/text.js +10 -6
- package/dist/text.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { readFile } from 'node:fs/promises';
|
|
10
10
|
import { parseArgs } from 'node:util';
|
|
11
11
|
import { TransactionError, ConflictError } from './apply.js';
|
|
12
|
+
import { reported } from './collisions.js';
|
|
12
13
|
import { CONFIG_FILES, ConfigError, initialConfig } from './config.js';
|
|
13
14
|
import { BriefEngine, EngineError, today } from './engine.js';
|
|
14
15
|
import { NodeFileSystem } from './fs.js';
|
|
@@ -36,7 +37,7 @@ const GLOBAL = {
|
|
|
36
37
|
help: { type: 'boolean', short: 'h' },
|
|
37
38
|
version: { type: 'boolean', short: 'v' },
|
|
38
39
|
};
|
|
39
|
-
const
|
|
40
|
+
const TABLE = {
|
|
40
41
|
init: { options: { briefs: { type: 'string' }, archive: { type: 'string' } }, formats: ['pretty', 'json'] },
|
|
41
42
|
new: {
|
|
42
43
|
options: {
|
|
@@ -66,6 +67,12 @@ const COMMANDS = {
|
|
|
66
67
|
},
|
|
67
68
|
unarchive: { options: { 'dry-run': { type: 'boolean' } }, formats: ['pretty', 'json'] },
|
|
68
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* The commands by name, and nothing for a command line that names none. A map,
|
|
72
|
+
* because an object answers to more names than it was given: `constructor` and
|
|
73
|
+
* `toString` are no commands.
|
|
74
|
+
*/
|
|
75
|
+
const COMMANDS = new Map(Object.entries(TABLE));
|
|
69
76
|
/**
|
|
70
77
|
* The formats each command writes, read from the table `main` checks a
|
|
71
78
|
* `--format` against, so the help names no format a command refuses: the
|
|
@@ -73,7 +80,7 @@ const COMMANDS = {
|
|
|
73
80
|
*/
|
|
74
81
|
function formatsHelp() {
|
|
75
82
|
const groups = new Map();
|
|
76
|
-
for (const [command, spec] of Object.entries(
|
|
83
|
+
for (const [command, spec] of Object.entries(TABLE)) {
|
|
77
84
|
const formats = spec.formats.join(', ');
|
|
78
85
|
groups.set(formats, [...(groups.get(formats) ?? []), command]);
|
|
79
86
|
}
|
|
@@ -163,7 +170,7 @@ function commandOf(argv) {
|
|
|
163
170
|
}
|
|
164
171
|
export function parse(argv) {
|
|
165
172
|
const command = commandOf(argv);
|
|
166
|
-
const spec =
|
|
173
|
+
const spec = COMMANDS.get(command);
|
|
167
174
|
if (command !== undefined && spec === undefined) {
|
|
168
175
|
throw new UsageError(`"${command}" is not a command; run spec-brief --help`);
|
|
169
176
|
}
|
|
@@ -180,9 +187,9 @@ export function parse(argv) {
|
|
|
180
187
|
throw new UsageError(error.message);
|
|
181
188
|
}
|
|
182
189
|
}
|
|
190
|
+
/** The value of an option declared a string, which is all `parseArgs` gives one. */
|
|
183
191
|
function text(values, key) {
|
|
184
|
-
|
|
185
|
-
return typeof value === 'string' ? value : undefined;
|
|
192
|
+
return values[key];
|
|
186
193
|
}
|
|
187
194
|
function flag(values, key) {
|
|
188
195
|
return values[key] === true;
|
|
@@ -213,11 +220,9 @@ function useColor(values, io) {
|
|
|
213
220
|
return (io.stdout ?? process.stdout).isTTY === true;
|
|
214
221
|
}
|
|
215
222
|
async function openEngine(run) {
|
|
216
|
-
const root = text(run.values, 'root');
|
|
217
|
-
const config = text(run.values, 'config');
|
|
218
223
|
return BriefEngine.open({
|
|
219
|
-
cwd: root
|
|
220
|
-
|
|
224
|
+
cwd: text(run.values, 'root') ?? run.cwd,
|
|
225
|
+
config: text(run.values, 'config'),
|
|
221
226
|
...(flag(run.values, 'no-git') ? { git: null } : {}),
|
|
222
227
|
noConfig: flag(run.values, 'no-config'),
|
|
223
228
|
});
|
|
@@ -328,7 +333,8 @@ async function runMatrix(run) {
|
|
|
328
333
|
const { report, findings } = await engine.collisions({ all: flag(run.values, 'all-waves') });
|
|
329
334
|
const sorted = sortFindings(findings);
|
|
330
335
|
if (run.format === 'pretty') {
|
|
331
|
-
run
|
|
336
|
+
// Drawn from what the run reports, so a rule that is off marks nothing in the grid.
|
|
337
|
+
run.out(`${prettyMatrix(reported(engine.corpus, report), run.style)}\n`);
|
|
332
338
|
return failing(sorted, run.strict) ? EXIT_FAILED : EXIT_OK;
|
|
333
339
|
}
|
|
334
340
|
return emitFindings(run, 'matrix', sorted, engine.corpus.live.length, matrixJson(report));
|
|
@@ -408,9 +414,10 @@ export async function main(argv = process.argv.slice(2), io = {}) {
|
|
|
408
414
|
}
|
|
409
415
|
if (flag(values, 'help') || command === undefined) {
|
|
410
416
|
stdout.write(HELP);
|
|
411
|
-
|
|
417
|
+
// Asked for, the help is the answer; printed because no command was named, it is the error.
|
|
418
|
+
return flag(values, 'help') ? EXIT_OK : EXIT_ERROR;
|
|
412
419
|
}
|
|
413
|
-
const spec = COMMANDS
|
|
420
|
+
const spec = COMMANDS.get(command);
|
|
414
421
|
const format = (text(values, 'format') ?? 'pretty');
|
|
415
422
|
if (!spec.formats.includes(format)) {
|
|
416
423
|
err(`--format for ${command} is one of ${spec.formats.join(', ')}, not "${format}"`);
|
|
@@ -422,7 +429,8 @@ export async function main(argv = process.argv.slice(2), io = {}) {
|
|
|
422
429
|
stdout.write(t);
|
|
423
430
|
},
|
|
424
431
|
err,
|
|
425
|
-
|
|
432
|
+
// Read by the pretty format alone: the others have nothing to colour.
|
|
433
|
+
style: { color: useColor(values, io) },
|
|
426
434
|
format,
|
|
427
435
|
strict: flag(values, 'strict'),
|
|
428
436
|
version: await version(),
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The command line: argument parsing, dispatch, output and exit codes.\n *\n * Exit 0: done, nothing found. Exit 1: findings, collisions, or an action\n * refused. Exit 2: the run could not be trusted - a bad flag, a configuration\n * that does not load, a brief that does not exist. A run that checked nothing\n * must never exit as though it found nothing.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { parseArgs, type ParseArgsConfig } from 'node:util';\n\nimport { TransactionError, ConflictError } from './apply.js';\nimport type { Plan } from './archive.js';\nimport { CONFIG_FILES, ConfigError, initialConfig } from './config.js';\nimport { BriefEngine, EngineError, today } from './engine.js';\nimport { NodeFileSystem } from './fs.js';\nimport { failing, sortFindings, summarise } from './lint.js';\nimport { normalisePath } from './links.js';\nimport {\n briefJson,\n findingJson,\n FORMATS,\n type Format,\n githubCommands,\n gitlabCodeQuality,\n jsonDocument,\n type ListRow,\n matrixJson,\n planJson,\n prettyFindings,\n prettyList,\n prettyMatrix,\n prettyPlan,\n prettySchedule,\n sarif,\n scheduleJson,\n sectionsJson,\n type Style,\n summaryLine,\n} from './report.js';\nimport type { Finding } from './types.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\nexport interface CliIO {\n readonly stdout?: { write(text: string): unknown; readonly isTTY?: boolean };\n readonly stderr?: { write(text: string): unknown };\n readonly cwd?: string;\n readonly env?: Readonly<Record<string, string | undefined>>;\n}\n\ntype Values = Record<string, string | boolean | undefined>;\n\nconst GLOBAL: NonNullable<ParseArgsConfig['options']> = {\n root: { type: 'string' },\n config: { type: 'string' },\n 'no-config': { type: 'boolean' },\n format: { type: 'string' },\n strict: { type: 'boolean' },\n 'no-git': { type: 'boolean' },\n color: { type: 'boolean' },\n 'no-color': { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n};\n\nconst COMMANDS: Readonly<Record<string, { options: NonNullable<ParseArgsConfig['options']>; formats: readonly Format[] }>> = {\n init: { options: { briefs: { type: 'string' }, archive: { type: 'string' } }, formats: ['pretty', 'json'] },\n new: {\n options: {\n id: { type: 'string' },\n type: { type: 'string' },\n wave: { type: 'string' },\n 'depends-on': { type: 'string' },\n date: { type: 'string' },\n },\n formats: ['pretty', 'json'],\n },\n lint: { options: {}, formats: FORMATS },\n list: { options: { ready: { type: 'boolean' }, archived: { type: 'boolean' } }, formats: ['pretty', 'json'] },\n matrix: { options: { 'all-waves': { type: 'boolean' } }, formats: FORMATS },\n schedule: { options: { write: { type: 'boolean' } }, formats: FORMATS },\n archive: {\n options: {\n pr: { type: 'string' },\n commit: { type: 'string' },\n base: { type: 'string' },\n summary: { type: 'string' },\n date: { type: 'string' },\n 'allow-dirty': { type: 'boolean' },\n 'dry-run': { type: 'boolean' },\n },\n formats: ['pretty', 'json'],\n },\n unarchive: { options: { 'dry-run': { type: 'boolean' } }, formats: ['pretty', 'json'] },\n};\n\n/**\n * The formats each command writes, read from the table `main` checks a\n * `--format` against, so the help names no format a command refuses: the\n * finding formats are `lint`'s, `matrix`'s and `schedule`'s alone.\n */\nfunction formatsHelp(): string {\n const groups = new Map<string, string[]>();\n for (const [command, spec] of Object.entries(COMMANDS)) {\n const formats = spec.formats.join(', ');\n groups.set(formats, [...(groups.get(formats) ?? []), command]);\n }\n const rows = [...groups].map(([formats, commands]) => [commands.join(', '), formats] as const);\n const width = Math.max(...rows.map(([commands]) => commands.length));\n return rows.map(([commands, formats]) => ` ${commands.padEnd(width)} ${formats}`).join('\\n');\n}\n\nexport const HELP = `spec-brief - lint, schedule and archive task briefs\n\nUsage:\n spec-brief <command> [options]\n\nCommands:\n init write .spec-brief.json and the brief directories\n new <title> scaffold a brief with the next free id\n lint [brief...] check briefs against the configuration\n list briefs with their status, wave and readiness\n matrix scope collisions between briefs in the same wave\n schedule the waves the live briefs can run in, computed\n archive <brief> close a round: check it, stamp it, freeze it, move it\n unarchive <brief> reopen an archived brief\n\nOptions for every command:\n --root <dir> run from this directory instead of the current one\n --config <file> use this configuration file\n --no-config use the defaults, ignoring any configuration file\n --format <format> the output format, pretty by default; each command's are below\n --strict treat warnings as errors\n --no-git leave git out; read the files on disk instead\n --color, --no-color force colour on or off\n -h, --help show this help\n -v, --version show the version\n\nFormats:\n${formatsHelp()}\n\ninit:\n --briefs <dir> the directory of live briefs; default briefs\n --archive <dir> the directory of archived briefs; default <briefs>/archive\n\nnew:\n --id <id> use this id instead of the next free one\n --type <type> a brief type from the configuration\n --wave <n> the wave it runs in\n --depends-on <ids> comma-separated ids it runs after\n --date <YYYY-MM-DD> the date in its front matter; default today\n\nlist:\n --ready only briefs that can run now: dependencies archived, not drafts or deferred\n --archived include archived briefs\n\nmatrix:\n --all-waves compare every live brief, whatever its wave\n\nschedule:\n --write write each proposed wave into its brief's front matter\n\narchive:\n --pr <number> the pull request the round merged in\n --commit <rev> the commit the round landed as\n --base <rev> the branch it started from; the diff runs from the merge base\n --summary <text> what the round did, for the banner\n --date <YYYY-MM-DD> the archival date; default today\n --allow-dirty archive although the tree has uncommitted work\n --dry-run print the plan and change nothing\n\nunarchive:\n --dry-run print the plan and change nothing\n\nExit codes: 0 clean, 1 findings or refused, 2 the run could not be trusted.\n`;\n\ninterface Parsed {\n readonly command: string | undefined;\n readonly values: Values;\n readonly positionals: readonly string[];\n}\n\n/** Global options that take a value, so the value is not mistaken for the command. */\nconst VALUED = new Set(['--root', '--config', '--format']);\n\nfunction commandOf(argv: readonly string[]): string | undefined {\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i] as string;\n if (arg === '--') return argv[i + 1];\n if (VALUED.has(arg)) {\n i += 1;\n continue;\n }\n if (!arg.startsWith('-')) return arg;\n }\n return undefined;\n}\n\nexport function parse(argv: readonly string[]): Parsed {\n const command = commandOf(argv);\n const spec = command === undefined ? undefined : COMMANDS[command];\n if (command !== undefined && spec === undefined) {\n throw new UsageError(`\"${command}\" is not a command; run spec-brief --help`);\n }\n try {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: { ...GLOBAL, ...(spec?.options ?? {}) },\n allowPositionals: true,\n strict: true,\n });\n return { command, values: values as Values, positionals: positionals.slice(1) };\n } catch (error) {\n throw new UsageError((error as Error).message);\n }\n}\n\nfunction text(values: Values, key: string): string | undefined {\n const value = values[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction flag(values: Values, key: string): boolean {\n return values[key] === true;\n}\n\nfunction integer(values: Values, key: string, minimum: number): number | undefined {\n const raw = text(values, key);\n if (raw === undefined) return undefined;\n if (!/^\\d+$/.test(raw) || Number(raw) < minimum) throw new UsageError(`--${key} must be a whole number of at least ${minimum}, not \"${raw}\"`);\n return Number(raw);\n}\n\nasync function version(): Promise<string> {\n const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')) as { version: string };\n return pkg.version;\n}\n\ninterface Run {\n /** Today, as the environment the run was given says. */\n readonly today: string;\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n readonly style: Style;\n readonly format: Format;\n readonly strict: boolean;\n readonly version: string;\n readonly values: Values;\n readonly positionals: readonly string[];\n readonly cwd: string;\n}\n\nfunction useColor(values: Values, io: CliIO): boolean {\n if (flag(values, 'no-color')) return false;\n if (flag(values, 'color')) return true;\n const env = io.env ?? process.env;\n if ((env['NO_COLOR'] ?? '') !== '') return false;\n const force = env['FORCE_COLOR'];\n if (force !== undefined && force !== '') return force !== '0';\n return (io.stdout ?? process.stdout).isTTY === true;\n}\n\nasync function openEngine(run: Run): Promise<BriefEngine> {\n const root = text(run.values, 'root');\n const config = text(run.values, 'config');\n return BriefEngine.open({\n cwd: root === undefined ? run.cwd : root,\n ...(config === undefined ? {} : { config }),\n ...(flag(run.values, 'no-git') ? { git: null } : {}),\n noConfig: flag(run.values, 'no-config'),\n });\n}\n\nfunction emitFindings(run: Run, command: string, findings: readonly Finding[], checked: number, extra: Record<string, unknown> = {}): number {\n const failed = failing(findings, run.strict);\n switch (run.format) {\n case 'json':\n run.out(jsonDocument(command, run.version, { ok: !failed, checked, summary: summarise(findings), findings: findings.map(findingJson), ...extra }));\n break;\n case 'sarif':\n run.out(sarif(findings, run.version));\n break;\n case 'github':\n run.out(githubCommands(findings));\n break;\n case 'gitlab':\n run.out(gitlabCodeQuality(findings));\n break;\n case 'pretty':\n if (findings.length > 0) run.out(`${prettyFindings(findings, run.style)}\\n\\n`);\n run.out(`${findings.length === 0 ? `${checked} brief(s) checked, no findings` : `${summaryLine(findings)} in ${checked} brief(s)`}\\n`);\n break;\n }\n return failed ? EXIT_FAILED : EXIT_OK;\n}\n\nasync function runInit(run: Run): Promise<number> {\n const root = text(run.values, 'root') ?? run.cwd;\n const fs = new NodeFileSystem(root);\n for (const name of CONFIG_FILES) {\n if (await fs.exists(name)) throw new UsageError(`${name} already exists here; edit it instead`);\n }\n const briefs = normalisePath(text(run.values, 'briefs') ?? 'briefs');\n const archive = normalisePath(text(run.values, 'archive') ?? `${briefs}/archive`);\n if (briefs === '' || archive === '' || briefs === archive) throw new UsageError('--briefs and --archive must be two directories');\n const config = `${JSON.stringify(initialConfig(briefs, archive), null, 2)}\\n`;\n await fs.write('.spec-brief.json', config);\n const written = ['.spec-brief.json'];\n for (const dir of [briefs, archive]) {\n const keep = `${dir}/.gitkeep`;\n if ((await fs.list(dir)) === null) {\n await fs.write(keep, '');\n written.push(keep);\n }\n }\n if (run.format === 'json') run.out(jsonDocument('init', run.version, { ok: true, written }));\n else run.out(`${written.map((w) => `wrote ${w}`).join('\\n')}\\nnext: spec-brief new \"<title>\"\\n`);\n return EXIT_OK;\n}\n\nasync function runNew(run: Run): Promise<number> {\n const title = run.positionals.join(' ').trim();\n if (title === '') throw new UsageError('new needs a title: spec-brief new \"<title>\"');\n const engine = await openEngine(run);\n const dependsOn = text(run.values, 'depends-on')\n ?.split(',')\n .map((d) => d.trim())\n .filter((d) => d !== '');\n const created = await engine.create({\n title,\n id: text(run.values, 'id'),\n type: text(run.values, 'type'),\n wave: integer(run.values, 'wave', 0),\n dependsOn,\n date: text(run.values, 'date') ?? run.today,\n });\n if (run.format === 'json') run.out(jsonDocument('new', run.version, { ok: true, file: created.file }));\n else run.out(`wrote ${created.file}\\n`);\n return EXIT_OK;\n}\n\nasync function runLint(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const findings = await engine.lint(run.positionals);\n const checked = run.positionals.length === 0 ? engine.corpus.briefs.length : run.positionals.length;\n return emitFindings(run, 'lint', findings, checked, { sections: sectionsJson(engine.config) });\n}\n\nasync function runList(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const briefs = flag(run.values, 'archived') ? engine.corpus.briefs : engine.corpus.live;\n const ready = new Set(engine.ready());\n const rows: ListRow[] = briefs\n .map((brief) => ({ brief, ready: ready.has(brief), waitingOn: engine.waitingOn(brief) }))\n .filter((row) => !flag(run.values, 'ready') || row.ready);\n if (run.format === 'json') {\n run.out(\n jsonDocument('list', run.version, {\n ok: true,\n briefs: rows.map((r) => briefJson(r.brief, { ready: r.ready, waitingOn: r.waitingOn })),\n sections: sectionsJson(engine.config),\n }),\n );\n } else {\n run.out(`${prettyList(rows, run.style)}\\n`);\n }\n return EXIT_OK;\n}\n\nasync function runMatrix(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const { report, findings } = await engine.collisions({ all: flag(run.values, 'all-waves') });\n const sorted = sortFindings(findings);\n if (run.format === 'pretty') {\n run.out(`${prettyMatrix(report, run.style)}\\n`);\n return failing(sorted, run.strict) ? EXIT_FAILED : EXIT_OK;\n }\n return emitFindings(run, 'matrix', sorted, engine.corpus.live.length, matrixJson(report));\n}\n\n/**\n * The waves, computed. The view shows the moves, their reasons and the\n * cycles; the other findings - briefs that run alone, pairs the search could\n * not decide, a front matter that cannot take a wave - print beneath it.\n */\nasync function runSchedule(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const { schedule: computed, findings } = await engine.schedule();\n let written: string[] | null = null;\n let reported: Finding[] = findings;\n if (flag(run.values, 'write')) {\n const result = await engine.writeWaves(computed);\n written = result.written;\n // Written, the waves hold, and the findings that said they did not are answered.\n reported = [...(written.length > 0 ? findings.filter((f) => f.rule !== 'wave-schedule') : findings), ...result.refused];\n }\n const sorted = sortFindings(reported);\n if (run.format === 'pretty') {\n run.out(`${prettySchedule(computed, written, run.style)}\\n`);\n const rest = sorted.filter((f) => f.rule !== 'wave-schedule' && f.rule !== 'dependency-cycle');\n if (rest.length > 0) run.out(`\\n${prettyFindings(rest, run.style)}\\n`);\n return failing(sorted, run.strict) ? EXIT_FAILED : EXIT_OK;\n }\n return emitFindings(run, 'schedule', sorted, engine.corpus.live.length, { ...scheduleJson(computed), written });\n}\n\nasync function runTransition(run: Run, action: 'archive' | 'unarchive'): Promise<number> {\n const reference = run.positionals[0];\n if (reference === undefined || run.positionals.length > 1) throw new UsageError(`${action} takes one brief: spec-brief ${action} <brief>`);\n const engine = await openEngine(run);\n const dryRun = flag(run.values, 'dry-run');\n const plan: Plan =\n action === 'archive'\n ? await engine.planArchive(reference, {\n pr: integer(run.values, 'pr', 1),\n commit: text(run.values, 'commit'),\n base: text(run.values, 'base'),\n summary: text(run.values, 'summary'),\n date: text(run.values, 'date') ?? run.today,\n allowDirty: flag(run.values, 'allow-dirty'),\n strict: run.strict,\n noGit: flag(run.values, 'no-git'),\n })\n : engine.planUnarchive(reference, { strict: run.strict });\n const refused = plan.blocking.length > 0;\n if (!refused && !dryRun) await engine.apply(plan);\n if (run.format === 'json') run.out(jsonDocument(action, run.version, { ok: !refused, dryRun, plan: planJson(plan) }));\n else run.out(`${prettyPlan(plan, dryRun, run.style)}\\n`);\n return refused ? EXIT_FAILED : EXIT_OK;\n}\n\nexport async function main(argv: readonly string[] = process.argv.slice(2), io: CliIO = {}): Promise<number> {\n const stdout = io.stdout ?? process.stdout;\n const stderr = io.stderr ?? process.stderr;\n const err = (message: string): void => {\n stderr.write(`spec-brief: ${message}\\n`);\n };\n let parsed: Parsed;\n try {\n parsed = parse(argv);\n } catch (error) {\n err((error as Error).message);\n return EXIT_ERROR;\n }\n const { command, values, positionals } = parsed;\n if (flag(values, 'version')) {\n stdout.write(`${await version()}\\n`);\n return EXIT_OK;\n }\n if (flag(values, 'help') || command === undefined) {\n stdout.write(HELP);\n return command === undefined && !flag(values, 'help') ? EXIT_ERROR : EXIT_OK;\n }\n const spec = COMMANDS[command] as (typeof COMMANDS)[string];\n const format = (text(values, 'format') ?? 'pretty') as Format;\n if (!spec.formats.includes(format)) {\n err(`--format for ${command} is one of ${spec.formats.join(', ')}, not \"${format}\"`);\n return EXIT_ERROR;\n }\n const run: Run = {\n today: today(io.env ?? process.env),\n out: (t) => {\n stdout.write(t);\n },\n err,\n style: { color: format === 'pretty' && useColor(values, io) },\n format,\n strict: flag(values, 'strict'),\n version: await version(),\n values,\n positionals,\n cwd: io.cwd ?? process.cwd(),\n };\n try {\n switch (command) {\n case 'init':\n return await runInit(run);\n case 'new':\n return await runNew(run);\n case 'lint':\n return await runLint(run);\n case 'list':\n return await runList(run);\n case 'matrix':\n return await runMatrix(run);\n case 'schedule':\n return await runSchedule(run);\n case 'archive':\n return await runTransition(run, 'archive');\n default:\n return await runTransition(run, 'unarchive');\n }\n } catch (error) {\n const expected = [ConfigError, UsageError, EngineError, ConflictError, TransactionError];\n if (expected.some((kind) => error instanceof kind)) {\n err((error as Error).message);\n return EXIT_ERROR;\n }\n err(`unexpected error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n return EXIT_ERROR;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The command line: argument parsing, dispatch, output and exit codes.\n *\n * Exit 0: done, nothing found. Exit 1: findings, collisions, or an action\n * refused. Exit 2: the run could not be trusted - a bad flag, a configuration\n * that does not load, a brief that does not exist. A run that checked nothing\n * must never exit as though it found nothing.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { parseArgs, type ParseArgsConfig } from 'node:util';\n\nimport { TransactionError, ConflictError } from './apply.js';\nimport type { Plan } from './archive.js';\nimport { reported } from './collisions.js';\nimport { CONFIG_FILES, ConfigError, initialConfig } from './config.js';\nimport { BriefEngine, EngineError, today } from './engine.js';\nimport { NodeFileSystem } from './fs.js';\nimport { failing, sortFindings, summarise } from './lint.js';\nimport { normalisePath } from './links.js';\nimport {\n briefJson,\n findingJson,\n FORMATS,\n type Format,\n githubCommands,\n gitlabCodeQuality,\n jsonDocument,\n type ListRow,\n matrixJson,\n planJson,\n prettyFindings,\n prettyList,\n prettyMatrix,\n prettyPlan,\n prettySchedule,\n sarif,\n scheduleJson,\n sectionsJson,\n type Style,\n summaryLine,\n} from './report.js';\nimport type { Finding } from './types.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\nexport interface CliIO {\n readonly stdout?: { write(text: string): unknown; readonly isTTY?: boolean };\n readonly stderr?: { write(text: string): unknown };\n readonly cwd?: string;\n readonly env?: Readonly<Record<string, string | undefined>>;\n}\n\ntype Values = Record<string, string | boolean | undefined>;\n\nconst GLOBAL: NonNullable<ParseArgsConfig['options']> = {\n root: { type: 'string' },\n config: { type: 'string' },\n 'no-config': { type: 'boolean' },\n format: { type: 'string' },\n strict: { type: 'boolean' },\n 'no-git': { type: 'boolean' },\n color: { type: 'boolean' },\n 'no-color': { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n};\n\ninterface CommandSpec {\n readonly options: NonNullable<ParseArgsConfig['options']>;\n readonly formats: readonly Format[];\n}\n\nconst TABLE: Readonly<Record<string, CommandSpec>> = {\n init: { options: { briefs: { type: 'string' }, archive: { type: 'string' } }, formats: ['pretty', 'json'] },\n new: {\n options: {\n id: { type: 'string' },\n type: { type: 'string' },\n wave: { type: 'string' },\n 'depends-on': { type: 'string' },\n date: { type: 'string' },\n },\n formats: ['pretty', 'json'],\n },\n lint: { options: {}, formats: FORMATS },\n list: { options: { ready: { type: 'boolean' }, archived: { type: 'boolean' } }, formats: ['pretty', 'json'] },\n matrix: { options: { 'all-waves': { type: 'boolean' } }, formats: FORMATS },\n schedule: { options: { write: { type: 'boolean' } }, formats: FORMATS },\n archive: {\n options: {\n pr: { type: 'string' },\n commit: { type: 'string' },\n base: { type: 'string' },\n summary: { type: 'string' },\n date: { type: 'string' },\n 'allow-dirty': { type: 'boolean' },\n 'dry-run': { type: 'boolean' },\n },\n formats: ['pretty', 'json'],\n },\n unarchive: { options: { 'dry-run': { type: 'boolean' } }, formats: ['pretty', 'json'] },\n};\n\n/**\n * The commands by name, and nothing for a command line that names none. A map,\n * because an object answers to more names than it was given: `constructor` and\n * `toString` are no commands.\n */\nconst COMMANDS: ReadonlyMap<string | undefined, CommandSpec> = new Map(Object.entries(TABLE));\n\n/**\n * The formats each command writes, read from the table `main` checks a\n * `--format` against, so the help names no format a command refuses: the\n * finding formats are `lint`'s, `matrix`'s and `schedule`'s alone.\n */\nfunction formatsHelp(): string {\n const groups = new Map<string, string[]>();\n for (const [command, spec] of Object.entries(TABLE)) {\n const formats = spec.formats.join(', ');\n groups.set(formats, [...(groups.get(formats) ?? []), command]);\n }\n const rows = [...groups].map(([formats, commands]) => [commands.join(', '), formats] as const);\n const width = Math.max(...rows.map(([commands]) => commands.length));\n return rows.map(([commands, formats]) => ` ${commands.padEnd(width)} ${formats}`).join('\\n');\n}\n\nexport const HELP = `spec-brief - lint, schedule and archive task briefs\n\nUsage:\n spec-brief <command> [options]\n\nCommands:\n init write .spec-brief.json and the brief directories\n new <title> scaffold a brief with the next free id\n lint [brief...] check briefs against the configuration\n list briefs with their status, wave and readiness\n matrix scope collisions between briefs in the same wave\n schedule the waves the live briefs can run in, computed\n archive <brief> close a round: check it, stamp it, freeze it, move it\n unarchive <brief> reopen an archived brief\n\nOptions for every command:\n --root <dir> run from this directory instead of the current one\n --config <file> use this configuration file\n --no-config use the defaults, ignoring any configuration file\n --format <format> the output format, pretty by default; each command's are below\n --strict treat warnings as errors\n --no-git leave git out; read the files on disk instead\n --color, --no-color force colour on or off\n -h, --help show this help\n -v, --version show the version\n\nFormats:\n${formatsHelp()}\n\ninit:\n --briefs <dir> the directory of live briefs; default briefs\n --archive <dir> the directory of archived briefs; default <briefs>/archive\n\nnew:\n --id <id> use this id instead of the next free one\n --type <type> a brief type from the configuration\n --wave <n> the wave it runs in\n --depends-on <ids> comma-separated ids it runs after\n --date <YYYY-MM-DD> the date in its front matter; default today\n\nlist:\n --ready only briefs that can run now: dependencies archived, not drafts or deferred\n --archived include archived briefs\n\nmatrix:\n --all-waves compare every live brief, whatever its wave\n\nschedule:\n --write write each proposed wave into its brief's front matter\n\narchive:\n --pr <number> the pull request the round merged in\n --commit <rev> the commit the round landed as\n --base <rev> the branch it started from; the diff runs from the merge base\n --summary <text> what the round did, for the banner\n --date <YYYY-MM-DD> the archival date; default today\n --allow-dirty archive although the tree has uncommitted work\n --dry-run print the plan and change nothing\n\nunarchive:\n --dry-run print the plan and change nothing\n\nExit codes: 0 clean, 1 findings or refused, 2 the run could not be trusted.\n`;\n\ninterface Parsed {\n readonly command: string | undefined;\n readonly values: Values;\n readonly positionals: readonly string[];\n}\n\n/** Global options that take a value, so the value is not mistaken for the command. */\nconst VALUED = new Set(['--root', '--config', '--format']);\n\nfunction commandOf(argv: readonly string[]): string | undefined {\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i] as string;\n if (arg === '--') return argv[i + 1];\n if (VALUED.has(arg)) {\n i += 1;\n continue;\n }\n if (!arg.startsWith('-')) return arg;\n }\n return undefined;\n}\n\nexport function parse(argv: readonly string[]): Parsed {\n const command = commandOf(argv);\n const spec = COMMANDS.get(command);\n if (command !== undefined && spec === undefined) {\n throw new UsageError(`\"${command}\" is not a command; run spec-brief --help`);\n }\n try {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: { ...GLOBAL, ...(spec?.options ?? {}) },\n allowPositionals: true,\n strict: true,\n });\n return { command, values: values as Values, positionals: positionals.slice(1) };\n } catch (error) {\n throw new UsageError((error as Error).message);\n }\n}\n\n/** The value of an option declared a string, which is all `parseArgs` gives one. */\nfunction text(values: Values, key: string): string | undefined {\n return values[key] as string | undefined;\n}\n\nfunction flag(values: Values, key: string): boolean {\n return values[key] === true;\n}\n\nfunction integer(values: Values, key: string, minimum: number): number | undefined {\n const raw = text(values, key);\n if (raw === undefined) return undefined;\n if (!/^\\d+$/.test(raw) || Number(raw) < minimum) throw new UsageError(`--${key} must be a whole number of at least ${minimum}, not \"${raw}\"`);\n return Number(raw);\n}\n\nasync function version(): Promise<string> {\n const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')) as { version: string };\n return pkg.version;\n}\n\ninterface Run {\n /** Today, as the environment the run was given says. */\n readonly today: string;\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n readonly style: Style;\n readonly format: Format;\n readonly strict: boolean;\n readonly version: string;\n readonly values: Values;\n readonly positionals: readonly string[];\n readonly cwd: string;\n}\n\nfunction useColor(values: Values, io: CliIO): boolean {\n if (flag(values, 'no-color')) return false;\n if (flag(values, 'color')) return true;\n const env = io.env ?? process.env;\n if ((env['NO_COLOR'] ?? '') !== '') return false;\n const force = env['FORCE_COLOR'];\n if (force !== undefined && force !== '') return force !== '0';\n return (io.stdout ?? process.stdout).isTTY === true;\n}\n\nasync function openEngine(run: Run): Promise<BriefEngine> {\n return BriefEngine.open({\n cwd: text(run.values, 'root') ?? run.cwd,\n config: text(run.values, 'config'),\n ...(flag(run.values, 'no-git') ? { git: null } : {}),\n noConfig: flag(run.values, 'no-config'),\n });\n}\n\nfunction emitFindings(run: Run, command: string, findings: readonly Finding[], checked: number, extra: Record<string, unknown> = {}): number {\n const failed = failing(findings, run.strict);\n switch (run.format) {\n case 'json':\n run.out(jsonDocument(command, run.version, { ok: !failed, checked, summary: summarise(findings), findings: findings.map(findingJson), ...extra }));\n break;\n case 'sarif':\n run.out(sarif(findings, run.version));\n break;\n case 'github':\n run.out(githubCommands(findings));\n break;\n case 'gitlab':\n run.out(gitlabCodeQuality(findings));\n break;\n case 'pretty':\n if (findings.length > 0) run.out(`${prettyFindings(findings, run.style)}\\n\\n`);\n run.out(`${findings.length === 0 ? `${checked} brief(s) checked, no findings` : `${summaryLine(findings)} in ${checked} brief(s)`}\\n`);\n break;\n }\n return failed ? EXIT_FAILED : EXIT_OK;\n}\n\nasync function runInit(run: Run): Promise<number> {\n const root = text(run.values, 'root') ?? run.cwd;\n const fs = new NodeFileSystem(root);\n for (const name of CONFIG_FILES) {\n if (await fs.exists(name)) throw new UsageError(`${name} already exists here; edit it instead`);\n }\n const briefs = normalisePath(text(run.values, 'briefs') ?? 'briefs');\n const archive = normalisePath(text(run.values, 'archive') ?? `${briefs}/archive`);\n if (briefs === '' || archive === '' || briefs === archive) throw new UsageError('--briefs and --archive must be two directories');\n const config = `${JSON.stringify(initialConfig(briefs, archive), null, 2)}\\n`;\n await fs.write('.spec-brief.json', config);\n const written = ['.spec-brief.json'];\n for (const dir of [briefs, archive]) {\n const keep = `${dir}/.gitkeep`;\n if ((await fs.list(dir)) === null) {\n await fs.write(keep, '');\n written.push(keep);\n }\n }\n if (run.format === 'json') run.out(jsonDocument('init', run.version, { ok: true, written }));\n else run.out(`${written.map((w) => `wrote ${w}`).join('\\n')}\\nnext: spec-brief new \"<title>\"\\n`);\n return EXIT_OK;\n}\n\nasync function runNew(run: Run): Promise<number> {\n const title = run.positionals.join(' ').trim();\n if (title === '') throw new UsageError('new needs a title: spec-brief new \"<title>\"');\n const engine = await openEngine(run);\n const dependsOn = text(run.values, 'depends-on')\n ?.split(',')\n .map((d) => d.trim())\n .filter((d) => d !== '');\n const created = await engine.create({\n title,\n id: text(run.values, 'id'),\n type: text(run.values, 'type'),\n wave: integer(run.values, 'wave', 0),\n dependsOn,\n date: text(run.values, 'date') ?? run.today,\n });\n if (run.format === 'json') run.out(jsonDocument('new', run.version, { ok: true, file: created.file }));\n else run.out(`wrote ${created.file}\\n`);\n return EXIT_OK;\n}\n\nasync function runLint(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const findings = await engine.lint(run.positionals);\n const checked = run.positionals.length === 0 ? engine.corpus.briefs.length : run.positionals.length;\n return emitFindings(run, 'lint', findings, checked, { sections: sectionsJson(engine.config) });\n}\n\nasync function runList(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const briefs = flag(run.values, 'archived') ? engine.corpus.briefs : engine.corpus.live;\n const ready = new Set(engine.ready());\n const rows: ListRow[] = briefs\n .map((brief) => ({ brief, ready: ready.has(brief), waitingOn: engine.waitingOn(brief) }))\n .filter((row) => !flag(run.values, 'ready') || row.ready);\n if (run.format === 'json') {\n run.out(\n jsonDocument('list', run.version, {\n ok: true,\n briefs: rows.map((r) => briefJson(r.brief, { ready: r.ready, waitingOn: r.waitingOn })),\n sections: sectionsJson(engine.config),\n }),\n );\n } else {\n run.out(`${prettyList(rows, run.style)}\\n`);\n }\n return EXIT_OK;\n}\n\nasync function runMatrix(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const { report, findings } = await engine.collisions({ all: flag(run.values, 'all-waves') });\n const sorted = sortFindings(findings);\n if (run.format === 'pretty') {\n // Drawn from what the run reports, so a rule that is off marks nothing in the grid.\n run.out(`${prettyMatrix(reported(engine.corpus, report), run.style)}\\n`);\n return failing(sorted, run.strict) ? EXIT_FAILED : EXIT_OK;\n }\n return emitFindings(run, 'matrix', sorted, engine.corpus.live.length, matrixJson(report));\n}\n\n/**\n * The waves, computed. The view shows the moves, their reasons and the\n * cycles; the other findings - briefs that run alone, pairs the search could\n * not decide, a front matter that cannot take a wave - print beneath it.\n */\nasync function runSchedule(run: Run): Promise<number> {\n const engine = await openEngine(run);\n engine.requireBriefs();\n const { schedule: computed, findings } = await engine.schedule();\n let written: string[] | null = null;\n let reported: Finding[] = findings;\n if (flag(run.values, 'write')) {\n const result = await engine.writeWaves(computed);\n written = result.written;\n // Written, the waves hold, and the findings that said they did not are answered.\n reported = [...(written.length > 0 ? findings.filter((f) => f.rule !== 'wave-schedule') : findings), ...result.refused];\n }\n const sorted = sortFindings(reported);\n if (run.format === 'pretty') {\n run.out(`${prettySchedule(computed, written, run.style)}\\n`);\n const rest = sorted.filter((f) => f.rule !== 'wave-schedule' && f.rule !== 'dependency-cycle');\n if (rest.length > 0) run.out(`\\n${prettyFindings(rest, run.style)}\\n`);\n return failing(sorted, run.strict) ? EXIT_FAILED : EXIT_OK;\n }\n return emitFindings(run, 'schedule', sorted, engine.corpus.live.length, { ...scheduleJson(computed), written });\n}\n\nasync function runTransition(run: Run, action: 'archive' | 'unarchive'): Promise<number> {\n const reference = run.positionals[0];\n if (reference === undefined || run.positionals.length > 1) throw new UsageError(`${action} takes one brief: spec-brief ${action} <brief>`);\n const engine = await openEngine(run);\n const dryRun = flag(run.values, 'dry-run');\n const plan: Plan =\n action === 'archive'\n ? await engine.planArchive(reference, {\n pr: integer(run.values, 'pr', 1),\n commit: text(run.values, 'commit'),\n base: text(run.values, 'base'),\n summary: text(run.values, 'summary'),\n date: text(run.values, 'date') ?? run.today,\n allowDirty: flag(run.values, 'allow-dirty'),\n strict: run.strict,\n noGit: flag(run.values, 'no-git'),\n })\n : engine.planUnarchive(reference, { strict: run.strict });\n const refused = plan.blocking.length > 0;\n if (!refused && !dryRun) await engine.apply(plan);\n if (run.format === 'json') run.out(jsonDocument(action, run.version, { ok: !refused, dryRun, plan: planJson(plan) }));\n else run.out(`${prettyPlan(plan, dryRun, run.style)}\\n`);\n return refused ? EXIT_FAILED : EXIT_OK;\n}\n\nexport async function main(argv: readonly string[] = process.argv.slice(2), io: CliIO = {}): Promise<number> {\n const stdout = io.stdout ?? process.stdout;\n const stderr = io.stderr ?? process.stderr;\n const err = (message: string): void => {\n stderr.write(`spec-brief: ${message}\\n`);\n };\n let parsed: Parsed;\n try {\n parsed = parse(argv);\n } catch (error) {\n err((error as Error).message);\n return EXIT_ERROR;\n }\n const { command, values, positionals } = parsed;\n if (flag(values, 'version')) {\n stdout.write(`${await version()}\\n`);\n return EXIT_OK;\n }\n if (flag(values, 'help') || command === undefined) {\n stdout.write(HELP);\n // Asked for, the help is the answer; printed because no command was named, it is the error.\n return flag(values, 'help') ? EXIT_OK : EXIT_ERROR;\n }\n const spec = COMMANDS.get(command) as CommandSpec;\n const format = (text(values, 'format') ?? 'pretty') as Format;\n if (!spec.formats.includes(format)) {\n err(`--format for ${command} is one of ${spec.formats.join(', ')}, not \"${format}\"`);\n return EXIT_ERROR;\n }\n const run: Run = {\n today: today(io.env ?? process.env),\n out: (t) => {\n stdout.write(t);\n },\n err,\n // Read by the pretty format alone: the others have nothing to colour.\n style: { color: useColor(values, io) },\n format,\n strict: flag(values, 'strict'),\n version: await version(),\n values,\n positionals,\n cwd: io.cwd ?? process.cwd(),\n };\n try {\n switch (command) {\n case 'init':\n return await runInit(run);\n case 'new':\n return await runNew(run);\n case 'lint':\n return await runLint(run);\n case 'list':\n return await runList(run);\n case 'matrix':\n return await runMatrix(run);\n case 'schedule':\n return await runSchedule(run);\n case 'archive':\n return await runTransition(run, 'archive');\n default:\n return await runTransition(run, 'unarchive');\n }\n } catch (error) {\n const expected = [ConfigError, UsageError, EngineError, ConflictError, TransactionError];\n if (expected.some((kind) => error instanceof kind)) {\n err((error as Error).message);\n return EXIT_ERROR;\n }\n err(`unexpected error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n return EXIT_ERROR;\n }\n}\n"]}
|
package/dist/collisions.d.ts
CHANGED
|
@@ -72,6 +72,15 @@ export interface CollisionOptions {
|
|
|
72
72
|
export declare function collisions(corpus: Corpus, options?: CollisionOptions): CollisionReport;
|
|
73
73
|
/** A pair whose collision the search could not decide, as one finding on the later brief. */
|
|
74
74
|
export declare function undecidedFinding(severity: Severity, a: Brief, b: Brief, patterns: readonly (readonly [string, string])[], wave: number | null): Finding;
|
|
75
|
+
/**
|
|
76
|
+
* The report as the run tells it: what a rule that is off would have marked
|
|
77
|
+
* is left out. The findings are made from this and the matrix a terminal
|
|
78
|
+
* shows is drawn from it, so the two agree by construction: every mark in the
|
|
79
|
+
* grid, and every line beneath it, is a finding of the run. A rule that is
|
|
80
|
+
* off marks nothing, and `shared-directory` is off unless a repository turns
|
|
81
|
+
* it on (ADR-0005).
|
|
82
|
+
*/
|
|
83
|
+
export declare function reported(corpus: Corpus, report: CollisionReport): CollisionReport;
|
|
75
84
|
/**
|
|
76
85
|
* The report as findings. One pair of briefs is one finding, placed on the
|
|
77
86
|
* later brief of the pair, which is usually the one still being written.
|
package/dist/collisions.js
CHANGED
|
@@ -26,6 +26,7 @@ function matrix(wave, briefs, scopes, budget, files) {
|
|
|
26
26
|
const undecided = [];
|
|
27
27
|
const shared = [];
|
|
28
28
|
const scoped = briefs.map((brief) => scopes.get(brief));
|
|
29
|
+
// One past the end there is no later brief to pair with, so the bound could be one further.
|
|
29
30
|
for (let i = 0; i < scoped.length; i += 1) {
|
|
30
31
|
for (let j = i + 1; j < scoped.length; j += 1) {
|
|
31
32
|
const left = scoped[i];
|
|
@@ -98,54 +99,63 @@ export function undecidedFinding(severity, a, b, patterns, wave) {
|
|
|
98
99
|
subject: label(a),
|
|
99
100
|
};
|
|
100
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* The report as the run tells it: what a rule that is off would have marked
|
|
104
|
+
* is left out. The findings are made from this and the matrix a terminal
|
|
105
|
+
* shows is drawn from it, so the two agree by construction: every mark in the
|
|
106
|
+
* grid, and every line beneath it, is a finding of the run. A rule that is
|
|
107
|
+
* off marks nothing, and `shared-directory` is off unless a repository turns
|
|
108
|
+
* it on (ADR-0005).
|
|
109
|
+
*/
|
|
110
|
+
export function reported(corpus, report) {
|
|
111
|
+
const on = (rule) => configuredSeverity(corpus, rule) !== null;
|
|
112
|
+
const waves = report.waves.map((wave) => ({
|
|
113
|
+
...wave,
|
|
114
|
+
collisions: on('collision') ? wave.collisions : [],
|
|
115
|
+
undecided: on('collision-undecided') ? wave.undecided : [],
|
|
116
|
+
shared: on('shared-directory') ? wave.shared : [],
|
|
117
|
+
unscoped: on('unscoped') ? wave.unscoped : [],
|
|
118
|
+
}));
|
|
119
|
+
return { ...report, waves };
|
|
120
|
+
}
|
|
101
121
|
/**
|
|
102
122
|
* The report as findings. One pair of briefs is one finding, placed on the
|
|
103
123
|
* later brief of the pair, which is usually the one still being written.
|
|
104
124
|
*/
|
|
105
125
|
export function collisionFindings(corpus, report) {
|
|
106
126
|
const findings = [];
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// The pair is the problem, whichever patterns meet and whatever file is named.
|
|
123
|
-
subject: label(c.a),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (undecided !== null) {
|
|
128
|
-
for (const u of wave.undecided)
|
|
129
|
-
findings.push(undecidedFinding(undecided, u.a, u.b, u.patterns, wave.wave));
|
|
130
|
-
}
|
|
131
|
-
if (sharedDirectory !== null) {
|
|
132
|
-
for (const s of wave.shared) {
|
|
133
|
-
findings.push({
|
|
134
|
-
rule: 'shared-directory',
|
|
135
|
-
severity: sharedDirectory,
|
|
136
|
-
message: `writes into ${inWords(s.directories.map((d) => `${d}/`))}, as ${label(s.a)} does ${where(wave.wave)}`,
|
|
137
|
-
file: s.b.file,
|
|
138
|
-
line: lineOfField(s.b, 'affectedFiles') + 1,
|
|
139
|
-
brief: s.b.id ?? undefined,
|
|
140
|
-
hint: 'check that the two do not depend on one decision in that directory; if they do, order them',
|
|
141
|
-
subject: label(s.a),
|
|
142
|
-
});
|
|
143
|
-
}
|
|
127
|
+
// Asked only of a rule with something left in the report told, which is a rule that is on.
|
|
128
|
+
const severity = (rule) => configuredSeverity(corpus, rule);
|
|
129
|
+
for (const wave of reported(corpus, report).waves) {
|
|
130
|
+
for (const c of wave.collisions) {
|
|
131
|
+
findings.push({
|
|
132
|
+
rule: 'collision',
|
|
133
|
+
severity: severity('collision'),
|
|
134
|
+
message: collisionMessage(c, wave.wave),
|
|
135
|
+
file: c.b.file,
|
|
136
|
+
line: lineOfField(c.b, 'affectedFiles') + 1,
|
|
137
|
+
brief: c.b.id ?? undefined,
|
|
138
|
+
hint: `run them in different waves, make one depend on the other, or narrow a scope`,
|
|
139
|
+
// The pair is the problem, whichever patterns meet and whatever file is named.
|
|
140
|
+
subject: label(c.a),
|
|
141
|
+
});
|
|
144
142
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
for (const u of wave.undecided)
|
|
144
|
+
findings.push(undecidedFinding(severity('collision-undecided'), u.a, u.b, u.patterns, wave.wave));
|
|
145
|
+
for (const s of wave.shared) {
|
|
146
|
+
findings.push({
|
|
147
|
+
rule: 'shared-directory',
|
|
148
|
+
severity: severity('shared-directory'),
|
|
149
|
+
message: `writes into ${inWords(s.directories.map((d) => `${d}/`))}, as ${label(s.a)} does ${where(wave.wave)}`,
|
|
150
|
+
file: s.b.file,
|
|
151
|
+
line: lineOfField(s.b, 'affectedFiles') + 1,
|
|
152
|
+
brief: s.b.id ?? undefined,
|
|
153
|
+
hint: 'check that the two do not depend on one decision in that directory; if they do, order them',
|
|
154
|
+
subject: label(s.a),
|
|
155
|
+
});
|
|
148
156
|
}
|
|
157
|
+
for (const brief of wave.unscoped)
|
|
158
|
+
findings.push(unscopedFinding(severity('unscoped'), brief, `the ${wave.briefs.length - 1} other brief(s) ${where(wave.wave)}`));
|
|
149
159
|
}
|
|
150
160
|
return findings;
|
|
151
161
|
}
|
package/dist/collisions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collisions.js","sourceRoot":"","sources":["../src/collisions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAe,iBAAiB,EAAgB,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,IAAI,EAA4B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4DpC,SAAS,MAAM,CAAC,IAAmB,EAAE,MAAwB,EAAE,MAAiC,EAAE,MAAc,EAAE,KAA+B;IAC/I,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAoB,EAAE,CAAC;IACtC,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAU,CAAC,CAAC;IACjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAU,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAU,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YACjD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC/E,SAAS;YACX,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC/E,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAClG,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACzH,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5G,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAO,GAAqB,EAAE;IACvE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC5H,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,MAAM,WAAW,GAAY,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;YAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC5C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAY,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACvI,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,KAAK,CAAC,KAAY;IACzB,OAAO,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC;AAChC,CAAC;AAED,SAAS,KAAK,CAAC,IAAmB;IAChC,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAY,EAAE,IAAmB;IACzD,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAY,CAAC;QACtC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,gBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;IAChI,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7H,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,uBAAuB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACpH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,gBAAgB,CAAC,QAAkB,EAAE,CAAQ,EAAE,CAAQ,EAAE,QAAgD,EAAE,IAAmB;IAC5I,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,QAAQ;QACR,OAAO,EAAE,+BAA+B,KAAK,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,IAAI,CAAC,gDAAgD,OAAO,CAAC,KAAK,CAAC,EAAE;QACtI,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC;QACzC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,SAAS;QACxB,IAAI,EAAE,+DAA+D;QACrE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,MAAuB;IACvE,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACvE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,WAAW;oBACjB,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;oBACvC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;oBACd,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC;oBAC3C,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,SAAS;oBAC1B,IAAI,EAAE,8EAA8E;oBACpF,+EAA+E;oBAC/E,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS;gBAAE,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9G,CAAC;QACD,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,kBAAkB;oBACxB,QAAQ,EAAE,eAAe;oBACzB,OAAO,EAAE,eAAe,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAC/G,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;oBACd,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC;oBAC3C,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,SAAS;oBAC1B,IAAI,EAAE,4FAA4F;oBAClG,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ;gBAAE,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,mBAAmB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzJ,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,QAAkB,EAAE,KAAY,EAAE,MAAc;IAC9E,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ;QACR,OAAO,EAAE,8DAA8D,MAAM,EAAE;QAC/E,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,SAAS;QAC5B,IAAI,EAAE,iEAAiE;KACxE,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The collision matrix: which briefs scheduled to run side by side declare\n * scopes that can name the same file.\n *\n * Scopes are compared as globs, not as strings. `src/auth/**` and\n * `src/**\\/session.ts` share no prefix a string comparison would see and both\n * cover `src/auth/session.ts`; the witness search finds that, and names the\n * file. What a brief protects it will not write, so a file either brief of a\n * pair protects is no collision. A brief that declares no scope cannot be\n * proven apart from anything, so it is reported as unscoped rather than\n * silently counted as safe, and a pair the search could not decide is reported\n * as undecided rather than as either.\n *\n * Two briefs that collide are one defect however many of their patterns\n * meet: they are fixed together, by a wave, a dependency or a narrower scope.\n * So a pair is one collision, carrying every pair of patterns that meets.\n */\n\nimport type { Brief } from './brief.js';\nimport { lineOfField } from './brief.js';\nimport { type Corpus, waitingOnDeferred, type Waiting } from './corpus.js';\nimport { globBases, readingIn, WITNESS_BUDGET } from './glob.js';\nimport { configuredSeverity } from './lint.js';\nimport { meet, type Overlap, type Scope, scopeOf, witnessText } from './scope.js';\nimport { inWords } from './text.js';\nimport type { Finding, Severity } from './types.js';\n\nexport type { Overlap } from './scope.js';\n\nexport interface Collision {\n readonly a: Brief;\n readonly b: Brief;\n /** Every pair of patterns that meets, `a`'s first; never empty. */\n readonly overlaps: readonly Overlap[];\n}\n\n/** Two briefs no pair of whose patterns is known to meet, and some pair the search could not decide. */\nexport interface UndecidedPair {\n readonly a: Brief;\n readonly b: Brief;\n /** The pairs of patterns, `a`'s first; never empty. */\n readonly patterns: readonly (readonly [string, string])[];\n}\n\nexport interface SharedDirectory {\n readonly a: Brief;\n readonly b: Brief;\n /** The directories both write into, sorted; never empty. */\n readonly directories: readonly string[];\n}\n\nexport interface WaveMatrix {\n /** `null` when every live brief is compared regardless of wave. */\n readonly wave: number | null;\n readonly briefs: readonly Brief[];\n readonly collisions: readonly Collision[];\n readonly undecided: readonly UndecidedPair[];\n readonly shared: readonly SharedDirectory[];\n readonly unscoped: readonly Brief[];\n}\n\nexport interface CollisionReport {\n readonly waves: readonly WaveMatrix[];\n /** Live briefs with no wave, which the matrix does not place. */\n readonly unscheduled: readonly Brief[];\n /** Deferred briefs, which wait for their trigger and run in no wave. */\n readonly deferred: readonly Brief[];\n /**\n * Briefs that depend on a deferred brief, or on one that waits on one. They\n * cannot start before the deferred work, so they run in no wave either, and\n * are compared with nothing - as `schedule` places them nowhere. By id.\n */\n readonly waiting: readonly Waiting[];\n}\n\nexport interface CollisionOptions {\n /** Compare every live brief with every other, whatever its wave. */\n readonly all?: boolean;\n /** The files the tree holds, which say whether a literal path is a file or a directory. */\n readonly repoFiles?: readonly string[] | null;\n /** How many states one witness search may visit before it answers `undecided`. */\n readonly budget?: number;\n}\n\nfunction matrix(wave: number | null, briefs: readonly Brief[], scopes: ReadonlyMap<Brief, Scope>, budget: number, files: readonly string[] | null): WaveMatrix {\n const collisions: Collision[] = [];\n const undecided: UndecidedPair[] = [];\n const shared: SharedDirectory[] = [];\n const scoped = briefs.map((brief) => scopes.get(brief) as Scope);\n for (let i = 0; i < scoped.length; i += 1) {\n for (let j = i + 1; j < scoped.length; j += 1) {\n const left = scoped[i] as Scope;\n const right = scoped[j] as Scope;\n const meeting = meet(left, right, budget, files);\n if (meeting.overlaps.length > 0) {\n collisions.push({ a: left.brief, b: right.brief, overlaps: meeting.overlaps });\n continue;\n }\n if (meeting.undecided.length > 0) {\n undecided.push({ a: left.brief, b: right.brief, patterns: meeting.undecided });\n continue;\n }\n const leftDirs = new Set(left.affected.flatMap((s) => globBases(s.glob)).filter((d) => d !== ''));\n const directories = [...new Set(right.affected.flatMap((s) => globBases(s.glob)))].filter((d) => leftDirs.has(d)).sort();\n if (directories.length > 0) shared.push({ a: left.brief, b: right.brief, directories });\n }\n }\n const unscoped = briefs.length > 1 ? scoped.filter((s) => s.affected.length === 0).map((s) => s.brief) : [];\n return { wave, briefs, collisions, undecided, shared, unscoped };\n}\n\nexport function collisions(corpus: Corpus, options: CollisionOptions = {}): CollisionReport {\n const files = options.repoFiles ?? null;\n const reading = readingIn(files);\n const budget = options.budget ?? WITNESS_BUDGET;\n const deferred = corpus.live.filter((b) => b.status === 'deferred');\n const waiting = waitingOnDeferred(corpus);\n const held = new Set(waiting.map((w) => w.brief));\n const live = corpus.live.filter((b) => b.status !== 'deferred' && !held.has(b));\n const scopes = new Map(live.map((brief) => [brief, scopeOf(brief, reading)]));\n if (options.all === true) return { waves: [matrix(null, live, scopes, budget, files)], unscheduled: [], deferred, waiting };\n const byWave = new Map<number, Brief[]>();\n const unscheduled: Brief[] = [];\n for (const brief of live) {\n if (brief.wave === null) unscheduled.push(brief);\n else byWave.set(brief.wave, [...(byWave.get(brief.wave) ?? []), brief]);\n }\n const waves = [...byWave.keys()].sort((a, b) => a - b).map((wave) => matrix(wave, byWave.get(wave) as Brief[], scopes, budget, files));\n return { waves, unscheduled, deferred, waiting };\n}\n\nfunction label(brief: Brief): string {\n return brief.id ?? brief.name;\n}\n\nfunction where(wave: number | null): string {\n return wave === null ? 'among the live briefs' : `in wave ${wave}`;\n}\n\nfunction collisionMessage(c: Collision, wave: number | null): string {\n if (c.overlaps.length === 1) {\n const only = c.overlaps[0] as Overlap;\n return `\"${only.patterns[1]}\" overlaps ${label(c.a)}'s \"${only.patterns[0]}\" ${where(wave)}; both cover ${witnessText(only)}`;\n }\n const each = c.overlaps.map((o) => `\"${o.patterns[1]}\" and ${label(c.a)}'s \"${o.patterns[0]}\" both cover ${witnessText(o)}`);\n return `overlaps ${label(c.a)} ${where(wave)} through ${c.overlaps.length} pairs of patterns: ${each.join('; ')}`;\n}\n\n/** A pair whose collision the search could not decide, as one finding on the later brief. */\nexport function undecidedFinding(severity: Severity, a: Brief, b: Brief, patterns: readonly (readonly [string, string])[], wave: number | null): Finding {\n const pairs = patterns.map(([x, y]) => `\"${y}\" and ${label(a)}'s \"${x}\"`);\n return {\n rule: 'collision-undecided',\n severity,\n message: `whether it can write a file ${label(a)} writes ${where(wave)} is undecided: the search met its budget for ${inWords(pairs)}`,\n file: b.file,\n line: lineOfField(b, 'affectedFiles') + 1,\n brief: b.id ?? undefined,\n hint: 'narrow one of the patterns, or run the two in different waves',\n subject: label(a),\n };\n}\n\n/**\n * The report as findings. One pair of briefs is one finding, placed on the\n * later brief of the pair, which is usually the one still being written.\n */\nexport function collisionFindings(corpus: Corpus, report: CollisionReport): Finding[] {\n const findings: Finding[] = [];\n const collision = configuredSeverity(corpus, 'collision');\n const undecided = configuredSeverity(corpus, 'collision-undecided');\n const unscoped = configuredSeverity(corpus, 'unscoped');\n const sharedDirectory = configuredSeverity(corpus, 'shared-directory');\n for (const wave of report.waves) {\n if (collision !== null) {\n for (const c of wave.collisions) {\n findings.push({\n rule: 'collision',\n severity: collision,\n message: collisionMessage(c, wave.wave),\n file: c.b.file,\n line: lineOfField(c.b, 'affectedFiles') + 1,\n brief: c.b.id ?? undefined,\n hint: `run them in different waves, make one depend on the other, or narrow a scope`,\n // The pair is the problem, whichever patterns meet and whatever file is named.\n subject: label(c.a),\n });\n }\n }\n if (undecided !== null) {\n for (const u of wave.undecided) findings.push(undecidedFinding(undecided, u.a, u.b, u.patterns, wave.wave));\n }\n if (sharedDirectory !== null) {\n for (const s of wave.shared) {\n findings.push({\n rule: 'shared-directory',\n severity: sharedDirectory,\n message: `writes into ${inWords(s.directories.map((d) => `${d}/`))}, as ${label(s.a)} does ${where(wave.wave)}`,\n file: s.b.file,\n line: lineOfField(s.b, 'affectedFiles') + 1,\n brief: s.b.id ?? undefined,\n hint: 'check that the two do not depend on one decision in that directory; if they do, order them',\n subject: label(s.a),\n });\n }\n }\n if (unscoped !== null) {\n for (const brief of wave.unscoped) findings.push(unscopedFinding(unscoped, brief, `the ${wave.briefs.length - 1} other brief(s) ${where(wave.wave)}`));\n }\n }\n return findings;\n}\n\n/** A brief with no scope, which nothing can be proved apart from. */\nexport function unscopedFinding(severity: Severity, brief: Brief, others: string): Finding {\n return {\n rule: 'unscoped',\n severity,\n message: `declares no affectedFiles, so it cannot be checked against ${others}`,\n file: brief.file,\n line: 1,\n brief: brief.id ?? undefined,\n hint: 'list the files or globs this round writes under \"affectedFiles\"',\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"collisions.js","sourceRoot":"","sources":["../src/collisions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The collision matrix: which briefs scheduled to run side by side declare\n * scopes that can name the same file.\n *\n * Scopes are compared as globs, not as strings. `src/auth/**` and\n * `src/**\\/session.ts` share no prefix a string comparison would see and both\n * cover `src/auth/session.ts`; the witness search finds that, and names the\n * file. What a brief protects it will not write, so a file either brief of a\n * pair protects is no collision. A brief that declares no scope cannot be\n * proven apart from anything, so it is reported as unscoped rather than\n * silently counted as safe, and a pair the search could not decide is reported\n * as undecided rather than as either.\n *\n * Two briefs that collide are one defect however many of their patterns\n * meet: they are fixed together, by a wave, a dependency or a narrower scope.\n * So a pair is one collision, carrying every pair of patterns that meets.\n */\n\nimport type { Brief } from './brief.js';\nimport { lineOfField } from './brief.js';\nimport { type Corpus, waitingOnDeferred, type Waiting } from './corpus.js';\nimport { globBases, readingIn, WITNESS_BUDGET } from './glob.js';\nimport { configuredSeverity } from './lint.js';\nimport { meet, type Overlap, type Scope, scopeOf, witnessText } from './scope.js';\nimport { inWords } from './text.js';\nimport type { Finding, Severity } from './types.js';\n\nexport type { Overlap } from './scope.js';\n\nexport interface Collision {\n readonly a: Brief;\n readonly b: Brief;\n /** Every pair of patterns that meets, `a`'s first; never empty. */\n readonly overlaps: readonly Overlap[];\n}\n\n/** Two briefs no pair of whose patterns is known to meet, and some pair the search could not decide. */\nexport interface UndecidedPair {\n readonly a: Brief;\n readonly b: Brief;\n /** The pairs of patterns, `a`'s first; never empty. */\n readonly patterns: readonly (readonly [string, string])[];\n}\n\nexport interface SharedDirectory {\n readonly a: Brief;\n readonly b: Brief;\n /** The directories both write into, sorted; never empty. */\n readonly directories: readonly string[];\n}\n\nexport interface WaveMatrix {\n /** `null` when every live brief is compared regardless of wave. */\n readonly wave: number | null;\n readonly briefs: readonly Brief[];\n readonly collisions: readonly Collision[];\n readonly undecided: readonly UndecidedPair[];\n readonly shared: readonly SharedDirectory[];\n readonly unscoped: readonly Brief[];\n}\n\nexport interface CollisionReport {\n readonly waves: readonly WaveMatrix[];\n /** Live briefs with no wave, which the matrix does not place. */\n readonly unscheduled: readonly Brief[];\n /** Deferred briefs, which wait for their trigger and run in no wave. */\n readonly deferred: readonly Brief[];\n /**\n * Briefs that depend on a deferred brief, or on one that waits on one. They\n * cannot start before the deferred work, so they run in no wave either, and\n * are compared with nothing - as `schedule` places them nowhere. By id.\n */\n readonly waiting: readonly Waiting[];\n}\n\nexport interface CollisionOptions {\n /** Compare every live brief with every other, whatever its wave. */\n readonly all?: boolean;\n /** The files the tree holds, which say whether a literal path is a file or a directory. */\n readonly repoFiles?: readonly string[] | null;\n /** How many states one witness search may visit before it answers `undecided`. */\n readonly budget?: number;\n}\n\nfunction matrix(wave: number | null, briefs: readonly Brief[], scopes: ReadonlyMap<Brief, Scope>, budget: number, files: readonly string[] | null): WaveMatrix {\n const collisions: Collision[] = [];\n const undecided: UndecidedPair[] = [];\n const shared: SharedDirectory[] = [];\n const scoped = briefs.map((brief) => scopes.get(brief) as Scope);\n // One past the end there is no later brief to pair with, so the bound could be one further.\n for (let i = 0; i < scoped.length; i += 1) {\n for (let j = i + 1; j < scoped.length; j += 1) {\n const left = scoped[i] as Scope;\n const right = scoped[j] as Scope;\n const meeting = meet(left, right, budget, files);\n if (meeting.overlaps.length > 0) {\n collisions.push({ a: left.brief, b: right.brief, overlaps: meeting.overlaps });\n continue;\n }\n if (meeting.undecided.length > 0) {\n undecided.push({ a: left.brief, b: right.brief, patterns: meeting.undecided });\n continue;\n }\n const leftDirs = new Set(left.affected.flatMap((s) => globBases(s.glob)).filter((d) => d !== ''));\n const directories = [...new Set(right.affected.flatMap((s) => globBases(s.glob)))].filter((d) => leftDirs.has(d)).sort();\n if (directories.length > 0) shared.push({ a: left.brief, b: right.brief, directories });\n }\n }\n const unscoped = briefs.length > 1 ? scoped.filter((s) => s.affected.length === 0).map((s) => s.brief) : [];\n return { wave, briefs, collisions, undecided, shared, unscoped };\n}\n\nexport function collisions(corpus: Corpus, options: CollisionOptions = {}): CollisionReport {\n const files = options.repoFiles ?? null;\n const reading = readingIn(files);\n const budget = options.budget ?? WITNESS_BUDGET;\n const deferred = corpus.live.filter((b) => b.status === 'deferred');\n const waiting = waitingOnDeferred(corpus);\n const held = new Set(waiting.map((w) => w.brief));\n const live = corpus.live.filter((b) => b.status !== 'deferred' && !held.has(b));\n const scopes = new Map(live.map((brief) => [brief, scopeOf(brief, reading)]));\n if (options.all === true) return { waves: [matrix(null, live, scopes, budget, files)], unscheduled: [], deferred, waiting };\n const byWave = new Map<number, Brief[]>();\n const unscheduled: Brief[] = [];\n for (const brief of live) {\n if (brief.wave === null) unscheduled.push(brief);\n else byWave.set(brief.wave, [...(byWave.get(brief.wave) ?? []), brief]);\n }\n const waves = [...byWave.keys()].sort((a, b) => a - b).map((wave) => matrix(wave, byWave.get(wave) as Brief[], scopes, budget, files));\n return { waves, unscheduled, deferred, waiting };\n}\n\nfunction label(brief: Brief): string {\n return brief.id ?? brief.name;\n}\n\nfunction where(wave: number | null): string {\n return wave === null ? 'among the live briefs' : `in wave ${wave}`;\n}\n\nfunction collisionMessage(c: Collision, wave: number | null): string {\n if (c.overlaps.length === 1) {\n const only = c.overlaps[0] as Overlap;\n return `\"${only.patterns[1]}\" overlaps ${label(c.a)}'s \"${only.patterns[0]}\" ${where(wave)}; both cover ${witnessText(only)}`;\n }\n const each = c.overlaps.map((o) => `\"${o.patterns[1]}\" and ${label(c.a)}'s \"${o.patterns[0]}\" both cover ${witnessText(o)}`);\n return `overlaps ${label(c.a)} ${where(wave)} through ${c.overlaps.length} pairs of patterns: ${each.join('; ')}`;\n}\n\n/** A pair whose collision the search could not decide, as one finding on the later brief. */\nexport function undecidedFinding(severity: Severity, a: Brief, b: Brief, patterns: readonly (readonly [string, string])[], wave: number | null): Finding {\n const pairs = patterns.map(([x, y]) => `\"${y}\" and ${label(a)}'s \"${x}\"`);\n return {\n rule: 'collision-undecided',\n severity,\n message: `whether it can write a file ${label(a)} writes ${where(wave)} is undecided: the search met its budget for ${inWords(pairs)}`,\n file: b.file,\n line: lineOfField(b, 'affectedFiles') + 1,\n brief: b.id ?? undefined,\n hint: 'narrow one of the patterns, or run the two in different waves',\n subject: label(a),\n };\n}\n\n/**\n * The report as the run tells it: what a rule that is off would have marked\n * is left out. The findings are made from this and the matrix a terminal\n * shows is drawn from it, so the two agree by construction: every mark in the\n * grid, and every line beneath it, is a finding of the run. A rule that is\n * off marks nothing, and `shared-directory` is off unless a repository turns\n * it on (ADR-0005).\n */\nexport function reported(corpus: Corpus, report: CollisionReport): CollisionReport {\n const on = (rule: string): boolean => configuredSeverity(corpus, rule) !== null;\n const waves = report.waves.map((wave) => ({\n ...wave,\n collisions: on('collision') ? wave.collisions : [],\n undecided: on('collision-undecided') ? wave.undecided : [],\n shared: on('shared-directory') ? wave.shared : [],\n unscoped: on('unscoped') ? wave.unscoped : [],\n }));\n return { ...report, waves };\n}\n\n/**\n * The report as findings. One pair of briefs is one finding, placed on the\n * later brief of the pair, which is usually the one still being written.\n */\nexport function collisionFindings(corpus: Corpus, report: CollisionReport): Finding[] {\n const findings: Finding[] = [];\n // Asked only of a rule with something left in the report told, which is a rule that is on.\n const severity = (rule: string): Severity => configuredSeverity(corpus, rule) as Severity;\n for (const wave of reported(corpus, report).waves) {\n for (const c of wave.collisions) {\n findings.push({\n rule: 'collision',\n severity: severity('collision'),\n message: collisionMessage(c, wave.wave),\n file: c.b.file,\n line: lineOfField(c.b, 'affectedFiles') + 1,\n brief: c.b.id ?? undefined,\n hint: `run them in different waves, make one depend on the other, or narrow a scope`,\n // The pair is the problem, whichever patterns meet and whatever file is named.\n subject: label(c.a),\n });\n }\n for (const u of wave.undecided) findings.push(undecidedFinding(severity('collision-undecided'), u.a, u.b, u.patterns, wave.wave));\n for (const s of wave.shared) {\n findings.push({\n rule: 'shared-directory',\n severity: severity('shared-directory'),\n message: `writes into ${inWords(s.directories.map((d) => `${d}/`))}, as ${label(s.a)} does ${where(wave.wave)}`,\n file: s.b.file,\n line: lineOfField(s.b, 'affectedFiles') + 1,\n brief: s.b.id ?? undefined,\n hint: 'check that the two do not depend on one decision in that directory; if they do, order them',\n subject: label(s.a),\n });\n }\n for (const brief of wave.unscoped) findings.push(unscopedFinding(severity('unscoped'), brief, `the ${wave.briefs.length - 1} other brief(s) ${where(wave.wave)}`));\n }\n return findings;\n}\n\n/** A brief with no scope, which nothing can be proved apart from. */\nexport function unscopedFinding(severity: Severity, brief: Brief, others: string): Finding {\n return {\n rule: 'unscoped',\n severity,\n message: `declares no affectedFiles, so it cannot be checked against ${others}`,\n file: brief.file,\n line: 1,\n brief: brief.id ?? undefined,\n hint: 'list the files or globs this round writes under \"affectedFiles\"',\n };\n}\n"]}
|
package/dist/config.d.ts
CHANGED
|
@@ -97,6 +97,12 @@ export declare class ConfigError extends Error {
|
|
|
97
97
|
* names its own sections means those sections and not the defaults as well.
|
|
98
98
|
*/
|
|
99
99
|
export declare function resolveConfig(raw: unknown, file?: string): Config;
|
|
100
|
+
/**
|
|
101
|
+
* The sections a brief type adds, or `undefined` for a type the configuration
|
|
102
|
+
* does not define. Its own keys only: every object answers to `constructor`
|
|
103
|
+
* and `toString`, and a brief may say either.
|
|
104
|
+
*/
|
|
105
|
+
export declare function typeSections(config: Config, type: string): readonly SectionRule[] | undefined;
|
|
100
106
|
/** The status words a repository uses, in lifecycle order. */
|
|
101
107
|
export declare function statusWords(config: Config): string[];
|
|
102
108
|
/** Parses the text of a configuration file. */
|
package/dist/config.js
CHANGED
|
@@ -269,6 +269,14 @@ export function resolveConfig(raw, file = 'configuration') {
|
|
|
269
269
|
throw new ConfigError(file, semantic);
|
|
270
270
|
return config;
|
|
271
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* The sections a brief type adds, or `undefined` for a type the configuration
|
|
274
|
+
* does not define. Its own keys only: every object answers to `constructor`
|
|
275
|
+
* and `toString`, and a brief may say either.
|
|
276
|
+
*/
|
|
277
|
+
export function typeSections(config, type) {
|
|
278
|
+
return Object.hasOwn(config.types, type) ? config.types[type] : undefined;
|
|
279
|
+
}
|
|
272
280
|
/** The status words a repository uses, in lifecycle order. */
|
|
273
281
|
export function statusWords(config) {
|
|
274
282
|
const { draft, active, deferred, archived } = config.status;
|
|
@@ -341,7 +349,9 @@ export function parseConfig(text, file) {
|
|
|
341
349
|
*/
|
|
342
350
|
export async function locateConfig(start, exists) {
|
|
343
351
|
let directory = start;
|
|
344
|
-
//
|
|
352
|
+
// Each pass moves one directory up, and no path is 256 deep: the bound is
|
|
353
|
+
// never met, however it is counted. It is there so that the loop ends by
|
|
354
|
+
// construction.
|
|
345
355
|
for (let depth = 0; depth < 256; depth += 1) {
|
|
346
356
|
const found = [];
|
|
347
357
|
for (const name of CONFIG_FILES) {
|
|
@@ -355,6 +365,7 @@ export async function locateConfig(start, exists) {
|
|
|
355
365
|
if (found[0] !== undefined)
|
|
356
366
|
return found[0];
|
|
357
367
|
const parent = dirname(directory);
|
|
368
|
+
// The root is its own parent. Left to the bound, every pass after this one would ask the root again and hear the same.
|
|
358
369
|
if (parent === directory)
|
|
359
370
|
return undefined;
|
|
360
371
|
directory = parent;
|
|
@@ -374,19 +385,19 @@ export function configJsonSchema() {
|
|
|
374
385
|
/** The configuration `init` writes: every default spelled out, so it can be edited rather than looked up. */
|
|
375
386
|
export function initialConfig(briefs, archive) {
|
|
376
387
|
const d = DEFAULT_CONFIG;
|
|
388
|
+
// What the default sections say, and no more: each has a hint, and none
|
|
389
|
+
// requires text. A default that comes to say more fails the test that this
|
|
390
|
+
// file means the defaults.
|
|
377
391
|
const sectionJson = (s) => {
|
|
378
392
|
const out = { name: s.name };
|
|
379
393
|
if (s.aliases.length > 0)
|
|
380
394
|
out['aliases'] = s.aliases;
|
|
381
|
-
if (s.mustContain.length > 0)
|
|
382
|
-
out['mustContain'] = s.mustContain;
|
|
383
395
|
if (s.checklist)
|
|
384
396
|
out['checklist'] = true;
|
|
385
397
|
if (s.optional)
|
|
386
398
|
out['optional'] = true;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return Object.keys(out).length === 1 ? s.name : out;
|
|
399
|
+
out['hint'] = s.hint;
|
|
400
|
+
return out;
|
|
390
401
|
};
|
|
391
402
|
return {
|
|
392
403
|
$schema: SCHEMA_PATH,
|