@bfra.me/doc-sync 0.1.0 → 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.
@@ -1,6 +1,13 @@
1
1
  import {
2
2
  SENTINEL_MARKERS
3
3
  } from "./chunk-ROLA7SBB.js";
4
+ import {
5
+ extractCodeBlocks,
6
+ parseJSXTags,
7
+ sanitizeAttribute,
8
+ sanitizeForMDX,
9
+ sanitizeJSXTag
10
+ } from "./chunk-GZ2MP3VN.js";
4
11
 
5
12
  // src/generators/api-reference-generator.ts
6
13
  function generateAPIReference(api) {
@@ -435,80 +442,6 @@ function formatGroupedExamples(api, options = {}) {
435
442
  return sections.join("\n").trim();
436
443
  }
437
444
 
438
- // src/utils/sanitization.ts
439
- import { sanitizeInput } from "@bfra.me/es/validation";
440
- import escapeHtml from "escape-html";
441
- function sanitizeForMDX(content) {
442
- const escaped = sanitizeInput(content, { trim: false });
443
- return escaped.replaceAll("{", "{").replaceAll("}", "}");
444
- }
445
- function sanitizeAttribute(value) {
446
- return escapeHtml(value);
447
- }
448
- function parseJSXAttributes(tag) {
449
- const attrs = [];
450
- const spaceIndex = tag.indexOf(" ");
451
- if (spaceIndex === -1) return attrs;
452
- const closeIndex = tag.lastIndexOf(">");
453
- if (closeIndex === -1) return attrs;
454
- const attrRegion = tag.slice(spaceIndex + 1, closeIndex).trim();
455
- let i = 0;
456
- while (i < attrRegion.length) {
457
- while (i < attrRegion.length && /\s/.test(attrRegion.charAt(i))) i++;
458
- if (i >= attrRegion.length) break;
459
- let name = "";
460
- while (i < attrRegion.length && /[\w-]/.test(attrRegion.charAt(i))) {
461
- name += attrRegion[i];
462
- i++;
463
- }
464
- if (!name) break;
465
- while (i < attrRegion.length && /\s/.test(attrRegion.charAt(i))) i++;
466
- if (i >= attrRegion.length || attrRegion[i] !== "=") {
467
- attrs.push({ name, value: null });
468
- continue;
469
- }
470
- i++;
471
- while (i < attrRegion.length && /\s/.test(attrRegion.charAt(i))) i++;
472
- if (i >= attrRegion.length) {
473
- attrs.push({ name, value: "" });
474
- break;
475
- }
476
- let value = "";
477
- const quote = attrRegion[i];
478
- if (quote === '"' || quote === "'") {
479
- i++;
480
- while (i < attrRegion.length && attrRegion[i] !== quote) {
481
- value += attrRegion[i];
482
- i++;
483
- }
484
- if (i < attrRegion.length) i++;
485
- } else {
486
- while (i < attrRegion.length && !/[\s/>]/.test(attrRegion.charAt(i))) {
487
- value += attrRegion[i];
488
- i++;
489
- }
490
- }
491
- attrs.push({ name, value });
492
- }
493
- return attrs;
494
- }
495
- function sanitizeJSXTag(tag) {
496
- const tagMatch = tag.match(/^<([A-Z][a-zA-Z0-9]*)/);
497
- if (!tagMatch || typeof tagMatch[1] !== "string" || tagMatch[1].length === 0) {
498
- return escapeHtml(tag);
499
- }
500
- const tagName = tagMatch[1];
501
- const selfClosing = tag.endsWith("/>");
502
- const attributes = parseJSXAttributes(tag);
503
- const sanitizedAttrs = attributes.map(({ name, value }) => {
504
- if (value === null) return name;
505
- const escaped = escapeHtml(value);
506
- return `${name}="${escaped}"`;
507
- });
508
- const attrString = sanitizedAttrs.length > 0 ? ` ${sanitizedAttrs.join(" ")}` : "";
509
- return `<${tagName}${attrString}${selfClosing ? " />" : ">"}`;
510
- }
511
-
512
445
  // src/generators/component-mapper.ts
513
446
  var DEFAULT_CONFIG = {
514
447
  featureSections: ["features", "highlights", "key features"],
@@ -1167,179 +1100,6 @@ function stripQuotes(value) {
1167
1100
 
1168
1101
  // src/generators/mdx-generator.ts
1169
1102
  import { err as err2, ok as ok2 } from "@bfra.me/es/result";
1170
-
1171
- // src/utils/safe-patterns.ts
1172
- import remarkParse from "remark-parse";
1173
- import { unified } from "unified";
1174
- function createHeadingPattern(level) {
1175
- if (level < 1 || level > 6) {
1176
- throw new Error("Heading level must be between 1 and 6");
1177
- }
1178
- const hashes = "#".repeat(level);
1179
- return new RegExp(`^${hashes} ([^\r
1180
- ]+)$`, "gm");
1181
- }
1182
- function hasComponent(content, componentName) {
1183
- const escaped = componentName.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
1184
- const pattern = new RegExp(String.raw`<${escaped}(?:\s[^>]*)?(?:>|\/>)`);
1185
- return pattern.test(content);
1186
- }
1187
- function extractCodeBlocks(content) {
1188
- const processor = unified().use(remarkParse);
1189
- let tree;
1190
- try {
1191
- tree = processor.parse(content);
1192
- } catch {
1193
- return [];
1194
- }
1195
- const blocks = [];
1196
- function visit(node) {
1197
- if (node.type === "code") {
1198
- const lang = node.lang ?? "";
1199
- const value = node.value ?? "";
1200
- blocks.push(`\`\`\`${lang}
1201
- ${value}
1202
- \`\`\``);
1203
- }
1204
- if (Array.isArray(node.children)) {
1205
- for (const child of node.children) {
1206
- visit(child);
1207
- }
1208
- }
1209
- }
1210
- visit(tree);
1211
- return blocks;
1212
- }
1213
- function parseJSXTags(content) {
1214
- const results = [];
1215
- let i = 0;
1216
- while (i < content.length) {
1217
- if (content[i] !== "<") {
1218
- i++;
1219
- continue;
1220
- }
1221
- const startIndex = i;
1222
- i++;
1223
- if (i >= content.length) break;
1224
- const isClosing = content[i] === "/";
1225
- if (isClosing) {
1226
- i++;
1227
- if (i >= content.length) break;
1228
- }
1229
- if (!/^[A-Z]/.test(content[i] ?? "")) {
1230
- continue;
1231
- }
1232
- let tagName = "";
1233
- while (i < content.length && /^[a-z0-9]/i.test(content[i] ?? "")) {
1234
- tagName += content[i];
1235
- i++;
1236
- }
1237
- if (tagName.length === 0) {
1238
- continue;
1239
- }
1240
- if (isClosing) {
1241
- if (i < content.length && content[i] === ">") {
1242
- results.push({
1243
- tag: `</${tagName}>`,
1244
- index: startIndex,
1245
- isClosing: true,
1246
- isSelfClosing: false
1247
- });
1248
- i++;
1249
- }
1250
- continue;
1251
- }
1252
- let depth = 0;
1253
- let foundEnd = false;
1254
- let isSelfClosing = false;
1255
- while (i < content.length && !foundEnd) {
1256
- const char = content[i];
1257
- if ((char === '"' || char === "'") && depth === 0) {
1258
- const quote = char;
1259
- i++;
1260
- while (i < content.length && content[i] !== quote) {
1261
- if (content[i] === "\\" && i + 1 < content.length) {
1262
- i += 2;
1263
- } else {
1264
- i++;
1265
- }
1266
- }
1267
- if (i < content.length) i++;
1268
- continue;
1269
- }
1270
- if (char === "{") {
1271
- depth++;
1272
- i++;
1273
- continue;
1274
- }
1275
- if (char === "}") {
1276
- depth = Math.max(0, depth - 1);
1277
- i++;
1278
- continue;
1279
- }
1280
- if (depth === 0) {
1281
- if (char === "/" && i + 1 < content.length && content[i + 1] === ">") {
1282
- isSelfClosing = true;
1283
- foundEnd = true;
1284
- i += 2;
1285
- continue;
1286
- }
1287
- if (char === ">") {
1288
- foundEnd = true;
1289
- i++;
1290
- continue;
1291
- }
1292
- }
1293
- i++;
1294
- }
1295
- if (foundEnd) {
1296
- const fullTag = content.slice(startIndex, i);
1297
- results.push({ tag: fullTag, index: startIndex, isClosing: false, isSelfClosing });
1298
- }
1299
- }
1300
- return results;
1301
- }
1302
- function findEmptyMarkdownLinks(content) {
1303
- const positions = [];
1304
- let pos = 0;
1305
- while (pos < content.length) {
1306
- const openBracket = content.indexOf("[", pos);
1307
- if (openBracket === -1) break;
1308
- let bracketDepth = 1;
1309
- let closeBracket = openBracket + 1;
1310
- while (closeBracket < content.length && bracketDepth > 0) {
1311
- if (content[closeBracket] === "[") {
1312
- bracketDepth++;
1313
- } else if (content[closeBracket] === "]") {
1314
- bracketDepth--;
1315
- }
1316
- closeBracket++;
1317
- }
1318
- if (bracketDepth !== 0) {
1319
- pos = openBracket + 1;
1320
- continue;
1321
- }
1322
- closeBracket--;
1323
- if (closeBracket + 1 < content.length && content[closeBracket + 1] === "(") {
1324
- let parenPos = closeBracket + 2;
1325
- let isEmptyOrWhitespace = true;
1326
- while (parenPos < content.length && content[parenPos] !== ")") {
1327
- if (!/^\s/.test(content[parenPos] ?? "")) {
1328
- isEmptyOrWhitespace = false;
1329
- break;
1330
- }
1331
- parenPos++;
1332
- }
1333
- if (isEmptyOrWhitespace && parenPos < content.length && content[parenPos] === ")") {
1334
- positions.push(openBracket);
1335
- }
1336
- }
1337
- pos = closeBracket + 1;
1338
- }
1339
- return positions;
1340
- }
1341
-
1342
- // src/generators/mdx-generator.ts
1343
1103
  var DEFAULT_OPTIONS2 = {
1344
1104
  includeAPI: true,
1345
1105
  includeExamples: true,
@@ -1442,11 +1202,7 @@ function sanitizeTextContent(content) {
1442
1202
  if (index > lastIndex) {
1443
1203
  parts.push(sanitizeContent(content.slice(lastIndex, index)));
1444
1204
  }
1445
- if (isClosing) {
1446
- parts.push(tag);
1447
- } else {
1448
- parts.push(sanitizeJSXTag(tag));
1449
- }
1205
+ parts.push(isClosing ? tag : sanitizeJSXTag(tag));
1450
1206
  lastIndex = index + tag.length;
1451
1207
  }
1452
1208
  if (lastIndex < content.length) {
@@ -1471,17 +1227,23 @@ function validateMDXSyntax(mdx) {
1471
1227
  }
1472
1228
  return ok2(true);
1473
1229
  }
1230
+ function isTypeScriptGeneric(tag) {
1231
+ const tagNameMatch = tag.match(/<\/?([A-Z][a-zA-Z0-9]*)/);
1232
+ const tagName = tagNameMatch?.[1];
1233
+ return tagName !== void 0 && tagName.length === 1;
1234
+ }
1474
1235
  function checkForUnclosedTags(mdx) {
1475
1236
  const unclosed = [];
1476
1237
  const tagStack = [];
1477
1238
  const codeBlocks = extractCodeBlocks(mdx);
1478
- let contentWithoutCodeBlocks = mdx;
1239
+ let contentWithoutCode = mdx;
1479
1240
  for (const block of codeBlocks) {
1480
1241
  const lineCount = block.split("\n").length;
1481
1242
  const placeholder = "\n".repeat(lineCount);
1482
- contentWithoutCodeBlocks = contentWithoutCodeBlocks.replace(block, placeholder);
1243
+ contentWithoutCode = contentWithoutCode.replace(block, placeholder);
1483
1244
  }
1484
- const jsxTags = parseJSXTags(contentWithoutCodeBlocks);
1245
+ const allJSXTags = parseJSXTags(contentWithoutCode);
1246
+ const jsxTags = allJSXTags.filter(({ tag }) => !isTypeScriptGeneric(tag));
1485
1247
  for (const { tag, isClosing, isSelfClosing } of jsxTags) {
1486
1248
  const tagNameMatch = isClosing ? tag.match(/^<\/([A-Z][a-zA-Z0-9]*)>$/) : tag.match(/^<([A-Z][a-zA-Z0-9]*)/);
1487
1249
  const tagName = tagNameMatch?.[1];
@@ -1548,13 +1310,9 @@ export {
1548
1310
  generateFrontmatter,
1549
1311
  stringifyFrontmatter,
1550
1312
  parseFrontmatter,
1551
- createHeadingPattern,
1552
- hasComponent,
1553
- extractCodeBlocks,
1554
- findEmptyMarkdownLinks,
1555
1313
  generateMDXDocument,
1556
1314
  sanitizeContent,
1557
1315
  sanitizeTextContent,
1558
1316
  validateMDXSyntax
1559
1317
  };
1560
- //# sourceMappingURL=chunk-G5KKGJYO.js.map
1318
+ //# sourceMappingURL=chunk-LOB73H77.js.map