@thallylabs/migrate 0.2.5 → 0.2.6
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 +4 -0
- package/dist/index.js +212 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,10 @@ interface MigrationDocsConfig {
|
|
|
66
66
|
display?: 'tabs' | 'dropdown';
|
|
67
67
|
};
|
|
68
68
|
theme?: 'default' | 'maple' | 'sharp' | 'minimal';
|
|
69
|
+
/** Icon set for content `icon` names; Mintlify's `icons.library` carries through. */
|
|
70
|
+
icons?: {
|
|
71
|
+
library?: 'lucide' | 'fontawesome' | 'tabler';
|
|
72
|
+
};
|
|
69
73
|
banner?: MigrationBannerConfig;
|
|
70
74
|
navbar?: MigrationNavbarConfig;
|
|
71
75
|
footer?: MigrationFooterConfig;
|
package/dist/index.js
CHANGED
|
@@ -1104,8 +1104,30 @@ function normalizeDocusaurusAdmonitions(body) {
|
|
|
1104
1104
|
return line;
|
|
1105
1105
|
}).join("\n");
|
|
1106
1106
|
}
|
|
1107
|
+
var GLOBAL_DOCUSARUS_COMPONENTS = /* @__PURE__ */ new Set([
|
|
1108
|
+
"Tabs",
|
|
1109
|
+
"TabItem",
|
|
1110
|
+
"Link",
|
|
1111
|
+
"DocCardList",
|
|
1112
|
+
"TOCInline"
|
|
1113
|
+
]);
|
|
1114
|
+
function isGlobalDocusaurusImport(line) {
|
|
1115
|
+
const trimmed = line.trim().replace(/;$/, "").trimEnd();
|
|
1116
|
+
if (!trimmed.startsWith("import ")) return false;
|
|
1117
|
+
const separator = trimmed.indexOf(" from ", "import ".length);
|
|
1118
|
+
if (separator < 0) return false;
|
|
1119
|
+
const component = trimmed.slice("import ".length, separator).trim();
|
|
1120
|
+
if (!GLOBAL_DOCUSARUS_COMPONENTS.has(component)) return false;
|
|
1121
|
+
const source = trimmed.slice(separator + " from ".length);
|
|
1122
|
+
if (source.length < 3) return false;
|
|
1123
|
+
const quote = source[0];
|
|
1124
|
+
if (quote !== "'" && quote !== '"' || source.at(-1) !== quote) return false;
|
|
1125
|
+
const moduleName = source.slice(1, -1);
|
|
1126
|
+
return moduleName.startsWith("@theme/") || moduleName.startsWith("@docusaurus/");
|
|
1127
|
+
}
|
|
1107
1128
|
function normalizeMdx(body) {
|
|
1108
|
-
|
|
1129
|
+
const withoutGlobalImports = body.split("\n").filter((line) => !isGlobalDocusaurusImport(line)).join("\n");
|
|
1130
|
+
return normalizeDocusaurusAdmonitions(withoutGlobalImports).replace(/<TabItem\b([^>]*)>/g, (_match, attributes) => {
|
|
1109
1131
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
1110
1132
|
return `<Tab title="${title.replace(/"/g, """)}">`;
|
|
1111
1133
|
}).replace(/<\/TabItem>/g, "</Tab>").replace(/<Link\b([^>]*)\bto=(?:"([^"]*)"|'([^']*)')([^>]*)>/g, (_match, before, doubleQuoted, singleQuoted, after) => `<a${before}href="${doubleQuoted ?? singleQuoted}"${after}>`).replace(/<\/Link>/g, "</a>").replace(/<(?:DocCardList|TOCInline)\b[^>]*\/>/g, "").replace(/<!--([\s\S]*?)-->/g, (_match, content) => `{/*${content}*/}`).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<Check(\s[^>]*)?>/g, "<Note$1>").replace(/<\/Check>/g, "</Note>").replace(/<Tree\.Folder/g, "<Folder").replace(/<\/Tree\.Folder>/g, "</Folder>").replace(/<Tree\.File/g, "<File").replace(/<\/Tree\.File>/g, "</File>");
|
|
@@ -1571,6 +1593,8 @@ function projectedCompatibleConfig(config) {
|
|
|
1571
1593
|
const headingFont = projectedFont(objectValue2(config.fonts)?.heading);
|
|
1572
1594
|
const feedback = objectValue2(config.feedback);
|
|
1573
1595
|
const seo = objectValue2(config.seo);
|
|
1596
|
+
const iconLibrary = objectValue2(config.icons)?.library;
|
|
1597
|
+
const projectedIconLibrary = ["lucide", "fontawesome", "tabler"].includes(String(iconLibrary)) ? String(iconLibrary) : void 0;
|
|
1574
1598
|
const navbar = projectedNavbar(config.navbar);
|
|
1575
1599
|
const globalLinks = projectedGlobalNavigationLinks(objectValue2(config.navigation)?.global);
|
|
1576
1600
|
const navbarLinks = [...new Map(
|
|
@@ -1579,6 +1603,7 @@ function projectedCompatibleConfig(config) {
|
|
|
1579
1603
|
const projectedNavigation = navbarLinks.length > 0 || navbar?.primary ? { ...navbarLinks.length > 0 ? { links: navbarLinks } : {}, ...navbar?.primary ? { primary: navbar.primary } : {} } : void 0;
|
|
1580
1604
|
return {
|
|
1581
1605
|
...projectedTheme(config.theme) ? { theme: projectedTheme(config.theme) } : {},
|
|
1606
|
+
...projectedIconLibrary ? { icons: { library: projectedIconLibrary } } : {},
|
|
1582
1607
|
...bannerContent ? {
|
|
1583
1608
|
banner: {
|
|
1584
1609
|
content: bannerContent,
|
|
@@ -2670,7 +2695,10 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
2670
2695
|
|
|
2671
2696
|
// src/url.ts
|
|
2672
2697
|
import { load } from "cheerio";
|
|
2698
|
+
import remarkMdx3 from "remark-mdx";
|
|
2699
|
+
import remarkParse3 from "remark-parse";
|
|
2673
2700
|
import TurndownService from "turndown";
|
|
2701
|
+
import { unified as unified3 } from "unified";
|
|
2674
2702
|
import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
|
|
2675
2703
|
var DEFAULT_MAX_PAGES = 1e3;
|
|
2676
2704
|
var DEFAULT_MAX_TOTAL_BYTES = 1e8;
|
|
@@ -2773,6 +2801,8 @@ var LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
|
2773
2801
|
"zh-hant"
|
|
2774
2802
|
]);
|
|
2775
2803
|
var PORTABLE_URL_PROPS = "href|src|img|primaryHref|secondaryHref";
|
|
2804
|
+
var PORTABLE_URL_PROP_NAMES = new Set(PORTABLE_URL_PROPS.split("|"));
|
|
2805
|
+
var remoteMarkdownParser = unified3().use(remarkParse3).use(remarkMdx3);
|
|
2776
2806
|
function validateMigrationUrl(value) {
|
|
2777
2807
|
let url;
|
|
2778
2808
|
try {
|
|
@@ -2902,7 +2932,7 @@ function detectUrlPlatform(document) {
|
|
|
2902
2932
|
const value = `${document.body.slice(0, 2e5)} ${headers}`.toLowerCase();
|
|
2903
2933
|
if (value.includes("__mintlify") || value.includes("/mintlify-assets/") || value.includes("x-mintlify-") || value.includes("/_mintlify/")) return "mintlify";
|
|
2904
2934
|
if (value.includes("docusaurus") || value.includes("__docusaurus")) return "docusaurus";
|
|
2905
|
-
if (value.includes("gitbook")
|
|
2935
|
+
if (value.includes("gitbook")) return "gitbook";
|
|
2906
2936
|
if (value.includes("nextra")) return "nextra";
|
|
2907
2937
|
if (value.includes("vitepress")) return "vitepress";
|
|
2908
2938
|
if (value.includes("starlight")) return "starlight";
|
|
@@ -3041,19 +3071,184 @@ function stripEventHandlerProps(line) {
|
|
|
3041
3071
|
}
|
|
3042
3072
|
return cursor === 0 ? line : `${result}${line.slice(cursor)}`;
|
|
3043
3073
|
}
|
|
3044
|
-
function
|
|
3045
|
-
|
|
3046
|
-
|
|
3074
|
+
function isSafePortableUrl(value) {
|
|
3075
|
+
try {
|
|
3076
|
+
const parsed = new URL(value, "https://relative.thally.invalid");
|
|
3077
|
+
return ["http:", "https:", "mailto:", "tel:"].includes(parsed.protocol);
|
|
3078
|
+
} catch {
|
|
3079
|
+
return false;
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
function decodeHtmlAttribute(value) {
|
|
3083
|
+
const document = load(`<i data-thally-value="${value.replace(/"/g, """)}"></i>`);
|
|
3084
|
+
return document("i").attr("data-thally-value") ?? "";
|
|
3085
|
+
}
|
|
3086
|
+
function escapeMarkdownText(value, escapeTableSeparator = false) {
|
|
3087
|
+
let escaped = "";
|
|
3088
|
+
for (const character of value) {
|
|
3089
|
+
if (character === "\\") escaped += "\\\\";
|
|
3090
|
+
else if (character === "[" || character === "]") escaped += `\\${character}`;
|
|
3091
|
+
else if (character === "<") escaped += "<";
|
|
3092
|
+
else if (character === ">") escaped += ">";
|
|
3093
|
+
else if (character === "{") escaped += "{";
|
|
3094
|
+
else if (character === "}") escaped += "}";
|
|
3095
|
+
else if (character === "|" && escapeTableSeparator) escaped += "\\|";
|
|
3096
|
+
else escaped += character;
|
|
3097
|
+
}
|
|
3098
|
+
return escaped;
|
|
3099
|
+
}
|
|
3100
|
+
function markdownLinkDestination(value) {
|
|
3101
|
+
try {
|
|
3102
|
+
return encodeURI(value).replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/&/g, "&");
|
|
3103
|
+
} catch {
|
|
3104
|
+
return null;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
function htmlOpeningTagEnd(value, start) {
|
|
3108
|
+
let quote = "";
|
|
3109
|
+
for (let index = start; index < value.length; index += 1) {
|
|
3110
|
+
const character = value[index];
|
|
3111
|
+
if (quote) {
|
|
3112
|
+
if (character === quote) quote = "";
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
if (character === '"' || character === "'") {
|
|
3116
|
+
quote = character;
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
if (character === ">") return index + 1;
|
|
3120
|
+
}
|
|
3121
|
+
return null;
|
|
3122
|
+
}
|
|
3123
|
+
function replacePortableHtmlImages(value) {
|
|
3124
|
+
const matcher = /<img\b/gi;
|
|
3125
|
+
let cursor = 0;
|
|
3126
|
+
let output = "";
|
|
3127
|
+
for (const match of value.matchAll(matcher)) {
|
|
3128
|
+
const start = match.index;
|
|
3129
|
+
if (start < cursor) continue;
|
|
3130
|
+
const end = htmlOpeningTagEnd(value, start + match[0].length);
|
|
3131
|
+
if (end === null) continue;
|
|
3132
|
+
const fragment = load(value.slice(start, end));
|
|
3133
|
+
const image = fragment("img").first();
|
|
3134
|
+
const source = image.attr("src");
|
|
3135
|
+
const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
|
|
3136
|
+
const alternative = escapeMarkdownText(image.attr("alt") ?? "");
|
|
3137
|
+
output += value.slice(cursor, start);
|
|
3138
|
+
output += destination ? `` : alternative;
|
|
3139
|
+
cursor = end;
|
|
3140
|
+
}
|
|
3141
|
+
return cursor === 0 ? value : `${output}${value.slice(cursor)}`;
|
|
3142
|
+
}
|
|
3143
|
+
function replacePortableHtmlAnchors(value) {
|
|
3144
|
+
const matcher = /<a\b/gi;
|
|
3145
|
+
let cursor = 0;
|
|
3146
|
+
let output = "";
|
|
3147
|
+
for (const match of value.matchAll(matcher)) {
|
|
3148
|
+
const start = match.index;
|
|
3149
|
+
if (start < cursor) continue;
|
|
3150
|
+
const openingEnd = htmlOpeningTagEnd(value, start + match[0].length);
|
|
3151
|
+
if (openingEnd === null) continue;
|
|
3152
|
+
const closingMatch = /<\/a\s*>/gi;
|
|
3153
|
+
closingMatch.lastIndex = openingEnd;
|
|
3154
|
+
const closing = closingMatch.exec(value);
|
|
3155
|
+
if (!closing) continue;
|
|
3156
|
+
const end = closing.index + closing[0].length;
|
|
3157
|
+
const fragment = load(value.slice(start, end));
|
|
3158
|
+
const anchor = fragment("a").first();
|
|
3159
|
+
const href = anchor.attr("href");
|
|
3160
|
+
const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
|
|
3161
|
+
const destination = href && isSafePortableUrl(href) ? markdownLinkDestination(href) : null;
|
|
3162
|
+
output += value.slice(cursor, start);
|
|
3163
|
+
output += destination ? `[${label}](${destination})` : label;
|
|
3164
|
+
cursor = end;
|
|
3165
|
+
}
|
|
3166
|
+
return cursor === 0 ? value : `${output}${value.slice(cursor)}`;
|
|
3167
|
+
}
|
|
3168
|
+
function markdownNodeText(node) {
|
|
3169
|
+
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
3170
|
+
if (node.type === "image" || node.type === "imageReference") return node.alt ?? "";
|
|
3171
|
+
return (node.children ?? []).map(markdownNodeText).join("");
|
|
3172
|
+
}
|
|
3173
|
+
function sanitizeMarkdownUrls(value) {
|
|
3174
|
+
let root;
|
|
3175
|
+
try {
|
|
3176
|
+
root = remoteMarkdownParser.parse(value);
|
|
3177
|
+
} catch {
|
|
3178
|
+
return null;
|
|
3179
|
+
}
|
|
3180
|
+
let hasUnsafeMdxSyntax = false;
|
|
3181
|
+
const validateMdxSyntax = (node) => {
|
|
3182
|
+
if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" || node.type === "mdxjsEsm") {
|
|
3183
|
+
hasUnsafeMdxSyntax = true;
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
if (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") {
|
|
3187
|
+
for (const attribute of node.attributes ?? []) {
|
|
3188
|
+
if (attribute.type !== "mdxJsxAttribute" || !attribute.name || /^on[A-Z]/.test(attribute.name)) {
|
|
3189
|
+
hasUnsafeMdxSyntax = true;
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
let staticValue = attribute.value;
|
|
3193
|
+
if (attribute.value && typeof attribute.value === "object") {
|
|
3194
|
+
if (attribute.value.type !== "mdxJsxAttributeValueExpression") {
|
|
3195
|
+
hasUnsafeMdxSyntax = true;
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
try {
|
|
3199
|
+
staticValue = JSON.parse(attribute.value.value);
|
|
3200
|
+
} catch {
|
|
3201
|
+
hasUnsafeMdxSyntax = true;
|
|
3202
|
+
return;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
if (PORTABLE_URL_PROP_NAMES.has(attribute.name) && (typeof staticValue !== "string" || !isSafePortableUrl(decodeHtmlAttribute(staticValue)))) {
|
|
3206
|
+
hasUnsafeMdxSyntax = true;
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
for (const child of node.children ?? []) validateMdxSyntax(child);
|
|
3212
|
+
};
|
|
3213
|
+
validateMdxSyntax(root);
|
|
3214
|
+
if (hasUnsafeMdxSyntax) return null;
|
|
3215
|
+
const unsafeDefinitions = /* @__PURE__ */ new Set();
|
|
3216
|
+
const visitDefinitions = (node) => {
|
|
3217
|
+
if (node.type === "definition" && node.identifier && node.url && !isSafePortableUrl(node.url)) unsafeDefinitions.add(node.identifier.toLowerCase());
|
|
3218
|
+
for (const child of node.children ?? []) visitDefinitions(child);
|
|
3219
|
+
};
|
|
3220
|
+
visitDefinitions(root);
|
|
3221
|
+
const replacements = [];
|
|
3222
|
+
const replaceNode = (node, replacement) => {
|
|
3223
|
+
const start = node.position?.start.offset;
|
|
3224
|
+
const end = node.position?.end.offset;
|
|
3225
|
+
if (start === void 0 || end === void 0) return false;
|
|
3226
|
+
replacements.push({ start, end, value: replacement });
|
|
3227
|
+
return true;
|
|
3228
|
+
};
|
|
3229
|
+
const visitUrls = (node) => {
|
|
3230
|
+
const hasUnsafeDirectUrl = (node.type === "link" || node.type === "image") && typeof node.url === "string" && !isSafePortableUrl(node.url);
|
|
3231
|
+
const hasUnsafeReference = (node.type === "linkReference" || node.type === "imageReference") && typeof node.identifier === "string" && unsafeDefinitions.has(node.identifier.toLowerCase());
|
|
3232
|
+
if (hasUnsafeDirectUrl || hasUnsafeReference) {
|
|
3233
|
+
if (replaceNode(node, escapeMarkdownText(markdownNodeText(node)))) return;
|
|
3234
|
+
}
|
|
3235
|
+
if (node.type === "definition" && node.identifier && unsafeDefinitions.has(node.identifier.toLowerCase())) {
|
|
3236
|
+
if (replaceNode(node, "")) return;
|
|
3237
|
+
}
|
|
3238
|
+
for (const child of node.children ?? []) visitUrls(child);
|
|
3239
|
+
};
|
|
3240
|
+
visitUrls(root);
|
|
3241
|
+
return replacements.sort((left, right) => right.start - left.start).reduce((result, replacement) => `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`, value);
|
|
3047
3242
|
}
|
|
3048
3243
|
function hasUnsafePortableUrlProp(value) {
|
|
3049
3244
|
const quotedProps = new RegExp(`\\b(?:${PORTABLE_URL_PROPS})\\s*=\\s*(['"])([^'"\\r\\n]*)\\1`, "gi");
|
|
3050
3245
|
for (const match of value.matchAll(quotedProps)) {
|
|
3051
|
-
if (
|
|
3246
|
+
if (!isSafePortableUrl(decodeHtmlAttribute(match[2]))) return true;
|
|
3052
3247
|
}
|
|
3053
3248
|
const expressionProps = new RegExp(`\\b(?:${PORTABLE_URL_PROPS})\\s*=\\s*\\{\\s*("(?:\\\\.|[^"\\\\])*")\\s*\\}`, "gi");
|
|
3054
3249
|
for (const match of value.matchAll(expressionProps)) {
|
|
3055
3250
|
try {
|
|
3056
|
-
if (
|
|
3251
|
+
if (!isSafePortableUrl(decodeHtmlAttribute(JSON.parse(match[1])))) return true;
|
|
3057
3252
|
} catch {
|
|
3058
3253
|
return true;
|
|
3059
3254
|
}
|
|
@@ -3061,12 +3256,7 @@ function hasUnsafePortableUrlProp(value) {
|
|
|
3061
3256
|
return false;
|
|
3062
3257
|
}
|
|
3063
3258
|
function portableHtmlToMarkdown(value) {
|
|
3064
|
-
return value.replace(/<span\b[^>]*>|<\/span>/gi, "").replace(/<
|
|
3065
|
-
const href = attributes.match(/\bhref\s*=\s*(['"])(.*?)\1/i)?.[2];
|
|
3066
|
-
const plainLabel = label.replace(/<[^>]+>/g, "").trim();
|
|
3067
|
-
if (!href || hasUnsafeUrlScheme(href)) return plainLabel;
|
|
3068
|
-
return `[${plainLabel.replace(/]/g, "\\]")}](${href})`;
|
|
3069
|
-
}).replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_original, level, content) => {
|
|
3259
|
+
return replacePortableHtmlAnchors(replacePortableHtmlImages(value)).replace(/<span\b[^>]*>|<\/span>/gi, "").replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_original, level, content) => {
|
|
3070
3260
|
return `
|
|
3071
3261
|
${"#".repeat(Number(level))} ${content.trim()}
|
|
3072
3262
|
`;
|
|
@@ -3123,16 +3313,11 @@ function sanitizeRemoteMarkdown(body) {
|
|
|
3123
3313
|
return line;
|
|
3124
3314
|
}
|
|
3125
3315
|
if (fenceCharacter) return line;
|
|
3126
|
-
return portableHtmlToMarkdown(stripEventHandlerProps(line.replace(/!\[([^\]]*)\]\(\s*([^)]+)\)/g, (original, alternative, destination) =>
|
|
3127
|
-
return hasUnsafeUrlScheme(destination.replace(/^<|>$/g, "")) ? alternative : original;
|
|
3128
|
-
}).replace(/<img\s+([^>]*?)\/?\s*>/gi, (original, attributes) => {
|
|
3129
|
-
const source = attributes.match(/\bsrc\s*=\s*(['"])(.*?)\1/i)?.[2];
|
|
3130
|
-
const alternative = attributes.match(/\balt\s*=\s*(['"])(.*?)\1/i)?.[2] ?? "";
|
|
3131
|
-
if (!source || hasUnsafeUrlScheme(source)) return "";
|
|
3132
|
-
return `![${alternative.replace(/]/g, "\\]")}](${source})`;
|
|
3133
|
-
})));
|
|
3316
|
+
return portableHtmlToMarkdown(stripEventHandlerProps(line.replace(/!\[([^\]]*)\]\(\s*([^)]+)\)/g, (original, alternative, destination) => isSafePortableUrl(decodeHtmlAttribute(destination.replace(/^<|>$/g, ""))) ? original : escapeMarkdownText(alternative))));
|
|
3134
3317
|
}).join("\n");
|
|
3135
|
-
const
|
|
3318
|
+
const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized);
|
|
3319
|
+
if (urlSafeMarkdown === null) return null;
|
|
3320
|
+
const executable = withoutFencedCode(urlSafeMarkdown).replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<(?:https?:\/\/|mailto:)[^>]+>/gi, "").replace(/(`+)[\s\S]*?\1/g, "");
|
|
3136
3321
|
if (/^\s*(?:import|export)\s/m.test(executable)) return null;
|
|
3137
3322
|
let hasUnsafeExpression = false;
|
|
3138
3323
|
const withoutStaticProps = executable.replace(/=\{([^{}\n]*)\}/g, (original, value) => {
|
|
@@ -3149,7 +3334,7 @@ function sanitizeRemoteMarkdown(body) {
|
|
|
3149
3334
|
const component = match[1].split(".", 1)[0];
|
|
3150
3335
|
if (!PORTABLE_MDX_COMPONENTS.has(component)) return null;
|
|
3151
3336
|
}
|
|
3152
|
-
return
|
|
3337
|
+
return urlSafeMarkdown;
|
|
3153
3338
|
}
|
|
3154
3339
|
function migratedHref(href, currentUrl, source, scopePath, importedIds, sourceHomePageId) {
|
|
3155
3340
|
if (/^(?:javascript|data|vbscript):/i.test(href)) return "#";
|
|
@@ -3436,7 +3621,10 @@ ${tabs.join("\n\n")}
|
|
|
3436
3621
|
filter: "table",
|
|
3437
3622
|
replacement: (_content, node) => {
|
|
3438
3623
|
const fragment = load(node.outerHTML);
|
|
3439
|
-
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) =>
|
|
3624
|
+
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => escapeMarkdownText(
|
|
3625
|
+
fragment(cell).text().replace(/\s+/g, " ").trim(),
|
|
3626
|
+
true
|
|
3627
|
+
))).filter((row) => row.length > 0);
|
|
3440
3628
|
if (rows.length === 0) return "";
|
|
3441
3629
|
const width = Math.max(...rows.map((row) => row.length));
|
|
3442
3630
|
const normalized = rows.map((row) => [...row, ...Array(width - row.length).fill("")]);
|