paperlint 7.1.0 → 8.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 +3 -0
- package/.github/workflows/deadlines.yml +100 -0
- package/README.md +6 -0
- package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
- package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.js +26 -0
- package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
- package/dist/adapters/hotcrp/deadlines.js +134 -0
- package/dist/adapters/hotcrp/deadlines.js.map +1 -0
- package/dist/adapters/hotcrp/index.d.ts +2 -0
- package/dist/adapters/hotcrp/index.d.ts.map +1 -1
- package/dist/adapters/hotcrp/index.js +2 -0
- package/dist/adapters/hotcrp/index.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1 -6
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +3 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -47
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +24 -0
- package/dist/cycle-rules.d.ts.map +1 -0
- package/dist/cycle-rules.js +212 -0
- package/dist/cycle-rules.js.map +1 -0
- package/dist/domain/cycle.d.ts +148 -0
- package/dist/domain/cycle.d.ts.map +1 -0
- package/dist/domain/cycle.js +281 -0
- package/dist/domain/cycle.js.map +1 -0
- package/dist/domain/deadline.d.ts +128 -0
- package/dist/domain/deadline.d.ts.map +1 -0
- package/dist/domain/deadline.js +281 -0
- package/dist/domain/deadline.js.map +1 -0
- package/dist/eslint-rules/cycle-stage.d.ts +39 -0
- package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
- package/dist/eslint-rules/cycle-stage.js +119 -0
- package/dist/eslint-rules/cycle-stage.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +10 -13
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/eslint-rules/paper-context.js +2 -2
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +22 -91
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/papers.d.ts +1 -3
- package/dist/eslint-rules/papers.d.ts.map +1 -1
- package/dist/eslint-rules/papers.js +2 -4
- package/dist/eslint-rules/papers.js.map +1 -1
- package/dist/eslint-rules/tex-build.d.ts.map +1 -1
- package/dist/eslint-rules/tex-build.js +8 -25
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/init.d.ts +1 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -9
- package/dist/init.js.map +1 -1
- package/dist/lib/markdown.d.ts +5 -81
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +5 -116
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -2
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/lib/skill-eval-fixture.d.ts +6 -4
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -1
- package/dist/lib/skill-eval-fixture.js +25 -18
- package/dist/lib/skill-eval-fixture.js.map +1 -1
- package/dist/new-paper.d.ts +5 -11
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +12 -23
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-prose.d.ts +17 -0
- package/dist/paper-prose.d.ts.map +1 -0
- package/dist/paper-prose.js +46 -0
- package/dist/paper-prose.js.map +1 -0
- package/dist/paper-settings.d.ts +7 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +61 -8
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/venue-deadlines.d.ts +14 -0
- package/dist/ports/venue-deadlines.d.ts.map +1 -0
- package/dist/ports/venue-deadlines.js +2 -0
- package/dist/ports/venue-deadlines.js.map +1 -0
- package/dist/presets.d.ts +12 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -11
- package/dist/presets.js.map +1 -1
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +4 -3
- package/dist/structure.js.map +1 -1
- package/dist/submission.d.ts +8 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +41 -10
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +3 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +19 -0
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +19 -0
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +69 -0
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts +10 -0
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +26 -10
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +146 -20
- package/docs/e2e.md +4 -6
- package/docs/prior-art/paper-folder-scaffolding.md +24 -29
- package/docs/rules/cycle/evidence.md +87 -0
- package/docs/rules/cycle/port-done.md +75 -0
- package/docs/rules/cycle/record.md +152 -0
- package/docs/rules/cycle/stage.md +85 -0
- package/docs/rules/tex/claim-provenance.md +0 -1
- package/docs/rules.md +48 -47
- package/docs/submission.md +5 -0
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint-rules/latex-language.harness.mjs +3 -3
- package/eslint-rules/latex-language.ts +10 -13
- package/eslint-rules/paper-context.ts +2 -2
- package/eslint-rules/paper-typography.test.mjs +17 -85
- package/eslint-rules/paper-typography.ts +22 -98
- package/eslint-rules/papers.harness.mjs +1 -1
- package/eslint-rules/papers.ts +2 -4
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/tex-build.ts +8 -25
- package/eslint.config.mjs +41 -2
- package/hooks/hooks.harness.ts +15 -15
- package/hooks/paper-edit-guard.hook.mjs +5 -8
- package/hooks/paper-edit-guard.test.ts +12 -3
- package/hooks/paper-skills-nudge.hook.mjs +2 -3
- package/hooks/paper-skills-nudge.test.ts +13 -3
- package/hooks/paper-status-gates.hook.mjs +2 -2
- package/hooks/paper-status-gates.test.ts +14 -2
- package/lib/markdown.test.mjs +5 -60
- package/lib/markdown.ts +9 -136
- package/lib/paper-config.ts +3 -2
- package/lib/skill-checks.test.mjs +4 -1
- package/lib/skill-eval-fixture.ts +25 -18
- package/package.json +1 -3
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/msr.jsonc +25 -1
- package/presets/msr.md +5 -1
- package/presets/realm.jsonc +1 -1
- package/presets/venue-profile.schema.json +39 -0
- package/scripts/check.ts +4 -0
- package/scripts/harness-api.frozen.json +0 -5
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/skills/cold-read-diff/SKILL.md +3 -3
- package/skills/cold-read-diff/SKILL.md.spec.ts +2 -2
- package/skills/grade-paper-writing/SKILL.md +14 -22
- package/skills/grade-paper-writing/SKILL.md.spec.ts +13 -21
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +40 -26
- package/skills/grade-paper-writing/prose-lint.mjs +108 -278
- package/skills/grade-paper-writing/prose-lint.test.ts +184 -81
- package/skills/paper-adversarial-review/SKILL.md +2 -2
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +9 -12
- package/skills/paper-pipeline/SKILL.md.spec.ts +8 -11
- package/skills/paper-pipeline/description-language.eval.mjs +25 -15
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +25 -15
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +25 -15
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +27 -16
- package/skills/paper-pipeline/pipeline-language.eval.mjs +25 -15
- package/skills/paper-pipeline/references/acceptance-gate.md +16 -20
- package/skills/paper-pipeline/references/artifact-checklist.md +4 -5
- package/skills/paper-pipeline/references/pipeline-status-template.md +1 -1
- package/skills/paper-pipeline/scripts/README.md +0 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +8 -72
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +48 -156
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +81 -75
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +3 -3
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/SKILL.md +2 -2
- package/skills/render-paper/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/extract-pdf-facts.mjs +2 -2
- package/skills/tighten-paper/SKILL.md +29 -42
- package/skills/tighten-paper/SKILL.md.spec.ts +28 -41
- package/skills/verify-citations/scripts/bib-authors.check.test.mjs +0 -13
- package/skills/verify-citations/scripts/bib-authors.cli.test.ts +23 -11
- package/skills/verify-citations/scripts/bib-authors.mjs +8 -93
- package/skills/verify-citations/scripts/bib-authors.test.mjs +1 -44
- package/src/adapters/hotcrp/cassette/README.md +22 -0
- package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
- package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
- package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
- package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
- package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
- package/src/adapters/hotcrp/deadlines.io.ts +27 -0
- package/src/adapters/hotcrp/deadlines.test.ts +170 -0
- package/src/adapters/hotcrp/deadlines.ts +161 -0
- package/src/adapters/hotcrp/index.ts +2 -0
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +1 -1
- package/src/build.ts +1 -6
- package/src/cli.harness.mjs +26 -46
- package/src/cli.test.ts +41 -40
- package/src/cli.ts +57 -58
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/cycle.test.ts +456 -0
- package/src/domain/cycle.ts +432 -0
- package/src/domain/deadline.test.ts +386 -0
- package/src/domain/deadline.ts +472 -0
- package/src/folder-venue-rule.test.ts +1 -1
- package/src/init.test.ts +6 -7
- package/src/init.ts +3 -22
- package/src/lint-fix.test.ts +10 -4
- package/src/new-paper.harness.mjs +9 -28
- package/src/new-paper.test.ts +7 -6
- package/src/new-paper.ts +11 -30
- package/src/paper-files.test.ts +5 -4
- package/src/paper-files.ts +4 -4
- package/src/paper-prose.test.ts +49 -0
- package/src/paper-prose.ts +67 -0
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/parse-args.test.ts +0 -1
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/presets.test.ts +105 -0
- package/src/presets.ts +43 -10
- package/src/rule-cases.test.ts +144 -0
- package/src/structure.harness.ts +5 -11
- package/src/structure.ts +4 -3
- package/src/submission.test.ts +105 -1
- package/src/submission.ts +73 -12
- package/src/tex-requirements.ts +31 -0
- package/src/tex-venue-rules.test.ts +76 -0
- package/src/tex-venue-rules.ts +85 -0
- package/src/types.ts +0 -2
- package/src/venue-rules.test.ts +82 -0
- package/src/venue-rules.ts +44 -12
- package/fixtures/real-markdown-paper/baseline.json +0 -22
- package/fixtures/real-markdown-paper/baseline.ts +0 -70
- package/skills/paper-pipeline/PIPELINE-MAP.md +0 -381
- package/skills/paper-pipeline/pipeline-firing.baseline.json +0 -270
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +0 -585
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +0 -492
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +0 -397
- package/skills/paper-pipeline/scripts/artifact-coverage.test.ts +0 -86
- package/skills/paper-pipeline/scripts/check-provenance.mjs +0 -184
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +0 -135
- package/skills/paper-pipeline/scripts/population-map.mjs +0 -188
- package/skills/paper-pipeline/scripts/population-map.test.mjs +0 -154
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +0 -266
- package/skills/paper-pipeline/scripts/round-diff.harness.ts +0 -907
- package/skills/paper-pipeline/scripts/round-diff.mjs +0 -583
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +0 -159
- package/skills/tighten-paper/__snapshots__/structure.test.ts.snap +0 -133
- package/skills/tighten-paper/structure.mjs +0 -364
- package/skills/tighten-paper/structure.test.ts +0 -117
- package/templates/paper/paper.md +0 -4
package/src/cli.harness.mjs
CHANGED
|
@@ -42,6 +42,10 @@ const { PROGRAMS } = await import(join(HERE, "doctor.ts"));
|
|
|
42
42
|
// loads vigiles without recording a check is reported as having verified nothing.
|
|
43
43
|
const check = createChecker();
|
|
44
44
|
|
|
45
|
+
/** A paper.tex whose whole body is `body`. */
|
|
46
|
+
const tex = (body) =>
|
|
47
|
+
`\\documentclass{article}\n\\begin{document}\n${body}\n\\end{document}\n`;
|
|
48
|
+
|
|
45
49
|
/** Runs the utility with output captured — quieter and faster than spawning a process. */
|
|
46
50
|
async function cli(args, cwd) {
|
|
47
51
|
// 🔴 THE STREAMS ARE SPLIT, AND THAT IS LOAD-BEARING. As long as the harness dumped log and
|
|
@@ -129,11 +133,11 @@ check(
|
|
|
129
133
|
const ruleBlocks = (c) => c.filter((b) => Array.isArray(b.files));
|
|
130
134
|
check(
|
|
131
135
|
"without a LaTeX language the config still gets built — a corpus with no .tex is not a reason to refuse",
|
|
132
|
-
Array.isArray(cfg) && ruleBlocks(cfg).length ===
|
|
136
|
+
Array.isArray(cfg) && ruleBlocks(cfg).length === 3,
|
|
133
137
|
);
|
|
134
138
|
check(
|
|
135
139
|
"with a LaTeX language two blocks are added: paper.tex, and the files it includes",
|
|
136
|
-
ruleBlocks(buildConfig({}, {})).length ===
|
|
140
|
+
ruleBlocks(buildConfig({}, {})).length === 5,
|
|
137
141
|
);
|
|
138
142
|
check(
|
|
139
143
|
"🔴 the project's paper TEMPLATE directory is ignored — flat config does not skip dot-directories",
|
|
@@ -178,7 +182,7 @@ check(
|
|
|
178
182
|
join(paper, "versions", "2026-07-22-submitted.pdf"),
|
|
179
183
|
"x".repeat(100),
|
|
180
184
|
);
|
|
181
|
-
writeFileSync(join(paper, "paper.
|
|
185
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
182
186
|
writeFileSync(
|
|
183
187
|
join(paper, "PIPELINE-STATUS.md"),
|
|
184
188
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: 100\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`,
|
|
@@ -703,7 +707,7 @@ check(
|
|
|
703
707
|
const status = (bytes) =>
|
|
704
708
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: ${bytes}\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`;
|
|
705
709
|
writeFileSync(join(paper, "versions", "s.tex"), "abcd");
|
|
706
|
-
writeFileSync(join(paper, "paper.
|
|
710
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
707
711
|
|
|
708
712
|
writeFileSync(join(paper, "PIPELINE-STATUS.md"), status(100));
|
|
709
713
|
const clean = await cli(["lint", "papers"], root);
|
|
@@ -788,7 +792,7 @@ check(
|
|
|
788
792
|
join(paper, "versions", "2026-07-22-submitted.pdf"),
|
|
789
793
|
"x".repeat(100),
|
|
790
794
|
);
|
|
791
|
-
writeFileSync(join(paper, "paper.
|
|
795
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
792
796
|
const status = (bytes) =>
|
|
793
797
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: ${bytes}\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`;
|
|
794
798
|
writeFileSync(join(paper, "PIPELINE-STATUS.md"), status(100));
|
|
@@ -897,8 +901,8 @@ check(
|
|
|
897
901
|
);
|
|
898
902
|
// Three § signs — `paper/section-word`, warn level and only warn.
|
|
899
903
|
writeFileSync(
|
|
900
|
-
join(paper, "paper.
|
|
901
|
-
"
|
|
904
|
+
join(paper, "paper.tex"),
|
|
905
|
+
tex("RQ1: does it hold?\n\nSee \u00a7 5 and \u00a7 6 and \u00a7 7."),
|
|
902
906
|
);
|
|
903
907
|
writeFileSync(
|
|
904
908
|
join(paper, "PIPELINE-STATUS.md"),
|
|
@@ -1040,7 +1044,7 @@ check(
|
|
|
1040
1044
|
const paper = join(tree, "papers", "p");
|
|
1041
1045
|
mkdirSync(paper, { recursive: true });
|
|
1042
1046
|
// Two findings of different kinds: a structural one (no scorecard) and a rule one (`§`).
|
|
1043
|
-
writeFileSync(join(paper, "paper.
|
|
1047
|
+
writeFileSync(join(paper, "paper.tex"), tex("See § 3 and §4."));
|
|
1044
1048
|
|
|
1045
1049
|
const inside = await cli(["lint", "--json", "papers"], tree);
|
|
1046
1050
|
const outside = await cli(
|
|
@@ -1273,22 +1277,10 @@ console.log(
|
|
|
1273
1277
|
|
|
1274
1278
|
// ── the new flags parse, and none of them becomes the directory argument ─────────────────
|
|
1275
1279
|
{
|
|
1276
|
-
const a = parseArgs([
|
|
1277
|
-
"init",
|
|
1278
|
-
"--yes",
|
|
1279
|
-
"--no-hooks",
|
|
1280
|
-
"--paper",
|
|
1281
|
-
"demo",
|
|
1282
|
-
"--format",
|
|
1283
|
-
"md",
|
|
1284
|
-
]);
|
|
1280
|
+
const a = parseArgs(["init", "--yes", "--no-hooks", "--paper", "demo"]);
|
|
1285
1281
|
check(
|
|
1286
1282
|
"init flags parse into fields, not into paths",
|
|
1287
|
-
a.yes &&
|
|
1288
|
-
a.noHooks &&
|
|
1289
|
-
a.paper === "demo" &&
|
|
1290
|
-
a.format === "md" &&
|
|
1291
|
-
a.paths.length === 0,
|
|
1283
|
+
a.yes && a.noHooks && a.paper === "demo" && a.paths.length === 0,
|
|
1292
1284
|
);
|
|
1293
1285
|
check("-y is --yes", parseArgs(["init", "-y"]).yes === true);
|
|
1294
1286
|
check(
|
|
@@ -1300,11 +1292,6 @@ console.log(
|
|
|
1300
1292
|
"🔴 --hooks=local is NOT implemented, and it is refused by name rather than read as a path",
|
|
1301
1293
|
r.code === 2 && /--hooks=local is not implemented/.test(r.out),
|
|
1302
1294
|
);
|
|
1303
|
-
const f = await cli(["init", "--format", "pdf"]);
|
|
1304
|
-
check(
|
|
1305
|
-
"an unknown --format is refused before anything is written",
|
|
1306
|
-
f.code === 2 && /--format must be one of tex, md/.test(f.out),
|
|
1307
|
-
);
|
|
1308
1295
|
}
|
|
1309
1296
|
|
|
1310
1297
|
// ── init wires the hooks, and a human can say no ─────────────────────────────────────────
|
|
@@ -1315,7 +1302,7 @@ console.log(
|
|
|
1315
1302
|
const mk = (name) => {
|
|
1316
1303
|
const dir = join(root, name);
|
|
1317
1304
|
mkdirSync(join(dir, "papers", "p1"), { recursive: true });
|
|
1318
|
-
writeFileSync(join(dir, "papers", "p1", "paper.
|
|
1305
|
+
writeFileSync(join(dir, "papers", "p1", "paper.tex"), "% P\n");
|
|
1319
1306
|
writeFileSync(
|
|
1320
1307
|
join(dir, "papers", "p1", "PIPELINE-STATUS.md"),
|
|
1321
1308
|
"---\n---\n",
|
|
@@ -1426,19 +1413,17 @@ console.log(
|
|
|
1426
1413
|
ask: async (q) => {
|
|
1427
1414
|
asked.push(q);
|
|
1428
1415
|
if (/create a first paper/.test(q)) return "first";
|
|
1429
|
-
if (/format:/.test(q)) return "md";
|
|
1430
1416
|
return "";
|
|
1431
1417
|
},
|
|
1432
|
-
createPaper: async (papersRoot, name
|
|
1433
|
-
made.push({ papersRoot, name
|
|
1418
|
+
createPaper: async (papersRoot, name) => {
|
|
1419
|
+
made.push({ papersRoot, name });
|
|
1434
1420
|
return 0;
|
|
1435
1421
|
},
|
|
1436
1422
|
});
|
|
1437
1423
|
check(
|
|
1438
|
-
"a human with no paper is offered one, and the answer
|
|
1424
|
+
"a human with no paper is offered one, and the answer reaches `paperlint new`'s routine",
|
|
1439
1425
|
made.length === 1 &&
|
|
1440
1426
|
made[0].name === "first" &&
|
|
1441
|
-
made[0].format === "md" &&
|
|
1442
1427
|
made[0].papersRoot === join(dir, "papers"),
|
|
1443
1428
|
);
|
|
1444
1429
|
}
|
|
@@ -1466,22 +1451,17 @@ console.log(
|
|
|
1466
1451
|
log: () => {},
|
|
1467
1452
|
interactive: false,
|
|
1468
1453
|
paper: "given",
|
|
1469
|
-
createPaper: async (papersRoot, name
|
|
1470
|
-
made.push({ name, format }),
|
|
1471
|
-
0
|
|
1472
|
-
),
|
|
1454
|
+
createPaper: async (papersRoot, name) => (made.push({ name }), 0),
|
|
1473
1455
|
});
|
|
1474
1456
|
check(
|
|
1475
|
-
"--paper creates it without a human
|
|
1476
|
-
made.length === 1 &&
|
|
1477
|
-
made[0].name === "given" &&
|
|
1478
|
-
made[0].format === "tex",
|
|
1457
|
+
"--paper creates it without a human",
|
|
1458
|
+
made.length === 1 && made[0].name === "given",
|
|
1479
1459
|
);
|
|
1480
1460
|
}
|
|
1481
1461
|
{
|
|
1482
1462
|
const dir = bare("has-one");
|
|
1483
1463
|
mkdirSync(join(dir, "papers", "p1"), { recursive: true });
|
|
1484
|
-
writeFileSync(join(dir, "papers", "p1", "paper.
|
|
1464
|
+
writeFileSync(join(dir, "papers", "p1", "paper.tex"), "% P\n");
|
|
1485
1465
|
const asked = [];
|
|
1486
1466
|
await init(dir, {
|
|
1487
1467
|
...base,
|
|
@@ -1569,7 +1549,7 @@ console.log(
|
|
|
1569
1549
|
`{"${PAPERS_DIR_FIELD}":"papers"}\n`,
|
|
1570
1550
|
);
|
|
1571
1551
|
mkdirSync(join(root, "papers", "p1"), { recursive: true });
|
|
1572
|
-
writeFileSync(join(root, "papers", "p1", "paper.
|
|
1552
|
+
writeFileSync(join(root, "papers", "p1", "paper.tex"), "% P\n");
|
|
1573
1553
|
writeFileSync(
|
|
1574
1554
|
join(root, "papers", "p1", "PIPELINE-STATUS.md"),
|
|
1575
1555
|
"---\n---\n",
|
|
@@ -1591,7 +1571,7 @@ console.log(
|
|
|
1591
1571
|
join(root, "papers", "p2", "PIPELINE-STATUS.md"),
|
|
1592
1572
|
readFileSync(join(root, "papers", ".template", "PIPELINE-STATUS.md")),
|
|
1593
1573
|
);
|
|
1594
|
-
writeFileSync(join(root, "papers", "p2", "paper.
|
|
1574
|
+
writeFileSync(join(root, "papers", "p2", "paper.tex"), "% P\n");
|
|
1595
1575
|
const r2 = await cli(["lint"], root);
|
|
1596
1576
|
check(
|
|
1597
1577
|
"and the same scorecard in a real paper folder fails — the ignore is scoped, not a blind spot",
|
|
@@ -1625,11 +1605,11 @@ console.log(
|
|
|
1625
1605
|
/names no venue preset yet/.test(r.out) &&
|
|
1626
1606
|
/\(0 errors, 1 warning\)/.test(r.out),
|
|
1627
1607
|
);
|
|
1628
|
-
const again = await cli(["new", "demo"
|
|
1608
|
+
const again = await cli(["new", "demo"], root);
|
|
1629
1609
|
check(
|
|
1630
1610
|
"a second run on the same folder adds nothing — the tex source already stands",
|
|
1631
1611
|
again.code === 0 &&
|
|
1632
|
-
|
|
1612
|
+
!/\+ /.test(again.out) &&
|
|
1633
1613
|
/kept — never overwritten/.test(again.out),
|
|
1634
1614
|
);
|
|
1635
1615
|
const bad = await cli(["new", "Demo"], root);
|
package/src/cli.test.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import assert from "node:assert/strict";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
|
-
import { existsSync } from "node:fs";
|
|
9
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { test } from "vitest";
|
|
@@ -50,14 +50,14 @@ test("parseArgs: the boolean flags of build and toolchain, and a value flag with
|
|
|
50
50
|
[
|
|
51
51
|
parseArgs(["build", "--all", "--dry-run", "papers/p"]),
|
|
52
52
|
parseArgs(["toolchain", "--check"]),
|
|
53
|
-
parseArgs(["new", "x", "--
|
|
53
|
+
parseArgs(["new", "x", "--paper"]),
|
|
54
54
|
parseArgs(["new", "x", "--venue="]),
|
|
55
55
|
parseArgs(["new", "x", "--kind", "short", "--max-warnings=3"]),
|
|
56
56
|
],
|
|
57
57
|
[
|
|
58
58
|
{ ...base, cmd: "build", all: true, dryRun: true, paths: ["papers/p"] },
|
|
59
59
|
{ ...base, cmd: "toolchain", check: true },
|
|
60
|
-
{ ...base, cmd: "new", paths: ["x"], missingValue: "--
|
|
60
|
+
{ ...base, cmd: "new", paths: ["x"], missingValue: "--paper" },
|
|
61
61
|
{ ...base, cmd: "new", paths: ["x"], missingValue: "--venue" },
|
|
62
62
|
{ ...base, cmd: "new", paths: ["x"], kind: "short", maxWarnings: 3 },
|
|
63
63
|
],
|
|
@@ -142,20 +142,18 @@ test("build refuses with no target, and --all over no papers", async () => {
|
|
|
142
142
|
);
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
-
test("new refuses
|
|
145
|
+
test("new refuses a broken config, and a config that names no papers directory", async () => {
|
|
146
146
|
const broken = join(root, "new-broken");
|
|
147
147
|
writeTree(broken, { "paperlint.json": "{ not json" });
|
|
148
148
|
const none = join(root, "new-none");
|
|
149
149
|
writeTree(none, { "paperlint.json": JSON.stringify({ papersDir: [] }) });
|
|
150
150
|
const results = [
|
|
151
|
-
await cli(["new", "p"
|
|
152
|
-
await cli(["new", "p"
|
|
153
|
-
await cli(["new", "p", "--format", "md"], none),
|
|
151
|
+
await cli(["new", "p"], broken),
|
|
152
|
+
await cli(["new", "p"], none),
|
|
154
153
|
];
|
|
155
154
|
assert.deepEqual(
|
|
156
155
|
results.map((r) => ({ code: r.code, err: r.err.split(":")[0] })),
|
|
157
156
|
[
|
|
158
|
-
{ code: 2, err: "--format must be one of tex, md — got `docx`" },
|
|
159
157
|
{ code: 2, err: join(broken, "paperlint.json is not valid JSON") },
|
|
160
158
|
{
|
|
161
159
|
code: 2,
|
|
@@ -170,7 +168,7 @@ test("new with several papers directories says which one it used", async () => {
|
|
|
170
168
|
writeTree(dir, {
|
|
171
169
|
"paperlint.json": JSON.stringify({ papersDir: ["a", "b"] }),
|
|
172
170
|
});
|
|
173
|
-
const r = await cli(["new", "p", "--
|
|
171
|
+
const r = await cli(["new", "p", "--yes"], dir);
|
|
174
172
|
assert.equal(
|
|
175
173
|
r.out.split("\n")[0],
|
|
176
174
|
"several papers directories are declared — using the first: a",
|
|
@@ -274,20 +272,20 @@ async function withoutTex<T>(dir: string, fn: () => Promise<T>): Promise<T> {
|
|
|
274
272
|
const noTexDir = join(root, "build-notex");
|
|
275
273
|
writeTree(noTexDir, {
|
|
276
274
|
"package.json": "{}",
|
|
277
|
-
"papers/
|
|
275
|
+
"papers/nosource/notes.txt": "x\n",
|
|
278
276
|
"papers/tex/paper.tex":
|
|
279
277
|
"\\documentclass{article}\\begin{document}x\\end{document}\n",
|
|
280
278
|
});
|
|
281
279
|
const venues = texInputsDir();
|
|
282
280
|
|
|
283
|
-
test("build: a
|
|
284
|
-
const
|
|
285
|
-
cli(["build", "papers/
|
|
281
|
+
test("build: a paper with no paper.tex is refused, naming paper.tex", async () => {
|
|
282
|
+
const none = await withoutTex(noTexDir, () =>
|
|
283
|
+
cli(["build", "papers/nosource"], noTexDir),
|
|
286
284
|
);
|
|
287
|
-
assert.deepEqual(
|
|
285
|
+
assert.deepEqual(none, {
|
|
288
286
|
code: 1,
|
|
289
287
|
out: [
|
|
290
|
-
"papers/
|
|
288
|
+
"papers/nosource",
|
|
291
289
|
` inputs: TEXINPUTS += the paper's directory, then ${venues}`,
|
|
292
290
|
" compile: refused — no paper.tex; paperlint compiles LaTeX, and this paper has none",
|
|
293
291
|
" measure: skipped — nothing is compiled",
|
|
@@ -295,7 +293,7 @@ test("build: a markdown-only paper is refused", async () => {
|
|
|
295
293
|
" ✗ nothing to compile: no paper.tex",
|
|
296
294
|
].join("\n"),
|
|
297
295
|
err:
|
|
298
|
-
"\nNo paper.tex in: papers/
|
|
296
|
+
"\nNo paper.tex in: papers/nosource.\n" +
|
|
299
297
|
'This is NOT "nothing to build" — paperlint compiles LaTeX, and these papers have no LaTeX source.\n' +
|
|
300
298
|
"Write the paper in paper.tex; `paperlint new <name>` creates one.",
|
|
301
299
|
});
|
|
@@ -344,6 +342,15 @@ test("no command prints the usage and exits 2; --kind with no value is refused",
|
|
|
344
342
|
assert.equal(parseArgs(["new", "x", "--kind"]).missingValue, "--kind");
|
|
345
343
|
});
|
|
346
344
|
|
|
345
|
+
test("--help names the cycles and where a deadline comes from — the only place a newcomer looks first", async () => {
|
|
346
|
+
const r = await cli(["--help"], root);
|
|
347
|
+
assert.equal(r.code, 0);
|
|
348
|
+
assert.match(r.out, /"cycles": \[ \{ "id"/);
|
|
349
|
+
assert.match(r.out, /A venue preset carries the portal's deadlines/);
|
|
350
|
+
assert.match(r.out, /"override": \{ "at", "reason", "evidence" \}/);
|
|
351
|
+
assert.match(r.out, /the earlier reading binds/);
|
|
352
|
+
});
|
|
353
|
+
|
|
347
354
|
test("src/cli.ts run as the program itself answers like the bin", () => {
|
|
348
355
|
const r = runNode(join(import.meta.dirname, "cli.ts"), ["--help"]);
|
|
349
356
|
assert.equal(r.status, 0);
|
|
@@ -390,7 +397,7 @@ test("build --dry-run on a paper with a venue plans for the preset's packages",
|
|
|
390
397
|
|
|
391
398
|
test("a paper's paperlint.json that does not parse stops the lint with its path", () => {
|
|
392
399
|
const dir = writeTree(join(root, "rules-broken"), {
|
|
393
|
-
"p/paper.
|
|
400
|
+
"p/paper.tex": "% P\n",
|
|
394
401
|
"p/paperlint.json": "{ nope",
|
|
395
402
|
});
|
|
396
403
|
const r = paperRuleBlocks([join(dir, "p")]);
|
|
@@ -435,15 +442,12 @@ test("silentOptionalRules: a rule no block names for any paper is silent — a g
|
|
|
435
442
|
);
|
|
436
443
|
});
|
|
437
444
|
|
|
438
|
-
test("init with no path
|
|
445
|
+
test("init with no path sets up the current directory, and its first paper is paper.tex", async () => {
|
|
439
446
|
const dir = writeTree(join(root, "init-here"), { "package.json": "{}" });
|
|
440
|
-
const r = await cli(
|
|
441
|
-
|
|
442
|
-
dir,
|
|
443
|
-
|
|
444
|
-
assert.equal(
|
|
445
|
-
existsSync(join(dir, "papers", "first", "paper.md")),
|
|
446
|
-
true,
|
|
447
|
+
const r = await cli(["init", "--yes", "--no-hooks", "--paper", "first"], dir);
|
|
448
|
+
assert.deepEqual(
|
|
449
|
+
readdirSync(join(dir, "papers", "first")).sort(),
|
|
450
|
+
["PIPELINE-STATUS.md", "paper.tex", "paperlint.json"],
|
|
447
451
|
r.out + r.err,
|
|
448
452
|
);
|
|
449
453
|
});
|
|
@@ -479,12 +483,9 @@ test("lint: a named directory with nothing paperlint lints is named in full when
|
|
|
479
483
|
test("new from inside the paper it names adds the missing files and names the paper in full", async () => {
|
|
480
484
|
const dir = writeTree(join(root, "new-inside"), {
|
|
481
485
|
"paperlint.json": JSON.stringify({ papersDir: "papers" }),
|
|
482
|
-
"papers/p/paper.
|
|
486
|
+
"papers/p/paper.tex": "% P\n",
|
|
483
487
|
});
|
|
484
|
-
const r = await cli(
|
|
485
|
-
["new", "p", "--format", "md", "--yes"],
|
|
486
|
-
join(dir, "papers", "p"),
|
|
487
|
-
);
|
|
488
|
+
const r = await cli(["new", "p", "--yes"], join(dir, "papers", "p"));
|
|
488
489
|
assert.match(
|
|
489
490
|
r.out,
|
|
490
491
|
new RegExp(
|
|
@@ -498,7 +499,7 @@ test("new with several directories, run from the first, names it in full", async
|
|
|
498
499
|
"paperlint.json": JSON.stringify({ papersDir: ["a", "b"] }),
|
|
499
500
|
"a/.keep": "",
|
|
500
501
|
});
|
|
501
|
-
const r = await cli(["new", "p", "--
|
|
502
|
+
const r = await cli(["new", "p", "--yes"], join(dir, "a"));
|
|
502
503
|
assert.equal(
|
|
503
504
|
r.out.split("\n")[0],
|
|
504
505
|
`several papers directories are declared — using the first: ${join(dir, "a")}`,
|
|
@@ -510,7 +511,7 @@ test("lint: an ESLint failure that is not an empty set is not swallowed", async
|
|
|
510
511
|
"paperlint.json": JSON.stringify({
|
|
511
512
|
rules: { "paper/stages": ["error", { x: 1 }] },
|
|
512
513
|
}),
|
|
513
|
-
"papers/p/paper.
|
|
514
|
+
"papers/p/paper.tex": "% P\n",
|
|
514
515
|
"papers/p/PIPELINE-STATUS.md": "---\nstages: []\n---\n# S\n",
|
|
515
516
|
});
|
|
516
517
|
await assert.rejects(cli(["lint"], dir), {
|
|
@@ -573,11 +574,11 @@ function newAtTerminal(cwd: string, answers: Record<string, string>) {
|
|
|
573
574
|
});
|
|
574
575
|
}
|
|
575
576
|
|
|
576
|
-
test("new at a terminal asks for the
|
|
577
|
-
const
|
|
578
|
-
const r = await newAtTerminal(
|
|
577
|
+
test("new at a terminal asks for the venue; the answer picks it", async () => {
|
|
578
|
+
const dir = writeTree(join(root, "new-tty"), { "package.json": "{}" });
|
|
579
|
+
const r = await newAtTerminal(dir, { "venue:": "none\n" });
|
|
579
580
|
assert.equal(r.status, 0, r.out);
|
|
580
|
-
assert.equal(existsSync(join(
|
|
581
|
+
assert.equal(existsSync(join(dir, "papers", "first", "paper.tex")), true);
|
|
581
582
|
});
|
|
582
583
|
|
|
583
584
|
test("chooseVenue: a question that fails (the stream ended) takes the default, no venue", async () => {
|
|
@@ -616,9 +617,9 @@ test("init's TeX Live install runs the toolchain over the project's papers, neve
|
|
|
616
617
|
assert.equal(typeof here.installed(), "boolean");
|
|
617
618
|
});
|
|
618
619
|
|
|
619
|
-
test("lint with its LaTeX language missing from the install still lints the
|
|
620
|
+
test("lint with its LaTeX language missing from the install still lints the scorecards", () => {
|
|
620
621
|
const dir = writeTree(join(root, "lint-no-tex-language"), {
|
|
621
|
-
"papers/p/
|
|
622
|
+
"papers/p/PIPELINE-STATUS.md": "---\nstages: []\n---\n# S\n",
|
|
622
623
|
"no-latex.mjs":
|
|
623
624
|
"import { register } from 'node:module';\n" +
|
|
624
625
|
"register('data:text/javascript,' + encodeURIComponent(\"export async function resolve(s, c, next) { if (s === '#eslint-rules/latex-language') throw Object.assign(new Error('gone'), { code: 'ERR_MODULE_NOT_FOUND' }); return next(s, c); }\"));\n",
|
|
@@ -628,9 +629,9 @@ test("lint with its LaTeX language missing from the install still lints the mark
|
|
|
628
629
|
nodeArgs: ["--import", join(dir, "no-latex.mjs")],
|
|
629
630
|
});
|
|
630
631
|
const whole = runNode(BIN, ["lint", "papers"], { cwd: dir });
|
|
631
|
-
// A
|
|
632
|
+
// A scorecard needs no LaTeX language: the same report, the same code.
|
|
632
633
|
assert.deepEqual(without, whole);
|
|
633
|
-
assert.match(whole.stdout, /missing `
|
|
634
|
+
assert.match(whole.stdout, /missing all of `paper\.tex`/);
|
|
634
635
|
});
|
|
635
636
|
|
|
636
637
|
test("`paperlint hook` with no name: exit 2, and the usage names an example", async () => {
|