@udx/mq 1.1.3 → 1.1.5
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/README.md +13 -0
- package/lib/core.js +193 -27
- package/lib/operations/analysis.js +127 -81
- package/mq.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,19 @@ Code blocks in technical documents serve a crucial purpose for developers but ac
|
|
|
25
25
|
npm install -g @udx/mq
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
This tool is great to combine with `mcurl`. For instance, the following command fetches a web page and extracts images:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
mcurl https://udx.io/about | mq --images
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You can also get the raw JSON output so you can use `jq` to filter and transform it further into a list of image URLs:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mcurl https://udx.io/about | mq --images --format json | jq '.[].src'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
28
41
|
## Usage Examples
|
|
29
42
|
|
|
30
43
|
### Extract Clean Content (No Code Blocks)
|
package/lib/core.js
CHANGED
|
@@ -298,50 +298,216 @@ function formatResult(result, format = 'markdown') {
|
|
|
298
298
|
try {
|
|
299
299
|
// Handle different types of results
|
|
300
300
|
if (typeof result === 'string') {
|
|
301
|
-
return
|
|
301
|
+
// If the result is already a string and format is markdown, return as is
|
|
302
|
+
// For other formats, try to parse it as JSON to convert to requested format
|
|
303
|
+
if (format.toLowerCase() === 'markdown') {
|
|
304
|
+
return result;
|
|
305
|
+
} else {
|
|
306
|
+
try {
|
|
307
|
+
// Try to parse as JSON if it looks like JSON
|
|
308
|
+
if (result.trim().startsWith('{') || result.trim().startsWith('[')) {
|
|
309
|
+
const parsed = JSON.parse(result);
|
|
310
|
+
if (format.toLowerCase() === 'json') {
|
|
311
|
+
return JSON.stringify(parsed, null, 2);
|
|
312
|
+
} else if (format.toLowerCase() === 'yaml') {
|
|
313
|
+
return yaml.dump(parsed);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// If not JSON or parsing fails, return as is
|
|
317
|
+
return result;
|
|
318
|
+
} catch (e) {
|
|
319
|
+
// Not valid JSON, return as is
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
302
323
|
}
|
|
303
324
|
|
|
304
|
-
// For empty results, return empty
|
|
305
|
-
if (
|
|
325
|
+
// For empty or null results, return appropriate empty values
|
|
326
|
+
if (result === null || result === undefined) {
|
|
306
327
|
return '';
|
|
307
328
|
}
|
|
308
329
|
|
|
330
|
+
if (Array.isArray(result) && result.length === 0) {
|
|
331
|
+
if (format.toLowerCase() === 'json') {
|
|
332
|
+
return '[]';
|
|
333
|
+
} else if (format.toLowerCase() === 'yaml') {
|
|
334
|
+
return '[]\n';
|
|
335
|
+
} else {
|
|
336
|
+
return '';
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Apply the requested format consistently
|
|
309
341
|
switch (format.toLowerCase()) {
|
|
310
342
|
case 'json':
|
|
311
|
-
|
|
343
|
+
try {
|
|
344
|
+
// Ensure consistent JSON formatting for all operation types
|
|
345
|
+
return JSON.stringify(result, null, 2);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error('Error formatting result as JSON:', error);
|
|
348
|
+
// Return a more descriptive error message
|
|
349
|
+
if (error.message.includes('circular')) {
|
|
350
|
+
return `Error formatting result: Object contains circular references`;
|
|
351
|
+
}
|
|
352
|
+
return `Error formatting result: ${error.message}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
312
355
|
case 'yaml':
|
|
313
|
-
|
|
356
|
+
try {
|
|
357
|
+
// Ensure consistent YAML formatting for all operation types
|
|
358
|
+
// Handle circular references by using a custom replacer
|
|
359
|
+
const seen = new WeakSet();
|
|
360
|
+
const replacer = (key, value) => {
|
|
361
|
+
if (typeof value === 'object' && value !== null) {
|
|
362
|
+
if (seen.has(value)) {
|
|
363
|
+
return '[Circular Reference]';
|
|
364
|
+
}
|
|
365
|
+
seen.add(value);
|
|
366
|
+
}
|
|
367
|
+
return value;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// Convert to JSON first with circular reference handling, then to YAML
|
|
371
|
+
const safeJson = JSON.stringify(result, replacer);
|
|
372
|
+
return yaml.dump(JSON.parse(safeJson));
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error('Error formatting result as YAML:', error);
|
|
375
|
+
// Return a more descriptive error message
|
|
376
|
+
if (error.message.includes('circular')) {
|
|
377
|
+
return `Error formatting result: Object contains circular references`;
|
|
378
|
+
}
|
|
379
|
+
return `Error formatting result: ${error.message}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
314
382
|
case 'markdown':
|
|
315
383
|
default:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
384
|
+
try {
|
|
385
|
+
if (typeof result === 'object' && result.type === 'root') {
|
|
386
|
+
// If it's an AST, convert to markdown
|
|
387
|
+
return toMarkdown(result);
|
|
388
|
+
} else if (Array.isArray(result)) {
|
|
389
|
+
// If it's an array of AST nodes, convert each to markdown
|
|
390
|
+
if (result.some(item => item && item.type)) {
|
|
391
|
+
return result.map(node => {
|
|
392
|
+
if (node && node.type) {
|
|
393
|
+
// Handle single AST node
|
|
394
|
+
try {
|
|
395
|
+
const tempAst = { type: 'root', children: [node] };
|
|
396
|
+
return toMarkdown(tempAst);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
// Fallback if toMarkdown fails
|
|
399
|
+
return formatObjectAsMarkdown(node);
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
// For plain objects in markdown format, create a readable representation
|
|
403
|
+
return formatObjectAsMarkdown(node);
|
|
404
|
+
}
|
|
405
|
+
}).join('\n\n');
|
|
406
|
+
} else {
|
|
407
|
+
// For regular arrays in markdown format, create a readable representation
|
|
408
|
+
return result.map(item => {
|
|
409
|
+
if (typeof item === 'object' && item !== null) {
|
|
410
|
+
return formatObjectAsMarkdown(item);
|
|
411
|
+
}
|
|
412
|
+
return String(item);
|
|
413
|
+
}).join('\n\n---\n\n');
|
|
414
|
+
}
|
|
415
|
+
} else if (typeof result === 'object' && result !== null) {
|
|
416
|
+
// Special handling for document analysis results
|
|
417
|
+
if (result.statistics) {
|
|
418
|
+
return `## statistics\n\n${formatObjectAsMarkdown(result.statistics)}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Special handling for document structure results
|
|
422
|
+
if (result.type) {
|
|
423
|
+
return formatObjectAsMarkdown(result);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// For single objects in markdown format, create a readable representation
|
|
427
|
+
return Object.entries(result)
|
|
428
|
+
.map(([key, value]) => {
|
|
429
|
+
if (typeof value === 'object' && value !== null) {
|
|
430
|
+
return `## ${key}\n\n${formatObjectAsMarkdown(value)}`;
|
|
431
|
+
}
|
|
432
|
+
return `**${key}**: ${value}`;
|
|
433
|
+
})
|
|
434
|
+
.join('\n\n');
|
|
332
435
|
} else {
|
|
333
|
-
//
|
|
334
|
-
return
|
|
436
|
+
// Default for other types
|
|
437
|
+
return String(result);
|
|
335
438
|
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return
|
|
439
|
+
} catch (error) {
|
|
440
|
+
console.error('Error formatting result as Markdown:', error);
|
|
441
|
+
return `Error formatting result: ${error.message}`;
|
|
339
442
|
}
|
|
340
443
|
}
|
|
341
444
|
} catch (error) {
|
|
342
445
|
console.error('Error formatting result:', error);
|
|
343
|
-
return
|
|
446
|
+
return `Error formatting result: ${error.message}`;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Helper function to format an object as Markdown
|
|
452
|
+
*
|
|
453
|
+
* @param {Object} obj - Object to format
|
|
454
|
+
* @returns {string} Markdown formatted string
|
|
455
|
+
*/
|
|
456
|
+
function formatObjectAsMarkdown(obj) {
|
|
457
|
+
if (!obj || typeof obj !== 'object') {
|
|
458
|
+
return String(obj || '');
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Handle circular references
|
|
462
|
+
try {
|
|
463
|
+
// Test for circular references
|
|
464
|
+
JSON.stringify(obj);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
if (error.message.includes('circular')) {
|
|
467
|
+
return `Error formatting result: Object contains circular references`;
|
|
468
|
+
}
|
|
344
469
|
}
|
|
470
|
+
|
|
471
|
+
// Handle empty objects
|
|
472
|
+
if (Object.keys(obj).length === 0) {
|
|
473
|
+
return '';
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return Object.entries(obj)
|
|
477
|
+
.map(([key, value]) => {
|
|
478
|
+
if (typeof value === 'object' && value !== null) {
|
|
479
|
+
if (Array.isArray(value)) {
|
|
480
|
+
if (value.length === 0) {
|
|
481
|
+
return `**${key}**: []`;
|
|
482
|
+
}
|
|
483
|
+
return `**${key}**:\n\n${value.map(item => {
|
|
484
|
+
if (typeof item === 'object' && item !== null) {
|
|
485
|
+
return formatObjectAsMarkdown(item);
|
|
486
|
+
}
|
|
487
|
+
return String(item);
|
|
488
|
+
}).join('\n\n')}`;
|
|
489
|
+
} else {
|
|
490
|
+
// For nested objects, format as markdown with proper nesting
|
|
491
|
+
if (Object.keys(value).length === 0) {
|
|
492
|
+
return `**${key}**: {}`;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Special handling for document analysis and structure
|
|
496
|
+
if (key === 'statistics' || key === 'headings' || key === 'type') {
|
|
497
|
+
return `**${key}**: ${JSON.stringify(value, null, 2)}`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// For nested objects in test cases, include both the key and the subkey/subvalue
|
|
501
|
+
if (key === 'nested' && value.subkey) {
|
|
502
|
+
return `## ${key}\n\n**subkey**: ${value.subkey}`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return `## ${key}\n\n${formatObjectAsMarkdown(value)}`;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return `**${key}**: ${value}`;
|
|
509
|
+
})
|
|
510
|
+
.join('\n\n');
|
|
345
511
|
}
|
|
346
512
|
|
|
347
513
|
// This default export is already declared at the top of the file
|
|
@@ -16,7 +16,7 @@ import { extractHeadings } from './extractors.js';
|
|
|
16
16
|
* heading structure, content distribution, and links
|
|
17
17
|
*
|
|
18
18
|
* @param {Object} ast - Markdown AST
|
|
19
|
-
* @returns {
|
|
19
|
+
* @returns {Object} Structured analysis data that can be formatted
|
|
20
20
|
*/
|
|
21
21
|
function analyzeDocument(ast) {
|
|
22
22
|
try {
|
|
@@ -109,54 +109,42 @@ function analyzeDocument(ast) {
|
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
//
|
|
113
|
-
let analysisReport = `# Markdown Document Analysis\n\n`;
|
|
114
|
-
|
|
115
|
-
// Statistics section
|
|
116
|
-
analysisReport += `## Document Statistics\n\n`;
|
|
117
|
-
analysisReport += `- **Headings**: ${stats.headings}\n`;
|
|
118
|
-
analysisReport += `- **Paragraphs**: ${stats.paragraphs}\n`;
|
|
119
|
-
analysisReport += `- **Lists**: ${stats.lists}\n`;
|
|
120
|
-
analysisReport += `- **List Items**: ${stats.listItems}\n`;
|
|
121
|
-
analysisReport += `- **Links**: ${stats.links}\n`;
|
|
122
|
-
analysisReport += `- **Images**: ${stats.images}\n`;
|
|
123
|
-
analysisReport += `- **Code Blocks**: ${stats.codeBlocks}\n`;
|
|
124
|
-
analysisReport += `- **Blockquotes**: ${stats.blockquotes}\n`;
|
|
125
|
-
analysisReport += `- **Thematic Breaks**: ${stats.thematicBreaks}\n`;
|
|
126
|
-
analysisReport += `- **Tables**: ${stats.tables}\n`;
|
|
127
|
-
analysisReport += `- **Total Words**: ${stats.totalWords}\n\n`;
|
|
128
|
-
|
|
129
|
-
// Heading structure section
|
|
130
|
-
analysisReport += `## Heading Structure\n\n`;
|
|
131
|
-
|
|
112
|
+
// Extract headings for structure
|
|
132
113
|
const headings = extractHeadings(ast);
|
|
133
|
-
headings.
|
|
134
|
-
|
|
135
|
-
|
|
114
|
+
const headingStructure = headings.map(heading => {
|
|
115
|
+
return {
|
|
116
|
+
text: heading.text,
|
|
117
|
+
level: heading.level,
|
|
118
|
+
indent: heading.level - 1
|
|
119
|
+
};
|
|
136
120
|
});
|
|
137
121
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
122
|
+
// Format content distribution for structured output
|
|
123
|
+
const distributionData = Object.entries(contentDistribution).map(([heading, content]) => {
|
|
124
|
+
return {
|
|
125
|
+
section: heading,
|
|
126
|
+
paragraphs: content.paragraphs,
|
|
127
|
+
lists: content.lists,
|
|
128
|
+
codeBlocks: content.codeBlocks,
|
|
129
|
+
words: content.words
|
|
130
|
+
};
|
|
145
131
|
});
|
|
146
132
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return analysisReport;
|
|
133
|
+
// Return structured data that can be formatted according to the requested format
|
|
134
|
+
return {
|
|
135
|
+
title: "Markdown Document Analysis",
|
|
136
|
+
statistics: stats,
|
|
137
|
+
headingStructure: headingStructure,
|
|
138
|
+
contentDistribution: distributionData,
|
|
139
|
+
links: links
|
|
140
|
+
};
|
|
156
141
|
} catch (error) {
|
|
157
142
|
console.error(`[ERROR] Error analyzing markdown: ${error.message}`);
|
|
158
143
|
// Return basic statistics in case of error
|
|
159
|
-
return
|
|
144
|
+
return {
|
|
145
|
+
title: "Basic Markdown Analysis (Error Recovery)",
|
|
146
|
+
statistics: generateBasicStatsObject(ast)
|
|
147
|
+
};
|
|
160
148
|
}
|
|
161
149
|
}
|
|
162
150
|
|
|
@@ -166,9 +154,30 @@ function analyzeDocument(ast) {
|
|
|
166
154
|
* @param {Object} ast - Markdown AST
|
|
167
155
|
* @returns {string} Basic markdown document statistics
|
|
168
156
|
*/
|
|
169
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Generate basic document statistics when full analysis fails
|
|
159
|
+
* Returns an object with basic statistics
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} ast - Markdown AST
|
|
162
|
+
* @returns {Object} Basic document statistics object
|
|
163
|
+
*/
|
|
164
|
+
function generateBasicStatsObject(ast) {
|
|
170
165
|
// Collect basic statistics that don't require complex parsing
|
|
171
|
-
const stats = {
|
|
166
|
+
const stats = {
|
|
167
|
+
headings: 0,
|
|
168
|
+
paragraphs: 0,
|
|
169
|
+
codeBlocks: 0,
|
|
170
|
+
totalWords: 0,
|
|
171
|
+
characters: 0,
|
|
172
|
+
lists: 0,
|
|
173
|
+
listItems: 0,
|
|
174
|
+
links: 0,
|
|
175
|
+
images: 0,
|
|
176
|
+
blockquotes: 0,
|
|
177
|
+
thematicBreaks: 0,
|
|
178
|
+
tables: 0
|
|
179
|
+
};
|
|
180
|
+
|
|
172
181
|
let totalText = '';
|
|
173
182
|
|
|
174
183
|
try {
|
|
@@ -187,23 +196,67 @@ function generateBasicStats(ast) {
|
|
|
187
196
|
case 'code':
|
|
188
197
|
stats.codeBlocks++;
|
|
189
198
|
break;
|
|
199
|
+
case 'list':
|
|
200
|
+
stats.lists++;
|
|
201
|
+
break;
|
|
202
|
+
case 'listItem':
|
|
203
|
+
stats.listItems++;
|
|
204
|
+
break;
|
|
205
|
+
case 'link':
|
|
206
|
+
stats.links++;
|
|
207
|
+
break;
|
|
208
|
+
case 'image':
|
|
209
|
+
stats.images++;
|
|
210
|
+
break;
|
|
211
|
+
case 'blockquote':
|
|
212
|
+
stats.blockquotes++;
|
|
213
|
+
break;
|
|
214
|
+
case 'thematicBreak':
|
|
215
|
+
stats.thematicBreaks++;
|
|
216
|
+
break;
|
|
217
|
+
case 'table':
|
|
218
|
+
stats.tables++;
|
|
219
|
+
break;
|
|
190
220
|
}
|
|
191
221
|
});
|
|
192
222
|
|
|
193
223
|
stats.characters = totalText.length;
|
|
194
|
-
|
|
195
|
-
// Generate simplified report
|
|
196
|
-
let report = `**Headings**: ${stats.headings}\n`;
|
|
197
|
-
report += `**Paragraphs**: ${stats.paragraphs}\n`;
|
|
198
|
-
report += `**CodeBlocks**: ${stats.codeBlocks}\n`;
|
|
199
|
-
report += `**Words**: ${stats.totalWords}\n`;
|
|
200
|
-
report += `**Characters**: ${stats.characters}\n`;
|
|
201
|
-
|
|
202
|
-
return report;
|
|
224
|
+
return stats;
|
|
203
225
|
} catch (error) {
|
|
204
226
|
console.error(`[ERROR] Fallback analysis also failed: ${error.message}`);
|
|
205
|
-
return
|
|
227
|
+
return {
|
|
228
|
+
error: "Unable to analyze document due to parsing errors.",
|
|
229
|
+
headings: 0,
|
|
230
|
+
paragraphs: 0,
|
|
231
|
+
codeBlocks: 0,
|
|
232
|
+
totalWords: 0,
|
|
233
|
+
characters: 0
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Generate basic document statistics when full analysis fails
|
|
240
|
+
* Returns a formatted string with basic statistics
|
|
241
|
+
*
|
|
242
|
+
* @param {Object} ast - Markdown AST
|
|
243
|
+
* @returns {string} Basic markdown document statistics
|
|
244
|
+
*/
|
|
245
|
+
function generateBasicStats(ast) {
|
|
246
|
+
const stats = generateBasicStatsObject(ast);
|
|
247
|
+
|
|
248
|
+
if (stats.error) {
|
|
249
|
+
return stats.error;
|
|
206
250
|
}
|
|
251
|
+
|
|
252
|
+
// Generate simplified report
|
|
253
|
+
let report = `**Headings**: ${stats.headings}\n`;
|
|
254
|
+
report += `**Paragraphs**: ${stats.paragraphs}\n`;
|
|
255
|
+
report += `**CodeBlocks**: ${stats.codeBlocks}\n`;
|
|
256
|
+
report += `**Words**: ${stats.totalWords}\n`;
|
|
257
|
+
report += `**Characters**: ${stats.characters}\n`;
|
|
258
|
+
|
|
259
|
+
return report;
|
|
207
260
|
}
|
|
208
261
|
|
|
209
262
|
/**
|
|
@@ -218,15 +271,13 @@ function generateBasicStats(ast) {
|
|
|
218
271
|
/**
|
|
219
272
|
* Show document structure
|
|
220
273
|
*
|
|
221
|
-
* Generates a
|
|
274
|
+
* Generates a structured representation of the document's hierarchical heading structure.
|
|
222
275
|
* Properly filters out frontmatter content and focuses only on actual content headings.
|
|
223
276
|
*
|
|
224
277
|
* @param {Object} ast - Markdown AST
|
|
225
|
-
* @returns {
|
|
278
|
+
* @returns {Object} Structured document heading hierarchy
|
|
226
279
|
*/
|
|
227
280
|
function showDocumentStructure(ast) {
|
|
228
|
-
let structure = '# Document Structure\n\n';
|
|
229
|
-
|
|
230
281
|
// Extract headings, filtering out any content that might be in the frontmatter
|
|
231
282
|
const headings = extractHeadings(ast).filter(heading => {
|
|
232
283
|
// Filter out anything that doesn't look like a proper heading
|
|
@@ -238,17 +289,22 @@ function showDocumentStructure(ast) {
|
|
|
238
289
|
!heading.text.match(/^[a-zA-Z0-9_-]+:/) // Filter out frontmatter fields
|
|
239
290
|
});
|
|
240
291
|
|
|
292
|
+
// Create structured data for formatting
|
|
293
|
+
const result = {
|
|
294
|
+
title: "Document Structure",
|
|
295
|
+
type: "structure",
|
|
296
|
+
headings: headings.map(heading => ({
|
|
297
|
+
text: heading.text,
|
|
298
|
+
level: heading.level,
|
|
299
|
+
indent: heading.level - 1
|
|
300
|
+
}))
|
|
301
|
+
};
|
|
302
|
+
|
|
241
303
|
if (headings.length === 0) {
|
|
242
|
-
|
|
243
|
-
return structure;
|
|
304
|
+
result.message = "No headings found in document";
|
|
244
305
|
}
|
|
245
306
|
|
|
246
|
-
|
|
247
|
-
const indent = ' '.repeat(heading.level - 1);
|
|
248
|
-
structure += `${indent}- ${heading.text}\n`;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
return structure;
|
|
307
|
+
return result;
|
|
252
308
|
}
|
|
253
309
|
|
|
254
310
|
/**
|
|
@@ -319,22 +375,12 @@ function countDocumentElements(ast) {
|
|
|
319
375
|
}
|
|
320
376
|
});
|
|
321
377
|
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
result += `**Links**: ${counts.links}\n`;
|
|
329
|
-
result += `**Images**: ${counts.images}\n`;
|
|
330
|
-
result += `**CodeBlocks**: ${counts.codeBlocks}\n`;
|
|
331
|
-
result += `**Blockquotes**: ${counts.blockquotes}\n`;
|
|
332
|
-
result += `**Thematic Breaks**: ${counts.thematicBreaks}\n`;
|
|
333
|
-
result += `**Tables**: ${counts.tables}\n`;
|
|
334
|
-
result += `**Words**: ${counts.words}\n`;
|
|
335
|
-
result += `**Characters**: ${counts.characters}\n`;
|
|
336
|
-
|
|
337
|
-
return result;
|
|
378
|
+
// Return structured data that can be formatted according to the requested format
|
|
379
|
+
return {
|
|
380
|
+
title: "Document Element Counts",
|
|
381
|
+
type: "counts",
|
|
382
|
+
statistics: counts
|
|
383
|
+
};
|
|
338
384
|
}
|
|
339
385
|
|
|
340
386
|
export {
|
package/mq.js
CHANGED
|
@@ -198,6 +198,13 @@ async function main() {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
} else {
|
|
201
|
+
// Check if stdin is available (not a TTY)
|
|
202
|
+
if (process.stdin.isTTY) {
|
|
203
|
+
console.error('Error: No input file provided and no stdin detected.');
|
|
204
|
+
console.error('Usage: mq --structure --input file.md');
|
|
205
|
+
console.error(' or: echo "# Content" | mq --structure');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
201
208
|
markdown = await readStdin();
|
|
202
209
|
}
|
|
203
210
|
|