@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.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Parser utilities for Markdown Query
3
+ *
4
+ * Provides utilities for parsing queries and transforming expressions
5
+ */
6
+
7
+ import { fromMarkdown } from 'mdast-util-from-markdown';
8
+ import _ from 'lodash';
9
+
10
+ /**
11
+ * Parse a query into parts
12
+ * Splits a query string by pipe character and trims each part
13
+ *
14
+ * @param {string} query - Query string to parse
15
+ * @returns {Array} Array of query parts
16
+ */
17
+ function parseQuery(query) {
18
+ return query.split('|').map(part => part.trim());
19
+ }
20
+
21
+ /**
22
+ * Parse a transform query
23
+ * Splits a transformation query string by pipe character and parses each part
24
+ *
25
+ * @param {string} query - Transform query string to parse
26
+ * @returns {Array} Array of transform operations
27
+ */
28
+ function parseTransformQuery(query) {
29
+ const parts = [];
30
+ let currentPart = '';
31
+ let depth = 0;
32
+
33
+ // Parse the query, handling nested parentheses
34
+ for (let i = 0; i < query.length; i++) {
35
+ const char = query[i];
36
+
37
+ if (char === '(') {
38
+ depth++;
39
+ } else if (char === ')') {
40
+ depth--;
41
+ }
42
+
43
+ if (char === '|' && depth === 0) {
44
+ parts.push(currentPart.trim());
45
+ currentPart = '';
46
+ } else {
47
+ currentPart += char;
48
+ }
49
+ }
50
+
51
+ if (currentPart.trim()) {
52
+ parts.push(currentPart.trim());
53
+ }
54
+
55
+ // Parse each part into selector and operation
56
+ return parts.map(part => {
57
+ const match = part.match(/(.+?)\s+\|=\s+(.+)/);
58
+ if (match) {
59
+ return {
60
+ selector: match[1].trim(),
61
+ operation: match[2].trim()
62
+ };
63
+ }
64
+ return { selector: part.trim() };
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Parse markdown to AST with error handling for malformed markdown
70
+ *
71
+ * @param {string} markdown - Markdown content to parse
72
+ * @param {boolean} verbose - Whether to output verbose logs
73
+ * @returns {Object} Markdown AST
74
+ */
75
+ function parseMarkdown(markdown, verbose = false) {
76
+ try {
77
+ return fromMarkdown(markdown);
78
+ } catch (parseError) {
79
+ if (verbose) {
80
+ console.error(`Warning: Error parsing markdown: ${parseError.message}`);
81
+ }
82
+
83
+ // Create a simplified AST for basic operations
84
+ const ast = {
85
+ type: 'root',
86
+ children: []
87
+ };
88
+
89
+ // Try to extract headings and content even from malformed markdown
90
+ const lines = markdown.split('\n');
91
+ let inCodeBlock = false;
92
+ let currentCodeBlock = null;
93
+
94
+ for (const line of lines) {
95
+ // Handle code blocks
96
+ if (line.trim().startsWith('```')) {
97
+ if (!inCodeBlock) {
98
+ // Start of code block
99
+ inCodeBlock = true;
100
+ currentCodeBlock = {
101
+ type: 'code',
102
+ lang: line.trim().substring(3).trim(),
103
+ value: ''
104
+ };
105
+ } else {
106
+ // End of code block
107
+ inCodeBlock = false;
108
+ if (currentCodeBlock) {
109
+ ast.children.push(currentCodeBlock);
110
+ currentCodeBlock = null;
111
+ }
112
+ }
113
+ continue;
114
+ }
115
+
116
+ // Add content to code block if we're in one
117
+ if (inCodeBlock && currentCodeBlock) {
118
+ currentCodeBlock.value += line + '\n';
119
+ continue;
120
+ }
121
+
122
+ // Handle headings
123
+ if (line.startsWith('#')) {
124
+ // Count leading # characters for heading level
125
+ let level = 0;
126
+ for (let i = 0; i < line.length; i++) {
127
+ if (line[i] === '#') level++;
128
+ else break;
129
+ }
130
+
131
+ const text = line.substring(level).trim();
132
+
133
+ // Add heading to AST
134
+ ast.children.push({
135
+ type: 'heading',
136
+ depth: level,
137
+ children: [{ type: 'text', value: text }]
138
+ });
139
+ } else if (line.trim().length > 0) {
140
+ // Handle links in paragraphs
141
+ const linkMatch = line.match(/\[([^\]]+)\]\(([^)]+)\)/);
142
+ if (linkMatch) {
143
+ ast.children.push({
144
+ type: 'paragraph',
145
+ children: [{
146
+ type: 'link',
147
+ url: linkMatch[2],
148
+ children: [{ type: 'text', value: linkMatch[1] }]
149
+ }]
150
+ });
151
+ } else {
152
+ // Add paragraph for non-empty lines
153
+ ast.children.push({
154
+ type: 'paragraph',
155
+ children: [{ type: 'text', value: line.trim() }]
156
+ });
157
+ }
158
+ }
159
+ }
160
+
161
+ return ast;
162
+ }
163
+ }
164
+
165
+ export { parseQuery, parseTransformQuery, parseMarkdown };