@thallylabs/migrate 0.2.10 → 0.2.12
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 +36 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1406 -173
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { createRequire } from "module";
|
|
17
17
|
import { basename as basename3, dirname as dirname4, extname as extname5, isAbsolute as isAbsolute2, relative as relative5, resolve as resolvePath, sep as sep2 } from "path";
|
|
18
18
|
import { parse as parseYaml4 } from "yaml";
|
|
19
|
-
import * as
|
|
19
|
+
import * as ts3 from "typescript";
|
|
20
20
|
|
|
21
21
|
// src/components.ts
|
|
22
22
|
import { createHash } from "crypto";
|
|
@@ -26,11 +26,25 @@ import postcss from "postcss";
|
|
|
26
26
|
import selectorParser from "postcss-selector-parser";
|
|
27
27
|
import remarkMdx2 from "remark-mdx";
|
|
28
28
|
import remarkParse2 from "remark-parse";
|
|
29
|
-
import
|
|
29
|
+
import ts2 from "typescript";
|
|
30
30
|
import { unified as unified2 } from "unified";
|
|
31
31
|
|
|
32
32
|
// src/frontmatter.ts
|
|
33
33
|
import { parse as parseYaml } from "yaml";
|
|
34
|
+
function salvageFrontmatterLines(matter) {
|
|
35
|
+
const data = {};
|
|
36
|
+
for (const line of matter.split(/\r?\n/)) {
|
|
37
|
+
if (!/^[A-Za-z_][\w-]*:\s?/.test(line)) continue;
|
|
38
|
+
try {
|
|
39
|
+
const parsedLine = parseYaml(line);
|
|
40
|
+
if (parsedLine && typeof parsedLine === "object" && !Array.isArray(parsedLine)) {
|
|
41
|
+
Object.assign(data, parsedLine);
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
34
48
|
function parseFrontmatter(raw) {
|
|
35
49
|
const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
36
50
|
const opening = /^---([^\r\n]*)\r?\n/.exec(source);
|
|
@@ -45,11 +59,19 @@ function parseFrontmatter(raw) {
|
|
|
45
59
|
if (matter.trim() === "" || !["", "yaml", "yml"].includes(language)) {
|
|
46
60
|
return { content, data: {} };
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
try {
|
|
63
|
+
const parsed = parseYaml(matter);
|
|
64
|
+
return {
|
|
65
|
+
content,
|
|
66
|
+
data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
|
|
67
|
+
};
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return {
|
|
70
|
+
content,
|
|
71
|
+
data: salvageFrontmatterLines(matter),
|
|
72
|
+
error: err instanceof Error ? err.message : String(err)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
// src/mdx.ts
|
|
@@ -120,7 +142,15 @@ var THALLY_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
120
142
|
"Visibility",
|
|
121
143
|
"Human",
|
|
122
144
|
"Agent",
|
|
123
|
-
"BannerPreview"
|
|
145
|
+
"BannerPreview",
|
|
146
|
+
"Banner",
|
|
147
|
+
"CodeBlock",
|
|
148
|
+
"Column",
|
|
149
|
+
"FileTree",
|
|
150
|
+
"LiteYouTubeEmbed",
|
|
151
|
+
"MDX",
|
|
152
|
+
"Video",
|
|
153
|
+
"YouTube"
|
|
124
154
|
]);
|
|
125
155
|
function isThallyBuiltinComponent(name) {
|
|
126
156
|
return THALLY_BUILTIN_COMPONENTS.has(name) || THALLY_BUILTIN_COMPONENTS.has(name.split(".")[0]);
|
|
@@ -233,8 +263,9 @@ function unwrapDocusaurusMdxCodeBlocks(body) {
|
|
|
233
263
|
}
|
|
234
264
|
return isMdxCodeBlock ? body : output.join("\n");
|
|
235
265
|
}
|
|
236
|
-
function normalizeExplicitHeadingIds(
|
|
237
|
-
|
|
266
|
+
function normalizeExplicitHeadingIds(raw) {
|
|
267
|
+
const { front, body } = splitFrontmatterBlock(raw);
|
|
268
|
+
return front + replaceOutsideCode(body, (segment) => segment.split("\n").map((line) => {
|
|
238
269
|
if (!/^[ \t]{0,3}#{1,6}[ \t]+/.test(line)) return line;
|
|
239
270
|
const trimmed = line.trimEnd();
|
|
240
271
|
if (trimmed.endsWith("}")) {
|
|
@@ -276,6 +307,7 @@ function removeGlobalDocusaurusImports(body) {
|
|
|
276
307
|
}
|
|
277
308
|
var JS_LITERAL_KEYWORDS = /* @__PURE__ */ new Set(["true", "false", "null", "undefined"]);
|
|
278
309
|
var BARE_IDENTIFIER_PATH = /^[\p{ID_Start}$_][\p{ID_Continue}$]*(?:\.[\p{ID_Start}$_][\p{ID_Continue}$]*)*$/u;
|
|
310
|
+
var BARE_OBJECT_SHORTHAND = /^\{\s*([\p{ID_Start}$_][\p{ID_Continue}$]*)\s*\}$/u;
|
|
279
311
|
var SAFE_BUILTIN_ROOTS = /* @__PURE__ */ new Set([
|
|
280
312
|
"Math",
|
|
281
313
|
"JSON",
|
|
@@ -500,12 +532,13 @@ function escapeFernLiteralBraces(raw) {
|
|
|
500
532
|
const end = node.position?.end.offset;
|
|
501
533
|
const root2 = value.split(".")[0];
|
|
502
534
|
const safeBuiltinPath = value.includes(".") && SAFE_BUILTIN_ROOTS.has(root2);
|
|
503
|
-
const
|
|
535
|
+
const isBareIdentifier = BARE_IDENTIFIER_PATH.test(value) && !JS_LITERAL_KEYWORDS.has(value) && !declared.has(root2) && !safeBuiltinPath;
|
|
536
|
+
const shorthandMatch = value.match(BARE_OBJECT_SHORTHAND);
|
|
537
|
+
const isDoubleBraceTemplate = shorthandMatch !== null && !declared.has(shorthandMatch[1]);
|
|
504
538
|
const isIssueList = /^[A-Za-z][A-Za-z0-9]*-\d+(?:\s*,\s*[A-Za-z][A-Za-z0-9]*-\d+)*$/.test(value);
|
|
505
|
-
if ((
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
edits.push({ start, end, value: `\\{${value}\\}` });
|
|
539
|
+
if ((isBareIdentifier || isDoubleBraceTemplate || isIssueList) && start !== void 0 && end !== void 0) {
|
|
540
|
+
const rawSpan = body.slice(start, end);
|
|
541
|
+
edits.push({ start, end, value: rawSpan.replace(/\\/g, "\\\\").replace(/[{}]/g, "\\$&") });
|
|
509
542
|
}
|
|
510
543
|
return;
|
|
511
544
|
}
|
|
@@ -1069,12 +1102,117 @@ function normalizeMintlifyParameterAnchors(body) {
|
|
|
1069
1102
|
(match, _tag, _before, doubleQuoted, singleQuoted) => `${anchorFor(doubleQuoted ?? singleQuoted)}${match}`
|
|
1070
1103
|
));
|
|
1071
1104
|
}
|
|
1105
|
+
function matchFenceLine(line) {
|
|
1106
|
+
const match = line.match(/^(\s*)(`{3,}|~{3,})(.*)$/);
|
|
1107
|
+
if (!match) return null;
|
|
1108
|
+
return { index: -1, indent: match[1], char: match[2][0], length: match[2].length, info: match[3].trim(), rest: match[3] };
|
|
1109
|
+
}
|
|
1110
|
+
function normalizeNestedCodeFences(body) {
|
|
1111
|
+
const lines = body.split("\n");
|
|
1112
|
+
const fences = lines.map((line, index) => {
|
|
1113
|
+
const fence = matchFenceLine(line);
|
|
1114
|
+
return fence ? { ...fence, index } : null;
|
|
1115
|
+
}).filter((fence) => fence !== null);
|
|
1116
|
+
if (fences.length === 0) return body;
|
|
1117
|
+
const stack = [];
|
|
1118
|
+
const roots = [];
|
|
1119
|
+
for (const fence of fences) {
|
|
1120
|
+
const top = stack.at(-1);
|
|
1121
|
+
if (top && fence.info === "" && fence.char === top.open.char && fence.length >= top.open.length) {
|
|
1122
|
+
stack.pop();
|
|
1123
|
+
const block = { open: top.open, close: fence, children: top.children };
|
|
1124
|
+
const parent = stack.at(-1);
|
|
1125
|
+
if (parent) parent.children.push(block);
|
|
1126
|
+
else roots.push(block);
|
|
1127
|
+
} else {
|
|
1128
|
+
stack.push({ open: fence, children: [] });
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
if (stack.length > 0) return body;
|
|
1132
|
+
const widenedLengths = /* @__PURE__ */ new Map();
|
|
1133
|
+
function requiredLength(block) {
|
|
1134
|
+
const deepestChild = Math.max(0, ...block.children.map(requiredLength));
|
|
1135
|
+
const length = Math.max(block.open.length, deepestChild > 0 ? deepestChild + 1 : 0);
|
|
1136
|
+
widenedLengths.set(block.open.index, length);
|
|
1137
|
+
widenedLengths.set(block.close.index, length);
|
|
1138
|
+
return length;
|
|
1139
|
+
}
|
|
1140
|
+
roots.forEach(requiredLength);
|
|
1141
|
+
return lines.map((line, index) => {
|
|
1142
|
+
const targetLength = widenedLengths.get(index);
|
|
1143
|
+
const fence = targetLength ? matchFenceLine(line) : null;
|
|
1144
|
+
if (!targetLength || !fence || targetLength <= fence.length) return line;
|
|
1145
|
+
return `${fence.indent}${fence.char.repeat(targetLength)}${fence.rest}`;
|
|
1146
|
+
}).join("\n");
|
|
1147
|
+
}
|
|
1148
|
+
function maskCodeRegions(body) {
|
|
1149
|
+
let inFence = false;
|
|
1150
|
+
return body.split("\n").map((line) => {
|
|
1151
|
+
if (/^\s*(`{3,}|~{3,})/.test(line)) {
|
|
1152
|
+
inFence = !inFence;
|
|
1153
|
+
return " ".repeat(line.length);
|
|
1154
|
+
}
|
|
1155
|
+
if (inFence) return " ".repeat(line.length);
|
|
1156
|
+
return line.replace(/`[^`]*`/g, (span) => " ".repeat(span.length));
|
|
1157
|
+
}).join("\n");
|
|
1158
|
+
}
|
|
1159
|
+
function isKnownComponentName(name, body) {
|
|
1160
|
+
if (isThallyBuiltinComponent(name)) return true;
|
|
1161
|
+
if (new RegExp(`^\\s*export\\s+(?:const|function|default\\s+function)\\s+${name}\\b`, "m").test(body)) return true;
|
|
1162
|
+
if (new RegExp(`^\\s*import\\s+(?:\\{[^}]*\\b${name}\\b[^}]*\\}|${name})\\s+from\\s+['"][^'"]+['"]`, "m").test(body)) return true;
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
function escapeOrphanCapitalizedTags(body) {
|
|
1166
|
+
const masked = maskCodeRegions(body);
|
|
1167
|
+
const tagPattern = /<(\/?)([A-Z][A-Za-z0-9]*)(?:\s+[^<>]*?)?(\/?)>/g;
|
|
1168
|
+
const opens = /* @__PURE__ */ new Map();
|
|
1169
|
+
const closedOrSelfClosed = /* @__PURE__ */ new Set();
|
|
1170
|
+
for (const match of masked.matchAll(tagPattern)) {
|
|
1171
|
+
const [full, closing, name, selfClosing] = match;
|
|
1172
|
+
if (closing || selfClosing) {
|
|
1173
|
+
closedOrSelfClosed.add(name);
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
const start = match.index;
|
|
1177
|
+
const list = opens.get(name) ?? [];
|
|
1178
|
+
list.push({ start, end: start + full.length });
|
|
1179
|
+
opens.set(name, list);
|
|
1180
|
+
}
|
|
1181
|
+
const edits = [];
|
|
1182
|
+
for (const [name, ranges] of opens) {
|
|
1183
|
+
if (closedOrSelfClosed.has(name) || isKnownComponentName(name, body)) continue;
|
|
1184
|
+
edits.push(...ranges);
|
|
1185
|
+
}
|
|
1186
|
+
if (edits.length === 0) return body;
|
|
1187
|
+
try {
|
|
1188
|
+
compileSync(body, { outputFormat: "program" });
|
|
1189
|
+
return body;
|
|
1190
|
+
} catch {
|
|
1191
|
+
}
|
|
1192
|
+
edits.sort((a, b) => a.start - b.start);
|
|
1193
|
+
let result = "";
|
|
1194
|
+
let cursor = 0;
|
|
1195
|
+
for (const edit of edits) {
|
|
1196
|
+
result += body.slice(cursor, edit.start);
|
|
1197
|
+
result += body.slice(edit.start, edit.end).replace(/^</, "<").replace(/>$/, ">");
|
|
1198
|
+
cursor = edit.end;
|
|
1199
|
+
}
|
|
1200
|
+
return result + body.slice(cursor);
|
|
1201
|
+
}
|
|
1202
|
+
var REACT_HOOK_NAMES = ["useState", "useEffect", "useRef", "useCallback", "useMemo", "useContext", "useReducer"];
|
|
1203
|
+
function injectReactHookImports(body) {
|
|
1204
|
+
const masked = maskCodeRegions(body);
|
|
1205
|
+
const used = REACT_HOOK_NAMES.filter((hook) => new RegExp(`\\b${hook}\\s*\\(`).test(masked) && !new RegExp(`import\\s*\\{[^}]*\\b${hook}\\b[^}]*\\}\\s*from\\s*['"]react['"]`).test(body));
|
|
1206
|
+
return used.length > 0 ? `import { ${used.join(", ")} } from 'react'
|
|
1207
|
+
|
|
1208
|
+
${body}` : body;
|
|
1209
|
+
}
|
|
1072
1210
|
function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
1073
1211
|
const runFern = platform === "fern";
|
|
1074
1212
|
const runMintlify = platform === "mintlify";
|
|
1075
1213
|
const runDocusaurus = platform === "docusaurus";
|
|
1076
1214
|
const sourceBody = runDocusaurus && unwrapMdxCodeBlocks ? unwrapDocusaurusMdxCodeBlocks(body) : body;
|
|
1077
|
-
let rewritten = normalizeDocusaurusAdmonitions(normalizeExplicitHeadingIds(runFern ? normalizeFernFileTrees(normalizeFernCallouts(sourceBody)) : sourceBody));
|
|
1215
|
+
let rewritten = normalizeDocusaurusAdmonitions(normalizeExplicitHeadingIds(runFern ? normalizeFernFileTrees(normalizeFernCallouts(normalizeNestedCodeFences(sourceBody))) : normalizeNestedCodeFences(sourceBody)));
|
|
1078
1216
|
if (runDocusaurus) {
|
|
1079
1217
|
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
1080
1218
|
/:([+\w-]+):/g,
|
|
@@ -1108,7 +1246,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
1108
1246
|
return result;
|
|
1109
1247
|
});
|
|
1110
1248
|
if (runFern) rewritten = unwrapFernCodeBlockTabs(rewritten);
|
|
1111
|
-
const normalized = provideScopedGlobalReferences(rewritten);
|
|
1249
|
+
const normalized = escapeOrphanCapitalizedTags(provideScopedGlobalReferences(rewritten));
|
|
1112
1250
|
if (runDocusaurus && sourceBody !== body) {
|
|
1113
1251
|
try {
|
|
1114
1252
|
compileSync(normalized, { outputFormat: "program" });
|
|
@@ -1126,7 +1264,7 @@ function parseMarkdownPage(input) {
|
|
|
1126
1264
|
...input.locale ? { locale: input.locale } : {}
|
|
1127
1265
|
};
|
|
1128
1266
|
const identity = input.resolveIdentity?.(parsed.data, fallbackIdentity) ?? fallbackIdentity;
|
|
1129
|
-
let body = normalizeMdx(parsed.content, input.platform).trim();
|
|
1267
|
+
let body = injectReactHookImports(normalizeMdx(parsed.content, input.platform)).trim();
|
|
1130
1268
|
const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
|
|
1131
1269
|
const heading = leadingPageHeading(body);
|
|
1132
1270
|
const title = typeof parsed.data.title === "string" && parsed.data.title.trim() ? parsed.data.title.trim() : heading?.title ?? titleFromId(identity.navigationId);
|
|
@@ -1153,7 +1291,8 @@ function parseMarkdownPage(input) {
|
|
|
1153
1291
|
noindex: parsed.data.noindex === true || parsed.data.noindex === "true" ? true : void 0,
|
|
1154
1292
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
1155
1293
|
body,
|
|
1156
|
-
source: input.source
|
|
1294
|
+
source: input.source,
|
|
1295
|
+
...parsed.error ? { frontmatterError: parsed.error } : {}
|
|
1157
1296
|
};
|
|
1158
1297
|
}
|
|
1159
1298
|
|
|
@@ -1206,6 +1345,11 @@ function mintlifyLocalizedReference(value, locale, defaultPageIds = /* @__PURE__
|
|
|
1206
1345
|
const candidateId = pageIdFromReference(candidate, true);
|
|
1207
1346
|
if (candidateId && defaultPageIds.has(candidateId)) return candidate;
|
|
1208
1347
|
}
|
|
1348
|
+
const segments = reference.split("/");
|
|
1349
|
+
const localeIndex = segments.findIndex((segment) => segment.toLowerCase() === locale.toLowerCase());
|
|
1350
|
+
if (localeIndex > 0) {
|
|
1351
|
+
return [...segments.slice(0, localeIndex), ...segments.slice(localeIndex + 1)].join("/");
|
|
1352
|
+
}
|
|
1209
1353
|
return value;
|
|
1210
1354
|
}
|
|
1211
1355
|
function resolveWithin(root, candidate) {
|
|
@@ -1230,6 +1374,543 @@ function normalizeAssetPath(value) {
|
|
|
1230
1374
|
return segments.join("/");
|
|
1231
1375
|
}
|
|
1232
1376
|
|
|
1377
|
+
// src/inline-extraction.ts
|
|
1378
|
+
import ts from "typescript";
|
|
1379
|
+
var SERVER_ONLY_IDENTIFIERS = /* @__PURE__ */ new Set(["process", "require", "module", "exports", "__dirname", "__filename", "Buffer"]);
|
|
1380
|
+
var BROWSER_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
1381
|
+
"window",
|
|
1382
|
+
"document",
|
|
1383
|
+
"localStorage",
|
|
1384
|
+
"sessionStorage",
|
|
1385
|
+
"navigator",
|
|
1386
|
+
"location",
|
|
1387
|
+
"history",
|
|
1388
|
+
"IntersectionObserver",
|
|
1389
|
+
"ResizeObserver",
|
|
1390
|
+
"MutationObserver",
|
|
1391
|
+
"matchMedia",
|
|
1392
|
+
"requestAnimationFrame",
|
|
1393
|
+
"cancelAnimationFrame",
|
|
1394
|
+
"alert",
|
|
1395
|
+
"confirm",
|
|
1396
|
+
"prompt",
|
|
1397
|
+
"HTMLElement",
|
|
1398
|
+
"Element",
|
|
1399
|
+
"Node",
|
|
1400
|
+
"Event",
|
|
1401
|
+
"KeyboardEvent",
|
|
1402
|
+
"MouseEvent",
|
|
1403
|
+
"CustomEvent",
|
|
1404
|
+
"FormData",
|
|
1405
|
+
"File",
|
|
1406
|
+
"FileReader",
|
|
1407
|
+
"Image",
|
|
1408
|
+
"Audio",
|
|
1409
|
+
"XMLHttpRequest",
|
|
1410
|
+
"WebSocket",
|
|
1411
|
+
"getComputedStyle",
|
|
1412
|
+
"innerWidth",
|
|
1413
|
+
"innerHeight",
|
|
1414
|
+
"scrollTo"
|
|
1415
|
+
]);
|
|
1416
|
+
var JS_GLOBALS = /* @__PURE__ */ new Set([
|
|
1417
|
+
"undefined",
|
|
1418
|
+
"NaN",
|
|
1419
|
+
"Infinity",
|
|
1420
|
+
"globalThis",
|
|
1421
|
+
"arguments",
|
|
1422
|
+
"Object",
|
|
1423
|
+
"Function",
|
|
1424
|
+
"Array",
|
|
1425
|
+
"Number",
|
|
1426
|
+
"Boolean",
|
|
1427
|
+
"String",
|
|
1428
|
+
"Symbol",
|
|
1429
|
+
"BigInt",
|
|
1430
|
+
"Date",
|
|
1431
|
+
"RegExp",
|
|
1432
|
+
"Error",
|
|
1433
|
+
"AggregateError",
|
|
1434
|
+
"EvalError",
|
|
1435
|
+
"RangeError",
|
|
1436
|
+
"ReferenceError",
|
|
1437
|
+
"SyntaxError",
|
|
1438
|
+
"TypeError",
|
|
1439
|
+
"URIError",
|
|
1440
|
+
"JSON",
|
|
1441
|
+
"Math",
|
|
1442
|
+
"Intl",
|
|
1443
|
+
"Reflect",
|
|
1444
|
+
"Proxy",
|
|
1445
|
+
"Promise",
|
|
1446
|
+
"Map",
|
|
1447
|
+
"Set",
|
|
1448
|
+
"WeakMap",
|
|
1449
|
+
"WeakSet",
|
|
1450
|
+
"WeakRef",
|
|
1451
|
+
"ArrayBuffer",
|
|
1452
|
+
"SharedArrayBuffer",
|
|
1453
|
+
"DataView",
|
|
1454
|
+
"Atomics",
|
|
1455
|
+
"Int8Array",
|
|
1456
|
+
"Uint8Array",
|
|
1457
|
+
"Uint8ClampedArray",
|
|
1458
|
+
"Int16Array",
|
|
1459
|
+
"Uint16Array",
|
|
1460
|
+
"Int32Array",
|
|
1461
|
+
"Uint32Array",
|
|
1462
|
+
"Float32Array",
|
|
1463
|
+
"Float64Array",
|
|
1464
|
+
"BigInt64Array",
|
|
1465
|
+
"BigUint64Array",
|
|
1466
|
+
"parseInt",
|
|
1467
|
+
"parseFloat",
|
|
1468
|
+
"isNaN",
|
|
1469
|
+
"isFinite",
|
|
1470
|
+
"encodeURI",
|
|
1471
|
+
"encodeURIComponent",
|
|
1472
|
+
"decodeURI",
|
|
1473
|
+
"decodeURIComponent",
|
|
1474
|
+
"console",
|
|
1475
|
+
"fetch",
|
|
1476
|
+
"URL",
|
|
1477
|
+
"URLSearchParams",
|
|
1478
|
+
"Headers",
|
|
1479
|
+
"Request",
|
|
1480
|
+
"Response",
|
|
1481
|
+
"AbortController",
|
|
1482
|
+
"AbortSignal",
|
|
1483
|
+
"TextEncoder",
|
|
1484
|
+
"TextDecoder",
|
|
1485
|
+
"atob",
|
|
1486
|
+
"btoa",
|
|
1487
|
+
"structuredClone",
|
|
1488
|
+
"queueMicrotask",
|
|
1489
|
+
"performance",
|
|
1490
|
+
"setTimeout",
|
|
1491
|
+
"clearTimeout",
|
|
1492
|
+
"setInterval",
|
|
1493
|
+
"clearInterval"
|
|
1494
|
+
]);
|
|
1495
|
+
var IMPLICIT_MODULE_NAMES = /* @__PURE__ */ new Set(["React", "MintlifyComponents"]);
|
|
1496
|
+
function parse(source) {
|
|
1497
|
+
return ts.createSourceFile("inline.tsx", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
1498
|
+
}
|
|
1499
|
+
function bindingNames(name, into) {
|
|
1500
|
+
if (ts.isIdentifier(name)) into.add(name.text);
|
|
1501
|
+
else for (const element of name.elements) if (ts.isBindingElement(element)) bindingNames(element.name, into);
|
|
1502
|
+
}
|
|
1503
|
+
function isFunctionLike(node) {
|
|
1504
|
+
return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
|
|
1505
|
+
}
|
|
1506
|
+
function functionScope(fn) {
|
|
1507
|
+
const scope = /* @__PURE__ */ new Set();
|
|
1508
|
+
if ((ts.isFunctionExpression(fn) || ts.isFunctionDeclaration(fn)) && fn.name) scope.add(fn.name.text);
|
|
1509
|
+
for (const parameter of fn.parameters) bindingNames(parameter.name, scope);
|
|
1510
|
+
function collect(node) {
|
|
1511
|
+
if (ts.isVariableDeclaration(node)) bindingNames(node.name, scope);
|
|
1512
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) scope.add(node.name.text);
|
|
1513
|
+
if (isFunctionLike(node)) return;
|
|
1514
|
+
ts.forEachChild(node, collect);
|
|
1515
|
+
}
|
|
1516
|
+
if (fn.body) ts.forEachChild(fn.body, collect);
|
|
1517
|
+
if (fn.body && !ts.isBlock(fn.body)) collect(fn.body);
|
|
1518
|
+
return scope;
|
|
1519
|
+
}
|
|
1520
|
+
function collectRefs(root) {
|
|
1521
|
+
const refs = [];
|
|
1522
|
+
const assigned = [];
|
|
1523
|
+
let awaitOutsideAsync = false;
|
|
1524
|
+
let dynamicImport = false;
|
|
1525
|
+
function bound(name, scopes) {
|
|
1526
|
+
return scopes.some((scope) => scope.has(name));
|
|
1527
|
+
}
|
|
1528
|
+
function push(name, kind, scopes) {
|
|
1529
|
+
if (!bound(name, scopes)) refs.push({ name, kind });
|
|
1530
|
+
}
|
|
1531
|
+
function walkNode(node, scopes, inAsync) {
|
|
1532
|
+
if (ts.isTypeNode(node)) return;
|
|
1533
|
+
if (isFunctionLike(node)) {
|
|
1534
|
+
const nextScopes = [...scopes, functionScope(node)];
|
|
1535
|
+
const async = !!node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword);
|
|
1536
|
+
for (const parameter of node.parameters) {
|
|
1537
|
+
if (!ts.isIdentifier(parameter.name)) walkNode(parameter.name, nextScopes, async);
|
|
1538
|
+
if (parameter.initializer) walkNode(parameter.initializer, nextScopes, async);
|
|
1539
|
+
}
|
|
1540
|
+
if (node.body) walkNode(node.body, nextScopes, async);
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (ts.isAwaitExpression(node) && !inAsync) awaitOutsideAsync = true;
|
|
1544
|
+
const target = ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts.SyntaxKind.LastAssignment ? node.left : (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken) ? node.operand : void 0;
|
|
1545
|
+
if (target) {
|
|
1546
|
+
let base = target;
|
|
1547
|
+
while (ts.isPropertyAccessExpression(base) || ts.isElementAccessExpression(base)) base = base.expression;
|
|
1548
|
+
if (ts.isIdentifier(base) && !bound(base.text, scopes)) assigned.push(base.text);
|
|
1549
|
+
}
|
|
1550
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) dynamicImport = true;
|
|
1551
|
+
if (ts.isIdentifier(node)) {
|
|
1552
|
+
const parent = node.parent;
|
|
1553
|
+
const kind = ts.isJsxExpression(parent) && parent.parent && ts.isJsxAttribute(parent.parent) ? "prop" : "other";
|
|
1554
|
+
push(node.text, kind, scopes);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
1558
|
+
let tag = node.tagName;
|
|
1559
|
+
while (ts.isPropertyAccessExpression(tag)) tag = tag.expression;
|
|
1560
|
+
if (ts.isIdentifier(tag)) {
|
|
1561
|
+
if (node.tagName !== tag || /^[A-Z]/.test(tag.text)) push(tag.text, node.tagName === tag ? "tag" : "member", scopes);
|
|
1562
|
+
}
|
|
1563
|
+
walkNode(node.attributes, scopes, inAsync);
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
if (ts.isJsxClosingElement(node)) return;
|
|
1567
|
+
if (ts.isJsxAttribute(node)) {
|
|
1568
|
+
if (node.initializer) walkNode(node.initializer, scopes, inAsync);
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
1572
|
+
walkNode(node.expression, scopes, inAsync);
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
if (ts.isQualifiedName(node)) return;
|
|
1576
|
+
if (ts.isPropertyAssignment(node)) {
|
|
1577
|
+
if (ts.isComputedPropertyName(node.name)) walkNode(node.name.expression, scopes, inAsync);
|
|
1578
|
+
walkNode(node.initializer, scopes, inAsync);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (ts.isShorthandPropertyAssignment(node)) {
|
|
1582
|
+
push(node.name.text, "other", scopes);
|
|
1583
|
+
if (node.objectAssignmentInitializer) walkNode(node.objectAssignmentInitializer, scopes, inAsync);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (ts.isBindingElement(node)) {
|
|
1587
|
+
if (node.propertyName && ts.isComputedPropertyName(node.propertyName)) walkNode(node.propertyName.expression, scopes, inAsync);
|
|
1588
|
+
if (node.initializer) walkNode(node.initializer, scopes, inAsync);
|
|
1589
|
+
if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync);
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
if (ts.isVariableDeclaration(node)) {
|
|
1593
|
+
if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync);
|
|
1594
|
+
if (node.initializer) walkNode(node.initializer, scopes, inAsync);
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
|
1598
|
+
node.members.forEach((member) => walkNode(member, scopes, inAsync));
|
|
1599
|
+
for (const clause of node.heritageClauses ?? []) for (const type of clause.types) walkNode(type.expression, scopes, inAsync);
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (ts.isPropertyDeclaration(node)) {
|
|
1603
|
+
if (node.initializer) walkNode(node.initializer, scopes, inAsync);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (ts.isLabeledStatement(node)) {
|
|
1607
|
+
walkNode(node.statement, scopes, inAsync);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
if (ts.isBreakOrContinueStatement(node)) return;
|
|
1611
|
+
ts.forEachChild(node, (child) => walkNode(child, scopes, inAsync));
|
|
1612
|
+
}
|
|
1613
|
+
walkNode(root, [], false);
|
|
1614
|
+
return { refs, assigned, awaitOutsideAsync, dynamicImport };
|
|
1615
|
+
}
|
|
1616
|
+
function declaredNames(statement) {
|
|
1617
|
+
const isDefault = !!(ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) || ts.isExportAssignment(statement);
|
|
1618
|
+
const names = /* @__PURE__ */ new Set();
|
|
1619
|
+
let componentLike = false;
|
|
1620
|
+
if (ts.isVariableStatement(statement)) {
|
|
1621
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1622
|
+
bindingNames(declaration.name, names);
|
|
1623
|
+
const initializer = declaration.initializer && unwrap(declaration.initializer);
|
|
1624
|
+
if (ts.isIdentifier(declaration.name) && /^[A-Z]/.test(declaration.name.text) && initializer && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer) || ts.isClassExpression(initializer))) {
|
|
1625
|
+
componentLike = true;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
} else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) {
|
|
1629
|
+
names.add(statement.name.text);
|
|
1630
|
+
componentLike = /^[A-Z]/.test(statement.name.text);
|
|
1631
|
+
}
|
|
1632
|
+
return { names: [...names], componentLike, defaultExport: isDefault };
|
|
1633
|
+
}
|
|
1634
|
+
function unwrap(expression) {
|
|
1635
|
+
let current = expression;
|
|
1636
|
+
while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) current = current.expression;
|
|
1637
|
+
return current;
|
|
1638
|
+
}
|
|
1639
|
+
function analyzeUnit(index, source, reactGlobals) {
|
|
1640
|
+
const sf = parse(source);
|
|
1641
|
+
const statement = sf.statements[0];
|
|
1642
|
+
const blockers = [];
|
|
1643
|
+
if (!statement || sf.statements.length !== 1) {
|
|
1644
|
+
return { index, source, names: [], refs: [], assigned: [], hooks: false, clientOnly: false, componentLike: false, blockers: ["is not a single declaration"] };
|
|
1645
|
+
}
|
|
1646
|
+
const { names, componentLike, defaultExport } = declaredNames(statement);
|
|
1647
|
+
if (defaultExport) blockers.push("is a default export");
|
|
1648
|
+
const { refs, assigned, awaitOutsideAsync, dynamicImport } = collectRefs(statement);
|
|
1649
|
+
if (awaitOutsideAsync) blockers.push("uses await outside an async function");
|
|
1650
|
+
if (dynamicImport) blockers.push("uses a dynamic import()");
|
|
1651
|
+
const initializer = ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1 ? statement.declarationList.declarations[0].initializer && unwrap(statement.declarationList.declarations[0].initializer) : statement;
|
|
1652
|
+
if (initializer && isFunctionLike(initializer) && initializer.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
1653
|
+
blockers.push("is an async (server) component");
|
|
1654
|
+
}
|
|
1655
|
+
for (const { name } of refs) {
|
|
1656
|
+
if (SERVER_ONLY_IDENTIFIERS.has(name)) blockers.push(`uses the server-only \`${name}\``);
|
|
1657
|
+
}
|
|
1658
|
+
let handlers = false;
|
|
1659
|
+
function scanHandlers(node) {
|
|
1660
|
+
if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && /^on[A-Z]/.test(node.name.text)) handlers = true;
|
|
1661
|
+
ts.forEachChild(node, scanHandlers);
|
|
1662
|
+
}
|
|
1663
|
+
scanHandlers(statement);
|
|
1664
|
+
return {
|
|
1665
|
+
index,
|
|
1666
|
+
source,
|
|
1667
|
+
names,
|
|
1668
|
+
refs,
|
|
1669
|
+
assigned,
|
|
1670
|
+
hooks: refs.some(({ name }) => reactGlobals.has(name)) || /\bReact\.(?:use[A-Z]\w*|createContext|forwardRef|memo)\b/.test(source),
|
|
1671
|
+
clientOnly: handlers || refs.some(({ name }) => BROWSER_IDENTIFIERS.has(name)),
|
|
1672
|
+
componentLike,
|
|
1673
|
+
blockers: [...new Set(blockers)]
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
function helperNames(source) {
|
|
1677
|
+
const statement = parse(source).statements[0];
|
|
1678
|
+
return statement ? declaredNames(statement).names : [];
|
|
1679
|
+
}
|
|
1680
|
+
function importLocals(statementText) {
|
|
1681
|
+
const sf = parse(statementText);
|
|
1682
|
+
const locals = [];
|
|
1683
|
+
for (const statement of sf.statements) {
|
|
1684
|
+
if (!ts.isImportDeclaration(statement) || !statement.importClause) continue;
|
|
1685
|
+
const clause = statement.importClause;
|
|
1686
|
+
if (clause.name) locals.push(clause.name.text);
|
|
1687
|
+
const named = clause.namedBindings;
|
|
1688
|
+
if (named && ts.isNamespaceImport(named)) locals.push(named.name.text);
|
|
1689
|
+
if (named && ts.isNamedImports(named)) for (const element of named.elements) locals.push(element.name.text);
|
|
1690
|
+
}
|
|
1691
|
+
return locals;
|
|
1692
|
+
}
|
|
1693
|
+
function expressionRefs(expression) {
|
|
1694
|
+
const sf = parse(`const __expression = (${expression})`);
|
|
1695
|
+
const statement = sf.statements[0];
|
|
1696
|
+
return statement ? collectRefs(statement).refs : [];
|
|
1697
|
+
}
|
|
1698
|
+
function bodyReferences(tree) {
|
|
1699
|
+
const mdastTags = /* @__PURE__ */ new Set();
|
|
1700
|
+
const refs = [];
|
|
1701
|
+
function visit(node) {
|
|
1702
|
+
if (node.type === "mdxjsEsm") return;
|
|
1703
|
+
if (["mdxJsxFlowElement", "mdxJsxTextElement"].includes(node.type) && node.name) {
|
|
1704
|
+
const root = node.name.split(".")[0];
|
|
1705
|
+
if (node.name.includes(".")) refs.push({ name: root, kind: "member" });
|
|
1706
|
+
else mdastTags.add(root);
|
|
1707
|
+
}
|
|
1708
|
+
if (["mdxFlowExpression", "mdxTextExpression"].includes(node.type) && node.value) refs.push(...expressionRefs(node.value));
|
|
1709
|
+
for (const attribute of node.attributes ?? []) {
|
|
1710
|
+
const value = attribute.value;
|
|
1711
|
+
if (attribute.type === "mdxJsxExpressionAttribute" && typeof value === "string") {
|
|
1712
|
+
refs.push(...expressionRefs(`{${value}}`));
|
|
1713
|
+
} else if (value && typeof value === "object" && typeof value.value === "string") {
|
|
1714
|
+
refs.push(...expressionRefs(value.value).map((ref, _index, all) => all.length === 1 && value.value.trim() === ref.name ? { name: ref.name, kind: "prop" } : ref));
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
for (const child of node.children ?? []) visit(child);
|
|
1718
|
+
}
|
|
1719
|
+
visit(tree);
|
|
1720
|
+
return { mdastTags, refs };
|
|
1721
|
+
}
|
|
1722
|
+
function planInlineExtraction(input) {
|
|
1723
|
+
const units = input.declarations.map((declaration, index) => analyzeUnit(index, declaration.source, input.reactGlobals));
|
|
1724
|
+
const owner = /* @__PURE__ */ new Map();
|
|
1725
|
+
for (const unit of units) for (const name of unit.names) owner.set(name, unit.index);
|
|
1726
|
+
for (let changed = true; changed; ) {
|
|
1727
|
+
changed = false;
|
|
1728
|
+
for (const unit of units) {
|
|
1729
|
+
if (unit.hooks) continue;
|
|
1730
|
+
if (unit.refs.some(({ name }) => /^use[A-Z]/.test(name) && units[owner.get(name) ?? -1]?.hooks)) {
|
|
1731
|
+
unit.hooks = true;
|
|
1732
|
+
changed = true;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
const importByLocal = /* @__PURE__ */ new Map();
|
|
1737
|
+
for (const statement of input.copyableImports) for (const local of importLocals(statement)) importByLocal.set(local, statement);
|
|
1738
|
+
const helperByName = /* @__PURE__ */ new Map();
|
|
1739
|
+
for (const helper of input.helperDeclarations) {
|
|
1740
|
+
for (const name of helperNames(helper)) helperByName.set(name, helper);
|
|
1741
|
+
}
|
|
1742
|
+
const body = bodyReferences(input.tree);
|
|
1743
|
+
const dependencies = (unit) => {
|
|
1744
|
+
const found = /* @__PURE__ */ new Set();
|
|
1745
|
+
for (const { name } of unit.refs) {
|
|
1746
|
+
const target = owner.get(name);
|
|
1747
|
+
if (target !== void 0 && target !== unit.index) found.add(target);
|
|
1748
|
+
}
|
|
1749
|
+
return found;
|
|
1750
|
+
};
|
|
1751
|
+
const closureOf = (rootIndex) => {
|
|
1752
|
+
const seen = /* @__PURE__ */ new Set([rootIndex]);
|
|
1753
|
+
const queue = [rootIndex];
|
|
1754
|
+
while (queue.length) for (const next of dependencies(units[queue.pop()])) {
|
|
1755
|
+
if (!seen.has(next)) {
|
|
1756
|
+
seen.add(next);
|
|
1757
|
+
queue.push(next);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return seen;
|
|
1761
|
+
};
|
|
1762
|
+
const intrinsicBlocker = (unit) => {
|
|
1763
|
+
if (unit.blockers.length > 0) return `\`${unit.names[0] ?? "a dependency"}\` ${unit.blockers[0]}`;
|
|
1764
|
+
for (const { name, kind } of unit.refs) {
|
|
1765
|
+
if (owner.has(name) || importByLocal.has(name) || helperByName.has(name)) continue;
|
|
1766
|
+
if (input.unavailableImports.has(name)) {
|
|
1767
|
+
return `\`${unit.names[0]}\` uses \`${name}\`, imported from ${input.unavailableImports.get(name)}, which is not available in the migrated project`;
|
|
1768
|
+
}
|
|
1769
|
+
if (input.reactGlobals.has(name) || IMPLICIT_MODULE_NAMES.has(name) || JS_GLOBALS.has(name) || BROWSER_IDENTIFIERS.has(name)) continue;
|
|
1770
|
+
if ((kind === "tag" || kind === "member") && isThallyBuiltinComponent(name)) continue;
|
|
1771
|
+
return `\`${unit.names[0]}\` uses \`${name}\`, which is not declared or imported on the page`;
|
|
1772
|
+
}
|
|
1773
|
+
return void 0;
|
|
1774
|
+
};
|
|
1775
|
+
const roots = units.filter((unit) => unit.hooks && unit.names.some((name) => /^[A-Z]/.test(name))).map((unit) => unit.index);
|
|
1776
|
+
const blocked = [];
|
|
1777
|
+
const active = new Set(roots);
|
|
1778
|
+
const block = (rootIndex, reason) => {
|
|
1779
|
+
active.delete(rootIndex);
|
|
1780
|
+
blocked.push({ name: units[rootIndex].names.find((name) => /^[A-Z]/.test(name)) ?? units[rootIndex].names[0], reason });
|
|
1781
|
+
};
|
|
1782
|
+
for (const rootIndex of roots) {
|
|
1783
|
+
for (const member of closureOf(rootIndex)) {
|
|
1784
|
+
const reason = intrinsicBlocker(units[member]);
|
|
1785
|
+
if (reason) {
|
|
1786
|
+
block(rootIndex, reason);
|
|
1787
|
+
break;
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
for (let round = 0; round <= units.length + 1; round += 1) {
|
|
1792
|
+
const inModule = /* @__PURE__ */ new Set();
|
|
1793
|
+
for (const rootIndex of active) for (const member of closureOf(rootIndex)) inModule.add(member);
|
|
1794
|
+
const clientSide = /* @__PURE__ */ new Set();
|
|
1795
|
+
for (const index of inModule) if (units[index].hooks || units[index].clientOnly) clientSide.add(index);
|
|
1796
|
+
for (let changed = true; changed; ) {
|
|
1797
|
+
changed = false;
|
|
1798
|
+
for (const index of inModule) {
|
|
1799
|
+
if (clientSide.has(index)) continue;
|
|
1800
|
+
const unit = units[index];
|
|
1801
|
+
const callsClient = unit.refs.some(({ name, kind }) => {
|
|
1802
|
+
const target = owner.get(name);
|
|
1803
|
+
return target !== void 0 && clientSide.has(target) && kind !== "tag" && kind !== "prop";
|
|
1804
|
+
});
|
|
1805
|
+
if (callsClient) {
|
|
1806
|
+
clientSide.add(index);
|
|
1807
|
+
changed = true;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
for (const rootIndex of active) clientSide.add(rootIndex);
|
|
1812
|
+
const nonModuleRefs = [...body.refs];
|
|
1813
|
+
for (const unit of units) if (!inModule.has(unit.index)) nonModuleRefs.push(...unit.refs);
|
|
1814
|
+
const referencedByPage = (index) => units[index].names.some((name) => body.mdastTags.has(name) || nonModuleRefs.some((ref) => ref.name === name));
|
|
1815
|
+
const kept = /* @__PURE__ */ new Set();
|
|
1816
|
+
for (const index of inModule) {
|
|
1817
|
+
if (clientSide.has(index)) continue;
|
|
1818
|
+
if (!units[index].componentLike || referencedByPage(index)) kept.add(index);
|
|
1819
|
+
}
|
|
1820
|
+
for (let changed = true; changed; ) {
|
|
1821
|
+
changed = false;
|
|
1822
|
+
for (const index of kept) for (const dependency of dependencies(units[index])) {
|
|
1823
|
+
if (inModule.has(dependency) && !clientSide.has(dependency) && !kept.has(dependency)) {
|
|
1824
|
+
kept.add(dependency);
|
|
1825
|
+
changed = true;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
const moved = new Set([...inModule].filter((index) => !kept.has(index)));
|
|
1830
|
+
const pageRefs = [...nonModuleRefs];
|
|
1831
|
+
for (const index of kept) pageRefs.push(...units[index].refs);
|
|
1832
|
+
const violation = [...moved].find((index) => clientSide.has(index) && units[index].names.some((name) => pageRefs.some((ref) => ref.name === name && ref.kind !== "tag" && ref.kind !== "prop")));
|
|
1833
|
+
if (violation !== void 0) {
|
|
1834
|
+
const names = units[violation].names;
|
|
1835
|
+
const readsIt = (ref) => names.includes(ref.name) && ref.kind !== "tag" && ref.kind !== "prop";
|
|
1836
|
+
const readers = units.filter((unit) => !moved.has(unit.index) && unit.refs.some(readsIt));
|
|
1837
|
+
const promotable = !body.refs.some(readsIt) && readers.every((unit) => unit.componentLike && !active.has(unit.index) && [...closureOf(unit.index)].every((member) => !intrinsicBlocker(units[member])));
|
|
1838
|
+
if (promotable && readers.length > 0) {
|
|
1839
|
+
for (const unit of readers) active.add(unit.index);
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
for (const rootIndex of [...active]) {
|
|
1843
|
+
if (closureOf(rootIndex).has(violation)) {
|
|
1844
|
+
block(rootIndex, `\`${names[0]}\` is also read or called by page code that stays on the server`);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1849
|
+
const reachable = new Set([...kept].filter(referencedByPage));
|
|
1850
|
+
for (let changed = true; changed; ) {
|
|
1851
|
+
changed = false;
|
|
1852
|
+
for (const index of [...reachable]) for (const dependency of dependencies(units[index])) {
|
|
1853
|
+
if (kept.has(dependency) && !reachable.has(dependency)) {
|
|
1854
|
+
reachable.add(dependency);
|
|
1855
|
+
changed = true;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
const mutated = [...reachable].find((index) => units.some((unit) => unit.assigned.some((name) => units[index].names.includes(name))));
|
|
1860
|
+
if (mutated !== void 0) {
|
|
1861
|
+
for (const rootIndex of [...active]) {
|
|
1862
|
+
if (closureOf(rootIndex).has(mutated)) {
|
|
1863
|
+
block(rootIndex, `\`${units[mutated].names[0]}\` is mutable state that page code also uses, so a copy in the client module would diverge`);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
const movedNames = [...moved].flatMap((index) => units[index].names);
|
|
1869
|
+
const registryNames = movedNames.filter((name) => body.mdastTags.has(name));
|
|
1870
|
+
const importNames = movedNames.filter((name) => pageRefs.some((ref) => ref.name === name));
|
|
1871
|
+
const moduleMembers = [...inModule].sort((a, b) => a - b);
|
|
1872
|
+
const moduleNames = new Set(moduleMembers.flatMap((index) => units[index].names));
|
|
1873
|
+
const usedImports = [];
|
|
1874
|
+
const usedHelpers = [];
|
|
1875
|
+
for (const index of moduleMembers) {
|
|
1876
|
+
for (const { name } of units[index].refs) {
|
|
1877
|
+
const statement = importByLocal.get(name);
|
|
1878
|
+
if (statement && !usedImports.includes(statement)) usedImports.push(statement);
|
|
1879
|
+
const helper = helperByName.get(name);
|
|
1880
|
+
if (helper && !usedHelpers.includes(helper)) usedHelpers.push(helper);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
for (const statement of usedImports) for (const local of importLocals(statement)) moduleNames.add(local);
|
|
1884
|
+
for (const helper of usedHelpers) for (const name of helperNames(helper)) moduleNames.add(name);
|
|
1885
|
+
const pageReferences = new Set(pageRefs.map((ref) => ref.name));
|
|
1886
|
+
for (const tag of body.mdastTags) pageReferences.add(tag);
|
|
1887
|
+
return {
|
|
1888
|
+
blocked,
|
|
1889
|
+
moved: [...moved].sort((a, b) => a - b),
|
|
1890
|
+
copied: [...kept].sort((a, b) => a - b),
|
|
1891
|
+
movedNames,
|
|
1892
|
+
moduleImports: usedImports,
|
|
1893
|
+
moduleHelpers: usedHelpers,
|
|
1894
|
+
registryNames: [...new Set(registryNames)],
|
|
1895
|
+
pageImportNames: [...new Set(importNames)],
|
|
1896
|
+
moduleNames,
|
|
1897
|
+
pageReferences
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
return { blocked, moved: [], copied: [], movedNames: [], moduleImports: [], moduleHelpers: [], registryNames: [], pageImportNames: [], moduleNames: /* @__PURE__ */ new Set(), pageReferences: /* @__PURE__ */ new Set() };
|
|
1901
|
+
}
|
|
1902
|
+
function unboundTags(moduleSource, boundNames) {
|
|
1903
|
+
const sf = parse(moduleSource);
|
|
1904
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1905
|
+
for (const statement of sf.statements) {
|
|
1906
|
+
if (ts.isImportDeclaration(statement)) continue;
|
|
1907
|
+
for (const ref of collectRefs(statement).refs) {
|
|
1908
|
+
if ((ref.kind === "tag" || ref.kind === "member") && /^[A-Z]/.test(ref.name) && !boundNames.has(ref.name)) tags.add(ref.name);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
return [...tags].sort();
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1233
1914
|
// src/components.ts
|
|
1234
1915
|
function normalizeIndentedFences(content) {
|
|
1235
1916
|
const lines = content.split("\n");
|
|
@@ -1276,23 +1957,23 @@ function normalizeIndentedFences(content) {
|
|
|
1276
1957
|
}
|
|
1277
1958
|
function staticServerHelper(path, imported, local) {
|
|
1278
1959
|
if (imported === "default") return null;
|
|
1279
|
-
const parsed =
|
|
1960
|
+
const parsed = ts2.createSourceFile(path, readFileSync(path, "utf8"), ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
1280
1961
|
const isLiteral = (node) => {
|
|
1281
|
-
if (
|
|
1282
|
-
if (
|
|
1283
|
-
if ([
|
|
1284
|
-
if (
|
|
1285
|
-
if (
|
|
1286
|
-
if (
|
|
1962
|
+
if (ts2.isParenthesizedExpression(node)) return isLiteral(node.expression);
|
|
1963
|
+
if (ts2.isStringLiteral(node) || ts2.isNoSubstitutionTemplateLiteral(node) || ts2.isNumericLiteral(node)) return true;
|
|
1964
|
+
if ([ts2.SyntaxKind.TrueKeyword, ts2.SyntaxKind.FalseKeyword, ts2.SyntaxKind.NullKeyword].includes(node.kind)) return true;
|
|
1965
|
+
if (ts2.isPrefixUnaryExpression(node)) return [ts2.SyntaxKind.PlusToken, ts2.SyntaxKind.MinusToken].includes(node.operator) && ts2.isNumericLiteral(node.operand);
|
|
1966
|
+
if (ts2.isArrayLiteralExpression(node)) return node.elements.every((element) => ts2.isExpression(element) && isLiteral(element));
|
|
1967
|
+
if (ts2.isObjectLiteralExpression(node)) return node.properties.every((property) => ts2.isPropertyAssignment(property) && (ts2.isIdentifier(property.name) || ts2.isStringLiteral(property.name) || ts2.isNumericLiteral(property.name)) && isLiteral(property.initializer));
|
|
1287
1968
|
return false;
|
|
1288
1969
|
};
|
|
1289
1970
|
for (const statement of parsed.statements) {
|
|
1290
|
-
if (!
|
|
1971
|
+
if (!ts2.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword)) continue;
|
|
1291
1972
|
for (const declaration of statement.declarationList.declarations) {
|
|
1292
|
-
if (!
|
|
1973
|
+
if (!ts2.isIdentifier(declaration.name) || declaration.name.text !== imported || !declaration.initializer || !ts2.isArrowFunction(declaration.initializer)) continue;
|
|
1293
1974
|
const arrow = declaration.initializer;
|
|
1294
1975
|
if (arrow.parameters.length > 0) continue;
|
|
1295
|
-
const result =
|
|
1976
|
+
const result = ts2.isBlock(arrow.body) ? arrow.body.statements.length === 1 && ts2.isReturnStatement(arrow.body.statements[0]) ? arrow.body.statements[0].expression : void 0 : arrow.body;
|
|
1296
1977
|
if (result && isLiteral(result)) return `export const ${local} = () => (${result.getText(parsed)});`;
|
|
1297
1978
|
}
|
|
1298
1979
|
}
|
|
@@ -1516,12 +2197,12 @@ function wrapImpureCssModuleSelectors(css) {
|
|
|
1516
2197
|
return root.toString();
|
|
1517
2198
|
}
|
|
1518
2199
|
function sourceFile(source, filename) {
|
|
1519
|
-
return
|
|
2200
|
+
return ts2.createSourceFile(
|
|
1520
2201
|
filename,
|
|
1521
2202
|
source,
|
|
1522
|
-
|
|
2203
|
+
ts2.ScriptTarget.Latest,
|
|
1523
2204
|
true,
|
|
1524
|
-
/\.[cm]?ts$/.test(filename) ?
|
|
2205
|
+
/\.[cm]?ts$/.test(filename) ? ts2.ScriptKind.TS : ts2.ScriptKind.TSX
|
|
1525
2206
|
);
|
|
1526
2207
|
}
|
|
1527
2208
|
function walk(node, visitor) {
|
|
@@ -1532,24 +2213,24 @@ function implicitReactImports(source) {
|
|
|
1532
2213
|
const bindings = /* @__PURE__ */ new Set();
|
|
1533
2214
|
const references = /* @__PURE__ */ new Set();
|
|
1534
2215
|
function bind(name) {
|
|
1535
|
-
if (
|
|
1536
|
-
else for (const element of name.elements) if (
|
|
2216
|
+
if (ts2.isIdentifier(name)) bindings.add(name.text);
|
|
2217
|
+
else for (const element of name.elements) if (ts2.isBindingElement(element)) bind(element.name);
|
|
1537
2218
|
}
|
|
1538
2219
|
for (const statement of source.statements) {
|
|
1539
|
-
if (
|
|
2220
|
+
if (ts2.isImportDeclaration(statement) && statement.importClause) {
|
|
1540
2221
|
const clause = statement.importClause;
|
|
1541
2222
|
if (clause.name) bindings.add(clause.name.text);
|
|
1542
2223
|
if (clause.namedBindings) {
|
|
1543
|
-
if (
|
|
2224
|
+
if (ts2.isNamespaceImport(clause.namedBindings)) bindings.add(clause.namedBindings.name.text);
|
|
1544
2225
|
else for (const element of clause.namedBindings.elements) bindings.add(element.name.text);
|
|
1545
2226
|
}
|
|
1546
2227
|
}
|
|
1547
|
-
if (
|
|
1548
|
-
if ((
|
|
2228
|
+
if (ts2.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) bind(declaration.name);
|
|
2229
|
+
if ((ts2.isFunctionDeclaration(statement) || ts2.isClassDeclaration(statement)) && statement.name) bindings.add(statement.name.text);
|
|
1549
2230
|
}
|
|
1550
2231
|
function visit(node) {
|
|
1551
|
-
if (
|
|
1552
|
-
|
|
2232
|
+
if (ts2.isIdentifier(node) && !(ts2.isPropertyAccessExpression(node.parent) && node.parent.name === node) && !(ts2.isPropertyAssignment(node.parent) && node.parent.name === node)) references.add(node.text);
|
|
2233
|
+
ts2.forEachChild(node, visit);
|
|
1553
2234
|
}
|
|
1554
2235
|
visit(source);
|
|
1555
2236
|
const hooks = [...REACT_GLOBALS].filter((name) => references.has(name) && !bindings.has(name)).sort();
|
|
@@ -1565,13 +2246,22 @@ function implicitReactImports(source) {
|
|
|
1565
2246
|
usesMintlifyComponents ? mintlifyComponentsShim() : ""
|
|
1566
2247
|
].filter(Boolean).join("\n");
|
|
1567
2248
|
}
|
|
2249
|
+
function resolveInlineBuiltinReferences(source, moduleNames) {
|
|
2250
|
+
const unresolved = unboundTags(source, moduleNames);
|
|
2251
|
+
if (unresolved.length === 0) return source;
|
|
2252
|
+
return [
|
|
2253
|
+
"import { builtinMdxComponents } from '@/components/mdx/builtin-components';",
|
|
2254
|
+
`const { ${unresolved.join(", ")} } = builtinMdxComponents;`,
|
|
2255
|
+
source
|
|
2256
|
+
].join("\n");
|
|
2257
|
+
}
|
|
1568
2258
|
function imports(statement) {
|
|
1569
|
-
if (!
|
|
2259
|
+
if (!ts2.isStringLiteral(statement.moduleSpecifier) || !statement.importClause) return [];
|
|
1570
2260
|
const clause = statement.importClause;
|
|
1571
2261
|
if (clause.isTypeOnly) return [];
|
|
1572
2262
|
const source = statement.moduleSpecifier.text;
|
|
1573
2263
|
const bindings = clause.name ? [{ local: clause.name.text, imported: "default", source }] : [];
|
|
1574
|
-
if (clause.namedBindings &&
|
|
2264
|
+
if (clause.namedBindings && ts2.isNamedImports(clause.namedBindings)) {
|
|
1575
2265
|
for (const element of clause.namedBindings.elements) {
|
|
1576
2266
|
if (!element.isTypeOnly) bindings.push({ local: element.name.text, imported: element.propertyName?.text ?? element.name.text, source });
|
|
1577
2267
|
}
|
|
@@ -1587,6 +2277,7 @@ var CLIENT_BUILTIN_COMPONENT_TAGS = /* @__PURE__ */ new Set([
|
|
|
1587
2277
|
"ParamField",
|
|
1588
2278
|
"Expandable",
|
|
1589
2279
|
"CodeGroup",
|
|
2280
|
+
"CodeBlock",
|
|
1590
2281
|
"Badge",
|
|
1591
2282
|
"Tooltip",
|
|
1592
2283
|
"Tabs",
|
|
@@ -1596,6 +2287,7 @@ var CLIENT_BUILTIN_COMPONENT_TAGS = /* @__PURE__ */ new Set([
|
|
|
1596
2287
|
"InlineRequestExample",
|
|
1597
2288
|
"InlineResponseExample",
|
|
1598
2289
|
"Tree",
|
|
2290
|
+
"FileTree",
|
|
1599
2291
|
"Folder",
|
|
1600
2292
|
"File",
|
|
1601
2293
|
"Mermaid",
|
|
@@ -1617,8 +2309,8 @@ var CLIENT_BUILTIN_COMPONENT_TAGS = /* @__PURE__ */ new Set([
|
|
|
1617
2309
|
function isConfirmedClientBoundaryTag(name) {
|
|
1618
2310
|
return EXTRACTED_CLIENT_COMPONENT_TAG.test(name) || CLIENT_BUILTIN_COMPONENT_TAGS.has(name);
|
|
1619
2311
|
}
|
|
1620
|
-
function functionValuedAttribute(node,
|
|
1621
|
-
const isFunctionValue = (value) =>
|
|
2312
|
+
function functionValuedAttribute(node, declaredNames2) {
|
|
2313
|
+
const isFunctionValue = (value) => declaredNames2.has(value.trim()) || isFunctionInitializer(value.trim(), 0);
|
|
1622
2314
|
for (const attribute of node.attributes ?? []) {
|
|
1623
2315
|
const raw = attribute.value;
|
|
1624
2316
|
const value = raw && typeof raw === "object" ? raw.value : void 0;
|
|
@@ -1631,21 +2323,21 @@ function functionValuedAttribute(node, declaredNames) {
|
|
|
1631
2323
|
}
|
|
1632
2324
|
return void 0;
|
|
1633
2325
|
}
|
|
1634
|
-
function propsTargetExtractedClientComponent(body,
|
|
2326
|
+
function propsTargetExtractedClientComponent(body, declaredNames2) {
|
|
1635
2327
|
const tree = parser.parse(body);
|
|
1636
2328
|
let found = false;
|
|
1637
2329
|
walk(tree, (node) => {
|
|
1638
2330
|
if (found || !node.name || !isConfirmedClientBoundaryTag(node.name)) return;
|
|
1639
|
-
if (functionValuedAttribute(node,
|
|
2331
|
+
if (functionValuedAttribute(node, declaredNames2) !== void 0) found = true;
|
|
1640
2332
|
});
|
|
1641
2333
|
return found;
|
|
1642
2334
|
}
|
|
1643
|
-
function hasAnyFunctionValuedProp(body,
|
|
2335
|
+
function hasAnyFunctionValuedProp(body, declaredNames2) {
|
|
1644
2336
|
const tree = parser.parse(body);
|
|
1645
2337
|
let found = false;
|
|
1646
2338
|
walk(tree, (node) => {
|
|
1647
2339
|
if (found || !node.name) return;
|
|
1648
|
-
if (functionValuedAttribute(node,
|
|
2340
|
+
if (functionValuedAttribute(node, declaredNames2) !== void 0) found = true;
|
|
1649
2341
|
});
|
|
1650
2342
|
return found;
|
|
1651
2343
|
}
|
|
@@ -1653,17 +2345,30 @@ function declarationsReferenceBrowserGlobal(body) {
|
|
|
1653
2345
|
const tree = parser.parse(body);
|
|
1654
2346
|
let found = false;
|
|
1655
2347
|
function inspect(node) {
|
|
1656
|
-
if (
|
|
1657
|
-
|
|
2348
|
+
if (ts2.isIdentifier(node) && (node.text === "document" || node.text === "window")) found = true;
|
|
2349
|
+
ts2.forEachChild(node, inspect);
|
|
1658
2350
|
}
|
|
1659
2351
|
for (const node of tree.children ?? []) {
|
|
1660
2352
|
if (found || node.type !== "mdxjsEsm" || node.value === void 0) continue;
|
|
1661
2353
|
for (const statement of sourceFile(node.value, "inline.tsx").statements) {
|
|
1662
|
-
if (
|
|
2354
|
+
if (ts2.isVariableStatement(statement) || ts2.isFunctionDeclaration(statement)) inspect(statement);
|
|
1663
2355
|
}
|
|
1664
2356
|
}
|
|
1665
2357
|
return found;
|
|
1666
2358
|
}
|
|
2359
|
+
function neutralizeUnresolvableImportsWithoutAst(content, currentFile, warn) {
|
|
2360
|
+
const importPattern = /^import\s+(?:\{\s*(?:default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}|([A-Z][A-Za-z0-9_]*))\s+from\s+(['"])((?:@site\/|\.\.?\/|\/)[^'"]+)\3\s*;?[ \t]*$/gm;
|
|
2361
|
+
const dropped = [];
|
|
2362
|
+
let result = content.replace(importPattern, (_match, namedComponent, defaultComponent, _quote, specifier) => {
|
|
2363
|
+
dropped.push({ name: namedComponent ?? defaultComponent, specifier });
|
|
2364
|
+
return "";
|
|
2365
|
+
});
|
|
2366
|
+
for (const { name, specifier } of dropped) {
|
|
2367
|
+
warn(`Component import ${JSON.stringify(specifier)} could not be resolved because this page has invalid MDX (see the previous warning), so the import and its usage were removed to keep the site building.`, currentFile);
|
|
2368
|
+
result = result.replace(new RegExp(`<${name}(?:\\s[^>]*)?/>`, "g"), `{/* Removed <${name}>: unresolved during fallback migration */}`).replace(new RegExp(`<${name}(?:\\s[^>]*)?>[\\s\\S]*?<\\/${name}>`, "g"), `{/* Removed <${name}>: unresolved during fallback migration */}`);
|
|
2369
|
+
}
|
|
2370
|
+
return result;
|
|
2371
|
+
}
|
|
1667
2372
|
function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIdentity) {
|
|
1668
2373
|
const root = resolve2(siteRoot);
|
|
1669
2374
|
const confined = resolve2(confinementRoot);
|
|
@@ -1750,14 +2455,14 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1750
2455
|
return;
|
|
1751
2456
|
}
|
|
1752
2457
|
const text = content.toString("utf8");
|
|
1753
|
-
const syntax =
|
|
2458
|
+
const syntax = ts2.transpileModule(text, {
|
|
1754
2459
|
fileName: path,
|
|
1755
2460
|
reportDiagnostics: true,
|
|
1756
|
-
compilerOptions: { jsx:
|
|
2461
|
+
compilerOptions: { jsx: ts2.JsxEmit.Preserve, target: ts2.ScriptTarget.ESNext, module: ts2.ModuleKind.ESNext }
|
|
1757
2462
|
});
|
|
1758
|
-
if (syntax.diagnostics?.some((diagnostic) => diagnostic.category ===
|
|
2463
|
+
if (syntax.diagnostics?.some((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error)) throw new Error("component source contains unsupported JavaScript or TypeScript syntax");
|
|
1759
2464
|
const ast = sourceFile(text, path);
|
|
1760
|
-
if (ast.statements.some((statement) =>
|
|
2465
|
+
if (ast.statements.some((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression) && statement.expression.text === "use server")) throw new Error("server-only component modules require manual migration");
|
|
1761
2466
|
const edits = [];
|
|
1762
2467
|
function dependency(literal, importDeclaration) {
|
|
1763
2468
|
const specifier = literal.text;
|
|
@@ -1767,10 +2472,10 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1767
2472
|
const localName = importDeclaration?.importClause?.name?.text;
|
|
1768
2473
|
if (localName) {
|
|
1769
2474
|
let renameToProp2 = function(node) {
|
|
1770
|
-
if (
|
|
2475
|
+
if (ts2.isJsxAttribute(node) && node.name.getText(ast) === "to" && ts2.isJsxOpeningLikeElement(node.parent.parent) && node.parent.parent.tagName.getText(ast) === localName) {
|
|
1771
2476
|
edits.push({ start: node.name.getStart(ast), end: node.name.end, value: "href" });
|
|
1772
2477
|
}
|
|
1773
|
-
|
|
2478
|
+
ts2.forEachChild(node, renameToProp2);
|
|
1774
2479
|
};
|
|
1775
2480
|
var renameToProp = renameToProp2;
|
|
1776
2481
|
renameToProp2(ast);
|
|
@@ -1793,15 +2498,15 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1793
2498
|
edits.push({ start: literal.getStart(ast), end: literal.end, value: JSON.stringify(portableSpecifier(nextPath.startsWith(".") ? nextPath : `./${nextPath}`)) });
|
|
1794
2499
|
}
|
|
1795
2500
|
function inspect(node) {
|
|
1796
|
-
if ((
|
|
1797
|
-
dependency(node.moduleSpecifier,
|
|
2501
|
+
if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier && ts2.isStringLiteral(node.moduleSpecifier)) {
|
|
2502
|
+
dependency(node.moduleSpecifier, ts2.isImportDeclaration(node) ? node : void 0);
|
|
1798
2503
|
}
|
|
1799
|
-
if (
|
|
2504
|
+
if (ts2.isCallExpression(node) && (node.expression.kind === ts2.SyntaxKind.ImportKeyword || ts2.isIdentifier(node.expression) && node.expression.text === "require")) {
|
|
1800
2505
|
const argument = node.arguments[0];
|
|
1801
|
-
if (node.arguments.length !== 1 || !argument || !
|
|
2506
|
+
if (node.arguments.length !== 1 || !argument || !ts2.isStringLiteralLike(argument)) throw new Error("computed component imports require manual migration");
|
|
1802
2507
|
dependency(argument);
|
|
1803
2508
|
}
|
|
1804
|
-
|
|
2509
|
+
ts2.forEachChild(node, inspect);
|
|
1805
2510
|
}
|
|
1806
2511
|
inspect(ast);
|
|
1807
2512
|
const replaced = applyReplacements(text, edits);
|
|
@@ -1858,16 +2563,17 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1858
2563
|
function transform(raw, currentFile) {
|
|
1859
2564
|
const parsedFrontmatter = parseFrontmatter(raw).content;
|
|
1860
2565
|
const frontmatter = raw.slice(0, raw.length - parsedFrontmatter.length);
|
|
1861
|
-
const content = normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter));
|
|
2566
|
+
const content = normalizeExplicitHeadingIds(normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter)));
|
|
1862
2567
|
let tree;
|
|
1863
2568
|
try {
|
|
1864
2569
|
tree = parser.parse(content);
|
|
1865
2570
|
} catch {
|
|
1866
2571
|
warn("Custom component analysis could not parse this MDX; its source was preserved for manual migration.", currentFile);
|
|
1867
|
-
return
|
|
2572
|
+
return frontmatter + neutralizeUnresolvableImportsWithoutAst(content, currentFile, warn);
|
|
1868
2573
|
}
|
|
1869
2574
|
const aliases = /* @__PURE__ */ new Map();
|
|
1870
2575
|
const unsupportedImports = /* @__PURE__ */ new Map();
|
|
2576
|
+
const droppedMdxDataBindings = /* @__PURE__ */ new Set();
|
|
1871
2577
|
const edits = [];
|
|
1872
2578
|
const declarations = [];
|
|
1873
2579
|
const moduleImports = [];
|
|
@@ -1878,15 +2584,15 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1878
2584
|
function hasExpressionReference(names, options = {}) {
|
|
1879
2585
|
let found = false;
|
|
1880
2586
|
function inspect2(node) {
|
|
1881
|
-
if (
|
|
1882
|
-
|
|
2587
|
+
if (ts2.isIdentifier(node) && names.has(node.text)) found = true;
|
|
2588
|
+
ts2.forEachChild(node, inspect2);
|
|
1883
2589
|
}
|
|
1884
2590
|
walk(tree, (node) => {
|
|
1885
2591
|
if (options.excludedNodes?.some((excluded) => node.position?.start.offset !== void 0 && node.position.end.offset !== void 0 && node.position.start.offset >= excluded.position.start.offset && node.position.end.offset <= excluded.position.end.offset)) return;
|
|
1886
2592
|
if (node.name && [...names].some((name) => node.name.startsWith(`${name}.`) || options.includeDirectTags && node.name === name)) found = true;
|
|
1887
2593
|
if (options.includeDeclarations !== false && node.type === "mdxjsEsm" && node.value) {
|
|
1888
2594
|
for (const statement of sourceFile(node.value, "expression.tsx").statements) {
|
|
1889
|
-
if (!
|
|
2595
|
+
if (!ts2.isImportDeclaration(statement)) inspect2(statement);
|
|
1890
2596
|
}
|
|
1891
2597
|
}
|
|
1892
2598
|
const expressions = [
|
|
@@ -1915,13 +2621,13 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1915
2621
|
if (node.value === void 0 || node.position?.start.offset === void 0) continue;
|
|
1916
2622
|
const ast = sourceFile(node.value, "inline.tsx");
|
|
1917
2623
|
for (const statement of ast.statements) {
|
|
1918
|
-
if (!
|
|
2624
|
+
if (!ts2.isImportDeclaration(statement)) {
|
|
1919
2625
|
let inspect2 = function(node2) {
|
|
1920
|
-
if (
|
|
1921
|
-
|
|
2626
|
+
if (ts2.isCallExpression(node2) && (node2.expression.kind === ts2.SyntaxKind.ImportKeyword || ts2.isIdentifier(node2.expression) && node2.expression.text === "require")) unsupportedDependency = true;
|
|
2627
|
+
ts2.forEachChild(node2, inspect2);
|
|
1922
2628
|
};
|
|
1923
2629
|
var inspect = inspect2;
|
|
1924
|
-
let unsupportedDependency =
|
|
2630
|
+
let unsupportedDependency = ts2.isExportDeclaration(statement) && !!statement.moduleSpecifier;
|
|
1925
2631
|
inspect2(statement);
|
|
1926
2632
|
if (unsupportedDependency) {
|
|
1927
2633
|
hasUnsupportedImports = true;
|
|
@@ -1935,7 +2641,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1935
2641
|
continue;
|
|
1936
2642
|
}
|
|
1937
2643
|
const bindings = imports(statement);
|
|
1938
|
-
const rawSpecifier =
|
|
2644
|
+
const rawSpecifier = ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
|
|
1939
2645
|
const specifier = rawSpecifier.startsWith("@site/") ? `/${rawSpecifier.slice("@site/".length)}` : rawSpecifier;
|
|
1940
2646
|
if (isScaffoldProvidedImport(specifier)) {
|
|
1941
2647
|
if (specifier === "react" && bindings.length > 0 && bindings.every((binding) => REACT_GLOBALS.has(binding.imported)) && !hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {
|
|
@@ -1967,21 +2673,38 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1967
2673
|
}
|
|
1968
2674
|
continue;
|
|
1969
2675
|
}
|
|
1970
|
-
if (/\.mdx?$/.test(specifier))
|
|
2676
|
+
if (/\.mdx?$/.test(specifier)) {
|
|
2677
|
+
if (bindings.length > 0 && bindings.every((binding) => !/^[A-Z]/.test(binding.local))) {
|
|
2678
|
+
warn(`MDX named import of ${JSON.stringify(rawSpecifier)} isn't a component and has no equivalent in the migrated site (for example Docusaurus's generated 'toc' export), so it was removed, along with any '...${bindings.map((binding) => binding.local).join(", '...")}' spread of it.`, currentFile);
|
|
2679
|
+
edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
2680
|
+
for (const binding of bindings) droppedMdxDataBindings.add(binding.local);
|
|
2681
|
+
}
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
1971
2684
|
if (bindings.length && RESCUABLE_ASSET_EXTENSIONS.has(extname(specifier).toLowerCase()) && !bindings.some((binding) => usedAsJsxTagName(binding.local))) {
|
|
1972
2685
|
try {
|
|
1973
2686
|
const href = copyPublicAsset(resolveDependency(specifier, currentFile));
|
|
1974
2687
|
edits.push({
|
|
1975
2688
|
start: node.position.start.offset + statement.getStart(ast),
|
|
1976
2689
|
end: node.position.start.offset + statement.end,
|
|
1977
|
-
|
|
2690
|
+
// `export`, not a bare `const`: this replaces an `import`
|
|
2691
|
+
// statement in place, and the final MDX compile (a fresh
|
|
2692
|
+
// parse of this text, not a reuse of this pass's own AST)
|
|
2693
|
+
// only recognizes a top-level JS block as ESM — evaluated and
|
|
2694
|
+
// its bindings put in scope — when it starts with `import` or
|
|
2695
|
+
// `export`. A bare `const` there is just prose to that parser
|
|
2696
|
+
// and renders as literal paragraph text instead, leaving
|
|
2697
|
+
// every reference to this binding a `ReferenceError` at
|
|
2698
|
+
// render (reproduced against hasura/graphql-engine's
|
|
2699
|
+
// databases/overview.mdx, which binds asset URLs this way).
|
|
2700
|
+
value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(href)};`).join("\n")
|
|
1978
2701
|
});
|
|
1979
2702
|
warn(`Asset import ${JSON.stringify(rawSpecifier)} was copied to ${href} and bound to that URL instead of importing it as a component.`, currentFile);
|
|
1980
2703
|
continue;
|
|
1981
2704
|
} catch {
|
|
1982
2705
|
}
|
|
1983
2706
|
}
|
|
1984
|
-
if (!bindings.length || statement.importClause?.namedBindings &&
|
|
2707
|
+
if (!bindings.length || statement.importClause?.namedBindings && ts2.isNamespaceImport(statement.importClause.namedBindings)) {
|
|
1985
2708
|
warn("Namespace or side-effect MDX imports require manual registration; the import was preserved.", currentFile);
|
|
1986
2709
|
hasUnsupportedImports = true;
|
|
1987
2710
|
continue;
|
|
@@ -2042,7 +2765,9 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2042
2765
|
edits.push({
|
|
2043
2766
|
start: importStart,
|
|
2044
2767
|
end: importEnd,
|
|
2045
|
-
|
|
2768
|
+
// See the identical `export const` note on the other asset
|
|
2769
|
+
// rescue above — same reason.
|
|
2770
|
+
value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(rescuedAssetHref)};`).join("\n")
|
|
2046
2771
|
});
|
|
2047
2772
|
warn(`Asset import ${JSON.stringify(rawSpecifier)} could not be copied as a component; it was copied to ${rescuedAssetHref} and bound to that URL instead.`, currentFile);
|
|
2048
2773
|
} else {
|
|
@@ -2052,6 +2777,77 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2052
2777
|
}
|
|
2053
2778
|
}
|
|
2054
2779
|
}
|
|
2780
|
+
const refusedInlineComponents = /* @__PURE__ */ new Set();
|
|
2781
|
+
{
|
|
2782
|
+
const plan = planInlineExtraction({
|
|
2783
|
+
declarations,
|
|
2784
|
+
tree,
|
|
2785
|
+
copyableImports: [...moduleImports, ...realPageImports],
|
|
2786
|
+
helperDeclarations: serverPageDeclarations,
|
|
2787
|
+
unavailableImports: unsupportedImports,
|
|
2788
|
+
reactGlobals: REACT_GLOBALS
|
|
2789
|
+
});
|
|
2790
|
+
for (const { name, reason } of plan.blocked) {
|
|
2791
|
+
refusedInlineComponents.add(name);
|
|
2792
|
+
warn(`Inline component "${name}" calls a React hook but was left in the page: ${reason}. Move it and everything it uses into a client component manually.`, currentFile);
|
|
2793
|
+
}
|
|
2794
|
+
const moduleSource = plan.moved.length === 0 ? "" : [
|
|
2795
|
+
...plan.moduleImports,
|
|
2796
|
+
...plan.moduleHelpers,
|
|
2797
|
+
...[...plan.moved, ...plan.copied].sort((a, b) => a - b).map((index) => declarations[index].source)
|
|
2798
|
+
].join("\n\n");
|
|
2799
|
+
if (plan.moved.length > 0 && (copied.size >= MAX_COMPONENT_FILES || Buffer.byteLength(moduleSource) > MAX_FILE_BYTES || copiedBytes + Buffer.byteLength(moduleSource) > MAX_COMPONENT_BYTES)) {
|
|
2800
|
+
warn(`Inline component "${plan.movedNames[0]}" exceeded the component migration budget; source was preserved.`, currentFile);
|
|
2801
|
+
} else if (plan.moved.length > 0) {
|
|
2802
|
+
const path = `${destinationRoot}/inline-${hash(`${relative2(root, currentFile)}:components`)}.jsx`;
|
|
2803
|
+
const inlineSource = `${resolveInlineBuiltinReferences(moduleSource, plan.moduleNames)}
|
|
2804
|
+
`;
|
|
2805
|
+
copied.set(path, { path, content: `'use client';
|
|
2806
|
+
${implicitReactImports(sourceFile(inlineSource, "inline.jsx"))}
|
|
2807
|
+
${inlineSource}` });
|
|
2808
|
+
copiedBytes += Buffer.byteLength(inlineSource);
|
|
2809
|
+
const movedIndexes = new Set(plan.moved);
|
|
2810
|
+
for (const index of plan.moved) edits.push({ start: declarations[index].start, end: declarations[index].end, value: "" });
|
|
2811
|
+
for (const name of plan.registryNames) {
|
|
2812
|
+
const registered = register(path, name);
|
|
2813
|
+
aliases.set(name, registered);
|
|
2814
|
+
moduleImports.push(`import { ${name} } from ${JSON.stringify(portableSpecifier(`./${relative2(destinationRoot, path).replace(/\\/g, "/")}`))};`);
|
|
2815
|
+
}
|
|
2816
|
+
if (plan.pageImportNames.length > 0) {
|
|
2817
|
+
realPageImports.push(`import { ${plan.pageImportNames.join(", ")} } from ${JSON.stringify(portableSpecifier(`@/${path.replace(/^src\//, "").replace(/\\/g, "/")}`))};`);
|
|
2818
|
+
}
|
|
2819
|
+
for (const entry of [...sharedImportEdits]) {
|
|
2820
|
+
const locals = [...sourceFile(content.slice(entry.start, entry.end), "shared.tsx").statements].flatMap((statement) => ts2.isImportDeclaration(statement) ? imports(statement).map((binding) => binding.local) : []);
|
|
2821
|
+
if (locals.length === 0 || locals.some((local) => plan.pageReferences.has(local))) continue;
|
|
2822
|
+
edits.push(entry);
|
|
2823
|
+
sharedImportEdits.splice(sharedImportEdits.indexOf(entry), 1);
|
|
2824
|
+
}
|
|
2825
|
+
for (let index = declarations.length - 1; index >= 0; index -= 1) {
|
|
2826
|
+
if (movedIndexes.has(index)) declarations.splice(index, 1);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
if (droppedMdxDataBindings.size > 0) {
|
|
2831
|
+
for (const node of esmNodes) {
|
|
2832
|
+
let stripDroppedSpreads2 = function(inner) {
|
|
2833
|
+
if (ts2.isSpreadElement(inner) && ts2.isIdentifier(inner.expression) && droppedMdxDataBindings.has(inner.expression.text)) {
|
|
2834
|
+
const start = nodeStart + inner.getStart(ast);
|
|
2835
|
+
let end = nodeStart + inner.end;
|
|
2836
|
+
const following = nodeText.slice(inner.end).match(/^\s*,/);
|
|
2837
|
+
if (following) end += following[0].length;
|
|
2838
|
+
edits.push({ start, end, value: "" });
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
ts2.forEachChild(inner, stripDroppedSpreads2);
|
|
2842
|
+
};
|
|
2843
|
+
var stripDroppedSpreads = stripDroppedSpreads2;
|
|
2844
|
+
if (node.value === void 0 || node.position?.start.offset === void 0) continue;
|
|
2845
|
+
const nodeStart = node.position.start.offset;
|
|
2846
|
+
const nodeText = node.value;
|
|
2847
|
+
const ast = sourceFile(nodeText, "inline.tsx");
|
|
2848
|
+
for (const statement of ast.statements) stripDroppedSpreads2(statement);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2055
2851
|
walk(tree, (node) => {
|
|
2056
2852
|
for (const attribute of node.attributes ?? []) {
|
|
2057
2853
|
if (attribute.type !== "mdxJsxAttribute" || !attribute.value || typeof attribute.value !== "object") continue;
|
|
@@ -2076,7 +2872,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2076
2872
|
}
|
|
2077
2873
|
});
|
|
2078
2874
|
const statefulDeclarationNames = new Set(
|
|
2079
|
-
declarations.filter(({ source }) => /\bon[A-Z]\w*\s*=|\buse[A-Z]\w*\s*\(/.test(source)).map(({ source }) => source.match(/^export const (\w+)/)?.[1]).filter((name) => !!name)
|
|
2875
|
+
declarations.filter(({ source }) => /\bon[A-Z]\w*\s*=|\buse[A-Z]\w*\s*\(/.test(source)).map(({ source }) => source.match(/^export const (\w+)/)?.[1]).filter((name) => !!name && !refusedInlineComponents.has(name))
|
|
2080
2876
|
);
|
|
2081
2877
|
const extracted = [];
|
|
2082
2878
|
for (const candidate of tree.children ?? []) {
|
|
@@ -2095,12 +2891,12 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2095
2891
|
continue;
|
|
2096
2892
|
}
|
|
2097
2893
|
const jsx = content.slice(node.position.start.offset, node.position.end.offset);
|
|
2098
|
-
const result =
|
|
2894
|
+
const result = ts2.transpileModule(`export default function Inline() { return (${jsx}); }`, {
|
|
2099
2895
|
fileName: "inline.jsx",
|
|
2100
|
-
compilerOptions: { jsx:
|
|
2896
|
+
compilerOptions: { jsx: ts2.JsxEmit.Preserve },
|
|
2101
2897
|
reportDiagnostics: true
|
|
2102
2898
|
});
|
|
2103
|
-
if (result.diagnostics?.some((diagnostic) => diagnostic.category ===
|
|
2899
|
+
if (result.diagnostics?.some((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error)) {
|
|
2104
2900
|
warn("Interactive MDX could not be safely extracted as JSX; source was preserved.", currentFile);
|
|
2105
2901
|
continue;
|
|
2106
2902
|
}
|
|
@@ -2108,15 +2904,15 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2108
2904
|
}
|
|
2109
2905
|
if (extracted.length && declarations.length) {
|
|
2110
2906
|
let bind2 = function(name) {
|
|
2111
|
-
if (
|
|
2112
|
-
else for (const element of name.elements) if (
|
|
2907
|
+
if (ts2.isIdentifier(name)) declared.add(name.text);
|
|
2908
|
+
else for (const element of name.elements) if (ts2.isBindingElement(element)) bind2(element.name);
|
|
2113
2909
|
};
|
|
2114
2910
|
var bind = bind2;
|
|
2115
2911
|
const declared = /* @__PURE__ */ new Set();
|
|
2116
2912
|
for (const declaration of declarations) {
|
|
2117
2913
|
for (const statement of sourceFile(declaration.source, "declaration.tsx").statements) {
|
|
2118
|
-
if ((
|
|
2119
|
-
if (
|
|
2914
|
+
if ((ts2.isFunctionDeclaration(statement) || ts2.isClassDeclaration(statement)) && statement.name) declared.add(statement.name.text);
|
|
2915
|
+
if (ts2.isVariableStatement(statement)) for (const variable of statement.declarationList.declarations) bind2(variable.name);
|
|
2120
2916
|
}
|
|
2121
2917
|
}
|
|
2122
2918
|
const hasSharedDeclarations = hasExpressionReference(declared, {
|
|
@@ -2277,22 +3073,22 @@ function mergeComponentRegistry(existing, incoming) {
|
|
|
2277
3073
|
const name = `MigratedRegistry${fingerprint}`;
|
|
2278
3074
|
const registryPath = `./migrated-components-${fingerprint}`;
|
|
2279
3075
|
const source = sourceFile(existing, "custom-components.tsx");
|
|
2280
|
-
const alreadyImported = source.statements.some((statement) =>
|
|
3076
|
+
const alreadyImported = source.statements.some((statement) => ts2.isImportDeclaration(statement) && ts2.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === registryPath);
|
|
2281
3077
|
let initializer;
|
|
2282
3078
|
for (const statement of source.statements) {
|
|
2283
|
-
if (!
|
|
3079
|
+
if (!ts2.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword)) continue;
|
|
2284
3080
|
for (const declaration of statement.declarationList.declarations) {
|
|
2285
|
-
if (
|
|
3081
|
+
if (ts2.isIdentifier(declaration.name) && declaration.name.text === "customComponents") initializer = declaration.initializer;
|
|
2286
3082
|
}
|
|
2287
3083
|
}
|
|
2288
|
-
while (initializer && (
|
|
2289
|
-
if (!initializer || !
|
|
3084
|
+
while (initializer && (ts2.isAsExpression(initializer) || ts2.isSatisfiesExpression(initializer) || ts2.isParenthesizedExpression(initializer))) initializer = initializer.expression;
|
|
3085
|
+
if (!initializer || !ts2.isObjectLiteralExpression(initializer)) {
|
|
2290
3086
|
throw new Error("Cannot safely merge src/mdx/custom-components.tsx: export customComponents as an object literal before importing components. The existing registry was preserved.");
|
|
2291
3087
|
}
|
|
2292
3088
|
const content = alreadyImported ? existing : applyReplacements(existing, [
|
|
2293
3089
|
{
|
|
2294
|
-
start: source.statements.filter((statement) =>
|
|
2295
|
-
end: source.statements.filter((statement) =>
|
|
3090
|
+
start: source.statements.filter((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,
|
|
3091
|
+
end: source.statements.filter((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,
|
|
2296
3092
|
value: `
|
|
2297
3093
|
import { customComponents as ${name} } from ${JSON.stringify(registryPath)}
|
|
2298
3094
|
`
|
|
@@ -2315,7 +3111,7 @@ import { parse as parseYaml2 } from "yaml";
|
|
|
2315
3111
|
// src/navigation.ts
|
|
2316
3112
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
2317
3113
|
import { dirname as dirname2, extname as extname2, relative as relative3 } from "path";
|
|
2318
|
-
var MAX_MINTLIFY_CONFIG_BYTES =
|
|
3114
|
+
var MAX_MINTLIFY_CONFIG_BYTES = 2e7;
|
|
2319
3115
|
function addMintlifyDirectoryRedirects(config, pages) {
|
|
2320
3116
|
const pageIds = new Set(pages.map((page) => page.id));
|
|
2321
3117
|
const redirects = [...config.redirects ?? []];
|
|
@@ -2363,15 +3159,10 @@ function addMintlifyDirectoryRedirects(config, pages) {
|
|
|
2363
3159
|
}
|
|
2364
3160
|
}
|
|
2365
3161
|
}
|
|
2366
|
-
for (const
|
|
2367
|
-
|
|
2368
|
-
if (!destination.startsWith("/") || destination.startsWith("//") || /[:*()[\]{}]/.test(destination)) continue;
|
|
2369
|
-
const directory = trimEdgeSlashes(destination);
|
|
2370
|
-
if (!directory || pageIds.has(directory)) continue;
|
|
3162
|
+
for (const [directory, landing] of firstDescendantByDirectory) {
|
|
3163
|
+
if (pageIds.has(directory)) continue;
|
|
2371
3164
|
const source = `/${directory}`;
|
|
2372
3165
|
if (hasAuthoredRedirect(source)) continue;
|
|
2373
|
-
const landing = firstDescendantByDirectory.get(directory);
|
|
2374
|
-
if (!landing) continue;
|
|
2375
3166
|
redirects.push({ source, destination: `/${landing.id}`, permanent: false });
|
|
2376
3167
|
redirectSources.add(source);
|
|
2377
3168
|
}
|
|
@@ -2422,6 +3213,18 @@ var LANGUAGE_LABELS = {
|
|
|
2422
3213
|
function objectValue(value) {
|
|
2423
3214
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2424
3215
|
}
|
|
3216
|
+
function withFlattenedProductGroups(container) {
|
|
3217
|
+
if (!Array.isArray(container.productGroups)) return container;
|
|
3218
|
+
const flattened = container.productGroups.flatMap((entry) => {
|
|
3219
|
+
const group = objectValue(entry);
|
|
3220
|
+
return group && Array.isArray(group.products) ? group.products : [];
|
|
3221
|
+
});
|
|
3222
|
+
if (flattened.length === 0) return container;
|
|
3223
|
+
return {
|
|
3224
|
+
...container,
|
|
3225
|
+
products: [...Array.isArray(container.products) ? container.products : [], ...flattened]
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
2425
3228
|
function projectedHref(value) {
|
|
2426
3229
|
if (typeof value !== "string") return void 0;
|
|
2427
3230
|
const href = value.trim();
|
|
@@ -2509,12 +3312,23 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
2509
3312
|
const references = [];
|
|
2510
3313
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus", "languages"];
|
|
2511
3314
|
const nestedKeys = ["groups", "pages"];
|
|
2512
|
-
function visit(
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
3315
|
+
function visit(rawNode, tabLabel) {
|
|
3316
|
+
const node = withFlattenedProductGroups(rawNode);
|
|
3317
|
+
for (const kind of ["openapi", "asyncapi"]) {
|
|
3318
|
+
const value = node[kind];
|
|
3319
|
+
if (typeof value === "string" && value.trim()) {
|
|
3320
|
+
references.push({ value: value.trim(), kind, tabLabel });
|
|
3321
|
+
continue;
|
|
3322
|
+
}
|
|
3323
|
+
const object = objectValue(value);
|
|
3324
|
+
if (object && typeof object.source === "string" && object.source.trim()) {
|
|
3325
|
+
references.push({
|
|
3326
|
+
value: object.source.trim(),
|
|
3327
|
+
kind,
|
|
3328
|
+
tabLabel,
|
|
3329
|
+
...typeof object.directory === "string" && object.directory.trim() ? { directory: object.directory.trim() } : {}
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
2518
3332
|
}
|
|
2519
3333
|
for (const key of containerKeys) {
|
|
2520
3334
|
const entries = node[key];
|
|
@@ -2575,8 +3389,19 @@ function registerReference(value, context) {
|
|
|
2575
3389
|
}
|
|
2576
3390
|
return navigationId;
|
|
2577
3391
|
}
|
|
3392
|
+
var OPENAPI_OPERATION_REF = /^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+\S/;
|
|
2578
3393
|
function convertPage(value, context) {
|
|
2579
|
-
if (typeof value === "string")
|
|
3394
|
+
if (typeof value === "string") {
|
|
3395
|
+
if (OPENAPI_OPERATION_REF.test(value)) {
|
|
3396
|
+
warnOnce(
|
|
3397
|
+
context,
|
|
3398
|
+
"openapi-operation-list",
|
|
3399
|
+
'One or more navigation groups hand-pick or reorder individual OpenAPI operations (e.g. "GET /users"); Thally always renders every operation of a bound spec and has no manual-selection equivalent, so that list was dropped. The operations themselves are still available through the bound API reference.'
|
|
3400
|
+
);
|
|
3401
|
+
return null;
|
|
3402
|
+
}
|
|
3403
|
+
return registerReference(value, context);
|
|
3404
|
+
}
|
|
2580
3405
|
const object = objectValue(value);
|
|
2581
3406
|
if (!object) return null;
|
|
2582
3407
|
if (typeof object.page === "string") return registerReference(object.page, context);
|
|
@@ -2619,8 +3444,9 @@ function convertNavigationValues(values, context) {
|
|
|
2619
3444
|
});
|
|
2620
3445
|
}
|
|
2621
3446
|
function convertContainerToTabs(containerValue, context, fallbackTab, trace, depth = 0) {
|
|
2622
|
-
const
|
|
2623
|
-
if (!
|
|
3447
|
+
const rawContainer = objectValue(containerValue);
|
|
3448
|
+
if (!rawContainer) return [];
|
|
3449
|
+
const container = withFlattenedProductGroups(rawContainer);
|
|
2624
3450
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus"];
|
|
2625
3451
|
for (const key of containerKeys) {
|
|
2626
3452
|
if (!Array.isArray(container[key])) continue;
|
|
@@ -2842,6 +3668,47 @@ function isRedirectPathSafe(rawSource, rawDestination) {
|
|
|
2842
3668
|
}
|
|
2843
3669
|
return true;
|
|
2844
3670
|
}
|
|
3671
|
+
function mintlifyDefaultVersionPrefixes(config) {
|
|
3672
|
+
const navigation = objectValue(config.navigation) ?? config;
|
|
3673
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
3674
|
+
function collect(container) {
|
|
3675
|
+
if (Array.isArray(container.versions)) {
|
|
3676
|
+
const entries = container.versions.map((value) => objectValue(value)).filter((value) => value !== null);
|
|
3677
|
+
const defaultEntry = entries.find((entry) => entry.default === true) ?? entries[0];
|
|
3678
|
+
if (defaultEntry && typeof defaultEntry.version === "string" && defaultEntry.version.trim()) {
|
|
3679
|
+
prefixes.add(defaultEntry.version.trim());
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
if (Array.isArray(container.languages)) {
|
|
3683
|
+
for (const value of container.languages) {
|
|
3684
|
+
const language = objectValue(value);
|
|
3685
|
+
if (language) collect(language);
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
collect(navigation);
|
|
3690
|
+
return prefixes;
|
|
3691
|
+
}
|
|
3692
|
+
function mintlifyAllVersionPrefixes(config) {
|
|
3693
|
+
const navigation = objectValue(config.navigation) ?? config;
|
|
3694
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
3695
|
+
function collect(container) {
|
|
3696
|
+
if (Array.isArray(container.versions)) {
|
|
3697
|
+
for (const value of container.versions) {
|
|
3698
|
+
const entry = objectValue(value);
|
|
3699
|
+
if (entry && typeof entry.version === "string" && entry.version.trim()) prefixes.add(entry.version.trim());
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
if (Array.isArray(container.languages)) {
|
|
3703
|
+
for (const value of container.languages) {
|
|
3704
|
+
const language = objectValue(value);
|
|
3705
|
+
if (language) collect(language);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
collect(navigation);
|
|
3710
|
+
return prefixes;
|
|
3711
|
+
}
|
|
2845
3712
|
function projectMintlifyNavigation(config, options = {}) {
|
|
2846
3713
|
const warnings = [];
|
|
2847
3714
|
const references = [];
|
|
@@ -3087,7 +3954,7 @@ function pruneMissingNavigationPages(config, availableIds) {
|
|
|
3087
3954
|
}
|
|
3088
3955
|
|
|
3089
3956
|
// src/docusaurus.ts
|
|
3090
|
-
var MAX_CONFIG_BYTES =
|
|
3957
|
+
var MAX_CONFIG_BYTES = 2e7;
|
|
3091
3958
|
var CATEGORY_FILENAMES = ["_category_.json", "_category_.yml", "_category_.yaml"];
|
|
3092
3959
|
var SIDEBAR_FILENAMES = [
|
|
3093
3960
|
"sidebars.json",
|
|
@@ -3284,7 +4151,8 @@ function rewriteDocusaurusLinks(body, current, descriptors, options = {}) {
|
|
|
3284
4151
|
return options.assetPaths?.has(path.slice(1)) ? target : null;
|
|
3285
4152
|
}
|
|
3286
4153
|
function rewriteTarget(target) {
|
|
3287
|
-
if (!target ||
|
|
4154
|
+
if (!target || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(target)) return target;
|
|
4155
|
+
if (target.startsWith("#")) return target;
|
|
3288
4156
|
const suffixIndex = target.search(/[?#]/);
|
|
3289
4157
|
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
3290
4158
|
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
@@ -3338,7 +4206,7 @@ function readDocusaurusSiteOrigin(repositoryRoot) {
|
|
|
3338
4206
|
}
|
|
3339
4207
|
function readBoundedText(path) {
|
|
3340
4208
|
if (lstatSync3(path).size > MAX_CONFIG_BYTES) {
|
|
3341
|
-
throw new Error("Docusaurus sidebar config
|
|
4209
|
+
throw new Error("The Docusaurus sidebar config is larger than 20 MB and could not be imported.");
|
|
3342
4210
|
}
|
|
3343
4211
|
return readFileSync3(path, "utf8");
|
|
3344
4212
|
}
|
|
@@ -3970,7 +4838,7 @@ import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as rea
|
|
|
3970
4838
|
import { basename as basename2, dirname as dirname3, extname as extname4, relative as relative4 } from "path";
|
|
3971
4839
|
import { load } from "cheerio";
|
|
3972
4840
|
import { parse as parseYaml3 } from "yaml";
|
|
3973
|
-
var MAX_CONFIG_BYTES2 =
|
|
4841
|
+
var MAX_CONFIG_BYTES2 = 2e7;
|
|
3974
4842
|
function announcementMarkdown(message) {
|
|
3975
4843
|
const document = load(message);
|
|
3976
4844
|
document("script, style, iframe, object, embed, svg, math").remove();
|
|
@@ -4005,7 +4873,7 @@ function titleCase3(value) {
|
|
|
4005
4873
|
}
|
|
4006
4874
|
function readBoundedYaml(path) {
|
|
4007
4875
|
if (lstatSync4(path).size > MAX_CONFIG_BYTES2) {
|
|
4008
|
-
throw new Error("Fern config
|
|
4876
|
+
throw new Error("The Fern config is larger than 20 MB and could not be imported.");
|
|
4009
4877
|
}
|
|
4010
4878
|
return parseYaml3(readFileSync4(path, "utf8"));
|
|
4011
4879
|
}
|
|
@@ -4067,7 +4935,7 @@ function registerPageAt(rawPath, base, context, navTitle, hidden = false) {
|
|
|
4067
4935
|
let sourcePath;
|
|
4068
4936
|
try {
|
|
4069
4937
|
const absolute = resolveWithinRoot(
|
|
4070
|
-
context.pathPrefix ?
|
|
4938
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
4071
4939
|
rawPath.trim(),
|
|
4072
4940
|
context.repositoryRoot
|
|
4073
4941
|
);
|
|
@@ -4096,7 +4964,7 @@ function registerFolder(object, parentSegments, context) {
|
|
|
4096
4964
|
let folder;
|
|
4097
4965
|
try {
|
|
4098
4966
|
folder = resolveWithinRoot(
|
|
4099
|
-
context.pathPrefix ?
|
|
4967
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
4100
4968
|
rawPath,
|
|
4101
4969
|
context.repositoryRoot
|
|
4102
4970
|
);
|
|
@@ -4202,10 +5070,11 @@ function convertNode(node, parentSegments, context) {
|
|
|
4202
5070
|
return null;
|
|
4203
5071
|
}
|
|
4204
5072
|
if (typeof object.api === "string") {
|
|
5073
|
+
const ownSegment = segmentFor(object, object.api, context);
|
|
4205
5074
|
context.apiSections.push({
|
|
4206
5075
|
name: typeof object["api-name"] === "string" ? object["api-name"] : object.api,
|
|
4207
5076
|
nameExplicit: typeof object["api-name"] === "string",
|
|
4208
|
-
routeSegments: parentSegments
|
|
5077
|
+
routeSegments: ownSegment ? [...parentSegments, ownSegment] : parentSegments
|
|
4209
5078
|
});
|
|
4210
5079
|
return null;
|
|
4211
5080
|
}
|
|
@@ -4247,7 +5116,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
4247
5116
|
}
|
|
4248
5117
|
if (!chosen || typeof chosen.path !== "string") return config;
|
|
4249
5118
|
try {
|
|
4250
|
-
const versionPath = resolveWithinRoot(configDir, chosen.path,
|
|
5119
|
+
const versionPath = resolveWithinRoot(configDir, chosen.path, context.repositoryRoot);
|
|
4251
5120
|
if (!existsSync4(versionPath) || !lstatSync4(versionPath).isFile()) {
|
|
4252
5121
|
warnOnce2(context, "fern-version-not-file", `Fern version file "${chosen.path}" is not a regular file and was skipped.`);
|
|
4253
5122
|
return config;
|
|
@@ -4485,7 +5354,16 @@ function projectFernNavigation(input) {
|
|
|
4485
5354
|
return section;
|
|
4486
5355
|
}
|
|
4487
5356
|
const distinguishingSegment = section.routeSegments.at(-1);
|
|
4488
|
-
|
|
5357
|
+
let tabLabel = distinguishingSegment ? `${section.tabLabel}: ${titleCase3(distinguishingSegment)}` : section.tabLabel;
|
|
5358
|
+
if (!tabLabel || tabLabel === `${section.tabLabel}: ${section.tabLabel}` || claimedTabLabels.has(tabLabel)) {
|
|
5359
|
+
let ordinal = 2;
|
|
5360
|
+
let candidate = `${section.tabLabel} (${ordinal})`;
|
|
5361
|
+
while (claimedTabLabels.has(candidate)) {
|
|
5362
|
+
ordinal += 1;
|
|
5363
|
+
candidate = `${section.tabLabel} (${ordinal})`;
|
|
5364
|
+
}
|
|
5365
|
+
tabLabel = candidate;
|
|
5366
|
+
}
|
|
4489
5367
|
claimedTabLabels.add(tabLabel);
|
|
4490
5368
|
return { ...section, tabLabel };
|
|
4491
5369
|
});
|
|
@@ -4535,11 +5413,14 @@ var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4535
5413
|
".ico",
|
|
4536
5414
|
".jpeg",
|
|
4537
5415
|
".jpg",
|
|
5416
|
+
".m4a",
|
|
4538
5417
|
".mp3",
|
|
4539
5418
|
".mp4",
|
|
5419
|
+
".ogg",
|
|
4540
5420
|
".pdf",
|
|
4541
5421
|
".png",
|
|
4542
5422
|
".svg",
|
|
5423
|
+
".wav",
|
|
4543
5424
|
".webm",
|
|
4544
5425
|
".webp"
|
|
4545
5426
|
]);
|
|
@@ -4727,9 +5608,92 @@ function readDocusaurusConfigSource(projectRoot) {
|
|
|
4727
5608
|
if (!path || lstatSync5(path).size > 1e6) return "";
|
|
4728
5609
|
return readFileSync5(path, "utf8");
|
|
4729
5610
|
}
|
|
5611
|
+
function matchingBraceIndex(source, openBraceIndex) {
|
|
5612
|
+
let depth = 0;
|
|
5613
|
+
for (let i = openBraceIndex; i < source.length; i++) {
|
|
5614
|
+
const ch = source[i];
|
|
5615
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5616
|
+
const quote = ch;
|
|
5617
|
+
i++;
|
|
5618
|
+
while (i < source.length && source[i] !== quote) {
|
|
5619
|
+
if (source[i] === "\\") i++;
|
|
5620
|
+
i++;
|
|
5621
|
+
}
|
|
5622
|
+
continue;
|
|
5623
|
+
}
|
|
5624
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
5625
|
+
const newline = source.indexOf("\n", i);
|
|
5626
|
+
i = newline === -1 ? source.length : newline;
|
|
5627
|
+
continue;
|
|
5628
|
+
}
|
|
5629
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
5630
|
+
const end = source.indexOf("*/", i + 2);
|
|
5631
|
+
i = end === -1 ? source.length : end + 1;
|
|
5632
|
+
continue;
|
|
5633
|
+
}
|
|
5634
|
+
if (ch === "{") depth++;
|
|
5635
|
+
else if (ch === "}") {
|
|
5636
|
+
depth--;
|
|
5637
|
+
if (depth === 0) return i;
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
return -1;
|
|
5641
|
+
}
|
|
5642
|
+
function topLevelStringProperty(objectBody, key) {
|
|
5643
|
+
let depth = 0;
|
|
5644
|
+
const keyPattern = new RegExp(`^${key}\\s*:\\s*(['"])`);
|
|
5645
|
+
for (let i = 0; i < objectBody.length; i++) {
|
|
5646
|
+
const ch = objectBody[i];
|
|
5647
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5648
|
+
const quote = ch;
|
|
5649
|
+
i++;
|
|
5650
|
+
while (i < objectBody.length && objectBody[i] !== quote) {
|
|
5651
|
+
if (objectBody[i] === "\\") i++;
|
|
5652
|
+
i++;
|
|
5653
|
+
}
|
|
5654
|
+
continue;
|
|
5655
|
+
}
|
|
5656
|
+
if (ch === "/" && objectBody[i + 1] === "/") {
|
|
5657
|
+
const newline = objectBody.indexOf("\n", i);
|
|
5658
|
+
i = newline === -1 ? objectBody.length : newline;
|
|
5659
|
+
continue;
|
|
5660
|
+
}
|
|
5661
|
+
if (ch === "/" && objectBody[i + 1] === "*") {
|
|
5662
|
+
const end = objectBody.indexOf("*/", i + 2);
|
|
5663
|
+
i = end === -1 ? objectBody.length : end + 1;
|
|
5664
|
+
continue;
|
|
5665
|
+
}
|
|
5666
|
+
if (ch === "{" || ch === "[" || ch === "(") {
|
|
5667
|
+
depth++;
|
|
5668
|
+
continue;
|
|
5669
|
+
}
|
|
5670
|
+
if (ch === "}" || ch === "]" || ch === ")") {
|
|
5671
|
+
depth--;
|
|
5672
|
+
continue;
|
|
5673
|
+
}
|
|
5674
|
+
if (depth === 0) {
|
|
5675
|
+
const match = keyPattern.exec(objectBody.slice(i));
|
|
5676
|
+
if (match) {
|
|
5677
|
+
const quote = match[1];
|
|
5678
|
+
const valueStart = i + match[0].length;
|
|
5679
|
+
const valueEnd = objectBody.indexOf(quote, valueStart);
|
|
5680
|
+
if (valueEnd !== -1) return objectBody.slice(valueStart, valueEnd);
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
return void 0;
|
|
5685
|
+
}
|
|
4730
5686
|
function primaryDocusaurusDocsDirectory(projectRoot) {
|
|
4731
5687
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
4732
|
-
const
|
|
5688
|
+
const docsKeyMatch = source.match(/\bdocs\s*:\s*\{/);
|
|
5689
|
+
let configured;
|
|
5690
|
+
if (docsKeyMatch?.index !== void 0) {
|
|
5691
|
+
const openBrace = docsKeyMatch.index + docsKeyMatch[0].length - 1;
|
|
5692
|
+
const closeBrace = matchingBraceIndex(source, openBrace);
|
|
5693
|
+
if (closeBrace !== -1) {
|
|
5694
|
+
configured = topLevelStringProperty(source.slice(openBrace + 1, closeBrace), "path");
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
4733
5697
|
return trimTrailingSlashes(configured?.replace(/^\.\//, "") ?? "") || "docs";
|
|
4734
5698
|
}
|
|
4735
5699
|
function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
@@ -4781,7 +5745,7 @@ function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
|
4781
5745
|
function additionalDocusaurusPluginRoots(repositoryDir, projectRoot, warnings) {
|
|
4782
5746
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
4783
5747
|
const plugins = [];
|
|
4784
|
-
const matcher = /['"]
|
|
5748
|
+
const matcher = /['"](?:@docusaurus\/plugin-)?content-docs['"][\s\S]{0,3000}?\bpath\s*:\s*(['"])([^'"]+)\1[\s\S]{0,1000}?\brouteBasePath\s*:\s*(['"])([^'"]+)\3/g;
|
|
4785
5749
|
for (const match of source.matchAll(matcher)) {
|
|
4786
5750
|
const localPath = trimTrailingSlashes(match[2].replace(/^\.\//, ""));
|
|
4787
5751
|
const routePrefix = trimEdgeSlashes(match[4]);
|
|
@@ -5019,8 +5983,10 @@ function detectRepositoryDocsDir(repositoryDir) {
|
|
|
5019
5983
|
}
|
|
5020
5984
|
return "";
|
|
5021
5985
|
}
|
|
5022
|
-
|
|
5986
|
+
var MAX_RANKED_WALK_FILES = MAX_SOURCE_FILES * 20;
|
|
5987
|
+
function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
5023
5988
|
const files = [];
|
|
5989
|
+
const walkCap = rank ? MAX_RANKED_WALK_FILES : MAX_SOURCE_FILES;
|
|
5024
5990
|
let confinementReal;
|
|
5025
5991
|
try {
|
|
5026
5992
|
confinementReal = realpathSync2(confinementRoot);
|
|
@@ -5029,7 +5995,7 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5029
5995
|
}
|
|
5030
5996
|
const visitedRealPaths = /* @__PURE__ */ new Set();
|
|
5031
5997
|
function visit(directory, logicalDirectory = directory) {
|
|
5032
|
-
if (files.length >=
|
|
5998
|
+
if (files.length >= walkCap) return;
|
|
5033
5999
|
let directoryReal;
|
|
5034
6000
|
try {
|
|
5035
6001
|
directoryReal = realpathSync2(directory);
|
|
@@ -5045,7 +6011,7 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5045
6011
|
return;
|
|
5046
6012
|
}
|
|
5047
6013
|
for (const entry of entries) {
|
|
5048
|
-
if (files.length >=
|
|
6014
|
+
if (files.length >= walkCap) return;
|
|
5049
6015
|
if (isIgnoredContentDirectory(entry.name)) {
|
|
5050
6016
|
if (warnings && entry.isDirectory() && !entry.isSymbolicLink() && containsMarkdown(resolveWithin(directory, entry.name))) {
|
|
5051
6017
|
warnings.push({
|
|
@@ -5082,8 +6048,44 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5082
6048
|
}
|
|
5083
6049
|
}
|
|
5084
6050
|
visit(root);
|
|
6051
|
+
if (rank && files.length >= walkCap) {
|
|
6052
|
+
warnings?.push({
|
|
6053
|
+
code: "limit-reached",
|
|
6054
|
+
message: `Stopped scanning after ${walkCap} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.`
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
5085
6057
|
return files;
|
|
5086
6058
|
}
|
|
6059
|
+
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label) {
|
|
6060
|
+
if (scanned.length <= MAX_SOURCE_FILES) return scanned;
|
|
6061
|
+
const ranked = scanned.map((file) => ({ file, priority: rank(file.relativePath) })).sort((left, right) => left.priority - right.priority || (left.file.relativePath < right.file.relativePath ? -1 : left.file.relativePath > right.file.relativePath ? 1 : 0));
|
|
6062
|
+
const dropped = ranked.slice(MAX_SOURCE_FILES).map(({ file }) => file);
|
|
6063
|
+
if (warnings) {
|
|
6064
|
+
const droppedPages = label === "file" ? dropped.filter((file) => isDocumentationExtension(file.relativePath)) : dropped;
|
|
6065
|
+
const noun = label === "file" ? "page" : "image or media file";
|
|
6066
|
+
const droppedVersions = /* @__PURE__ */ new Set();
|
|
6067
|
+
for (const file of dropped) {
|
|
6068
|
+
const firstSegment = file.relativePath.split("/", 1)[0];
|
|
6069
|
+
if (allVersionPrefixes.has(firstSegment)) droppedVersions.add(firstSegment);
|
|
6070
|
+
}
|
|
6071
|
+
const examples = droppedPages.slice(0, 3).map((file) => file.relativePath);
|
|
6072
|
+
const rest = droppedPages.length - examples.length;
|
|
6073
|
+
warnings.push({
|
|
6074
|
+
code: "limit-reached",
|
|
6075
|
+
message: `This repository has more than ${MAX_SOURCE_FILES} ${label === "file" ? "files" : "assets"}, so only the first ${MAX_SOURCE_FILES} (in navigation order, default version first) were migrated. ${droppedPages.length} ${noun}(s) were left out` + (examples.length > 0 ? `: ${examples.join(", ")}${rest > 0 ? `, and ${rest} more` : ""}` : "") + (droppedVersions.size > 0 ? ` (versions: ${[...droppedVersions].slice(0, 5).join(", ")}${droppedVersions.size > 5 ? `, and ${droppedVersions.size - 5} more` : ""})` : "") + (label === "file" ? ". To include them, run the migration on a smaller part of the repository with --docs-dir." : ". Copy them into public/ manually if your pages use them.")
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
return ranked.slice(0, MAX_SOURCE_FILES).map(({ file }) => file);
|
|
6079
|
+
}
|
|
6080
|
+
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes) {
|
|
6081
|
+
const isAsset = (file) => ASSET_EXTENSIONS.has(extname5(file.relativePath).toLowerCase());
|
|
6082
|
+
const assetFiles = scanned.filter(isAsset);
|
|
6083
|
+
const otherFiles = scanned.filter((file) => !isAsset(file));
|
|
6084
|
+
return [
|
|
6085
|
+
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file"),
|
|
6086
|
+
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset")
|
|
6087
|
+
];
|
|
6088
|
+
}
|
|
5087
6089
|
function readMintignoreMatcher(mintlifyRoot) {
|
|
5088
6090
|
const path = resolveWithin(mintlifyRoot, MINTIGNORE_FILENAME);
|
|
5089
6091
|
if (!existsSync5(path) || !lstatSync5(path).isFile()) return null;
|
|
@@ -5200,10 +6202,159 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
5200
6202
|
});
|
|
5201
6203
|
continue;
|
|
5202
6204
|
}
|
|
5203
|
-
|
|
6205
|
+
if (reference.directory) {
|
|
6206
|
+
warnings.push({
|
|
6207
|
+
code: "unsupported-config",
|
|
6208
|
+
message: `The OpenAPI spec "${reference.value}"${tabSuffix} was limited to pages under "${reference.directory}" in the source, but Thally's API reference always covers a whole tab, so it was migrated as the tab's full API reference. Update any links to "${reference.directory}/..." pages manually.`
|
|
6209
|
+
});
|
|
6210
|
+
}
|
|
6211
|
+
specs.push({
|
|
6212
|
+
filename: basename3(match.relativePath),
|
|
6213
|
+
content: readFileSync5(match.absolutePath),
|
|
6214
|
+
tabLabel: reference.tabLabel,
|
|
6215
|
+
sourcePath: match.relativePath,
|
|
6216
|
+
directory: reference.directory
|
|
6217
|
+
});
|
|
6218
|
+
}
|
|
6219
|
+
const filenameOwners = /* @__PURE__ */ new Map();
|
|
6220
|
+
for (const spec of specs) {
|
|
6221
|
+
const groupKey = spec.tabLabel ?? "\0default";
|
|
6222
|
+
const owner = filenameOwners.get(spec.filename);
|
|
6223
|
+
if (owner !== void 0 && owner !== groupKey) {
|
|
6224
|
+
let disambiguated = spec.sourcePath.replace(/\//g, "-");
|
|
6225
|
+
while (filenameOwners.has(disambiguated) && filenameOwners.get(disambiguated) !== groupKey) {
|
|
6226
|
+
disambiguated = `${groupKey.replace(/\W+/g, "-")}-${disambiguated}`;
|
|
6227
|
+
}
|
|
6228
|
+
spec.filename = disambiguated;
|
|
6229
|
+
}
|
|
6230
|
+
filenameOwners.set(spec.filename, groupKey);
|
|
5204
6231
|
}
|
|
5205
6232
|
return specs;
|
|
5206
6233
|
}
|
|
6234
|
+
var OPENAPI_HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head", "trace"];
|
|
6235
|
+
var FERN_CRUD_METHOD_NAMES = {
|
|
6236
|
+
findall: "list",
|
|
6237
|
+
findone: "get",
|
|
6238
|
+
remove: "delete"
|
|
6239
|
+
};
|
|
6240
|
+
function mintlifyOperationSlugSegment(value) {
|
|
6241
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
6242
|
+
}
|
|
6243
|
+
function thallyOperationSlugSegments(path, method) {
|
|
6244
|
+
const cleaned = path.split("/").filter(Boolean).map((segment) => segment.replace(/[{}]/g, "").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").toLowerCase());
|
|
6245
|
+
if (!cleaned.length) cleaned.push("root");
|
|
6246
|
+
cleaned.push(method.toLowerCase());
|
|
6247
|
+
return cleaned;
|
|
6248
|
+
}
|
|
6249
|
+
function slugifyApiSpecId(value) {
|
|
6250
|
+
return value.toLowerCase().replace(/[^a-z0-9/]+/g, "-").replace(/(^-|-$)+/g, "").replace(/\//g, "-") || value.toLowerCase();
|
|
6251
|
+
}
|
|
6252
|
+
function parseOpenApiOperationsForLinking(content) {
|
|
6253
|
+
let document;
|
|
6254
|
+
try {
|
|
6255
|
+
document = parseYaml4(content.toString("utf8"));
|
|
6256
|
+
} catch {
|
|
6257
|
+
return [];
|
|
6258
|
+
}
|
|
6259
|
+
const paths = document && typeof document === "object" ? document.paths : null;
|
|
6260
|
+
if (!paths || typeof paths !== "object") return [];
|
|
6261
|
+
const operations = [];
|
|
6262
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
6263
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
6264
|
+
for (const method of OPENAPI_HTTP_METHODS) {
|
|
6265
|
+
const operation = pathItem[method];
|
|
6266
|
+
if (!operation || typeof operation !== "object") continue;
|
|
6267
|
+
const op = operation;
|
|
6268
|
+
const tags = Array.isArray(op.tags) ? op.tags.filter((tag) => typeof tag === "string") : [];
|
|
6269
|
+
operations.push({
|
|
6270
|
+
method,
|
|
6271
|
+
path,
|
|
6272
|
+
tag: tags[0],
|
|
6273
|
+
summary: typeof op.summary === "string" ? op.summary : void 0,
|
|
6274
|
+
operationId: typeof op.operationId === "string" ? op.operationId : void 0
|
|
6275
|
+
});
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
return operations;
|
|
6279
|
+
}
|
|
6280
|
+
function apiOperationLinkMap(sources, docsConfig) {
|
|
6281
|
+
const map = /* @__PURE__ */ new Map();
|
|
6282
|
+
const prefixLandings = /* @__PURE__ */ new Map();
|
|
6283
|
+
const apiTabs = docsConfig.tabs.filter((tab) => !tab.hidden && tab.api);
|
|
6284
|
+
apiTabs.forEach((tab, index) => {
|
|
6285
|
+
const source = sources.find((entry) => tab.api?.source === `/${entry.filename}`);
|
|
6286
|
+
if (!source || !source.prefix) return;
|
|
6287
|
+
const specId = index === 0 ? "default" : slugifyApiSpecId(tab.tab);
|
|
6288
|
+
const prefix = source.prefix.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
6289
|
+
if (prefix) prefixLandings.set(prefix, specId === "default" ? "/api" : `/api/${specId}`);
|
|
6290
|
+
for (const operation of parseOpenApiOperationsForLinking(source.content)) {
|
|
6291
|
+
const thallyHref = `/api/${specId}/${thallyOperationSlugSegments(operation.path, operation.method).join("/")}`;
|
|
6292
|
+
const leafCandidates = /* @__PURE__ */ new Set();
|
|
6293
|
+
if (operation.operationId) {
|
|
6294
|
+
const parts = operation.operationId.split(/[_.]/).filter(Boolean);
|
|
6295
|
+
const lastPart = parts.at(-1) ?? operation.operationId;
|
|
6296
|
+
const conventional = FERN_CRUD_METHOD_NAMES[lastPart.toLowerCase()];
|
|
6297
|
+
if (conventional) leafCandidates.add(conventional);
|
|
6298
|
+
leafCandidates.add(mintlifyOperationSlugSegment(lastPart));
|
|
6299
|
+
}
|
|
6300
|
+
if (operation.summary) leafCandidates.add(mintlifyOperationSlugSegment(operation.summary));
|
|
6301
|
+
if (operation.operationId) leafCandidates.add(mintlifyOperationSlugSegment(operation.operationId));
|
|
6302
|
+
leafCandidates.add(mintlifyOperationSlugSegment(`${operation.method} ${operation.path}`));
|
|
6303
|
+
const tagSegment = mintlifyOperationSlugSegment(operation.tag ?? "default");
|
|
6304
|
+
for (const leaf of leafCandidates) {
|
|
6305
|
+
if (!leaf) continue;
|
|
6306
|
+
const tagged = `${source.prefix}/${tagSegment}/${leaf}`.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
6307
|
+
const flat = `${source.prefix}/${leaf}`.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
6308
|
+
if (!map.has(tagged)) map.set(tagged, thallyHref);
|
|
6309
|
+
if (!map.has(flat)) map.set(flat, thallyHref);
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
});
|
|
6313
|
+
return { operationLinks: map, prefixLandings };
|
|
6314
|
+
}
|
|
6315
|
+
function rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings) {
|
|
6316
|
+
if (operationLinks.size === 0 && prefixLandings.size === 0) return;
|
|
6317
|
+
const unmatched = /* @__PURE__ */ new Set();
|
|
6318
|
+
for (const page of pages) {
|
|
6319
|
+
page.body = rewriteApiOperationLinks(page.body, operationLinks, prefixLandings, unmatched);
|
|
6320
|
+
}
|
|
6321
|
+
if (unmatched.size === 0) return;
|
|
6322
|
+
const sorted = [...unmatched].sort();
|
|
6323
|
+
warnings.push({
|
|
6324
|
+
code: "unsupported-config",
|
|
6325
|
+
message: `${sorted.length} link(s) to API endpoints matched no endpoint in the OpenAPI spec, so they now point to the API section's overview page instead: ${sorted.slice(0, 10).join(", ")}${sorted.length > 10 ? `, and ${sorted.length - 10} more` : ""}. Update those links if you want them to reach a specific endpoint.`
|
|
6326
|
+
});
|
|
6327
|
+
}
|
|
6328
|
+
function rewriteApiOperationLinks(body, linkMap, prefixLandings, unmatched) {
|
|
6329
|
+
if (linkMap.size === 0 && prefixLandings.size === 0) return body;
|
|
6330
|
+
const orderedPrefixes = [...prefixLandings.entries()].sort((a, b) => b[0].length - a[0].length);
|
|
6331
|
+
let codeFence = null;
|
|
6332
|
+
return body.split("\n").map((line) => {
|
|
6333
|
+
const fence = line.match(/^\s*(`{3,}|~{3,})/);
|
|
6334
|
+
if (fence) {
|
|
6335
|
+
if (!codeFence) codeFence = fence[1][0];
|
|
6336
|
+
else if (fence[1][0] === codeFence) codeFence = null;
|
|
6337
|
+
return line;
|
|
6338
|
+
}
|
|
6339
|
+
if (codeFence) return line;
|
|
6340
|
+
const rewriteTarget = (target) => {
|
|
6341
|
+
const suffixIndex = target.search(/[?#]/);
|
|
6342
|
+
const path = (suffixIndex >= 0 ? target.slice(0, suffixIndex) : target).replace(/^\/+/, "").toLowerCase();
|
|
6343
|
+
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
6344
|
+
const mapped = linkMap.get(path);
|
|
6345
|
+
if (mapped) return `${mapped}${suffix}`;
|
|
6346
|
+
for (const [prefix, landing] of orderedPrefixes) {
|
|
6347
|
+
if (path === prefix) return `${landing}${suffix}`;
|
|
6348
|
+
if (path.startsWith(`${prefix}/`)) {
|
|
6349
|
+
unmatched.add(target);
|
|
6350
|
+
return `${landing}${suffix}`;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
return target;
|
|
6354
|
+
};
|
|
6355
|
+
return line.replace(/(\]\()\/([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(`/${target}`)}`).replace(/(\bhref=")\/([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(`/${target}`)}${suffix}`);
|
|
6356
|
+
}).join("\n");
|
|
6357
|
+
}
|
|
5207
6358
|
var MAX_FERN_GENERATORS_BYTES = 2e6;
|
|
5208
6359
|
function fernGeneratorsOpenApiPaths(config) {
|
|
5209
6360
|
const api = config.api;
|
|
@@ -5333,18 +6484,18 @@ function interpolateSnippet(snippet, attributes, children) {
|
|
|
5333
6484
|
}).join("\n");
|
|
5334
6485
|
}
|
|
5335
6486
|
function snippetComponentBody(source, componentName) {
|
|
5336
|
-
const parsed =
|
|
6487
|
+
const parsed = ts3.createSourceFile("snippet.tsx", source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
5337
6488
|
for (const statement of parsed.statements) {
|
|
5338
|
-
if (!
|
|
6489
|
+
if (!ts3.isVariableStatement(statement)) continue;
|
|
5339
6490
|
for (const declaration of statement.declarationList.declarations) {
|
|
5340
|
-
if (!
|
|
6491
|
+
if (!ts3.isIdentifier(declaration.name) || declaration.name.text !== componentName) continue;
|
|
5341
6492
|
const initializer = declaration.initializer;
|
|
5342
|
-
if (!initializer || !
|
|
5343
|
-
const returned =
|
|
6493
|
+
if (!initializer || !ts3.isArrowFunction(initializer)) continue;
|
|
6494
|
+
const returned = ts3.isBlock(initializer.body) ? initializer.body.statements.find(ts3.isReturnStatement)?.expression : initializer.body;
|
|
5344
6495
|
if (!returned) continue;
|
|
5345
6496
|
let expression = returned;
|
|
5346
|
-
while (
|
|
5347
|
-
if (
|
|
6497
|
+
while (ts3.isParenthesizedExpression(expression)) expression = expression.expression;
|
|
6498
|
+
if (ts3.isJsxElement(expression) || ts3.isJsxSelfClosingElement(expression) || ts3.isJsxFragment(expression)) {
|
|
5348
6499
|
return expression.getText(parsed);
|
|
5349
6500
|
}
|
|
5350
6501
|
}
|
|
@@ -5352,30 +6503,31 @@ function snippetComponentBody(source, componentName) {
|
|
|
5352
6503
|
return source;
|
|
5353
6504
|
}
|
|
5354
6505
|
function statefulSnippetDeclaration(source, componentName) {
|
|
5355
|
-
const parsed =
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
6506
|
+
const parsed = ts3.createSourceFile("snippet.tsx", source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
6507
|
+
const isEsm = (statement2) => ts3.isImportDeclaration(statement2) || (ts3.isVariableStatement(statement2) || ts3.isFunctionDeclaration(statement2) || ts3.isClassDeclaration(statement2)) && !!ts3.getModifiers(statement2)?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword);
|
|
6508
|
+
if (parsed.statements.length === 0 || !parsed.statements.every(isEsm)) return null;
|
|
6509
|
+
const statement = parsed.statements.find((candidate) => ts3.isVariableStatement(candidate) && candidate.declarationList.declarations.length === 1 && ts3.isIdentifier(candidate.declarationList.declarations[0].name) && candidate.declarationList.declarations[0].name.text === componentName);
|
|
6510
|
+
if (!statement || !ts3.isVariableStatement(statement)) return null;
|
|
5359
6511
|
const declaration = statement.declarationList.declarations[0];
|
|
5360
|
-
if (!
|
|
6512
|
+
if (!declaration.initializer || !ts3.isArrowFunction(declaration.initializer) || !ts3.isBlock(declaration.initializer.body)) return null;
|
|
5361
6513
|
const body = declaration.initializer.body.statements;
|
|
5362
|
-
if (body.length < 2 || !body.some(
|
|
6514
|
+
if (body.length < 2 || !body.some(ts3.isReturnStatement)) return null;
|
|
5363
6515
|
return source.trim();
|
|
5364
6516
|
}
|
|
5365
6517
|
function staticNamedSnippetValues(source) {
|
|
5366
6518
|
const values = /* @__PURE__ */ new Map();
|
|
5367
|
-
const parsed =
|
|
6519
|
+
const parsed = ts3.createSourceFile("snippet.tsx", source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
5368
6520
|
for (const statement of parsed.statements) {
|
|
5369
|
-
if (!
|
|
6521
|
+
if (!ts3.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword)) continue;
|
|
5370
6522
|
for (const declaration of statement.declarationList.declarations) {
|
|
5371
|
-
if (!
|
|
6523
|
+
if (!ts3.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
5372
6524
|
const expression = declaration.initializer;
|
|
5373
|
-
if (
|
|
6525
|
+
if (ts3.isStringLiteral(expression) || ts3.isNoSubstitutionTemplateLiteral(expression)) {
|
|
5374
6526
|
values.set(declaration.name.text, JSON.stringify(expression.text));
|
|
5375
|
-
} else if (
|
|
6527
|
+
} else if (ts3.isNumericLiteral(expression)) {
|
|
5376
6528
|
values.set(declaration.name.text, expression.text);
|
|
5377
|
-
} else if (expression.kind ===
|
|
5378
|
-
values.set(declaration.name.text, expression.kind ===
|
|
6529
|
+
} else if (expression.kind === ts3.SyntaxKind.TrueKeyword || expression.kind === ts3.SyntaxKind.FalseKeyword) {
|
|
6530
|
+
values.set(declaration.name.text, expression.kind === ts3.SyntaxKind.TrueKeyword ? "true" : "false");
|
|
5379
6531
|
}
|
|
5380
6532
|
}
|
|
5381
6533
|
}
|
|
@@ -5791,8 +6943,17 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
5791
6943
|
result = result.replace(SNIPPET_TAG_PATTERN, (_tag, doubleQuoted, singleQuoted) => {
|
|
5792
6944
|
const filePath = doubleQuoted ?? singleQuoted;
|
|
5793
6945
|
try {
|
|
5794
|
-
const candidate =
|
|
5795
|
-
|
|
6946
|
+
const candidate = [
|
|
6947
|
+
() => resolveWithin(siteRoot, `snippets/${filePath}`),
|
|
6948
|
+
() => resolveSnippetPath(filePath, currentFile, repositoryRoot, siteRoot)
|
|
6949
|
+
].map((resolveCandidate) => {
|
|
6950
|
+
try {
|
|
6951
|
+
return resolveCandidate();
|
|
6952
|
+
} catch {
|
|
6953
|
+
return void 0;
|
|
6954
|
+
}
|
|
6955
|
+
}).find((path) => path !== void 0 && existsSync5(path) && lstatSync5(path).isFile());
|
|
6956
|
+
if (!candidate) throw new Error("file not found");
|
|
5796
6957
|
return inlineMdxSnippets(
|
|
5797
6958
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
5798
6959
|
candidate,
|
|
@@ -5814,23 +6975,33 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
5814
6975
|
if (preservedDeclarations.size > 0) {
|
|
5815
6976
|
const frontmatter = result.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
5816
6977
|
const prefix = frontmatter?.[0] ?? "";
|
|
5817
|
-
result = `${prefix}${[...preservedDeclarations.values()].join("\n\n")}
|
|
6978
|
+
result = `${prefix}${[...new Set(preservedDeclarations.values())].join("\n\n")}
|
|
5818
6979
|
|
|
5819
6980
|
${result.slice(prefix.length)}`;
|
|
5820
6981
|
}
|
|
5821
6982
|
return depth === 0 ? hoistMdxImports(result) : result;
|
|
5822
6983
|
}
|
|
5823
|
-
function injectOpenApiSpecs(config, specs) {
|
|
6984
|
+
function injectOpenApiSpecs(config, specs, warnings) {
|
|
5824
6985
|
let tabs = config.tabs.map((tab) => ({ ...tab }));
|
|
5825
6986
|
for (const spec of specs) {
|
|
5826
6987
|
const apiTab = spec.tabLabel ? tabs.find((tab) => tab.tab === spec.tabLabel) : tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
5827
|
-
if (apiTab)
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
6988
|
+
if (apiTab) {
|
|
6989
|
+
if (apiTab.api && warnings) {
|
|
6990
|
+
warnings.push({
|
|
6991
|
+
code: "unsupported-config",
|
|
6992
|
+
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "/${spec.filename}" was not added. Put it in its own tab to include it.`
|
|
6993
|
+
});
|
|
6994
|
+
continue;
|
|
6995
|
+
}
|
|
6996
|
+
apiTab.api = {
|
|
6997
|
+
source: `/${spec.filename}`,
|
|
6998
|
+
// An API-only tab needs generated endpoint navigation; an authored
|
|
6999
|
+
// page tab keeps its own groups alongside the bound spec.
|
|
7000
|
+
...apiTab.groups?.length || apiTab.pages?.length ? { navigation: false } : {}
|
|
7001
|
+
};
|
|
7002
|
+
} else {
|
|
7003
|
+
tabs = [...tabs, { tab: spec.tabLabel ?? "API Reference", api: { source: `/${spec.filename}` } }];
|
|
7004
|
+
}
|
|
5834
7005
|
}
|
|
5835
7006
|
return { ...config, tabs };
|
|
5836
7007
|
}
|
|
@@ -5870,6 +7041,8 @@ function migrateRepository(options) {
|
|
|
5870
7041
|
const componentRoot = mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir;
|
|
5871
7042
|
const componentMigrator = platform === "mintlify" || platform === "docusaurus" || platform === "fern" ? createComponentMigrator(componentRoot, repositoryDir, warnings, componentSourceIdentity(options.sourceUrl, repositoryDir, componentRoot)) : void 0;
|
|
5872
7043
|
let docsConfig = { tabs: [] };
|
|
7044
|
+
let defaultVersionPrefixes = /* @__PURE__ */ new Set();
|
|
7045
|
+
let allVersionPrefixes = /* @__PURE__ */ new Set();
|
|
5873
7046
|
const referenceMap = /* @__PURE__ */ new Map();
|
|
5874
7047
|
const exactReferenceMap = /* @__PURE__ */ new Map();
|
|
5875
7048
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
@@ -5879,6 +7052,7 @@ function migrateRepository(options) {
|
|
|
5879
7052
|
let fernBasePath = "";
|
|
5880
7053
|
let fernVersionPath = "";
|
|
5881
7054
|
let fernApiSections = [];
|
|
7055
|
+
const fernExternalSourcePaths = /* @__PURE__ */ new Set();
|
|
5882
7056
|
let fernChangelogIndexes = [];
|
|
5883
7057
|
const fernReferencedPaths = /* @__PURE__ */ new Set();
|
|
5884
7058
|
const fernSourceLinkAliases = /* @__PURE__ */ new Map();
|
|
@@ -5889,6 +7063,8 @@ function migrateRepository(options) {
|
|
|
5889
7063
|
const config = readMintlifyConfig(mintlifyProjectRoot ?? repositoryDir);
|
|
5890
7064
|
if (config) {
|
|
5891
7065
|
mintlifyConfig = config;
|
|
7066
|
+
defaultVersionPrefixes = mintlifyDefaultVersionPrefixes(config);
|
|
7067
|
+
allVersionPrefixes = mintlifyAllVersionPrefixes(config);
|
|
5892
7068
|
const projected = projectMintlifyNavigation(config);
|
|
5893
7069
|
docsConfig = projected.docsConfig;
|
|
5894
7070
|
warnings.push(...projected.warnings);
|
|
@@ -5939,6 +7115,7 @@ function migrateRepository(options) {
|
|
|
5939
7115
|
const exactKey = exactReferenceKey(descriptor.sourcePath);
|
|
5940
7116
|
if (!exactReferenceMap.has(exactKey)) exactReferenceMap.set(exactKey, { navigationId: descriptor.navigationId });
|
|
5941
7117
|
if (!referenceOrder.has(key)) referenceOrder.set(key, index);
|
|
7118
|
+
if (descriptor.sourcePath.startsWith("../")) fernExternalSourcePaths.add(descriptor.sourcePath);
|
|
5942
7119
|
}
|
|
5943
7120
|
}
|
|
5944
7121
|
} catch (error) {
|
|
@@ -5963,15 +7140,35 @@ function migrateRepository(options) {
|
|
|
5963
7140
|
throw new Error(`Documentation directory does not exist: ${configuredDocsDir || "."}`);
|
|
5964
7141
|
}
|
|
5965
7142
|
const mintignoreMatcher = platform === "mintlify" && mintlifyProjectRoot ? readMintignoreMatcher(mintlifyProjectRoot) : null;
|
|
5966
|
-
const
|
|
7143
|
+
const lastReferencedIndexByVersionLocale = /* @__PURE__ */ new Map();
|
|
7144
|
+
for (const [key, index] of referenceOrder) {
|
|
7145
|
+
const blockKey = key.split("/").slice(0, 2).join("/");
|
|
7146
|
+
const current = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
7147
|
+
if (current === void 0 || index > current) lastReferencedIndexByVersionLocale.set(blockKey, index);
|
|
7148
|
+
}
|
|
7149
|
+
const discoveryRank = referenceOrder.size > 0 ? (relativePath) => {
|
|
7150
|
+
const referencedIndex = referenceOrder.get(normalizedReferenceKey(relativePath));
|
|
7151
|
+
if (referencedIndex !== void 0) return referencedIndex;
|
|
7152
|
+
const blockKey = relativePath.split("/").slice(0, 2).join("/");
|
|
7153
|
+
const versionCeiling = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
7154
|
+
return versionCeiling !== void 0 ? versionCeiling + 1 : Number.MAX_SAFE_INTEGER;
|
|
7155
|
+
} : void 0;
|
|
7156
|
+
const scannedFiles = scanFiles(contentRoot, repositoryDir, warnings, discoveryRank);
|
|
7157
|
+
const mintignoreFilteredFiles = mintignoreMatcher ? scannedFiles.filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scannedFiles;
|
|
7158
|
+
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length > MAX_SOURCE_FILES;
|
|
7159
|
+
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes) : mintignoreFilteredFiles;
|
|
5967
7160
|
if (platform === "fern" && fernProjectRoot) {
|
|
5968
|
-
const
|
|
5969
|
-
for (const sourcePath of
|
|
7161
|
+
const discovered2 = new Set(files.map((file) => file.relativePath));
|
|
7162
|
+
for (const sourcePath of fernExternalSourcePaths) {
|
|
7163
|
+
if (discovered2.has(sourcePath)) continue;
|
|
5970
7164
|
try {
|
|
5971
7165
|
const absolutePath = resolveWithinRoot(fernProjectRoot, sourcePath, repositoryDir);
|
|
5972
|
-
if (
|
|
5973
|
-
|
|
5974
|
-
|
|
7166
|
+
if (existsSync5(absolutePath) && lstatSync5(absolutePath).isFile()) {
|
|
7167
|
+
files.push({ absolutePath, relativePath: sourcePath });
|
|
7168
|
+
discovered2.add(sourcePath);
|
|
7169
|
+
} else {
|
|
7170
|
+
warnings.push({ code: "missing-page", message: "A Fern navigation path could not be read within this repository.", source: sourcePath });
|
|
7171
|
+
}
|
|
5975
7172
|
} catch {
|
|
5976
7173
|
warnings.push({ code: "missing-page", message: "A Fern navigation path could not be read within this repository.", source: sourcePath });
|
|
5977
7174
|
}
|
|
@@ -6057,6 +7254,9 @@ function migrateRepository(options) {
|
|
|
6057
7254
|
mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir,
|
|
6058
7255
|
snippetAliases
|
|
6059
7256
|
);
|
|
7257
|
+
if (platform === "fern" || platform === "mintlify" || platform === "docusaurus") {
|
|
7258
|
+
raw = normalizeExplicitHeadingIds(raw);
|
|
7259
|
+
}
|
|
6060
7260
|
if (componentMigrator) {
|
|
6061
7261
|
const warningsBeforeTransform = warnings.length;
|
|
6062
7262
|
raw = componentMigrator.transform(raw, file.absolutePath);
|
|
@@ -6132,6 +7332,13 @@ function migrateRepository(options) {
|
|
|
6132
7332
|
skipped++;
|
|
6133
7333
|
continue;
|
|
6134
7334
|
}
|
|
7335
|
+
if (page.frontmatterError) {
|
|
7336
|
+
warnings.push({
|
|
7337
|
+
code: "unsupported-config",
|
|
7338
|
+
message: `The page's frontmatter is not valid YAML (${page.frontmatterError}); the page was kept and as much of its metadata as could be read was used.`,
|
|
7339
|
+
source: relative5(repositoryDir, file.absolutePath).replace(/\\/g, "/")
|
|
7340
|
+
});
|
|
7341
|
+
}
|
|
6135
7342
|
if (platform === "fern" && !page.navTitle) {
|
|
6136
7343
|
const navTitle = fernNavTitles.get(navigationId);
|
|
6137
7344
|
if (navTitle && navTitle !== page.title) page.navTitle = navTitle;
|
|
@@ -6172,9 +7379,9 @@ function migrateRepository(options) {
|
|
|
6172
7379
|
});
|
|
6173
7380
|
continue;
|
|
6174
7381
|
}
|
|
6175
|
-
const
|
|
6176
|
-
if (hasAnyFunctionValuedProp(page.body,
|
|
6177
|
-
if (propsTargetExtractedClientComponent(page.body,
|
|
7382
|
+
const declaredNames2 = functionDeclaredNames(page.body);
|
|
7383
|
+
if (hasAnyFunctionValuedProp(page.body, declaredNames2)) {
|
|
7384
|
+
if (propsTargetExtractedClientComponent(page.body, declaredNames2)) {
|
|
6178
7385
|
skipped++;
|
|
6179
7386
|
warnings.push({
|
|
6180
7387
|
code: "skipped-file",
|
|
@@ -6211,6 +7418,13 @@ function migrateRepository(options) {
|
|
|
6211
7418
|
if (sourcePath !== page.id && /^[A-Za-z0-9_./-]+$/.test(sourcePath)) {
|
|
6212
7419
|
routeAliases.push({ source: `/${sourcePath}`, destination: `/${page.id}`, permanent: false });
|
|
6213
7420
|
}
|
|
7421
|
+
const versionPrefix = [...defaultVersionPrefixes].find((prefix) => sourcePath === prefix || sourcePath.startsWith(`${prefix}/`));
|
|
7422
|
+
if (versionPrefix) {
|
|
7423
|
+
const unversioned = sourcePath.slice(versionPrefix.length).replace(/^\/+/, "");
|
|
7424
|
+
if (unversioned && unversioned !== sourcePath && unversioned !== page.id && /^[A-Za-z0-9_./-]+$/.test(unversioned) && !routeAliases.some((redirect) => redirect.source === `/${unversioned}`)) {
|
|
7425
|
+
routeAliases.push({ source: `/${unversioned}`, destination: `/${page.id}`, permanent: false });
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
6214
7428
|
}
|
|
6215
7429
|
if (docusaurusDescriptor) {
|
|
6216
7430
|
docusaurusDescriptors.push({ ...docusaurusDescriptor, title: page.title });
|
|
@@ -6289,7 +7503,7 @@ function migrateRepository(options) {
|
|
|
6289
7503
|
const referencingPages = referencedAssetPaths.get(assetPath);
|
|
6290
7504
|
warnings.push({
|
|
6291
7505
|
code: "limit-reached",
|
|
6292
|
-
message:
|
|
7506
|
+
message: "This file was not copied because it is larger than 25 MB or the copied files would exceed 500 MB in total. Add it to public/ manually" + (referencingPages ? `; it is used by ${[...referencingPages].slice(0, 3).join(", ")}${referencingPages.size > 3 ? `, and ${referencingPages.size - 3} more` : ""}.` : "."),
|
|
6293
7507
|
source: file.relativePath
|
|
6294
7508
|
});
|
|
6295
7509
|
continue;
|
|
@@ -6306,8 +7520,8 @@ function migrateRepository(options) {
|
|
|
6306
7520
|
assets.push({ path: assetPath, content });
|
|
6307
7521
|
totalAssetBytes += size;
|
|
6308
7522
|
}
|
|
6309
|
-
if (files.length >= MAX_SOURCE_FILES) {
|
|
6310
|
-
warnings.push({ code: "limit-reached", message: `
|
|
7523
|
+
if (!discoveryBudgetApplied && files.length >= MAX_SOURCE_FILES) {
|
|
7524
|
+
warnings.push({ code: "limit-reached", message: `Stopped scanning after ${MAX_SOURCE_FILES} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.` });
|
|
6311
7525
|
}
|
|
6312
7526
|
if (platform === "docusaurus") {
|
|
6313
7527
|
const projected = projectDocusaurusNavigation({
|
|
@@ -6393,10 +7607,11 @@ function migrateRepository(options) {
|
|
|
6393
7607
|
filename = `${prefix}${filename}`;
|
|
6394
7608
|
}
|
|
6395
7609
|
specFilenameSources.set(filename, spec.absolutePath);
|
|
7610
|
+
const specContent = readFileSync5(spec.absolutePath);
|
|
6396
7611
|
if (!assets.some((asset) => asset.path === filename)) {
|
|
6397
|
-
assets.push({ path: filename, content:
|
|
7612
|
+
assets.push({ path: filename, content: specContent });
|
|
6398
7613
|
}
|
|
6399
|
-
resolvedSpecs.push({ filename, tabLabel: section.tabLabel });
|
|
7614
|
+
resolvedSpecs.push({ filename, tabLabel: section.tabLabel, content: specContent, routeSegments: section.routeSegments ?? [] });
|
|
6400
7615
|
continue;
|
|
6401
7616
|
}
|
|
6402
7617
|
if (resolution.unsupported.length > 0) {
|
|
@@ -6424,7 +7639,14 @@ function migrateRepository(options) {
|
|
|
6424
7639
|
message: checked.length ? `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API. Checked: ${checked.join(", ")}. Point generators.yml at a spec file that exists.` : `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API, because its api-name is not a valid folder name. Add the spec manually.`
|
|
6425
7640
|
});
|
|
6426
7641
|
}
|
|
6427
|
-
if (resolvedSpecs.length > 0)
|
|
7642
|
+
if (resolvedSpecs.length > 0) {
|
|
7643
|
+
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
7644
|
+
const { operationLinks, prefixLandings } = apiOperationLinkMap(
|
|
7645
|
+
resolvedSpecs.map((spec) => ({ filename: spec.filename, content: spec.content, prefix: spec.routeSegments.join("/") })),
|
|
7646
|
+
docsConfig
|
|
7647
|
+
);
|
|
7648
|
+
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
7649
|
+
}
|
|
6428
7650
|
} else if (platform === "mintlify") {
|
|
6429
7651
|
const resolvedSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs);
|
|
6430
7652
|
for (const spec of resolvedSpecs) {
|
|
@@ -6433,7 +7655,12 @@ function migrateRepository(options) {
|
|
|
6433
7655
|
}
|
|
6434
7656
|
}
|
|
6435
7657
|
if (resolvedSpecs.length > 0) {
|
|
6436
|
-
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs);
|
|
7658
|
+
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
7659
|
+
const { operationLinks, prefixLandings } = apiOperationLinkMap(
|
|
7660
|
+
resolvedSpecs.map((spec) => ({ filename: spec.filename, content: spec.content, prefix: spec.directory })),
|
|
7661
|
+
docsConfig
|
|
7662
|
+
);
|
|
7663
|
+
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
6437
7664
|
} else {
|
|
6438
7665
|
const fallback = findOpenApi(files);
|
|
6439
7666
|
if (fallback) {
|
|
@@ -6466,7 +7693,7 @@ function migrateRepository(options) {
|
|
|
6466
7693
|
];
|
|
6467
7694
|
for (const plugin of additionalRoots) {
|
|
6468
7695
|
if (discovered >= MAX_SOURCE_FILES) {
|
|
6469
|
-
warnings.push({ code: "limit-reached", message: `Docusaurus docs
|
|
7696
|
+
warnings.push({ code: "limit-reached", message: `Stopped importing Docusaurus docs plugins after ${MAX_SOURCE_FILES} files; the remaining plugin docs were not migrated.` });
|
|
6470
7697
|
break;
|
|
6471
7698
|
}
|
|
6472
7699
|
if (plugin.docsDir === configuredDocsDir) continue;
|
|
@@ -6957,6 +8184,7 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
6957
8184
|
"Card",
|
|
6958
8185
|
"CardGroup",
|
|
6959
8186
|
"Check",
|
|
8187
|
+
"CodeBlock",
|
|
6960
8188
|
"CodeGroup",
|
|
6961
8189
|
"Color",
|
|
6962
8190
|
"Column",
|
|
@@ -6967,6 +8195,7 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
6967
8195
|
"Expandable",
|
|
6968
8196
|
"Agent",
|
|
6969
8197
|
"AgentPrompt",
|
|
8198
|
+
"Banner",
|
|
6970
8199
|
"BannerPreview",
|
|
6971
8200
|
"Embed",
|
|
6972
8201
|
"File",
|
|
@@ -6983,6 +8212,8 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
6983
8212
|
"InlineResponseExample",
|
|
6984
8213
|
"Latex",
|
|
6985
8214
|
"LegacyView",
|
|
8215
|
+
"LiteYouTubeEmbed",
|
|
8216
|
+
"MDX",
|
|
6986
8217
|
"Mermaid",
|
|
6987
8218
|
"Note",
|
|
6988
8219
|
"Panel",
|
|
@@ -7006,9 +8237,11 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
7006
8237
|
"Tooltip",
|
|
7007
8238
|
"Tree",
|
|
7008
8239
|
"Update",
|
|
8240
|
+
"Video",
|
|
7009
8241
|
"View",
|
|
7010
8242
|
"Visibility",
|
|
7011
|
-
"Warning"
|
|
8243
|
+
"Warning",
|
|
8244
|
+
"YouTube"
|
|
7012
8245
|
]);
|
|
7013
8246
|
var LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
7014
8247
|
"ar",
|