docrev 0.9.18 → 0.10.1
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/.gitattributes +1 -1
- package/CHANGELOG.md +173 -149
- package/PLAN-tables-and-postprocess.md +850 -850
- package/README.md +431 -406
- package/bin/rev.js +11 -11
- package/bin/rev.ts +145 -145
- package/completions/rev.bash +127 -127
- package/completions/rev.ps1 +210 -210
- package/completions/rev.zsh +207 -207
- package/dist/lib/build.d.ts +8 -0
- package/dist/lib/build.d.ts.map +1 -1
- package/dist/lib/build.js +62 -6
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/commands/context.d.ts +1 -1
- package/dist/lib/commands/context.d.ts.map +1 -1
- package/dist/lib/commands/context.js +1 -1
- package/dist/lib/commands/context.js.map +1 -1
- package/dist/lib/commands/sections.js +7 -7
- package/dist/lib/commands/sections.js.map +1 -1
- package/dist/lib/commands/sync.d.ts.map +1 -1
- package/dist/lib/commands/sync.js +15 -14
- package/dist/lib/commands/sync.js.map +1 -1
- package/dist/lib/commands/utilities.js +164 -164
- package/dist/lib/commands/verify-anchors.js +6 -6
- package/dist/lib/commands/verify-anchors.js.map +1 -1
- package/dist/lib/commands/word-tools.js +8 -8
- package/dist/lib/grammar.js +3 -3
- package/dist/lib/macro-filter.lua +201 -0
- package/dist/lib/macros.d.ts +102 -0
- package/dist/lib/macros.d.ts.map +1 -0
- package/dist/lib/macros.js +218 -0
- package/dist/lib/macros.js.map +1 -0
- package/dist/lib/pdf-comments.js +44 -44
- package/dist/lib/plugins.js +57 -57
- package/dist/lib/pptx-color-filter.lua +37 -0
- package/dist/lib/pptx-themes.js +115 -115
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +34 -0
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/sections.d.ts +35 -0
- package/dist/lib/sections.d.ts.map +1 -1
- package/dist/lib/sections.js +81 -0
- package/dist/lib/sections.js.map +1 -1
- package/dist/lib/spelling.js +2 -2
- package/dist/lib/templates.js +387 -387
- package/dist/lib/themes.js +51 -51
- package/eslint.config.js +27 -27
- package/lib/anchor-match.ts +276 -276
- package/lib/annotations.ts +644 -644
- package/lib/build.ts +1766 -1694
- package/lib/citations.ts +160 -160
- package/lib/commands/build.ts +855 -855
- package/lib/commands/citations.ts +515 -515
- package/lib/commands/comments.ts +1050 -1050
- package/lib/commands/context.ts +176 -174
- package/lib/commands/core.ts +309 -309
- package/lib/commands/doi.ts +435 -435
- package/lib/commands/file-ops.ts +372 -372
- package/lib/commands/history.ts +320 -320
- package/lib/commands/index.ts +87 -87
- package/lib/commands/init.ts +259 -259
- package/lib/commands/merge-resolve.ts +378 -378
- package/lib/commands/preview.ts +178 -178
- package/lib/commands/project-info.ts +244 -244
- package/lib/commands/quality.ts +517 -517
- package/lib/commands/response.ts +454 -454
- package/lib/commands/section-boundaries.ts +82 -82
- package/lib/commands/sections.ts +451 -451
- package/lib/commands/sync.ts +709 -706
- package/lib/commands/text-ops.ts +449 -449
- package/lib/commands/utilities.ts +448 -448
- package/lib/commands/verify-anchors.ts +272 -272
- package/lib/commands/word-tools.ts +340 -340
- package/lib/comment-realign.ts +517 -517
- package/lib/config.ts +84 -84
- package/lib/crossref.ts +781 -781
- package/lib/csl.ts +191 -191
- package/lib/dependencies.ts +98 -98
- package/lib/diff-engine.ts +465 -465
- package/lib/doi-cache.ts +115 -115
- package/lib/doi.ts +897 -897
- package/lib/equations.ts +506 -506
- package/lib/errors.ts +346 -346
- package/lib/format.ts +541 -541
- package/lib/git.ts +326 -326
- package/lib/grammar.ts +303 -303
- package/lib/image-registry.ts +180 -180
- package/lib/import.ts +911 -911
- package/lib/journals.ts +543 -543
- package/lib/macro-filter.lua +201 -0
- package/lib/macros.ts +273 -0
- package/lib/merge.ts +633 -633
- package/lib/orcid.ts +144 -144
- package/lib/pdf-comments.ts +263 -263
- package/lib/pdf-import.ts +524 -524
- package/lib/plugins.ts +362 -362
- package/lib/postprocess.ts +188 -188
- package/lib/pptx-color-filter.lua +37 -37
- package/lib/pptx-template.ts +469 -469
- package/lib/pptx-themes.ts +483 -483
- package/lib/protect-restore.ts +520 -520
- package/lib/rate-limiter.ts +94 -94
- package/lib/response.ts +197 -197
- package/lib/restore-references.ts +240 -240
- package/lib/review.ts +327 -327
- package/lib/schema.ts +488 -454
- package/lib/scientific-words.ts +73 -73
- package/lib/sections.ts +425 -335
- package/lib/slides.ts +756 -756
- package/lib/spelling.ts +334 -334
- package/lib/templates.ts +526 -526
- package/lib/themes.ts +742 -742
- package/lib/trackchanges.ts +247 -247
- package/lib/tui.ts +450 -450
- package/lib/types.ts +550 -550
- package/lib/undo.ts +250 -250
- package/lib/utils.ts +69 -69
- package/lib/variables.ts +179 -179
- package/lib/word-extraction.ts +806 -806
- package/lib/word.ts +643 -643
- package/lib/wordcomments.ts +840 -840
- package/package.json +137 -137
- package/scripts/postbuild.js +47 -28
- package/skill/REFERENCE.md +539 -539
- package/skill/SKILL.md +295 -295
- package/tsconfig.json +26 -26
- package/types/index.d.ts +525 -525
- package/issues.md +0 -180
- package/site/assets/extra.css +0 -208
- package/site/commands.html +0 -926
- package/site/configuration.html +0 -469
- package/site/index.html +0 -288
- package/site/troubleshooting.html +0 -461
- package/site/workflow.html +0 -518
package/lib/schema.ts
CHANGED
|
@@ -1,454 +1,488 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON Schema validation for rev.yaml configuration
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Validation error
|
|
7
|
-
*/
|
|
8
|
-
interface ValidationError {
|
|
9
|
-
path: string;
|
|
10
|
-
message: string;
|
|
11
|
-
value?: unknown;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Validation warning
|
|
16
|
-
*/
|
|
17
|
-
interface ValidationWarning {
|
|
18
|
-
path: string;
|
|
19
|
-
message: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Validation result
|
|
24
|
-
*/
|
|
25
|
-
interface ValidationResult {
|
|
26
|
-
valid: boolean;
|
|
27
|
-
errors: ValidationError[];
|
|
28
|
-
warnings: ValidationWarning[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* JSON Schema type
|
|
33
|
-
*/
|
|
34
|
-
interface Schema {
|
|
35
|
-
$schema?: string;
|
|
36
|
-
title?: string;
|
|
37
|
-
description?: string;
|
|
38
|
-
type?: string;
|
|
39
|
-
properties?: Record<string, Schema>;
|
|
40
|
-
required?: string[];
|
|
41
|
-
items?: Schema;
|
|
42
|
-
oneOf?: Schema[];
|
|
43
|
-
enum?: string[];
|
|
44
|
-
pattern?: string;
|
|
45
|
-
format?: string;
|
|
46
|
-
minimum?: number;
|
|
47
|
-
maximum?: number;
|
|
48
|
-
minItems?: number;
|
|
49
|
-
maxItems?: number;
|
|
50
|
-
additionalProperties?: boolean;
|
|
51
|
-
default?: unknown;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* JSON Schema for rev.yaml
|
|
56
|
-
*/
|
|
57
|
-
export const revYamlSchema: Schema = {
|
|
58
|
-
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
59
|
-
title: 'rev.yaml configuration',
|
|
60
|
-
description: 'Configuration file for docrev document workflow',
|
|
61
|
-
type: 'object',
|
|
62
|
-
properties: {
|
|
63
|
-
title: {
|
|
64
|
-
type: 'string',
|
|
65
|
-
description: 'Document title',
|
|
66
|
-
},
|
|
67
|
-
version: {
|
|
68
|
-
type: 'string',
|
|
69
|
-
description: 'Document version',
|
|
70
|
-
},
|
|
71
|
-
authors: {
|
|
72
|
-
type: 'array',
|
|
73
|
-
description: 'List of authors',
|
|
74
|
-
items: {
|
|
75
|
-
oneOf: [
|
|
76
|
-
{ type: 'string' },
|
|
77
|
-
{
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
name: { type: 'string' },
|
|
81
|
-
affiliation: { type: 'string' },
|
|
82
|
-
email: { type: 'string', format: 'email' },
|
|
83
|
-
orcid: { type: 'string', pattern: '^\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]$' },
|
|
84
|
-
},
|
|
85
|
-
required: ['name'],
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
journal: {
|
|
91
|
-
type: 'string',
|
|
92
|
-
description: 'Journal profile name for formatting defaults and validation',
|
|
93
|
-
},
|
|
94
|
-
'pandoc-args': {
|
|
95
|
-
type: 'array',
|
|
96
|
-
description: 'Extra pandoc args applied to every format build (e.g. --lua-filter=...). Format-specific lists are appended after these; --pandoc-arg CLI values are appended last.',
|
|
97
|
-
items: { type: 'string' },
|
|
98
|
-
},
|
|
99
|
-
output: {
|
|
100
|
-
type: 'object',
|
|
101
|
-
description: 'Per-format output filenames. Keys are format names (pdf, docx, tex, beamer, pptx); values are paths. Relative paths resolve under outputDir; absolute paths are honored as-is. Extension auto-added if missing. CLI `-o` overrides this map.',
|
|
102
|
-
properties: {
|
|
103
|
-
pdf: { type: 'string' },
|
|
104
|
-
docx: { type: 'string' },
|
|
105
|
-
tex: { type: 'string' },
|
|
106
|
-
beamer: { type: 'string' },
|
|
107
|
-
pptx: { type: 'string' },
|
|
108
|
-
},
|
|
109
|
-
additionalProperties: false,
|
|
110
|
-
},
|
|
111
|
-
sections: {
|
|
112
|
-
type: 'array',
|
|
113
|
-
description: 'Ordered list of section files to include',
|
|
114
|
-
items: { type: 'string', pattern: '.*\\.md$' },
|
|
115
|
-
},
|
|
116
|
-
bibliography: {
|
|
117
|
-
type: 'string',
|
|
118
|
-
description: 'Path to bibliography file (.bib)',
|
|
119
|
-
pattern: '.*\\.bib$',
|
|
120
|
-
},
|
|
121
|
-
csl: {
|
|
122
|
-
type: 'string',
|
|
123
|
-
description: 'Path to CSL citation style file',
|
|
124
|
-
},
|
|
125
|
-
crossref: {
|
|
126
|
-
type: 'object',
|
|
127
|
-
description: 'pandoc-crossref settings',
|
|
128
|
-
properties: {
|
|
129
|
-
figureTitle: { type: 'string', default: 'Figure' },
|
|
130
|
-
tableTitle: { type: 'string', default: 'Table' },
|
|
131
|
-
figPrefix: {
|
|
132
|
-
oneOf: [
|
|
133
|
-
{ type: 'string' },
|
|
134
|
-
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
135
|
-
],
|
|
136
|
-
},
|
|
137
|
-
tblPrefix: {
|
|
138
|
-
oneOf: [
|
|
139
|
-
{ type: 'string' },
|
|
140
|
-
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
141
|
-
],
|
|
142
|
-
},
|
|
143
|
-
eqnPrefix: {
|
|
144
|
-
oneOf: [
|
|
145
|
-
{ type: 'string' },
|
|
146
|
-
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
secPrefix: {
|
|
150
|
-
oneOf: [
|
|
151
|
-
{ type: 'string' },
|
|
152
|
-
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
153
|
-
],
|
|
154
|
-
},
|
|
155
|
-
linkReferences: { type: 'boolean', default: true },
|
|
156
|
-
},
|
|
157
|
-
additionalProperties: true,
|
|
158
|
-
},
|
|
159
|
-
pdf: {
|
|
160
|
-
type: 'object',
|
|
161
|
-
description: 'PDF output settings',
|
|
162
|
-
properties: {
|
|
163
|
-
template: { type: 'string' },
|
|
164
|
-
documentclass: {
|
|
165
|
-
type: 'string',
|
|
166
|
-
enum: ['article', 'report', 'book', 'memoir', 'scrartcl', 'scrreprt', 'scrbook'],
|
|
167
|
-
default: 'article',
|
|
168
|
-
},
|
|
169
|
-
fontsize: {
|
|
170
|
-
type: 'string',
|
|
171
|
-
pattern: '^\\d{1,2}pt$',
|
|
172
|
-
default: '12pt',
|
|
173
|
-
},
|
|
174
|
-
geometry: { type: 'string', default: 'margin=1in' },
|
|
175
|
-
linestretch: { type: 'number', minimum: 1, maximum: 3, default: 1.5 },
|
|
176
|
-
numbersections: { type: 'boolean', default: false },
|
|
177
|
-
toc: { type: 'boolean', default: false },
|
|
178
|
-
header: { type: 'string' },
|
|
179
|
-
footer: { type: 'string' },
|
|
180
|
-
'pandoc-args': {
|
|
181
|
-
type: 'array',
|
|
182
|
-
description: 'Extra pandoc args for PDF builds. Appended after the top-level pandoc-args list.',
|
|
183
|
-
items: { type: 'string' },
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
additionalProperties: true,
|
|
187
|
-
},
|
|
188
|
-
docx: {
|
|
189
|
-
type: 'object',
|
|
190
|
-
description: 'Word output settings',
|
|
191
|
-
properties: {
|
|
192
|
-
reference: { type: 'string', description: 'Reference document for styling' },
|
|
193
|
-
keepComments: { type: 'boolean', default: true },
|
|
194
|
-
toc: { type: 'boolean', default: false },
|
|
195
|
-
translateRawFigures: {
|
|
196
|
-
type: 'boolean',
|
|
197
|
-
default: true,
|
|
198
|
-
description: 'Auto-translate the common \\begin{figure}...\\end{figure} shape to portable ![](){#fig: ...} markdown so figures render in docx. Pandoc strips raw LaTeX in docx output silently otherwise.',
|
|
199
|
-
},
|
|
200
|
-
'pandoc-args': {
|
|
201
|
-
type: 'array',
|
|
202
|
-
description: 'Extra pandoc args for DOCX builds. Appended after the top-level pandoc-args list.',
|
|
203
|
-
items: { type: 'string' },
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
additionalProperties: true,
|
|
207
|
-
},
|
|
208
|
-
tex: {
|
|
209
|
-
type: 'object',
|
|
210
|
-
description: 'LaTeX output settings',
|
|
211
|
-
properties: {
|
|
212
|
-
standalone: { type: 'boolean', default: true },
|
|
213
|
-
'pandoc-args': {
|
|
214
|
-
type: 'array',
|
|
215
|
-
description: 'Extra pandoc args for TeX builds. Appended after the top-level pandoc-args list.',
|
|
216
|
-
items: { type: 'string' },
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
additionalProperties: true,
|
|
220
|
-
},
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
if (schema.type
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema validation for rev.yaml configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validation error
|
|
7
|
+
*/
|
|
8
|
+
interface ValidationError {
|
|
9
|
+
path: string;
|
|
10
|
+
message: string;
|
|
11
|
+
value?: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validation warning
|
|
16
|
+
*/
|
|
17
|
+
interface ValidationWarning {
|
|
18
|
+
path: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validation result
|
|
24
|
+
*/
|
|
25
|
+
interface ValidationResult {
|
|
26
|
+
valid: boolean;
|
|
27
|
+
errors: ValidationError[];
|
|
28
|
+
warnings: ValidationWarning[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* JSON Schema type
|
|
33
|
+
*/
|
|
34
|
+
interface Schema {
|
|
35
|
+
$schema?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
type?: string;
|
|
39
|
+
properties?: Record<string, Schema>;
|
|
40
|
+
required?: string[];
|
|
41
|
+
items?: Schema;
|
|
42
|
+
oneOf?: Schema[];
|
|
43
|
+
enum?: string[];
|
|
44
|
+
pattern?: string;
|
|
45
|
+
format?: string;
|
|
46
|
+
minimum?: number;
|
|
47
|
+
maximum?: number;
|
|
48
|
+
minItems?: number;
|
|
49
|
+
maxItems?: number;
|
|
50
|
+
additionalProperties?: boolean;
|
|
51
|
+
default?: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* JSON Schema for rev.yaml
|
|
56
|
+
*/
|
|
57
|
+
export const revYamlSchema: Schema = {
|
|
58
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
59
|
+
title: 'rev.yaml configuration',
|
|
60
|
+
description: 'Configuration file for docrev document workflow',
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
title: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Document title',
|
|
66
|
+
},
|
|
67
|
+
version: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Document version',
|
|
70
|
+
},
|
|
71
|
+
authors: {
|
|
72
|
+
type: 'array',
|
|
73
|
+
description: 'List of authors',
|
|
74
|
+
items: {
|
|
75
|
+
oneOf: [
|
|
76
|
+
{ type: 'string' },
|
|
77
|
+
{
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
name: { type: 'string' },
|
|
81
|
+
affiliation: { type: 'string' },
|
|
82
|
+
email: { type: 'string', format: 'email' },
|
|
83
|
+
orcid: { type: 'string', pattern: '^\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]$' },
|
|
84
|
+
},
|
|
85
|
+
required: ['name'],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
journal: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Journal profile name for formatting defaults and validation',
|
|
93
|
+
},
|
|
94
|
+
'pandoc-args': {
|
|
95
|
+
type: 'array',
|
|
96
|
+
description: 'Extra pandoc args applied to every format build (e.g. --lua-filter=...). Format-specific lists are appended after these; --pandoc-arg CLI values are appended last.',
|
|
97
|
+
items: { type: 'string' },
|
|
98
|
+
},
|
|
99
|
+
output: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
description: 'Per-format output filenames. Keys are format names (pdf, docx, tex, beamer, pptx); values are paths. Relative paths resolve under outputDir; absolute paths are honored as-is. Extension auto-added if missing. CLI `-o` overrides this map.',
|
|
102
|
+
properties: {
|
|
103
|
+
pdf: { type: 'string' },
|
|
104
|
+
docx: { type: 'string' },
|
|
105
|
+
tex: { type: 'string' },
|
|
106
|
+
beamer: { type: 'string' },
|
|
107
|
+
pptx: { type: 'string' },
|
|
108
|
+
},
|
|
109
|
+
additionalProperties: false,
|
|
110
|
+
},
|
|
111
|
+
sections: {
|
|
112
|
+
type: 'array',
|
|
113
|
+
description: 'Ordered list of section files to include',
|
|
114
|
+
items: { type: 'string', pattern: '.*\\.md$' },
|
|
115
|
+
},
|
|
116
|
+
bibliography: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Path to bibliography file (.bib)',
|
|
119
|
+
pattern: '.*\\.bib$',
|
|
120
|
+
},
|
|
121
|
+
csl: {
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'Path to CSL citation style file',
|
|
124
|
+
},
|
|
125
|
+
crossref: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
description: 'pandoc-crossref settings',
|
|
128
|
+
properties: {
|
|
129
|
+
figureTitle: { type: 'string', default: 'Figure' },
|
|
130
|
+
tableTitle: { type: 'string', default: 'Table' },
|
|
131
|
+
figPrefix: {
|
|
132
|
+
oneOf: [
|
|
133
|
+
{ type: 'string' },
|
|
134
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
tblPrefix: {
|
|
138
|
+
oneOf: [
|
|
139
|
+
{ type: 'string' },
|
|
140
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
eqnPrefix: {
|
|
144
|
+
oneOf: [
|
|
145
|
+
{ type: 'string' },
|
|
146
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
secPrefix: {
|
|
150
|
+
oneOf: [
|
|
151
|
+
{ type: 'string' },
|
|
152
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
linkReferences: { type: 'boolean', default: true },
|
|
156
|
+
},
|
|
157
|
+
additionalProperties: true,
|
|
158
|
+
},
|
|
159
|
+
pdf: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
description: 'PDF output settings',
|
|
162
|
+
properties: {
|
|
163
|
+
template: { type: 'string' },
|
|
164
|
+
documentclass: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
enum: ['article', 'report', 'book', 'memoir', 'scrartcl', 'scrreprt', 'scrbook'],
|
|
167
|
+
default: 'article',
|
|
168
|
+
},
|
|
169
|
+
fontsize: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
pattern: '^\\d{1,2}pt$',
|
|
172
|
+
default: '12pt',
|
|
173
|
+
},
|
|
174
|
+
geometry: { type: 'string', default: 'margin=1in' },
|
|
175
|
+
linestretch: { type: 'number', minimum: 1, maximum: 3, default: 1.5 },
|
|
176
|
+
numbersections: { type: 'boolean', default: false },
|
|
177
|
+
toc: { type: 'boolean', default: false },
|
|
178
|
+
header: { type: 'string' },
|
|
179
|
+
footer: { type: 'string' },
|
|
180
|
+
'pandoc-args': {
|
|
181
|
+
type: 'array',
|
|
182
|
+
description: 'Extra pandoc args for PDF builds. Appended after the top-level pandoc-args list.',
|
|
183
|
+
items: { type: 'string' },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
additionalProperties: true,
|
|
187
|
+
},
|
|
188
|
+
docx: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
description: 'Word output settings',
|
|
191
|
+
properties: {
|
|
192
|
+
reference: { type: 'string', description: 'Reference document for styling' },
|
|
193
|
+
keepComments: { type: 'boolean', default: true },
|
|
194
|
+
toc: { type: 'boolean', default: false },
|
|
195
|
+
translateRawFigures: {
|
|
196
|
+
type: 'boolean',
|
|
197
|
+
default: true,
|
|
198
|
+
description: 'Auto-translate the common \\begin{figure}...\\end{figure} shape to portable ![](){#fig: ...} markdown so figures render in docx. Pandoc strips raw LaTeX in docx output silently otherwise.',
|
|
199
|
+
},
|
|
200
|
+
'pandoc-args': {
|
|
201
|
+
type: 'array',
|
|
202
|
+
description: 'Extra pandoc args for DOCX builds. Appended after the top-level pandoc-args list.',
|
|
203
|
+
items: { type: 'string' },
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
additionalProperties: true,
|
|
207
|
+
},
|
|
208
|
+
tex: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
description: 'LaTeX output settings',
|
|
211
|
+
properties: {
|
|
212
|
+
standalone: { type: 'boolean', default: true },
|
|
213
|
+
'pandoc-args': {
|
|
214
|
+
type: 'array',
|
|
215
|
+
description: 'Extra pandoc args for TeX builds. Appended after the top-level pandoc-args list.',
|
|
216
|
+
items: { type: 'string' },
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
additionalProperties: true,
|
|
220
|
+
},
|
|
221
|
+
macros: {
|
|
222
|
+
type: 'array',
|
|
223
|
+
description: 'Placeholder/highlight macros (e.g. \\tofill{X}). Built-in macros are merged automatically; entries here add new macros or override built-ins by name.',
|
|
224
|
+
items: {
|
|
225
|
+
type: 'object',
|
|
226
|
+
properties: {
|
|
227
|
+
name: {
|
|
228
|
+
type: 'string',
|
|
229
|
+
description: 'Macro command name without leading backslash, e.g. "tofill" for \\tofill{...}',
|
|
230
|
+
pattern: '^[A-Za-z][A-Za-z0-9]*$',
|
|
231
|
+
},
|
|
232
|
+
default: {
|
|
233
|
+
type: 'object',
|
|
234
|
+
description: 'Default rendering rules across formats',
|
|
235
|
+
properties: {
|
|
236
|
+
color: { type: 'string', pattern: '^[0-9A-Fa-f]{6}$' },
|
|
237
|
+
bold: { type: 'boolean' },
|
|
238
|
+
italic: { type: 'boolean' },
|
|
239
|
+
bracket: { type: 'boolean', default: true },
|
|
240
|
+
prefix: { type: 'string' },
|
|
241
|
+
suffix: { type: 'string' },
|
|
242
|
+
},
|
|
243
|
+
additionalProperties: false,
|
|
244
|
+
},
|
|
245
|
+
formats: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
description: 'Per-format overrides (keys: docx, pdf, latex, html, ...)',
|
|
248
|
+
additionalProperties: true,
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
required: ['name'],
|
|
252
|
+
additionalProperties: false,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
additionalProperties: true,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Validate a value against a simple schema
|
|
261
|
+
*/
|
|
262
|
+
function validateValue(value: unknown, schema: Schema, path = ''): ValidationError[] {
|
|
263
|
+
const errors: ValidationError[] = [];
|
|
264
|
+
|
|
265
|
+
// Handle oneOf
|
|
266
|
+
if (schema.oneOf) {
|
|
267
|
+
const validForAny = schema.oneOf.some((subSchema) => {
|
|
268
|
+
const subErrors = validateValue(value, subSchema, path);
|
|
269
|
+
return subErrors.length === 0;
|
|
270
|
+
});
|
|
271
|
+
if (!validForAny) {
|
|
272
|
+
errors.push({
|
|
273
|
+
path,
|
|
274
|
+
message: `Value does not match any allowed type`,
|
|
275
|
+
value,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return errors;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Type check
|
|
282
|
+
if (schema.type) {
|
|
283
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
284
|
+
if (actualType !== schema.type) {
|
|
285
|
+
errors.push({
|
|
286
|
+
path,
|
|
287
|
+
message: `Expected ${schema.type}, got ${actualType}`,
|
|
288
|
+
value,
|
|
289
|
+
});
|
|
290
|
+
return errors; // Stop further validation if type is wrong
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// String validation
|
|
295
|
+
if (schema.type === 'string' && typeof value === 'string') {
|
|
296
|
+
if (schema.pattern) {
|
|
297
|
+
const regex = new RegExp(schema.pattern);
|
|
298
|
+
if (!regex.test(value)) {
|
|
299
|
+
errors.push({
|
|
300
|
+
path,
|
|
301
|
+
message: `Value "${value}" does not match pattern ${schema.pattern}`,
|
|
302
|
+
value,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
307
|
+
errors.push({
|
|
308
|
+
path,
|
|
309
|
+
message: `Value "${value}" must be one of: ${schema.enum.join(', ')}`,
|
|
310
|
+
value,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Number validation
|
|
316
|
+
if (schema.type === 'number' && typeof value === 'number') {
|
|
317
|
+
if (schema.minimum !== undefined && value < schema.minimum) {
|
|
318
|
+
errors.push({
|
|
319
|
+
path,
|
|
320
|
+
message: `Value ${value} is less than minimum ${schema.minimum}`,
|
|
321
|
+
value,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (schema.maximum !== undefined && value > schema.maximum) {
|
|
325
|
+
errors.push({
|
|
326
|
+
path,
|
|
327
|
+
message: `Value ${value} is greater than maximum ${schema.maximum}`,
|
|
328
|
+
value,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Array validation
|
|
334
|
+
if (schema.type === 'array' && Array.isArray(value)) {
|
|
335
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
336
|
+
errors.push({
|
|
337
|
+
path,
|
|
338
|
+
message: `Array must have at least ${schema.minItems} items`,
|
|
339
|
+
value,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (schema.maxItems !== undefined && value.length > schema.maxItems) {
|
|
343
|
+
errors.push({
|
|
344
|
+
path,
|
|
345
|
+
message: `Array must have at most ${schema.maxItems} items`,
|
|
346
|
+
value,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (schema.items) {
|
|
350
|
+
value.forEach((item, index) => {
|
|
351
|
+
errors.push(...validateValue(item, schema.items!, `${path}[${index}]`));
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Object validation
|
|
357
|
+
if (schema.type === 'object' && typeof value === 'object' && value !== null) {
|
|
358
|
+
const obj = value as Record<string, unknown>;
|
|
359
|
+
if (schema.properties) {
|
|
360
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
361
|
+
if (obj[key] !== undefined) {
|
|
362
|
+
errors.push(...validateValue(obj[key], propSchema, path ? `${path}.${key}` : key));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (schema.required) {
|
|
367
|
+
for (const key of schema.required) {
|
|
368
|
+
if (obj[key] === undefined) {
|
|
369
|
+
errors.push({
|
|
370
|
+
path: path ? `${path}.${key}` : key,
|
|
371
|
+
message: `Required property "${key}" is missing`,
|
|
372
|
+
value: undefined,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return errors;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Validate rev.yaml configuration
|
|
384
|
+
*/
|
|
385
|
+
export function validateConfig(config: Record<string, unknown>): ValidationResult {
|
|
386
|
+
const errors = validateValue(config, revYamlSchema);
|
|
387
|
+
const warnings: ValidationWarning[] = [];
|
|
388
|
+
|
|
389
|
+
// Additional semantic validations
|
|
390
|
+
if (config.sections && Array.isArray(config.sections) && config.sections.length === 0) {
|
|
391
|
+
warnings.push({
|
|
392
|
+
path: 'sections',
|
|
393
|
+
message: 'No sections specified - build will auto-detect .md files',
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (config.bibliography && typeof config.bibliography === 'string' && !config.bibliography.endsWith('.bib')) {
|
|
398
|
+
warnings.push({
|
|
399
|
+
path: 'bibliography',
|
|
400
|
+
message: 'Bibliography file should have .bib extension',
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const pdf = config.pdf as { linestretch?: number } | undefined;
|
|
405
|
+
if (pdf?.linestretch && (pdf.linestretch < 1 || pdf.linestretch > 3)) {
|
|
406
|
+
warnings.push({
|
|
407
|
+
path: 'pdf.linestretch',
|
|
408
|
+
message: 'Line stretch values outside 1-3 range may produce unexpected results',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Check for common typos
|
|
413
|
+
const knownKeys = Object.keys(revYamlSchema.properties || {});
|
|
414
|
+
for (const key of Object.keys(config)) {
|
|
415
|
+
if (key.startsWith('_')) continue; // Internal keys
|
|
416
|
+
if (!knownKeys.includes(key)) {
|
|
417
|
+
// Check for similar keys (possible typos)
|
|
418
|
+
const similar = knownKeys.find(
|
|
419
|
+
(k) => levenshtein(key.toLowerCase(), k.toLowerCase()) <= 2
|
|
420
|
+
);
|
|
421
|
+
if (similar) {
|
|
422
|
+
warnings.push({
|
|
423
|
+
path: key,
|
|
424
|
+
message: `Unknown property "${key}" - did you mean "${similar}"?`,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
valid: errors.length === 0,
|
|
432
|
+
errors,
|
|
433
|
+
warnings,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Format validation results for display
|
|
439
|
+
*/
|
|
440
|
+
export function formatValidationResult(
|
|
441
|
+
result: ValidationResult,
|
|
442
|
+
chalk: { red: (s: string) => string; yellow: (s: string) => string; green: (s: string) => string }
|
|
443
|
+
): string {
|
|
444
|
+
const lines: string[] = [];
|
|
445
|
+
|
|
446
|
+
if (result.errors.length > 0) {
|
|
447
|
+
lines.push(chalk.red('Configuration errors:'));
|
|
448
|
+
for (const error of result.errors) {
|
|
449
|
+
lines.push(chalk.red(` ✗ ${error.path}: ${error.message}`));
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (result.warnings.length > 0) {
|
|
454
|
+
if (lines.length > 0) lines.push('');
|
|
455
|
+
lines.push(chalk.yellow('Warnings:'));
|
|
456
|
+
for (const warning of result.warnings) {
|
|
457
|
+
lines.push(chalk.yellow(` ! ${warning.path}: ${warning.message}`));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (result.valid && result.warnings.length === 0) {
|
|
462
|
+
lines.push(chalk.green('✓ Configuration is valid'));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return lines.join('\n');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Levenshtein distance for typo detection
|
|
470
|
+
*/
|
|
471
|
+
function levenshtein(a: string, b: string): number {
|
|
472
|
+
const matrix = Array(b.length + 1)
|
|
473
|
+
.fill(null)
|
|
474
|
+
.map(() => Array(a.length + 1).fill(null));
|
|
475
|
+
for (let i = 0; i <= a.length; i++) matrix[0][i] = i;
|
|
476
|
+
for (let j = 0; j <= b.length; j++) matrix[j][0] = j;
|
|
477
|
+
for (let j = 1; j <= b.length; j++) {
|
|
478
|
+
for (let i = 1; i <= a.length; i++) {
|
|
479
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
480
|
+
matrix[j][i] = Math.min(
|
|
481
|
+
matrix[j][i - 1] + 1,
|
|
482
|
+
matrix[j - 1][i] + 1,
|
|
483
|
+
matrix[j - 1][i - 1] + cost
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return matrix[b.length][a.length];
|
|
488
|
+
}
|