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/references.ts
CHANGED
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
* "bib-authors" in a scorecard table, or a command the project configured. Now the build runs
|
|
9
9
|
* them and records the result as data, beside the facts it already writes about the PDF:
|
|
10
10
|
*
|
|
11
|
-
* { "schema":
|
|
12
|
-
* "bib": { "
|
|
11
|
+
* { "schema": 3,
|
|
12
|
+
* "bib": { "sources": ["refs.bib"], "sha256": "…" }, the databases that were checked, as
|
|
13
|
+
* bibtex names them, and a hash of their text
|
|
13
14
|
* "status": "checked" | "not-checked", "why": "…", not-checked: nothing could be asked
|
|
14
15
|
* "entries": [ { "key", "exists", "authors", "why" } ] }
|
|
16
|
+
* one per entry checked, in its order
|
|
15
17
|
*
|
|
16
18
|
* A sibling file, not a new field of `paper.facts.json`: those facts are about ONE PDF and are
|
|
17
|
-
* stale when it changes (`pdf_sha256`); these are about the
|
|
18
|
-
*
|
|
19
|
+
* stale when it changes (`pdf_sha256`); these are about the databases and are stale when THEY
|
|
20
|
+
* change (`bib.sha256`). One staleness key per file, the way `pdf/fresh` already works.
|
|
19
21
|
*
|
|
20
22
|
* 🔴 WHAT THE CHECKERS ANSWERED IS KEPT, COMMITTED, IN `<paper>/repro/references-cache.json`
|
|
21
23
|
* (`domain/lookup-cache.ts`). Loaded before the check and saved after it — only when something new
|
|
@@ -23,6 +25,18 @@
|
|
|
23
25
|
* MAX_AGE_DAYS, not once per build (#107: 217 s for 27 references, every build). The verdicts above are NOT cached: they are derived from the cached
|
|
24
26
|
* answers on every build, so a fix to the checkers reaches every paper at once.
|
|
25
27
|
*
|
|
28
|
+
* ── WHICH DATABASES, AND WHAT BIBTEX TYPESET ───────────────────────────────────────────
|
|
29
|
+
* The databases checked are the ones the build's bibtex OPENED (`_build/sources.json`, written by
|
|
30
|
+
* `sources-record.ts` from TeX's own files; docs/design/paper-sources.md §1) — nothing here reads
|
|
31
|
+
* TeX source to decide which. Their entries are read by the one bibtex reader. bibtex reads
|
|
32
|
+
* differently from it in one place: it has no comment syntax, so an entry behind `%` or inside
|
|
33
|
+
* `@comment{…}` is one it typesets and the reader does not see. The record holds the keys bibtex
|
|
34
|
+
* typeset, so `paper/refs-checked` names the ones no entry of those databases has (`unseenKeys`).
|
|
35
|
+
* A malformed `.bib` never gets here: bibtex fails on it, and the build fails with bibtex's own lines.
|
|
36
|
+
*
|
|
37
|
+
* Without a record of the build, or with one the paper has changed since, there is no answer to
|
|
38
|
+
* "which databases": the step checks nothing, and the rules are silent — `paper/sources-fresh` speaks.
|
|
39
|
+
*
|
|
26
40
|
* 🔴 THE STEP NEVER FAILS THE BUILD. A build without network still builds the PDF; the reference
|
|
27
41
|
* step records `not-checked` and the lint rule says so, as a warning. Recording "not checked" as
|
|
28
42
|
* a pass would be the counter that counts what it never looked at.
|
|
@@ -44,10 +58,22 @@ import type {
|
|
|
44
58
|
ReferencesCheck,
|
|
45
59
|
ReferencesRun,
|
|
46
60
|
} from "./ports/check-references.ts";
|
|
47
|
-
import { bibRange } from "#eslint-rules/paper-typography";
|
|
48
61
|
import { messageOf } from "./domain/text.ts";
|
|
62
|
+
import {
|
|
63
|
+
describeChanges,
|
|
64
|
+
type SourcesRecord,
|
|
65
|
+
} from "./domain/sources-record.ts";
|
|
66
|
+
import type { PaperRecord } from "./paper-record.ts";
|
|
67
|
+
import {
|
|
68
|
+
entriesOfDatabases,
|
|
69
|
+
recordedBibliography,
|
|
70
|
+
type ReadDatabase,
|
|
71
|
+
type RecordedEntry,
|
|
72
|
+
type RecordedReadDeps,
|
|
73
|
+
} from "./recorded-bibliography.ts";
|
|
49
74
|
|
|
50
|
-
|
|
75
|
+
/** 3: the record of what bibtex read lives in `_build/sources.json`; an older one reads as none. */
|
|
76
|
+
export const REFERENCES_SCHEMA = 3;
|
|
51
77
|
export const REFERENCES_FILE = "references.json";
|
|
52
78
|
|
|
53
79
|
/** The lookup cache's place in a paper: committed, beside the paper's other reproduction files. */
|
|
@@ -65,44 +91,127 @@ const text = (files: Files, p: string): string | null => {
|
|
|
65
91
|
return b === null ? null : new TextDecoder().decode(b);
|
|
66
92
|
};
|
|
67
93
|
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
/** `xs` without the elements whose `key` an earlier one has, in order. */
|
|
95
|
+
const distinctBy = <T>(xs: readonly T[], key: (x: T) => string): readonly T[] =>
|
|
96
|
+
xs.filter((x, i) => xs.findIndex((o) => key(o) === key(x)) === i);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The bibliography the build checks: the databases bibtex opened, in the order it opened them, and
|
|
100
|
+
* their entries as the bibtex reader read them. The verdicts are recorded one per entry, in this
|
|
101
|
+
* order: a verdict is about an entry, not about its key.
|
|
102
|
+
*/
|
|
103
|
+
export interface CheckedBibliography {
|
|
104
|
+
/** The build's record these databases were read from. */
|
|
105
|
+
readonly record: SourcesRecord;
|
|
106
|
+
readonly databases: readonly [ReadDatabase, ...ReadDatabase[]];
|
|
107
|
+
readonly entries: readonly RecordedEntry[];
|
|
108
|
+
/** The keys bibtex typeset, in the order it did: the `.bbl`'s `\bibitem`s. */
|
|
109
|
+
readonly typeset: readonly string[];
|
|
74
110
|
}
|
|
75
111
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
112
|
+
/** What reading the bibliography takes: the record's readers, and the bibtex reader. */
|
|
113
|
+
export type BibliographyDeps = RecordedReadDeps;
|
|
114
|
+
|
|
115
|
+
/** What a paper's record comes to for the references. */
|
|
116
|
+
export type BibliographyRead =
|
|
117
|
+
| { readonly kind: "read"; readonly bibliography: CheckedBibliography }
|
|
118
|
+
/** No record of the build, an unreadable one, or one the paper has changed since. */
|
|
119
|
+
| {
|
|
120
|
+
readonly kind: "no-record";
|
|
121
|
+
readonly record: Exclude<PaperRecord, { readonly kind: "fresh" }>;
|
|
122
|
+
}
|
|
123
|
+
/** bibtex opened no database: the build ran none, or the paper names none. */
|
|
124
|
+
| { readonly kind: "nothing" }
|
|
125
|
+
/** Databases bibtex opened that are not in the paper directory: it found them on its search path. */
|
|
126
|
+
| { readonly kind: "unread"; readonly databases: readonly string[] }
|
|
127
|
+
/** The build was not given a reader. */
|
|
128
|
+
| { readonly kind: "not-wired" };
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The bibliography the build recorded for the paper in `dir`: the databases bibtex opened, read by
|
|
132
|
+
* the bibtex reader. Only a fresh record answers (`paperRecord`).
|
|
133
|
+
*/
|
|
134
|
+
export function readBibliography(
|
|
135
|
+
dir: string,
|
|
136
|
+
deps: BibliographyDeps,
|
|
137
|
+
): BibliographyRead {
|
|
138
|
+
const rec = recordedBibliography(dir, deps);
|
|
139
|
+
if (rec.kind === "unrecorded")
|
|
140
|
+
return { kind: "no-record", record: rec.record };
|
|
141
|
+
if (rec.unread.length > 0) return { kind: "unread", databases: rec.unread };
|
|
142
|
+
const [first, ...rest] = rec.databases;
|
|
143
|
+
const bibtex = rec.record.bibtex;
|
|
144
|
+
if (first === undefined || !bibtex.ran) return { kind: "nothing" };
|
|
145
|
+
return {
|
|
146
|
+
kind: "read",
|
|
147
|
+
bibliography: {
|
|
148
|
+
record: rec.record,
|
|
149
|
+
databases: [first, ...rest],
|
|
150
|
+
entries: entriesOfDatabases(rec.databases),
|
|
151
|
+
typeset: bibtex.keys,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** A paper's bibliography by its directory — what the build's steps are handed by the composition root. */
|
|
157
|
+
export type ReadBibliography = (dir: string) => BibliographyRead;
|
|
158
|
+
|
|
159
|
+
/** `readBibliography` over `deps`, as a `ReadBibliography`. */
|
|
160
|
+
export const bibliographyReader =
|
|
161
|
+
(deps: BibliographyDeps): ReadBibliography =>
|
|
162
|
+
(dir) =>
|
|
163
|
+
readBibliography(dir, deps);
|
|
164
|
+
|
|
165
|
+
/** No reader: the build was not given one. Never "no bibliography" — it says so. */
|
|
166
|
+
export const notWiredBibliography: ReadBibliography = () => ({
|
|
167
|
+
kind: "not-wired",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The keys bibtex typeset that no entry the reader read has — an entry behind `%` or inside
|
|
172
|
+
* `@comment{…}`, which bibtex reads and the reader does not. In the order bibtex typeset them, each
|
|
173
|
+
* once. bibtex's keys are case-insensitive (`\cite{SMITH}` typesets `\bibitem{SMITH}` from an entry
|
|
174
|
+
* `smith`, measured), so they are compared so.
|
|
175
|
+
*/
|
|
176
|
+
export function unseenKeys(b: CheckedBibliography): readonly string[] {
|
|
177
|
+
const read = new Set(b.entries.map((e) => e.entry.key.toLowerCase()));
|
|
178
|
+
return distinctBy(
|
|
179
|
+
b.typeset.filter((k) => !read.has(k.toLowerCase())),
|
|
180
|
+
(k) => k.toLowerCase(),
|
|
181
|
+
);
|
|
86
182
|
}
|
|
87
183
|
|
|
88
|
-
|
|
89
|
-
|
|
184
|
+
/** The bytes of the databases checked, in order: what a later lint compares to see the verdicts are current. */
|
|
185
|
+
export const bibHash = (b: CheckedBibliography): string =>
|
|
186
|
+
sha256Hex(
|
|
187
|
+
new TextEncoder().encode(
|
|
188
|
+
b.databases
|
|
189
|
+
.map((d) => d.bib.text.slice(d.bib.body.start, d.bib.body.end))
|
|
190
|
+
.join("\n"),
|
|
191
|
+
),
|
|
192
|
+
);
|
|
90
193
|
|
|
91
194
|
/** What `_build/references.json` holds. */
|
|
92
195
|
export interface ReferencesDocument {
|
|
93
196
|
readonly schema: number;
|
|
94
|
-
readonly bib: {
|
|
197
|
+
readonly bib: {
|
|
198
|
+
readonly sources: readonly string[];
|
|
199
|
+
readonly sha256: string;
|
|
200
|
+
};
|
|
95
201
|
readonly status: "checked" | "not-checked";
|
|
96
202
|
readonly why?: string;
|
|
97
203
|
readonly entries: readonly EntryVerdict[];
|
|
98
204
|
}
|
|
99
205
|
|
|
100
206
|
export const documentOf = (
|
|
101
|
-
bib:
|
|
207
|
+
bib: CheckedBibliography,
|
|
102
208
|
check: ReferencesCheck,
|
|
103
209
|
): ReferencesDocument => ({
|
|
104
210
|
schema: REFERENCES_SCHEMA,
|
|
105
|
-
bib: {
|
|
211
|
+
bib: {
|
|
212
|
+
sources: bib.databases.map((d) => d.name),
|
|
213
|
+
sha256: bibHash(bib),
|
|
214
|
+
},
|
|
106
215
|
...(check.kind === "checked"
|
|
107
216
|
? { status: "checked" as const, entries: check.entries }
|
|
108
217
|
: { status: "not-checked" as const, why: check.why, entries: [] }),
|
|
@@ -156,32 +265,66 @@ const newAnswers = (before: LookupCache, after: LookupCache): number => {
|
|
|
156
265
|
);
|
|
157
266
|
};
|
|
158
267
|
|
|
159
|
-
/** Run the checker; a throw is `not-checked`, with the cache as it was. */
|
|
268
|
+
/** Run the checker over the bibliography's entries; a throw is `not-checked`, with the cache as it was. */
|
|
160
269
|
async function run(
|
|
161
270
|
check: CheckReferences,
|
|
162
|
-
bib:
|
|
271
|
+
bib: CheckedBibliography,
|
|
163
272
|
cache: LookupCache,
|
|
164
273
|
): Promise<ReferencesRun> {
|
|
165
274
|
try {
|
|
166
|
-
return await check(
|
|
275
|
+
return await check(
|
|
276
|
+
bib.entries.map((f) => f.entry),
|
|
277
|
+
cache,
|
|
278
|
+
);
|
|
167
279
|
} catch (e) {
|
|
168
280
|
return { check: { kind: "not-checked", why: messageOf(e) }, cache };
|
|
169
281
|
}
|
|
170
282
|
}
|
|
171
283
|
|
|
284
|
+
/** Why the build's record cannot say which databases were read, in the words a note carries. */
|
|
285
|
+
function noRecord(
|
|
286
|
+
r: Extract<BibliographyRead, { readonly kind: "no-record" }>["record"],
|
|
287
|
+
): string {
|
|
288
|
+
switch (r.kind) {
|
|
289
|
+
case "none":
|
|
290
|
+
return r.unreadable === null
|
|
291
|
+
? "the build recorded no sources (_build/sources.json was not written)"
|
|
292
|
+
: `_build/sources.json cannot be used (${r.unreadable})`;
|
|
293
|
+
case "stale":
|
|
294
|
+
return `the paper changed since the build recorded it (${describeChanges(r.changed)})`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** The note for a paper whose bibliography was not read. */
|
|
299
|
+
function unreadNote(
|
|
300
|
+
read: Exclude<BibliographyRead, { readonly kind: "read" }>,
|
|
301
|
+
): string {
|
|
302
|
+
switch (read.kind) {
|
|
303
|
+
case "no-record":
|
|
304
|
+
return `references NOT checked — ${noRecord(read.record)}; lint will say so`;
|
|
305
|
+
case "nothing":
|
|
306
|
+
return "no bibliography database — bibtex opened none, nothing to check";
|
|
307
|
+
case "unread":
|
|
308
|
+
return `references NOT checked — ${read.databases.join(", ")}, which bibtex opened, is not in the paper directory; lint will say so`;
|
|
309
|
+
case "not-wired":
|
|
310
|
+
return "references NOT checked — no bibliography reader was wired into this build; lint will say so";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
172
314
|
/**
|
|
173
|
-
* The build step's work: check the bibliography, serving what it can from the
|
|
174
|
-
* cache, and record the verdicts. Returns the one-line note the build prints. A
|
|
175
|
-
* throws, or a cache that does not parse, is recorded as `not-checked` — the step
|
|
176
|
-
* never refuses. A cache that does not parse is left as it is, and named.
|
|
315
|
+
* The build step's work: check the bibliography the build recorded, serving what it can from the
|
|
316
|
+
* paper's lookup cache, and record the verdicts. Returns the one-line note the build prints. A
|
|
317
|
+
* checker that throws, or a cache that does not parse, is recorded as `not-checked` — the step
|
|
318
|
+
* reports, it never refuses. A cache that does not parse is left as it is, and named.
|
|
177
319
|
*/
|
|
178
320
|
export async function recordReferences(
|
|
179
321
|
files: Files,
|
|
180
322
|
paperDir: string,
|
|
323
|
+
read: BibliographyRead,
|
|
181
324
|
check: CheckReferences,
|
|
182
325
|
): Promise<string> {
|
|
183
|
-
|
|
184
|
-
|
|
326
|
+
if (read.kind !== "read") return unreadNote(read);
|
|
327
|
+
const bib = read.bibliography;
|
|
185
328
|
const record = (c: ReferencesCheck): ReferencesDocument => {
|
|
186
329
|
const doc = documentOf(bib, c);
|
|
187
330
|
files.writeAtomic(
|
|
@@ -199,7 +342,7 @@ export async function recordReferences(
|
|
|
199
342
|
return notChecked(
|
|
200
343
|
`${LOOKUP_CACHE_FILE} cannot be read (${cache.error}) — fix it, or delete it to ask every question again`,
|
|
201
344
|
);
|
|
202
|
-
const result = await run(check, bib
|
|
345
|
+
const result = await run(check, bib, cache.value);
|
|
203
346
|
if (result.check.kind === "not-checked") return notChecked(result.check.why);
|
|
204
347
|
const doc = record(result.check);
|
|
205
348
|
const fetched = newAnswers(cache.value, result.cache);
|
|
@@ -208,6 +351,11 @@ export async function recordReferences(
|
|
|
208
351
|
callerPath(lookupCachePath(paperDir)),
|
|
209
352
|
new TextEncoder().encode(serializeLookupCache(result.cache)),
|
|
210
353
|
);
|
|
354
|
+
return summary(doc, fetched);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The note a checked run prints: how many entries, how many failing, what was fetched. */
|
|
358
|
+
function summary(doc: ReferencesDocument, fetched: number): string {
|
|
211
359
|
const bad = doc.entries.filter(
|
|
212
360
|
(e) => e.exists === "false" || e.authors === "mismatch",
|
|
213
361
|
).length;
|
|
@@ -17,6 +17,7 @@ import { fileURLToPath } from "node:url";
|
|
|
17
17
|
import { describe, expect, it } from "vitest";
|
|
18
18
|
import { latexReader } from "./adapters/latex/index.ts";
|
|
19
19
|
import { nodeFiles } from "./adapters/node/index.ts";
|
|
20
|
+
import { sourcesCodec } from "./adapters/sources-record/index.ts";
|
|
20
21
|
import {
|
|
21
22
|
bandOf,
|
|
22
23
|
rateOf,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
standingOf,
|
|
37
38
|
} from "./register-bands.ts";
|
|
38
39
|
import { readPaper } from "./tex-paper.ts";
|
|
40
|
+
import { withRecord } from "../test/recorded-fixture.ts";
|
|
39
41
|
import { useTempDir, writeTree } from "../test/support.ts";
|
|
40
42
|
|
|
41
43
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -56,8 +58,9 @@ const aidc = (() => {
|
|
|
56
58
|
const sourceOf = (paper: string): string => {
|
|
57
59
|
const file = join(CORPUS, paper, "paper.tex");
|
|
58
60
|
return readPaper(file, readFileSync(file, "utf8"), {
|
|
59
|
-
files: nodeFiles,
|
|
61
|
+
files: withRecord(nodeFiles, join(CORPUS, paper)),
|
|
60
62
|
latex: latexReader,
|
|
63
|
+
codec: sourcesCodec,
|
|
61
64
|
}).text;
|
|
62
65
|
};
|
|
63
66
|
|
|
@@ -286,6 +289,7 @@ describe("the rules, as ESLint calls them", () => {
|
|
|
286
289
|
files: nodeFiles,
|
|
287
290
|
venuesDir: presetsDir(),
|
|
288
291
|
latex: latexReader,
|
|
292
|
+
codec: sourcesCodec,
|
|
289
293
|
};
|
|
290
294
|
registerBandRules(deps)
|
|
291
295
|
[rule].create({
|
package/src/register-bands.ts
CHANGED
|
@@ -40,7 +40,7 @@ import type { Emphasis, Passage, Span } from "./domain/tex-document.ts";
|
|
|
40
40
|
import type { LatexReader } from "./ports/latex.ts";
|
|
41
41
|
import { paperPreset } from "./presets.ts";
|
|
42
42
|
import { MIN_WORDS, startOf } from "./register.ts";
|
|
43
|
-
import { readPaper, reportInPaper } from "./tex-paper.ts";
|
|
43
|
+
import { readPaper, reportInPaper, type ProseDeps } from "./tex-paper.ts";
|
|
44
44
|
import {
|
|
45
45
|
ruleDocsUrl,
|
|
46
46
|
type Located,
|
|
@@ -49,10 +49,8 @@ import {
|
|
|
49
49
|
} from "./tex-venue-rules.ts";
|
|
50
50
|
import type { VenueRuleDeps } from "./venue-rules.ts";
|
|
51
51
|
|
|
52
|
-
/** What the three rules read: the preset store, and the
|
|
53
|
-
export interface RegisterBandDeps extends VenueRuleDeps {
|
|
54
|
-
readonly latex: LatexReader;
|
|
55
|
-
}
|
|
52
|
+
/** What the three rules read: the preset store, the LaTeX reader, and the paper's files as the build recorded them. */
|
|
53
|
+
export interface RegisterBandDeps extends VenueRuleDeps, ProseDeps {}
|
|
56
54
|
|
|
57
55
|
export type RegisterBandRuleName =
|
|
58
56
|
"contrast-frames" | "claim-emphasis" | "relation-markers";
|
package/src/register.test.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { describe, expect, it } from "vitest";
|
|
8
8
|
import { latexReader } from "./adapters/latex/index.ts";
|
|
9
9
|
import { memoryFiles } from "./adapters/memory/index.ts";
|
|
10
|
+
import { sourcesCodec } from "./adapters/sources-record/index.ts";
|
|
10
11
|
import {
|
|
11
12
|
judgeRegister,
|
|
12
13
|
measureRegister,
|
|
@@ -125,7 +126,11 @@ describe("the rule — reported at the start of the body, the option read from t
|
|
|
125
126
|
}
|
|
126
127
|
const run = (src: string, options?: readonly unknown[]): Seen[] => {
|
|
127
128
|
const seen: Seen[] = [];
|
|
128
|
-
registerRule({
|
|
129
|
+
registerRule({
|
|
130
|
+
files: memoryFiles({}),
|
|
131
|
+
latex: latexReader,
|
|
132
|
+
codec: sourcesCodec,
|
|
133
|
+
})
|
|
129
134
|
.create({
|
|
130
135
|
filename: "/p/paper.tex",
|
|
131
136
|
...(options === undefined ? {} : { options }),
|
package/src/register.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { Passage, Span } from "./domain/tex-document.ts";
|
|
19
19
|
import { bodySentences, wordsOf } from "./domain/register.ts";
|
|
20
|
-
import { readPaper, reportInPaper, type
|
|
20
|
+
import { readPaper, reportInPaper, type ProseDeps } from "./tex-paper.ts";
|
|
21
21
|
import {
|
|
22
22
|
ruleDocsUrl,
|
|
23
23
|
type Located,
|
|
@@ -125,7 +125,7 @@ const META: TexRuleModule["meta"] = {
|
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
/** The rule, over the body's prose of the whole paper, its includes spliced (`readPaper`). */
|
|
128
|
-
export const registerRule = (deps:
|
|
128
|
+
export const registerRule = (deps: ProseDeps): TexRuleModule => ({
|
|
129
129
|
meta: META,
|
|
130
130
|
create(context) {
|
|
131
131
|
return {
|
package/src/rule-cases.test.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { describe, expect, it } from "vitest";
|
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
import { run, rulePlugins } from "./cli.ts";
|
|
19
19
|
import { sha256Hex } from "./domain/sha256.ts";
|
|
20
|
-
import {
|
|
20
|
+
import { recordText } from "../test/recorded-fixture.ts";
|
|
21
21
|
import { useTempDir, venuePreset, writeTree } from "../test/support.ts";
|
|
22
22
|
|
|
23
23
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -172,32 +172,84 @@ const built = (
|
|
|
172
172
|
|
|
173
173
|
const BIB =
|
|
174
174
|
"@article{a,\n title = {A},\n author = {Doe, J.},\n year = {2026},\n doi = {10.1/x}\n}\n";
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
/** The hash the build records for a bibliography that is BIB alone (`bibHash` over its one text). */
|
|
176
|
+
const sha: string = sha256Hex(new TextEncoder().encode(BIB));
|
|
177
|
+
/** The paper `cited` builds: it cites refs.bib. */
|
|
178
|
+
const CITING = tex("See~\\cite{a}.\n\\bibliography{refs}");
|
|
179
|
+
const digest = (text: string): string =>
|
|
180
|
+
sha256Hex(new TextEncoder().encode(text));
|
|
181
|
+
/**
|
|
182
|
+
* A paper citing refs.bib that was built — TeX read paper.tex, bibtex opened refs.bib and typeset `a`
|
|
183
|
+
* — with its references record (`status`, `entries`) or none.
|
|
184
|
+
*/
|
|
177
185
|
const cited = (
|
|
178
186
|
body: Record<string, unknown> | null,
|
|
179
187
|
bibSha = sha,
|
|
180
188
|
): Record<string, string> =>
|
|
181
|
-
paper(
|
|
189
|
+
paper(CITING, {
|
|
182
190
|
[`${P}/refs.bib`]: BIB,
|
|
191
|
+
[`${P}/_build/sources.json`]: JSON.stringify({
|
|
192
|
+
schema: 1,
|
|
193
|
+
inputs: [{ path: "paper.tex", role: "body" }],
|
|
194
|
+
written: [],
|
|
195
|
+
bibdata: ["refs"],
|
|
196
|
+
bibtex: {
|
|
197
|
+
ran: true,
|
|
198
|
+
databases: ["refs.bib"],
|
|
199
|
+
keys: ["a"],
|
|
200
|
+
exit: 0,
|
|
201
|
+
errors: [],
|
|
202
|
+
},
|
|
203
|
+
sha256: { "paper.tex": digest(CITING), "refs.bib": digest(BIB) },
|
|
204
|
+
}),
|
|
183
205
|
...(body === null
|
|
184
206
|
? {}
|
|
185
207
|
: {
|
|
186
208
|
[`${P}/_build/references.json`]: JSON.stringify({
|
|
187
|
-
schema:
|
|
188
|
-
bib: {
|
|
209
|
+
schema: 3,
|
|
210
|
+
bib: { sources: ["refs.bib"], sha256: bibSha },
|
|
189
211
|
...body,
|
|
190
212
|
}),
|
|
191
213
|
}),
|
|
192
214
|
});
|
|
193
|
-
/** A paper whose bibliography is written inline, by `filecontents
|
|
215
|
+
/** A paper whose bibliography is written inline, by `filecontents*[overwrite]`. */
|
|
194
216
|
const inlineBib = (entry: string): string =>
|
|
195
|
-
`\\documentclass{article}\n\\begin{filecontents*}{refs.bib}\n${entry}\n\\end{filecontents*}\n\\begin{document}\nSee~\\cite{a}.\n\\bibliography{refs}\n\\end{document}\n`;
|
|
217
|
+
`\\documentclass{article}\n\\begin{filecontents*}[overwrite]{refs.bib}\n${entry}\n\\end{filecontents*}\n\\begin{document}\nSee~\\cite{a}.\n\\bibliography{refs}\n\\end{document}\n`;
|
|
218
|
+
/** That paper as a build leaves it: the `.bib` TeX wrote from the block, and the record of the build. */
|
|
219
|
+
const inlineBuilt = (entry: string): Record<string, string> => {
|
|
220
|
+
const paperTex = inlineBib(entry);
|
|
221
|
+
return paper(paperTex, {
|
|
222
|
+
[`${P}/refs.bib`]: `${entry}\n`,
|
|
223
|
+
[`${P}/_build/sources.json`]: recordText(
|
|
224
|
+
{ "paper.tex": paperTex, "refs.bib": `${entry}\n` },
|
|
225
|
+
{
|
|
226
|
+
inputs: [["paper.tex", "body"]],
|
|
227
|
+
written: ["refs.bib"],
|
|
228
|
+
databases: ["refs.bib"],
|
|
229
|
+
},
|
|
230
|
+
),
|
|
231
|
+
});
|
|
232
|
+
};
|
|
196
233
|
const checked = (exists: string, authors: string) => ({
|
|
197
234
|
status: "checked",
|
|
198
235
|
entries: [{ key: "a", exists, authors }],
|
|
199
236
|
});
|
|
200
237
|
|
|
238
|
+
// ── the build's record of the sources: what `paperlint build` would write after TeX read paper.tex ───
|
|
239
|
+
|
|
240
|
+
/** A paper that was built: the record of its one file, hashed as it stands. */
|
|
241
|
+
const sourced = (paperTex: string): Record<string, string> =>
|
|
242
|
+
paper(paperTex, {
|
|
243
|
+
[`${P}/_build/sources.json`]: JSON.stringify({
|
|
244
|
+
schema: 1,
|
|
245
|
+
inputs: [{ path: "paper.tex", role: "body" }],
|
|
246
|
+
written: [],
|
|
247
|
+
bibdata: [],
|
|
248
|
+
bibtex: { ran: false },
|
|
249
|
+
sha256: { "paper.tex": sha256Hex(new TextEncoder().encode(paperTex)) },
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
|
|
201
253
|
// ── a BlindConf paper, for the LaTeX venue rules ──────────────────────────────────────────────
|
|
202
254
|
|
|
203
255
|
const BLIND_PAPER = onBlindConf(P);
|
|
@@ -517,7 +569,7 @@ const CASES: Readonly<Record<string, RuleCases>> = {
|
|
|
517
569
|
tree: cited(checked("true", "mismatch")),
|
|
518
570
|
file: TEX_FILE,
|
|
519
571
|
severity: 2,
|
|
520
|
-
line: 1,
|
|
572
|
+
line: 1, // refs.bib is a file the author keeps: the top of paper.tex, naming it
|
|
521
573
|
},
|
|
522
574
|
silent: cited(checked("true", "match")),
|
|
523
575
|
},
|
|
@@ -526,7 +578,7 @@ const CASES: Readonly<Record<string, RuleCases>> = {
|
|
|
526
578
|
tree: cited(checked("false", "match")),
|
|
527
579
|
file: TEX_FILE,
|
|
528
580
|
severity: 2,
|
|
529
|
-
line: 1,
|
|
581
|
+
line: 1, // refs.bib is a file the author keeps: the top of paper.tex, naming it
|
|
530
582
|
},
|
|
531
583
|
silent: cited(checked("true", "match")),
|
|
532
584
|
},
|
|
@@ -543,6 +595,10 @@ const CASES: Readonly<Record<string, RuleCases>> = {
|
|
|
543
595
|
},
|
|
544
596
|
silent: cited(checked("true", "match")),
|
|
545
597
|
},
|
|
598
|
+
"paper/sources-fresh": {
|
|
599
|
+
reports: { tree: paper(tex("x")), file: TEX_FILE, severity: 1, line: 1 },
|
|
600
|
+
silent: sourced(tex("x")),
|
|
601
|
+
},
|
|
546
602
|
"review/frontmatter": {
|
|
547
603
|
reports: {
|
|
548
604
|
tree: paper(tex("x"), {
|
|
@@ -697,12 +753,12 @@ const CASES: Readonly<Record<string, RuleCases>> = {
|
|
|
697
753
|
},
|
|
698
754
|
"bib/reachable-entry": {
|
|
699
755
|
reports: {
|
|
700
|
-
tree:
|
|
756
|
+
tree: inlineBuilt("@book{a,\n title = {A},\n year = {2026}\n}"),
|
|
701
757
|
file: TEX_FILE,
|
|
702
758
|
severity: 1,
|
|
703
759
|
line: 3,
|
|
704
760
|
},
|
|
705
|
-
silent:
|
|
761
|
+
silent: inlineBuilt(BIB.trimEnd()),
|
|
706
762
|
},
|
|
707
763
|
"pdf/fresh": {
|
|
708
764
|
reports: onPdf(built({ pdf_sha256: "0".repeat(64) })),
|
package/src/rule-docs.test.ts
CHANGED
|
@@ -19,7 +19,6 @@ const PAGES = "https://github.com/zernie/paperlint/blob/main/";
|
|
|
19
19
|
|
|
20
20
|
/** Rules shipped before per-rule pages, each waiting for its page in #131. Only ever shrinks. */
|
|
21
21
|
const AWAITING_PAGE: ReadonlySet<string> = new Set([
|
|
22
|
-
"bib/reachable-entry",
|
|
23
22
|
"paper/author-list",
|
|
24
23
|
"paper/cite-exists",
|
|
25
24
|
"paper/figure-ref-style",
|