paperlint 2.0.1 → 3.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.
Files changed (226) hide show
  1. package/CLAUDE.md +6 -1
  2. package/CONTRIBUTING.md +21 -0
  3. package/README.md +231 -180
  4. package/action.yml +4 -6
  5. package/dist/adapters/references/index.d.ts +13 -0
  6. package/dist/adapters/references/index.d.ts.map +1 -0
  7. package/dist/adapters/references/index.js +55 -0
  8. package/dist/adapters/references/index.js.map +1 -0
  9. package/dist/adapters/references/reach.io.d.ts +3 -0
  10. package/dist/adapters/references/reach.io.d.ts.map +1 -0
  11. package/dist/adapters/references/reach.io.js +14 -0
  12. package/dist/adapters/references/reach.io.js.map +1 -0
  13. package/dist/build-engine.d.ts.map +1 -1
  14. package/dist/build-engine.js +5 -2
  15. package/dist/build-engine.js.map +1 -1
  16. package/dist/build.d.ts +16 -1
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/build.js +40 -5
  19. package/dist/build.js.map +1 -1
  20. package/dist/cli.d.ts +37 -20
  21. package/dist/cli.d.ts.map +1 -1
  22. package/dist/cli.js +377 -251
  23. package/dist/cli.js.map +1 -1
  24. package/dist/doctor.d.ts.map +1 -1
  25. package/dist/doctor.js +37 -52
  26. package/dist/doctor.js.map +1 -1
  27. package/dist/facts-file.d.ts +33 -9
  28. package/dist/facts-file.d.ts.map +1 -1
  29. package/dist/facts-file.js +117 -17
  30. package/dist/facts-file.js.map +1 -1
  31. package/dist/hooks-settings.d.ts +0 -21
  32. package/dist/hooks-settings.d.ts.map +1 -1
  33. package/dist/hooks-settings.js +17 -135
  34. package/dist/hooks-settings.js.map +1 -1
  35. package/dist/init.d.ts +40 -19
  36. package/dist/init.d.ts.map +1 -1
  37. package/dist/init.js +144 -109
  38. package/dist/init.js.map +1 -1
  39. package/dist/link-skills.d.ts +1 -1
  40. package/dist/link-skills.d.ts.map +1 -1
  41. package/dist/link-skills.js +11 -29
  42. package/dist/link-skills.js.map +1 -1
  43. package/dist/new-paper.d.ts +2 -2
  44. package/dist/new-paper.d.ts.map +1 -1
  45. package/dist/new-paper.js +6 -2
  46. package/dist/new-paper.js.map +1 -1
  47. package/dist/paper-files.d.ts +63 -0
  48. package/dist/paper-files.d.ts.map +1 -0
  49. package/dist/paper-files.js +69 -0
  50. package/dist/paper-files.js.map +1 -0
  51. package/dist/paper-settings.d.ts +40 -0
  52. package/dist/paper-settings.d.ts.map +1 -0
  53. package/dist/paper-settings.js +144 -0
  54. package/dist/paper-settings.js.map +1 -0
  55. package/dist/ports/check-references.d.ts +31 -0
  56. package/dist/ports/check-references.d.ts.map +1 -0
  57. package/dist/ports/check-references.js +7 -0
  58. package/dist/ports/check-references.js.map +1 -0
  59. package/dist/presets.d.ts +84 -0
  60. package/dist/presets.d.ts.map +1 -0
  61. package/dist/presets.js +184 -0
  62. package/dist/presets.js.map +1 -0
  63. package/dist/reference-rules.d.ts +47 -0
  64. package/dist/reference-rules.d.ts.map +1 -0
  65. package/dist/reference-rules.js +156 -0
  66. package/dist/reference-rules.js.map +1 -0
  67. package/dist/references.d.ts +36 -0
  68. package/dist/references.d.ts.map +1 -0
  69. package/dist/references.js +91 -0
  70. package/dist/references.js.map +1 -0
  71. package/dist/rules-config.d.ts +11 -2
  72. package/dist/rules-config.d.ts.map +1 -1
  73. package/dist/rules-config.js +25 -11
  74. package/dist/rules-config.js.map +1 -1
  75. package/dist/structure.d.ts.map +1 -1
  76. package/dist/structure.js +3 -2
  77. package/dist/structure.js.map +1 -1
  78. package/dist/tex-requirements.d.ts +63 -7
  79. package/dist/tex-requirements.d.ts.map +1 -1
  80. package/dist/tex-requirements.js +91 -24
  81. package/dist/tex-requirements.js.map +1 -1
  82. package/dist/types.d.ts +20 -18
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/venue-rules.d.ts +109 -0
  85. package/dist/venue-rules.d.ts.map +1 -0
  86. package/dist/venue-rules.js +389 -0
  87. package/dist/venue-rules.js.map +1 -0
  88. package/docs/configuration.md +152 -77
  89. package/docs/e2e.md +1 -1
  90. package/docs/install.md +25 -40
  91. package/docs/optional-rules.md +26 -24
  92. package/docs/rules.md +181 -15
  93. package/eslint-rules/bib-reachable-entry.mjs +75 -0
  94. package/eslint-rules/latex-language.mjs +29 -1
  95. package/eslint-rules/paper-stages.harness.mjs +1 -117
  96. package/eslint-rules/paper-stages.mjs +0 -120
  97. package/eslint-rules/paper-stages.mutations.mjs +0 -76
  98. package/eslint-rules/paper-typography.mjs +267 -192
  99. package/eslint-rules/paper-typography.test.mjs +297 -0
  100. package/eslint-rules/papers.harness.mjs +8 -12
  101. package/eslint-rules/papers.mjs +17 -27
  102. package/eslint-rules/pdf-last-page-balance.mjs +23 -5
  103. package/eslint-rules/review-frontmatter.mjs +118 -0
  104. package/eslint-rules/review-frontmatter.schema.json +36 -0
  105. package/eslint-rules/review-frontmatter.test.mjs +118 -0
  106. package/eslint-rules/sibling-frontmatter.mjs +19 -0
  107. package/eslint-rules/sibling-frontmatter.schema.json +14 -0
  108. package/eslint.config.mjs +0 -15
  109. package/fixtures/build-e2e/acmart/paperlint.json +1 -0
  110. package/fixtures/real-markdown-paper/baseline.json +6 -7
  111. package/hooks/hooks.harness.mjs +39 -98
  112. package/hooks/hooks.mutations.mjs +6 -6
  113. package/hooks/paper-edit-guard.hook.d.mts +2 -3
  114. package/hooks/paper-edit-guard.hook.mjs +31 -42
  115. package/hooks/paper-skills-nudge.hook.mjs +21 -19
  116. package/hooks/paper-status-gates.hook.mjs +21 -19
  117. package/hooks/paper-status-gates.sh +8 -13
  118. package/lib/paper-config.d.mts +15 -18
  119. package/lib/paper-config.harness.mjs +18 -35
  120. package/lib/paper-config.mjs +90 -81
  121. package/lib/paper-config.mutations.mjs +2 -2
  122. package/lib/skill-checks.mjs +1 -1
  123. package/lib/skill-trigger-cases.harness.mjs +9 -8
  124. package/lib/skill-trigger-cases.mjs +7 -15
  125. package/package.json +1 -1
  126. package/scripts/eslint-report-guard.mjs +1 -1
  127. package/scripts/harness-api.frozen.json +0 -3
  128. package/scripts/mutation-batteries.frozen.json +4 -13
  129. package/skills/analyze-sibling-paper/SKILL.md +16 -12
  130. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +15 -11
  131. package/skills/cold-read-diff/SKILL.md +20 -1
  132. package/skills/cold-read-diff/SKILL.md.spec.ts +19 -0
  133. package/skills/find-venue/SKILL.md +5 -1
  134. package/skills/find-venue/SKILL.md.spec.ts +4 -0
  135. package/skills/grade-paper-writing/SKILL.md +20 -1
  136. package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
  137. package/skills/paper-pipeline/PIPELINE-MAP.md +14 -4
  138. package/skills/paper-pipeline/SKILL.md +4 -3
  139. package/skills/paper-pipeline/SKILL.md.spec.ts +3 -2
  140. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -1
  141. package/skills/paper-pipeline/scripts/consumer.harness.mjs +8 -12
  142. package/skills/paper-pipeline/scripts/consumer.mjs +19 -33
  143. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +6 -4
  144. package/skills/paper-pipeline/scripts/run-mechanical.mjs +2 -11
  145. package/skills/plan-paper-timeline/SKILL.md +2 -2
  146. package/skills/plan-paper-timeline/SKILL.md.spec.ts +1 -1
  147. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +1 -1
  148. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +2 -2
  149. package/skills/render-paper/check-render.sh +2 -3
  150. package/skills/render-paper/extract-pdf-facts.harness.mjs +5 -5
  151. package/skills/render-paper/extract-pdf-facts.mjs +4 -4
  152. package/skills/render-paper/render-paper.harness.mjs +2 -2
  153. package/skills/study-accepted-papers/SKILL.md +2 -1
  154. package/skills/study-accepted-papers/SKILL.md.spec.ts +1 -0
  155. package/skills/submit-paper/SKILL.md +9 -1
  156. package/skills/submit-paper/SKILL.md.spec.ts +8 -0
  157. package/skills/submit-paper/references/venues/acm-sigconf.jsonc +86 -0
  158. package/skills/submit-paper/references/venues/agenticdev.jsonc +34 -102
  159. package/skills/submit-paper/references/venues/aisec.jsonc +21 -82
  160. package/skills/submit-paper/references/venues/realm.jsonc +45 -43
  161. package/skills/submit-paper/references/venues/tex-base.jsonc +1 -1
  162. package/skills/submit-paper/references/venues/venue-profile.schema.json +119 -29
  163. package/skills/verify-citations/SKILL.md +10 -1
  164. package/skills/verify-citations/SKILL.md.spec.ts +9 -0
  165. package/skills/verify-citations/scripts/bib-authors.mjs +48 -26
  166. package/src/adapters/references/index.ts +102 -0
  167. package/src/adapters/references/reach.io.ts +12 -0
  168. package/src/build-engine.ts +10 -2
  169. package/src/build.harness.mjs +31 -22
  170. package/src/build.ts +65 -8
  171. package/src/cli.harness.mjs +151 -294
  172. package/src/cli.mutations.mjs +35 -44
  173. package/src/cli.ts +457 -288
  174. package/src/doctor.harness.mjs +41 -84
  175. package/src/doctor.mutations.mjs +9 -9
  176. package/src/doctor.ts +41 -61
  177. package/src/facts-file.test.ts +47 -7
  178. package/src/facts-file.ts +145 -24
  179. package/src/hooks-settings.harness.mjs +7 -92
  180. package/src/hooks-settings.mutations.mjs +2 -17
  181. package/src/hooks-settings.ts +18 -162
  182. package/src/init.test.ts +206 -0
  183. package/src/init.ts +193 -159
  184. package/src/link-skills.harness.mjs +1 -38
  185. package/src/link-skills.mutations.mjs +9 -5
  186. package/src/link-skills.ts +10 -34
  187. package/src/lint-fix.test.ts +120 -0
  188. package/src/new-paper.harness.mjs +9 -6
  189. package/src/new-paper.test.ts +133 -0
  190. package/src/new-paper.ts +7 -2
  191. package/src/paper-files.test.ts +290 -0
  192. package/src/paper-files.ts +144 -0
  193. package/src/paper-settings-commands.test.ts +274 -0
  194. package/src/paper-settings.test.ts +249 -0
  195. package/src/paper-settings.ts +208 -0
  196. package/src/ports/check-references.ts +28 -0
  197. package/src/presets.test.ts +236 -0
  198. package/src/presets.ts +297 -0
  199. package/src/reference-rules.ts +229 -0
  200. package/src/references.test.ts +209 -0
  201. package/src/references.ts +138 -0
  202. package/src/rules-config.harness.mjs +24 -8
  203. package/src/rules-config.ts +30 -13
  204. package/src/structure.harness.mjs +6 -4
  205. package/src/structure.ts +3 -2
  206. package/src/tex-requirements.harness.mjs +11 -17
  207. package/src/tex-requirements.ts +179 -29
  208. package/src/types.ts +30 -18
  209. package/src/venue-rules.test.ts +554 -0
  210. package/src/venue-rules.ts +571 -0
  211. package/templates/paper/paperlint.json +4 -0
  212. package/eslint-rules/doc-fields.harness.mjs +0 -336
  213. package/eslint-rules/doc-fields.mjs +0 -186
  214. package/eslint-rules/doc-fields.mutations.mjs +0 -96
  215. package/eslint-rules/paper-typography.harness.mjs +0 -291
  216. package/eslint-rules/paper-typography.mutations.mjs +0 -131
  217. package/eslint-rules/review-findings-cause.harness.mjs +0 -228
  218. package/eslint-rules/review-findings-cause.mjs +0 -135
  219. package/eslint-rules/review-findings-cause.mutations.mjs +0 -72
  220. package/fixtures/build-e2e/acmart/venue.json +0 -1
  221. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +0 -16
  222. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +0 -17
  223. package/fixtures/review-findings-cause/clean.md +0 -17
  224. package/fixtures/review-findings-cause/defect.md +0 -14
  225. package/fixtures/review-findings-cause/old-debt.md +0 -14
  226. package/fixtures/review-findings-cause/quiet-in-fence.md +0 -16
@@ -20,12 +20,13 @@
20
20
  * allowed to know where this package is installed (rule 10).
21
21
  */
22
22
  // eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
23
- import { existsSync, readFileSync, readdirSync } from "node:fs";
23
+ import { readFileSync, readdirSync } from "node:fs";
24
24
  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
28
  import { packageVenuesDir } from "../skills/paper-pipeline/scripts/consumer.mjs";
29
+ import { CONFIG_FILE } from "../lib/paper-config.mjs";
29
30
 
30
31
  /** CTAN package name → the names that prove it is installed. */
31
32
  export type PackageProofs = Readonly<Record<string, readonly string[]>>;
@@ -78,18 +79,133 @@ function violations(file: string, v: Validate): string[] {
78
79
  );
79
80
  }
80
81
 
82
+ /** One kind of paper a venue takes (`short`, `research`, …) and its page limits; null = not limited. */
83
+ export interface KindLimits {
84
+ readonly bodyPagesMax: number | null;
85
+ readonly refPagesMax: number | null;
86
+ }
87
+
88
+ /**
89
+ * The format a venue's call for papers sets — what the `pdf/*` venue rules judge a built PDF
90
+ * against. Every field the profile leaves out is null, and a rule that finds null checks nothing:
91
+ * a profile that does not name a number never makes one up.
92
+ */
93
+ export interface VenueFormat {
94
+ readonly pageWidthIn: number | null;
95
+ readonly pageHeightIn: number | null;
96
+ readonly columns: number | null;
97
+ readonly bodyPt: number | null;
98
+ readonly bodyPtTol: number | null;
99
+ readonly refPtMin: number | null;
100
+ readonly refPtMax: number | null;
101
+ /** The prefix a font name of the body text starts with (`LinLibertine`). */
102
+ readonly fontsText: string | null;
103
+ readonly fontsTitle: string | null;
104
+ readonly kinds: ReadonlyMap<string, KindLimits>;
105
+ }
106
+
107
+ /** Every format field a preset left out is null; a merge fills it from the parent. */
108
+ export const NO_FORMAT: VenueFormat = {
109
+ pageWidthIn: null,
110
+ pageHeightIn: null,
111
+ columns: null,
112
+ bodyPt: null,
113
+ bodyPtTol: null,
114
+ refPtMin: null,
115
+ refPtMax: null,
116
+ fontsText: null,
117
+ fontsTitle: null,
118
+ kinds: new Map(),
119
+ };
120
+
121
+ /**
122
+ * One preset FILE, parsed — before its `extends` chain is resolved (`src/presets.ts` does that).
123
+ * A preset is a venue or a template family: `{ extends?, name?, template?, format?, tex?, rules? }`.
124
+ */
125
+ export interface PresetFile {
126
+ /** The preset this one builds on: `paperlint:<name>` or a relative path. */
127
+ readonly extends: string | null;
128
+ /** A display name for messages; the file name otherwise. */
129
+ readonly name: string | null;
130
+ readonly template: string | null;
131
+ /** Null when the file declares no `tex` block (allowed only with `extends`). */
132
+ readonly tex: TexRequirements | null;
133
+ readonly format: VenueFormat;
134
+ /** rule id → ESLint entry, checked against the shipped rules where a config is built. */
135
+ readonly rules: Readonly<Record<string, unknown>>;
136
+ }
137
+
138
+ type KindsJson = Readonly<
139
+ Record<string, { body_pages_max?: number; ref_pages_max?: number }>
140
+ >;
141
+
142
+ /** A preset's `format` block after the schema accepted it. */
143
+ interface FormatJson {
144
+ readonly page_w_in?: number;
145
+ readonly page_h_in?: number;
146
+ readonly columns?: number;
147
+ readonly body_pt?: number;
148
+ readonly body_pt_tol?: number;
149
+ readonly ref_pt_min?: number;
150
+ readonly ref_pt_max?: number;
151
+ readonly fonts_text?: string;
152
+ readonly fonts_title?: string;
153
+ readonly kinds?: KindsJson;
154
+ }
155
+
156
+ /** The preset's JSON after the schema accepted it — the shape `venue-profile.schema.json` allows. */
157
+ interface PresetJson {
158
+ readonly extends?: string;
159
+ readonly name?: string;
160
+ readonly template?: string;
161
+ readonly tex?: Partial<TexRequirements>;
162
+ readonly format?: FormatJson;
163
+ readonly rules?: Readonly<Record<string, unknown>>;
164
+ }
165
+
166
+ /** An optional field as the typed preset holds it: absent is null. */
167
+ const orNull = <T>(v: T | undefined): T | null => (v === undefined ? null : v);
168
+
169
+ function kindsOf(k: KindsJson | undefined): ReadonlyMap<string, KindLimits> {
170
+ return new Map(
171
+ Object.entries(k ?? {}).map(([name, v]) => [
172
+ name,
173
+ {
174
+ bodyPagesMax: orNull(v.body_pages_max),
175
+ refPagesMax: orNull(v.ref_pages_max),
176
+ },
177
+ ]),
178
+ );
179
+ }
180
+
181
+ function formatOf(j: FormatJson = {}): VenueFormat {
182
+ return {
183
+ pageWidthIn: orNull(j.page_w_in),
184
+ pageHeightIn: orNull(j.page_h_in),
185
+ columns: orNull(j.columns),
186
+ bodyPt: orNull(j.body_pt),
187
+ bodyPtTol: orNull(j.body_pt_tol),
188
+ refPtMin: orNull(j.ref_pt_min),
189
+ refPtMax: orNull(j.ref_pt_max),
190
+ fontsText: orNull(j.fonts_text),
191
+ fontsTitle: orNull(j.fonts_title),
192
+ kinds: kindsOf(j.kinds),
193
+ };
194
+ }
195
+
81
196
  /**
82
- * The text of one profile → its typed requirements, or an Error naming every problem.
197
+ * The text of one preset file → the typed file, or an Error naming every problem. The ONE parser
198
+ * of a preset: the toolchain reads its `tex`, the venue rules its `format`, the config its `rules`.
83
199
  *
84
200
  * @param text the file's contents
85
201
  * @param file the name to put in messages
86
202
  * @param dir the directory holding the schema (the shipped venues directory by default)
87
203
  */
88
- export function parseProfile(
204
+ export function parsePreset(
89
205
  text: string,
90
206
  file: string,
91
207
  dir: string = packageVenuesDir(),
92
- ): TexRequirements {
208
+ ): PresetFile {
93
209
  const { config, error } = typescript().parseConfigFileTextToJson(file, text);
94
210
  if (error)
95
211
  throw new Error(
@@ -100,8 +216,38 @@ export function parseProfile(
100
216
  throw new Error(
101
217
  `${file} does not match ${SCHEMA_FILE}:\n ${violations(file, validate).join("\n ")}`,
102
218
  );
103
- const tex = (config as { tex: Partial<TexRequirements> }).tex;
104
- return { packages: tex.packages ?? {}, tools: tex.tools ?? {} };
219
+ const j = config as PresetJson;
220
+ return {
221
+ extends: orNull(j.extends),
222
+ name: orNull(j.name),
223
+ template: orNull(j.template),
224
+ tex: j.tex
225
+ ? { packages: j.tex.packages ?? {}, tools: j.tex.tools ?? {} }
226
+ : null,
227
+ format: formatOf(j.format),
228
+ rules: j.rules ?? {},
229
+ };
230
+ }
231
+
232
+ /** The text of one preset → the TeX requirements it declares itself (none without a `tex` block). */
233
+ export function parseProfile(
234
+ text: string,
235
+ file: string,
236
+ dir: string = packageVenuesDir(),
237
+ ): TexRequirements {
238
+ return parsePreset(text, file, dir).tex ?? NO_REQUIREMENTS;
239
+ }
240
+
241
+ /**
242
+ * The file a venue's profile lives in, or null when the name cannot be a venue (the base set is a
243
+ * profile file but not a venue). Whether the file exists is the caller's question.
244
+ */
245
+ export function profileFileOf(venue: string): string | null {
246
+ return venue === BASE_PROFILE.slice(0, -PROFILE_EXT.length) ||
247
+ venue.includes("/") ||
248
+ venue.includes("\\")
249
+ ? null
250
+ : `${venue}${PROFILE_EXT}`;
105
251
  }
106
252
 
107
253
  function readProfile(dir: string, file: string): TexRequirements {
@@ -141,41 +287,45 @@ export function packageNames(tex: TexRequirements): string[] {
141
287
  }
142
288
 
143
289
  /**
144
- * What ONE paper needs: the base set plus its venue's block. A paper with no venue, or a venue this
145
- * package has no profile for, gets the base set — and the source says which, so an ACM paper built
146
- * without `venue.json` is visibly running on the base set rather than silently.
290
+ * What ONE paper needs: the base set plus its venue preset's `tex` (the union over the preset's
291
+ * `extends` chain, resolved by `src/presets.ts`). A paper with no preset gets the base set, and the
292
+ * source says so, so an ACM paper built without a preset is visibly running on the base set.
147
293
  */
148
294
  export function requirementsFor(
149
- venue: string | null,
295
+ preset: { readonly label: string; readonly tex: TexRequirements } | null,
150
296
  dir: string = packageVenuesDir(),
151
297
  ): PaperRequirements {
152
298
  const base = readProfile(dir, BASE_PROFILE);
153
- if (venue === null)
154
- return { source: "the base set (no venue.json)", tex: base };
155
- const file = `${venue}${PROFILE_EXT}`;
156
- if (
157
- venue === BASE_PROFILE.slice(0, -PROFILE_EXT.length) ||
158
- !existsSync(join(dir, file))
159
- )
160
- return {
161
- source: `the base set (venue ${venue} has no profile in paperlint)`,
162
- tex: base,
163
- };
164
- return {
165
- source: `venue ${venue}`,
166
- tex: mergeRequirements(base, readProfile(dir, file)),
167
- };
299
+ return preset === null
300
+ ? {
301
+ source: `the base set (no venue preset in ${CONFIG_FILE})`,
302
+ tex: base,
303
+ }
304
+ : {
305
+ source: `venue ${preset.label}`,
306
+ tex: mergeRequirements(base, preset.tex),
307
+ };
168
308
  }
169
309
 
170
- /** Everything any profile declares — the set `paperlint toolchain` installs, so one tree builds any paper. */
171
- export function declaredUnion(dir: string = packageVenuesDir()): {
310
+ /**
311
+ * Everything any shipped preset declares, plus `extra` — the resolved presets of the project's own
312
+ * papers, which may live outside the package. The set `paperlint toolchain` installs, so one tree
313
+ * builds any paper.
314
+ */
315
+ export function declaredUnion(
316
+ dir: string = packageVenuesDir(),
317
+ extra: readonly TexRequirements[] = [],
318
+ ): {
172
319
  readonly tex: TexRequirements;
173
320
  readonly profiles: number;
174
321
  } {
175
322
  const venues = venueNames(dir);
176
- const tex = venues.reduce(
323
+ const shipped = venues.reduce(
177
324
  (acc, v) => mergeRequirements(acc, readProfile(dir, `${v}${PROFILE_EXT}`)),
178
325
  readProfile(dir, BASE_PROFILE),
179
326
  );
180
- return { tex, profiles: venues.length + 1 };
327
+ return {
328
+ tex: extra.reduce(mergeRequirements, shipped),
329
+ profiles: venues.length + 1,
330
+ };
181
331
  }
package/src/types.ts CHANGED
@@ -5,29 +5,27 @@ import type { RuleBlock } from "./rules-config.ts";
5
5
  * in someone's head: a typo in a config key read as "field not set", not as an error.
6
6
  */
7
7
 
8
- /** The consumer's settings — the `paperlint` key of its `package.json`. */
8
+ /**
9
+ * The project's settings — the root `paperlint.json`, optional. Every key is optional; the key list
10
+ * (and which keys a paper's own `paperlint.json` may also hold) is `SETTINGS_KEYS` in
11
+ * lib/paper-config.mjs.
12
+ */
9
13
  export interface PaperlintConfig {
10
- /** 🔴 REQUIRED. The paper director(ies), relative to the config file ITSELF. The field name is
14
+ /** The paper director(ies), relative to the project root. Default `papers`. The field name is
11
15
  * `PAPERS_DIR_FIELD` in lib/paper-config.mjs; code reads it through `papersDirOf()` in cli.ts. */
12
16
  papersDir?: string | string[];
13
- /** The command that prints the author list from .bib — each corpus has its own. */
14
- authorListCommand?: string;
15
- /** Per-paper typography debt: how many findings already exist; the number may only go down. */
16
- typographyDebt?: Record<string, Record<string, number>>;
17
- /** Frontmatter fields required for review notes, and the permitted values of each. */
18
- docFields?: Record<string, { values: string[] }>;
19
- /** Ignore review findings older than this date. */
20
- reviewSince?: string;
21
- /** How many findings a cold read must produce to count as a cold read. */
22
- minFindings?: number;
23
- /** The word with which review notes introduce the cause. */
24
- causeMarker?: string;
25
17
  /** Which files a paper directory must carry; `false` turns the check off entirely. */
26
18
  structure?: StructureConfig | false;
27
- /** REMOVED — `paperlint build` compiles the paper itself. Still typed so a leftover key can be named. */
28
- buildScripts?: unknown;
29
19
  /** ESLint config blocks appended after paperlint's own — PARSED by `parseSettings` in cli.ts. */
30
20
  rules?: readonly RuleBlock[];
21
+ /** The default venue preset of every paper that names none (`src/paper-settings.ts`). */
22
+ extends?: string | null;
23
+ /** The default kind of paper. */
24
+ kind?: string;
25
+ /** The default path of the built PDF, relative to each paper. */
26
+ pdf?: string;
27
+ /** A note for humans; ignored. */
28
+ $comment?: unknown;
31
29
  }
32
30
 
33
31
  export interface StructureConfig {
@@ -47,6 +45,8 @@ export interface Args {
47
45
  paths: string[];
48
46
  config: string | null;
49
47
  json: boolean;
48
+ /** `--fix`: `lint` writes every fix a rule offers, then reports what is left. */
49
+ fix: boolean;
50
50
  all: boolean;
51
51
  dryRun: boolean;
52
52
  /** `--check`: `toolchain` reports what is missing and changes nothing. */
@@ -102,5 +102,17 @@ export interface BuildResult {
102
102
 
103
103
  /** The result of reading the config: either data, or the exit code the caller exits with. */
104
104
  export type ConfigRead =
105
- | { opts: PaperlintConfig; configPath: string | null; code?: undefined }
106
- | { code: number; opts?: undefined; configPath?: undefined };
105
+ | {
106
+ opts: PaperlintConfig;
107
+ /** The root `paperlint.json`, or null when the project has none. */
108
+ configPath: string | null;
109
+ /** The project root: `papersDir` and the `rules` globs are relative to it. */
110
+ root: string;
111
+ code?: undefined;
112
+ }
113
+ | {
114
+ code: number;
115
+ opts?: undefined;
116
+ configPath?: undefined;
117
+ root?: undefined;
118
+ };