paperlint 8.0.0 → 9.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/CLAUDE.md +9 -25
- package/CONTRIBUTING.md +5 -4
- package/README.md +4 -3
- package/dist/adapters/bibtex/index.d.ts +3 -0
- package/dist/adapters/bibtex/index.d.ts.map +1 -0
- package/dist/adapters/bibtex/index.js +103 -0
- package/dist/adapters/bibtex/index.js.map +1 -0
- package/dist/adapters/latex/filecontents.d.ts +33 -0
- package/dist/adapters/latex/filecontents.d.ts.map +1 -0
- package/dist/adapters/latex/filecontents.js +65 -0
- package/dist/adapters/latex/filecontents.js.map +1 -0
- package/dist/adapters/latex/includes.d.ts +3 -3
- package/dist/adapters/latex/includes.d.ts.map +1 -1
- package/dist/adapters/latex/includes.js +18 -10
- package/dist/adapters/latex/includes.js.map +1 -1
- package/dist/adapters/latex/index.d.ts.map +1 -1
- package/dist/adapters/latex/index.js +2 -0
- package/dist/adapters/latex/index.js.map +1 -1
- package/dist/adapters/latex/nodes.d.ts +12 -0
- package/dist/adapters/latex/nodes.d.ts.map +1 -1
- package/dist/adapters/latex/nodes.js +39 -0
- package/dist/adapters/latex/nodes.js.map +1 -1
- package/dist/adapters/latex/parse.d.ts +7 -1
- package/dist/adapters/latex/parse.d.ts.map +1 -1
- package/dist/adapters/latex/parse.js +55 -6
- package/dist/adapters/latex/parse.js.map +1 -1
- package/dist/adapters/memory/index.d.ts +3 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +9 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +27 -24
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/sources-record/index.d.ts +9 -0
- package/dist/adapters/sources-record/index.d.ts.map +1 -0
- package/dist/adapters/sources-record/index.js +68 -0
- package/dist/adapters/sources-record/index.js.map +1 -0
- package/dist/adapters/tex-output/blg.d.ts +9 -0
- package/dist/adapters/tex-output/blg.d.ts.map +1 -0
- package/dist/adapters/tex-output/blg.js +20 -0
- package/dist/adapters/tex-output/blg.js.map +1 -0
- package/dist/adapters/tex-output/fls.d.ts +8 -0
- package/dist/adapters/tex-output/fls.d.ts.map +1 -0
- package/dist/adapters/tex-output/fls.js +16 -0
- package/dist/adapters/tex-output/fls.js.map +1 -0
- package/dist/adapters/tex-output/index.d.ts +7 -0
- package/dist/adapters/tex-output/index.d.ts.map +1 -0
- package/dist/adapters/tex-output/index.js +7 -0
- package/dist/adapters/tex-output/index.js.map +1 -0
- package/dist/bib-rules.d.ts +47 -0
- package/dist/bib-rules.d.ts.map +1 -0
- package/dist/bib-rules.js +127 -0
- package/dist/bib-rules.js.map +1 -0
- package/dist/build.d.ts +29 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +40 -24
- package/dist/build.js.map +1 -1
- package/dist/claim-provenance.d.ts +2 -2
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +30 -26
- package/dist/cli.js.map +1 -1
- package/dist/domain/paper-source.d.ts +1 -10
- package/dist/domain/paper-source.d.ts.map +1 -1
- package/dist/domain/paper-source.js +34 -24
- package/dist/domain/paper-source.js.map +1 -1
- package/dist/domain/paper-sources.d.ts +72 -0
- package/dist/domain/paper-sources.d.ts.map +1 -0
- package/dist/domain/paper-sources.js +18 -0
- package/dist/domain/paper-sources.js.map +1 -0
- package/dist/domain/sources-record.d.ts +102 -0
- package/dist/domain/sources-record.d.ts.map +1 -0
- package/dist/domain/sources-record.js +38 -0
- package/dist/domain/sources-record.js.map +1 -0
- package/dist/domain/tex-run.d.ts +25 -0
- package/dist/domain/tex-run.d.ts.map +1 -0
- package/dist/domain/tex-run.js +79 -0
- package/dist/domain/tex-run.js.map +1 -0
- package/dist/eslint-rules/paper-context.d.ts.map +1 -1
- package/dist/eslint-rules/paper-context.js +4 -3
- package/dist/eslint-rules/paper-context.js.map +1 -1
- package/dist/eslint-rules/paper-typography.d.ts +0 -9
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +12 -5
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/latex-log.d.ts +10 -0
- package/dist/latex-log.d.ts.map +1 -1
- package/dist/latex-log.js +10 -0
- package/dist/latex-log.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -0
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js.map +1 -1
- package/dist/package-dirs.d.ts.map +1 -1
- package/dist/package-dirs.js +3 -2
- package/dist/package-dirs.js.map +1 -1
- package/dist/paper-includes.d.ts +4 -7
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +29 -21
- package/dist/paper-includes.js.map +1 -1
- package/dist/paper-prose.d.ts +2 -2
- package/dist/paper-record.d.ts +25 -0
- package/dist/paper-record.d.ts.map +1 -0
- package/dist/paper-record.js +39 -0
- package/dist/paper-record.js.map +1 -0
- package/dist/paper-sources.d.ts +66 -0
- package/dist/paper-sources.d.ts.map +1 -0
- package/dist/paper-sources.js +92 -0
- package/dist/paper-sources.js.map +1 -0
- package/dist/ports/bib-reader.d.ts +18 -0
- package/dist/ports/bib-reader.d.ts.map +1 -0
- package/dist/ports/bib-reader.js +2 -0
- package/dist/ports/bib-reader.js.map +1 -0
- package/dist/ports/check-references.d.ts +9 -4
- package/dist/ports/check-references.d.ts.map +1 -1
- package/dist/ports/latex.d.ts +15 -0
- package/dist/ports/latex.d.ts.map +1 -1
- package/dist/ports/sources-codec.d.ts +11 -0
- package/dist/ports/sources-codec.d.ts.map +1 -0
- package/dist/ports/sources-codec.js +2 -0
- package/dist/ports/sources-codec.js.map +1 -0
- package/dist/ports/tex-output.d.ts +17 -0
- package/dist/ports/tex-output.d.ts.map +1 -0
- package/dist/ports/tex-output.js +2 -0
- package/dist/ports/tex-output.js.map +1 -0
- package/dist/recorded-bibliography.d.ts +89 -0
- package/dist/recorded-bibliography.d.ts.map +1 -0
- package/dist/recorded-bibliography.js +136 -0
- package/dist/recorded-bibliography.js.map +1 -0
- package/dist/reference-rules.d.ts +5 -5
- package/dist/reference-rules.d.ts.map +1 -1
- package/dist/reference-rules.js +150 -77
- package/dist/reference-rules.js.map +1 -1
- package/dist/references.d.ts +71 -16
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +110 -26
- package/dist/references.js.map +1 -1
- package/dist/register-bands.d.ts +3 -3
- package/dist/register-bands.d.ts.map +1 -1
- package/dist/register-bands.js.map +1 -1
- package/dist/register.d.ts +2 -2
- package/dist/sources-record.d.ts +38 -0
- package/dist/sources-record.d.ts.map +1 -0
- package/dist/sources-record.js +123 -0
- package/dist/sources-record.js.map +1 -0
- package/dist/sources-rules.d.ts +44 -0
- package/dist/sources-rules.d.ts.map +1 -0
- package/dist/sources-rules.js +76 -0
- package/dist/sources-rules.js.map +1 -0
- package/dist/tex-paper.d.ts +10 -15
- package/dist/tex-paper.d.ts.map +1 -1
- package/dist/tex-paper.js +24 -40
- package/dist/tex-paper.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +3 -3
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js.map +1 -1
- package/docs/configuration.md +12 -4
- package/docs/design/paper-sources.md +197 -0
- package/docs/package-shape-options.md +1 -1
- package/docs/references.md +34 -8
- package/docs/rules/bib/reachable-entry.md +64 -0
- package/docs/rules/paper/sources-fresh.md +81 -0
- package/docs/rules.md +62 -57
- package/eslint-rules/paper-context.ts +4 -3
- package/eslint-rules/paper-typography.test.mjs +12 -72
- package/eslint-rules/paper-typography.ts +13 -6
- package/eslint.config.mjs +22 -0
- package/fixtures/build-e2e/aidc/paper.tex +1 -1
- package/fixtures/build-e2e/anon-ack/paper.tex +1 -1
- package/fixtures/build-e2e/anon-author/paper.tex +1 -1
- package/fixtures/build-e2e/anon-bib/paper.tex +1 -1
- package/fixtures/build-e2e/anon-href/paper.tex +1 -1
- package/fixtures/build-e2e/anon-macro/paper.tex +1 -1
- package/fixtures/build-e2e/anon-pdfauthor/paper.tex +1 -1
- package/fixtures/paper-sources/p1/paper.tex +30 -0
- package/fixtures/paper-sources/p1/refs.bib +6 -0
- package/fixtures/paper-sources/p1/sections/dead.tex +6 -0
- package/fixtures/paper-sources/p1/sections/intro.tex +7 -0
- package/fixtures/paper-sources/p1/sections/parked.tex +6 -0
- package/fixtures/paper-sources/p1/tex-truth.json +37 -0
- package/fixtures/paper-sources/p1/versions/2026-01-01-review.tex +4 -0
- package/fixtures/paper-sources/v1-stale/paper.tex +14 -0
- package/fixtures/paper-sources/v1-stale/refs.bib +6 -0
- package/fixtures/paper-sources/v1-stale/tex-truth.json +32 -0
- package/fixtures/paper-sources/v10-ifthenelse/anon.bib +1 -0
- package/fixtures/paper-sources/v10-ifthenelse/paper.tex +9 -0
- package/fixtures/paper-sources/v10-ifthenelse/real.bib +1 -0
- package/fixtures/paper-sources/v10-ifthenelse/tex-truth.json +28 -0
- package/fixtures/paper-sources/v11-two-blocks/paper.tex +12 -0
- package/fixtures/paper-sources/v11-two-blocks/tex-truth.json +30 -0
- package/fixtures/paper-sources/v13-percent-text/paper.tex +6 -0
- package/fixtures/paper-sources/v13-percent-text/refs.bib +3 -0
- package/fixtures/paper-sources/v13-percent-text/tex-truth.json +30 -0
- package/fixtures/paper-sources/v15-after-end/paper.tex +6 -0
- package/fixtures/paper-sources/v15-after-end/refs.bib +1 -0
- package/fixtures/paper-sources/v15-after-end/tex-truth.json +17 -0
- package/fixtures/paper-sources/v16-included-block/bibblock.tex +3 -0
- package/fixtures/paper-sources/v16-included-block/paper.tex +7 -0
- package/fixtures/paper-sources/v16-included-block/refs.bib +1 -0
- package/fixtures/paper-sources/v16-included-block/tex-truth.json +35 -0
- package/fixtures/paper-sources/v17-include-order/bibsetup.tex +1 -0
- package/fixtures/paper-sources/v17-include-order/first.bib +1 -0
- package/fixtures/paper-sources/v17-include-order/later.bib +1 -0
- package/fixtures/paper-sources/v17-include-order/paper.tex +7 -0
- package/fixtures/paper-sources/v17-include-order/tex-truth.json +40 -0
- package/fixtures/paper-sources/v2-overwrite/paper.tex +14 -0
- package/fixtures/paper-sources/v2-overwrite/refs.bib +6 -0
- package/fixtures/paper-sources/v2-overwrite/tex-truth.json +30 -0
- package/fixtures/paper-sources/v22-parked-thebibliography/paper.tex +7 -0
- package/fixtures/paper-sources/v22-parked-thebibliography/tex-truth.json +17 -0
- package/fixtures/paper-sources/v23-parked-include/old.bib +1 -0
- package/fixtures/paper-sources/v23-parked-include/paper.tex +7 -0
- package/fixtures/paper-sources/v23-parked-include/parked.tex +1 -0
- package/fixtures/paper-sources/v23-parked-include/refs.bib +1 -0
- package/fixtures/paper-sources/v23-parked-include/tex-truth.json +28 -0
- package/fixtures/paper-sources/v24-quoted-fields/paper.tex +6 -0
- package/fixtures/paper-sources/v24-quoted-fields/refs.bib +5 -0
- package/fixtures/paper-sources/v24-quoted-fields/tex-truth.json +32 -0
- package/fixtures/paper-sources/v25-string-differs/paper.tex +10 -0
- package/fixtures/paper-sources/v25-string-differs/refs.bib +2 -0
- package/fixtures/paper-sources/v25-string-differs/tex-truth.json +32 -0
- package/fixtures/paper-sources/v26-comment-in-names/one.bib +1 -0
- package/fixtures/paper-sources/v26-comment-in-names/paper.tex +7 -0
- package/fixtures/paper-sources/v26-comment-in-names/tex-truth.json +32 -0
- package/fixtures/paper-sources/v26-comment-in-names/two.bib +1 -0
- package/fixtures/paper-sources/v27-shared-string/abbreviations.bib +2 -0
- package/fixtures/paper-sources/v27-shared-string/paper.tex +6 -0
- package/fixtures/paper-sources/v27-shared-string/references.bib +6 -0
- package/fixtures/paper-sources/v27-shared-string/tex-truth.json +31 -0
- package/fixtures/paper-sources/v28-shared-string-block/abbreviations.bib +2 -0
- package/fixtures/paper-sources/v28-shared-string-block/paper.tex +14 -0
- package/fixtures/paper-sources/v28-shared-string-block/tex-truth.json +33 -0
- package/fixtures/paper-sources/v29-generated-input/paper.tex +9 -0
- package/fixtures/paper-sources/v29-generated-input/tex-truth.json +24 -0
- package/fixtures/paper-sources/v3-declared/paper.bib +6 -0
- package/fixtures/paper-sources/v3-declared/paper.tex +6 -0
- package/fixtures/paper-sources/v3-declared/tex-truth.json +28 -0
- package/fixtures/paper-sources/v30-name-next-line/paper.tex +14 -0
- package/fixtures/paper-sources/v30-name-next-line/tex-truth.json +30 -0
- package/fixtures/paper-sources/v4-commented/paper.tex +14 -0
- package/fixtures/paper-sources/v4-commented/refs.bib +6 -0
- package/fixtures/paper-sources/v4-commented/tex-truth.json +28 -0
- package/fixtures/paper-sources/v5-percent-entry/paper.tex +20 -0
- package/fixtures/paper-sources/v5-percent-entry/tex-truth.json +37 -0
- package/fixtures/paper-sources/v6-unclosed/paper.tex +6 -0
- package/fixtures/paper-sources/v6-unclosed/refs.bib +5 -0
- package/fixtures/paper-sources/v6-unclosed/tex-truth.json +37 -0
- package/fixtures/paper-sources/v8-jobname/paper.tex +9 -0
- package/fixtures/paper-sources/v8-jobname/tex-truth.json +30 -0
- package/fixtures/paper-sources/v9-two-bibliographies/one.bib +1 -0
- package/fixtures/paper-sources/v9-two-bibliographies/paper.tex +7 -0
- package/fixtures/paper-sources/v9-two-bibliographies/tex-truth.json +35 -0
- package/fixtures/paper-sources/v9-two-bibliographies/two.bib +1 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +1 -1
- package/fixtures/paper-typography/messy-paper/paper.tex +1 -1
- package/package.json +2 -10
- package/scripts/check.test.ts +23 -1
- package/scripts/check.ts +7 -3
- package/scripts/coverage-config.test.ts +19 -10
- package/scripts/layer-legacy-frozen.test.ts +41 -2
- package/scripts/layer-legacy-frozen.ts +31 -2
- package/skills/grade-paper-writing/SKILL.md +4 -3
- package/skills/grade-paper-writing/SKILL.md.spec.ts +3 -2
- package/skills/grade-paper-writing/prose-lint.mjs +11 -2
- package/skills/grade-paper-writing/prose-lint.test.ts +52 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +83 -33
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +67 -115
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +128 -121
- package/skills/verify-citations/SKILL.md +7 -4
- package/skills/verify-citations/SKILL.md.spec.ts +6 -3
- package/skills/verify-citations/scripts/bib-authors.check.test.mjs +17 -19
- package/skills/verify-citations/scripts/bib-authors.cli.test.ts +129 -46
- package/skills/verify-citations/scripts/bib-authors.d.mts +22 -2
- package/skills/verify-citations/scripts/bib-authors.mjs +57 -111
- package/skills/verify-citations/scripts/verify-cites.cli.test.ts +16 -0
- package/skills/verify-citations/scripts/verify-cites.d.mts +19 -3
- package/skills/verify-citations/scripts/verify-cites.mjs +42 -108
- package/skills/verify-citations/scripts/verify-cites.test.mjs +23 -36
- package/src/adapters/bibtex/index.test.ts +163 -0
- package/src/adapters/bibtex/index.ts +155 -0
- package/src/adapters/latex/filecontents.ts +114 -0
- package/src/adapters/latex/includes.ts +18 -10
- package/src/adapters/latex/index.test.ts +73 -0
- package/src/adapters/latex/index.ts +2 -0
- package/src/adapters/latex/nodes.ts +64 -0
- package/src/adapters/latex/parse.test.ts +38 -0
- package/src/adapters/latex/parse.ts +67 -6
- package/src/adapters/memory/index.ts +17 -0
- package/src/adapters/references/index.test.ts +75 -21
- package/src/adapters/references/index.ts +37 -32
- package/src/adapters/sources-record/index.test.ts +87 -0
- package/src/adapters/sources-record/index.ts +82 -0
- package/src/adapters/tex-output/blg.ts +27 -0
- package/src/adapters/tex-output/fls.ts +24 -0
- package/src/adapters/tex-output/index.test.ts +171 -0
- package/src/adapters/tex-output/index.ts +12 -0
- package/src/bib-rules.test.ts +356 -0
- package/src/bib-rules.ts +228 -0
- package/src/bibliography-at.test.ts +286 -0
- package/src/build.test.ts +210 -0
- package/src/build.ts +100 -35
- package/src/claim-provenance.test.ts +33 -7
- package/src/claim-provenance.ts +2 -2
- package/src/cli.harness.mjs +20 -10
- package/src/cli.test.ts +14 -10
- package/src/cli.ts +37 -28
- package/src/domain/paper-source.test.ts +58 -20
- package/src/domain/paper-source.ts +38 -42
- package/src/domain/paper-sources.ts +94 -0
- package/src/domain/sources-record.test.ts +71 -0
- package/src/domain/sources-record.ts +136 -0
- package/src/domain/tex-run.test.ts +228 -0
- package/src/domain/tex-run.ts +139 -0
- package/src/heading-case.test.ts +24 -10
- package/src/latex-log.ts +20 -0
- package/src/latex-loop.harness.ts +1 -0
- package/src/latex-loop.ts +2 -0
- package/src/linted-set.test.ts +9 -1
- package/src/new-paper.harness.mjs +3 -2
- package/src/new-paper.test.ts +9 -5
- package/src/package-dirs.ts +3 -2
- package/src/paper-includes.test.ts +157 -38
- package/src/paper-includes.ts +39 -35
- package/src/paper-prose.test.ts +34 -5
- package/src/paper-prose.ts +2 -2
- package/src/paper-record.test.ts +173 -0
- package/src/paper-record.ts +66 -0
- package/src/paper-settings-commands.test.ts +5 -2
- package/src/paper-sources.ts +151 -0
- package/src/ports/bib-reader.ts +27 -0
- package/src/ports/check-references.ts +7 -3
- package/src/ports/latex.ts +19 -0
- package/src/ports/sources-codec.ts +11 -0
- package/src/ports/tex-output.ts +17 -0
- package/src/recorded-bibliography.ts +297 -0
- package/src/reference-rules.ts +217 -104
- package/src/references.test.ts +520 -177
- package/src/references.ts +185 -37
- package/src/register-bands.test.ts +5 -1
- package/src/register-bands.ts +3 -5
- package/src/register.test.ts +6 -1
- package/src/register.ts +2 -2
- package/src/rule-cases.test.ts +68 -12
- package/src/rule-docs.test.ts +0 -1
- package/src/sources-record.test.ts +308 -0
- package/src/sources-record.ts +231 -0
- package/src/sources-rules.test.ts +152 -0
- package/src/sources-rules.ts +117 -0
- package/src/tex-paper.test.ts +148 -78
- package/src/tex-paper.ts +31 -61
- package/src/tex-venue-rules.test.ts +36 -8
- package/src/tex-venue-rules.ts +3 -5
- package/dist/eslint-rules/bib-reachable-entry.d.ts +0 -22
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +0 -1
- package/dist/eslint-rules/bib-reachable-entry.js +0 -75
- package/dist/eslint-rules/bib-reachable-entry.js.map +0 -1
- package/eslint-rules/bib-reachable-entry.ts +0 -76
package/src/reference-rules.ts
CHANGED
|
@@ -5,25 +5,47 @@
|
|
|
5
5
|
*
|
|
6
6
|
* entry whose authors are the preprint's paper/author-list (error), on the entry
|
|
7
7
|
* entry whose identifier provably fails paper/cite-exists (error), on the entry
|
|
8
|
-
* no
|
|
9
|
-
*
|
|
8
|
+
* no verdicts, or recorded "not checked" paper/refs-checked (warn) — like pdf/measured
|
|
9
|
+
* a key bibtex typeset that no entry the paper/refs-checked (warn): that reference was not
|
|
10
|
+
* reader read has (`unseenKeys`) checked
|
|
11
|
+
* a database bibtex found outside the paper/refs-checked (warn): its references were not checked
|
|
12
|
+
* paper directory (BIBINPUTS)
|
|
13
|
+
* databases edited after the build paper/refs-fresh (error) — like pdf/fresh
|
|
10
14
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
15
|
+
* The databases are the ones the last build's bibtex opened (`_build/sources.json`,
|
|
16
|
+
* docs/design/paper-sources.md §1) — `readBibliography` answers only from a record the paper has not
|
|
17
|
+
* changed since. With no record, or a stale one, every rule here is silent: `paper/sources-fresh`
|
|
18
|
+
* speaks once. While the verdicts are stale, the per-entry rules are silent too: they would judge
|
|
19
|
+
* databases that no longer exist, and refs-fresh already says so once. A paper whose build ran no
|
|
20
|
+
* bibtex gets nothing.
|
|
21
|
+
*
|
|
22
|
+
* `paper/author-list` and `paper/cite-exists` lay the verdicts on the entries of those databases. ESLint
|
|
23
|
+
* does not lint a `.bib`, so a finding is a place in `paper.tex`: an entry of a `.bib` TeX wrote from a
|
|
24
|
+
* block of `paper.tex` is reported on its line there (`entryReports`); any other entry at the top of
|
|
25
|
+
* `paper.tex`, with the entry's file, line and column at the front of the message (`refs.bib:12:1:`).
|
|
14
26
|
*
|
|
15
27
|
* `paper/author-list` used to live on PIPELINE-STATUS.md and ask whether the scorecard mentioned
|
|
16
28
|
* a run. It lives here now because its subject is the bibliography, and the record of the run is
|
|
17
29
|
* the run's own output.
|
|
18
30
|
*/
|
|
19
31
|
import { basename, dirname } from "node:path";
|
|
20
|
-
import
|
|
32
|
+
import { callerPath } from "./caller-path.ts";
|
|
33
|
+
import { MAIN_FILE } from "./domain/paper-sources.ts";
|
|
34
|
+
import type { EntryVerdict } from "./ports/check-references.ts";
|
|
35
|
+
import {
|
|
36
|
+
entryReports,
|
|
37
|
+
type EntryReport,
|
|
38
|
+
type RecordedDeps,
|
|
39
|
+
type RecordedEntry,
|
|
40
|
+
} from "./recorded-bibliography.ts";
|
|
21
41
|
import {
|
|
22
42
|
bibHash,
|
|
23
|
-
|
|
43
|
+
readBibliography,
|
|
24
44
|
readReferences,
|
|
25
45
|
REFERENCES_FILE,
|
|
26
|
-
|
|
46
|
+
unseenKeys,
|
|
47
|
+
type CheckedBibliography,
|
|
48
|
+
type ReferencesDocument,
|
|
27
49
|
} from "./references.ts";
|
|
28
50
|
|
|
29
51
|
interface Loc {
|
|
@@ -34,6 +56,8 @@ interface RuleContext {
|
|
|
34
56
|
readonly filename: string;
|
|
35
57
|
readonly cwd: string;
|
|
36
58
|
readonly sourceCode: {
|
|
59
|
+
/** The file as written (the `tex/latex` language's source code). */
|
|
60
|
+
readonly raw: string;
|
|
37
61
|
getLocFromIndex(i: number): Loc;
|
|
38
62
|
};
|
|
39
63
|
report(d: {
|
|
@@ -54,50 +78,77 @@ export interface ReferenceRuleModule {
|
|
|
54
78
|
|
|
55
79
|
type Name = "author-list" | "cite-exists" | "refs-checked" | "refs-fresh";
|
|
56
80
|
|
|
81
|
+
/** The checked entries of the databases, each with the verdict the build recorded for it. */
|
|
82
|
+
interface Judged {
|
|
83
|
+
readonly bib: CheckedBibliography;
|
|
84
|
+
readonly verdicts: readonly (readonly [RecordedEntry, EntryVerdict])[];
|
|
85
|
+
}
|
|
86
|
+
|
|
57
87
|
/** What one lint run knows about a paper's references. */
|
|
58
88
|
type Assessment =
|
|
59
|
-
|
|
89
|
+
/** No usable record of the build, or a build that ran no bibtex: nothing to judge. */
|
|
90
|
+
| { readonly kind: "silent" }
|
|
91
|
+
| { readonly kind: "unread"; readonly databases: readonly string[] }
|
|
60
92
|
| { readonly kind: "unrecorded" }
|
|
61
93
|
| { readonly kind: "not-checked"; readonly why: string }
|
|
62
94
|
| { readonly kind: "stale" }
|
|
63
|
-
| {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
| { readonly kind: "ready"; readonly judged: Judged };
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Each entry checked with its verdict — when the record holds one verdict per entry, in its order, as
|
|
99
|
+
* the build writes it. One that does not (written by hand) is not about these databases: null.
|
|
100
|
+
*/
|
|
101
|
+
function laid(
|
|
102
|
+
verdicts: readonly EntryVerdict[],
|
|
103
|
+
entries: readonly RecordedEntry[],
|
|
104
|
+
): Judged["verdicts"] | null {
|
|
105
|
+
const pairs = entries.flatMap((f, i) => {
|
|
106
|
+
const v = verdicts[i];
|
|
107
|
+
return v?.key === f.entry.key ? [[f, v] as const] : [];
|
|
108
|
+
});
|
|
109
|
+
return pairs.length === entries.length && verdicts.length === entries.length
|
|
110
|
+
? pairs
|
|
111
|
+
: null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** What the verdicts on disk say about the databases of `bib`. */
|
|
115
|
+
function judgedVerdicts(
|
|
116
|
+
bib: CheckedBibliography,
|
|
117
|
+
doc: ReferencesDocument | null,
|
|
118
|
+
): Assessment {
|
|
77
119
|
if (doc === null) return { kind: "unrecorded" };
|
|
78
120
|
if (doc.bib.sha256 !== bibHash(bib)) return { kind: "stale" };
|
|
79
121
|
if (doc.status === "not-checked")
|
|
80
122
|
return { kind: "not-checked", why: doc.why ?? "no reason recorded" };
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
...(e.exists === "false"
|
|
86
|
-
? [{ key: e.key, rule: "cite-exists" as const, why: e.why ?? "" }]
|
|
87
|
-
: []),
|
|
88
|
-
]);
|
|
89
|
-
return { kind: "ready", bib, failing };
|
|
123
|
+
const verdicts = laid(doc.entries, bib.entries);
|
|
124
|
+
return verdicts === null
|
|
125
|
+
? { kind: "stale" }
|
|
126
|
+
: { kind: "ready", judged: { bib, verdicts } };
|
|
90
127
|
}
|
|
91
128
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
function assess(deps: RecordedDeps, paperDir: string): Assessment {
|
|
130
|
+
const read = readBibliography(paperDir, deps);
|
|
131
|
+
switch (read.kind) {
|
|
132
|
+
case "no-record":
|
|
133
|
+
case "nothing":
|
|
134
|
+
case "not-wired":
|
|
135
|
+
return { kind: "silent" };
|
|
136
|
+
case "unread":
|
|
137
|
+
return { kind: "unread", databases: read.databases };
|
|
138
|
+
case "read":
|
|
139
|
+
return judgedVerdicts(
|
|
140
|
+
read.bibliography,
|
|
141
|
+
readReferences(deps.files, paperDir),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
97
144
|
}
|
|
98
145
|
|
|
99
146
|
const BUILD = "`npx paperlint build`";
|
|
100
147
|
|
|
148
|
+
const MISMATCH =
|
|
149
|
+
"`{{key}}`: the authors are not those of the version cited — {{why}}. The citation resolves and the id resolves, and the list is the PREPRINT's under a published venue";
|
|
150
|
+
const MISSING = "`{{key}}` does not resolve to the work cited — {{why}}";
|
|
151
|
+
|
|
101
152
|
const META: Readonly<Record<Name, ReferenceRuleModule["meta"]>> = {
|
|
102
153
|
"author-list": {
|
|
103
154
|
type: "problem",
|
|
@@ -106,10 +157,7 @@ const META: Readonly<Record<Name, ReferenceRuleModule["meta"]>> = {
|
|
|
106
157
|
"each entry's authors are those of the version it cites, not the preprint's (recorded by paperlint build)",
|
|
107
158
|
},
|
|
108
159
|
schema: [],
|
|
109
|
-
messages: {
|
|
110
|
-
mismatch:
|
|
111
|
-
"`{{key}}`: the authors are not those of the version cited — {{why}}. The citation resolves and the id resolves, and the list is the PREPRINT's under a published venue",
|
|
112
|
-
},
|
|
160
|
+
messages: { mismatch: MISMATCH, mismatchIn: `{{where}}: ${MISMATCH}` },
|
|
113
161
|
},
|
|
114
162
|
"cite-exists": {
|
|
115
163
|
type: "problem",
|
|
@@ -118,9 +166,7 @@ const META: Readonly<Record<Name, ReferenceRuleModule["meta"]>> = {
|
|
|
118
166
|
"each entry's identifier resolves to the work cited (recorded by paperlint build)",
|
|
119
167
|
},
|
|
120
168
|
schema: [],
|
|
121
|
-
messages: {
|
|
122
|
-
missing: "`{{key}}` does not resolve to the work cited — {{why}}",
|
|
123
|
-
},
|
|
169
|
+
messages: { missing: MISSING, missingIn: `{{where}}: ${MISSING}` },
|
|
124
170
|
},
|
|
125
171
|
"refs-checked": {
|
|
126
172
|
type: "suggestion",
|
|
@@ -132,13 +178,16 @@ const META: Readonly<Record<Name, ReferenceRuleModule["meta"]>> = {
|
|
|
132
178
|
messages: {
|
|
133
179
|
unrecorded: `the references have not been checked — ${BUILD} checks them online and records the result in _build/${REFERENCES_FILE}`,
|
|
134
180
|
notChecked: `the last build could not check the references ({{why}}) — run ${BUILD} with network`,
|
|
181
|
+
unseen:
|
|
182
|
+
"the last build's bibtex typeset {{what}}, which paperlint's reader did not read from the databases bibtex opened, so those references were not checked: an entry behind `%` or inside `@comment{…}` — bibtex has no comment syntax and reads it, the reader skips it. Delete the entry, or remove its `@`",
|
|
183
|
+
unread: `{{what}}, which the last build's bibtex opened, is not in the paper directory (bibtex found it on its search path, BIBINPUTS), so its references were not checked — move it beside paper.tex`,
|
|
135
184
|
},
|
|
136
185
|
},
|
|
137
186
|
"refs-fresh": {
|
|
138
187
|
type: "problem",
|
|
139
188
|
docs: {
|
|
140
189
|
description:
|
|
141
|
-
"the recorded reference verdicts are about the
|
|
190
|
+
"the recorded reference verdicts are about the databases on disk, not an earlier set",
|
|
142
191
|
},
|
|
143
192
|
schema: [],
|
|
144
193
|
messages: {
|
|
@@ -147,76 +196,140 @@ const META: Readonly<Record<Name, ReferenceRuleModule["meta"]>> = {
|
|
|
147
196
|
},
|
|
148
197
|
};
|
|
149
198
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
199
|
+
interface Report {
|
|
200
|
+
/** Where in paper.tex; the start of the file when absent. */
|
|
201
|
+
readonly at?: EntryReport | undefined;
|
|
202
|
+
readonly messageId: string;
|
|
203
|
+
readonly data?: Record<string, string>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const AT_START: EntryReport = { kind: "here", span: { start: 0, end: 0 } };
|
|
207
|
+
|
|
208
|
+
const JUDGES: Readonly<
|
|
209
|
+
Record<"refs-checked" | "refs-fresh", (a: Assessment) => readonly Report[]>
|
|
210
|
+
> = {
|
|
211
|
+
"refs-checked": (a) => {
|
|
212
|
+
switch (a.kind) {
|
|
213
|
+
case "unrecorded":
|
|
214
|
+
return [{ messageId: "unrecorded" }];
|
|
215
|
+
case "not-checked":
|
|
216
|
+
return [{ messageId: "notChecked", data: { why: a.why } }];
|
|
217
|
+
case "unread":
|
|
218
|
+
return [
|
|
219
|
+
{ messageId: "unread", data: { what: a.databases.join(", ") } },
|
|
220
|
+
];
|
|
221
|
+
case "ready": {
|
|
222
|
+
const unseen = unseenKeys(a.judged.bib);
|
|
223
|
+
return unseen.length === 0
|
|
224
|
+
? []
|
|
225
|
+
: [
|
|
226
|
+
{
|
|
227
|
+
messageId: "unseen",
|
|
228
|
+
data: { what: unseen.map((k) => `\`${k}\``).join(", ") },
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
case "silent":
|
|
233
|
+
case "stale":
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"refs-fresh": (a) => (a.kind === "stale" ? [{ messageId: "stale" }] : []),
|
|
181
238
|
};
|
|
182
239
|
|
|
183
|
-
/**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
240
|
+
/**
|
|
241
|
+
* One finding, at its entry in paper.tex — or, for an entry in a `.bib`, at the top, with the `…In`
|
|
242
|
+
* message that names the entry's file, line and column first.
|
|
243
|
+
*/
|
|
244
|
+
function report(context: RuleContext, r: Report): void {
|
|
245
|
+
const loc = (i: number) => context.sourceCode.getLocFromIndex(i);
|
|
246
|
+
const at = r.at ?? AT_START;
|
|
247
|
+
context.report({
|
|
248
|
+
loc: { start: loc(at.span.start), end: loc(at.span.end) },
|
|
249
|
+
...(at.kind === "here"
|
|
250
|
+
? { messageId: r.messageId, ...(r.data ? { data: r.data } : {}) }
|
|
251
|
+
: {
|
|
252
|
+
messageId: `${r.messageId}In`,
|
|
253
|
+
data: { ...r.data, where: at.where },
|
|
254
|
+
}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** The verdicts a rule reports, and the message each makes. */
|
|
259
|
+
const VERDICT_RULES = {
|
|
260
|
+
"author-list": {
|
|
261
|
+
failed: (v: EntryVerdict) => v.authors === "mismatch",
|
|
262
|
+
messageId: "mismatch",
|
|
263
|
+
},
|
|
264
|
+
"cite-exists": {
|
|
265
|
+
failed: (v: EntryVerdict) => v.exists === "false",
|
|
266
|
+
messageId: "missing",
|
|
267
|
+
},
|
|
268
|
+
} as const;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The entries the verdicts judged failing for `rule`, each where it is reported (`entryReports`): the
|
|
272
|
+
* line of its `filecontents` block in the paper's text when TeX wrote its `.bib` from one, else the
|
|
273
|
+
* top of `paper.tex`.
|
|
274
|
+
*/
|
|
275
|
+
function verdictReports(
|
|
276
|
+
rule: keyof typeof VERDICT_RULES,
|
|
277
|
+
context: RuleContext,
|
|
278
|
+
deps: RecordedDeps,
|
|
279
|
+
): readonly Report[] {
|
|
280
|
+
const main = callerPath(context.filename);
|
|
281
|
+
const dir = dirname(main);
|
|
282
|
+
const a = assess(deps, dir);
|
|
283
|
+
if (a.kind !== "ready") return [];
|
|
284
|
+
const { failed, messageId } = VERDICT_RULES[rule];
|
|
285
|
+
const flagged = a.judged.verdicts.filter(([, v]) => failed(v));
|
|
286
|
+
const at = entryReports(
|
|
287
|
+
{
|
|
288
|
+
dir,
|
|
289
|
+
record: a.judged.bib.record,
|
|
290
|
+
main: { path: main, text: context.sourceCode.raw },
|
|
291
|
+
},
|
|
292
|
+
flagged.map(([f]) => f),
|
|
293
|
+
deps,
|
|
294
|
+
);
|
|
295
|
+
return flagged.map(([, v], i) => ({
|
|
296
|
+
at: at[i],
|
|
297
|
+
messageId,
|
|
298
|
+
data: { key: v.key, why: v.why ?? "" },
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** The four rules, reading the paper through `deps`. They act on `paper.tex` only. */
|
|
303
|
+
export function referenceRules(
|
|
304
|
+
deps: RecordedDeps,
|
|
305
|
+
): Record<Name, ReferenceRuleModule> {
|
|
306
|
+
const make = (
|
|
307
|
+
name: Name,
|
|
308
|
+
judge: (context: RuleContext) => readonly Report[],
|
|
309
|
+
): ReferenceRuleModule => ({
|
|
190
310
|
meta: META[name],
|
|
191
311
|
create: (context) =>
|
|
192
|
-
basename(context.filename) !==
|
|
312
|
+
basename(context.filename) !== MAIN_FILE
|
|
193
313
|
? {}
|
|
194
314
|
: {
|
|
195
315
|
"root:exit": () => {
|
|
196
|
-
const
|
|
197
|
-
const a = assess(files, paperDir);
|
|
198
|
-
// An external refs.bib: the entry is not in this file, so the finding sits at its
|
|
199
|
-
// start and names the key.
|
|
200
|
-
const external =
|
|
201
|
-
a.kind === "ready" && a.bib.source !== "paper.tex";
|
|
202
|
-
for (const r of JUDGES[name](a)) {
|
|
203
|
-
const loc = context.sourceCode.getLocFromIndex(
|
|
204
|
-
external ? 0 : r.at,
|
|
205
|
-
);
|
|
206
|
-
context.report({
|
|
207
|
-
loc: { start: loc, end: loc },
|
|
208
|
-
messageId: r.messageId,
|
|
209
|
-
...(r.data ? { data: r.data } : {}),
|
|
210
|
-
});
|
|
211
|
-
}
|
|
316
|
+
for (const r of judge(context)) report(context, r);
|
|
212
317
|
},
|
|
213
318
|
},
|
|
214
319
|
});
|
|
320
|
+
const assessed =
|
|
321
|
+
(name: "refs-checked" | "refs-fresh") =>
|
|
322
|
+
(context: RuleContext): readonly Report[] =>
|
|
323
|
+
JUDGES[name](assess(deps, dirname(callerPath(context.filename))));
|
|
215
324
|
return {
|
|
216
|
-
"author-list": make("author-list")
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"
|
|
325
|
+
"author-list": make("author-list", (c) =>
|
|
326
|
+
verdictReports("author-list", c, deps),
|
|
327
|
+
),
|
|
328
|
+
"cite-exists": make("cite-exists", (c) =>
|
|
329
|
+
verdictReports("cite-exists", c, deps),
|
|
330
|
+
),
|
|
331
|
+
"refs-checked": make("refs-checked", assessed("refs-checked")),
|
|
332
|
+
"refs-fresh": make("refs-fresh", assessed("refs-fresh")),
|
|
220
333
|
};
|
|
221
334
|
}
|
|
222
335
|
|