docrev 0.5.0 → 0.5.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/lib/annotations.js +75 -12
- package/lib/import.js +6 -1
- package/package.json +1 -1
package/lib/annotations.js
CHANGED
|
@@ -173,6 +173,7 @@ export function parseAnnotations(text) {
|
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
175
|
* Strip annotations from text, applying changes
|
|
176
|
+
* Handles nested annotations by iterating until stable
|
|
176
177
|
* @param {string} text
|
|
177
178
|
* @param {{keepComments?: boolean}} options
|
|
178
179
|
* @returns {string}
|
|
@@ -180,26 +181,88 @@ export function parseAnnotations(text) {
|
|
|
180
181
|
export function stripAnnotations(text, options = {}) {
|
|
181
182
|
const { keepComments = false } = options;
|
|
182
183
|
|
|
183
|
-
//
|
|
184
|
-
|
|
184
|
+
// Iterate until no more changes (handles nested annotations)
|
|
185
|
+
let prev;
|
|
186
|
+
let iterations = 0;
|
|
187
|
+
const maxIterations = 20; // Safety limit
|
|
185
188
|
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
do {
|
|
190
|
+
prev = text;
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
// Apply substitutions: {~~old~>new~~} → new
|
|
193
|
+
text = text.replace(PATTERNS.substitute, '$2');
|
|
191
194
|
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
// Apply insertions: {++text++} → text
|
|
196
|
+
text = text.replace(PATTERNS.insert, '$1');
|
|
194
197
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
// Apply deletions: {--text--} → nothing
|
|
199
|
+
text = text.replace(PATTERNS.delete, '');
|
|
200
|
+
|
|
201
|
+
// Remove highlights: {==text==} → text
|
|
202
|
+
text = text.replace(PATTERNS.highlight, '$1');
|
|
203
|
+
|
|
204
|
+
// Remove comments unless keeping
|
|
205
|
+
if (!keepComments) {
|
|
206
|
+
text = text.replace(PATTERNS.comment, '');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Clean up partial/orphaned markers within the loop
|
|
210
|
+
// This handles cases where nested annotations leave behind fragments
|
|
211
|
+
|
|
212
|
+
// Empty annotations (from nested stripping)
|
|
213
|
+
text = text.replace(/\{----\}/g, '');
|
|
214
|
+
text = text.replace(/\{\+\+\+\+\}/g, '');
|
|
215
|
+
text = text.replace(/\{--\s*--\}/g, '');
|
|
216
|
+
text = text.replace(/\{\+\+\s*\+\+\}/g, '');
|
|
217
|
+
|
|
218
|
+
// Orphaned substitution fragments: ~>text~~} or {~~text (no proper pairs)
|
|
219
|
+
text = text.replace(/~>[^{]*?~~\}/g, '');
|
|
220
|
+
text = text.replace(/\{~~[^~}]*$/gm, '');
|
|
221
|
+
|
|
222
|
+
// Handle malformed substitution from nested: {~~{~~old → just strip the {~~
|
|
223
|
+
text = text.replace(/\{~~\{~~/g, '{~~');
|
|
224
|
+
text = text.replace(/~~\}~~\}/g, '~~}');
|
|
225
|
+
|
|
226
|
+
iterations++;
|
|
227
|
+
} while (text !== prev && iterations < maxIterations);
|
|
228
|
+
|
|
229
|
+
// Final cleanup of any remaining orphaned markers
|
|
230
|
+
// Orphaned closing markers
|
|
231
|
+
text = text.replace(/--\}(?:--\})+/g, '');
|
|
232
|
+
text = text.replace(/\+\+\}(?:\+\+\})+/g, '');
|
|
233
|
+
text = text.replace(/~~\}(?:~~\})+/g, '');
|
|
234
|
+
text = text.replace(/--\}/g, '');
|
|
235
|
+
text = text.replace(/\+\+\}/g, '');
|
|
236
|
+
text = text.replace(/~~\}/g, '');
|
|
237
|
+
|
|
238
|
+
// Orphaned opening markers
|
|
239
|
+
text = text.replace(/\{--(?:\{--)+/g, '');
|
|
240
|
+
text = text.replace(/\{\+\+(?:\{\+\+)+/g, '');
|
|
241
|
+
text = text.replace(/\{~~(?:\{~~)+/g, '');
|
|
242
|
+
text = text.replace(/\{--/g, '');
|
|
243
|
+
text = text.replace(/\{\+\+/g, '');
|
|
244
|
+
text = text.replace(/\{~~/g, '');
|
|
245
|
+
text = text.replace(/~>/g, '');
|
|
246
|
+
|
|
247
|
+
// Clean up multiple spaces (but preserve structure like newlines)
|
|
248
|
+
text = text.replace(/ +/g, ' ');
|
|
199
249
|
|
|
200
250
|
return text;
|
|
201
251
|
}
|
|
202
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Check if text contains any CriticMarkup annotations
|
|
255
|
+
* @param {string} text
|
|
256
|
+
* @returns {boolean}
|
|
257
|
+
*/
|
|
258
|
+
export function hasAnnotations(text) {
|
|
259
|
+
return PATTERNS.insert.test(text) ||
|
|
260
|
+
PATTERNS.delete.test(text) ||
|
|
261
|
+
PATTERNS.substitute.test(text) ||
|
|
262
|
+
PATTERNS.comment.test(text) ||
|
|
263
|
+
PATTERNS.highlight.test(text);
|
|
264
|
+
}
|
|
265
|
+
|
|
203
266
|
/**
|
|
204
267
|
* Apply a decision to a single annotation
|
|
205
268
|
* @param {string} text
|
package/lib/import.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { diffWords } from 'diff';
|
|
8
|
+
import { stripAnnotations } from './annotations.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Extract comments directly from Word docx comments.xml
|
|
@@ -985,7 +986,11 @@ export async function importFromWord(docxPath, originalMdPath, options = {}) {
|
|
|
985
986
|
}
|
|
986
987
|
|
|
987
988
|
// Read original markdown
|
|
988
|
-
|
|
989
|
+
let originalMd = fs.readFileSync(originalMdPath, 'utf-8');
|
|
990
|
+
|
|
991
|
+
// IMPORTANT: Strip any existing annotations to prevent nested annotations
|
|
992
|
+
// This ensures we always diff clean text against Word text
|
|
993
|
+
originalMd = stripAnnotations(originalMd, { keepComments: false });
|
|
989
994
|
|
|
990
995
|
// Generate diff
|
|
991
996
|
let annotated = generateSmartDiff(originalMd, wordText, author);
|