@hawkeyexl/manni 3.0.0 → 4.0.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/dist/cli.js CHANGED
@@ -7,40 +7,55 @@ import {
7
7
  readStdin,
8
8
  reportConfig,
9
9
  splitList
10
- } from "./chunk-G7INFKNJ.js";
10
+ } from "./chunk-J3KIKNWZ.js";
11
11
  import {
12
12
  CiteError,
13
+ INFER_FORMATS,
13
14
  KeyError,
15
+ LINT_JOBS,
16
+ LintError,
14
17
  RULE_ID_PREFIX,
15
18
  SEVERITIES,
16
19
  SEVERITY_LIST,
20
+ TOOLS_BY_JOB,
17
21
  asCiteError,
18
22
  codeEndAt,
19
23
  codeRegions,
20
24
  errorSite,
21
25
  findingMessage,
22
26
  isCiteRule,
27
+ isInferFormat,
23
28
  isSeverity,
29
+ loadConfig,
24
30
  meetsSeverity,
31
+ rebaseConfig,
32
+ render,
25
33
  renderCheckGithub,
26
34
  renderCheckJson,
27
35
  renderCheckPretty,
28
36
  renderRemoveJson,
29
37
  renderRemovePretty,
38
+ renderTemplates,
39
+ renderTools,
30
40
  renderUpdateJson,
31
41
  renderUpdatePretty,
42
+ resolveStructureTool,
32
43
  runAdd,
33
44
  runCheck,
34
45
  runKeyRotate,
35
46
  runKeySet,
47
+ runLint,
36
48
  runRemove,
49
+ runTemplates,
50
+ runTemplatesInfer,
51
+ runTools,
37
52
  runUpdate,
38
53
  shortCommit,
39
54
  shortLine,
40
55
  shortPin,
41
56
  shortSrc,
42
57
  spellSource
43
- } from "./chunk-W75SLU7Z.js";
58
+ } from "./chunk-YDUFTHBZ.js";
44
59
  import {
45
60
  COMMON_FORMAT_LIST,
46
61
  CONCEPT_FIELDS,
@@ -85,10 +100,10 @@ import {
85
100
  terminalConfirm,
86
101
  valeConfigPath,
87
102
  valueAt
88
- } from "./chunk-I6PYC7EP.js";
103
+ } from "./chunk-O7RMPQZ4.js";
89
104
 
90
105
  // src/cli.ts
91
- import { Command as Command5 } from "commander";
106
+ import { Command as Command6 } from "commander";
92
107
 
93
108
  // src/a11y/cli.ts
94
109
  import { Command } from "commander";
@@ -843,7 +858,7 @@ var A11Y_FORMAT_LIST = A11Y_FORMATS.join(" | ");
843
858
  function isA11yFormat(value) {
844
859
  return A11Y_FORMATS.includes(value);
845
860
  }
846
- function render(format, run, opts) {
861
+ function render2(format, run, opts) {
847
862
  switch (format) {
848
863
  case "pretty":
849
864
  return renderPretty(run, opts);
@@ -1094,7 +1109,7 @@ function buildProgram2() {
1094
1109
  { analyzer: createPlaywrightAnalyzer(), onProgress: progress.listener }
1095
1110
  );
1096
1111
  const color = resolveColor(parent, process.stdout);
1097
- const text2 = render(format, run, { color, quiet: Boolean(options.quiet) });
1112
+ const text2 = render2(format, run, { color, quiet: Boolean(options.quiet) });
1098
1113
  if (text2.length > 0 || format !== "github") {
1099
1114
  process.stdout.write(`${text2}
1100
1115
  `);
@@ -1750,8 +1765,286 @@ function buildProgram4() {
1750
1765
  return program;
1751
1766
  }
1752
1767
 
1753
- // src/term/cli.ts
1768
+ // src/lint/cli.ts
1754
1769
  import { Command as Command4 } from "commander";
1770
+ var LIST_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
1771
+ var TOOL_OPTIONS_GROUP = "Tool options:";
1772
+ function resolveColor2(program) {
1773
+ const noColor = program.opts().color === false;
1774
+ return shouldColor({ noColor, isTTY: process.stdout.isTTY });
1775
+ }
1776
+ function reportFormat(value) {
1777
+ const format = String(value);
1778
+ if (!isReportFormat(format)) {
1779
+ throw new LintError(
1780
+ `Unknown --format "${format}". Use ${REPORT_FORMAT_LIST}.`
1781
+ );
1782
+ }
1783
+ return format;
1784
+ }
1785
+ function omittedWhenClean(format) {
1786
+ return format !== "explain" && OMITTED_WHEN_CLEAN.has(format);
1787
+ }
1788
+ function listFormat(value) {
1789
+ const format = String(value);
1790
+ if (!LIST_FORMATS.has(format)) {
1791
+ throw new LintError(
1792
+ `Unknown --format "${format}". Use pretty or json.`
1793
+ );
1794
+ }
1795
+ return format;
1796
+ }
1797
+ function inferFormat(value) {
1798
+ const format = String(value);
1799
+ if (!isInferFormat(format)) {
1800
+ throw new LintError(`Unknown --format "${format}". Use yaml or json.`);
1801
+ }
1802
+ return format;
1803
+ }
1804
+ function assertStructureTool(value) {
1805
+ if (value === void 0) return;
1806
+ resolveStructureTool(value);
1807
+ }
1808
+ function withConfigOptions(command) {
1809
+ return command.option("-c, --config <path>", "path to a manni config file").option("--no-config", "ignore any discovered config file");
1810
+ }
1811
+ function optionValue(command, key) {
1812
+ for (let cmd = command; cmd !== null; cmd = cmd.parent ?? null) {
1813
+ if (cmd.getOptionValueSource(key) === "cli") return cmd.getOptionValue(key);
1814
+ }
1815
+ return command.getOptionValue(key);
1816
+ }
1817
+ function withInputOptions(command) {
1818
+ command.argument(
1819
+ "[paths...]",
1820
+ "files, directories, or globs to lint (use - for stdin)"
1821
+ ).option(
1822
+ "--collection <name>",
1823
+ "configured collection to run over; repeatable",
1824
+ collect,
1825
+ []
1826
+ ).option("--ext <list>", "comma-separated extensions for directory walks").option("--exclude <glob>", "glob to exclude; repeatable", collect, []).option("--as <format>", "force an input format (e.g. markdown, mdx)").option(
1827
+ "-f, --format <format>",
1828
+ `output: ${REPORT_FORMATS.join(" | ")}`,
1829
+ "pretty"
1830
+ );
1831
+ return withConfigOptions(command).option("--allow-empty", "treat zero matched files as success").option("--no-gitignore", "lint files .gitignore covers");
1832
+ }
1833
+ function inputOptions(paths, options) {
1834
+ return {
1835
+ inputs: paths,
1836
+ collection: options.collection,
1837
+ ...options.ext ? { exts: splitList(options.ext) } : {},
1838
+ exclude: options.exclude,
1839
+ ...options.as === void 0 ? {} : { as: options.as },
1840
+ ...configOption(options.config),
1841
+ // `undefined` rather than `false` when absent, so config still wins.
1842
+ ...options.allowEmpty ? { allowEmpty: true } : {},
1843
+ respectGitignore: explicitFalse(options.gitignore),
1844
+ onNotice: notice
1845
+ };
1846
+ }
1847
+ function buildProgram5() {
1848
+ const program = new Command4();
1849
+ program.name("lint").description(
1850
+ "Validate document structure against doctype templates. Deterministic, CI-friendly."
1851
+ ).version(package_default.version, "-V, --version").option("--no-color", "disable colored output").showHelpAfterError().exitOverride();
1852
+ const lint = async (paths, options, command, job = {}) => {
1853
+ const tool = job.tool;
1854
+ const format = reportFormat(options.format);
1855
+ const explain = tool?.explain === true;
1856
+ const stdinContent = paths.includes(STDIN_TOKEN) ? await readStdin() : void 0;
1857
+ const cwd = process.cwd();
1858
+ const run = await runLint({
1859
+ ...inputOptions(paths, options),
1860
+ ...tool?.tool === void 0 ? {} : { tool: tool.tool },
1861
+ ...tool?.template === void 0 ? {} : { template: tool.template },
1862
+ ...tool && tool.templates.length > 0 ? { templates: tool.templates } : {},
1863
+ ...explain ? { explain: true } : {},
1864
+ ...stdinContent === void 0 ? {} : { stdinContent },
1865
+ // Which config governed the run, said where a report cannot be parsed
1866
+ // around: discovery walks up to the project boundary, so an unexpected
1867
+ // ancestor config is the difference between a five-minute diagnosis and
1868
+ // an hour of confusion.
1869
+ onConfigLoaded: reportConfig(format === "pretty", cwd)
1870
+ });
1871
+ const color = resolveColor2(command.parent ?? command);
1872
+ const chosen = explain ? "explain" : format;
1873
+ const text2 = render(run, chosen, { color });
1874
+ if (text2.length > 0 || !omittedWhenClean(chosen)) {
1875
+ process.stdout.write(`${text2}
1876
+ `);
1877
+ }
1878
+ process.exitCode = explain ? 0 : run.summary.failed > 0 ? 1 : 0;
1879
+ };
1880
+ withInputOptions(
1881
+ program.command("check").description("Run every configured lint job over the given files/dirs/globs")
1882
+ ).addHelpText(
1883
+ "after",
1884
+ [
1885
+ "",
1886
+ "Jobs: structure.",
1887
+ "",
1888
+ "Examples:",
1889
+ " manni lint check docs/ # every configured job",
1890
+ " manni lint check # targets from collections:",
1891
+ " manni lint check --collection guides",
1892
+ ' manni lint check "**/*.md" -f github # CI annotations',
1893
+ " cat page.md | manni lint check - --as markdown"
1894
+ ].join("\n")
1895
+ ).action(async (paths, options, command) => {
1896
+ try {
1897
+ if (options.format === "pretty") {
1898
+ process.stderr.write(`Checked: ${LINT_JOBS.join(", ")}.
1899
+ `);
1900
+ }
1901
+ await lint(paths, options, command);
1902
+ } catch (err) {
1903
+ fail(err);
1904
+ }
1905
+ });
1906
+ const structure = withInputOptions(
1907
+ program.command("structure").description(
1908
+ "Lint document structure against doctype templates, routing each page by its `type`"
1909
+ )
1910
+ );
1911
+ structure.optionsGroup(TOOL_OPTIONS_GROUP).option(
1912
+ "--tool <name>",
1913
+ `tool that performs the job: ${TOOLS_BY_JOB.structure.join(", ")}`
1914
+ ).option(
1915
+ "-t, --template <ref>",
1916
+ "apply this template to every file, overriding type routing"
1917
+ ).option(
1918
+ "--templates <path>",
1919
+ "template file to route by: its `types:` win over built-ins; repeatable",
1920
+ collect,
1921
+ []
1922
+ ).option(
1923
+ "--explain",
1924
+ "print how each file's template was chosen, and lint nothing (always exits 0, unrouted pages included)"
1925
+ ).addHelpText(
1926
+ "after",
1927
+ [
1928
+ "",
1929
+ "Examples:",
1930
+ " manni lint structure docs/ # route each page by its `type`",
1931
+ " manni lint structure docs/ --templates ./templates.yaml # add your own templates",
1932
+ " manni lint structure # targets from collections:",
1933
+ " manni lint structure docs/ --explain # show why each page routed where",
1934
+ " manni lint structure page.md -t tgdp:how-to:1.6 # force one template",
1935
+ ' manni lint structure "**/*.md" -f github # CI annotations',
1936
+ " manni lint structure docs/ --exclude '**/drafts/**'",
1937
+ " cat page.md | manni lint structure - -t tgdp:how-to:1.6 --as markdown"
1938
+ ].join("\n")
1939
+ ).action(
1940
+ async (paths, options, command) => {
1941
+ try {
1942
+ assertStructureTool(options.tool);
1943
+ await lint(paths, options, command, { tool: options });
1944
+ } catch (err) {
1945
+ fail(err);
1946
+ }
1947
+ }
1948
+ );
1949
+ const templates = program.command("templates").description("List the templates that can be applied, and the types they serve").option(
1950
+ "--templates <path>",
1951
+ "also list the templates in this file; repeatable",
1952
+ collect,
1953
+ []
1954
+ );
1955
+ withConfigOptions(templates).option("-f, --format <format>", "output: pretty | json", "pretty").action(async (options, command) => {
1956
+ try {
1957
+ const format = listFormat(options.format);
1958
+ const { configPath, noConfig } = configOption(options.config);
1959
+ const found = noConfig ? null : await loadConfig(configPath);
1960
+ const info = await runTemplates({
1961
+ templates: options.templates.length > 0 ? options.templates : rebaseConfig(found).templates
1962
+ });
1963
+ const color = resolveColor2(command.parent ?? command);
1964
+ process.stdout.write(`${renderTemplates(info, format, { color })}
1965
+ `);
1966
+ } catch (err) {
1967
+ fail(err);
1968
+ }
1969
+ });
1970
+ const inferCommand = templates.command("infer").description("Write a first template from a page that already has the shape you want").argument("<page>", "one file to infer from (use - for stdin)").option("--as <format>", "force an input format (e.g. markdown, mdx)").option(
1971
+ "--name <name>",
1972
+ "name for the template; defaults to the page's `type`, else its filename"
1973
+ ).option("-f, --format <format>", `output: ${INFER_FORMATS.join(" | ")}`, "yaml").option("-o, --out <path>", "write the template here instead of to stdout").option("--force", "overwrite the --out path");
1974
+ withConfigOptions(inferCommand).addHelpText(
1975
+ "after",
1976
+ [
1977
+ "",
1978
+ "Examples:",
1979
+ " manni lint templates infer docs/guides/install.md",
1980
+ " manni lint templates infer docs/guides/install.md --name how-to",
1981
+ " manni lint templates infer page.mdx -f json",
1982
+ " manni lint templates infer page.md -o ./templates.yaml",
1983
+ " manni lint templates infer page.md -o ./templates.yaml --force",
1984
+ " cat page.md | manni lint templates infer - --as markdown",
1985
+ "",
1986
+ "One page, and every rule it writes is exactly one occurrence: a single",
1987
+ "page cannot show what varies. Loosening a heading into a list or a",
1988
+ "pattern, setting `min: 0`, and adding a trailing rule with no heading",
1989
+ "for the sections that may follow are your edits to make."
1990
+ ].join("\n")
1991
+ ).action(
1992
+ async (page, options, command) => {
1993
+ try {
1994
+ const format = inferFormat(optionValue(command, "format"));
1995
+ const { configPath, noConfig } = configOption(
1996
+ optionValue(command, "config")
1997
+ );
1998
+ const stdinContent = page === STDIN_TOKEN ? await readStdin() : void 0;
1999
+ const result = await runTemplatesInfer({
2000
+ page,
2001
+ format,
2002
+ ...options.as === void 0 ? {} : { as: options.as },
2003
+ ...options.name === void 0 ? {} : { name: options.name },
2004
+ ...options.out === void 0 ? {} : { out: options.out },
2005
+ ...options.force === void 0 ? {} : { force: options.force },
2006
+ ...configPath === void 0 ? {} : { configPath },
2007
+ ...noConfig === void 0 ? {} : { noConfig },
2008
+ ...stdinContent === void 0 ? {} : { stdinContent },
2009
+ // The template is the primary output, so with no --out it owns
2010
+ // stdout and the config line goes to stderr beside it. With --out
2011
+ // stdout is free, and the line still belongs on stderr: a reader
2012
+ // piping nothing is not a reason to move a diagnostic.
2013
+ onConfigLoaded: reportConfig(false, process.cwd())
2014
+ });
2015
+ if (result.written === void 0) {
2016
+ process.stdout.write(result.text);
2017
+ return;
2018
+ }
2019
+ process.stderr.write(
2020
+ `Wrote template "${result.name}" to ${result.written}
2021
+ `
2022
+ );
2023
+ } catch (err) {
2024
+ fail(err);
2025
+ }
2026
+ }
2027
+ );
2028
+ withConfigOptions(
2029
+ program.command("tools").description(
2030
+ "List the lint jobs, the tool that performs each, and what it reads"
2031
+ )
2032
+ ).option("-f, --format <format>", "output: pretty | json", "pretty").action(async (options, command) => {
2033
+ try {
2034
+ const format = listFormat(options.format);
2035
+ const tools = await runTools(configOption(options.config));
2036
+ const color = resolveColor2(command.parent ?? command);
2037
+ process.stdout.write(`${renderTools(tools, format, { color })}
2038
+ `);
2039
+ } catch (err) {
2040
+ fail(err);
2041
+ }
2042
+ });
2043
+ return program;
2044
+ }
2045
+
2046
+ // src/term/cli.ts
2047
+ import { Command as Command5 } from "commander";
1755
2048
 
1756
2049
  // src/term/commands/check.ts
1757
2050
  import { existsSync as existsSync2 } from "node:fs";
@@ -4702,9 +4995,9 @@ var INTERCHANGE_LABELS = {
4702
4995
  };
4703
4996
  function rendered(writers, format, construct) {
4704
4997
  return writers.some((writer) => {
4705
- const render2 = DOCUMENT_RENDERS[writer.format];
4706
- if (render2?.input !== format) return false;
4707
- return writer.shapes.some((shape) => render2.shapes[shape] === construct);
4998
+ const render3 = DOCUMENT_RENDERS[writer.format];
4999
+ if (render3?.input !== format) return false;
5000
+ return writer.shapes.some((shape) => render3.shapes[shape] === construct);
4708
5001
  });
4709
5002
  }
4710
5003
  function listFormats(opts = {}) {
@@ -4997,7 +5290,7 @@ async function lintTermSet(set, opts = {}) {
4997
5290
  }
4998
5291
 
4999
5292
  // src/term/commands/lint.ts
5000
- async function runLint(opts) {
5293
+ async function runLint2(opts) {
5001
5294
  const { cwd, run, set } = await loadTerms(opts);
5002
5295
  const valeConfig = valeConfigFor(run);
5003
5296
  const findings = await lintTermSet(set, {
@@ -5019,7 +5312,7 @@ async function runLint(opts) {
5019
5312
  }
5020
5313
 
5021
5314
  // src/term/commands/list.ts
5022
- var LIST_FORMATS = ["pretty", "json", "csv"];
5315
+ var LIST_FORMATS2 = ["pretty", "json", "csv"];
5023
5316
  async function runList(opts) {
5024
5317
  const { set } = await loadTerms(opts);
5025
5318
  return { terms: set.terms, set };
@@ -5143,14 +5436,14 @@ async function runWrite(opts) {
5143
5436
  const shape = format === "vale" || /[\\/]$/.test(opts.out ?? "") || isDirectory(targetPath) ? "directory" : "file";
5144
5437
  const target = { path: targetPath, shape };
5145
5438
  const existing = await existingFor(target);
5146
- let render2;
5439
+ let render3;
5147
5440
  try {
5148
- render2 = writer.render(set.terms, target, { existing });
5441
+ render3 = writer.render(set.terms, target, { existing });
5149
5442
  } catch (error) {
5150
5443
  if (error instanceof TermError) throw relativized(error, [...existing.keys(), targetPath], cwd);
5151
5444
  throw error;
5152
5445
  }
5153
- const changes = await applyChanges(render2.files, render2.removals, existing, cwd, !check && !dryRun);
5446
+ const changes = await applyChanges(render3.files, render3.removals, existing, cwd, !check && !dryRun);
5154
5447
  const shown = format === "vale" ? displayPath(join4(targetPath, STYLE2), cwd) : opts.out ?? displayPath(targetPath, cwd);
5155
5448
  return {
5156
5449
  mode: "render",
@@ -5159,11 +5452,11 @@ async function runWrite(opts) {
5159
5452
  target: shown,
5160
5453
  terms: set.terms.length,
5161
5454
  changes,
5162
- files: render2.files.map((file) => ({ path: displayPath(file.path, cwd), content: file.content })),
5455
+ files: render3.files.map((file) => ({ path: displayPath(file.path, cwd), content: file.content })),
5163
5456
  // A Vale style holds designations only, by design; its dropped definitions are not news.
5164
- dropped: format === "vale" ? [] : render2.dropped,
5457
+ dropped: format === "vale" ? [] : render3.dropped,
5165
5458
  droppedFrom: droppedFrom(format, shape, readers),
5166
- skipped: render2.skipped,
5459
+ skipped: render3.skipped,
5167
5460
  check,
5168
5461
  dryRun,
5169
5462
  ...wiring === void 0 ? {} : { wiring }
@@ -5630,12 +5923,12 @@ function renderFindings(report, format, options, references2) {
5630
5923
  if (text2.length > 0 || !OMITTED_WHEN_CLEAN.has(format)) print(text2);
5631
5924
  process.exitCode = report.summary.failed > 0 ? 1 : 0;
5632
5925
  }
5633
- function buildProgram5() {
5634
- const program = new Command4();
5926
+ function buildProgram6() {
5927
+ const program = new Command5();
5635
5928
  program.name("term").description("Check, lint and render a docset's terms and the references into them.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride();
5636
5929
  withInputs(
5637
5930
  program.command("list").description("List the resolved entries").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
5638
- ).option("-f, --format <format>", `output: ${LIST_FORMATS.join(" | ")}`, "pretty").addHelpText(
5931
+ ).option("-f, --format <format>", `output: ${LIST_FORMATS2.join(" | ")}`, "pretty").addHelpText(
5639
5932
  "after",
5640
5933
  [
5641
5934
  "",
@@ -5646,7 +5939,7 @@ function buildProgram5() {
5646
5939
  ].join("\n")
5647
5940
  ).action(async (paths, options) => {
5648
5941
  try {
5649
- const format = formatChoice(options.format, LIST_FORMATS);
5942
+ const format = formatChoice(options.format, LIST_FORMATS2);
5650
5943
  const { terms } = await runList(await coreOptions(paths, options));
5651
5944
  print(format === "json" ? renderListJson(terms) : format === "csv" ? renderListCsv(terms) : renderListPretty(terms));
5652
5945
  process.exitCode = 0;
@@ -5700,7 +5993,7 @@ function buildProgram5() {
5700
5993
  ).action(async (paths, options) => {
5701
5994
  try {
5702
5995
  const format = formatChoice(options.format, FINDING_FORMATS);
5703
- const report = await runLint(await coreOptions(paths, options));
5996
+ const report = await runLint2(await coreOptions(paths, options));
5704
5997
  renderFindings(report, format, options, false);
5705
5998
  } catch (err) {
5706
5999
  fail(err);
@@ -5749,12 +6042,12 @@ function buildProgram5() {
5749
6042
  }
5750
6043
 
5751
6044
  // src/cli.ts
5752
- function buildProgram6() {
6045
+ function buildProgram7() {
5753
6046
  const meta = buildProgram().name("meta").description(
5754
6047
  "Validate, read, query and fill document metadata against JSON Schema"
5755
6048
  );
5756
6049
  const metaCommands = new Set(meta.commands.map((c) => c.name()));
5757
- const program = new Command5();
6050
+ const program = new Command6();
5758
6051
  program.name("manni").description("Tools for documentation that is meant to be checked.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride().configureOutput({
5759
6052
  outputError: (str, write) => {
5760
6053
  write(str);
@@ -5780,15 +6073,20 @@ function buildProgram6() {
5780
6073
  )
5781
6074
  );
5782
6075
  program.addCommand(
5783
- buildProgram5().name("term").description("Check, lint and render a docset's terms and the references into them")
6076
+ buildProgram5().name("lint").description(
6077
+ "Validate document structure against doctype templates, routed by a page's type"
6078
+ )
6079
+ );
6080
+ program.addCommand(
6081
+ buildProgram6().name("term").description("Check, lint and render a docset's terms and the references into them")
5784
6082
  );
5785
6083
  program.addCommand(
5786
6084
  buildProgram4().name("key").description("Set and rotate the family key that encrypted values are encrypted with.")
5787
6085
  );
5788
6086
  return program;
5789
6087
  }
5790
- runIfMain(import.meta.url, buildProgram6);
6088
+ runIfMain(import.meta.url, buildProgram7);
5791
6089
  export {
5792
- buildProgram6 as buildProgram
6090
+ buildProgram7 as buildProgram
5793
6091
  };
5794
6092
  //# sourceMappingURL=cli.js.map