paperlint 3.1.2 → 3.2.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/.github/workflows/ci.yml +12 -7
- package/CLAUDE.md +3 -3
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +9 -5
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +0 -5
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +4 -5
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.js +2 -2
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +14 -11
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -34
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +3 -2
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +2 -1
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +14 -9
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +5 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +15 -7
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +142 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +2 -2
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +5 -4
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +2 -1
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +1 -1
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +3 -2
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +11 -6
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +6 -3
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +8 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/venue-rules.js +1 -1
- package/dist/venue-rules.js.map +1 -1
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +4 -3
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +16 -8
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +34 -15
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +74 -29
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +132 -54
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +88 -31
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +37 -15
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +19 -6
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +4 -3
- package/eslint.config.mjs +139 -30
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +8 -8
- package/lib/{paper-config.mjs → paper-config.ts} +19 -14
- package/lib/skill-checks.mjs +1 -1
- package/lib/skill-checks.test.mjs +1 -1
- package/lib/skill-corpus.mjs +1 -1
- package/lib/skill-eval-kit.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -37
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -15
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/grade-paper-writing/prose-lint.mjs +1 -1
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +2 -2
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/scripts/README.md +1 -1
- package/skills/paper-pipeline/scripts/announce.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.mjs +1 -5
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/fixtures/{scorecard.mjs → scorecard.ts} +36 -7
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/status.test.mjs +1 -1
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/tighten-paper/structure.mjs +1 -1
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +11 -1
- package/skills/verify-citations/scripts/verify-cites.net.test.mjs +35 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +14 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/index.test.ts +3 -6
- package/src/adapters/banal/index.ts +7 -4
- package/src/adapters/banal/invocation.test.ts +3 -1
- package/src/adapters/banal/invocation.ts +1 -1
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +3 -3
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +4 -2
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +3 -1
- package/src/adapters/node/process.io.ts +4 -3
- package/src/adapters/node/process.test.ts +1 -1
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +26 -58
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +2 -2
- package/src/build-engine.ts +2 -2
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +15 -14
- package/src/build.ts +28 -19
- package/src/cli.harness.mjs +6 -6
- package/src/cli.test.ts +13 -6
- package/src/cli.ts +39 -37
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.ts +3 -2
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/result.test.ts +2 -2
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +4 -3
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +2 -1
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +39 -20
- package/src/hooks-settings.ts +20 -9
- package/src/init.test.ts +12 -9
- package/src/init.ts +22 -12
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +2 -2
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +2 -2
- package/src/paper-files.test.ts +3 -5
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +5 -5
- package/src/paper-settings.ts +8 -9
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +10 -11
- package/src/pdf-facts.ts +2 -1
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +3 -1
- package/src/presets.ts +3 -2
- package/src/references.test.ts +51 -43
- package/src/references.ts +14 -7
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +7 -4
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.ts +8 -5
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +1 -1
- package/src/toolchain.ts +31 -17
- package/src/venue-rules.test.ts +62 -64
- package/src/venue-rules.ts +1 -1
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { RuleContext } from "./rule-context.ts";
|
|
2
|
+
/** A reason to report, in ESLint's terms. */
|
|
3
|
+
export interface Finding {
|
|
4
|
+
readonly messageId: string;
|
|
5
|
+
readonly data: Readonly<Record<string, string>>;
|
|
6
|
+
}
|
|
7
|
+
/** The `last_page` field, as far as this rule can judge it. */
|
|
8
|
+
export type LastPage = {
|
|
9
|
+
readonly kind: "stub" | "review";
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "measured";
|
|
12
|
+
readonly left: number;
|
|
13
|
+
readonly right: number;
|
|
14
|
+
};
|
|
15
|
+
/** What `parseFacts` reads out of `paper.facts.json`, or why it cannot. */
|
|
16
|
+
export type ParsedFacts = {
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
readonly pdf: string;
|
|
19
|
+
/** Whatever the file holds there; it matched the digest pattern, and is compared as-is. */
|
|
20
|
+
readonly sha: unknown;
|
|
21
|
+
readonly last: LastPage;
|
|
22
|
+
} | ({
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
} & Finding);
|
|
25
|
+
/** The difference, in points, that two columns may end apart. See the harness for why 120. */
|
|
26
|
+
export declare const DEFAULT_TOLERANCE_PT = 120;
|
|
27
|
+
/** The schema of `paper.facts.json` this rule reads (`src/facts-file.ts`, FACTS_SCHEMA). */
|
|
28
|
+
export declare const FACTS_SCHEMA = 2;
|
|
29
|
+
export declare const FACTS_REL: string;
|
|
30
|
+
/**
|
|
31
|
+
* The facts file's text → what this rule needs, or the reason it cannot be judged. Pure.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseFacts(text: string): ParsedFacts;
|
|
34
|
+
/** The finding for a measured page, or null when it is balanced. Pure. */
|
|
35
|
+
export declare function judgeColumns(last: LastPage, tolerancePt: number): Finding | null;
|
|
36
|
+
declare const _default: {
|
|
37
|
+
rules: {
|
|
38
|
+
"last-page-balance": {
|
|
39
|
+
meta: {
|
|
40
|
+
type: string;
|
|
41
|
+
docs: {
|
|
42
|
+
description: string;
|
|
43
|
+
};
|
|
44
|
+
schema: {
|
|
45
|
+
type: string;
|
|
46
|
+
properties: {
|
|
47
|
+
tolerancePt: {
|
|
48
|
+
type: string;
|
|
49
|
+
exclusiveMinimum: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
additionalProperties: boolean;
|
|
53
|
+
}[];
|
|
54
|
+
messages: {
|
|
55
|
+
unbalanced: string;
|
|
56
|
+
noFacts: string;
|
|
57
|
+
factsBroken: string;
|
|
58
|
+
schema: string;
|
|
59
|
+
pdfMissing: string;
|
|
60
|
+
stale: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
create(context: RuleContext): {
|
|
64
|
+
root?: never;
|
|
65
|
+
} | {
|
|
66
|
+
root(): void;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export default _default;
|
|
72
|
+
//# sourceMappingURL=pdf-last-page-balance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-last-page-balance.d.ts","sourceRoot":"","sources":["../../eslint-rules/pdf-last-page-balance.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,6CAA6C;AAC7C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAED,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,GACpC;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB,GACD,CAAC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAwBvC,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,4FAA4F;AAC5F,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,SAAS,QAAqC,CAAC;AAY5D;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA+BpD;AAmBD,0EAA0E;AAC1E,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,GAAG,IAAI,CAahB;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAqFiB,WAAW;;;;;;;;AAqB7B,wBAAwD"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pdf/last-page-balance — the two columns of a paper's last page end at about the same height.
|
|
3
|
+
*
|
|
4
|
+
* OPTIONAL, OFF BY DEFAULT. Balance is a requirement of SOME venues, not of papers: some ACM
|
|
5
|
+
* production vendors return a camera-ready for it (Conference Publishing Consulting, which produces
|
|
6
|
+
* ICSE/ASE/SIGSOFT/SIGPLAN proceedings; Sheridan for ACM SIG conferences such as CCS), IEEEtran
|
|
7
|
+
* advises it for camera-ready work, and ACL, USENIX, AAAI and ICML say nothing. No standard format
|
|
8
|
+
* checker (HotCRP's banal, aclpubcheck, IEEE PDF eXpress) tests it. So a consumer turns it on for
|
|
9
|
+
* the papers whose venue asks, in that paper's paperlint.json:
|
|
10
|
+
*
|
|
11
|
+
* { "rules": { "pdf/last-page-balance": ["error", { "tolerancePt": 120 }] } }
|
|
12
|
+
*
|
|
13
|
+
* (That is a line comment's worth of JSON in a block comment only because it has no asterisk
|
|
14
|
+
* followed by a slash; see rule 8 of CLAUDE.md before editing it.)
|
|
15
|
+
*
|
|
16
|
+
* ── WHAT IT READS: THE FACTS THE BUILD WROTE, NOT THE PDF ───────────────────────
|
|
17
|
+
* `paperlint build` measures the PDF with pdf.js and writes `<paper>/_build/paper.facts.json`. This rule
|
|
18
|
+
* runs on `paper.tex` — the one file every LaTeX paper has, and the one ESLint already parses — and
|
|
19
|
+
* judges that paper's facts file beside it. It never opens the PDF to measure it: it hashes it, to
|
|
20
|
+
* refuse facts about a different build than the one on disk.
|
|
21
|
+
*
|
|
22
|
+
* ── IT DOES NOT FIX, AND SAYS HOW TO ────────────────────────────────────────────
|
|
23
|
+
* paperlint used to search for a `\balance` position itself and fail the build when none worked. Every
|
|
24
|
+
* mechanism is documented as unreliable by its own authors (acmart: `balance` "may lead to
|
|
25
|
+
* problems", `pbalance` is "experimental"; pbalance: "this package is a hack"), and for ACM TAPS
|
|
26
|
+
* venues ACM recompiles the source, so an edit to a generated `.bbl` may never reach the proceedings.
|
|
27
|
+
* The message names the manual fixes the vendor itself lists, in its order.
|
|
28
|
+
*
|
|
29
|
+
* Silent, by design, where there is nothing to judge: a stub last page (a few lines) and a review
|
|
30
|
+
* build (numbered lines in the margins make both columns measure full height). Loud where the input
|
|
31
|
+
* is missing: no facts file (unless the paper extends a venue preset — then `pdf/measured` says it),
|
|
32
|
+
* a foreign schema, facts about a PDF that is not on disk or changed
|
|
33
|
+
* since — a rule that is on and reads nothing must not look like a rule that passed.
|
|
34
|
+
*/
|
|
35
|
+
import { createHash } from "node:crypto";
|
|
36
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
37
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
38
|
+
import assert from "node:assert/strict";
|
|
39
|
+
import { z } from "zod";
|
|
40
|
+
import { CONFIG_FILE } from "../lib/paper-config.js";
|
|
41
|
+
// The fields read, each as it is written; anything that is not a mapping has none of them.
|
|
42
|
+
const FactsFields = z
|
|
43
|
+
.looseObject({
|
|
44
|
+
schema: z.unknown().optional(),
|
|
45
|
+
pdf: z.unknown().optional(),
|
|
46
|
+
pdf_sha256: z.unknown().optional(),
|
|
47
|
+
last_page: z.unknown().optional(),
|
|
48
|
+
})
|
|
49
|
+
.catch({});
|
|
50
|
+
const LastPageFields = z
|
|
51
|
+
.looseObject({
|
|
52
|
+
kind: z.unknown().optional(),
|
|
53
|
+
columns_pt: z.unknown().optional(),
|
|
54
|
+
})
|
|
55
|
+
.catch({});
|
|
56
|
+
/** Two column heights: finite numbers, exactly two of them. */
|
|
57
|
+
const Columns = z.tuple([z.number(), z.number()]);
|
|
58
|
+
const Settings = z.looseObject({ extends: z.unknown().optional() }).catch({});
|
|
59
|
+
const RuleOptions = z
|
|
60
|
+
.object({ tolerancePt: z.number().optional() })
|
|
61
|
+
.default({});
|
|
62
|
+
/** The difference, in points, that two columns may end apart. See the harness for why 120. */
|
|
63
|
+
export const DEFAULT_TOLERANCE_PT = 120;
|
|
64
|
+
/** The schema of `paper.facts.json` this rule reads (`src/facts-file.ts`, FACTS_SCHEMA). */
|
|
65
|
+
export const FACTS_SCHEMA = 2;
|
|
66
|
+
export const FACTS_REL = join("_build", "paper.facts.json");
|
|
67
|
+
/** The `last_page` field, parsed. */
|
|
68
|
+
function lastPageOf(v) {
|
|
69
|
+
const { kind, columns_pt } = LastPageFields.parse(v);
|
|
70
|
+
if (kind === "stub" || kind === "review")
|
|
71
|
+
return { kind };
|
|
72
|
+
const c = Columns.safeParse(columns_pt);
|
|
73
|
+
if (kind === "measured" && c.success)
|
|
74
|
+
return { kind: "measured", left: c.data[0], right: c.data[1] };
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The facts file's text → what this rule needs, or the reason it cannot be judged. Pure.
|
|
79
|
+
*/
|
|
80
|
+
export function parseFacts(text) {
|
|
81
|
+
let raw;
|
|
82
|
+
try {
|
|
83
|
+
raw = JSON.parse(text);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// `JSON.parse` throws a `SyntaxError` and nothing else.
|
|
87
|
+
assert.ok(e instanceof Error);
|
|
88
|
+
return { ok: false, messageId: "factsBroken", data: { why: e.message } };
|
|
89
|
+
}
|
|
90
|
+
const d = FactsFields.parse(raw);
|
|
91
|
+
if (d.schema !== FACTS_SCHEMA)
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
messageId: "schema",
|
|
95
|
+
data: { got: JSON.stringify(d.schema ?? null) },
|
|
96
|
+
};
|
|
97
|
+
const last = lastPageOf(d.last_page);
|
|
98
|
+
if (typeof d.pdf !== "string" ||
|
|
99
|
+
typeof d.pdf_sha256 !== "string" ||
|
|
100
|
+
!/^[0-9a-f]{64}$/.test(d.pdf_sha256) ||
|
|
101
|
+
!last)
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
messageId: "factsBroken",
|
|
105
|
+
data: {
|
|
106
|
+
why: "no `pdf`, `pdf_sha256` or `last_page` in the expected shape",
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
return { ok: true, pdf: d.pdf, sha: d.pdf_sha256, last };
|
|
110
|
+
}
|
|
111
|
+
/** Whether the facts describe the PDF on disk: null when they do, else the reason. */
|
|
112
|
+
function staleness(paperDir, facts) {
|
|
113
|
+
const pdf = isAbsolute(facts.pdf) ? facts.pdf : join(paperDir, facts.pdf);
|
|
114
|
+
let got;
|
|
115
|
+
try {
|
|
116
|
+
got = createHash("sha256").update(readFileSync(pdf)).digest("hex");
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { messageId: "pdfMissing", data: { pdf: facts.pdf } };
|
|
120
|
+
}
|
|
121
|
+
return got === facts.sha
|
|
122
|
+
? null
|
|
123
|
+
: { messageId: "stale", data: { pdf: facts.pdf } };
|
|
124
|
+
}
|
|
125
|
+
/** The finding for a measured page, or null when it is balanced. Pure. */
|
|
126
|
+
export function judgeColumns(last, tolerancePt) {
|
|
127
|
+
if (last.kind !== "measured")
|
|
128
|
+
return null;
|
|
129
|
+
const diff = Math.abs(last.left - last.right);
|
|
130
|
+
if (diff <= tolerancePt)
|
|
131
|
+
return null;
|
|
132
|
+
return {
|
|
133
|
+
messageId: "unbalanced",
|
|
134
|
+
data: {
|
|
135
|
+
left: last.left.toFixed(1),
|
|
136
|
+
right: last.right.toFixed(1),
|
|
137
|
+
diff: diff.toFixed(1),
|
|
138
|
+
tol: String(tolerancePt),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Where a finding goes: the `\documentclass` line, where the class options that fix it live. */
|
|
143
|
+
function reportLine(file) {
|
|
144
|
+
try {
|
|
145
|
+
const lines = readFileSync(file, "utf8").split("\n");
|
|
146
|
+
const i = lines.findIndex((l) => /^\s*\\documentclass\b/.test(l));
|
|
147
|
+
return i >= 0 ? i + 1 : 1;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Whether the paper extends a venue preset. Then the venue rules run on it too, and `pdf/measured`
|
|
155
|
+
* already says — once, as a warning — that a paper with no facts was not checked; this rule
|
|
156
|
+
* repeating it as an error would fail a lint-only CI for every paper of a venue whose preset turns
|
|
157
|
+
* this rule on.
|
|
158
|
+
*/
|
|
159
|
+
function extendsPreset(paperDir) {
|
|
160
|
+
try {
|
|
161
|
+
const s = Settings.parse(JSON.parse(readFileSync(join(paperDir, CONFIG_FILE), "utf8")));
|
|
162
|
+
return typeof s.extends === "string" && s.extends !== "";
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Everything the rule decides for one paper, as a finding or null. */
|
|
169
|
+
function verdict(paperDir, tolerancePt) {
|
|
170
|
+
const factsFile = join(paperDir, FACTS_REL);
|
|
171
|
+
if (!existsSync(factsFile))
|
|
172
|
+
return extendsPreset(paperDir)
|
|
173
|
+
? null
|
|
174
|
+
: { messageId: "noFacts", data: { file: FACTS_REL } };
|
|
175
|
+
const facts = parseFacts(readFileSync(factsFile, "utf8"));
|
|
176
|
+
if (!facts.ok)
|
|
177
|
+
return { messageId: facts.messageId, data: facts.data };
|
|
178
|
+
return staleness(paperDir, facts) ?? judgeColumns(facts.last, tolerancePt);
|
|
179
|
+
}
|
|
180
|
+
const rule = {
|
|
181
|
+
meta: {
|
|
182
|
+
type: "problem",
|
|
183
|
+
docs: {
|
|
184
|
+
description: "the last page's two columns end at about the same height (optional; for venues that require it)",
|
|
185
|
+
},
|
|
186
|
+
schema: [
|
|
187
|
+
{
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
tolerancePt: { type: "number", exclusiveMinimum: 0 },
|
|
191
|
+
},
|
|
192
|
+
additionalProperties: false,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
messages: {
|
|
196
|
+
unbalanced: "the last page is unbalanced: its columns end at {{left}} and {{right}} pt, {{diff}} pt " +
|
|
197
|
+
"apart (tolerance {{tol}} pt). Some ACM production vendors send such a camera-ready back. " +
|
|
198
|
+
"Conference Publishing Consulting's advice, in its order: \"For the ACMART style, use " +
|
|
199
|
+
"option 'balance' or, if this does not work, option 'pbalance'\" — acmart already has " +
|
|
200
|
+
"`balance` on by default, and it calls \\balance from the second column, where it does " +
|
|
201
|
+
"nothing when the last page is all bibliography, so try `pbalance`; then " +
|
|
202
|
+
"`\\usepackage{flushend}`; then `\\balance` (balance.sty) placed in what would be the " +
|
|
203
|
+
"FIRST column of the last page — in the bibliography, before the \\bibitem that starts " +
|
|
204
|
+
"that column. Rebuild (`paperlint build`) and lint again.",
|
|
205
|
+
noFacts: "pdf/last-page-balance is on for this paper, but {{file}} does not exist beside it. The rule " +
|
|
206
|
+
"judges the facts `paperlint build` writes after a successful compile — build the paper first.",
|
|
207
|
+
factsBroken: "{{why}} in _build/paper.facts.json — rebuild the paper (`paperlint build`) to rewrite it",
|
|
208
|
+
schema: "_build/paper.facts.json has schema {{got}}; this rule reads schema 2 — rebuild the paper " +
|
|
209
|
+
"(`paperlint build`) to rewrite it",
|
|
210
|
+
pdfMissing: "_build/paper.facts.json describes {{pdf}}, which is not on disk (a failed build removes " +
|
|
211
|
+
"it) — rebuild the paper",
|
|
212
|
+
stale: "_build/paper.facts.json describes a DIFFERENT {{pdf}} than the one on disk (its SHA-256 " +
|
|
213
|
+
"differs), so judging it would judge an earlier build — rebuild the paper",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
create(context) {
|
|
217
|
+
// Judged once per paper, on its paper.tex: the paper directory is the file's directory.
|
|
218
|
+
if (basename(context.filename) !== "paper.tex")
|
|
219
|
+
return {};
|
|
220
|
+
const { tolerancePt = DEFAULT_TOLERANCE_PT } = RuleOptions.parse(context.options[0]);
|
|
221
|
+
return {
|
|
222
|
+
root() {
|
|
223
|
+
const v = verdict(dirname(context.filename), tolerancePt);
|
|
224
|
+
if (!v)
|
|
225
|
+
return;
|
|
226
|
+
const line = reportLine(context.filename);
|
|
227
|
+
context.report({
|
|
228
|
+
loc: { start: { line, column: 1 }, end: { line, column: 2 } },
|
|
229
|
+
messageId: v.messageId,
|
|
230
|
+
data: v.data,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
export default { rules: { "last-page-balance": rule } };
|
|
237
|
+
//# sourceMappingURL=pdf-last-page-balance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-last-page-balance.js","sourceRoot":"","sources":["../../eslint-rules/pdf-last-page-balance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ErrorObject } from "ajv";
|
|
2
|
+
import type { RuleContext } from "./rule-context.ts";
|
|
3
|
+
/** A compiled schema, as far as `errorsOf` uses one. Ajv's `ValidateFunction` is one. */
|
|
4
|
+
export interface Validate {
|
|
5
|
+
(data: unknown): unknown;
|
|
6
|
+
readonly errors?: readonly ErrorObject[] | null;
|
|
7
|
+
}
|
|
8
|
+
/** The schema's verdict on `data` as messages. Ajv types `errors` as possibly null; that is tolerated. */
|
|
9
|
+
export declare const errorsOf: (validate: Validate, data: unknown) => string[];
|
|
10
|
+
/**
|
|
11
|
+
* A rule that validates a markdown file's YAML frontmatter against the JSON Schema in `schemaUrl`
|
|
12
|
+
* (a file shipped beside the rule). A file with no frontmatter is validated as `{}`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function frontmatterRule(schemaUrl: URL | string, description: string): {
|
|
15
|
+
meta: {
|
|
16
|
+
type: string;
|
|
17
|
+
docs: {
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
schema: never[];
|
|
21
|
+
messages: {
|
|
22
|
+
malformed: string;
|
|
23
|
+
invalid: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
create(context: RuleContext): {
|
|
27
|
+
yaml(node: {
|
|
28
|
+
readonly value?: string;
|
|
29
|
+
}): void;
|
|
30
|
+
"root:exit"(root: object): void;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
declare const _default: {
|
|
34
|
+
rules: {
|
|
35
|
+
frontmatter: {
|
|
36
|
+
meta: {
|
|
37
|
+
type: string;
|
|
38
|
+
docs: {
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
schema: never[];
|
|
42
|
+
messages: {
|
|
43
|
+
malformed: string;
|
|
44
|
+
invalid: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
create(context: RuleContext): {
|
|
48
|
+
yaml(node: {
|
|
49
|
+
readonly value?: string;
|
|
50
|
+
}): void;
|
|
51
|
+
"root:exit"(root: object): void;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export default _default;
|
|
57
|
+
//# sourceMappingURL=review-frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-frontmatter.d.ts","sourceRoot":"","sources":["../../eslint-rules/review-frontmatter.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAKvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAAC;CACjD;AAyBD,0GAA0G;AAC1G,eAAO,MAAM,QAAQ,GAAI,UAAU,QAAQ,EAAE,MAAM,OAAO,KAAG,MAAM,EAGU,CAAC;AAE9E;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,GAAG,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM;;;;;;;;;;;;oBAexD,WAAW;mBAIZ;YAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE;0BAcpB,MAAM;;EAY/B;;;;;;;;;;;;;;;4BA9BmB,WAAW;2BAIZ;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;kCAcpB,MAAM;;;;;AAchC,wBAOE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `review/frontmatter` — a review report's frontmatter is a RECORD, validated by a JSON Schema.
|
|
3
|
+
*
|
|
4
|
+
* ── WHAT IT CHECKS ───────────────────────────────────────────────────────────────
|
|
5
|
+
* A review under `reviews/` declares what it found in its YAML frontmatter:
|
|
6
|
+
*
|
|
7
|
+
* findings:
|
|
8
|
+
* - id: 1
|
|
9
|
+
* status: open # open | fixed | wontfix
|
|
10
|
+
* cause: missing-skill # skill-defect | missing-skill | hook | rule — required when open
|
|
11
|
+
*
|
|
12
|
+
* The pipeline convention behind it: a review does not end with a list of findings, it ends with
|
|
13
|
+
* what in the PIPELINE let each one through. Otherwise the text gets fixed while the tool that
|
|
14
|
+
* missed it stays the same. The schema is `review-frontmatter.schema.json` beside this file, and
|
|
15
|
+
* "an open finding names its cause" is its `if`/`then`, not code here.
|
|
16
|
+
*
|
|
17
|
+
* The same mechanism, with a schema of its own, is `sibling/frontmatter`
|
|
18
|
+
* (`sibling-frontmatter.mjs`): `frontmatterRule` below is the one implementation.
|
|
19
|
+
*
|
|
20
|
+
* ── WHY THIS REPLACED TWO RULES IN 3.0.0 ────────────────────────────────────────────
|
|
21
|
+
* `review/findings-cause` GUESSED the findings from the prose: it counted numbered table rows and
|
|
22
|
+
* list items starting in bold, looked for a magic word (`causeMarker`, "Cause:") anywhere in the
|
|
23
|
+
* text, spoke only above a threshold (`minFindings`), and skipped reports older than a date
|
|
24
|
+
* (`sinceCreated`) — a date ratchet its own header said belonged in `ignores`. Every one of
|
|
25
|
+
* those was a convention in prose standing in for a field nobody had declared.
|
|
26
|
+
* `doc/fields` was a hand-rolled schema language in the config (`{ field: { values: [...] } }`),
|
|
27
|
+
* and its one real use was the `read:` field of sibling cards — which this package's own
|
|
28
|
+
* `analyze-sibling-paper` skill writes, so the package now ships that schema too. The record is
|
|
29
|
+
* data, the schema is JSON Schema, the validator is ajv.
|
|
30
|
+
*
|
|
31
|
+
* A review with no `findings` key is not a findings record and is not checked for findings — so
|
|
32
|
+
* there is no date to compare: old reports simply have no records. A file with no frontmatter is
|
|
33
|
+
* validated as `{}`, so a field a schema requires is still reported on it: a missing header must
|
|
34
|
+
* not be a way around the check.
|
|
35
|
+
*
|
|
36
|
+
* YAML is read with js-yaml's CORE schema, which leaves `2026-09-20` a string. The default schema
|
|
37
|
+
* turns it into a Date, and a schema asking for `"type": "string"` would then reject a date the
|
|
38
|
+
* author wrote correctly.
|
|
39
|
+
*/
|
|
40
|
+
import Ajv from "ajv";
|
|
41
|
+
import { CORE_SCHEMA, load } from "js-yaml";
|
|
42
|
+
import assert from "node:assert/strict";
|
|
43
|
+
import { readFileSync } from "node:fs";
|
|
44
|
+
import { z } from "zod";
|
|
45
|
+
/**
|
|
46
|
+
* What a parser failure says about itself. js-yaml's `YAMLException` carries a `reason`; anything
|
|
47
|
+
* else thrown may carry a `message`, or be a bare value that says nothing.
|
|
48
|
+
*/
|
|
49
|
+
const Thrown = z
|
|
50
|
+
.object({
|
|
51
|
+
reason: z.string().optional().catch(undefined),
|
|
52
|
+
message: z.string().optional().catch(undefined),
|
|
53
|
+
})
|
|
54
|
+
.catch({});
|
|
55
|
+
/** One ajv error as a line a person can act on. `if` errors only repeat their `then`. */
|
|
56
|
+
function describe(e) {
|
|
57
|
+
const where = e.dataPath
|
|
58
|
+
? `\`${e.dataPath.replace(/^\./, "")}\``
|
|
59
|
+
: "the frontmatter";
|
|
60
|
+
if (e.keyword === "enum" && "allowedValues" in e.params)
|
|
61
|
+
return `${where} must be one of: ${e.params.allowedValues.join(", ")}`;
|
|
62
|
+
if (e.keyword === "additionalProperties" && "additionalProperty" in e.params)
|
|
63
|
+
return `${where} has an unknown field \`${e.params.additionalProperty}\``;
|
|
64
|
+
return `${where} ${String(e.message)}`;
|
|
65
|
+
}
|
|
66
|
+
/** The schema's verdict on `data` as messages. Ajv types `errors` as possibly null; that is tolerated. */
|
|
67
|
+
export const errorsOf = (validate, data) => validate(data)
|
|
68
|
+
? []
|
|
69
|
+
: (validate.errors ?? []).filter((e) => e.keyword !== "if").map(describe);
|
|
70
|
+
/**
|
|
71
|
+
* A rule that validates a markdown file's YAML frontmatter against the JSON Schema in `schemaUrl`
|
|
72
|
+
* (a file shipped beside the rule). A file with no frontmatter is validated as `{}`.
|
|
73
|
+
*/
|
|
74
|
+
export function frontmatterRule(schemaUrl, description) {
|
|
75
|
+
const schema = JSON.parse(readFileSync(schemaUrl, "utf8"));
|
|
76
|
+
assert.ok(typeof schema === "object" && schema !== null, "not a schema");
|
|
77
|
+
const validate = new Ajv({ allErrors: true }).compile(schema);
|
|
78
|
+
return {
|
|
79
|
+
meta: {
|
|
80
|
+
type: "problem",
|
|
81
|
+
docs: { description },
|
|
82
|
+
schema: [],
|
|
83
|
+
messages: {
|
|
84
|
+
malformed: "the frontmatter does not parse as YAML ({{why}}) — there is no record to read",
|
|
85
|
+
invalid: "{{problem}}",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
create(context) {
|
|
89
|
+
let frontmatter = null;
|
|
90
|
+
let data = {};
|
|
91
|
+
return {
|
|
92
|
+
yaml(node) {
|
|
93
|
+
frontmatter = node;
|
|
94
|
+
try {
|
|
95
|
+
data = load(node.value ?? "", { schema: CORE_SCHEMA }) ?? {};
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
data = null;
|
|
99
|
+
const { reason, message } = Thrown.parse(e);
|
|
100
|
+
context.report({
|
|
101
|
+
node,
|
|
102
|
+
messageId: "malformed",
|
|
103
|
+
data: { why: reason ?? message ?? "unparseable" },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"root:exit"(root) {
|
|
108
|
+
if (data === null)
|
|
109
|
+
return;
|
|
110
|
+
for (const problem of errorsOf(validate, data))
|
|
111
|
+
context.report({
|
|
112
|
+
node: frontmatter ?? root,
|
|
113
|
+
messageId: "invalid",
|
|
114
|
+
data: { problem },
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export default {
|
|
122
|
+
rules: {
|
|
123
|
+
frontmatter: frontmatterRule(new URL("./review-frontmatter.schema.json", import.meta.url), "a review's frontmatter is a record: its findings, each with a status and, when open, the pipeline cause — validated by JSON Schema"),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=review-frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-frontmatter.js","sourceRoot":"","sources":["../../eslint-rules/review-frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "urn:paperlint:review-frontmatter",
|
|
4
|
+
"title": "paperlint review frontmatter",
|
|
5
|
+
"description": "The YAML frontmatter of a review report under reviews/. A review with no `findings` key is not a findings record and is not checked for findings.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"findings": {
|
|
9
|
+
"description": "What the review found, one record per finding.",
|
|
10
|
+
"type": "array",
|
|
11
|
+
"items": { "$ref": "#/definitions/finding" }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"definitions": {
|
|
15
|
+
"finding": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["id", "status"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"id": {
|
|
20
|
+
"description": "How the review body refers to this finding.",
|
|
21
|
+
"type": ["integer", "string"]
|
|
22
|
+
},
|
|
23
|
+
"status": { "enum": ["open", "fixed", "wontfix"] },
|
|
24
|
+
"cause": {
|
|
25
|
+
"description": "What in the PIPELINE let this through: a defective skill, a missing skill, a hook, or a rule. Fix that, and the text edit falls out of running the fixed tool.",
|
|
26
|
+
"enum": ["skill-defect", "missing-skill", "hook", "rule"]
|
|
27
|
+
},
|
|
28
|
+
"title": { "type": "string" },
|
|
29
|
+
"note": { "type": "string" }
|
|
30
|
+
},
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"if": { "properties": { "status": { "const": "open" } } },
|
|
33
|
+
"then": { "required": ["cause"] }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of ESLint's rule API the rules in this directory use.
|
|
3
|
+
*
|
|
4
|
+
* These rules run on three languages (JavaScript, markdown and the `.tex` language next door),
|
|
5
|
+
* and ESLint's own `Rule.RuleContext` is the JavaScript one. So the rules are typed against what
|
|
6
|
+
* they actually touch, the way `src/venue-rules.ts` and `src/reference-rules.ts` already are.
|
|
7
|
+
*/
|
|
8
|
+
/** A line and a 1-based column, as ESLint reports them. */
|
|
9
|
+
export interface Loc {
|
|
10
|
+
readonly line: number;
|
|
11
|
+
readonly column: number;
|
|
12
|
+
}
|
|
13
|
+
import type { Root } from "mdast";
|
|
14
|
+
/** The source code a rule reads. `raw` exists on the `.tex` language's source code only. */
|
|
15
|
+
export interface RuleSourceCode {
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly raw?: string;
|
|
18
|
+
/** mdast: the markdown language's tree, or the `.tex` language's projection of one. */
|
|
19
|
+
readonly ast?: Root;
|
|
20
|
+
getLocFromIndex(index: number): Loc;
|
|
21
|
+
getText(node?: object): string;
|
|
22
|
+
}
|
|
23
|
+
/** What a fix produces: replace `range` of the file with `text`. */
|
|
24
|
+
export interface RuleFix {
|
|
25
|
+
readonly range: readonly [number, number];
|
|
26
|
+
readonly text: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RuleFixer {
|
|
29
|
+
replaceTextRange(range: readonly [number, number], text: string): RuleFix;
|
|
30
|
+
insertTextBeforeRange(range: readonly [number, number], text: string): RuleFix;
|
|
31
|
+
}
|
|
32
|
+
export interface RuleSuggestion {
|
|
33
|
+
readonly messageId: string;
|
|
34
|
+
readonly data?: Readonly<Record<string, string | number>>;
|
|
35
|
+
fix(fixer: RuleFixer): RuleFix;
|
|
36
|
+
}
|
|
37
|
+
export interface RuleReport {
|
|
38
|
+
readonly node?: object;
|
|
39
|
+
readonly loc?: {
|
|
40
|
+
readonly start: Loc;
|
|
41
|
+
readonly end: Loc;
|
|
42
|
+
};
|
|
43
|
+
readonly messageId: string;
|
|
44
|
+
readonly data?: Readonly<Record<string, string | number>>;
|
|
45
|
+
readonly fix?: (fixer: RuleFixer) => RuleFix;
|
|
46
|
+
readonly suggest?: readonly RuleSuggestion[];
|
|
47
|
+
}
|
|
48
|
+
export interface RuleContext {
|
|
49
|
+
readonly filename: string;
|
|
50
|
+
readonly options: readonly unknown[];
|
|
51
|
+
readonly settings: Readonly<Record<string, unknown>>;
|
|
52
|
+
readonly sourceCode: RuleSourceCode;
|
|
53
|
+
report(descriptor: RuleReport): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=rule-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-context.d.ts","sourceRoot":"","sources":["../../eslint-rules/rule-context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2DAA2D;AAC3D,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAElC,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IACpB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC;IACpC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,oEAAoE;AACpE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1E,qBAAqB,CACnB,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAC1D,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CACtC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of ESLint's rule API the rules in this directory use.
|
|
3
|
+
*
|
|
4
|
+
* These rules run on three languages (JavaScript, markdown and the `.tex` language next door),
|
|
5
|
+
* and ESLint's own `Rule.RuleContext` is the JavaScript one. So the rules are typed against what
|
|
6
|
+
* they actually touch, the way `src/venue-rules.ts` and `src/reference-rules.ts` already are.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=rule-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-context.js","sourceRoot":"","sources":["../../eslint-rules/rule-context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|