@thallylabs/migrate 0.2.40 → 0.2.42
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 +72 -3
- package/dist/index.d.ts +10 -2
- package/dist/index.js +575 -82
- package/dist/index.js.map +1 -1
- package/dist/url-migration.d.ts +2 -2
- package/dist/url-migration.js +146 -29
- package/dist/url-migration.js.map +1 -1
- package/dist/{url-run-DFnoqEi-.d.ts → url-run-CPzQfjRW.d.ts} +35 -3
- package/package.json +2 -1
package/dist/url-migration.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as MigrationCapture } from './url-run-
|
|
2
|
-
export { a as MigrationAsset, f as MigrationBundle, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, g as MigrationDocsConfig, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, d as MigrationFetcher, o as MigrationJournalAsset, b as MigrationNavigationGroup, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, M as MigrationPage, e as MigrationPlatform, c as MigrationWarning, R as RenderedMigrationFile, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-run-
|
|
1
|
+
import { h as MigrationCapture } from './url-run-CPzQfjRW.js';
|
|
2
|
+
export { a as MigrationAsset, f as MigrationBundle, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, g as MigrationDocsConfig, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, d as MigrationFetcher, o as MigrationJournalAsset, b as MigrationNavigationGroup, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, M as MigrationPage, e as MigrationPlatform, c as MigrationWarning, R as RenderedMigrationFile, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-run-CPzQfjRW.js';
|
|
3
3
|
|
|
4
4
|
interface UrlMigrationScope {
|
|
5
5
|
scopePath: string;
|
package/dist/url-migration.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/url-run.ts
|
|
2
2
|
import { createHash as createHash4 } from "crypto";
|
|
3
3
|
import { load as load7 } from "cheerio";
|
|
4
|
+
import { parse as parse2, postprocess, preprocess } from "micromark";
|
|
4
5
|
import remarkParse4 from "remark-parse";
|
|
5
6
|
import remarkMdx4 from "remark-mdx";
|
|
6
7
|
import { unified as unified4 } from "unified";
|
|
@@ -1815,7 +1816,10 @@ var VIDEO_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
1815
1816
|
"width",
|
|
1816
1817
|
"height",
|
|
1817
1818
|
"preload",
|
|
1818
|
-
"aria-label"
|
|
1819
|
+
"aria-label",
|
|
1820
|
+
// Publisher asset provenance does not change playback and is never emitted.
|
|
1821
|
+
// Keep this explicit: arbitrary data attributes may drive custom controls.
|
|
1822
|
+
"data-path"
|
|
1819
1823
|
]);
|
|
1820
1824
|
var YOUTUBE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
1821
1825
|
"src",
|
|
@@ -2618,8 +2622,29 @@ ${trimmed.slice(0, marker).trimEnd()}`;
|
|
|
2618
2622
|
function headingIdMarker(id) {
|
|
2619
2623
|
return `{/* #${id} */}`;
|
|
2620
2624
|
}
|
|
2625
|
+
var HEADING_ID_WORD = /[\p{L}\p{M}\p{N}_+&/@\u2019\u201c\u201d\u2014\u2013\u2192]/u;
|
|
2621
2626
|
function mintlifyHeadingSlug(text2) {
|
|
2622
|
-
|
|
2627
|
+
const chars = [...text2.replace(/[\u200b*]/g, "").normalize("NFC").trim().toLowerCase()];
|
|
2628
|
+
const isWord = (char) => char !== void 0 && HEADING_ID_WORD.test(char);
|
|
2629
|
+
let id = "";
|
|
2630
|
+
let afterDash = false;
|
|
2631
|
+
let endsWithDash = false;
|
|
2632
|
+
chars.forEach((char, index) => {
|
|
2633
|
+
const before = chars[index - 1];
|
|
2634
|
+
const after = chars[index + 1];
|
|
2635
|
+
endsWithDash = false;
|
|
2636
|
+
if (isWord(char)) {
|
|
2637
|
+
id += char;
|
|
2638
|
+
afterDash = false;
|
|
2639
|
+
} else if (/[\s-]/.test(char) || char === "." && isWord(after) || char === "(" && isWord(before) && (isWord(after) || after === "{") || char === ")" && after !== void 0 && !/\s/.test(after)) {
|
|
2640
|
+
if (id && !afterDash) id += "-";
|
|
2641
|
+
afterDash = true;
|
|
2642
|
+
endsWithDash = true;
|
|
2643
|
+
} else if (char === "{" || char === ":" || char === '"') {
|
|
2644
|
+
afterDash = false;
|
|
2645
|
+
}
|
|
2646
|
+
});
|
|
2647
|
+
return endsWithDash ? id.replace(/-$/, "") : id;
|
|
2623
2648
|
}
|
|
2624
2649
|
function thallyHeadingSlug(text2) {
|
|
2625
2650
|
return text2.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/(^-|-$)/g, "");
|
|
@@ -2631,7 +2656,7 @@ function typesetQuotes(text2) {
|
|
|
2631
2656
|
}).join("");
|
|
2632
2657
|
}
|
|
2633
2658
|
function plainHeadingText(source) {
|
|
2634
|
-
return source.replace(/\{\/\*[\s\S]*?\*\/\}/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<\/?[A-Za-z][^>]*>/g, "").replace(/`([^`]*)`/g, "$1").replace(/(\*\*|__|~~|\*)/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/\\([\\`*_{}[\]()#+.!&<>|~-])/g, "$1");
|
|
2659
|
+
return source.replace(/\{\/\*[\s\S]*?\*\/\}/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<\/?[A-Za-z][^>]*>/g, "").replace(/`([^`]*)`/g, "$1").replace(/(?<!\\)(\*\*|__|~~|\*)/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/\\([\\`*_{}[\]()#+.!&<>|~-])/g, "$1");
|
|
2635
2660
|
}
|
|
2636
2661
|
function markMintlifyHeadings(body) {
|
|
2637
2662
|
let fence;
|
|
@@ -3343,7 +3368,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
3343
3368
|
}
|
|
3344
3369
|
if (runMintlify) rewritten = normalizeMintlifyParameterAnchors(rewritten);
|
|
3345
3370
|
rewritten = replaceOutsideCode(rewritten, (segment) => {
|
|
3346
|
-
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<
|
|
3371
|
+
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<Tree\.Folder/g, "<Folder").replace(/<\/Tree\.Folder>/g, "</Folder>").replace(/<Tree\.File/g, "<File").replace(/<\/Tree\.File>/g, "</File>");
|
|
3347
3372
|
if (runDocusaurus) {
|
|
3348
3373
|
result = result.replace(/<TabItem\b([^<>]*)>/g, (_match, attributes) => {
|
|
3349
3374
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
@@ -3352,7 +3377,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
3352
3377
|
result = normalizeDocusaurusLinkTags(result).replace(/<(?:DocCardList|TOCInline)\b[^<>]*\/>/g, "");
|
|
3353
3378
|
}
|
|
3354
3379
|
if (runMintlify) {
|
|
3355
|
-
result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
|
|
3380
|
+
result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Callout(\s[^<>]*)?>/g, (tag) => tag.replace(/(\s)type=(?:"[^"]*"|'[^']*'|\{[^{}]*\})/, '$1type="info"')).replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
|
|
3356
3381
|
result = pinStepsTitleSize(result);
|
|
3357
3382
|
}
|
|
3358
3383
|
if (runFern) {
|
|
@@ -3371,6 +3396,16 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
3371
3396
|
}
|
|
3372
3397
|
return normalizeFlowParagraphContainers(normalized);
|
|
3373
3398
|
}
|
|
3399
|
+
function externalPageUrl(value) {
|
|
3400
|
+
const url = typeof value === "string" ? value.trim() : "";
|
|
3401
|
+
if (!/^https?:\/\/[^\s]+$/i.test(url)) return void 0;
|
|
3402
|
+
try {
|
|
3403
|
+
new URL(url);
|
|
3404
|
+
return url;
|
|
3405
|
+
} catch {
|
|
3406
|
+
return void 0;
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3374
3409
|
function parseMarkdownPage(input) {
|
|
3375
3410
|
const parsed = parseFrontmatter(input.raw);
|
|
3376
3411
|
const fallbackIdentity = {
|
|
@@ -3395,6 +3430,7 @@ function parseMarkdownPage(input) {
|
|
|
3395
3430
|
const badge = typeof parsed.data.tag === "string" && parsed.data.tag.trim() ? parsed.data.tag.trim() : typeof parsed.data.badge === "string" && parsed.data.badge.trim() ? parsed.data.badge.trim() : void 0;
|
|
3396
3431
|
const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
|
|
3397
3432
|
const mode = sourceMode === "frame" ? "wide" : ["default", "wide", "custom", "center", "home"].includes(sourceMode ?? "") ? sourceMode : void 0;
|
|
3433
|
+
const externalUrl = input.platform === "mintlify" ? externalPageUrl(parsed.data.url) : void 0;
|
|
3398
3434
|
const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : typeof parsed.data.openapi === "string" && parsed.data.openapi.trim() ? "" : firstParagraph(body);
|
|
3399
3435
|
const page = {
|
|
3400
3436
|
id: identity.id,
|
|
@@ -3410,7 +3446,9 @@ function parseMarkdownPage(input) {
|
|
|
3410
3446
|
keywords,
|
|
3411
3447
|
mode,
|
|
3412
3448
|
hidden: parsed.data.hidden === true ? true : void 0,
|
|
3413
|
-
|
|
3449
|
+
// The path of a page that links elsewhere redirects, so it has no business in a sitemap.
|
|
3450
|
+
noindex: parsed.data.noindex === true || parsed.data.noindex === "true" || externalUrl ? true : void 0,
|
|
3451
|
+
...externalUrl ? { externalUrl } : {},
|
|
3414
3452
|
...Object.keys(meta).length > 0 ? { meta } : {},
|
|
3415
3453
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
3416
3454
|
...apiFrontmatter(parsed.data, input.warn),
|
|
@@ -5182,9 +5220,9 @@ function apiOperationCodeControls(body, specification, operation) {
|
|
|
5182
5220
|
}
|
|
5183
5221
|
|
|
5184
5222
|
// src/url-run.ts
|
|
5185
|
-
var CONVERTER_VERSION = "url-evidence-
|
|
5223
|
+
var CONVERTER_VERSION = "url-evidence-v7";
|
|
5186
5224
|
var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
|
|
5187
|
-
var ACCEPT = "text/
|
|
5225
|
+
var ACCEPT = "text/html,application/xhtml+xml";
|
|
5188
5226
|
var MAX_RESPONSE_BYTES = 8e6;
|
|
5189
5227
|
var MAX_ASSET_BYTES = 1e7;
|
|
5190
5228
|
var MAX_RECORDS = 1e5;
|
|
@@ -5214,6 +5252,11 @@ function canonical(value, base) {
|
|
|
5214
5252
|
return null;
|
|
5215
5253
|
}
|
|
5216
5254
|
}
|
|
5255
|
+
function sameRouteMarkdownRepresentation(alternate, primary) {
|
|
5256
|
+
const target2 = new URL(alternate), source = new URL(primary);
|
|
5257
|
+
const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
|
|
5258
|
+
return target2.origin === source.origin && target2.search === source.search && directory2(target2.pathname) === directory2(source.pathname) && /\.mdx?$/i.test(target2.pathname);
|
|
5259
|
+
}
|
|
5217
5260
|
function classifyMigrationResponse(document) {
|
|
5218
5261
|
if (document.status && document.status >= 400) return "unsupported";
|
|
5219
5262
|
if (/\/(?:llms(?:-full)?|robots)\.txt$|\/sitemap[^/]*\.xml$|\/search(?:\.html)?$/i.test(document.finalUrl.pathname) || /\.(?:json|ya?ml)$/i.test(document.finalUrl.pathname) || /(?:xml|json)/i.test(document.contentType)) return "index";
|
|
@@ -5464,7 +5507,7 @@ function resolveMigrationRecord(records, record) {
|
|
|
5464
5507
|
}
|
|
5465
5508
|
return record;
|
|
5466
5509
|
}
|
|
5467
|
-
function navigationConfig(checkpoint, records) {
|
|
5510
|
+
function navigationConfig(checkpoint, records, occurrencePages = /* @__PURE__ */ new Map()) {
|
|
5468
5511
|
const navigation = checkpoint.navigation;
|
|
5469
5512
|
const shortcuts = [];
|
|
5470
5513
|
const collectShortcuts = (nodes) => {
|
|
@@ -5489,7 +5532,7 @@ function navigationConfig(checkpoint, records) {
|
|
|
5489
5532
|
const project = (nodes) => nodes.flatMap((node) => {
|
|
5490
5533
|
const record = node.url ? records.get(canonical(node.url, node.url) ?? "") : void 0;
|
|
5491
5534
|
const target2 = resolveMigrationRecord(records, record);
|
|
5492
|
-
const page = target2?.page;
|
|
5535
|
+
const page = occurrencePages.get(node) ?? target2?.page;
|
|
5493
5536
|
const children = project(node.children ?? []);
|
|
5494
5537
|
if (children.length) {
|
|
5495
5538
|
const contains = (entries) => entries.some((entry) => typeof entry === "string" ? entry === (page && projection.navigationId(page)) : contains(entry.pages));
|
|
@@ -5789,8 +5832,19 @@ async function runUrlMigration(options) {
|
|
|
5789
5832
|
if (!record.evidence) continue;
|
|
5790
5833
|
const final = canonical(record.evidence.finalUrl, sourceUrl);
|
|
5791
5834
|
if (!final) continue;
|
|
5792
|
-
if (record.outcome === "imported")
|
|
5793
|
-
|
|
5835
|
+
if (record.outcome === "imported") {
|
|
5836
|
+
enqueueArticleLinks(record.evidence.links, final, record.url);
|
|
5837
|
+
for (const reference of record.evidence.markdownRepresentations ?? []) {
|
|
5838
|
+
const alternate = canonical(reference, final);
|
|
5839
|
+
if (!alternate || !sameRouteMarkdownRepresentation(alternate, final)) continue;
|
|
5840
|
+
enqueue(alternate, final, "declared-markdown-representation");
|
|
5841
|
+
const sibling = records.get(alternate);
|
|
5842
|
+
if (sibling && sibling.representationOf !== record.url) {
|
|
5843
|
+
sibling.representationOf = record.url;
|
|
5844
|
+
changed.push(sibling);
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
} else if (record.outcome === "excluded") {
|
|
5794
5848
|
if (classifyMigrationResponse({ finalUrl: new URL(final), contentType: record.evidence.contentType, body: "" }) === "index") {
|
|
5795
5849
|
for (const link of record.evidence.links) enqueue(link, final, record.url);
|
|
5796
5850
|
}
|
|
@@ -5873,6 +5927,7 @@ async function runUrlMigration(options) {
|
|
|
5873
5927
|
let claimedId;
|
|
5874
5928
|
let claimedFinal;
|
|
5875
5929
|
let markdownRepresentation;
|
|
5930
|
+
const markdownRepresentations = [];
|
|
5876
5931
|
let exactKey;
|
|
5877
5932
|
try {
|
|
5878
5933
|
delete record.reason;
|
|
@@ -5965,13 +6020,13 @@ async function runUrlMigration(options) {
|
|
|
5965
6020
|
$('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
|
|
5966
6021
|
const alternate = canonical($(node).attr("href"), final);
|
|
5967
6022
|
if (!alternate) return;
|
|
5968
|
-
|
|
5969
|
-
const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
|
|
5970
|
-
if (target2.origin !== primary.origin || target2.search !== primary.search || directory2(target2.pathname) !== directory2(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
|
|
6023
|
+
if (!sameRouteMarkdownRepresentation(alternate, final)) return;
|
|
5971
6024
|
enqueue(alternate, final, "declared-markdown-representation");
|
|
5972
6025
|
const sibling = records.get(alternate);
|
|
6026
|
+
if (!sibling) return;
|
|
5973
6027
|
sibling.representationOf = record.url;
|
|
5974
6028
|
markdownRepresentation ??= alternate;
|
|
6029
|
+
markdownRepresentations.push(alternate);
|
|
5975
6030
|
changed.push(sibling);
|
|
5976
6031
|
});
|
|
5977
6032
|
}
|
|
@@ -5984,7 +6039,8 @@ async function runUrlMigration(options) {
|
|
|
5984
6039
|
}
|
|
5985
6040
|
enqueueNavigation(observedNavigation, final);
|
|
5986
6041
|
}
|
|
5987
|
-
|
|
6042
|
+
const replacesRepresentation = imported >= maxPages && markdownRepresentations.some((url) => records.get(url)?.page);
|
|
6043
|
+
if (imported >= maxPages && !replacesRepresentation) {
|
|
5988
6044
|
record.outcome = "excluded";
|
|
5989
6045
|
record.reason = "A discovery endpoint resolved to an article after the selected page limit.";
|
|
5990
6046
|
return;
|
|
@@ -6003,14 +6059,17 @@ async function runUrlMigration(options) {
|
|
|
6003
6059
|
const locale = language.locale;
|
|
6004
6060
|
const navigationId = locale ? id.split("/").filter((segment) => segment.toLowerCase() !== locale.toLowerCase()).join("/") || "introduction" : id;
|
|
6005
6061
|
const identityId = locale ? `${locale}/${navigationId}` : id;
|
|
6006
|
-
|
|
6062
|
+
const owner = records.get(usedIds.get(identityId) ?? "");
|
|
6063
|
+
const sharesDeclaredRepresentation = owner && (owner.representationOf === record.url || record.representationOf === owner.url);
|
|
6064
|
+
if (owner && owner !== record && !sharesDeclaredRepresentation) {
|
|
6007
6065
|
record.outcome = "failed";
|
|
6008
6066
|
record.reason = `Destination collision for ${identityId}.`;
|
|
6009
6067
|
return;
|
|
6010
6068
|
}
|
|
6011
|
-
usedIds.set(identityId, record.url);
|
|
6069
|
+
if (!owner || owner === record || owner.representationOf === record.url) usedIds.set(identityId, record.url);
|
|
6012
6070
|
claimedId = identityId;
|
|
6013
6071
|
const evidence = evidenceOf(document);
|
|
6072
|
+
if (markdownRepresentations.length) evidence.markdownRepresentations = markdownRepresentations;
|
|
6014
6073
|
const page = convert(document, id);
|
|
6015
6074
|
if (!page) {
|
|
6016
6075
|
record.outcome = "failed";
|
|
@@ -6221,6 +6280,7 @@ ${difference}`;
|
|
|
6221
6280
|
if (!primary || primary === record) continue;
|
|
6222
6281
|
const winner = primary.outcome === "imported" ? primary : record.outcome === "imported" && primary.outcome === "failed" ? record : void 0;
|
|
6223
6282
|
if (!winner) continue;
|
|
6283
|
+
if (winner.page) usedIds.set(winner.page.id, winner.url);
|
|
6224
6284
|
const alternate = winner === primary ? record : primary;
|
|
6225
6285
|
if (alternate.outcome === "alias" && alternate.aliasOf === winner.url) continue;
|
|
6226
6286
|
if (alternate.outcome === "imported") {
|
|
@@ -6242,7 +6302,17 @@ ${difference}`;
|
|
|
6242
6302
|
settleArticleIdentities();
|
|
6243
6303
|
while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
|
|
6244
6304
|
if (imported >= maxPages) {
|
|
6245
|
-
const index = pending.findIndex((record, position) =>
|
|
6305
|
+
const index = pending.findIndex((record, position) => {
|
|
6306
|
+
if (position < cursor || record.outcome !== "pending") return false;
|
|
6307
|
+
const url = new URL(record.url);
|
|
6308
|
+
if (isDiscoveryIndex(url, source)) return true;
|
|
6309
|
+
if (/\.mdx?$/i.test(url.pathname)) return false;
|
|
6310
|
+
return [".md", ".mdx", "/index.md", "/index.mdx"].some((extension) => {
|
|
6311
|
+
const alternate = new URL(url);
|
|
6312
|
+
alternate.pathname = alternate.pathname.replace(/\/index$/i, "") + extension;
|
|
6313
|
+
return records.get(alternate.href)?.outcome === "imported";
|
|
6314
|
+
});
|
|
6315
|
+
});
|
|
6246
6316
|
if (index < 0) break;
|
|
6247
6317
|
pending.splice(cursor, 0, pending.splice(index, 1)[0]);
|
|
6248
6318
|
}
|
|
@@ -6266,19 +6336,60 @@ ${difference}`;
|
|
|
6266
6336
|
}
|
|
6267
6337
|
const navigationLabels = /* @__PURE__ */ new Map();
|
|
6268
6338
|
const navigationIssues = [];
|
|
6339
|
+
const navigationOccurrences = /* @__PURE__ */ new Map();
|
|
6269
6340
|
const labelPages = (nodes) => {
|
|
6270
6341
|
for (const node of nodes) {
|
|
6271
6342
|
const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
|
|
6272
6343
|
const target2 = resolveMigrationRecord(records, entry);
|
|
6273
|
-
if (target2?.page && node.label && !node.children?.length && !entry
|
|
6274
|
-
const
|
|
6275
|
-
|
|
6276
|
-
|
|
6344
|
+
if (entry && target2?.page && node.label && !node.children?.length && !entry.aliasAnchor) {
|
|
6345
|
+
const occurrences = navigationOccurrences.get(target2) ?? [];
|
|
6346
|
+
occurrences.push({ node, entry });
|
|
6347
|
+
navigationOccurrences.set(target2, occurrences);
|
|
6277
6348
|
}
|
|
6278
6349
|
labelPages(node.children ?? []);
|
|
6279
6350
|
}
|
|
6280
6351
|
};
|
|
6281
6352
|
labelPages(checkpoint.navigation);
|
|
6353
|
+
const occurrencePages = /* @__PURE__ */ new Map();
|
|
6354
|
+
const navigationAliases = /* @__PURE__ */ new Map();
|
|
6355
|
+
const occupiedPageIds = new Set(checkpoint.records.flatMap((record) => record.page ? [projection.pageId(record.page)] : []));
|
|
6356
|
+
const navigationRedirects = [];
|
|
6357
|
+
for (const [target2, occurrences] of navigationOccurrences) {
|
|
6358
|
+
const final = target2.evidence && canonical(target2.evidence.finalUrl, sourceUrl);
|
|
6359
|
+
const primary = occurrences.find(({ entry }) => entry.url === final) ?? occurrences.find(({ entry }) => entry === target2) ?? occurrences[0];
|
|
6360
|
+
navigationLabels.set(target2.url, primary.node.label);
|
|
6361
|
+
for (const { node, entry } of occurrences) {
|
|
6362
|
+
if (node.label === primary.node.label) continue;
|
|
6363
|
+
const existing = navigationAliases.get(entry.url);
|
|
6364
|
+
if (existing?.page.navTitle === node.label && existing.target === target2) {
|
|
6365
|
+
occurrencePages.set(node, existing.page);
|
|
6366
|
+
continue;
|
|
6367
|
+
}
|
|
6368
|
+
const sourceRoute = new URL(entry.url);
|
|
6369
|
+
const id = inScope(sourceRoute, source, scopePath) ? idForUrl(sourceRoute, scopePath) : null;
|
|
6370
|
+
const locale = projection.canonicalLocale(target2.page);
|
|
6371
|
+
let navigationId = id && (locale ? id.split("/").filter((segment) => segment.toLowerCase() !== locale.toLowerCase()).join("/") || "introduction" : id);
|
|
6372
|
+
const legacyPrefix = locale && `${locale.toLowerCase()}-`;
|
|
6373
|
+
if (navigationId && legacyPrefix && projection.navigationId(target2.page) !== target2.page.navigationId && navigationId.toLowerCase().startsWith(legacyPrefix)) navigationId = navigationId.slice(legacyPrefix.length);
|
|
6374
|
+
const page = navigationId ? {
|
|
6375
|
+
...target2.page,
|
|
6376
|
+
id: locale ? `${locale}/${navigationId}` : navigationId,
|
|
6377
|
+
navigationId,
|
|
6378
|
+
...locale ? { locale } : {},
|
|
6379
|
+
navTitle: node.label,
|
|
6380
|
+
source: entry.url,
|
|
6381
|
+
noindex: true
|
|
6382
|
+
} : void 0;
|
|
6383
|
+
if (!page || existing || occupiedPageIds.has(projection.pageId(page))) {
|
|
6384
|
+
navigationIssues.push({ code: "unsupported-component", source: target2.url, message: `This page has multiple source navigation labels (${primary.node.label}, ${node.label}); their source routes cannot retain distinct sidebar titles safely.` });
|
|
6385
|
+
continue;
|
|
6386
|
+
}
|
|
6387
|
+
occupiedPageIds.add(projection.pageId(page));
|
|
6388
|
+
navigationAliases.set(entry.url, { page, target: target2 });
|
|
6389
|
+
occurrencePages.set(node, page);
|
|
6390
|
+
navigationRedirects.push({ source: projection.route(page), destination: projection.route(target2.page), permanent: true });
|
|
6391
|
+
}
|
|
6392
|
+
}
|
|
6282
6393
|
const pages = checkpoint.records.flatMap((record) => record.page ? [{ ...record.page, id: projection.pageId(record.page), navigationId: projection.navigationId(record.page), ...projection.canonicalLocale(record.page) ? { locale: projection.canonicalLocale(record.page) } : {}, ...navigationLabels.has(record.url) ? { navTitle: navigationLabels.get(record.url) } : {}, body: rewriteMarkdownDestinations(record.page.body, (value) => {
|
|
6283
6394
|
let url;
|
|
6284
6395
|
try {
|
|
@@ -6299,6 +6410,10 @@ ${difference}`;
|
|
|
6299
6410
|
const hasBrokenSourceAnchor = Boolean(url.hash && destination?.outcome === "imported" && destination.evidence && !destination.evidence.anchors.includes(anchor));
|
|
6300
6411
|
return `${route}${hasBrokenSourceAnchor ? "" : effectiveHash}`;
|
|
6301
6412
|
}) }] : []);
|
|
6413
|
+
const projectedPages = new Map(pages.map((page) => [page.id, page]));
|
|
6414
|
+
for (const { page, target: target2 } of navigationAliases.values()) {
|
|
6415
|
+
pages.push({ ...page, id: projection.pageId(page), body: projectedPages.get(projection.pageId(target2.page)).body });
|
|
6416
|
+
}
|
|
6302
6417
|
const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
|
|
6303
6418
|
validation.issues.push(...navigationIssues);
|
|
6304
6419
|
if (navigationIssues.length && validation.status === "passed") validation.status = "partial";
|
|
@@ -6319,12 +6434,12 @@ ${difference}`;
|
|
|
6319
6434
|
platform: options.platform ?? "generic",
|
|
6320
6435
|
pages,
|
|
6321
6436
|
assets: [...assets.values()],
|
|
6322
|
-
docsConfig: navigationConfig(checkpoint, records) ?? buildNavigationFromPages(pages),
|
|
6437
|
+
docsConfig: navigationConfig(checkpoint, records, occurrencePages) ?? buildNavigationFromPages(pages),
|
|
6323
6438
|
warnings: [...validation.issues, ...validation.warnings ?? []].map((issue) => ({ code: issue.code === "missing-page" ? "missing-page" : "unsupported-config", message: issue.message, source: issue.source })),
|
|
6324
6439
|
urlMigrationValidation: validation,
|
|
6325
6440
|
stats: { discovered: records.size, imported: pages.length, skipped: records.size - pages.length }
|
|
6326
6441
|
};
|
|
6327
|
-
if (projection.redirects.length) bundle.docsConfig.redirects = projection.redirects;
|
|
6442
|
+
if (projection.redirects.length || navigationRedirects.length) bundle.docsConfig.redirects = [...projection.redirects, ...navigationRedirects];
|
|
6328
6443
|
if (projection.locales.length) {
|
|
6329
6444
|
const locales = [.../* @__PURE__ */ new Set([projection.defaultLocale, ...projection.locales])];
|
|
6330
6445
|
bundle.docsConfig.i18n = { ...bundle.docsConfig.i18n, defaultLocale: projection.defaultLocale, locales: locales.map((code) => ({ code, label: code === "en" ? "English" : code })) };
|
|
@@ -6342,9 +6457,11 @@ function rewriteMarkdownDestinations(body, rewrite) {
|
|
|
6342
6457
|
const original = body.slice(start, end);
|
|
6343
6458
|
const replacement = rewrite(node.url);
|
|
6344
6459
|
if (replacement === node.url) return null;
|
|
6345
|
-
const
|
|
6346
|
-
|
|
6347
|
-
|
|
6460
|
+
const destinations = postprocess(parse2().document().write(preprocess()(original, void 0, true))).filter(([event, token]) => event === "enter" && ["resourceDestinationString", "definitionDestinationString", "autolinkProtocol", "autolinkEmail"].includes(token.type));
|
|
6461
|
+
const destination = destinations.at(-1)?.[1];
|
|
6462
|
+
if (!destination) return null;
|
|
6463
|
+
const escaped = replacement.replace(/[\\&()<>]/g, "\\$&").replace(/[\t\n\r ]/g, (character) => encodeURIComponent(character));
|
|
6464
|
+
return { start: start + destination.start.offset, end: start + destination.end.offset, replacement: escaped };
|
|
6348
6465
|
}).filter((edit) => edit !== null).sort((a, b) => b.start - a.start);
|
|
6349
6466
|
for (const node of markdownNodes(body)) for (const attribute of node.attributes ?? []) {
|
|
6350
6467
|
if (!attribute.name || !["src", "poster", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
|
|
@@ -6477,7 +6594,7 @@ function renderPage(bundle, page) {
|
|
|
6477
6594
|
page.icon ? `icon: ${yamlString(page.icon)}` : null,
|
|
6478
6595
|
page.iconType ? `iconType: ${yamlString(page.iconType)}` : null,
|
|
6479
6596
|
`description: ${yamlString(page.description)}`,
|
|
6480
|
-
page.descriptionPlacement === "body" ?
|
|
6597
|
+
page.descriptionPlacement === "body" || page.descriptionPlacement === "operation" ? `descriptionPlacement: ${page.descriptionPlacement}` : null,
|
|
6481
6598
|
page.badge ? `badge: ${yamlString(page.badge)}` : null,
|
|
6482
6599
|
page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(", ")}]` : null,
|
|
6483
6600
|
page.mode ? `mode: ${yamlString(page.mode)}` : null,
|