paperlint 7.2.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 +41 -30
- package/dist/build.js.map +1 -1
- package/dist/claim-provenance.d.ts +2 -2
- package/dist/cli.d.ts +9 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +43 -68
- 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/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +10 -13
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/eslint-rules/paper-context.d.ts.map +1 -1
- package/dist/eslint-rules/paper-context.js +6 -5
- 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 +34 -96
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/papers.d.ts +1 -3
- package/dist/eslint-rules/papers.d.ts.map +1 -1
- package/dist/eslint-rules/papers.js +2 -4
- package/dist/eslint-rules/papers.js.map +1 -1
- package/dist/eslint-rules/tex-build.d.ts.map +1 -1
- package/dist/eslint-rules/tex-build.js +8 -25
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/init.d.ts +1 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -9
- package/dist/init.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/lib/markdown.d.ts +5 -81
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +5 -116
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +0 -2
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/lib/skill-eval-fixture.d.ts +6 -4
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -1
- package/dist/lib/skill-eval-fixture.js +25 -18
- package/dist/lib/skill-eval-fixture.js.map +1 -1
- package/dist/new-paper.d.ts +5 -11
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +12 -23
- package/dist/new-paper.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-files.d.ts +4 -4
- 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 +17 -0
- package/dist/paper-prose.d.ts.map +1 -0
- package/dist/paper-prose.js +46 -0
- package/dist/paper-prose.js.map +1 -0
- 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/structure.d.ts.map +1 -1
- package/dist/structure.js +4 -3
- package/dist/structure.js.map +1 -1
- 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/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/configuration.md +18 -12
- package/docs/design/paper-sources.md +197 -0
- package/docs/e2e.md +4 -6
- package/docs/package-shape-options.md +1 -1
- package/docs/prior-art/paper-folder-scaffolding.md +24 -29
- 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/tex/claim-provenance.md +0 -1
- package/docs/rules.md +62 -61
- package/eslint-rules/latex-language.harness.mjs +3 -3
- package/eslint-rules/latex-language.ts +10 -13
- package/eslint-rules/paper-context.ts +6 -5
- package/eslint-rules/paper-typography.test.mjs +29 -157
- package/eslint-rules/paper-typography.ts +35 -104
- package/eslint-rules/papers.harness.mjs +1 -1
- package/eslint-rules/papers.ts +2 -4
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/tex-build.ts +8 -25
- package/eslint.config.mjs +22 -1
- 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/hooks/hooks.harness.ts +15 -15
- package/hooks/paper-edit-guard.hook.mjs +5 -8
- package/hooks/paper-edit-guard.test.ts +12 -3
- package/hooks/paper-skills-nudge.hook.mjs +2 -3
- package/hooks/paper-skills-nudge.test.ts +13 -3
- package/hooks/paper-status-gates.hook.mjs +2 -2
- package/hooks/paper-status-gates.test.ts +14 -2
- package/lib/markdown.test.mjs +5 -60
- package/lib/markdown.ts +9 -136
- package/lib/paper-config.ts +0 -2
- package/lib/skill-checks.test.mjs +4 -1
- package/lib/skill-eval-fixture.ts +25 -18
- package/package.json +2 -12
- package/presets/realm.jsonc +1 -1
- package/scripts/check.test.ts +23 -1
- package/scripts/check.ts +7 -3
- package/scripts/coverage-config.test.ts +19 -10
- package/scripts/harness-api.frozen.json +0 -5
- package/scripts/layer-legacy-frozen.test.ts +41 -2
- package/scripts/layer-legacy-frozen.ts +31 -2
- package/skills/cold-read-diff/SKILL.md +3 -3
- package/skills/cold-read-diff/SKILL.md.spec.ts +2 -2
- package/skills/grade-paper-writing/SKILL.md +15 -22
- package/skills/grade-paper-writing/SKILL.md.spec.ts +14 -21
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +40 -26
- package/skills/grade-paper-writing/prose-lint.mjs +117 -278
- package/skills/grade-paper-writing/prose-lint.test.ts +230 -79
- package/skills/paper-adversarial-review/SKILL.md +2 -2
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +9 -12
- package/skills/paper-pipeline/SKILL.md.spec.ts +8 -11
- package/skills/paper-pipeline/description-language.eval.mjs +25 -15
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +25 -15
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +25 -15
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +27 -16
- package/skills/paper-pipeline/pipeline-language.eval.mjs +25 -15
- package/skills/paper-pipeline/references/acceptance-gate.md +16 -20
- package/skills/paper-pipeline/references/artifact-checklist.md +4 -5
- package/skills/paper-pipeline/references/pipeline-status-template.md +1 -1
- package/skills/paper-pipeline/scripts/README.md +0 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +91 -105
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +85 -241
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +166 -153
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +3 -3
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/SKILL.md +2 -2
- package/skills/render-paper/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/extract-pdf-facts.mjs +2 -2
- package/skills/tighten-paper/SKILL.md +29 -42
- package/skills/tighten-paper/SKILL.md.spec.ts +28 -41
- 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 -32
- package/skills/verify-citations/scripts/bib-authors.cli.test.ts +138 -43
- package/skills/verify-citations/scripts/bib-authors.d.mts +22 -2
- package/skills/verify-citations/scripts/bib-authors.mjs +54 -193
- package/skills/verify-citations/scripts/bib-authors.test.mjs +1 -44
- 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.harness.mjs +2 -2
- package/src/build.test.ts +211 -1
- package/src/build.ts +101 -41
- package/src/claim-provenance.test.ts +33 -7
- package/src/claim-provenance.ts +2 -2
- package/src/cli.harness.mjs +46 -56
- package/src/cli.test.ts +44 -48
- package/src/cli.ts +51 -82
- 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/folder-venue-rule.test.ts +1 -1
- package/src/heading-case.test.ts +24 -10
- package/src/init.test.ts +6 -7
- package/src/init.ts +3 -22
- package/src/latex-log.ts +20 -0
- package/src/latex-loop.harness.ts +1 -0
- package/src/latex-loop.ts +2 -0
- package/src/lint-fix.test.ts +10 -4
- package/src/linted-set.test.ts +9 -1
- package/src/new-paper.harness.mjs +12 -30
- package/src/new-paper.test.ts +15 -11
- package/src/new-paper.ts +11 -30
- package/src/package-dirs.ts +3 -2
- package/src/paper-files.test.ts +5 -4
- package/src/paper-files.ts +4 -4
- package/src/paper-includes.test.ts +157 -38
- package/src/paper-includes.ts +39 -35
- package/src/paper-prose.test.ts +78 -0
- package/src/paper-prose.ts +67 -0
- 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/parse-args.test.ts +0 -1
- 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/structure.harness.ts +5 -11
- package/src/structure.ts +4 -3
- 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/src/types.ts +0 -2
- 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/fixtures/real-markdown-paper/baseline.json +0 -22
- package/fixtures/real-markdown-paper/baseline.ts +0 -70
- package/skills/paper-pipeline/PIPELINE-MAP.md +0 -381
- package/skills/paper-pipeline/pipeline-firing.baseline.json +0 -270
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +0 -585
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +0 -492
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +0 -397
- package/skills/paper-pipeline/scripts/artifact-coverage.test.ts +0 -86
- package/skills/paper-pipeline/scripts/check-provenance.mjs +0 -184
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +0 -135
- package/skills/paper-pipeline/scripts/population-map.mjs +0 -188
- package/skills/paper-pipeline/scripts/population-map.test.mjs +0 -154
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +0 -266
- package/skills/paper-pipeline/scripts/round-diff.harness.ts +0 -907
- package/skills/paper-pipeline/scripts/round-diff.mjs +0 -583
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +0 -159
- package/skills/tighten-paper/__snapshots__/structure.test.ts.snap +0 -133
- package/skills/tighten-paper/structure.mjs +0 -364
- package/skills/tighten-paper/structure.test.ts +0 -117
- package/templates/paper/paper.md +0 -4
|
@@ -3,34 +3,32 @@
|
|
|
3
3
|
* extract-ref-facts.mjs — parse a paper's bibliography, ask the registries and write FACTS to JSON.
|
|
4
4
|
* It judges nothing.
|
|
5
5
|
*
|
|
6
|
-
* Usage: node extract-ref-facts.mjs <paper dir|paper.
|
|
6
|
+
* Usage: node extract-ref-facts.mjs <paper dir|paper.tex|refs.bib> [--offline] [--refresh]
|
|
7
7
|
* [--cache=PATH] [--out=PATH] [--quiet]
|
|
8
|
-
* Exit: 0 — facts written · 1 — nothing to write (no source,
|
|
8
|
+
* Exit: 0 — facts written · 1 — nothing to write (no source, a source that is neither a .bib nor
|
|
9
|
+
* a .tex, no entries).
|
|
9
10
|
*
|
|
10
11
|
* WHY THE SPLIT. Before 2026-08-26 measurement and judgement lived in one script
|
|
11
12
|
* (`verify-refs.mjs`, 20 emit sites, 8 kinds of findings) — that is, on the fifth rung of the
|
|
12
13
|
* ladder in `the consumer's papers CLAUDE.md`. The judgement moved into ESLint rules
|
|
13
14
|
* (`eslint-rules/ref-facts.mjs`): a registry of rules, severity from config, `eslint-disable` with a
|
|
14
|
-
* reason, `file:line:col` positions. What stayed here is the plumbing: parsing
|
|
15
|
-
*
|
|
15
|
+
* reason, `file:line:col` positions. What stayed here is the plumbing: parsing `.bib`, the network,
|
|
16
|
+
* parsing the registries' responses.
|
|
16
17
|
*
|
|
17
18
|
* The precedent in this same knowledge base is `render-paper/extract-pdf-facts.mjs` +
|
|
18
19
|
* `eslint-rules/pdf-facts.mjs`. The boundary is exactly the same: the script goes out into the
|
|
19
20
|
* world, the rule passes the judgement.
|
|
20
21
|
*
|
|
21
|
-
* 🔴 WHY THE RULE CANNOT LIVE OVER THE
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `paper.md`; that is why every entry in the facts carries the `line` of its source, and the rule
|
|
26
|
-
* prints it in the message.
|
|
22
|
+
* 🔴 WHY THE RULE CANNOT LIVE OVER THE BIBLIOGRAPHY ITSELF. No ESLint language plugin parses
|
|
23
|
+
* `.bib`, so the rule's input is the facts JSON. The price is known and written down: a finding is
|
|
24
|
+
* addressed into the facts, not into a line of the `.bib`; that is why every entry in the facts
|
|
25
|
+
* carries the `line` of its source, and the rule prints it in the message.
|
|
27
26
|
*
|
|
28
27
|
* 🔴 WHAT THIS MOVE FIXED (both legs were DEAD, measured 2026-08-26):
|
|
29
28
|
*
|
|
30
|
-
* 1. `.bib` WAS NEVER OPENED. `resolveSource`
|
|
31
|
-
*
|
|
32
|
-
* "or .bib
|
|
33
|
-
* "no paper.md, draft.md or .bib … nothing to check".
|
|
29
|
+
* 1. `.bib` WAS NEVER OPENED. `resolveSource` did not list `refs.bib`, although the error text
|
|
30
|
+
* promised "or .bib". The run: `verify-refs.mjs <papers-root>/<paper-b>` →
|
|
31
|
+
* "… or .bib … nothing to check".
|
|
34
32
|
* 2. THE `.bib` PARSER DID NOT TAKE THE REAL FILES. The regex `/@\w+\{([^,]+),([\s\S]*?)\n\}/g`
|
|
35
33
|
* required a `}` on a line of its own, while both of our `refs.bib` close an entry on the line
|
|
36
34
|
* of the last field (`note={arXiv:2310.06770}}`). A run straight at the file: "parsed 0
|
|
@@ -62,23 +60,17 @@
|
|
|
62
60
|
* registry's response, not by a comparison. If the cache kept parsed entries, those readers would
|
|
63
61
|
* stay uncovered while looking covered.
|
|
64
62
|
*/
|
|
65
|
-
import {
|
|
66
|
-
readFileSync,
|
|
67
|
-
writeFileSync,
|
|
68
|
-
existsSync,
|
|
69
|
-
mkdirSync,
|
|
70
|
-
statSync,
|
|
71
|
-
} from "node:fs";
|
|
63
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
72
64
|
import { createHash } from "node:crypto";
|
|
73
65
|
import { join, resolve, dirname, basename } from "node:path";
|
|
74
|
-
import {
|
|
66
|
+
import { latexReader } from "#src/adapters/latex/index";
|
|
67
|
+
import { nodeFiles } from "#src/adapters/node/index";
|
|
68
|
+
import { sourcesCodec } from "#src/adapters/sources-record/index";
|
|
69
|
+
import { absolutePath } from "#src/domain/paths";
|
|
70
|
+
import { bibReader } from "#src/adapters/bibtex/index";
|
|
71
|
+
import { bibliographyAt, bibliographyUnreadWhy } from "#src/paper-sources";
|
|
75
72
|
import { isMain } from "./consumer.mjs";
|
|
76
73
|
|
|
77
|
-
// Markup is parsed with a parser (`CLAUDE.md`, 2026-08-11). We fail rather than degrade: without
|
|
78
|
-
// the parser the reference list would not be found at all, and the facts would come out empty —
|
|
79
|
-
// that is, a clean verdict about a paper that in fact has forty references.
|
|
80
|
-
requireMarkdown();
|
|
81
|
-
|
|
82
74
|
const ROOT = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
83
75
|
const CROSSREF = "https://api.crossref.org/works/";
|
|
84
76
|
const ARXIV = "https://export.arxiv.org/api/query?id_list=";
|
|
@@ -89,135 +81,12 @@ const UA =
|
|
|
89
81
|
|
|
90
82
|
export const SCHEMA = 1;
|
|
91
83
|
|
|
92
|
-
// ── parsing: the reference list in markdown ──────────────────────────────────
|
|
93
|
-
//
|
|
94
|
-
// The same shape that `repro/md2submission.py` reads, deliberately: that script turns the list into
|
|
95
|
-
// the .bib actually uploaded to the venue, so whatever it counts as an entry is an entry.
|
|
96
|
-
// `^\d{1,2}. ` opens an entry, an indent continues it, a blank line closes it.
|
|
97
|
-
|
|
98
|
-
const REF_OPEN = /^(\d{1,2})\.\s+(.*)$/;
|
|
99
|
-
/** Working notes that never reach the PDF; md2submission strips them, so we do too. */
|
|
100
|
-
const BRACKET_NOTE = /\[(VERIFY|ANONYMIZ)[^\]]*\]/g;
|
|
101
|
-
|
|
102
|
-
export function parseMarkdownRefs(text) {
|
|
103
|
-
// The section is cut by the parser. The former `text.split(/^## References\s*$/m)[1]` opened the
|
|
104
|
-
// reference list on a `## References` quoted inside a ```-block — and in this repo papers quote
|
|
105
|
-
// their own markup in chunks.
|
|
106
|
-
const refsHs = mdHeadings(text).filter(
|
|
107
|
-
(h) => h.depth === 2 && /^References\s*$/u.test(h.text),
|
|
108
|
-
);
|
|
109
|
-
if (refsHs.length === 0) return [];
|
|
110
|
-
const nl = text.indexOf("\n", refsHs[0].offset);
|
|
111
|
-
const body = text.slice(
|
|
112
|
-
nl === -1 ? text.length : nl,
|
|
113
|
-
refsHs[1] ? refsHs[1].offset : text.length,
|
|
114
|
-
);
|
|
115
|
-
// The line where the body starts in the source file — so that an entry has an ADDRESS in
|
|
116
|
-
// `paper.md` and not just a number in a list. The rule prints it in the message: the facts file
|
|
117
|
-
// lives in `_build/`, and without the source line a finding would have to be hunted for by eye.
|
|
118
|
-
const bodyLine = text
|
|
119
|
-
.slice(0, nl === -1 ? text.length : nl)
|
|
120
|
-
.split("\n").length;
|
|
121
|
-
// A heading closes the list — appendices come AFTER the bibliography, and without this their
|
|
122
|
-
// numbered prose would be parsed as references. The line numbers come from the parser:
|
|
123
|
-
// `/^#{1,6}\s/` also counted a hash inside a ```-block as a heading.
|
|
124
|
-
const headingLines = new Set(mdHeadings(body).map((h) => h.line));
|
|
125
|
-
const entries = [];
|
|
126
|
-
let cur = null;
|
|
127
|
-
let buf = [];
|
|
128
|
-
let curLine = 0;
|
|
129
|
-
const flush = () => {
|
|
130
|
-
if (cur !== null)
|
|
131
|
-
entries.push({
|
|
132
|
-
n: cur,
|
|
133
|
-
line: curLine,
|
|
134
|
-
raw: buf.join(" ").replace(BRACKET_NOTE, "").trim(),
|
|
135
|
-
});
|
|
136
|
-
cur = null;
|
|
137
|
-
buf = [];
|
|
138
|
-
};
|
|
139
|
-
const lns = body.split("\n");
|
|
140
|
-
for (let i = 0; i < lns.length; i++) {
|
|
141
|
-
const ln = lns[i];
|
|
142
|
-
if (headingLines.has(i)) break;
|
|
143
|
-
const m = REF_OPEN.exec(ln);
|
|
144
|
-
if (m) {
|
|
145
|
-
flush();
|
|
146
|
-
cur = Number(m[1]);
|
|
147
|
-
curLine = bodyLine + i;
|
|
148
|
-
buf = [m[2].trim()];
|
|
149
|
-
} else if (cur !== null && ln.trim()) buf.push(ln.trim());
|
|
150
|
-
else if (cur !== null) flush();
|
|
151
|
-
}
|
|
152
|
-
flush();
|
|
153
|
-
return entries.map(splitEntry).filter(Boolean);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** `A, B, C. *Title.* rest` → authors / title / remainder — the shape md2submission produces. */
|
|
157
|
-
function splitEntry(e) {
|
|
158
|
-
const base = { n: e.n, line: e.line, key: null, raw: e.raw };
|
|
159
|
-
const mt = /\*(.+?)\*/.exec(e.raw);
|
|
160
|
-
if (!mt)
|
|
161
|
-
return {
|
|
162
|
-
...base,
|
|
163
|
-
authors: [],
|
|
164
|
-
truncated: false,
|
|
165
|
-
title: null,
|
|
166
|
-
year: yearIn(e.raw),
|
|
167
|
-
venue_text: e.raw,
|
|
168
|
-
};
|
|
169
|
-
const title = mt[1].trim().replace(/\.$/, "");
|
|
170
|
-
const rawAuthors = splitAuthors(e.raw.slice(0, mt.index));
|
|
171
|
-
const note = e.raw
|
|
172
|
-
.slice(mt.index + mt[0].length)
|
|
173
|
-
.trim()
|
|
174
|
-
.replace(/^,\s*/, "");
|
|
175
|
-
const { authors, truncated } = dropEtAl(rawAuthors);
|
|
176
|
-
return {
|
|
177
|
-
...base,
|
|
178
|
-
authors,
|
|
179
|
-
truncated,
|
|
180
|
-
title,
|
|
181
|
-
year: yearIn(note) ?? yearIn(e.raw),
|
|
182
|
-
venue_text: note,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function splitAuthors(a) {
|
|
187
|
-
const s = a
|
|
188
|
-
.trim()
|
|
189
|
-
.replace(/\.\s*$/, "")
|
|
190
|
-
.trim();
|
|
191
|
-
if (!s) return [];
|
|
192
|
-
return s
|
|
193
|
-
.split(",")
|
|
194
|
-
.map((p) => p.trim())
|
|
195
|
-
.filter(Boolean);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* `Z. Xiang et al.` — the entry deliberately gives a PREFIX of the author list. That is a fact about
|
|
200
|
-
* the entry, not a judgement, so the truncation is removed here and the rule receives an honest
|
|
201
|
-
* prefix plus a flag.
|
|
202
|
-
*/
|
|
203
|
-
const ET_AL = /\bet\s+al\.?$/i;
|
|
204
|
-
function dropEtAl(authors) {
|
|
205
|
-
if (!authors.length || !ET_AL.test(authors[authors.length - 1].trim()))
|
|
206
|
-
return { authors, truncated: false };
|
|
207
|
-
const out = authors.slice();
|
|
208
|
-
out[out.length - 1] = out[out.length - 1].replace(ET_AL, "").trim();
|
|
209
|
-
return { authors: out.filter(Boolean), truncated: true };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const yearIn = (s) => (/\b(19|20)\d{2}\b/.exec(s) ?? [null])[0];
|
|
213
|
-
|
|
214
84
|
// ── parsing: .bib WITH A REAL PARSER ─────────────────────────────────────────
|
|
215
85
|
|
|
216
86
|
/**
|
|
217
|
-
* The name order is normalised to "First name Surname"
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* rule's job.
|
|
87
|
+
* The name order is normalised to "First name Surname". Then the rule has ONE `surname()` function
|
|
88
|
+
* (the last alphabetic token). Normalising the format is the parser's job; comparing is the rule's
|
|
89
|
+
* job.
|
|
221
90
|
*
|
|
222
91
|
* 🔴 TWO SHAPES, AND BOTH ARE LISTED EXPLICITLY. The parser returns an institution
|
|
223
92
|
* (`author={{Adversa AI}}` — the double brace means "one name as a whole, do not split") as
|
|
@@ -241,47 +110,15 @@ const joinName = (a) =>
|
|
|
241
110
|
.trim();
|
|
242
111
|
|
|
243
112
|
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* call that only whoever extracts bibliography facts needs. The import here is dynamic anyway, so
|
|
248
|
-
* the laziness was already there; the manifest merely stopped lying about it being required.
|
|
249
|
-
*
|
|
250
|
-
* ⚠️ A silent skip here would be the worst of the options: a missing checker and a passing one look
|
|
251
|
-
* the same, and "the bibliography was not checked" reads as "the bibliography is fine". That is why
|
|
252
|
-
* the message names the command, not the fact.
|
|
113
|
+
* The facts' entries of one database, as the bibtex reader read them (src/ports/bib-reader.ts): each
|
|
114
|
+
* with its line in the file that holds it, so a finding points where the author edits (the block's
|
|
115
|
+
* lines in paper.tex, or the `.bib`'s).
|
|
253
116
|
*/
|
|
254
|
-
export
|
|
255
|
-
text,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
({ parse } = await load());
|
|
261
|
-
} catch (e) {
|
|
262
|
-
if (e?.code !== "ERR_MODULE_NOT_FOUND") throw e;
|
|
263
|
-
throw new Error(
|
|
264
|
-
"parsing .bib requires @retorquere/bibtex-parser — it is declared OPTIONAL because it " +
|
|
265
|
-
"weighs 15 MB and is needed only for bibliography facts.\n" +
|
|
266
|
-
" Install: npm i -D @retorquere/bibtex-parser\n" +
|
|
267
|
-
" Why not our own regex: measured 26.08 — the regex gave 0 entries on both real " +
|
|
268
|
-
"files, four libraries gave the correct 27 and 51.",
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
// `sentenceCase: false` — the title is needed as written. Our normalisation folds the case
|
|
272
|
-
// anyway, but a fact has to be a fact: a paraphrased title cannot be shown to a human.
|
|
273
|
-
const res = parse(text, { sentenceCase: false, verbatimFields: [] });
|
|
274
|
-
// The entry's line in the file — found by its key. The parser gives no positions, and a finding
|
|
275
|
-
// needs an address; a key in `.bib` is unique by definition of the format, so the search is
|
|
276
|
-
// unambiguous.
|
|
277
|
-
const lines = text.split("\n");
|
|
278
|
-
const lineOfKey = (key) => {
|
|
279
|
-
const i = lines.findIndex((l) => l.includes(`{${key},`));
|
|
280
|
-
return i === -1 ? 0 : i + 1;
|
|
281
|
-
};
|
|
282
|
-
return res.entries.map((e, i) => {
|
|
283
|
-
const f = e.fields ?? {};
|
|
284
|
-
const names = Array.isArray(f.author) ? f.author : [];
|
|
117
|
+
export function factEntries(bib) {
|
|
118
|
+
const lineOf = (at) => bib.text.slice(0, at).split("\n").length;
|
|
119
|
+
return bib.entries.map((e, i) => {
|
|
120
|
+
const f = e.fields;
|
|
121
|
+
const names = e.names.author ?? [];
|
|
285
122
|
// `and others` is BibTeX's `et al.`. The parser returns it as an author with no first name.
|
|
286
123
|
const isOthers = (a) => !a.firstName && /^others$/i.test(a.lastName ?? "");
|
|
287
124
|
const truncated = names.some(isOthers);
|
|
@@ -289,29 +126,20 @@ export async function parseBib(
|
|
|
289
126
|
.filter((a) => !isOthers(a))
|
|
290
127
|
.map(joinName)
|
|
291
128
|
.filter(Boolean);
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
// printed rather than dropped.
|
|
295
|
-
const str = (v) =>
|
|
296
|
-
Array.isArray(v)
|
|
297
|
-
? v.join(" ")
|
|
298
|
-
: typeof v === "string"
|
|
299
|
-
? v
|
|
300
|
-
: v == null
|
|
301
|
-
? ""
|
|
302
|
-
: String(v);
|
|
129
|
+
// Every field the reader gives is text; a field the entry lacks is "".
|
|
130
|
+
const str = (v) => v ?? "";
|
|
303
131
|
const venueText = [f.booktitle, f.journal, f.note, f.howpublished]
|
|
304
132
|
.map(str)
|
|
305
133
|
.filter(Boolean)
|
|
306
134
|
.join(" ");
|
|
307
135
|
return {
|
|
308
136
|
n: i + 1,
|
|
309
|
-
line:
|
|
137
|
+
line: lineOf(e.span.start),
|
|
310
138
|
key: e.key,
|
|
311
139
|
// `raw` is where identifiers are looked for: they hide in `note`, in `journal`
|
|
312
140
|
// (`arXiv preprint arXiv:2107.03374`), and in a separate `doi` field.
|
|
313
141
|
raw: [
|
|
314
|
-
|
|
142
|
+
authors.join(" and "),
|
|
315
143
|
str(f.title),
|
|
316
144
|
venueText,
|
|
317
145
|
str(f.doi),
|
|
@@ -527,27 +355,40 @@ export function recordFrom(key, cached) {
|
|
|
527
355
|
|
|
528
356
|
// ── assembling the facts ─────────────────────────────────────────────────────
|
|
529
357
|
|
|
530
|
-
/**
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
358
|
+
/**
|
|
359
|
+
* WHICH BIBLIOGRAPHY: the one the last build's bibtex opened, as `paperlint build` recorded it
|
|
360
|
+
* (`_build/sources.json`, docs/design/paper-sources.md §1) and `bibliographyAt` reads it
|
|
361
|
+
* (src/paper-sources.ts) — never an order of file names of our own, and never read out of TeX source.
|
|
362
|
+
*
|
|
363
|
+
* a directory the databases bibtex opened for its paper.tex
|
|
364
|
+
* a .tex the same, for that file's folder
|
|
365
|
+
* a .bib that file, as named
|
|
366
|
+
*
|
|
367
|
+
* A paper with no current record is refused: run `npx paperlint build` first. A database TeX wrote from
|
|
368
|
+
* a `filecontents` block is read from the block, where the author edits it.
|
|
369
|
+
*/
|
|
370
|
+
const DEPS = {
|
|
371
|
+
files: nodeFiles,
|
|
372
|
+
codec: sourcesCodec,
|
|
373
|
+
latex: latexReader,
|
|
374
|
+
bib: bibReader,
|
|
375
|
+
};
|
|
537
376
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
}
|
|
545
|
-
|
|
377
|
+
/**
|
|
378
|
+
* The bibliography of `target`: the texts to read, or why there are none — `bibliographyAt`
|
|
379
|
+
* (src/paper-sources.ts) answers, in its words. `paperDir` is where the cache and the facts go.
|
|
380
|
+
*/
|
|
381
|
+
export function bibliographyFrom(target) {
|
|
382
|
+
const r = bibliographyAt(absolutePath(resolve(target)), DEPS);
|
|
383
|
+
if (!r.ok) return { ok: false, why: bibliographyUnreadWhy(r.error) };
|
|
384
|
+
const { paperDir, texts } = r.value;
|
|
385
|
+
return { ok: true, paperDir, texts };
|
|
546
386
|
}
|
|
547
387
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
388
|
+
/** The entries of one text TeX reads, each with its file. */
|
|
389
|
+
export function loadEntries(bib) {
|
|
390
|
+
const file = rel(bib.path);
|
|
391
|
+
return factEntries(bib).map((e) => ({ ...e, file }));
|
|
551
392
|
}
|
|
552
393
|
|
|
553
394
|
/** Every registry key a list of entries needs. Computed once so the requests go in one batch. */
|
|
@@ -559,7 +400,11 @@ export function keysFor(entries) {
|
|
|
559
400
|
|
|
560
401
|
const rel = (p) => (p.startsWith(ROOT) ? p.slice(ROOT.length + 1) : p);
|
|
561
402
|
|
|
562
|
-
|
|
403
|
+
/**
|
|
404
|
+
* The facts of a run. `source` is the file the first database was read from (the only one, for a
|
|
405
|
+
* paper with one), `sources` every one; `source_sha256` hashes those files' bytes, in order.
|
|
406
|
+
*/
|
|
407
|
+
export function buildFacts({ texts, entries, cache, cachePath }) {
|
|
563
408
|
const withIds = entries.map((e) => {
|
|
564
409
|
const ids = extractIds(e);
|
|
565
410
|
return { ...e, ids, primary: primaryOf(ids), keys: allKeys(ids) };
|
|
@@ -570,9 +415,12 @@ export function buildFacts({ source, text, entries, cache, cachePath }) {
|
|
|
570
415
|
if (!(k in records)) records[k] = recordFrom(k, cache[k]);
|
|
571
416
|
return {
|
|
572
417
|
schema: SCHEMA,
|
|
573
|
-
source: rel(
|
|
574
|
-
|
|
575
|
-
|
|
418
|
+
source: rel(texts[0].path),
|
|
419
|
+
sources: [...new Set(texts.map((b) => rel(b.path)))],
|
|
420
|
+
source_kind: "bibtex",
|
|
421
|
+
source_sha256: [...new Set(texts.map((b) => b.text))]
|
|
422
|
+
.reduce((h, t) => h.update(t), createHash("sha256"))
|
|
423
|
+
.digest("hex"),
|
|
576
424
|
cache: cachePath ? rel(cachePath) : null,
|
|
577
425
|
generated: new Date().toISOString().slice(0, 10),
|
|
578
426
|
entries: withIds,
|
|
@@ -594,16 +442,12 @@ async function main(argv) {
|
|
|
594
442
|
.slice(1)
|
|
595
443
|
.join("=");
|
|
596
444
|
|
|
597
|
-
const
|
|
598
|
-
if (!
|
|
599
|
-
console.error(
|
|
600
|
-
`🛑 no ${SOURCE_ORDER.join(", ")} under ${resolve(target)} — nowhere to take a bibliography from.`,
|
|
601
|
-
);
|
|
445
|
+
const found = bibliographyFrom(target);
|
|
446
|
+
if (!found.ok) {
|
|
447
|
+
console.error(`🛑 ${found.why}.`);
|
|
602
448
|
return 1;
|
|
603
449
|
}
|
|
604
|
-
const paperDir =
|
|
605
|
-
? dirname(src)
|
|
606
|
-
: resolve(target);
|
|
450
|
+
const { paperDir, texts } = found;
|
|
607
451
|
const cachePath = opt("cache")
|
|
608
452
|
? resolve(opt("cache"))
|
|
609
453
|
: join(paperDir, "repro", "refs-cache.json");
|
|
@@ -611,13 +455,13 @@ async function main(argv) {
|
|
|
611
455
|
? resolve(opt("out"))
|
|
612
456
|
: join(paperDir, "_build", "refs.facts.json");
|
|
613
457
|
|
|
614
|
-
const
|
|
615
|
-
const
|
|
458
|
+
const entries = texts.flatMap(loadEntries);
|
|
459
|
+
const names = [...new Set(texts.map((b) => basename(b.path)))].join(", ");
|
|
616
460
|
if (!entries.length) {
|
|
617
461
|
// 🔴 Zero entries is a suspect, not a success. Facts with an empty list would look like a
|
|
618
462
|
// clean bibliography, so we do not write them at all and exit with a non-zero code.
|
|
619
463
|
console.error(
|
|
620
|
-
`🛑 parsed 0 entries out of ${rel(
|
|
464
|
+
`🛑 parsed 0 entries out of ${[...new Set(texts.map((b) => rel(b.path)))].join(", ")}. Silence here would look like a clean bibliography.`,
|
|
621
465
|
);
|
|
622
466
|
return 1;
|
|
623
467
|
}
|
|
@@ -644,12 +488,12 @@ async function main(argv) {
|
|
|
644
488
|
if (fetched) saveCache(cachePath, cache);
|
|
645
489
|
}
|
|
646
490
|
|
|
647
|
-
const facts = buildFacts({
|
|
491
|
+
const facts = buildFacts({ texts, entries, cache, cachePath });
|
|
648
492
|
mkdirSync(dirname(out), { recursive: true });
|
|
649
493
|
writeFileSync(out, `${JSON.stringify(facts, null, 2)}\n`);
|
|
650
494
|
const resolved = Object.values(facts.records).filter((r) => r.found).length;
|
|
651
495
|
console.log(
|
|
652
|
-
`📚 ${
|
|
496
|
+
`📚 ${names} → ${rel(out)} (${entries.length} entries, ${Object.keys(facts.records).length} identifiers, ` +
|
|
653
497
|
`${resolved} resolved${offline ? ", offline" : fetched ? `, +${fetched} fetched` : ", all from cache"})`,
|
|
654
498
|
);
|
|
655
499
|
return 0;
|