paperlint 3.1.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +12 -7
- package/CLAUDE.md +5 -5
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/failure.d.ts +1 -7
- package/dist/adapters/banal/failure.d.ts.map +1 -1
- package/dist/adapters/banal/failure.js +5 -7
- package/dist/adapters/banal/failure.js.map +1 -1
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +4 -3
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/install.d.ts.map +1 -1
- package/dist/adapters/banal/install.js +4 -3
- package/dist/adapters/banal/install.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +10 -5
- package/dist/adapters/banal/invocation.d.ts.map +1 -1
- package/dist/adapters/banal/invocation.js +13 -1
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/locate.d.ts +12 -9
- package/dist/adapters/banal/locate.d.ts.map +1 -1
- package/dist/adapters/banal/locate.js +19 -12
- package/dist/adapters/banal/locate.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +10 -1
- package/dist/adapters/banal/run.d.ts.map +1 -1
- package/dist/adapters/banal/run.js +8 -4
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +1 -1
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +2 -1
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -2
- package/dist/adapters/memory/index.js.map +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 +5 -7
- 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/node/workspace.io.d.ts.map +1 -1
- package/dist/adapters/node/workspace.io.js +4 -2
- package/dist/adapters/node/workspace.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 +8 -7
- 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.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +10 -3
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +2 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +19 -17
- package/dist/build.js.map +1 -1
- package/dist/caller-path.d.ts +3 -0
- package/dist/caller-path.d.ts.map +1 -0
- package/dist/caller-path.js +13 -0
- package/dist/caller-path.js.map +1 -0
- package/dist/cli.d.ts +10 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -46
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +12 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +5 -3
- 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/paths.d.ts +8 -2
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +12 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/ready.d.ts +24 -0
- package/dist/domain/ready.d.ts.map +1 -0
- package/dist/domain/ready.js +11 -0
- package/dist/domain/ready.js.map +1 -0
- package/dist/domain/record.d.ts +8 -0
- package/dist/domain/record.d.ts.map +1 -0
- package/dist/domain/record.js +8 -0
- package/dist/domain/record.js.map +1 -0
- package/dist/domain/sha256.d.ts.map +1 -1
- package/dist/domain/sha256.js +4 -2
- package/dist/domain/sha256.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 +23 -22
- 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 +31 -27
- 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 +31 -11
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +8 -9
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +33 -15
- 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 +138 -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 +7 -3
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -6
- package/dist/paper-files.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +8 -7
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts +51 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +25 -16
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +3 -4
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/tool-installer.d.ts +2 -14
- package/dist/ports/tool-installer.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -6
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +15 -10
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts +26 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +15 -12
- 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 +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +22 -5
- 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/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +21 -8
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +2 -2
- 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} +5 -4
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +18 -10
- 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} +35 -16
- 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} +76 -31
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +134 -56
- 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} +89 -32
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +38 -16
- 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} +20 -7
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +6 -5
- package/eslint.config.mjs +146 -30
- package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
- package/fixtures/install-path-literals/clean.md +2 -2
- package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
- package/fixtures/install-path-literals/defect.md +2 -2
- 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.sh +9 -19
- 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 +9 -9
- package/lib/{paper-config.mjs → paper-config.ts} +20 -19
- package/lib/skill-checks.mjs +24 -381
- package/lib/skill-checks.test.mjs +64 -116
- package/lib/skill-corpus.mjs +10 -251
- package/lib/skill-corpus.test.mjs +2 -13
- package/lib/skill-eval-kit.mjs +1 -1
- package/lib/skill-trigger-cases.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 -38
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -16
- 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/analyze-sibling-paper/SKILL.md +1 -39
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
- package/skills/argument-arc/SKILL.md +2 -36
- package/skills/argument-arc/SKILL.md.spec.ts +1 -43
- package/skills/build-benchmark/SKILL.md +1 -41
- package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
- package/skills/camera-ready/SKILL.md +1 -34
- package/skills/camera-ready/SKILL.md.spec.ts +0 -33
- package/skills/cold-read-diff/SKILL.md +1 -35
- package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
- package/skills/draft-paper/SKILL.md +2 -41
- package/skills/draft-paper/SKILL.md.spec.ts +1 -50
- package/skills/extend-paper/SKILL.md +2 -33
- package/skills/extend-paper/SKILL.md.spec.ts +1 -42
- package/skills/find-venue/SKILL.md +2 -38
- package/skills/find-venue/SKILL.md.spec.ts +1 -47
- package/skills/grade-paper-writing/SKILL.md +3 -38
- package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
- package/skills/grade-paper-writing/prose-lint.mjs +3 -4
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +3 -3
- package/skills/harden-paper/SKILL.md +1 -39
- package/skills/harden-paper/SKILL.md.spec.ts +0 -38
- package/skills/map-prior-work/SKILL.md +1 -35
- package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
- package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +3 -4
- package/skills/paper-adversarial-review/SKILL.md +2 -35
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
- package/skills/paper-pipeline/SKILL.md +2 -28
- package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
- 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 +2 -2
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
- package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
- package/skills/paper-pipeline/scripts/README.md +30 -225
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
- 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.harness.mjs +6 -104
- package/skills/paper-pipeline/scripts/consumer.mjs +24 -109
- 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/generated-code.harness.mjs +2 -3
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
- 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-status/SKILL.md +2 -42
- package/skills/paper-status/SKILL.md.spec.ts +1 -41
- package/skills/pc-panel-review/SKILL.md +2 -46
- package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
- package/skills/plan-paper-timeline/SKILL.md +2 -36
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
- 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/SKILL.md +1 -37
- package/skills/render-paper/SKILL.md.spec.ts +0 -36
- 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/research-ideate/SKILL.md +2 -39
- package/skills/research-ideate/SKILL.md.spec.ts +1 -47
- package/skills/study-accepted-papers/SKILL.md +1 -34
- package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
- package/skills/submit-paper/SKILL.md +1 -33
- package/skills/submit-paper/SKILL.md.spec.ts +0 -32
- package/skills/sweep-design-space/SKILL.md +1 -38
- package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
- package/skills/tighten-paper/SKILL.md +2 -39
- package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
- package/skills/tighten-paper/structure.mjs +5 -12
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/SKILL.md +2 -38
- package/skills/verify-citations/SKILL.md.spec.ts +1 -37
- 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/src/CLAUDE.md +1 -1
- package/src/adapters/banal/failure.ts +9 -10
- package/src/adapters/banal/index.test.ts +5 -8
- package/src/adapters/banal/index.ts +8 -7
- package/src/adapters/banal/install.ts +9 -4
- package/src/adapters/banal/invocation.test.ts +23 -9
- package/src/adapters/banal/invocation.ts +24 -9
- package/src/adapters/banal/locate.test.ts +2 -2
- package/src/adapters/banal/locate.ts +27 -21
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +5 -5
- package/src/adapters/banal/run.ts +16 -10
- package/src/adapters/banal/settings.ts +2 -2
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +8 -6
- package/src/adapters/memory/index.ts +4 -3
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +8 -4
- package/src/adapters/node/process.io.ts +13 -5
- package/src/adapters/node/process.test.ts +5 -5
- package/src/adapters/node/workspace.io.ts +9 -4
- 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 +33 -60
- 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 +13 -8
- package/src/build-engine.ts +12 -7
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +24 -24
- package/src/build.ts +34 -26
- package/src/caller-path.ts +13 -0
- package/src/cli.harness.mjs +6 -8
- package/src/cli.test.ts +31 -13
- package/src/cli.ts +117 -58
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.test.ts +1 -1
- package/src/doctor.ts +17 -7
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/paths.test.ts +15 -0
- package/src/domain/paths.ts +20 -1
- package/src/domain/ready.ts +31 -0
- package/src/domain/record.test.ts +20 -0
- package/src/domain/record.ts +12 -0
- package/src/domain/result.test.ts +2 -2
- package/src/domain/sha256.ts +5 -3
- 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 +34 -5
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +45 -29
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +56 -20
- package/src/hooks-settings.ts +48 -13
- package/src/init.test.ts +37 -14
- package/src/init.ts +48 -24
- 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 +9 -25
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +9 -5
- package/src/paper-files.test.ts +25 -24
- package/src/paper-files.ts +16 -27
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +7 -7
- package/src/paper-settings.ts +13 -14
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +25 -15
- package/src/pdf-facts.ts +81 -23
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +8 -4
- package/src/ports/tool-installer.ts +2 -17
- package/src/presets.ts +31 -16
- package/src/references.test.ts +76 -62
- package/src/references.ts +18 -12
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +46 -14
- 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.test.ts +21 -0
- package/src/tex-requirements.ts +37 -11
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +10 -6
- package/src/toolchain.ts +31 -17
- package/src/types.ts +5 -3
- package/src/venue-rules.test.ts +70 -68
- package/src/venue-rules.ts +22 -12
- 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/skills/paper-pipeline/scripts/announce.mjs +0 -67
- package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
- package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +0 -71
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
- package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
- package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -753
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
- package/skills/paper-pipeline/scripts/status.mjs +0 -295
- package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
- /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/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
package/src/references.test.ts
CHANGED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { describe, expect, it } from "vitest";
|
|
11
11
|
import { ESLint } from "eslint";
|
|
12
|
+
import { eslintConfig } from "./cli.ts";
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { memoryFiles } from "./adapters/memory/index.ts";
|
|
14
15
|
import { ok } from "./domain/result.ts";
|
|
15
|
-
import
|
|
16
|
+
import { absolutePath } from "./domain/paths.ts";
|
|
16
17
|
import type {
|
|
17
18
|
CheckReferences,
|
|
18
19
|
EntryVerdict,
|
|
@@ -32,8 +33,8 @@ import {
|
|
|
32
33
|
type LookupCache,
|
|
33
34
|
} from "./domain/lookup-cache.ts";
|
|
34
35
|
import { referenceRules, REFERENCE_RULE_LEVELS } from "./reference-rules.ts";
|
|
35
|
-
|
|
36
|
-
import {
|
|
36
|
+
import { texLanguage } from "../eslint-rules/latex-language.ts";
|
|
37
|
+
import { present } from "../test/support.ts";
|
|
37
38
|
|
|
38
39
|
const PAPER = "/work/papers/p";
|
|
39
40
|
const TEX = (entries: string) =>
|
|
@@ -63,22 +64,28 @@ const verdict = (
|
|
|
63
64
|
|
|
64
65
|
const checker =
|
|
65
66
|
(entries: readonly EntryVerdict[]): CheckReferences =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
67
|
+
(_bib, cache) =>
|
|
68
|
+
Promise.resolve({ check: { kind: "checked", entries }, cache });
|
|
69
|
+
const offline: CheckReferences = (_bib, cache) =>
|
|
70
|
+
Promise.resolve({
|
|
71
|
+
check: {
|
|
72
|
+
kind: "not-checked",
|
|
73
|
+
why: "the citation services cannot be reached (fetch failed)",
|
|
74
|
+
},
|
|
75
|
+
cache,
|
|
76
|
+
});
|
|
74
77
|
|
|
75
78
|
/** The step's context over `files`, with every port it does not use stubbed. */
|
|
76
79
|
const ctx = (files: ReturnType<typeof memoryFiles>) => ({
|
|
77
80
|
paperDir: PAPER,
|
|
78
81
|
env: {},
|
|
79
|
-
run: (
|
|
80
|
-
readPdf: (
|
|
81
|
-
measure:
|
|
82
|
+
run: () => ({ status: 0 }),
|
|
83
|
+
readPdf: () => Promise.reject(new Error("the references step reads no PDF")),
|
|
84
|
+
measure: {
|
|
85
|
+
measure: () => {
|
|
86
|
+
throw new Error("the references step measures nothing");
|
|
87
|
+
},
|
|
88
|
+
},
|
|
82
89
|
files,
|
|
83
90
|
});
|
|
84
91
|
|
|
@@ -92,37 +99,31 @@ async function build(tex: string, check: CheckReferences) {
|
|
|
92
99
|
return { files, out };
|
|
93
100
|
}
|
|
94
101
|
|
|
95
|
-
/**
|
|
96
|
-
* The rules as an ESLint plugin. Their context type is the narrow one they read (a custom
|
|
97
|
-
* language's SourceCode), which ESLint's JS-centred plugin type does not describe.
|
|
98
|
-
*/
|
|
99
|
-
const asPlugin = (rules: object): ESLint.Plugin => ({ rules }) as ESLint.Plugin;
|
|
100
|
-
|
|
101
102
|
/** Lint `tex` as the paper's paper.tex against what `files` recorded. */
|
|
102
103
|
async function lint(files: ReturnType<typeof memoryFiles>, tex: string) {
|
|
103
104
|
files.writeAtomic(
|
|
104
|
-
`${PAPER}/paper.tex`
|
|
105
|
+
absolutePath(`${PAPER}/paper.tex`),
|
|
105
106
|
new TextEncoder().encode(tex),
|
|
106
107
|
);
|
|
107
108
|
const eslint = new ESLint({
|
|
108
109
|
cwd: "/work",
|
|
109
110
|
overrideConfigFile: true,
|
|
110
|
-
overrideConfig: [
|
|
111
|
+
overrideConfig: eslintConfig([
|
|
111
112
|
{
|
|
112
113
|
files: ["**/paper.tex"],
|
|
113
114
|
plugins: {
|
|
114
115
|
tex: { languages: { latex: texLanguage } },
|
|
115
|
-
paper:
|
|
116
|
+
paper: { rules: referenceRules({ files }) },
|
|
116
117
|
},
|
|
117
118
|
language: "tex/latex",
|
|
118
119
|
rules: REFERENCE_RULE_LEVELS,
|
|
119
120
|
},
|
|
120
|
-
],
|
|
121
|
+
]),
|
|
121
122
|
});
|
|
122
123
|
const [res] = await eslint.lintText(tex, {
|
|
123
124
|
filePath: join(PAPER, "paper.tex"),
|
|
124
125
|
});
|
|
125
|
-
return res
|
|
126
|
+
return present(res, "one lint result").messages;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
describe("the references build step", () => {
|
|
@@ -135,7 +136,9 @@ describe("the references build step", () => {
|
|
|
135
136
|
expect(out.ok).toBe(true);
|
|
136
137
|
const doc = readReferences(files, PAPER);
|
|
137
138
|
expect(doc?.status).toBe("checked");
|
|
138
|
-
expect(doc?.bib.sha256).toBe(
|
|
139
|
+
expect(doc?.bib.sha256).toBe(
|
|
140
|
+
bibHash(present(bibliographyOf(files, PAPER), "the bibliography")),
|
|
141
|
+
);
|
|
139
142
|
expect(await lint(files, tex)).toEqual([]);
|
|
140
143
|
});
|
|
141
144
|
|
|
@@ -148,13 +151,13 @@ describe("the references build step", () => {
|
|
|
148
151
|
expect(msgs.map((m) => [m.ruleId, m.severity])).toEqual([
|
|
149
152
|
["paper/refs-checked", 1],
|
|
150
153
|
]);
|
|
151
|
-
expect(msgs[0]
|
|
154
|
+
expect(msgs[0]?.message).toMatch(/cannot be reached/);
|
|
152
155
|
});
|
|
153
156
|
|
|
154
157
|
it("a checker that throws is recorded as not checked, not as a failed build", async () => {
|
|
155
|
-
const { files, out } = await build(TEX(ENTRIES),
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
const { files, out } = await build(TEX(ENTRIES), () =>
|
|
159
|
+
Promise.reject(new Error("DBLP exploded")),
|
|
160
|
+
);
|
|
158
161
|
expect(out.ok).toBe(true);
|
|
159
162
|
expect(readReferences(files, PAPER)?.why).toMatch(/DBLP exploded/);
|
|
160
163
|
});
|
|
@@ -167,9 +170,9 @@ const ANSWER: CachedResponse = {
|
|
|
167
170
|
/** A checker that fetches `key` unless the cache has it, recording the cache it was handed. */
|
|
168
171
|
function fetching(key: string) {
|
|
169
172
|
const seen: LookupCache[] = [];
|
|
170
|
-
const check: CheckReferences =
|
|
173
|
+
const check: CheckReferences = (_bib, cache) => {
|
|
171
174
|
seen.push(cache);
|
|
172
|
-
return {
|
|
175
|
+
return Promise.resolve({
|
|
173
176
|
check: { kind: "checked", entries: [verdict("schick2023")] },
|
|
174
177
|
cache: cache.citations.has(key)
|
|
175
178
|
? cache
|
|
@@ -177,12 +180,12 @@ function fetching(key: string) {
|
|
|
177
180
|
citations: new Map([...cache.citations, [key, ANSWER]]),
|
|
178
181
|
dblp: cache.dblp,
|
|
179
182
|
},
|
|
180
|
-
};
|
|
183
|
+
});
|
|
181
184
|
};
|
|
182
185
|
return { check, seen };
|
|
183
186
|
}
|
|
184
187
|
const cacheText = (files: ReturnType<typeof memoryFiles>): string | null => {
|
|
185
|
-
const b = files.readBytes(lookupCachePath(PAPER)
|
|
188
|
+
const b = files.readBytes(absolutePath(lookupCachePath(PAPER)));
|
|
186
189
|
return b === null ? null : new TextDecoder().decode(b);
|
|
187
190
|
};
|
|
188
191
|
|
|
@@ -210,23 +213,26 @@ describe("the lookup cache — <paper>/repro/references-cache.json", () => {
|
|
|
210
213
|
note: "references: 1 checked, 0 failing → _build/references.json; nothing fetched",
|
|
211
214
|
});
|
|
212
215
|
// The second build was handed exactly what the first one wrote.
|
|
213
|
-
expect(parseLookupCache(written
|
|
216
|
+
expect(parseLookupCache(present(written, "the written cache"))).toEqual(
|
|
217
|
+
ok(f.seen[1]),
|
|
218
|
+
);
|
|
214
219
|
expect(cacheText(files)).toBe(written);
|
|
215
220
|
});
|
|
216
221
|
});
|
|
217
222
|
|
|
218
223
|
describe("the lookup cache — counting and refusing", () => {
|
|
219
224
|
it("two new answers are counted as two", async () => {
|
|
220
|
-
const two: CheckReferences =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
[
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
const two: CheckReferences = (_bib, cache) =>
|
|
226
|
+
Promise.resolve({
|
|
227
|
+
check: { kind: "checked", entries: [verdict("schick2023")] },
|
|
228
|
+
cache: {
|
|
229
|
+
citations: new Map([
|
|
230
|
+
["a", ANSWER],
|
|
231
|
+
["b", ANSWER],
|
|
232
|
+
]),
|
|
233
|
+
dblp: cache.dblp,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
230
236
|
const { out } = await build(TEX(ENTRIES), two);
|
|
231
237
|
expect(out).toEqual({
|
|
232
238
|
ok: true,
|
|
@@ -271,8 +277,8 @@ describe("the reference rules", () => {
|
|
|
271
277
|
);
|
|
272
278
|
const msgs = await lint(files, tex);
|
|
273
279
|
expect(msgs.map((m) => m.ruleId)).toEqual(["paper/author-list"]);
|
|
274
|
-
expect(msgs[0]
|
|
275
|
-
expect(msgs[0]
|
|
280
|
+
expect(msgs[0]?.line).toBe(3);
|
|
281
|
+
expect(msgs[0]?.message).toMatch(/`schick2023`.*missing hambro/);
|
|
276
282
|
});
|
|
277
283
|
|
|
278
284
|
it("an identifier that provably fails → paper/cite-exists on that entry", async () => {
|
|
@@ -311,7 +317,7 @@ describe("the reference rules", () => {
|
|
|
311
317
|
const files = memoryFiles({});
|
|
312
318
|
const msgs = await lint(files, TEX(ENTRIES));
|
|
313
319
|
expect(msgs.map((m) => m.ruleId)).toEqual(["paper/refs-checked"]);
|
|
314
|
-
expect(msgs[0]
|
|
320
|
+
expect(msgs[0]?.message).toMatch(/npx paperlint build/);
|
|
315
321
|
});
|
|
316
322
|
|
|
317
323
|
it("a paper with no bibliography gets nothing", async () => {
|
|
@@ -354,9 +360,9 @@ const record = (
|
|
|
354
360
|
files: ReturnType<typeof memoryFiles>,
|
|
355
361
|
body: Record<string, unknown>,
|
|
356
362
|
) => {
|
|
357
|
-
const bib = bibliographyOf(files, PAPER)
|
|
363
|
+
const bib = present(bibliographyOf(files, PAPER), "the bibliography");
|
|
358
364
|
files.writeAtomic(
|
|
359
|
-
referencesPath(PAPER)
|
|
365
|
+
absolutePath(referencesPath(PAPER)),
|
|
360
366
|
new TextEncoder().encode(
|
|
361
367
|
JSON.stringify({
|
|
362
368
|
schema: 1,
|
|
@@ -374,7 +380,7 @@ describe("the reference rules over a record written by hand", () => {
|
|
|
374
380
|
record(files, { status: "not-checked", entries: [] });
|
|
375
381
|
const msgs = await lint(files, tex);
|
|
376
382
|
expect(msgs.map((m) => m.ruleId)).toEqual(["paper/refs-checked"]);
|
|
377
|
-
expect(msgs[0]
|
|
383
|
+
expect(msgs[0]?.message).toMatch(/no reason recorded/);
|
|
378
384
|
});
|
|
379
385
|
|
|
380
386
|
it("a verdict without a reason, and one for a key the bibliography lacks, still report", async () => {
|
|
@@ -410,16 +416,23 @@ describe("the reference rules over refs.bib, and on other files", () => {
|
|
|
410
416
|
expect(msgs.map((m) => [m.ruleId, m.line])).toEqual([
|
|
411
417
|
["paper/author-list", 1],
|
|
412
418
|
]);
|
|
413
|
-
expect(msgs[0]
|
|
419
|
+
expect(msgs[0]?.message).toMatch(/schick2023/);
|
|
414
420
|
});
|
|
415
421
|
|
|
416
422
|
it("only paper.tex is judged", () => {
|
|
417
423
|
const files = memoryFiles({});
|
|
418
424
|
const rules = referenceRules({ files });
|
|
419
425
|
for (const rule of Object.values(rules))
|
|
420
|
-
expect(
|
|
421
|
-
{
|
|
422
|
-
|
|
426
|
+
expect(
|
|
427
|
+
rule.create({
|
|
428
|
+
filename: `${PAPER}/notes.tex`,
|
|
429
|
+
cwd: "/work",
|
|
430
|
+
sourceCode: { getLocFromIndex: () => ({ line: 1, column: 0 }) },
|
|
431
|
+
report: () => {
|
|
432
|
+
throw new Error("a file that is not paper.tex is never reported");
|
|
433
|
+
},
|
|
434
|
+
}),
|
|
435
|
+
).toEqual({});
|
|
423
436
|
});
|
|
424
437
|
});
|
|
425
438
|
|
|
@@ -430,13 +443,14 @@ describe("the lookup cache — refreshed answers", () => {
|
|
|
430
443
|
const OLD: CachedResponse = { ...ANSWER, fetched: "2026-08-01" };
|
|
431
444
|
const put =
|
|
432
445
|
(answer: CachedResponse): CheckReferences =>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
446
|
+
(_bib, cache) =>
|
|
447
|
+
Promise.resolve({
|
|
448
|
+
check: { kind: "checked", entries: [verdict("schick2023")] },
|
|
449
|
+
cache: {
|
|
450
|
+
citations: new Map([...cache.citations, ["a", answer]]),
|
|
451
|
+
dblp: cache.dblp,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
440
454
|
const { files } = await build(TEX(ENTRIES), put(OLD));
|
|
441
455
|
const again = await referencesStep.run({
|
|
442
456
|
...ctx(files),
|
package/src/references.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* a pass would be the counter that counts what it never looked at.
|
|
29
29
|
*/
|
|
30
30
|
import { join } from "node:path";
|
|
31
|
-
import
|
|
31
|
+
import { callerPath } from "./caller-path.ts";
|
|
32
32
|
import { sha256Hex } from "./domain/sha256.ts";
|
|
33
33
|
import type { Files } from "./ports/files.ts";
|
|
34
34
|
import {
|
|
@@ -44,8 +44,8 @@ import type {
|
|
|
44
44
|
ReferencesCheck,
|
|
45
45
|
ReferencesRun,
|
|
46
46
|
} from "./ports/check-references.ts";
|
|
47
|
-
|
|
48
|
-
import {
|
|
47
|
+
import { bibRange } from "#eslint-rules/paper-typography";
|
|
48
|
+
import { messageOf } from "./domain/text.ts";
|
|
49
49
|
|
|
50
50
|
export const REFERENCES_SCHEMA = 1;
|
|
51
51
|
export const REFERENCES_FILE = "references.json";
|
|
@@ -60,9 +60,8 @@ export const lookupCachePath = (paperDir: string): string =>
|
|
|
60
60
|
export const referencesPath = (paperDir: string): string =>
|
|
61
61
|
join(paperDir, "_build", REFERENCES_FILE);
|
|
62
62
|
|
|
63
|
-
const at = (p: string): AbsolutePath => p as AbsolutePath;
|
|
64
63
|
const text = (files: Files, p: string): string | null => {
|
|
65
|
-
const b = files.readBytes(
|
|
64
|
+
const b = files.readBytes(callerPath(p));
|
|
66
65
|
return b === null ? null : new TextDecoder().decode(b);
|
|
67
66
|
};
|
|
68
67
|
|
|
@@ -109,6 +108,15 @@ export const documentOf = (
|
|
|
109
108
|
: { status: "not-checked" as const, why: check.why, entries: [] }),
|
|
110
109
|
});
|
|
111
110
|
|
|
111
|
+
/** A parsed `references.json` of this schema, its entries a list. */
|
|
112
|
+
const isReferencesDocument = (d: unknown): d is ReferencesDocument =>
|
|
113
|
+
typeof d === "object" &&
|
|
114
|
+
d !== null &&
|
|
115
|
+
"schema" in d &&
|
|
116
|
+
d.schema === REFERENCES_SCHEMA &&
|
|
117
|
+
"entries" in d &&
|
|
118
|
+
Array.isArray(d.entries);
|
|
119
|
+
|
|
112
120
|
/** The recorded verdicts, or null when there are none or they do not parse. */
|
|
113
121
|
export function readReferences(
|
|
114
122
|
files: Files,
|
|
@@ -117,10 +125,8 @@ export function readReferences(
|
|
|
117
125
|
const raw = text(files, referencesPath(paperDir));
|
|
118
126
|
if (raw === null) return null;
|
|
119
127
|
try {
|
|
120
|
-
const d = JSON.parse(raw)
|
|
121
|
-
return d
|
|
122
|
-
? d
|
|
123
|
-
: null;
|
|
128
|
+
const d: unknown = JSON.parse(raw);
|
|
129
|
+
return isReferencesDocument(d) ? d : null;
|
|
124
130
|
} catch {
|
|
125
131
|
return null;
|
|
126
132
|
}
|
|
@@ -159,7 +165,7 @@ async function run(
|
|
|
159
165
|
try {
|
|
160
166
|
return await check(bib, cache);
|
|
161
167
|
} catch (e) {
|
|
162
|
-
return { check: { kind: "not-checked", why: (e
|
|
168
|
+
return { check: { kind: "not-checked", why: messageOf(e) }, cache };
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
|
|
@@ -179,7 +185,7 @@ export async function recordReferences(
|
|
|
179
185
|
const record = (c: ReferencesCheck): ReferencesDocument => {
|
|
180
186
|
const doc = documentOf(bib, c);
|
|
181
187
|
files.writeAtomic(
|
|
182
|
-
|
|
188
|
+
callerPath(referencesPath(paperDir)),
|
|
183
189
|
new TextEncoder().encode(`${JSON.stringify(doc, null, 2)}\n`),
|
|
184
190
|
);
|
|
185
191
|
return doc;
|
|
@@ -199,7 +205,7 @@ export async function recordReferences(
|
|
|
199
205
|
const fetched = newAnswers(cache.value, result.cache);
|
|
200
206
|
if (fetched > 0)
|
|
201
207
|
files.writeAtomic(
|
|
202
|
-
|
|
208
|
+
callerPath(lookupCachePath(paperDir)),
|
|
203
209
|
new TextEncoder().encode(serializeLookupCache(result.cache)),
|
|
204
210
|
);
|
|
205
211
|
const bad = doc.entries.filter(
|
|
@@ -3,24 +3,18 @@
|
|
|
3
3
|
* paperlint ships. The end-to-end path (a project's paperlint.json → `paperlint lint`) is in `cli.harness.mjs`;
|
|
4
4
|
* this pins the parser's output shape and each refusal.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { createChecker } from "../lib/check.mjs";
|
|
6
|
+
import { createChecker } from "../lib/check.ts";
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
join(HERE, "rules-config.ts")
|
|
13
|
-
);
|
|
8
|
+
const { parseRuleBlocks, shippedRuleIds, unknownKeys } =
|
|
9
|
+
await import("./rules-config.ts");
|
|
14
10
|
|
|
15
11
|
const check = createChecker();
|
|
16
12
|
|
|
17
13
|
const SHIPPED = new Set(["pdf/last-page-balance", "paper/typography"]);
|
|
18
|
-
const parse = (raw) => parseRuleBlocks(raw, "S", SHIPPED, "/consumer");
|
|
14
|
+
const parse = (raw: unknown) => parseRuleBlocks(raw, "S", SHIPPED, "/consumer");
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
parse(undefined).ok && parse(undefined).value.length === 0,
|
|
23
|
-
);
|
|
16
|
+
const none = parse(undefined);
|
|
17
|
+
check("no `rules` key: no blocks", none.ok && none.value.length === 0);
|
|
24
18
|
{
|
|
25
19
|
const r = parse([
|
|
26
20
|
{
|
|
@@ -62,7 +56,7 @@ check(
|
|
|
62
56
|
parse([{ rules: { "paper/typography": 0, "pdf/last-page-balance": "warn" } }])
|
|
63
57
|
.ok,
|
|
64
58
|
);
|
|
65
|
-
const refusals = [
|
|
59
|
+
const refusals: [unknown, string][] = [
|
|
66
60
|
["pdf/last-page-balance", 'S → "rules" must be { "<rule>": "<severity>" }'],
|
|
67
61
|
[{ a: 1 }, '"a" is not a rule paperlint ships'],
|
|
68
62
|
[[42], "S → rules[0] must be an object"],
|
package/src/rules-config.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* would need that plugin, which JSON cannot carry. Which rules paperlint ships is read off its own
|
|
26
26
|
* config (`buildConfig`), not listed a second time.
|
|
27
27
|
*/
|
|
28
|
-
import { SETTINGS_KEYS } from "
|
|
28
|
+
import { SETTINGS_KEYS } from "#lib/paper-config";
|
|
29
29
|
|
|
30
30
|
/** ESLint's severities, in both of its spellings. */
|
|
31
31
|
export type Severity = "off" | "warn" | "error" | 0 | 1 | 2;
|
|
@@ -39,11 +39,40 @@ export interface RuleBlock {
|
|
|
39
39
|
readonly rules: Readonly<Record<string, RuleEntry>>;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A plugin as a config block registers it, typed to what paperlint reads of it: its rules, and the
|
|
44
|
+
* languages it brings (`tex` brings LaTeX).
|
|
45
|
+
*/
|
|
46
|
+
export interface ConfigPlugin {
|
|
47
|
+
readonly rules?: Readonly<Record<string, unknown>>;
|
|
48
|
+
readonly languages?: Readonly<Record<string, unknown>>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An ESLint config block as paperlint builds and reads one. Not `Linter.Config`: ESLint types a
|
|
53
|
+
* plugin's rule as one that runs on ANY language, and paperlint's rules are written for the language
|
|
54
|
+
* they read (LaTeX, markdown), so none of them is assignable to it. ESLint checks a block when it
|
|
55
|
+
* loads it; `eslintConfig` in cli.ts is the one place these blocks become `Linter.Config`.
|
|
56
|
+
*/
|
|
57
|
+
export interface ConfigBlock {
|
|
58
|
+
readonly name?: string;
|
|
59
|
+
readonly basePath?: string;
|
|
60
|
+
readonly files?: readonly (string | readonly string[])[];
|
|
61
|
+
readonly ignores?: readonly string[];
|
|
62
|
+
readonly plugins?: Readonly<Record<string, ConfigPlugin>>;
|
|
63
|
+
readonly language?: string;
|
|
64
|
+
readonly languageOptions?: Readonly<Record<string, unknown>>;
|
|
65
|
+
readonly linterOptions?: Readonly<Record<string, unknown>>;
|
|
66
|
+
readonly rules?: Readonly<Record<string, unknown>>;
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
export type Parsed<T> =
|
|
43
70
|
| { readonly ok: true; readonly value: T }
|
|
44
71
|
| { readonly ok: false; readonly error: string };
|
|
45
72
|
|
|
46
|
-
const SEVERITIES: readonly
|
|
73
|
+
const SEVERITIES: readonly Severity[] = ["off", "warn", "error", 0, 1, 2];
|
|
74
|
+
const isSeverity = (v: unknown): v is Severity =>
|
|
75
|
+
SEVERITIES.some((s) => s === v);
|
|
47
76
|
|
|
48
77
|
const BLOCK_KEYS = new Set(["files", "ignores", "rules"]);
|
|
49
78
|
|
|
@@ -61,9 +90,10 @@ const isStringList = (v: unknown): v is string[] =>
|
|
|
61
90
|
|
|
62
91
|
/** A severity, or `[severity, ...options]`. */
|
|
63
92
|
function parseEntry(v: unknown): RuleEntry | null {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
const entry: readonly unknown[] = Array.isArray(v) ? v : [v];
|
|
94
|
+
const [sev, ...options] = entry;
|
|
95
|
+
if (!isSeverity(sev)) return null;
|
|
96
|
+
return Array.isArray(v) ? [sev, ...options] : sev;
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
/**
|
|
@@ -105,8 +135,8 @@ function badGlobs(o: Record<string, unknown>, at: string): string | null {
|
|
|
105
135
|
|
|
106
136
|
/** The block's `files`/`ignores`, present only when given. */
|
|
107
137
|
const globsOf = (o: Record<string, unknown>) => ({
|
|
108
|
-
...(o["files"] ? { files: o["files"]
|
|
109
|
-
...(o["ignores"] ? { ignores: o["ignores"]
|
|
138
|
+
...(isStringList(o["files"]) ? { files: o["files"] } : {}),
|
|
139
|
+
...(isStringList(o["ignores"]) ? { ignores: o["ignores"] } : {}),
|
|
110
140
|
});
|
|
111
141
|
|
|
112
142
|
const isRecord = (v: unknown): v is Record<string, unknown> =>
|
|
@@ -126,7 +156,7 @@ function parseBlock(
|
|
|
126
156
|
const extra = Object.keys(o).filter((k) => !BLOCK_KEYS.has(k));
|
|
127
157
|
if (extra.length)
|
|
128
158
|
return bad(
|
|
129
|
-
`${at}: unknown key "${extra[0]}" — a block takes "files", "ignores" and "rules"`,
|
|
159
|
+
`${at}: unknown key "${String(extra[0])}" — a block takes "files", "ignores" and "rules"`,
|
|
130
160
|
);
|
|
131
161
|
const globs = badGlobs(o, at);
|
|
132
162
|
if (globs) return bad(globs);
|
|
@@ -167,7 +197,10 @@ export function parseRuleBlocks(
|
|
|
167
197
|
);
|
|
168
198
|
const out: RuleBlock[] = [];
|
|
169
199
|
for (const [i, v] of raw.entries()) {
|
|
170
|
-
const b = parseBlock(v, `${where} → rules[${i}]`, {
|
|
200
|
+
const b = parseBlock(v, `${where} → rules[${String(i)}]`, {
|
|
201
|
+
shipped,
|
|
202
|
+
baseDir,
|
|
203
|
+
});
|
|
171
204
|
if (!b.ok) return b;
|
|
172
205
|
out.push(b.value);
|
|
173
206
|
}
|
|
@@ -179,16 +212,15 @@ export function parseRuleBlocks(
|
|
|
179
212
|
* `foreign` (a dependency's plugin, such as `@eslint/markdown`) are not paperlint's and are skipped.
|
|
180
213
|
*/
|
|
181
214
|
export function shippedRuleIds(
|
|
182
|
-
config: readonly
|
|
215
|
+
config: readonly ConfigBlock[],
|
|
183
216
|
foreign: readonly unknown[],
|
|
184
217
|
): Set<string> {
|
|
185
218
|
const ids = new Set<string>();
|
|
186
219
|
for (const block of config) {
|
|
187
|
-
const
|
|
188
|
-
for (const [name, plugin] of Object.entries(plugins ?? {})) {
|
|
220
|
+
for (const [name, plugin] of Object.entries(block.plugins ?? {})) {
|
|
189
221
|
if (foreign.includes(plugin)) continue;
|
|
190
|
-
const
|
|
191
|
-
|
|
222
|
+
for (const rule of Object.keys(plugin.rules ?? {}))
|
|
223
|
+
ids.add(`${name}/${rule}`);
|
|
192
224
|
}
|
|
193
225
|
}
|
|
194
226
|
return ids;
|
package/src/rules-off.test.ts
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { ESLint, type Linter } from "eslint";
|
|
11
11
|
import { describe, expect, it } from "vitest";
|
|
12
|
-
|
|
13
|
-
import { texLanguage } from "../eslint-rules/latex-language.mjs";
|
|
12
|
+
import { texLanguage } from "../eslint-rules/latex-language.ts";
|
|
14
13
|
import { rulePlugins, rulesOff, SHIPPED_RULES } from "./cli.ts";
|
|
14
|
+
import { present } from "../test/support.ts";
|
|
15
15
|
|
|
16
16
|
const PAPER = [
|
|
17
17
|
"\\documentclass{article}",
|
|
@@ -45,7 +45,7 @@ async function lintAsConsumer(
|
|
|
45
45
|
const [res] = await eslint.lintText(PAPER, {
|
|
46
46
|
filePath: "papers/a/paper.tex",
|
|
47
47
|
});
|
|
48
|
-
return res
|
|
48
|
+
return present(res, "one lint result").messages;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
describe("rulesOff — paperlint's rules, registered and off, for a consumer's own ESLint run", () => {
|
|
@@ -100,7 +100,9 @@ describe("rulesOff — paperlint's rules, registered and off, for a consumer's o
|
|
|
100
100
|
"// eslint-disable-next-line paper/leading-zero -- nothing to silence\nconst x = 1;\n",
|
|
101
101
|
{ filePath: "src/x.js" },
|
|
102
102
|
);
|
|
103
|
-
expect(
|
|
103
|
+
expect(
|
|
104
|
+
present(res, "one lint result").messages.map((m) => m.message),
|
|
105
|
+
).toEqual([
|
|
104
106
|
"Unused eslint-disable directive (no problems were reported from 'paper/leading-zero').",
|
|
105
107
|
]);
|
|
106
108
|
});
|
|
@@ -15,18 +15,27 @@ import {
|
|
|
15
15
|
} from "node:fs";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
17
|
import { join, dirname } from "node:path";
|
|
18
|
-
import {
|
|
19
|
-
import { createChecker } from "../lib/check.
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { createChecker } from "../lib/check.ts";
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/** `asEslintResults` promises only `unknown[]`; these are the fields the checks read. */
|
|
22
|
+
const EslintResults = z.array(
|
|
23
|
+
z.object({
|
|
24
|
+
errorCount: z.number(),
|
|
25
|
+
warningCount: z.number(),
|
|
26
|
+
messages: z.array(
|
|
27
|
+
z.object({ ruleId: z.string().nullable(), severity: z.number() }),
|
|
28
|
+
),
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
22
31
|
const { checkStructure, formatStructure, asEslintResults, STRUCTURE_DEFAULTS } =
|
|
23
|
-
await import(
|
|
32
|
+
await import("./structure.ts");
|
|
24
33
|
|
|
25
34
|
const check = createChecker();
|
|
26
35
|
|
|
27
36
|
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-struct-")));
|
|
28
37
|
const papers = join(root, "papers");
|
|
29
|
-
const paper = (name, files) => {
|
|
38
|
+
const paper = (name: string, files: readonly string[]) => {
|
|
30
39
|
const dir = join(papers, name);
|
|
31
40
|
mkdirSync(dir, { recursive: true });
|
|
32
41
|
for (const f of files) {
|
|
@@ -50,7 +59,7 @@ try {
|
|
|
50
59
|
writeFileSync(join(papers, ".hidden", "paper.tex"), "x");
|
|
51
60
|
|
|
52
61
|
const f = checkStructure([papers], undefined, { cwd: root });
|
|
53
|
-
const at = (name) => f.filter((x) => x.file.endsWith(name));
|
|
62
|
+
const at = (name: string) => f.filter((x) => x.file.endsWith(name));
|
|
54
63
|
|
|
55
64
|
check(
|
|
56
65
|
"a complete paper directory — NOT A SINGLE finding",
|
|
@@ -63,24 +72,26 @@ try {
|
|
|
63
72
|
check(
|
|
64
73
|
"a missing scorecard — a finding",
|
|
65
74
|
at("no-scorecard").length === 1 &&
|
|
66
|
-
/missing `PIPELINE-STATUS\.md`/.test(
|
|
75
|
+
/missing `PIPELINE-STATUS\.md`/.test(
|
|
76
|
+
at("no-scorecard")[0]?.message ?? "",
|
|
77
|
+
),
|
|
67
78
|
);
|
|
68
79
|
check(
|
|
69
80
|
"and the message names the CONSEQUENCE, not a restatement of the condition",
|
|
70
|
-
/are skipped/.test(at("no-scorecard")[0]
|
|
81
|
+
/are skipped/.test(at("no-scorecard")[0]?.message ?? "") &&
|
|
71
82
|
/stage, source and research-question checks/.test(
|
|
72
|
-
at("no-scorecard")[0]
|
|
83
|
+
at("no-scorecard")[0]?.message ?? "",
|
|
73
84
|
),
|
|
74
85
|
);
|
|
75
86
|
check(
|
|
76
87
|
"and the consequence names THIS directory by name",
|
|
77
|
-
/no-scorecard/.test(at("no-scorecard")[0]
|
|
88
|
+
/no-scorecard/.test(at("no-scorecard")[0]?.message ?? ""),
|
|
78
89
|
);
|
|
79
90
|
check(
|
|
80
91
|
"a directory with no source — a finding, and BOTH accepted forms are listed",
|
|
81
92
|
at("no-source").length === 1 &&
|
|
82
|
-
/`paper\.tex`/.test(at("no-source")[0]
|
|
83
|
-
/`paper\.md`/.test(at("no-source")[0]
|
|
93
|
+
/`paper\.tex`/.test(at("no-source")[0]?.message ?? "") &&
|
|
94
|
+
/`paper\.md`/.test(at("no-source")[0]?.message ?? ""),
|
|
84
95
|
);
|
|
85
96
|
|
|
86
97
|
// 🔴 THE PAIRED HALF: detection is GENEROUS. Without this the check would scream about every
|
|
@@ -136,7 +147,7 @@ try {
|
|
|
136
147
|
);
|
|
137
148
|
|
|
138
149
|
// ── one schema for both halves ────────────────────────────────────────────────────────
|
|
139
|
-
const asResults = asEslintResults(f);
|
|
150
|
+
const asResults = EslintResults.parse(asEslintResults(f));
|
|
140
151
|
check(
|
|
141
152
|
"findings come back shaped like an ESLint result — `--json` stays one array",
|
|
142
153
|
asResults.length === 2 &&
|
|
@@ -144,7 +155,7 @@ try {
|
|
|
144
155
|
(r) =>
|
|
145
156
|
r.errorCount === 1 &&
|
|
146
157
|
r.warningCount === 0 &&
|
|
147
|
-
r.messages[0]
|
|
158
|
+
r.messages[0]?.ruleId === "structure/required-file" &&
|
|
148
159
|
r.messages[0].severity === 2,
|
|
149
160
|
),
|
|
150
161
|
);
|
|
@@ -156,7 +167,7 @@ try {
|
|
|
156
167
|
{ require: ["PIPELINE-STATUS.md", "refs.bib"] },
|
|
157
168
|
{ cwd: root },
|
|
158
169
|
).filter((x) => x.file.endsWith("no-source"));
|
|
159
|
-
return asEslintResults(two)[0]
|
|
170
|
+
return EslintResults.parse(asEslintResults(two))[0]?.errorCount === 2;
|
|
160
171
|
})(),
|
|
161
172
|
);
|
|
162
173
|
check(
|