docrev 0.7.8 → 0.7.9

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/lib/build.js CHANGED
@@ -213,7 +213,7 @@ export function combineSections(directory, config, options = {}) {
213
213
  // Resolve forward references (refs that appear before their anchor definition)
214
214
  // This fixes pandoc-crossref limitation with multi-file documents
215
215
  if (hasPandocCrossref()) {
216
- const registry = buildRegistry(directory);
216
+ const registry = buildRegistry(directory, sections);
217
217
  const { text, resolved } = resolveForwardRefs(paperContent, registry);
218
218
  if (resolved.length > 0) {
219
219
  paperContent = text;
@@ -280,7 +280,8 @@ export function prepareForFormat(paperPath, format, config, options = {}) {
280
280
  let content = fs.readFileSync(paperPath, 'utf-8');
281
281
 
282
282
  // Build crossref registry for reference conversion
283
- const registry = buildRegistry(directory);
283
+ // Pass sections from config to ensure correct file ordering
284
+ const registry = buildRegistry(directory, config.sections);
284
285
 
285
286
  if (format === 'pdf' || format === 'tex') {
286
287
  // Strip all annotations for clean output
package/lib/crossref.js CHANGED
@@ -11,6 +11,49 @@ import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import YAML from 'yaml';
13
13
 
14
+ /**
15
+ * Discover section files from a directory by reading config files
16
+ * Only returns files explicitly defined in rev.yaml or sections.yaml
17
+ * Returns empty array if no config found (caller should handle this)
18
+ *
19
+ * @param {string} directory
20
+ * @returns {string[]} Ordered list of section filenames, or empty if no config
21
+ */
22
+ function discoverSectionFiles(directory) {
23
+ // Try rev.yaml first
24
+ const revYamlPath = path.join(directory, 'rev.yaml');
25
+ if (fs.existsSync(revYamlPath)) {
26
+ try {
27
+ const config = YAML.parse(fs.readFileSync(revYamlPath, 'utf-8'));
28
+ if (config.sections && Array.isArray(config.sections) && config.sections.length > 0) {
29
+ return config.sections.filter(f => fs.existsSync(path.join(directory, f)));
30
+ }
31
+ } catch {
32
+ // Ignore yaml errors, try next option
33
+ }
34
+ }
35
+
36
+ // Try sections.yaml
37
+ const sectionsPath = path.join(directory, 'sections.yaml');
38
+ if (fs.existsSync(sectionsPath)) {
39
+ try {
40
+ const config = YAML.parse(fs.readFileSync(sectionsPath, 'utf-8'));
41
+ if (config.sections) {
42
+ const sectionOrder = Object.entries(config.sections)
43
+ .sort((a, b) => (a[1].order ?? 999) - (b[1].order ?? 999))
44
+ .map(([file]) => file);
45
+ return sectionOrder.filter(f => fs.existsSync(path.join(directory, f)));
46
+ }
47
+ } catch {
48
+ // Ignore yaml errors
49
+ }
50
+ }
51
+
52
+ // No config found - return empty array
53
+ // Caller must handle this (either error or use explicit sections)
54
+ return [];
55
+ }
56
+
14
57
  /**
15
58
  * Patterns for detecting hardcoded references
16
59
  * Matches complex patterns including:
@@ -208,8 +251,14 @@ export function parseReferenceList(listStr) {
208
251
  * Build a registry of figure/table labels from .md files
209
252
  * Scans for {#fig:label} and {#tbl:label} anchors
210
253
  *
254
+ * IMPORTANT: This function requires either explicit sections or a rev.yaml/sections.yaml config.
255
+ * It will NOT guess by scanning all .md files, as this leads to incorrect numbering
256
+ * when temporary files (paper_clean.md, etc.) exist in the directory.
257
+ *
211
258
  * @param {string} directory - Directory containing .md files
212
- * @param {string[]} [excludeFiles] - Files to exclude
259
+ * @param {string[]} [sections] - Array of section filenames to scan (recommended).
260
+ * If not provided, reads from rev.yaml or sections.yaml.
261
+ * Returns empty registry if no sections can be determined.
213
262
  * @returns {{
214
263
  * figures: Map<string, {label: string, num: number, isSupp: boolean, file: string}>,
215
264
  * tables: Map<string, {label: string, num: number, isSupp: boolean, file: string}>,
@@ -217,7 +266,7 @@ export function parseReferenceList(listStr) {
217
266
  * byNumber: {fig: Map<string, string>, tbl: Map<string, string>, eq: Map<string, string>}
218
267
  * }}
219
268
  */
220
- export function buildRegistry(directory, excludeFiles = ['paper.md', 'README.md', 'CLAUDE.md']) {
269
+ export function buildRegistry(directory, sections) {
221
270
  const figures = new Map();
222
271
  const tables = new Map();
223
272
  const equations = new Map();
@@ -229,32 +278,16 @@ export function buildRegistry(directory, excludeFiles = ['paper.md', 'README.md'
229
278
  let tblSuppNum = 0;
230
279
  let eqNum = 0;
231
280
 
232
- // Get all .md files
233
- const files = fs.readdirSync(directory).filter((f) => {
234
- if (!f.endsWith('.md')) return false;
235
- if (excludeFiles.some((e) => f.toLowerCase() === e.toLowerCase())) return false;
236
- return true;
237
- });
281
+ let orderedFiles;
238
282
 
239
- // Sort by likely document order (use sections.yaml if available)
240
- let orderedFiles = files;
241
- const sectionsPath = path.join(directory, 'sections.yaml');
242
- if (fs.existsSync(sectionsPath)) {
243
- try {
244
- const config = YAML.parse(fs.readFileSync(sectionsPath, 'utf-8'));
245
- if (config.sections) {
246
- const sectionOrder = Object.entries(config.sections)
247
- .sort((a, b) => (a[1].order ?? 999) - (b[1].order ?? 999))
248
- .map(([file]) => file);
249
- orderedFiles = sectionOrder.filter((f) => files.includes(f));
250
- // Add any remaining files not in sections.yaml
251
- for (const f of files) {
252
- if (!orderedFiles.includes(f)) orderedFiles.push(f);
253
- }
254
- }
255
- } catch {
256
- // Ignore yaml errors, use default order
257
- }
283
+ if (Array.isArray(sections) && sections.length > 0) {
284
+ // Use explicitly provided section files - most reliable
285
+ orderedFiles = sections.filter(f => fs.existsSync(path.join(directory, f)));
286
+ } else {
287
+ // Try to determine sections from config files (rev.yaml or sections.yaml)
288
+ orderedFiles = discoverSectionFiles(directory);
289
+ // If no config found, return empty registry rather than guessing
290
+ // This prevents bugs from scanning wrong files
258
291
  }
259
292
 
260
293
  // Determine if a file is supplementary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docrev",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Academic paper revision workflow: Word ↔ Markdown round-trips, DOI validation, reviewer comments",
5
5
  "type": "module",
6
6
  "types": "types/index.d.ts",