paperlint 4.0.0 → 4.1.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.
Files changed (172) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +31 -11
  3. package/CONTRIBUTING.md +23 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/invocation.d.ts +1 -1
  6. package/dist/adapters/latex/document-class.d.ts +22 -0
  7. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  8. package/dist/adapters/latex/document-class.js +102 -0
  9. package/dist/adapters/latex/document-class.js.map +1 -0
  10. package/dist/adapters/latex/hidden.d.ts +16 -0
  11. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  12. package/dist/adapters/latex/hidden.js +49 -0
  13. package/dist/adapters/latex/hidden.js.map +1 -0
  14. package/dist/adapters/latex/index.d.ts +29 -0
  15. package/dist/adapters/latex/index.d.ts.map +1 -0
  16. package/dist/adapters/latex/index.js +18 -0
  17. package/dist/adapters/latex/index.js.map +1 -0
  18. package/dist/adapters/latex/nodes.d.ts +54 -0
  19. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  20. package/dist/adapters/latex/nodes.js +93 -0
  21. package/dist/adapters/latex/nodes.js.map +1 -0
  22. package/dist/adapters/latex/outline.d.ts +10 -0
  23. package/dist/adapters/latex/outline.d.ts.map +1 -0
  24. package/dist/adapters/latex/outline.js +39 -0
  25. package/dist/adapters/latex/outline.js.map +1 -0
  26. package/dist/adapters/latex/parse.d.ts +42 -0
  27. package/dist/adapters/latex/parse.d.ts.map +1 -0
  28. package/dist/adapters/latex/parse.js +94 -0
  29. package/dist/adapters/latex/parse.js.map +1 -0
  30. package/dist/adapters/latex/rendered.d.ts +10 -0
  31. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  32. package/dist/adapters/latex/rendered.js +39 -0
  33. package/dist/adapters/latex/rendered.js.map +1 -0
  34. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  35. package/dist/adapters/pdfjs/fill.js +12 -12
  36. package/dist/adapters/pdfjs/fill.js.map +1 -1
  37. package/dist/build.js +4 -4
  38. package/dist/build.js.map +1 -1
  39. package/dist/cli.d.ts.map +1 -1
  40. package/dist/cli.js +29 -5
  41. package/dist/cli.js.map +1 -1
  42. package/dist/domain/tex-document.d.ts +84 -0
  43. package/dist/domain/tex-document.d.ts.map +1 -0
  44. package/dist/domain/tex-document.js +43 -0
  45. package/dist/domain/tex-document.js.map +1 -0
  46. package/dist/facts-file.d.ts.map +1 -1
  47. package/dist/facts-file.js +2 -2
  48. package/dist/facts-file.js.map +1 -1
  49. package/dist/new-paper.d.ts +5 -0
  50. package/dist/new-paper.d.ts.map +1 -1
  51. package/dist/new-paper.js +3 -0
  52. package/dist/new-paper.js.map +1 -1
  53. package/dist/package-dirs.d.ts +5 -0
  54. package/dist/package-dirs.d.ts.map +1 -0
  55. package/dist/package-dirs.js +14 -0
  56. package/dist/package-dirs.js.map +1 -0
  57. package/dist/ports/latex.d.ts +24 -0
  58. package/dist/ports/latex.d.ts.map +1 -0
  59. package/dist/ports/latex.js +2 -0
  60. package/dist/ports/latex.js.map +1 -0
  61. package/dist/presets.d.ts +15 -2
  62. package/dist/presets.d.ts.map +1 -1
  63. package/dist/presets.js +19 -6
  64. package/dist/presets.js.map +1 -1
  65. package/dist/tex-requirements.d.ts +11 -0
  66. package/dist/tex-requirements.d.ts.map +1 -1
  67. package/dist/tex-requirements.js +14 -8
  68. package/dist/tex-requirements.js.map +1 -1
  69. package/dist/tex-venue-rules.d.ts +83 -0
  70. package/dist/tex-venue-rules.d.ts.map +1 -0
  71. package/dist/tex-venue-rules.js +254 -0
  72. package/dist/tex-venue-rules.js.map +1 -0
  73. package/dist/venue-rules.d.ts +1 -1
  74. package/dist/venue-rules.d.ts.map +1 -1
  75. package/docs/configuration.md +3 -2
  76. package/docs/e2e.md +69 -40
  77. package/docs/install.md +1 -1
  78. package/docs/prior-art/README.md +1 -1
  79. package/docs/prior-art/package-location.md +5 -5
  80. package/docs/prior-art/repro/README.md +1 -1
  81. package/docs/prior-art/test-tooling.md +21 -10
  82. package/docs/rules/tex/required-section.md +73 -0
  83. package/docs/rules/tex/template.md +62 -0
  84. package/docs/rules/tex/venue-leftover.md +71 -0
  85. package/docs/rules.md +70 -48
  86. package/docs/testing.md +17 -4
  87. package/docs/texlive-install-decision.md +1 -1
  88. package/docs/toolchain.md +2 -2
  89. package/eslint.config.mjs +155 -15
  90. package/fixtures/build-e2e/acmart/expect.json +26 -0
  91. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  92. package/fixtures/build-e2e/aidc/expect.json +14 -0
  93. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  94. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  95. package/fixtures/build-e2e/broken/expect.json +11 -0
  96. package/fixtures/build-e2e/cite/expect.json +27 -0
  97. package/fixtures/build-e2e/empty/expect.json +12 -0
  98. package/fixtures/build-e2e/fallback/expect.json +53 -0
  99. package/fixtures/build-e2e/guards/expect.json +6 -0
  100. package/fixtures/build-e2e/no-source/expect.json +10 -0
  101. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  102. package/fixtures/pdf-facts/README.md +1 -1
  103. package/fixtures/real-markdown-paper/baseline.ts +1 -1
  104. package/fixtures/render-paper/build-clean.sh +3 -5
  105. package/package.json +13 -2
  106. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  107. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  108. package/presets/aidc.jsonc +73 -0
  109. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  110. package/presets/ieee-conference.jsonc +66 -0
  111. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  112. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  113. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  114. package/scripts/check.harness.ts +64 -10
  115. package/scripts/check.test.ts +60 -13
  116. package/scripts/check.ts +83 -30
  117. package/scripts/test.test.ts +1 -1
  118. package/scripts/test.ts +1 -1
  119. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  120. package/skills/paper-pipeline/SKILL.md +2 -2
  121. package/skills/paper-pipeline/SKILL.md.spec.ts +1 -1
  122. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  123. package/skills/paper-pipeline/scripts/consumer.mjs +1 -15
  124. package/skills/render-paper/SKILL.md +2 -2
  125. package/skills/render-paper/SKILL.md.spec.ts +1 -1
  126. package/skills/render-paper/check-render.sh +16 -63
  127. package/skills/render-paper/render-paper.harness.mjs +17 -59
  128. package/src/adapters/banal/index.test.ts +1 -1
  129. package/src/adapters/banal/invocation.ts +1 -1
  130. package/src/adapters/banal/output.test.ts +1 -1
  131. package/src/adapters/banal/run.test.ts +1 -1
  132. package/src/adapters/banal/xml.test.ts +1 -1
  133. package/src/adapters/latex/document-class.ts +147 -0
  134. package/src/adapters/latex/hidden.ts +70 -0
  135. package/src/adapters/latex/index.test.ts +361 -0
  136. package/src/adapters/latex/index.ts +55 -0
  137. package/src/adapters/latex/nodes.ts +141 -0
  138. package/src/adapters/latex/outline.ts +64 -0
  139. package/src/adapters/latex/parse.ts +136 -0
  140. package/src/adapters/latex/rendered.ts +59 -0
  141. package/src/adapters/pdfjs/fill.test.ts +1 -1
  142. package/src/adapters/pdfjs/fill.ts +18 -10
  143. package/src/build.harness.mjs +5 -7
  144. package/src/build.ts +4 -4
  145. package/src/cli.harness.mjs +1 -1
  146. package/src/cli.test.ts +2 -8
  147. package/src/cli.ts +33 -8
  148. package/src/domain/tex-document.test.ts +80 -0
  149. package/src/domain/tex-document.ts +110 -0
  150. package/src/engine.harness.mjs +1 -1
  151. package/src/facts-file.test.ts +4 -7
  152. package/src/facts-file.ts +2 -2
  153. package/src/latex-loop.harness.ts +1 -1
  154. package/src/new-paper.test.ts +28 -3
  155. package/src/new-paper.ts +8 -0
  156. package/src/package-dirs.ts +17 -0
  157. package/src/paper-settings-commands.test.ts +6 -1
  158. package/src/ports/latex.ts +29 -0
  159. package/src/presets.test.ts +65 -4
  160. package/src/presets.ts +38 -15
  161. package/src/rule-cases.test.ts +474 -0
  162. package/src/rule-docs.test.ts +159 -0
  163. package/src/tex-requirements.harness.ts +13 -23
  164. package/src/tex-requirements.ts +35 -8
  165. package/src/tex-venue-rules.test.ts +659 -0
  166. package/src/tex-venue-rules.ts +429 -0
  167. package/src/toolchain.harness.mjs +1 -1
  168. package/src/venue-rules.test.ts +96 -4
  169. package/src/venue-rules.ts +1 -1
  170. package/templates/paper/paper.tex +2 -1
  171. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  172. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -16,8 +16,8 @@
16
16
  * `TexRequirements` and never looks at the text again; a malformed profile throws with the path
17
17
  * of every violation, it is never read as "no packages".
18
18
  *
19
- * Where the profiles live is answered by `packageVenuesDir()` in consumer.mjs — the one module
20
- * allowed to know where this package is installed (rule 10).
19
+ * Where the profiles live is answered by `presetsDir()` in `package-dirs.ts` — the one module
20
+ * allowed to know where this package's data is installed (rule 10).
21
21
  */
22
22
  // eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
23
23
  import { readFileSync, readdirSync } from "node:fs";
@@ -25,7 +25,7 @@ import { createRequire } from "node:module";
25
25
  import { join } from "node:path";
26
26
  // eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
27
27
  import Ajv from "ajv";
28
- import { packageVenuesDir } from "../skills/paper-pipeline/scripts/consumer.mjs";
28
+ import { presetsDir } from "./package-dirs.ts";
29
29
  import { CONFIG_FILE } from "#lib/paper-config";
30
30
  import { fieldOf } from "./domain/record.ts";
31
31
 
@@ -148,6 +148,13 @@ export const NO_FORMAT: VenueFormat = {
148
148
  kinds: new Map(),
149
149
  };
150
150
 
151
+ /** A section a venue requires: its exact title, and whether it must close the paper. */
152
+ export interface RequiredSection {
153
+ readonly title: string;
154
+ /** "last": after every section of the body; null: anywhere. */
155
+ readonly position: "last" | null;
156
+ }
157
+
151
158
  /**
152
159
  * One preset FILE, parsed — before its `extends` chain is resolved (`src/presets.ts` does that).
153
160
  * A preset is a venue or a template family: `{ extends?, name?, template?, format?, tex?, rules? }`.
@@ -157,7 +164,12 @@ export interface PresetFile {
157
164
  readonly extends: string | null;
158
165
  /** A display name for messages; the file name otherwise. */
159
166
  readonly name: string | null;
167
+ /** The `\\documentclass` the venue's template uses, as the file spells it; null when it names none. */
160
168
  readonly template: string | null;
169
+ /** Other names the venue goes by in a paper's text; empty when the file names none. */
170
+ readonly aliases: readonly string[];
171
+ /** Sections the venue requires; null when the file names none (a child's list replaces its parent's). */
172
+ readonly requiredSections: readonly RequiredSection[] | null;
161
173
  /** Null when the file declares no `tex` block (allowed only with `extends`). */
162
174
  readonly tex: TexRequirements | null;
163
175
  readonly format: VenueFormat;
@@ -188,6 +200,11 @@ interface PresetJson {
188
200
  readonly extends?: string;
189
201
  readonly name?: string;
190
202
  readonly template?: string;
203
+ readonly aliases?: readonly string[];
204
+ readonly required_sections?: readonly {
205
+ readonly title: string;
206
+ readonly position?: "last";
207
+ }[];
191
208
  /** The schema requires `packages` in a `tex` block; `tools` is optional. */
192
209
  readonly tex?: Pick<TexRequirements, "packages"> &
193
210
  Partial<Pick<TexRequirements, "tools">>;
@@ -225,6 +242,14 @@ function formatOf(j: FormatJson = {}): VenueFormat {
225
242
  };
226
243
  }
227
244
 
245
+ /** A preset's `required_sections`, or null when it names none. */
246
+ const sectionsOf = (
247
+ r: PresetJson["required_sections"],
248
+ ): readonly RequiredSection[] | null =>
249
+ r === undefined
250
+ ? null
251
+ : r.map((x) => ({ title: x.title, position: orNull(x.position) }));
252
+
228
253
  /**
229
254
  * The text of one preset file → the typed file, or an Error naming every problem. The ONE parser
230
255
  * of a preset: the toolchain reads its `tex`, the venue rules its `format`, the config its `rules`.
@@ -236,7 +261,7 @@ function formatOf(j: FormatJson = {}): VenueFormat {
236
261
  export function parsePreset(
237
262
  text: string,
238
263
  file: string,
239
- dir: string = packageVenuesDir(),
264
+ dir: string = presetsDir(),
240
265
  ): PresetFile {
241
266
  const parsed = typescript().parseConfigFileTextToJson(file, text);
242
267
  const config: unknown = parsed.config;
@@ -255,6 +280,8 @@ export function parsePreset(
255
280
  extends: orNull(j.extends),
256
281
  name: orNull(j.name),
257
282
  template: orNull(j.template),
283
+ aliases: j.aliases ?? [],
284
+ requiredSections: sectionsOf(j.required_sections),
258
285
  tex: j.tex ? { packages: j.tex.packages, tools: j.tex.tools ?? {} } : null,
259
286
  format: formatOf(j.format),
260
287
  rules: j.rules ?? {},
@@ -265,7 +292,7 @@ export function parsePreset(
265
292
  export function parseProfile(
266
293
  text: string,
267
294
  file: string,
268
- dir: string = packageVenuesDir(),
295
+ dir: string = presetsDir(),
269
296
  ): TexRequirements {
270
297
  return parsePreset(text, file, dir).tex ?? NO_REQUIREMENTS;
271
298
  }
@@ -287,7 +314,7 @@ function readProfile(dir: string, file: string): TexRequirements {
287
314
  }
288
315
 
289
316
  /** The venues that have a profile — every `.jsonc` in the directory except the base set. */
290
- export function venueNames(dir: string = packageVenuesDir()): string[] {
317
+ export function venueNames(dir: string = presetsDir()): string[] {
291
318
  return readdirSync(dir)
292
319
  .filter((f) => f.endsWith(PROFILE_EXT) && f !== BASE_PROFILE)
293
320
  .map((f) => f.slice(0, -PROFILE_EXT.length))
@@ -325,7 +352,7 @@ export function packageNames(tex: TexRequirements): string[] {
325
352
  */
326
353
  export function requirementsFor(
327
354
  preset: { readonly label: string; readonly tex: TexRequirements } | null,
328
- dir: string = packageVenuesDir(),
355
+ dir: string = presetsDir(),
329
356
  ): PaperRequirements {
330
357
  const base = readProfile(dir, BASE_PROFILE);
331
358
  return preset === null
@@ -345,7 +372,7 @@ export function requirementsFor(
345
372
  * builds any paper.
346
373
  */
347
374
  export function declaredUnion(
348
- dir: string = packageVenuesDir(),
375
+ dir: string = presetsDir(),
349
376
  extra: readonly TexRequirements[] = [],
350
377
  ): {
351
378
  readonly tex: TexRequirements;