@thallylabs/migrate 0.2.21 → 0.2.22
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/dist/index.d.ts +16 -0
- package/dist/index.js +345 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/repository.ts
|
|
2
2
|
import { compileSync as compileSync2 } from "@mdx-js/mdx";
|
|
3
|
+
import { createHash as createHash2 } from "crypto";
|
|
3
4
|
import { spawn } from "child_process";
|
|
4
5
|
import {
|
|
5
6
|
closeSync,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
import { createRequire } from "module";
|
|
17
18
|
import { basename as basename3, dirname as dirname4, extname as extname6, isAbsolute as isAbsolute2, posix as posix3, relative as relative5, resolve as resolvePath, sep as sep2 } from "path";
|
|
18
19
|
import { parse as parseYaml4 } from "yaml";
|
|
19
|
-
import * as
|
|
20
|
+
import * as ts4 from "typescript";
|
|
20
21
|
|
|
21
22
|
// src/components.ts
|
|
22
23
|
import { createHash } from "crypto";
|
|
@@ -428,6 +429,9 @@ function bracketDelta(line) {
|
|
|
428
429
|
return delta;
|
|
429
430
|
}
|
|
430
431
|
var ESM_OPEN = /^(?:import|export)\b/;
|
|
432
|
+
function latexElement(source, block = false) {
|
|
433
|
+
return `<Latex${block ? " block" : ""}>{${JSON.stringify(source)}}</Latex>`;
|
|
434
|
+
}
|
|
431
435
|
function protectMathBlocks(raw) {
|
|
432
436
|
const { front, body } = splitFrontmatterBlock(raw);
|
|
433
437
|
const lines = body.split(/\r\n|\r|\n/);
|
|
@@ -442,7 +446,7 @@ function protectMathBlocks(raw) {
|
|
|
442
446
|
const trimmed = line.trim();
|
|
443
447
|
if (mathBlockDelimiter) {
|
|
444
448
|
if (trimmed === mathBlockDelimiter) {
|
|
445
|
-
output.push("
|
|
449
|
+
output.push(latexElement(mathLines.join("\n"), true));
|
|
446
450
|
mathBlockDelimiter = null;
|
|
447
451
|
mathLines = [];
|
|
448
452
|
converted = true;
|
|
@@ -484,11 +488,11 @@ function protectMathBlocks(raw) {
|
|
|
484
488
|
(whole, block, inline) => {
|
|
485
489
|
if (block !== void 0) {
|
|
486
490
|
converted = true;
|
|
487
|
-
return
|
|
491
|
+
return latexElement(unmask(block));
|
|
488
492
|
}
|
|
489
493
|
if (inline !== void 0 && hasTexSignal(unmask(inline))) {
|
|
490
494
|
converted = true;
|
|
491
|
-
return
|
|
495
|
+
return latexElement(unmask(inline));
|
|
492
496
|
}
|
|
493
497
|
return whole;
|
|
494
498
|
}
|
|
@@ -1222,6 +1226,32 @@ function injectReactHookImports(body) {
|
|
|
1222
1226
|
|
|
1223
1227
|
${body}` : body;
|
|
1224
1228
|
}
|
|
1229
|
+
function normalizeFlowParagraphContainers(body) {
|
|
1230
|
+
if (!body.includes("<p")) return body;
|
|
1231
|
+
let tree;
|
|
1232
|
+
try {
|
|
1233
|
+
tree = descriptionParser.parse(body);
|
|
1234
|
+
} catch {
|
|
1235
|
+
return body;
|
|
1236
|
+
}
|
|
1237
|
+
const edits = [];
|
|
1238
|
+
function visit(node) {
|
|
1239
|
+
if (node.type === "mdxJsxFlowElement" && node.name === "p" && node.children?.some((child) => child.type === "paragraph" || child.type === "list" || child.type === "mdxJsxFlowElement")) {
|
|
1240
|
+
const start = node.position?.start.offset;
|
|
1241
|
+
const end = node.position?.end.offset;
|
|
1242
|
+
if (start !== void 0 && end !== void 0 && body.slice(start, start + 2) === "<p") {
|
|
1243
|
+
const closing = body.slice(start, end).match(/<\/p\s*>\s*$/i);
|
|
1244
|
+
if (closing?.index !== void 0) {
|
|
1245
|
+
edits.push({ offset: start + 1, value: "div" });
|
|
1246
|
+
edits.push({ offset: start + closing.index + 2, value: "div" });
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
for (const child of node.children ?? []) visit(child);
|
|
1251
|
+
}
|
|
1252
|
+
visit(tree);
|
|
1253
|
+
return edits.sort((a, b) => b.offset - a.offset).reduce((text2, edit) => text2.slice(0, edit.offset) + edit.value + text2.slice(edit.offset + 1), body);
|
|
1254
|
+
}
|
|
1225
1255
|
function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
1226
1256
|
const runFern = platform === "fern";
|
|
1227
1257
|
const runMintlify = platform === "mintlify";
|
|
@@ -1233,6 +1263,13 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
1233
1263
|
/:([+\w-]+):/g,
|
|
1234
1264
|
(shortcode, name) => nameToEmoji[name] ?? shortcode
|
|
1235
1265
|
));
|
|
1266
|
+
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
1267
|
+
/\b(href|src)=\{\s*require\(\s*(['"])@docusaurus\/useBaseUrl\2\s*\)\.default\(\s*(['"])([^'"]+)\3\s*\)\s*\}/g,
|
|
1268
|
+
(original, attribute, _moduleQuote, _pathQuote, path) => {
|
|
1269
|
+
if (!/^[\w./~%-]+$/.test(path) || path.split("/").includes("..")) return original;
|
|
1270
|
+
return `${attribute}="/${path.replace(/^\.\//, "").replace(/^\/+/, "")}"`;
|
|
1271
|
+
}
|
|
1272
|
+
));
|
|
1236
1273
|
rewritten = normalizeDocusaurusTabBlocks(normalizeDocusaurusTabs(removeGlobalDocusaurusImports(rewritten)));
|
|
1237
1274
|
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
1238
1275
|
/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@site\/[^'"]+)\2\s*;?/g,
|
|
@@ -1269,7 +1306,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
1269
1306
|
return normalizeMdx(body, platform, false);
|
|
1270
1307
|
}
|
|
1271
1308
|
}
|
|
1272
|
-
return normalized;
|
|
1309
|
+
return normalizeFlowParagraphContainers(normalized);
|
|
1273
1310
|
}
|
|
1274
1311
|
function parseMarkdownPage(input) {
|
|
1275
1312
|
const parsed = parseFrontmatter(input.raw);
|
|
@@ -2205,26 +2242,33 @@ function hash(value) {
|
|
|
2205
2242
|
function portableSpecifier(path) {
|
|
2206
2243
|
return path.replace(/\.tsx?$/, "");
|
|
2207
2244
|
}
|
|
2208
|
-
function
|
|
2245
|
+
function sanitizeCssModuleSelectors(css) {
|
|
2209
2246
|
const root = postcss.parse(css);
|
|
2247
|
+
let omitted = 0;
|
|
2210
2248
|
root.walkRules((rule) => {
|
|
2211
|
-
rule.
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
if (
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2249
|
+
if (rule.parent?.type === "atrule" && /keyframes$/i.test(rule.parent.name)) return;
|
|
2250
|
+
if (/^:export$|^:import\(/.test(rule.selector.trim())) return;
|
|
2251
|
+
const selectors = selectorParser().astSync(rule.selector);
|
|
2252
|
+
selectors.each((selector) => {
|
|
2253
|
+
let hasLocalClassOrId = false;
|
|
2254
|
+
selector.walk((node) => {
|
|
2255
|
+
if (node.type !== "class" && node.type !== "id") return;
|
|
2256
|
+
let parent = node.parent;
|
|
2257
|
+
while (parent) {
|
|
2258
|
+
if (parent.type === "pseudo" && parent.value === ":global") return;
|
|
2259
|
+
parent = parent.parent;
|
|
2260
|
+
}
|
|
2261
|
+
hasLocalClassOrId = true;
|
|
2224
2262
|
});
|
|
2225
|
-
|
|
2263
|
+
if (!hasLocalClassOrId) {
|
|
2264
|
+
selector.remove();
|
|
2265
|
+
omitted++;
|
|
2266
|
+
}
|
|
2267
|
+
});
|
|
2268
|
+
if (selectors.nodes.length === 0) rule.remove();
|
|
2269
|
+
else rule.selector = selectors.toString();
|
|
2226
2270
|
});
|
|
2227
|
-
return root.toString();
|
|
2271
|
+
return { css: root.toString(), omitted };
|
|
2228
2272
|
}
|
|
2229
2273
|
function sourceFile(source, filename) {
|
|
2230
2274
|
return ts2.createSourceFile(
|
|
@@ -2455,6 +2499,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2455
2499
|
}
|
|
2456
2500
|
function copyGraph(entry) {
|
|
2457
2501
|
const staged = /* @__PURE__ */ new Map();
|
|
2502
|
+
const stagedWarnings = [];
|
|
2458
2503
|
let stagedBytes = 0;
|
|
2459
2504
|
function stageShim(filename, content) {
|
|
2460
2505
|
const shimDestination = `${destinationRoot}/shims/${filename}`;
|
|
@@ -2475,13 +2520,14 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2475
2520
|
if (!CODE_EXTENSIONS.has(extension)) {
|
|
2476
2521
|
if (extension === ".css" && /@import\b|url\s*\(/i.test(content.toString("utf8"))) throw new Error("CSS resource dependencies require manual migration");
|
|
2477
2522
|
if (extension === ".css" && path.toLowerCase().endsWith(".module.css")) {
|
|
2478
|
-
let
|
|
2523
|
+
let sanitized;
|
|
2479
2524
|
try {
|
|
2480
|
-
|
|
2525
|
+
sanitized = sanitizeCssModuleSelectors(content.toString("utf8"));
|
|
2481
2526
|
} catch {
|
|
2482
2527
|
throw new Error("CSS module could not be parsed to check for Turbopack-safe selectors");
|
|
2483
2528
|
}
|
|
2484
|
-
|
|
2529
|
+
if (sanitized.omitted > 0) stagedWarnings.push({ path, omitted: sanitized.omitted });
|
|
2530
|
+
staged.set(destination, { path: destination, content: sanitized.css });
|
|
2485
2531
|
}
|
|
2486
2532
|
return;
|
|
2487
2533
|
}
|
|
@@ -2550,6 +2596,9 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2550
2596
|
}
|
|
2551
2597
|
visit(entry);
|
|
2552
2598
|
for (const [path, file] of staged) copied.set(path, file);
|
|
2599
|
+
for (const { path, omitted } of stagedWarnings) {
|
|
2600
|
+
warn(`Site-authored CSS module ${relative2(root, path).replace(/\\/g, "/")} has ${omitted} global-only selector branch(es) that cannot be copied into Thally CSS Modules; those styles were omitted.`, path);
|
|
2601
|
+
}
|
|
2553
2602
|
copiedBytes += stagedBytes;
|
|
2554
2603
|
return outputPath(entry);
|
|
2555
2604
|
}
|
|
@@ -2803,7 +2852,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2803
2852
|
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);
|
|
2804
2853
|
} else {
|
|
2805
2854
|
edits.push({ start: importStart, end: importEnd, value: "" });
|
|
2806
|
-
warn(`
|
|
2855
|
+
warn(`Site-authored component import ${JSON.stringify(rawSpecifier)} could not be copied and was removed: ${error instanceof Error ? error.message : "unsupported dependency"}. Its usage on this page was neutralized; review this custom implementation manually.`, currentFile);
|
|
2807
2856
|
}
|
|
2808
2857
|
}
|
|
2809
2858
|
}
|
|
@@ -3133,6 +3182,110 @@ import { customComponents as ${name} } from ${JSON.stringify(registryPath)}
|
|
|
3133
3182
|
];
|
|
3134
3183
|
}
|
|
3135
3184
|
|
|
3185
|
+
// src/source-styles.ts
|
|
3186
|
+
import postcss2 from "postcss";
|
|
3187
|
+
import selectorParser2 from "postcss-selector-parser";
|
|
3188
|
+
import ts3 from "typescript";
|
|
3189
|
+
var PLATFORM_CLASSES = /* @__PURE__ */ new Set(["navbar-link"]);
|
|
3190
|
+
var PLATFORM_IDS = /* @__PURE__ */ new Set(["footer", "navbar", "sidebar"]);
|
|
3191
|
+
var PLATFORM_CLASS_PREFIX = /^(?:mintlify[-_]|fern[-_]|docusaurus[-_]|theme-doc-|theme-code-block|DocSearch|pagination-nav|menu__)/i;
|
|
3192
|
+
var SHARED_THEME_CLASSES = /* @__PURE__ */ new Set(["dark", "light"]);
|
|
3193
|
+
var UTILITY_CLASS = /^(?:[mp][trblxy]?|w|h|min-w|max-w|min-h|max-h|text|bg|border|rounded|flex|grid|gap|space-[xy]|items|justify|overflow|truncate|hidden|block|inline|relative|absolute|sticky|z|opacity)-/;
|
|
3194
|
+
function withoutFencedCode(content) {
|
|
3195
|
+
let fence;
|
|
3196
|
+
return content.split("\n").map((line) => {
|
|
3197
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/)?.[1];
|
|
3198
|
+
if (fence) {
|
|
3199
|
+
if (marker?.[0] === fence.marker && marker.length >= fence.length) fence = void 0;
|
|
3200
|
+
return "";
|
|
3201
|
+
}
|
|
3202
|
+
if (marker) {
|
|
3203
|
+
fence = { marker: marker[0], length: marker.length };
|
|
3204
|
+
return "";
|
|
3205
|
+
}
|
|
3206
|
+
return line;
|
|
3207
|
+
}).join("\n");
|
|
3208
|
+
}
|
|
3209
|
+
function authoredStyleNames(contents) {
|
|
3210
|
+
const classes = /* @__PURE__ */ new Set();
|
|
3211
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3212
|
+
for (const original of contents) {
|
|
3213
|
+
const content = withoutFencedCode(original);
|
|
3214
|
+
for (const match of content.matchAll(/\b(?:className|class)\s*=\s*["']([^"']+)["']/g)) {
|
|
3215
|
+
for (const name of match[1].split(/\s+/)) if (name) classes.add(name);
|
|
3216
|
+
}
|
|
3217
|
+
for (const match of content.matchAll(/\bid\s*=\s*["']([^"']+)["']/g)) ids.add(match[1]);
|
|
3218
|
+
if (!content.includes("className={")) continue;
|
|
3219
|
+
const source = ts3.createSourceFile("component.tsx", content, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
3220
|
+
const visit = (node) => {
|
|
3221
|
+
if (ts3.isJsxAttribute(node) && ts3.isIdentifier(node.name) && node.name.text === "className" && node.initializer) {
|
|
3222
|
+
const collect = (value) => {
|
|
3223
|
+
if (ts3.isStringLiteral(value) || ts3.isNoSubstitutionTemplateLiteral(value)) {
|
|
3224
|
+
for (const name of value.text.split(/\s+/)) if (name) classes.add(name);
|
|
3225
|
+
} else ts3.forEachChild(value, collect);
|
|
3226
|
+
};
|
|
3227
|
+
collect(node.initializer);
|
|
3228
|
+
}
|
|
3229
|
+
ts3.forEachChild(node, visit);
|
|
3230
|
+
};
|
|
3231
|
+
visit(source);
|
|
3232
|
+
}
|
|
3233
|
+
return { classes, ids };
|
|
3234
|
+
}
|
|
3235
|
+
function projectAuthoredStyles(css, contents) {
|
|
3236
|
+
const names = authoredStyleNames(contents);
|
|
3237
|
+
const root = postcss2.parse(css);
|
|
3238
|
+
let omittedSelectors = 0;
|
|
3239
|
+
let rewrittenSelectors = 0;
|
|
3240
|
+
root.walkRules((rule) => {
|
|
3241
|
+
if (rule.parent?.type === "atrule" && /keyframes$/i.test(rule.parent.name)) return;
|
|
3242
|
+
const retained = [];
|
|
3243
|
+
try {
|
|
3244
|
+
selectorParser2((selectors) => {
|
|
3245
|
+
selectors.each((selector) => {
|
|
3246
|
+
let hasAuthoredAnchor = false;
|
|
3247
|
+
let hasPlatformAnchor = false;
|
|
3248
|
+
selector.walk((node) => {
|
|
3249
|
+
let ancestor = node.parent;
|
|
3250
|
+
let isNestedInPseudo = false;
|
|
3251
|
+
while (ancestor && ancestor !== selector) {
|
|
3252
|
+
if (ancestor.type === "pseudo") isNestedInPseudo = true;
|
|
3253
|
+
ancestor = ancestor.parent;
|
|
3254
|
+
}
|
|
3255
|
+
if (node.type === "class") {
|
|
3256
|
+
if (SHARED_THEME_CLASSES.has(node.value)) return;
|
|
3257
|
+
if (PLATFORM_CLASSES.has(node.value) || PLATFORM_CLASS_PREFIX.test(node.value) || UTILITY_CLASS.test(node.value) || !names.classes.has(node.value)) hasPlatformAnchor = true;
|
|
3258
|
+
else if (!isNestedInPseudo) hasAuthoredAnchor = true;
|
|
3259
|
+
} else if (node.type === "id") {
|
|
3260
|
+
if (PLATFORM_IDS.has(node.value) || !names.ids.has(node.value)) hasPlatformAnchor = true;
|
|
3261
|
+
else if (!isNestedInPseudo) hasAuthoredAnchor = true;
|
|
3262
|
+
} else if (node.type === "attribute" && node.attribute === "data-as") {
|
|
3263
|
+
node.remove();
|
|
3264
|
+
rewrittenSelectors += 1;
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
if (hasAuthoredAnchor && !hasPlatformAnchor) retained.push(selector.toString());
|
|
3268
|
+
else omittedSelectors += 1;
|
|
3269
|
+
});
|
|
3270
|
+
}).processSync(rule.selector);
|
|
3271
|
+
} catch {
|
|
3272
|
+
omittedSelectors += 1;
|
|
3273
|
+
}
|
|
3274
|
+
if (retained.length) rule.selector = retained.join(", ");
|
|
3275
|
+
else rule.remove();
|
|
3276
|
+
});
|
|
3277
|
+
const hasStyleRule = (nodes) => Boolean(nodes?.some((node) => node.type === "rule" || node.type === "atrule" && hasStyleRule(node.nodes)));
|
|
3278
|
+
root.walkAtRules((rule) => {
|
|
3279
|
+
if (rule.name === "font-face" || rule.name === "property") return;
|
|
3280
|
+
if (!hasStyleRule(rule.nodes)) rule.remove();
|
|
3281
|
+
});
|
|
3282
|
+
if (omittedSelectors === 0 && rewrittenSelectors === 0) return { css, omittedSelectors };
|
|
3283
|
+
root.walkComments((comment) => {
|
|
3284
|
+
comment.remove();
|
|
3285
|
+
});
|
|
3286
|
+
return { css: root.toString(), omittedSelectors };
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3136
3289
|
// src/docusaurus.ts
|
|
3137
3290
|
import { existsSync as existsSync3, lstatSync as lstatSync3, readFileSync as readFileSync3 } from "fs";
|
|
3138
3291
|
import { extname as extname4, posix as posix2 } from "path";
|
|
@@ -4131,6 +4284,47 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
4131
4284
|
message: "Mintlify navigation could not be projected; generated navigation will be used."
|
|
4132
4285
|
});
|
|
4133
4286
|
}
|
|
4287
|
+
const versionOwner = languages.length > 0 ? languages.find((entry) => entry.default === true) ?? languages[0] : navigation;
|
|
4288
|
+
const globalNavigation = objectValue2(versionOwner.global) ?? objectValue2(navigation.global);
|
|
4289
|
+
const shortcuts = Array.isArray(globalNavigation?.anchors) ? globalNavigation.anchors.flatMap((value) => {
|
|
4290
|
+
const anchor = objectValue2(value);
|
|
4291
|
+
const label = typeof anchor?.anchor === "string" ? anchor.anchor.trim() : "";
|
|
4292
|
+
const href = projectedHref(anchor?.href);
|
|
4293
|
+
if (!label || !href) return [];
|
|
4294
|
+
return [{ label, href, ...iconName(anchor?.icon) ? { icon: iconName(anchor?.icon) } : {} }];
|
|
4295
|
+
}) : [];
|
|
4296
|
+
const rawVersions = Array.isArray(versionOwner.versions) ? versionOwner.versions.map(objectValue2).filter((value) => value !== null).sort((left, right) => Number(right.default === true) - Number(left.default === true)) : [];
|
|
4297
|
+
const versionNames = rawVersions.map((entry) => typeof entry.version === "string" ? entry.version.trim() : "").filter(Boolean);
|
|
4298
|
+
const annotateVersions = (items) => items.map((item) => {
|
|
4299
|
+
const version = versionNames.find((name) => item.tab.startsWith(`${name}: `));
|
|
4300
|
+
return version ? { ...item, version, displayLabel: item.tab.slice(version.length + 2) } : item;
|
|
4301
|
+
});
|
|
4302
|
+
const firstPage = (item) => {
|
|
4303
|
+
const visit = (entries) => {
|
|
4304
|
+
for (const entry of entries) {
|
|
4305
|
+
if (typeof entry === "string") return entry;
|
|
4306
|
+
const nested = visit(entry.pages);
|
|
4307
|
+
if (nested) return nested;
|
|
4308
|
+
}
|
|
4309
|
+
return void 0;
|
|
4310
|
+
};
|
|
4311
|
+
return visit([...item.pages ?? [], ...item.groups ?? []]);
|
|
4312
|
+
};
|
|
4313
|
+
const defaultVersion = rawVersions.find((entry) => entry.default === true) ?? rawVersions[0];
|
|
4314
|
+
const versions = rawVersions.length > 1 ? rawVersions.flatMap((entry) => {
|
|
4315
|
+
const label = typeof entry.version === "string" ? entry.version.trim() : "";
|
|
4316
|
+
if (!label) return [];
|
|
4317
|
+
const candidates = tabs.filter((item) => item.tab.startsWith(`${label}: `)).map(firstPage).filter((page) => Boolean(page));
|
|
4318
|
+
const landing = candidates.find((page) => page.startsWith(`${label}/`)) ?? candidates[0];
|
|
4319
|
+
if (!landing) return [];
|
|
4320
|
+
const firstSegment = landing.split("/")[0];
|
|
4321
|
+
const prefix = firstSegment === label ? label : "";
|
|
4322
|
+
return [{ label, prefix, href: `/${landing}`, ...entry === defaultVersion ? { default: true } : {} }];
|
|
4323
|
+
}) : [];
|
|
4324
|
+
if (versions.length > 1) {
|
|
4325
|
+
tabs = annotateVersions(tabs);
|
|
4326
|
+
for (const [locale, items] of Object.entries(localizedNavigation)) localizedNavigation[locale] = annotateVersions(items);
|
|
4327
|
+
}
|
|
4134
4328
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
4135
4329
|
const redirect = objectValue2(value);
|
|
4136
4330
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
@@ -4155,7 +4349,11 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
4155
4349
|
return {
|
|
4156
4350
|
docsConfig: {
|
|
4157
4351
|
tabs,
|
|
4158
|
-
...projectionTrace.rootContainerKind === "dropdowns"
|
|
4352
|
+
...projectionTrace.rootContainerKind === "dropdowns" || versions.length > 1 || shortcuts.length > 0 ? { navigation: {
|
|
4353
|
+
...projectionTrace.rootContainerKind === "dropdowns" ? { display: "dropdown" } : {},
|
|
4354
|
+
...versions.length > 1 ? { versions } : {},
|
|
4355
|
+
...shortcuts.length > 0 ? { shortcuts } : {}
|
|
4356
|
+
} } : {},
|
|
4159
4357
|
...projectedCompatibleConfig(config, warnings),
|
|
4160
4358
|
...projectedApiMdx(config, warnings),
|
|
4161
4359
|
...i18n ? { i18n } : {},
|
|
@@ -4832,9 +5030,10 @@ function descriptorSort(left, right) {
|
|
|
4832
5030
|
if (leftPosition !== rightPosition) return leftPosition - rightPosition;
|
|
4833
5031
|
return left.sourcePath.localeCompare(right.sourcePath, void 0, { numeric: true });
|
|
4834
5032
|
}
|
|
4835
|
-
function registerDoc(docId, context) {
|
|
5033
|
+
function registerDoc(docId, context, sourceDirectory) {
|
|
4836
5034
|
const key = normalizeDocId(docId);
|
|
4837
|
-
const
|
|
5035
|
+
const scopedKey = sourceDirectory && !docId.startsWith("/") ? normalizeDocId(posix2.join(sourceDirectory, key)) : void 0;
|
|
5036
|
+
const descriptor = (scopedKey ? context.descriptorByDocId.get(scopedKey) : void 0) ?? context.descriptorByDocId.get(key) ?? context.descriptors.find((page) => page.navigationId === docusaurusRouteId(key));
|
|
4838
5037
|
if (!descriptor) {
|
|
4839
5038
|
context.warnings.push({
|
|
4840
5039
|
code: "missing-page",
|
|
@@ -4846,10 +5045,10 @@ function registerDoc(docId, context) {
|
|
|
4846
5045
|
context.referencedNavigationIds.add(descriptor.navigationId);
|
|
4847
5046
|
return descriptor.navigationId;
|
|
4848
5047
|
}
|
|
4849
|
-
function generatedIndexPage(label, linkValue, context) {
|
|
5048
|
+
function generatedIndexPage(label, linkValue, context, sourceDirectory) {
|
|
4850
5049
|
const link = objectValue3(linkValue);
|
|
4851
5050
|
if (!link) return null;
|
|
4852
|
-
if (link.type === "doc" && typeof link.id === "string") return registerDoc(link.id, context);
|
|
5051
|
+
if (link.type === "doc" && typeof link.id === "string") return registerDoc(link.id, context, sourceDirectory);
|
|
4853
5052
|
if (link.type !== "generated-index") return null;
|
|
4854
5053
|
const fallback = `category/${slugifySegment(label)}`;
|
|
4855
5054
|
const unprefixedNavigationId = typeof link.slug === "string" ? routeFromSlug(link.slug, fallback) ?? fallback : fallback;
|
|
@@ -5092,7 +5291,7 @@ function autogeneratedItems(dirName, context) {
|
|
|
5092
5291
|
};
|
|
5093
5292
|
}
|
|
5094
5293
|
}
|
|
5095
|
-
const landing = generatedIndexPage(metadata.label ?? titleCase2(segment), metadata.link, context);
|
|
5294
|
+
const landing = generatedIndexPage(metadata.label ?? titleCase2(segment), metadata.link, context, directory);
|
|
5096
5295
|
if (landing && !pages2.includes(landing)) pages2.unshift(landing);
|
|
5097
5296
|
return {
|
|
5098
5297
|
position: metadata.position ?? Number.MAX_SAFE_INTEGER,
|
|
@@ -5926,6 +6125,7 @@ var FRONTMATTER_HEAD_BYTES = 65536;
|
|
|
5926
6125
|
var MAX_WITHHELD_SCAN_BYTES = MAX_PAGE_BYTES * 8;
|
|
5927
6126
|
var MAX_DROPPED_GATED_WARNINGS = 20;
|
|
5928
6127
|
var MAX_ASSET_BYTES = 25e6;
|
|
6128
|
+
var MAX_REFERENCED_ASSET_BYTES = 1e8;
|
|
5929
6129
|
var MAX_TOTAL_ASSET_BYTES = 5e8;
|
|
5930
6130
|
var GIT_LFS_POINTER_PREFIX = "version https://git-lfs.github.com/spec/v1";
|
|
5931
6131
|
function listAssetPaths(paths) {
|
|
@@ -7186,18 +7386,18 @@ function isolateFences(snippet, source, start, end) {
|
|
|
7186
7386
|
return `${fence.test(lines[0]) && /\S/.test(before) ? "\n" : ""}${snippet}${fence.test(lines[lines.length - 1]) && /\S/.test(after) ? "\n" : ""}`;
|
|
7187
7387
|
}
|
|
7188
7388
|
function snippetComponentBody(source, componentName) {
|
|
7189
|
-
const parsed =
|
|
7389
|
+
const parsed = ts4.createSourceFile("snippet.tsx", source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
7190
7390
|
for (const statement of parsed.statements) {
|
|
7191
|
-
if (!
|
|
7391
|
+
if (!ts4.isVariableStatement(statement)) continue;
|
|
7192
7392
|
for (const declaration of statement.declarationList.declarations) {
|
|
7193
|
-
if (!
|
|
7393
|
+
if (!ts4.isIdentifier(declaration.name) || declaration.name.text !== componentName) continue;
|
|
7194
7394
|
const initializer = declaration.initializer;
|
|
7195
|
-
if (!initializer || !
|
|
7196
|
-
const returned =
|
|
7395
|
+
if (!initializer || !ts4.isArrowFunction(initializer)) continue;
|
|
7396
|
+
const returned = ts4.isBlock(initializer.body) ? initializer.body.statements.find(ts4.isReturnStatement)?.expression : initializer.body;
|
|
7197
7397
|
if (!returned) continue;
|
|
7198
7398
|
let expression = returned;
|
|
7199
|
-
while (
|
|
7200
|
-
if (
|
|
7399
|
+
while (ts4.isParenthesizedExpression(expression)) expression = expression.expression;
|
|
7400
|
+
if (ts4.isJsxElement(expression) || ts4.isJsxSelfClosingElement(expression) || ts4.isJsxFragment(expression)) {
|
|
7201
7401
|
return expression.getText(parsed);
|
|
7202
7402
|
}
|
|
7203
7403
|
}
|
|
@@ -7205,31 +7405,31 @@ function snippetComponentBody(source, componentName) {
|
|
|
7205
7405
|
return source;
|
|
7206
7406
|
}
|
|
7207
7407
|
function statefulSnippetDeclaration(source, componentName) {
|
|
7208
|
-
const parsed =
|
|
7209
|
-
const isEsm = (statement2) =>
|
|
7408
|
+
const parsed = ts4.createSourceFile("snippet.tsx", source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
7409
|
+
const isEsm = (statement2) => ts4.isImportDeclaration(statement2) || (ts4.isVariableStatement(statement2) || ts4.isFunctionDeclaration(statement2) || ts4.isClassDeclaration(statement2)) && !!ts4.getModifiers(statement2)?.some((modifier) => modifier.kind === ts4.SyntaxKind.ExportKeyword);
|
|
7210
7410
|
if (parsed.statements.length === 0 || !parsed.statements.every(isEsm)) return null;
|
|
7211
|
-
const statement = parsed.statements.find((candidate) =>
|
|
7212
|
-
if (!statement || !
|
|
7411
|
+
const statement = parsed.statements.find((candidate) => ts4.isVariableStatement(candidate) && candidate.declarationList.declarations.length === 1 && ts4.isIdentifier(candidate.declarationList.declarations[0].name) && candidate.declarationList.declarations[0].name.text === componentName);
|
|
7412
|
+
if (!statement || !ts4.isVariableStatement(statement)) return null;
|
|
7213
7413
|
const declaration = statement.declarationList.declarations[0];
|
|
7214
|
-
if (!declaration.initializer || !
|
|
7414
|
+
if (!declaration.initializer || !ts4.isArrowFunction(declaration.initializer) || !ts4.isBlock(declaration.initializer.body)) return null;
|
|
7215
7415
|
const body = declaration.initializer.body.statements;
|
|
7216
|
-
if (body.length < 2 || !body.some(
|
|
7416
|
+
if (body.length < 2 || !body.some(ts4.isReturnStatement)) return null;
|
|
7217
7417
|
return source.trim();
|
|
7218
7418
|
}
|
|
7219
7419
|
function staticNamedSnippetValues(source) {
|
|
7220
7420
|
const values = /* @__PURE__ */ new Map();
|
|
7221
|
-
const parsed =
|
|
7421
|
+
const parsed = ts4.createSourceFile("snippet.tsx", source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
7222
7422
|
for (const statement of parsed.statements) {
|
|
7223
|
-
if (!
|
|
7423
|
+
if (!ts4.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts4.SyntaxKind.ExportKeyword)) continue;
|
|
7224
7424
|
for (const declaration of statement.declarationList.declarations) {
|
|
7225
|
-
if (!
|
|
7425
|
+
if (!ts4.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
7226
7426
|
const expression = declaration.initializer;
|
|
7227
|
-
if (
|
|
7427
|
+
if (ts4.isStringLiteral(expression) || ts4.isNoSubstitutionTemplateLiteral(expression)) {
|
|
7228
7428
|
values.set(declaration.name.text, JSON.stringify(expression.text));
|
|
7229
|
-
} else if (
|
|
7429
|
+
} else if (ts4.isNumericLiteral(expression)) {
|
|
7230
7430
|
values.set(declaration.name.text, expression.text);
|
|
7231
|
-
} else if (expression.kind ===
|
|
7232
|
-
values.set(declaration.name.text, expression.kind ===
|
|
7431
|
+
} else if (expression.kind === ts4.SyntaxKind.TrueKeyword || expression.kind === ts4.SyntaxKind.FalseKeyword) {
|
|
7432
|
+
values.set(declaration.name.text, expression.kind === ts4.SyntaxKind.TrueKeyword ? "true" : "false");
|
|
7233
7433
|
}
|
|
7234
7434
|
}
|
|
7235
7435
|
}
|
|
@@ -7556,6 +7756,19 @@ function rewriteRepositoryAssetLinks(body, currentFile, siteRoot, onReferenced)
|
|
|
7556
7756
|
});
|
|
7557
7757
|
}
|
|
7558
7758
|
var LOCAL_EXPORT_DECLARATION = /^\s*export\s+(?:const|let|var|function|class|async\s+function)\s+([A-Za-z_$][\w$]*)/;
|
|
7759
|
+
function replaceOutsideMdxComments(body, transform) {
|
|
7760
|
+
const comment = /\{\/\*[\s\S]*?\*\/\}/g;
|
|
7761
|
+
let cursor = 0;
|
|
7762
|
+
let output = "";
|
|
7763
|
+
for (const match of body.matchAll(comment)) {
|
|
7764
|
+
output += transform(body.slice(cursor, match.index)) + match[0];
|
|
7765
|
+
cursor = match.index + match[0].length;
|
|
7766
|
+
}
|
|
7767
|
+
return output + transform(body.slice(cursor));
|
|
7768
|
+
}
|
|
7769
|
+
function replaceOutsideCodeAndMdxComments(body, transform) {
|
|
7770
|
+
return replaceOutsideCode(body, (segment) => replaceOutsideMdxComments(segment, transform));
|
|
7771
|
+
}
|
|
7559
7772
|
function locallyDeclaredNames(raw) {
|
|
7560
7773
|
const names = /* @__PURE__ */ new Set();
|
|
7561
7774
|
let codeFence = null;
|
|
@@ -7601,7 +7814,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7601
7814
|
});
|
|
7602
7815
|
return `{/* ${verdict.kind === "oversized" ? "Oversized" : "Unreadable"} content not inlined: ${shown} */}`;
|
|
7603
7816
|
};
|
|
7604
|
-
let withoutImports =
|
|
7817
|
+
let withoutImports = replaceOutsideCodeAndMdxComments(raw, (source2) => source2.replace(
|
|
7605
7818
|
SNIPPET_IMPORT_PATTERN,
|
|
7606
7819
|
(_statement, namedComponent, defaultComponent, sourcePath) => {
|
|
7607
7820
|
const componentName = namedComponent ?? defaultComponent;
|
|
@@ -7628,6 +7841,12 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7628
7841
|
else snippets.set(componentName, snippetComponentBody(nested, componentName));
|
|
7629
7842
|
return "";
|
|
7630
7843
|
} catch {
|
|
7844
|
+
let hasLiveUsage = false;
|
|
7845
|
+
replaceOutsideCodeAndMdxComments(raw, (segment) => {
|
|
7846
|
+
if (new RegExp(`<${componentName}(?:\\s|/?>)`).test(segment)) hasLiveUsage = true;
|
|
7847
|
+
return segment;
|
|
7848
|
+
});
|
|
7849
|
+
if (!hasLiveUsage) return "";
|
|
7631
7850
|
warnings.push({
|
|
7632
7851
|
code: "missing-page",
|
|
7633
7852
|
message: `Imported snippet ${sourcePath} could not be resolved and was left as a comment.`,
|
|
@@ -7638,7 +7857,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7638
7857
|
}
|
|
7639
7858
|
}
|
|
7640
7859
|
));
|
|
7641
|
-
withoutImports =
|
|
7860
|
+
withoutImports = replaceOutsideCodeAndMdxComments(withoutImports, (source2) => source2.replace(
|
|
7642
7861
|
SNIPPET_VALUE_IMPORT_PATTERN,
|
|
7643
7862
|
(statement, names, sourcePath) => {
|
|
7644
7863
|
const bindings = names.split(",").map((name) => name.trim()).filter(Boolean).map((name) => {
|
|
@@ -7682,7 +7901,12 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7682
7901
|
));
|
|
7683
7902
|
const localNames = locallyDeclaredNames(withoutImports);
|
|
7684
7903
|
for (const [componentName, candidate] of globalAliases) {
|
|
7685
|
-
|
|
7904
|
+
let hasLiveUsage = false;
|
|
7905
|
+
replaceOutsideCodeAndMdxComments(withoutImports, (source2) => {
|
|
7906
|
+
if (new RegExp(`<${componentName}(?:\\s|/?>)`).test(source2)) hasLiveUsage = true;
|
|
7907
|
+
return source2;
|
|
7908
|
+
});
|
|
7909
|
+
if (snippets.has(componentName) || localNames.has(componentName) || gate?.(candidate)?.kind === "gated" || !hasLiveUsage) continue;
|
|
7686
7910
|
const blocked = blockInline(candidate, relative5(repositoryRoot, candidate).replace(/\\/g, "/"));
|
|
7687
7911
|
if (blocked) {
|
|
7688
7912
|
snippets.set(componentName, blocked);
|
|
@@ -7705,9 +7929,9 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7705
7929
|
let result = replaceOutsideCodeAndComments(withoutImports, (segment) => {
|
|
7706
7930
|
for (const [componentName, snippet] of snippets) {
|
|
7707
7931
|
segment = segment.replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)\\s*/>`, "g"), (tag, attributes, offset, source2) => {
|
|
7708
|
-
return isolateFences(interpolateSnippet(snippet, attributes), source2, offset, offset + tag.length);
|
|
7932
|
+
return isolateFences(closeOpenCodeFence(interpolateSnippet(snippet, attributes)), source2, offset, offset + tag.length);
|
|
7709
7933
|
}).replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)>([\\s\\S]*?)<\\/${componentName}>`, "g"), (tag, attributes, children, offset, source2) => {
|
|
7710
|
-
return isolateFences(interpolateSnippet(snippet, attributes, children), source2, offset, offset + tag.length);
|
|
7934
|
+
return isolateFences(closeOpenCodeFence(interpolateSnippet(snippet, attributes, children)), source2, offset, offset + tag.length);
|
|
7711
7935
|
});
|
|
7712
7936
|
}
|
|
7713
7937
|
return segment.replace(SNIPPET_TAG_PATTERN, (tag, doubleQuoted, singleQuoted, offset, source2) => {
|
|
@@ -7726,7 +7950,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7726
7950
|
if (!candidate) throw new Error("file not found");
|
|
7727
7951
|
const blocked = blockInline(candidate, filePath, `Snippet file="${filePath}"`);
|
|
7728
7952
|
if (blocked) return blocked;
|
|
7729
|
-
return isolateFences(inlineMdxSnippets(
|
|
7953
|
+
return isolateFences(closeOpenCodeFence(inlineMdxSnippets(
|
|
7730
7954
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
7731
7955
|
candidate,
|
|
7732
7956
|
repositoryRoot,
|
|
@@ -7735,7 +7959,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7735
7959
|
siteRoot,
|
|
7736
7960
|
globalAliases,
|
|
7737
7961
|
gate
|
|
7738
|
-
), source2, offset, offset + tag.length);
|
|
7962
|
+
)), source2, offset, offset + tag.length);
|
|
7739
7963
|
} catch {
|
|
7740
7964
|
warnings.push({
|
|
7741
7965
|
code: "missing-page",
|
|
@@ -7940,7 +8164,7 @@ function migrateRepository(options) {
|
|
|
7940
8164
|
const sourceBudget = options.maxSourceFiles ?? MAX_SOURCE_FILES;
|
|
7941
8165
|
const scannedFiles = scanFiles(contentRoot, repositoryDir, warnings, discoveryRank, sourceBudget);
|
|
7942
8166
|
const mintignoreFilteredFiles = mintignoreMatcher ? scannedFiles.filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scannedFiles;
|
|
7943
|
-
const
|
|
8167
|
+
const isUnreferencedSnippetModule = (file) => file.relativePath.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase())) && isDocumentationExtension(file.relativePath) && !referenceOrder.has(normalizedReferenceKey(file.relativePath));
|
|
7944
8168
|
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes, sourceBudget) : mintignoreFilteredFiles;
|
|
7945
8169
|
if (platform === "fern" && fernProjectRoot) {
|
|
7946
8170
|
const discovered2 = new Set(files.map((file) => file.relativePath));
|
|
@@ -8019,6 +8243,10 @@ function migrateRepository(options) {
|
|
|
8019
8243
|
}
|
|
8020
8244
|
for (const file of mintignoreFilteredFiles) {
|
|
8021
8245
|
if (keptFiles.has(file) || !isDocFile(file)) continue;
|
|
8246
|
+
if (isUnreferencedSnippetModule(file)) {
|
|
8247
|
+
if (classifyPageGate(file).reason) withheldDocFiles.push(file);
|
|
8248
|
+
continue;
|
|
8249
|
+
}
|
|
8022
8250
|
droppedPageCount++;
|
|
8023
8251
|
if (!isGateCandidate(file)) {
|
|
8024
8252
|
const snippetReason = classifyPageGate(file).reason;
|
|
@@ -8253,13 +8481,7 @@ function migrateRepository(options) {
|
|
|
8253
8481
|
}
|
|
8254
8482
|
if (platform === "fern" || platform === "mintlify" || platform === "docusaurus") {
|
|
8255
8483
|
const protectedMath = protectMathBlocks(raw);
|
|
8256
|
-
if (protectedMath.
|
|
8257
|
-
warnings.push({
|
|
8258
|
-
code: "unsupported-config",
|
|
8259
|
-
message: "Math (KaTeX '$$...$$') has no renderer in Thally yet; it was kept as a fenced code block instead of being dropped.",
|
|
8260
|
-
source: relative5(repositoryDir, file.absolutePath).replace(/\\/g, "/")
|
|
8261
|
-
});
|
|
8262
|
-
} else if (protectedMath.guardTriggered) {
|
|
8484
|
+
if (protectedMath.guardTriggered) {
|
|
8263
8485
|
warnings.push({
|
|
8264
8486
|
code: "unsupported-config",
|
|
8265
8487
|
message: "A suspected math span looked like it needed converting, but doing so broke the page; the page was kept as originally authored instead.",
|
|
@@ -8351,7 +8573,7 @@ function migrateRepository(options) {
|
|
|
8351
8573
|
page.body = replaceUnknownComponents(page.body, (name) => {
|
|
8352
8574
|
warnings.push({
|
|
8353
8575
|
code: "unsupported-config",
|
|
8354
|
-
message: `
|
|
8576
|
+
message: `Unresolved MDX component <${name}> was replaced with a plain <div> (or removed, if self-closing) so the page still builds. It may be a site-authored or third-party implementation; review its source before adding a Thally equivalent.`,
|
|
8355
8577
|
source: file.relativePath
|
|
8356
8578
|
});
|
|
8357
8579
|
});
|
|
@@ -8478,9 +8700,31 @@ function migrateRepository(options) {
|
|
|
8478
8700
|
const source = /^src\/mdx\/migrated\/[^/]+\/source\/(.+)$/.exec(file.path)?.[1];
|
|
8479
8701
|
return source ? [resolvePath(repositoryDir, source)] : [];
|
|
8480
8702
|
}));
|
|
8703
|
+
const rootStylesheetPath = platform === "mintlify" && mintlifyProjectRoot ? resolveWithin(mintlifyProjectRoot, "style.css") : void 0;
|
|
8704
|
+
const hasRootStylesheet = Boolean(rootStylesheetPath && existsSync5(rootStylesheetPath) && lstatSync5(rootStylesheetPath).isFile() && !lstatSync5(rootStylesheetPath).isSymbolicLink());
|
|
8705
|
+
let rootStyleProjection;
|
|
8706
|
+
let rootStyleProblem;
|
|
8707
|
+
if (hasRootStylesheet && rootStylesheetPath && lstatSync5(rootStylesheetPath).size <= MAX_ASSET_BYTES) {
|
|
8708
|
+
const css = readFileSync5(rootStylesheetPath, "utf8");
|
|
8709
|
+
if (/@import\b|url\s*\(\s*['"]?\s*(?:javascript:|file:)/i.test(css)) rootStyleProblem = "unsafe";
|
|
8710
|
+
else {
|
|
8711
|
+
try {
|
|
8712
|
+
rootStyleProjection = projectAuthoredStyles(css, [
|
|
8713
|
+
...pages.map((page) => page.body),
|
|
8714
|
+
...(componentMigrator?.files() ?? []).map((file) => typeof file.content === "string" ? file.content : "")
|
|
8715
|
+
]);
|
|
8716
|
+
} catch {
|
|
8717
|
+
rootStyleProblem = "parse";
|
|
8718
|
+
}
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
if (trackPublishedRefs && rootStyleProjection?.css) {
|
|
8722
|
+
addPathReferences(rootStyleProjection.css, "", publishedExact, publishedLoose);
|
|
8723
|
+
}
|
|
8481
8724
|
const siteAssetPaths = /* @__PURE__ */ new Map();
|
|
8482
8725
|
const fontAssetByRelative = /* @__PURE__ */ new Map();
|
|
8483
8726
|
for (const file of [...files, ...repositoryAssets, ...fernReferencedAssets]) {
|
|
8727
|
+
if (rootStylesheetPath && file.absolutePath === rootStylesheetPath) continue;
|
|
8484
8728
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
8485
8729
|
const siteKind = platform !== "mintlify" ? void 0 : fontRelativePaths.has(file.relativePath) ? "font" : isMintlifyServedScriptOrStyle(file.relativePath) && !importedComponentSources.has(file.absolutePath) ? extname6(file.relativePath).toLowerCase() === ".css" ? "style" : "script" : void 0;
|
|
8486
8730
|
if (!siteKind && !ASSET_EXTENSIONS.has(extname6(file.relativePath).toLowerCase())) continue;
|
|
@@ -8526,7 +8770,8 @@ function migrateRepository(options) {
|
|
|
8526
8770
|
const lfsPointerAssets = [];
|
|
8527
8771
|
const ambiguouslyNamedAssets = [];
|
|
8528
8772
|
for (const { file, assetPath, size } of orderedAssetCandidates) {
|
|
8529
|
-
|
|
8773
|
+
const individualLimit = referencedAssetPaths.has(assetPath) ? MAX_REFERENCED_ASSET_BYTES : MAX_ASSET_BYTES;
|
|
8774
|
+
if (size > individualLimit || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
8530
8775
|
overBudgetAssets.push(file.relativePath);
|
|
8531
8776
|
if (referencedAssetPaths.has(assetPath)) overBudgetReferenced++;
|
|
8532
8777
|
continue;
|
|
@@ -8568,7 +8813,7 @@ function migrateRepository(options) {
|
|
|
8568
8813
|
for (const entry of copiedSite.filter((item) => item.kind === "style")) {
|
|
8569
8814
|
warnings.push({
|
|
8570
8815
|
code: "unsupported-config",
|
|
8571
|
-
message: `Migration gap: custom stylesheet ${entry.path} is NOT applied. It was copied to public/${entry.path} but
|
|
8816
|
+
message: `Migration gap: custom stylesheet ${entry.path} is NOT applied. It was copied to public/${entry.path} but was not declared in docs.json stylesheets; its selectors may target Mintlify's markup. Review and port its authored rules into a Thally stylesheet.`,
|
|
8572
8817
|
source: entry.path
|
|
8573
8818
|
});
|
|
8574
8819
|
}
|
|
@@ -8586,7 +8831,7 @@ function migrateRepository(options) {
|
|
|
8586
8831
|
if (overBudgetAssets.length > 0) {
|
|
8587
8832
|
warnings.push({
|
|
8588
8833
|
code: "limit-reached",
|
|
8589
|
-
message: `${overBudgetAssets.length} asset file${overBudgetAssets.length === 1 ? " was" : "s were"} not copied because files over ${MAX_ASSET_BYTES / 1e6} MB, or beyond ${MAX_TOTAL_ASSET_BYTES / 1e6} MB in total, are skipped: ${listAssetPaths(overBudgetAssets)}. ` + (overBudgetReferenced > 0 ? `${overBudgetReferenced} of them ${overBudgetReferenced === 1 ? "is" : "are"} used by pages, so those images will be broken until you copy ${overBudgetAssets.length === 1 ? "it" : "them"}. ` : "") + `Copy ${overBudgetAssets.length === 1 ? "it" : "them"} into public/ manually.`
|
|
8834
|
+
message: `${overBudgetAssets.length} asset file${overBudgetAssets.length === 1 ? " was" : "s were"} not copied because unreferenced files over ${MAX_ASSET_BYTES / 1e6} MB, referenced files over ${MAX_REFERENCED_ASSET_BYTES / 1e6} MB, or files beyond ${MAX_TOTAL_ASSET_BYTES / 1e6} MB in total, are skipped: ${listAssetPaths(overBudgetAssets)}. ` + (overBudgetReferenced > 0 ? `${overBudgetReferenced} of them ${overBudgetReferenced === 1 ? "is" : "are"} used by pages, so those images will be broken until you copy ${overBudgetAssets.length === 1 ? "it" : "them"}. ` : "") + `Copy ${overBudgetAssets.length === 1 ? "it" : "them"} into public/ manually.`
|
|
8590
8835
|
});
|
|
8591
8836
|
}
|
|
8592
8837
|
if (lfsPointerAssets.length > 0) {
|
|
@@ -8595,7 +8840,31 @@ function migrateRepository(options) {
|
|
|
8595
8840
|
message: `${lfsPointerAssets.length} asset file${lfsPointerAssets.length === 1 ? " is a Git LFS pointer" : "s are Git LFS pointers"}, not real content, and ${lfsPointerAssets.length === 1 ? "was" : "were"} not copied (Git LFS was skipped during clone because the host has no git-lfs binary): ${listAssetPaths(lfsPointerAssets)}. Install git-lfs and re-run the migration, or copy the real files into public/ manually.`
|
|
8596
8841
|
});
|
|
8597
8842
|
}
|
|
8598
|
-
if (
|
|
8843
|
+
if (hasRootStylesheet && rootStylesheetPath && mintlifyProjectRoot) {
|
|
8844
|
+
const size = lstatSync5(rootStylesheetPath).size;
|
|
8845
|
+
if (size > MAX_ASSET_BYTES || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
8846
|
+
warnings.push({ code: "limit-reached", message: "Mintlify root style.css exceeded the asset budget and was not imported.", source: "style.css" });
|
|
8847
|
+
} else if (rootStyleProblem === "unsafe") {
|
|
8848
|
+
warnings.push({ code: "unsupported-config", message: "Mintlify root style.css contains a CSS import or unsafe URL; review and import it manually.", source: "style.css" });
|
|
8849
|
+
} else if (rootStyleProblem === "parse" || !rootStyleProjection) {
|
|
8850
|
+
warnings.push({ code: "unsupported-config", message: "Mintlify root style.css could not be parsed and was not imported.", source: "style.css" });
|
|
8851
|
+
} else {
|
|
8852
|
+
if (rootStyleProjection.omittedSelectors > 0) warnings.push({
|
|
8853
|
+
code: "unsupported-config",
|
|
8854
|
+
source: "style.css",
|
|
8855
|
+
message: `Skipped ${rootStyleProjection.omittedSelectors} stylesheet selector${rootStyleProjection.omittedSelectors === 1 ? "" : "s"} that target platform-owned or unreferenced markup. Recreate any intentional shell customization with Thally configuration.`
|
|
8856
|
+
});
|
|
8857
|
+
if (rootStyleProjection.css.trim()) {
|
|
8858
|
+
const identity = createHash2("sha256").update(componentSourceIdentity(options.sourceUrl, repositoryDir, mintlifyProjectRoot)).digest("hex").slice(0, 12);
|
|
8859
|
+
const assetPath = `migrated/${identity}/style.css`;
|
|
8860
|
+
const content = Buffer.from(rootStyleProjection.css);
|
|
8861
|
+
assets.push({ path: assetPath, content });
|
|
8862
|
+
docsConfig = { ...docsConfig, stylesheets: [...docsConfig.stylesheets ?? [], `/${assetPath}`] };
|
|
8863
|
+
totalAssetBytes += content.length;
|
|
8864
|
+
}
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8867
|
+
if (!discoveryRank && files.length >= sourceBudget) {
|
|
8599
8868
|
warnings.push({ code: "limit-reached", message: `Stopped scanning after ${sourceBudget} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.` });
|
|
8600
8869
|
}
|
|
8601
8870
|
if (platform === "docusaurus") {
|
|
@@ -9268,6 +9537,7 @@ function mergeMigrationConfig(existing, incoming) {
|
|
|
9268
9537
|
// Collection presentation belongs to the imported information
|
|
9269
9538
|
// architecture; starter defaults must not coerce source dropdowns to tabs.
|
|
9270
9539
|
navigation: incoming.navigation ?? existing.navigation,
|
|
9540
|
+
...existing.stylesheets?.length || incoming.stylesheets?.length ? { stylesheets: [.../* @__PURE__ */ new Set([...existing.stylesheets ?? [], ...incoming.stylesheets ?? []])] } : {},
|
|
9271
9541
|
...i18n ? { i18n } : {},
|
|
9272
9542
|
// Existing providers win per provider; imported ones fill the gaps.
|
|
9273
9543
|
// A malformed (string/array) existing value must not spread into index keys.
|
|
@@ -9614,7 +9884,7 @@ function embeddedMintlifyConfig(body) {
|
|
|
9614
9884
|
function plainDescription(value) {
|
|
9615
9885
|
return value.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_>#-]/g, "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
9616
9886
|
}
|
|
9617
|
-
function
|
|
9887
|
+
function withoutFencedCode2(body) {
|
|
9618
9888
|
let fenceCharacter = "";
|
|
9619
9889
|
let fenceLength = 0;
|
|
9620
9890
|
return body.split("\n").map((line) => {
|
|
@@ -9924,7 +10194,7 @@ function sanitizeRemoteMarkdown(body) {
|
|
|
9924
10194
|
}).join("\n");
|
|
9925
10195
|
const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized);
|
|
9926
10196
|
if (urlSafeMarkdown === null) return null;
|
|
9927
|
-
const executable =
|
|
10197
|
+
const executable = withoutFencedCode2(urlSafeMarkdown).replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<(?:https?:\/\/|mailto:)[^>]+>/gi, "").replace(/(`+)[\s\S]*?\1/g, "");
|
|
9928
10198
|
if (/^\s*(?:import|export)\s/m.test(executable)) return null;
|
|
9929
10199
|
let hasUnsafeExpression = false;
|
|
9930
10200
|
const withoutStaticProps = executable.replace(/=\{([^{}\n]*)\}/g, (original, value) => {
|
|
@@ -10874,7 +11144,7 @@ async function migrateUrl(options) {
|
|
|
10874
11144
|
}
|
|
10875
11145
|
|
|
10876
11146
|
// src/remote-api.ts
|
|
10877
|
-
import { createHash as
|
|
11147
|
+
import { createHash as createHash3 } from "crypto";
|
|
10878
11148
|
import { lookup } from "dns/promises";
|
|
10879
11149
|
import { request } from "https";
|
|
10880
11150
|
import { isIP } from "net";
|
|
@@ -11034,7 +11304,7 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
|
|
|
11034
11304
|
const extension = /\.ya?ml$/i.test(url.pathname) ? "yaml" : "json";
|
|
11035
11305
|
const body = await downloadSpec(reference.url, fetcher);
|
|
11036
11306
|
const operations = parseOpenApi(body, extension);
|
|
11037
|
-
const filename = `openapi-${
|
|
11307
|
+
const filename = `openapi-${createHash3("sha256").update(reference.url).digest("hex").slice(0, 12)}.${extension}`;
|
|
11038
11308
|
assets.push({ path: `openapi/${filename}`, content: body, projectRelative: true });
|
|
11039
11309
|
for (const page of pages) {
|
|
11040
11310
|
const ref = page.openapi ? splitOpenApiRef(page.openapi) : null;
|