@udx/mq 0.1.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/examples/analyze-document.js +191 -0
- package/examples/cross-linker.js +47 -0
- package/examples/demo-architecture.js +93 -0
- package/examples/demo.js +200 -0
- package/examples/filter-code-blocks.js +64 -0
- package/examples/generate-toc.js +71 -0
- package/examples/make-collapsible.js +61 -0
- package/examples/query-headings.js +56 -0
- package/examples/toc-generator.js +44 -0
- package/lib/core.js +347 -0
- package/lib/integrations/mcurl.js +125 -0
- package/lib/operations/analysis.js +344 -0
- package/lib/operations/extractors.js +247 -0
- package/lib/operations/index.js +151 -0
- package/lib/operations/transformers.js +411 -0
- package/lib/utils/parser.js +165 -0
- package/mq.js +656 -0
- package/package.json +67 -0
- package/readme.md +242 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Analyze Document Structure
|
|
5
|
+
*
|
|
6
|
+
* This example demonstrates how to use mq to analyze the structure
|
|
7
|
+
* and content of a markdown document.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
11
|
+
import { toString } from 'mdast-util-to-string';
|
|
12
|
+
import { visit } from 'unist-util-visit';
|
|
13
|
+
|
|
14
|
+
// Example markdown content
|
|
15
|
+
const markdown = `# Architecture Overview
|
|
16
|
+
|
|
17
|
+
## Introduction
|
|
18
|
+
|
|
19
|
+
This document provides an overview of our system architecture.
|
|
20
|
+
|
|
21
|
+
## Components
|
|
22
|
+
|
|
23
|
+
### API Gateway
|
|
24
|
+
|
|
25
|
+
The API Gateway handles all incoming requests.
|
|
26
|
+
|
|
27
|
+
### Service Layer
|
|
28
|
+
|
|
29
|
+
The Service Layer contains business logic.
|
|
30
|
+
|
|
31
|
+
### Data Layer
|
|
32
|
+
|
|
33
|
+
The Data Layer manages data persistence.
|
|
34
|
+
|
|
35
|
+
## Deployment
|
|
36
|
+
|
|
37
|
+
We use a CI/CD pipeline for deployment.
|
|
38
|
+
|
|
39
|
+
## References
|
|
40
|
+
|
|
41
|
+
- [AWS Documentation](https://aws.amazon.com/documentation/)
|
|
42
|
+
- [Kubernetes](https://kubernetes.io/)
|
|
43
|
+
- [Docker](https://www.docker.com/)
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
// Parse markdown to AST
|
|
47
|
+
const ast = fromMarkdown(markdown);
|
|
48
|
+
|
|
49
|
+
// Analyze document structure
|
|
50
|
+
function analyzeDocument(ast) {
|
|
51
|
+
let analysis = '# Document Analysis\n\n';
|
|
52
|
+
|
|
53
|
+
// Count elements
|
|
54
|
+
const counts = {
|
|
55
|
+
headings: 0,
|
|
56
|
+
paragraphs: 0,
|
|
57
|
+
links: 0,
|
|
58
|
+
images: 0,
|
|
59
|
+
codeBlocks: 0,
|
|
60
|
+
lists: 0,
|
|
61
|
+
listItems: 0
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
visit(ast, (node) => {
|
|
65
|
+
switch (node.type) {
|
|
66
|
+
case 'heading':
|
|
67
|
+
counts.headings++;
|
|
68
|
+
break;
|
|
69
|
+
case 'paragraph':
|
|
70
|
+
counts.paragraphs++;
|
|
71
|
+
break;
|
|
72
|
+
case 'link':
|
|
73
|
+
counts.links++;
|
|
74
|
+
break;
|
|
75
|
+
case 'image':
|
|
76
|
+
counts.images++;
|
|
77
|
+
break;
|
|
78
|
+
case 'code':
|
|
79
|
+
counts.codeBlocks++;
|
|
80
|
+
break;
|
|
81
|
+
case 'list':
|
|
82
|
+
counts.lists++;
|
|
83
|
+
break;
|
|
84
|
+
case 'listItem':
|
|
85
|
+
counts.listItems++;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Add counts to analysis
|
|
91
|
+
analysis += '## Document Statistics\n\n';
|
|
92
|
+
Object.entries(counts).forEach(([key, value]) => {
|
|
93
|
+
analysis += `- **${key.charAt(0).toUpperCase() + key.slice(1)}**: ${value}\n`;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Analyze heading structure
|
|
97
|
+
analysis += '\n## Heading Structure\n\n';
|
|
98
|
+
|
|
99
|
+
const headings = [];
|
|
100
|
+
visit(ast, 'heading', (node) => {
|
|
101
|
+
headings.push({
|
|
102
|
+
level: node.depth,
|
|
103
|
+
text: toString(node)
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
headings.forEach(heading => {
|
|
108
|
+
const indent = ' '.repeat(heading.level - 1);
|
|
109
|
+
analysis += `${indent}- ${heading.text}\n`;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Analyze content distribution
|
|
113
|
+
analysis += '\n## Content Distribution\n\n';
|
|
114
|
+
|
|
115
|
+
// Count words in each section
|
|
116
|
+
const sections = [];
|
|
117
|
+
let currentHeading = null;
|
|
118
|
+
let currentContent = '';
|
|
119
|
+
|
|
120
|
+
visit(ast, (node) => {
|
|
121
|
+
if (node.type === 'heading') {
|
|
122
|
+
if (currentHeading) {
|
|
123
|
+
sections.push({
|
|
124
|
+
heading: currentHeading,
|
|
125
|
+
content: currentContent,
|
|
126
|
+
wordCount: currentContent.split(/\s+/).filter(Boolean).length
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
currentHeading = toString(node);
|
|
130
|
+
currentContent = '';
|
|
131
|
+
} else if (node.type === 'paragraph' || node.type === 'code') {
|
|
132
|
+
currentContent += toString(node) + ' ';
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Add the last section
|
|
137
|
+
if (currentHeading) {
|
|
138
|
+
sections.push({
|
|
139
|
+
heading: currentHeading,
|
|
140
|
+
content: currentContent,
|
|
141
|
+
wordCount: currentContent.split(/\s+/).filter(Boolean).length
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Sort sections by word count
|
|
146
|
+
sections.sort((a, b) => b.wordCount - a.wordCount);
|
|
147
|
+
|
|
148
|
+
// Display section word counts
|
|
149
|
+
analysis += '| Section | Word Count |\n';
|
|
150
|
+
analysis += '| --- | --- |\n';
|
|
151
|
+
sections.forEach(section => {
|
|
152
|
+
analysis += `| ${section.heading} | ${section.wordCount} |\n`;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Analyze links
|
|
156
|
+
analysis += '\n## Links\n\n';
|
|
157
|
+
|
|
158
|
+
const links = [];
|
|
159
|
+
visit(ast, 'link', (node) => {
|
|
160
|
+
links.push({
|
|
161
|
+
text: toString(node),
|
|
162
|
+
href: node.url
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (links.length > 0) {
|
|
167
|
+
analysis += '| Text | URL |\n';
|
|
168
|
+
analysis += '| --- | --- |\n';
|
|
169
|
+
links.forEach(link => {
|
|
170
|
+
analysis += `| ${link.text} | ${link.href} |\n`;
|
|
171
|
+
});
|
|
172
|
+
} else {
|
|
173
|
+
analysis += 'No links found in the document.\n';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return analysis;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const analysis = analyzeDocument(ast);
|
|
180
|
+
|
|
181
|
+
console.log('Original Markdown:');
|
|
182
|
+
console.log('=================');
|
|
183
|
+
console.log(markdown);
|
|
184
|
+
console.log('\n');
|
|
185
|
+
|
|
186
|
+
console.log('Document Analysis:');
|
|
187
|
+
console.log('=================');
|
|
188
|
+
console.log(analysis);
|
|
189
|
+
|
|
190
|
+
// How to run this example:
|
|
191
|
+
// node examples/analyze-document.js
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Cross-Linker
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to use the mdoc cross-linking
|
|
5
|
+
* functionality to add related document references.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { addCrossLinks } from '../index.js';
|
|
9
|
+
|
|
10
|
+
// Example markdown content
|
|
11
|
+
const markdown = `# Worker Architecture Documentation
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
1. [Introduction](#introduction)
|
|
15
|
+
2. [Core Components](#core-components)
|
|
16
|
+
3. [Implementation Guide](#implementation-guide)
|
|
17
|
+
|
|
18
|
+
## Introduction
|
|
19
|
+
|
|
20
|
+
This document describes the Worker architecture.
|
|
21
|
+
|
|
22
|
+
## Core Components
|
|
23
|
+
|
|
24
|
+
This section details the core components of the Worker architecture.
|
|
25
|
+
|
|
26
|
+
## Implementation Guide
|
|
27
|
+
|
|
28
|
+
This section provides a guide for implementing the Worker architecture.
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
// Add cross-links to related documents
|
|
32
|
+
const enhancedMarkdown = addCrossLinks(markdown, {
|
|
33
|
+
docs: [
|
|
34
|
+
{ path: 'pipeline-as-code.md', description: 'Explore the deployment automation that integrates with Worker' },
|
|
35
|
+
{ path: 'stateless.md', description: 'Learn how Worker enables stateless media management' },
|
|
36
|
+
{ path: 'rabbit-ci.md', description: 'Understand the CI/CD platform that works with Worker' }
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log('Original Markdown:');
|
|
41
|
+
console.log('=================');
|
|
42
|
+
console.log(markdown);
|
|
43
|
+
console.log('\n');
|
|
44
|
+
|
|
45
|
+
console.log('Enhanced Markdown with Cross-Links:');
|
|
46
|
+
console.log('================================');
|
|
47
|
+
console.log(enhancedMarkdown);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mq - Architecture Files Demo
|
|
5
|
+
*
|
|
6
|
+
* This script demonstrates using mq with architecture files from the content/architecture directory.
|
|
7
|
+
*
|
|
8
|
+
* @todo move files being tested with into test/fixtures
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { exec } from 'child_process';
|
|
15
|
+
import { promisify } from 'util';
|
|
16
|
+
|
|
17
|
+
const execAsync = promisify(exec);
|
|
18
|
+
|
|
19
|
+
// Get the directory name
|
|
20
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const architectureDir = path.join(__dirname, '..', '..', 'content', 'architecture');
|
|
22
|
+
const mqPath = path.join(__dirname, 'index.js');
|
|
23
|
+
|
|
24
|
+
// Test cases for architecture files
|
|
25
|
+
const TEST_CASES = [
|
|
26
|
+
{
|
|
27
|
+
name: 'Extract headings from pipeline-as-code.md',
|
|
28
|
+
command: `node ${mqPath} '.headings[]' -i ${path.join(architectureDir, 'pipeline-as-code.md')} | head -20`,
|
|
29
|
+
description: 'Extracts the first 20 headings from the pipeline-as-code.md file'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Generate TOC for data-discovery.md',
|
|
33
|
+
command: `node ${mqPath} '.toc' -i ${path.join(architectureDir, 'data-discovery.md')} | head -20`,
|
|
34
|
+
description: 'Generates a table of contents for the data-discovery.md file'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Count elements in content-website.md',
|
|
38
|
+
command: `node ${mqPath} --count -i ${path.join(architectureDir, 'content-website.md')}`,
|
|
39
|
+
description: 'Counts document elements in the content-website.md file'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Analyze document structure of wordpress-implementation.md',
|
|
43
|
+
command: `node ${mqPath} --structure -i ${path.join(architectureDir, 'wordpress-implementation.md')} | head -20`,
|
|
44
|
+
description: 'Shows the document structure of the wordpress-implementation.md file'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Extract code blocks from hoxler.md',
|
|
48
|
+
command: `node ${mqPath} '.codeBlocks[]' -i ${path.join(architectureDir, 'hoxler.md')} | head -20`,
|
|
49
|
+
description: 'Extracts code blocks from the hoxler.md file'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Extract links from transact-campus.md',
|
|
53
|
+
command: `node ${mqPath} '.links[]' -i ${path.join(architectureDir, 'transact-campus.md')} | head -20`,
|
|
54
|
+
description: 'Extracts links from the transact-campus.md file'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Full document analysis of worker.md',
|
|
58
|
+
command: `node ${mqPath} --analyze -i ${path.join(architectureDir, 'worker.md')} | head -40`,
|
|
59
|
+
description: 'Performs a full document analysis of the worker.md file'
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Run the demo
|
|
64
|
+
async function runDemo() {
|
|
65
|
+
console.log('Running mq demo with architecture files...\n');
|
|
66
|
+
|
|
67
|
+
for (const testCase of TEST_CASES) {
|
|
68
|
+
console.log(`\n=== ${testCase.name} ===`);
|
|
69
|
+
console.log(`Description: ${testCase.description}`);
|
|
70
|
+
console.log(`Command: ${testCase.command}`);
|
|
71
|
+
console.log('\nOutput:');
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const { stdout, stderr } = await execAsync(testCase.command);
|
|
75
|
+
|
|
76
|
+
if (stderr) {
|
|
77
|
+
console.error(`Error: ${stderr}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(stdout);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`Error running test "${testCase.name}":`, error.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log('\n' + '-'.repeat(80));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('\nDemo completed successfully!');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Run the demo
|
|
93
|
+
runDemo();
|
package/examples/demo.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mq - Markdown Query
|
|
5
|
+
* Demo script for testing with architecture files
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
|
|
12
|
+
// Main function
|
|
13
|
+
async function main() {
|
|
14
|
+
console.log('=== MQ DEMO WITH ARCHITECTURE FILES ===\n');
|
|
15
|
+
|
|
16
|
+
// Find markdown files in architecture directory
|
|
17
|
+
const architectureDir = path.join(process.cwd(), '../../content/architecture');
|
|
18
|
+
const files = await fs.readdir(architectureDir);
|
|
19
|
+
const markdownFiles = files.filter(file => file.endsWith('.md'));
|
|
20
|
+
|
|
21
|
+
console.log(`Found ${markdownFiles.length} markdown files in content/architecture/\n`);
|
|
22
|
+
|
|
23
|
+
// Select a file for demonstration
|
|
24
|
+
const demoFile = markdownFiles[0];
|
|
25
|
+
const demoFilePath = path.join(architectureDir, demoFile);
|
|
26
|
+
|
|
27
|
+
console.log(`Demonstrating mq with ${demoFile}:\n`);
|
|
28
|
+
|
|
29
|
+
// Demo 1: Extract headings
|
|
30
|
+
console.log('=== DEMO 1: EXTRACT HEADINGS ===');
|
|
31
|
+
console.log(`Command: cat ${demoFilePath} | mq '.headings[]'`);
|
|
32
|
+
console.log();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const headingsOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} '.headings[]'`).toString();
|
|
36
|
+
const headings = headingsOutput.split('\n').filter(Boolean);
|
|
37
|
+
|
|
38
|
+
// Show first few headings
|
|
39
|
+
const headingsToShow = Math.min(5, headings.length);
|
|
40
|
+
for (let i = 0; i < headingsToShow; i++) {
|
|
41
|
+
console.log(headings[i]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (headings.length > headingsToShow) {
|
|
45
|
+
console.log(`... and ${headings.length - headingsToShow} more headings\n`);
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(`Error extracting headings: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Demo 2: Generate TOC
|
|
52
|
+
console.log('=== DEMO 2: GENERATE TABLE OF CONTENTS ===');
|
|
53
|
+
console.log(`Command: cat ${demoFilePath} | mq '.toc'`);
|
|
54
|
+
console.log();
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const tocOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} '.toc'`).toString();
|
|
58
|
+
const tocLines = tocOutput.split('\n').filter(Boolean);
|
|
59
|
+
|
|
60
|
+
// Show first few TOC lines
|
|
61
|
+
const linesToShow = Math.min(10, tocLines.length);
|
|
62
|
+
for (let i = 0; i < linesToShow; i++) {
|
|
63
|
+
console.log(tocLines[i]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (tocLines.length > linesToShow) {
|
|
67
|
+
console.log('...\n');
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(`Error generating TOC: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Demo 3: Count document elements
|
|
74
|
+
console.log('=== DEMO 3: COUNT DOCUMENT ELEMENTS ===');
|
|
75
|
+
console.log(`Command: cat ${demoFilePath} | mq --count`);
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const countOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} --count`).toString();
|
|
80
|
+
console.log(countOutput);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(`Error counting elements: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Demo 4: Extract code blocks
|
|
86
|
+
console.log('=== DEMO 4: EXTRACT CODE BLOCKS ===');
|
|
87
|
+
console.log(`Command: cat ${demoFilePath} | mq '.codeBlocks[]'`);
|
|
88
|
+
console.log();
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const codeBlocksOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} '.codeBlocks[]'`).toString();
|
|
92
|
+
const codeBlocks = codeBlocksOutput.split('\n\n').filter(block => block.trim().startsWith('{'));
|
|
93
|
+
|
|
94
|
+
// Show first code block
|
|
95
|
+
if (codeBlocks.length > 0) {
|
|
96
|
+
console.log(codeBlocks[0]);
|
|
97
|
+
|
|
98
|
+
if (codeBlocks.length > 1) {
|
|
99
|
+
console.log(`... and ${codeBlocks.length - 1} more code blocks\n`);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
console.log('No code blocks found in the document.\n');
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`Error extracting code blocks: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Demo 5: Transform code blocks to collapsible
|
|
109
|
+
console.log('=== DEMO 5: TRANSFORM CODE BLOCKS TO COLLAPSIBLE ===');
|
|
110
|
+
console.log(`Command: cat ${demoFilePath} | mq --transform '.codeBlocks[] |= makeCollapsible'`);
|
|
111
|
+
console.log();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const transformOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} --transform '.codeBlocks[] |= makeCollapsible'`).toString();
|
|
115
|
+
const transformLines = transformOutput.split('\n');
|
|
116
|
+
|
|
117
|
+
// Find the first transformed code block
|
|
118
|
+
let detailsStartIndex = -1;
|
|
119
|
+
for (let i = 0; i < transformLines.length; i++) {
|
|
120
|
+
if (transformLines[i].includes('<details>')) {
|
|
121
|
+
detailsStartIndex = i;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (detailsStartIndex >= 0) {
|
|
127
|
+
// Show the transformed code block
|
|
128
|
+
for (let i = detailsStartIndex; i < Math.min(detailsStartIndex + 10, transformLines.length); i++) {
|
|
129
|
+
console.log(transformLines[i]);
|
|
130
|
+
}
|
|
131
|
+
console.log('...\n');
|
|
132
|
+
} else {
|
|
133
|
+
console.log('No code blocks found to transform.\n');
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(`Error transforming code blocks: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Demo 6: Analyze document structure
|
|
140
|
+
console.log('=== DEMO 6: ANALYZE DOCUMENT STRUCTURE ===');
|
|
141
|
+
console.log(`Command: cat ${demoFilePath} | mq --analyze`);
|
|
142
|
+
console.log();
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const analyzeOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} --analyze`).toString();
|
|
146
|
+
const analyzeLines = analyzeOutput.split('\n');
|
|
147
|
+
|
|
148
|
+
// Show the first part of the analysis
|
|
149
|
+
for (let i = 0; i < Math.min(20, analyzeLines.length); i++) {
|
|
150
|
+
console.log(analyzeLines[i]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (analyzeLines.length > 20) {
|
|
154
|
+
console.log('...\n');
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(`Error analyzing document: ${error.message}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Demo 7: Filter headings by level
|
|
161
|
+
console.log('=== DEMO 7: FILTER HEADINGS BY LEVEL ===');
|
|
162
|
+
console.log(`Command: cat ${demoFilePath} | mq '.headings[] | select(.level == 2)'`);
|
|
163
|
+
console.log();
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const filterOutput = execSync(`cat ${demoFilePath} | node ${path.join(process.cwd(), 'index.js')} '.headings[] | select(.level == 2)'`).toString();
|
|
167
|
+
const filterHeadings = filterOutput.split('\n\n').filter(Boolean);
|
|
168
|
+
|
|
169
|
+
// Show first few filtered headings
|
|
170
|
+
const headingsToShow = Math.min(5, filterHeadings.length);
|
|
171
|
+
for (let i = 0; i < headingsToShow; i++) {
|
|
172
|
+
console.log(filterHeadings[i]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (filterHeadings.length > headingsToShow) {
|
|
176
|
+
console.log(`... and ${filterHeadings.length - headingsToShow} more level 2 headings\n`);
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error(`Error filtering headings: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Summary
|
|
183
|
+
console.log('\n=== MQ USAGE SUMMARY ===');
|
|
184
|
+
console.log('After installing the mq package, you can use these commands:');
|
|
185
|
+
console.log('- Extract headings: mq \'.headings[]\'');
|
|
186
|
+
console.log('- Generate TOC: mq \'.toc\'');
|
|
187
|
+
console.log('- Count elements: mq --count');
|
|
188
|
+
console.log('- Show structure: mq --structure');
|
|
189
|
+
console.log('- Analyze document: mq --analyze');
|
|
190
|
+
console.log('- Transform code blocks: mq --transform \'.codeBlocks[] |= makeCollapsible\'');
|
|
191
|
+
console.log('- Filter headings: mq \'.headings[] | select(.level == 2)\'');
|
|
192
|
+
console.log('- Extract links: mq \'.links[]\'');
|
|
193
|
+
console.log('- Find external links: mq \'.links[] | select(.href | startswith("http"))\'');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Run the main function
|
|
197
|
+
main().catch(error => {
|
|
198
|
+
console.error('Error:', error.message);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Filter Code Blocks
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to use mdoc to filter code blocks
|
|
5
|
+
* by language.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
9
|
+
import { visit } from 'unist-util-visit';
|
|
10
|
+
|
|
11
|
+
// Example markdown content
|
|
12
|
+
const markdown = `# Code Examples
|
|
13
|
+
|
|
14
|
+
## JavaScript
|
|
15
|
+
|
|
16
|
+
\`\`\`javascript
|
|
17
|
+
function hello() {
|
|
18
|
+
console.log('Hello, world!');
|
|
19
|
+
}
|
|
20
|
+
\`\`\`
|
|
21
|
+
|
|
22
|
+
## PHP
|
|
23
|
+
|
|
24
|
+
\`\`\`php
|
|
25
|
+
function hello() {
|
|
26
|
+
echo 'Hello, world!';
|
|
27
|
+
}
|
|
28
|
+
\`\`\`
|
|
29
|
+
|
|
30
|
+
## Python
|
|
31
|
+
|
|
32
|
+
\`\`\`python
|
|
33
|
+
def hello():
|
|
34
|
+
print('Hello, world!')
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
## Shell
|
|
38
|
+
|
|
39
|
+
\`\`\`bash
|
|
40
|
+
echo 'Hello, world!'
|
|
41
|
+
\`\`\`
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
// Parse markdown to AST
|
|
45
|
+
const ast = fromMarkdown(markdown);
|
|
46
|
+
|
|
47
|
+
// Extract all code blocks
|
|
48
|
+
console.log('All code blocks:');
|
|
49
|
+
console.log('==============');
|
|
50
|
+
visit(ast, 'code', (node) => {
|
|
51
|
+
console.log(`Language: ${node.lang || 'none'}`);
|
|
52
|
+
console.log(node.value);
|
|
53
|
+
console.log('---');
|
|
54
|
+
});
|
|
55
|
+
console.log('\n');
|
|
56
|
+
|
|
57
|
+
// Filter code blocks by language
|
|
58
|
+
console.log('Only JavaScript code blocks:');
|
|
59
|
+
console.log('=========================');
|
|
60
|
+
visit(ast, 'code', (node) => {
|
|
61
|
+
if (node.lang === 'javascript') {
|
|
62
|
+
console.log(node.value);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Generate Table of Contents
|
|
5
|
+
*
|
|
6
|
+
* This example demonstrates how to use mq to generate a table of contents
|
|
7
|
+
* from a markdown document.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
11
|
+
import { toString } from 'mdast-util-to-string';
|
|
12
|
+
import { visit } from 'unist-util-visit';
|
|
13
|
+
|
|
14
|
+
// Example markdown content
|
|
15
|
+
const markdown = `# Architecture Documentation
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
This document provides an overview of the architecture.
|
|
20
|
+
|
|
21
|
+
## Components
|
|
22
|
+
|
|
23
|
+
### Frontend
|
|
24
|
+
|
|
25
|
+
The frontend is built with React.
|
|
26
|
+
|
|
27
|
+
### Backend
|
|
28
|
+
|
|
29
|
+
The backend is built with Node.js.
|
|
30
|
+
|
|
31
|
+
### Database
|
|
32
|
+
|
|
33
|
+
We use PostgreSQL for data storage.
|
|
34
|
+
|
|
35
|
+
## Deployment
|
|
36
|
+
|
|
37
|
+
Deployment is handled through CI/CD pipelines.
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
// Parse markdown to AST
|
|
41
|
+
const ast = fromMarkdown(markdown);
|
|
42
|
+
|
|
43
|
+
// Extract headings
|
|
44
|
+
const headings = [];
|
|
45
|
+
visit(ast, 'heading', (node) => {
|
|
46
|
+
if (node.depth <= 3) { // Only include h1, h2, h3
|
|
47
|
+
headings.push({
|
|
48
|
+
level: node.depth,
|
|
49
|
+
text: toString(node),
|
|
50
|
+
anchor: toString(node).toLowerCase().replace(/[^\w]+/g, '-')
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Generate TOC
|
|
56
|
+
const toc = headings.map((heading, index) => {
|
|
57
|
+
const indent = ' '.repeat(heading.level - 1);
|
|
58
|
+
return `${indent}${index + 1}. [${heading.text}](#${heading.anchor})`;
|
|
59
|
+
}).join('\n');
|
|
60
|
+
|
|
61
|
+
console.log('Original Markdown:');
|
|
62
|
+
console.log('=================');
|
|
63
|
+
console.log(markdown);
|
|
64
|
+
console.log('\n');
|
|
65
|
+
|
|
66
|
+
console.log('Generated Table of Contents:');
|
|
67
|
+
console.log('==========================');
|
|
68
|
+
console.log(toc);
|
|
69
|
+
|
|
70
|
+
// How to run this example:
|
|
71
|
+
// node examples/generate-toc.js
|