@crypto512/jicon-mcp 0.7.1 → 1.0.0
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 +128 -395
- package/TOOL_LIST.md +810 -120
- package/dist/config/constants.d.ts +1 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +1 -0
- package/dist/config/constants.js.map +1 -1
- package/dist/config/loader.d.ts +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +27 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +8 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -1
- package/dist/confluence/client.d.ts +38 -0
- package/dist/confluence/client.d.ts.map +1 -1
- package/dist/confluence/client.js +117 -0
- package/dist/confluence/client.js.map +1 -1
- package/dist/confluence/tools.d.ts +102 -75
- package/dist/confluence/tools.d.ts.map +1 -1
- package/dist/confluence/tools.js +510 -151
- package/dist/confluence/tools.js.map +1 -1
- package/dist/confluence/types.d.ts +55 -1
- package/dist/confluence/types.d.ts.map +1 -1
- package/dist/index.js +88 -2
- package/dist/index.js.map +1 -1
- package/dist/jira/tools.d.ts +0 -5
- package/dist/jira/tools.d.ts.map +1 -1
- package/dist/jira/tools.js +40 -87
- package/dist/jira/tools.js.map +1 -1
- package/dist/permissions/filter.d.ts +5 -0
- package/dist/permissions/filter.d.ts.map +1 -1
- package/dist/permissions/filter.js +29 -12
- package/dist/permissions/filter.js.map +1 -1
- package/dist/permissions/tool-registry.d.ts +23 -25
- package/dist/permissions/tool-registry.d.ts.map +1 -1
- package/dist/permissions/tool-registry.js +41 -45
- package/dist/permissions/tool-registry.js.map +1 -1
- package/dist/permissions/write-home-validator.d.ts +35 -0
- package/dist/permissions/write-home-validator.d.ts.map +1 -0
- package/dist/permissions/write-home-validator.js +140 -0
- package/dist/permissions/write-home-validator.js.map +1 -0
- package/dist/tempo/tools.d.ts.map +1 -1
- package/dist/tempo/tools.js +43 -44
- package/dist/tempo/tools.js.map +1 -1
- package/dist/utils/buffer-tools.d.ts +119 -1
- package/dist/utils/buffer-tools.d.ts.map +1 -1
- package/dist/utils/buffer-tools.js +610 -3
- package/dist/utils/buffer-tools.js.map +1 -1
- package/dist/utils/content-buffer.d.ts +34 -0
- package/dist/utils/content-buffer.d.ts.map +1 -1
- package/dist/utils/content-buffer.js +79 -0
- package/dist/utils/content-buffer.js.map +1 -1
- package/dist/utils/http-client.d.ts.map +1 -1
- package/dist/utils/http-client.js +4 -4
- package/dist/utils/http-client.js.map +1 -1
- package/dist/utils/jicon-help.d.ts +29 -0
- package/dist/utils/jicon-help.d.ts.map +1 -0
- package/dist/utils/jicon-help.js +873 -0
- package/dist/utils/jicon-help.js.map +1 -0
- package/dist/utils/plantuml/client.d.ts +40 -0
- package/dist/utils/plantuml/client.d.ts.map +1 -0
- package/dist/utils/plantuml/client.js +306 -0
- package/dist/utils/plantuml/client.js.map +1 -0
- package/dist/utils/plantuml/docker-manager.d.ts +35 -0
- package/dist/utils/plantuml/docker-manager.d.ts.map +1 -0
- package/dist/utils/plantuml/docker-manager.js +280 -0
- package/dist/utils/plantuml/docker-manager.js.map +1 -0
- package/dist/utils/plantuml/index.d.ts +11 -0
- package/dist/utils/plantuml/index.d.ts.map +1 -0
- package/dist/utils/plantuml/index.js +16 -0
- package/dist/utils/plantuml/index.js.map +1 -0
- package/dist/utils/plantuml/service.d.ts +46 -0
- package/dist/utils/plantuml/service.d.ts.map +1 -0
- package/dist/utils/plantuml/service.js +96 -0
- package/dist/utils/plantuml/service.js.map +1 -0
- package/dist/utils/plantuml/tools.d.ts +65 -0
- package/dist/utils/plantuml/tools.d.ts.map +1 -0
- package/dist/utils/plantuml/tools.js +272 -0
- package/dist/utils/plantuml/tools.js.map +1 -0
- package/dist/utils/plantuml/types.d.ts +130 -0
- package/dist/utils/plantuml/types.d.ts.map +1 -0
- package/dist/utils/plantuml/types.js +66 -0
- package/dist/utils/plantuml/types.js.map +1 -0
- package/dist/utils/response-formatter.d.ts +14 -0
- package/dist/utils/response-formatter.d.ts.map +1 -1
- package/dist/utils/response-formatter.js +84 -1
- package/dist/utils/response-formatter.js.map +1 -1
- package/dist/utils/url-tools.d.ts +49 -0
- package/dist/utils/url-tools.d.ts.map +1 -0
- package/dist/utils/url-tools.js +141 -0
- package/dist/utils/url-tools.js.map +1 -0
- package/dist/utils/xhtml/confluence-schema.d.ts +55 -0
- package/dist/utils/xhtml/confluence-schema.d.ts.map +1 -0
- package/dist/utils/xhtml/confluence-schema.js +215 -0
- package/dist/utils/xhtml/confluence-schema.js.map +1 -0
- package/dist/utils/xhtml/index.d.ts +17 -0
- package/dist/utils/xhtml/index.d.ts.map +1 -0
- package/dist/utils/xhtml/index.js +21 -0
- package/dist/utils/xhtml/index.js.map +1 -0
- package/dist/utils/xhtml/operations.d.ts +100 -0
- package/dist/utils/xhtml/operations.d.ts.map +1 -0
- package/dist/utils/xhtml/operations.js +596 -0
- package/dist/utils/xhtml/operations.js.map +1 -0
- package/dist/utils/xhtml/parser.d.ts +64 -0
- package/dist/utils/xhtml/parser.d.ts.map +1 -0
- package/dist/utils/xhtml/parser.js +180 -0
- package/dist/utils/xhtml/parser.js.map +1 -0
- package/dist/utils/xhtml/plantuml.d.ts +112 -0
- package/dist/utils/xhtml/plantuml.d.ts.map +1 -0
- package/dist/utils/xhtml/plantuml.js +251 -0
- package/dist/utils/xhtml/plantuml.js.map +1 -0
- package/dist/utils/xhtml/selector.d.ts +35 -0
- package/dist/utils/xhtml/selector.d.ts.map +1 -0
- package/dist/utils/xhtml/selector.js +358 -0
- package/dist/utils/xhtml/selector.js.map +1 -0
- package/dist/utils/xhtml/serializer.d.ts +26 -0
- package/dist/utils/xhtml/serializer.d.ts.map +1 -0
- package/dist/utils/xhtml/serializer.js +170 -0
- package/dist/utils/xhtml/serializer.js.map +1 -0
- package/dist/utils/xhtml/types.d.ts +134 -0
- package/dist/utils/xhtml/types.d.ts.map +1 -0
- package/dist/utils/xhtml/types.js +65 -0
- package/dist/utils/xhtml/types.js.map +1 -0
- package/dist/utils/xhtml/validator.d.ts +67 -0
- package/dist/utils/xhtml/validator.d.ts.map +1 -0
- package/dist/utils/xhtml/validator.js +300 -0
- package/dist/utils/xhtml/validator.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS-like Selector Engine for Confluence XHTML
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Tag name: p, h1, table
|
|
6
|
+
* - Namespaced: ac:structured-macro, ri:page
|
|
7
|
+
* - Attributes: [ac:name="plantuml"], ac:structured-macro[ac:name="code"]
|
|
8
|
+
* - Pseudo-selectors: :nth-child(2), :first-child, :last-child
|
|
9
|
+
* - Descendants: table tbody tr
|
|
10
|
+
* - Direct children: ul > li
|
|
11
|
+
*/
|
|
12
|
+
import { getContentRoot } from "./parser.js";
|
|
13
|
+
/**
|
|
14
|
+
* Parse a single simple selector (tag[attr]:pseudo)
|
|
15
|
+
*/
|
|
16
|
+
function parseSimpleSelector(selector) {
|
|
17
|
+
const token = {};
|
|
18
|
+
let remaining = selector;
|
|
19
|
+
// First, extract pseudo-selectors (they always have a pattern like :name or :name(...))
|
|
20
|
+
// Distinguish from namespace prefixes: ac:structured-macro vs h2:first-child
|
|
21
|
+
// - Pseudo-selectors: :first-child, :nth-child(2), :contains("text")
|
|
22
|
+
// - Namespace prefix: ac:structured-macro, ri:page (part of the tag name)
|
|
23
|
+
//
|
|
24
|
+
// Key insight: Pseudo-selectors either:
|
|
25
|
+
// 1. Have () immediately after: :contains(...), :nth-child(...)
|
|
26
|
+
// 2. Are at the end or followed by more pseudo/attr: :first-child, :empty
|
|
27
|
+
// Namespace prefixes are part of the tag and followed by more tag-name characters, [ or end
|
|
28
|
+
// Match pseudo-selectors with arguments first: :name(arg) or :name-name(arg)
|
|
29
|
+
const pseudoWithArgRegex = /:([\w-]+)\(([^)]+)\)/g;
|
|
30
|
+
let pseudoMatch;
|
|
31
|
+
while ((pseudoMatch = pseudoWithArgRegex.exec(remaining)) !== null) {
|
|
32
|
+
if (!token.pseudos) {
|
|
33
|
+
token.pseudos = [];
|
|
34
|
+
}
|
|
35
|
+
const arg = pseudoMatch[2];
|
|
36
|
+
token.pseudos.push({
|
|
37
|
+
name: pseudoMatch[1],
|
|
38
|
+
arg: isNaN(Number(arg)) ? arg : Number(arg),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Remove matched pseudos with args from remaining
|
|
42
|
+
remaining = remaining.replace(pseudoWithArgRegex, "");
|
|
43
|
+
// Match pseudo-selectors without arguments: :first-child, :empty
|
|
44
|
+
// Only match KNOWN pseudo-selectors to avoid confusion with namespace prefixes
|
|
45
|
+
// (e.g., ac:structured-macro should NOT treat :structured-macro as a pseudo)
|
|
46
|
+
const knownPseudos = ["first-child", "last-child", "only-child", "empty"];
|
|
47
|
+
for (const pseudoName of knownPseudos) {
|
|
48
|
+
const pseudoPattern = new RegExp(`:${pseudoName}(?=$|[:\\[])`);
|
|
49
|
+
if (pseudoPattern.test(remaining)) {
|
|
50
|
+
if (!token.pseudos) {
|
|
51
|
+
token.pseudos = [];
|
|
52
|
+
}
|
|
53
|
+
token.pseudos.push({
|
|
54
|
+
name: pseudoName,
|
|
55
|
+
arg: undefined,
|
|
56
|
+
});
|
|
57
|
+
remaining = remaining.replace(`:${pseudoName}`, "");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Match tag name (including namespace prefix like ac:structured-macro)
|
|
61
|
+
const tagMatch = remaining.match(/^([\w:-]+)/);
|
|
62
|
+
if (tagMatch) {
|
|
63
|
+
token.tag = tagMatch[1].toLowerCase();
|
|
64
|
+
remaining = remaining.slice(tagMatch[0].length);
|
|
65
|
+
}
|
|
66
|
+
// Match attribute selectors [name="value"]
|
|
67
|
+
const attrRegex = /\[([^\]=]+)(?:="([^"]*)")?\]/g;
|
|
68
|
+
let attrMatch;
|
|
69
|
+
while ((attrMatch = attrRegex.exec(remaining)) !== null) {
|
|
70
|
+
if (!token.attributes) {
|
|
71
|
+
token.attributes = new Map();
|
|
72
|
+
}
|
|
73
|
+
const attrName = attrMatch[1];
|
|
74
|
+
const attrValue = attrMatch[2] ?? ""; // Empty string means "has attribute"
|
|
75
|
+
token.attributes.set(attrName, attrValue);
|
|
76
|
+
}
|
|
77
|
+
return token;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse a full selector string into tokens
|
|
81
|
+
*/
|
|
82
|
+
function parseSelector(selector) {
|
|
83
|
+
const tokens = [];
|
|
84
|
+
const parts = selector.trim().split(/\s+/);
|
|
85
|
+
for (let i = 0; i < parts.length; i++) {
|
|
86
|
+
const part = parts[i];
|
|
87
|
+
if (part === ">") {
|
|
88
|
+
// Direct child combinator
|
|
89
|
+
if (tokens.length > 0) {
|
|
90
|
+
tokens[tokens.length - 1].combinator = "child";
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Check for > attached to the selector
|
|
95
|
+
if (part.endsWith(">")) {
|
|
96
|
+
const actualPart = part.slice(0, -1);
|
|
97
|
+
if (actualPart) {
|
|
98
|
+
const token = parseSimpleSelector(actualPart);
|
|
99
|
+
token.combinator = "child";
|
|
100
|
+
tokens.push(token);
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (part.startsWith(">")) {
|
|
105
|
+
// > attached to next selector
|
|
106
|
+
if (tokens.length > 0) {
|
|
107
|
+
tokens[tokens.length - 1].combinator = "child";
|
|
108
|
+
}
|
|
109
|
+
const actualPart = part.slice(1);
|
|
110
|
+
if (actualPart) {
|
|
111
|
+
tokens.push(parseSimpleSelector(actualPart));
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const token = parseSimpleSelector(part);
|
|
116
|
+
if (i < parts.length - 1 && parts[i + 1] !== ">") {
|
|
117
|
+
token.combinator = "descendant";
|
|
118
|
+
}
|
|
119
|
+
tokens.push(token);
|
|
120
|
+
}
|
|
121
|
+
return tokens;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if an element matches a selector token
|
|
125
|
+
*/
|
|
126
|
+
function matchesToken(element, token) {
|
|
127
|
+
// Check tag name
|
|
128
|
+
if (token.tag) {
|
|
129
|
+
const tagName = element.tagName.toLowerCase();
|
|
130
|
+
if (tagName !== token.tag) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check attributes
|
|
135
|
+
if (token.attributes) {
|
|
136
|
+
for (const [name, value] of token.attributes) {
|
|
137
|
+
const attrValue = element.getAttribute(name);
|
|
138
|
+
if (attrValue === null) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
// If value is specified, must match exactly
|
|
142
|
+
if (value !== "" && attrValue !== value) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Check pseudo-selectors
|
|
148
|
+
if (token.pseudos) {
|
|
149
|
+
for (const pseudo of token.pseudos) {
|
|
150
|
+
if (!matchesPseudo(element, pseudo)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
// Unsupported pseudo-selectors that LLMs commonly try to use
|
|
158
|
+
const UNSUPPORTED_PSEUDOS = new Set([
|
|
159
|
+
"has-text", // jQuery-style, not standard CSS
|
|
160
|
+
"contains", // jQuery-style (we'll add our own :contains())
|
|
161
|
+
"has", // CSS :has() is complex, not implemented
|
|
162
|
+
"not", // CSS :not() requires full selector parsing
|
|
163
|
+
"matches", // CSS :matches() / :is()
|
|
164
|
+
"where", // CSS :where()
|
|
165
|
+
]);
|
|
166
|
+
/**
|
|
167
|
+
* Check if a selector contains unsupported pseudo-selectors
|
|
168
|
+
* Returns the unsupported pseudo name or null
|
|
169
|
+
*/
|
|
170
|
+
export function findUnsupportedPseudo(selector) {
|
|
171
|
+
const pseudoRegex = /:(\w+)(?:\([^)]*\))?/g;
|
|
172
|
+
let match;
|
|
173
|
+
while ((match = pseudoRegex.exec(selector)) !== null) {
|
|
174
|
+
const pseudoName = match[1].toLowerCase();
|
|
175
|
+
// Check if it's unsupported (but not our supported ones)
|
|
176
|
+
if (UNSUPPORTED_PSEUDOS.has(pseudoName) && pseudoName !== "contains") {
|
|
177
|
+
return pseudoName;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Check if element matches a pseudo-selector
|
|
184
|
+
*/
|
|
185
|
+
function matchesPseudo(element, pseudo) {
|
|
186
|
+
const parent = element.parentElement;
|
|
187
|
+
switch (pseudo.name) {
|
|
188
|
+
case "first-child":
|
|
189
|
+
if (!parent)
|
|
190
|
+
return false;
|
|
191
|
+
return Array.from(parent.children).indexOf(element) === 0;
|
|
192
|
+
case "last-child":
|
|
193
|
+
if (!parent)
|
|
194
|
+
return false;
|
|
195
|
+
const siblings = Array.from(parent.children);
|
|
196
|
+
return siblings.indexOf(element) === siblings.length - 1;
|
|
197
|
+
case "nth-child": {
|
|
198
|
+
if (!parent)
|
|
199
|
+
return false;
|
|
200
|
+
const n = typeof pseudo.arg === "number" ? pseudo.arg : parseInt(pseudo.arg || "0", 10);
|
|
201
|
+
return Array.from(parent.children).indexOf(element) === n - 1; // nth-child is 1-based
|
|
202
|
+
}
|
|
203
|
+
case "only-child":
|
|
204
|
+
if (!parent)
|
|
205
|
+
return false;
|
|
206
|
+
return parent.children.length === 1;
|
|
207
|
+
case "empty":
|
|
208
|
+
return element.children.length === 0 && (element.textContent || "").trim() === "";
|
|
209
|
+
case "contains": {
|
|
210
|
+
// :contains("text") - matches if element contains the text
|
|
211
|
+
let searchText = typeof pseudo.arg === "string" ? pseudo.arg : String(pseudo.arg || "");
|
|
212
|
+
// Strip surrounding quotes if present
|
|
213
|
+
if ((searchText.startsWith('"') && searchText.endsWith('"')) ||
|
|
214
|
+
(searchText.startsWith("'") && searchText.endsWith("'"))) {
|
|
215
|
+
searchText = searchText.slice(1, -1);
|
|
216
|
+
}
|
|
217
|
+
const elementText = element.textContent || "";
|
|
218
|
+
return elementText.toLowerCase().includes(searchText.toLowerCase());
|
|
219
|
+
}
|
|
220
|
+
case "has-text": {
|
|
221
|
+
// Alias for :contains() - jQuery-style syntax that LLMs often try
|
|
222
|
+
let searchText = typeof pseudo.arg === "string" ? pseudo.arg : String(pseudo.arg || "");
|
|
223
|
+
// Strip surrounding quotes if present
|
|
224
|
+
if ((searchText.startsWith('"') && searchText.endsWith('"')) ||
|
|
225
|
+
(searchText.startsWith("'") && searchText.endsWith("'"))) {
|
|
226
|
+
searchText = searchText.slice(1, -1);
|
|
227
|
+
}
|
|
228
|
+
const elementText = element.textContent || "";
|
|
229
|
+
return elementText.toLowerCase().includes(searchText.toLowerCase());
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
// Unknown pseudo-selector - ignore (don't fail, just don't filter)
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Find all elements matching a selector token within a root
|
|
238
|
+
*/
|
|
239
|
+
function findMatchingElements(root, token, combinator, startFrom) {
|
|
240
|
+
const matches = [];
|
|
241
|
+
if (combinator === "child" && startFrom) {
|
|
242
|
+
// Direct children only
|
|
243
|
+
for (const child of startFrom.children) {
|
|
244
|
+
if (matchesToken(child, token)) {
|
|
245
|
+
matches.push(child);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (startFrom) {
|
|
250
|
+
// Descendants - use recursive traversal (no TreeWalker in linkedom Node.js)
|
|
251
|
+
const traverse = (el) => {
|
|
252
|
+
for (const child of el.children) {
|
|
253
|
+
if (matchesToken(child, token)) {
|
|
254
|
+
matches.push(child);
|
|
255
|
+
}
|
|
256
|
+
traverse(child);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
traverse(startFrom);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Search from root
|
|
263
|
+
const traverse = (el) => {
|
|
264
|
+
if (matchesToken(el, token)) {
|
|
265
|
+
matches.push(el);
|
|
266
|
+
}
|
|
267
|
+
for (const child of el.children) {
|
|
268
|
+
traverse(child);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
traverse(root);
|
|
272
|
+
}
|
|
273
|
+
return matches;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Execute a selector query on a document
|
|
277
|
+
*
|
|
278
|
+
* @param document - DOM Document to search
|
|
279
|
+
* @param selector - CSS-like selector string
|
|
280
|
+
* @returns SelectorResult with matches
|
|
281
|
+
*/
|
|
282
|
+
export function querySelector(document, selector) {
|
|
283
|
+
const root = getContentRoot(document);
|
|
284
|
+
if (!root) {
|
|
285
|
+
return { matches: [], totalCount: 0 };
|
|
286
|
+
}
|
|
287
|
+
const tokens = parseSelector(selector);
|
|
288
|
+
if (tokens.length === 0) {
|
|
289
|
+
return { matches: [], totalCount: 0 };
|
|
290
|
+
}
|
|
291
|
+
// Start with first token
|
|
292
|
+
let currentMatches = findMatchingElements(root, tokens[0], undefined, undefined);
|
|
293
|
+
// Process remaining tokens
|
|
294
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
295
|
+
const token = tokens[i];
|
|
296
|
+
const combinator = tokens[i - 1].combinator;
|
|
297
|
+
const nextMatches = [];
|
|
298
|
+
for (const match of currentMatches) {
|
|
299
|
+
const descendants = findMatchingElements(root, token, combinator, match);
|
|
300
|
+
nextMatches.push(...descendants);
|
|
301
|
+
}
|
|
302
|
+
currentMatches = nextMatches;
|
|
303
|
+
}
|
|
304
|
+
// Convert to SelectorMatch array
|
|
305
|
+
const matches = currentMatches.map((element, index) => ({
|
|
306
|
+
element,
|
|
307
|
+
index,
|
|
308
|
+
}));
|
|
309
|
+
return {
|
|
310
|
+
matches,
|
|
311
|
+
totalCount: matches.length,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Check if a selector is valid
|
|
316
|
+
*/
|
|
317
|
+
export function isValidSelector(selector) {
|
|
318
|
+
try {
|
|
319
|
+
const tokens = parseSelector(selector);
|
|
320
|
+
return tokens.length > 0;
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get a human-readable description of a selector
|
|
328
|
+
*/
|
|
329
|
+
export function describeSelector(selector) {
|
|
330
|
+
const tokens = parseSelector(selector);
|
|
331
|
+
const parts = [];
|
|
332
|
+
for (const token of tokens) {
|
|
333
|
+
let desc = token.tag || "*";
|
|
334
|
+
if (token.attributes) {
|
|
335
|
+
for (const [name, value] of token.attributes) {
|
|
336
|
+
if (value) {
|
|
337
|
+
desc += `[${name}="${value}"]`;
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
desc += `[${name}]`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (token.pseudos) {
|
|
345
|
+
for (const pseudo of token.pseudos) {
|
|
346
|
+
if (pseudo.arg !== undefined) {
|
|
347
|
+
desc += `:${pseudo.name}(${pseudo.arg})`;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
desc += `:${pseudo.name}`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
parts.push(desc);
|
|
355
|
+
}
|
|
356
|
+
return parts.join(" ");
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../../src/utils/xhtml/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAiB7C;;GAEG;AACH,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,IAAI,SAAS,GAAG,QAAQ,CAAC;IAEzB,wFAAwF;IACxF,6EAA6E;IAC7E,qEAAqE;IACrE,0EAA0E;IAC1E,EAAE;IACF,wCAAwC;IACxC,gEAAgE;IAChE,0EAA0E;IAC1E,4FAA4F;IAE5F,6EAA6E;IAC7E,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;IACnD,IAAI,WAAW,CAAC;IAChB,OAAO,CAAC,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QACrB,CAAC;QACD,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC3B,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YACjB,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;YACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IACD,kDAAkD;IAClD,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAEtD,iEAAiE;IACjE,+EAA+E;IAC/E,6EAA6E;IAC7E,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1E,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,IAAI,UAAU,cAAc,CAAC,CAAC;QAC/D,IAAI,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnB,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;YACrB,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjB,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,SAAS;aACf,CAAC,CAAC;YACH,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtC,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,2CAA2C;IAC3C,MAAM,SAAS,GAAG,+BAA+B,CAAC;IAClD,IAAI,SAAS,CAAC;IACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YACtB,KAAK,CAAC,UAAU,GAAG,IAAI,GAAG,EAAE,CAAC;QAC/B,CAAC;QACD,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,qCAAqC;QAC3E,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,QAAgB;IACrC,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,0BAA0B;YAC1B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC;YACjD,CAAC;YACD,SAAS;QACX,CAAC;QAED,uCAAuC;QACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;gBAC9C,KAAK,CAAC,UAAU,GAAG,OAAO,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,8BAA8B;YAC9B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC;YACjD,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;YAC/C,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACjD,KAAK,CAAC,UAAU,GAAG,YAAY,CAAC;QAClC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,OAAgB,EAAE,KAAoB;IAC1D,iBAAiB;IACjB,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,OAAO,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,4CAA4C;YAC5C,IAAI,KAAK,KAAK,EAAE,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;gBACxC,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;gBACpC,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6DAA6D;AAC7D,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC;IAClC,UAAU,EAAK,iCAAiC;IAChD,UAAU,EAAK,+CAA+C;IAC9D,KAAK,EAAU,yCAAyC;IACxD,KAAK,EAAU,4CAA4C;IAC3D,SAAS,EAAM,yBAAyB;IACxC,OAAO,EAAQ,eAAe;CAC/B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,WAAW,GAAG,uBAAuB,CAAC;IAC5C,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1C,yDAAyD;QACzD,IAAI,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;YACrE,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,OAAgB,EAAE,MAAsB;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAErC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,aAAa;YAChB,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAE5D,KAAK,YAAY;YACf,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC7C,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAE3D,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;YACxF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,uBAAuB;QACxF,CAAC;QAED,KAAK,YAAY;YACf,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;QAEtC,KAAK,OAAO;YACV,OAAO,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAEpF,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,2DAA2D;YAC3D,IAAI,UAAU,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;YACxF,sCAAsC;YACtC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACxD,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7D,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;YAC9C,OAAO,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,kEAAkE;YAClE,IAAI,UAAU,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;YACxF,sCAAsC;YACtC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACxD,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7D,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;YAC9C,OAAO,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QACtE,CAAC;QAED;YACE,mEAAmE;YACnE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,IAAa,EAAE,KAAoB,EAAE,UAA8C,EAAE,SAAmB;IACpI,MAAM,OAAO,GAAc,EAAE,CAAC;IAE9B,IAAI,UAAU,KAAK,OAAO,IAAI,SAAS,EAAE,CAAC;QACxC,uBAAuB;QACvB,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,SAAS,EAAE,CAAC;QACrB,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,CAAC,EAAW,EAAE,EAAE;YAC/B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;gBAChC,IAAI,YAAY,CAAC,KAAgB,EAAE,KAAK,CAAC,EAAE,CAAC;oBAC1C,OAAO,CAAC,IAAI,CAAC,KAAgB,CAAC,CAAC;gBACjC,CAAC;gBACD,QAAQ,CAAC,KAAgB,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,mBAAmB;QACnB,MAAM,QAAQ,GAAG,CAAC,EAAW,EAAE,EAAE;YAC/B,IAAI,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnB,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;gBAChC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB,EAAE,QAAgB;IAChE,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IACxC,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IACxC,CAAC;IAED,yBAAyB;IACzB,IAAI,cAAc,GAAG,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAEjF,2BAA2B;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;QAC5C,MAAM,WAAW,GAAc,EAAE,CAAC;QAElC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;YACzE,WAAW,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;QACnC,CAAC;QAED,cAAc,GAAG,WAAW,CAAC;IAC/B,CAAC;IAED,iCAAiC;IACjC,MAAM,OAAO,GAAoB,cAAc,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACvE,OAAO;QACP,KAAK;KACN,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,OAAO;QACP,UAAU,EAAE,OAAO,CAAC,MAAM;KAC3B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC;QAE5B,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBAC7C,IAAI,KAAK,EAAE,CAAC;oBACV,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACN,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;oBAC7B,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC;gBAC3C,CAAC;qBAAM,CAAC;oBACN,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XHTML Serializer
|
|
3
|
+
*
|
|
4
|
+
* Serializes DOM back to XHTML string with proper handling of:
|
|
5
|
+
* - Namespace prefixes (ac:, ri:)
|
|
6
|
+
* - CDATA sections
|
|
7
|
+
* - Self-closing tags
|
|
8
|
+
* - Attribute order preservation
|
|
9
|
+
*/
|
|
10
|
+
import type { Document, Element } from "linkedom";
|
|
11
|
+
/**
|
|
12
|
+
* Serialize a DOM Document back to XHTML string
|
|
13
|
+
*
|
|
14
|
+
* @param document - DOM Document to serialize
|
|
15
|
+
* @returns XHTML string (Confluence storage format)
|
|
16
|
+
*/
|
|
17
|
+
export declare function serializeXhtml(document: Document): string;
|
|
18
|
+
/**
|
|
19
|
+
* Serialize an element fragment (not a full document)
|
|
20
|
+
*/
|
|
21
|
+
export declare function serializeFragment(element: Element): string;
|
|
22
|
+
/**
|
|
23
|
+
* Pretty-print XHTML with consistent indentation
|
|
24
|
+
*/
|
|
25
|
+
export declare function prettyPrint(content: string): string;
|
|
26
|
+
//# sourceMappingURL=serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../../src/utils/xhtml/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AA6HlD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA4BzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQnD"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XHTML Serializer
|
|
3
|
+
*
|
|
4
|
+
* Serializes DOM back to XHTML string with proper handling of:
|
|
5
|
+
* - Namespace prefixes (ac:, ri:)
|
|
6
|
+
* - CDATA sections
|
|
7
|
+
* - Self-closing tags
|
|
8
|
+
* - Attribute order preservation
|
|
9
|
+
*/
|
|
10
|
+
import { getContentRoot } from "./parser.js";
|
|
11
|
+
// Elements that should always be self-closing when empty
|
|
12
|
+
const VOID_ELEMENTS = new Set([
|
|
13
|
+
"br", "hr", "img", "input", "meta", "link", "area", "base", "col", "embed",
|
|
14
|
+
"param", "source", "track", "wbr",
|
|
15
|
+
]);
|
|
16
|
+
// Elements that should preserve their content format (CDATA)
|
|
17
|
+
const CDATA_ELEMENTS = new Set([
|
|
18
|
+
"ac:plain-text-body",
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Escape XML special characters in text content
|
|
22
|
+
*/
|
|
23
|
+
function escapeXml(text) {
|
|
24
|
+
return text
|
|
25
|
+
.replace(/&/g, "&")
|
|
26
|
+
.replace(/</g, "<")
|
|
27
|
+
.replace(/>/g, ">")
|
|
28
|
+
.replace(/"/g, """)
|
|
29
|
+
.replace(/'/g, "'");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if text contains CDATA content
|
|
33
|
+
*/
|
|
34
|
+
function containsCData(text) {
|
|
35
|
+
return text.includes("<![CDATA[");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Serialize element attributes to string
|
|
39
|
+
*/
|
|
40
|
+
function serializeAttributes(element) {
|
|
41
|
+
const attrs = [];
|
|
42
|
+
for (const attr of element.attributes) {
|
|
43
|
+
// Skip xmlns declarations for our wrapper
|
|
44
|
+
if (attr.name.startsWith("xmlns:") && element.tagName.toLowerCase() === "xhtml-root") {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
attrs.push(`${attr.name}="${escapeXml(attr.value)}"`);
|
|
48
|
+
}
|
|
49
|
+
return attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Serialize a single element to XHTML string
|
|
53
|
+
*/
|
|
54
|
+
function serializeElement(element, indent = "", preserveWhitespace = false) {
|
|
55
|
+
const tagName = element.tagName.toLowerCase();
|
|
56
|
+
const attrs = serializeAttributes(element);
|
|
57
|
+
// Check if this is a CDATA-containing element
|
|
58
|
+
const isCDataElement = CDATA_ELEMENTS.has(tagName);
|
|
59
|
+
// Check if element has children
|
|
60
|
+
const hasChildren = element.childNodes.length > 0;
|
|
61
|
+
// Void elements
|
|
62
|
+
if (!hasChildren && VOID_ELEMENTS.has(tagName)) {
|
|
63
|
+
return `${indent}<${tagName}${attrs} />`;
|
|
64
|
+
}
|
|
65
|
+
// Empty non-void elements
|
|
66
|
+
if (!hasChildren) {
|
|
67
|
+
return `${indent}<${tagName}${attrs}></${tagName}>`;
|
|
68
|
+
}
|
|
69
|
+
// Serialize children
|
|
70
|
+
const children = [];
|
|
71
|
+
let hasElementChildren = false;
|
|
72
|
+
for (const child of element.childNodes) {
|
|
73
|
+
if (child.nodeType === 1) {
|
|
74
|
+
// Element node
|
|
75
|
+
hasElementChildren = true;
|
|
76
|
+
const childElement = child;
|
|
77
|
+
const childPreserve = preserveWhitespace ||
|
|
78
|
+
tagName === "pre" || tagName === "code" ||
|
|
79
|
+
isCDataElement;
|
|
80
|
+
children.push(serializeElement(childElement, preserveWhitespace ? "" : indent + " ", childPreserve));
|
|
81
|
+
}
|
|
82
|
+
else if (child.nodeType === 3) {
|
|
83
|
+
// Text node
|
|
84
|
+
const text = child.textContent || "";
|
|
85
|
+
// Handle CDATA content
|
|
86
|
+
if (isCDataElement && containsCData(text)) {
|
|
87
|
+
// Preserve CDATA as-is
|
|
88
|
+
children.push(text);
|
|
89
|
+
}
|
|
90
|
+
else if (isCDataElement || preserveWhitespace || tagName === "pre" || tagName === "code") {
|
|
91
|
+
// Preserve whitespace in these elements
|
|
92
|
+
children.push(text);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Normal text - trim if only whitespace
|
|
96
|
+
const trimmed = text.trim();
|
|
97
|
+
if (trimmed) {
|
|
98
|
+
children.push(escapeXml(trimmed));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (child.nodeType === 4) {
|
|
103
|
+
// CDATA section node
|
|
104
|
+
const cdataContent = child.textContent || "";
|
|
105
|
+
children.push(`<![CDATA[${cdataContent}]]>`);
|
|
106
|
+
}
|
|
107
|
+
else if (child.nodeType === 8) {
|
|
108
|
+
// Comment node
|
|
109
|
+
children.push(`<!--${child.textContent || ""}-->`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Format output
|
|
113
|
+
if (preserveWhitespace || isCDataElement || !hasElementChildren) {
|
|
114
|
+
// Inline format for text-heavy content
|
|
115
|
+
return `${indent}<${tagName}${attrs}>${children.join("")}</${tagName}>`;
|
|
116
|
+
}
|
|
117
|
+
// Multi-line format for structured content
|
|
118
|
+
const childContent = children.join("\n");
|
|
119
|
+
return `${indent}<${tagName}${attrs}>\n${childContent}\n${indent}</${tagName}>`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Serialize a DOM Document back to XHTML string
|
|
123
|
+
*
|
|
124
|
+
* @param document - DOM Document to serialize
|
|
125
|
+
* @returns XHTML string (Confluence storage format)
|
|
126
|
+
*/
|
|
127
|
+
export function serializeXhtml(document) {
|
|
128
|
+
const needsUnwrap = document.__needsUnwrap;
|
|
129
|
+
const root = getContentRoot(document);
|
|
130
|
+
if (!root) {
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
if (needsUnwrap) {
|
|
134
|
+
// Serialize children of our wrapper, not the wrapper itself
|
|
135
|
+
const children = [];
|
|
136
|
+
for (const child of root.childNodes) {
|
|
137
|
+
if (child.nodeType === 1) {
|
|
138
|
+
children.push(serializeElement(child));
|
|
139
|
+
}
|
|
140
|
+
else if (child.nodeType === 3) {
|
|
141
|
+
const text = (child.textContent || "").trim();
|
|
142
|
+
if (text) {
|
|
143
|
+
children.push(escapeXml(text));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return children.join("\n");
|
|
148
|
+
}
|
|
149
|
+
// Serialize the entire document
|
|
150
|
+
return serializeElement(root);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Serialize an element fragment (not a full document)
|
|
154
|
+
*/
|
|
155
|
+
export function serializeFragment(element) {
|
|
156
|
+
return serializeElement(element);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Pretty-print XHTML with consistent indentation
|
|
160
|
+
*/
|
|
161
|
+
export function prettyPrint(content) {
|
|
162
|
+
// This is a simple implementation - for more complex formatting,
|
|
163
|
+
// we would parse and re-serialize
|
|
164
|
+
return content
|
|
165
|
+
.replace(/>\s*</g, ">\n<")
|
|
166
|
+
.split("\n")
|
|
167
|
+
.map((line) => line)
|
|
168
|
+
.join("\n");
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../../src/utils/xhtml/serializer.ts"],"names":[],"mappings":"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"}
|