@thallylabs/migrate 0.2.33 → 0.2.34
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 +6 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +370 -122
- package/dist/index.js.map +1 -1
- package/dist/{url-migration-_wVauqSg.d.ts → url-migration-EwtII4P-.d.ts} +1 -0
- package/dist/url-migration.d.ts +1 -1
- package/dist/url-migration.js +54 -22
- package/dist/url-migration.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -230,8 +230,11 @@ var JS_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
230
230
|
"BroadcastChannel"
|
|
231
231
|
]);
|
|
232
232
|
var IMPLICIT_MODULE_NAMES = /* @__PURE__ */ new Set(["React", "MintlifyComponents"]);
|
|
233
|
-
function
|
|
234
|
-
return ts
|
|
233
|
+
function scriptKindFor(fileName) {
|
|
234
|
+
return /\.[cm]?ts$/.test(fileName) ? ts.ScriptKind.TS : ts.ScriptKind.TSX;
|
|
235
|
+
}
|
|
236
|
+
function parse(source, fileName = "inline.tsx") {
|
|
237
|
+
return ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, scriptKindFor(fileName));
|
|
235
238
|
}
|
|
236
239
|
function bindingNames(name, into) {
|
|
237
240
|
if (ts.isIdentifier(name)) into.add(name.text);
|
|
@@ -240,20 +243,44 @@ function bindingNames(name, into) {
|
|
|
240
243
|
function isFunctionLike(node) {
|
|
241
244
|
return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
|
|
242
245
|
}
|
|
246
|
+
function hoistedVarNames(root, into) {
|
|
247
|
+
const collect = (node) => {
|
|
248
|
+
if (isFunctionLike(node) || ts.isClassLike(node) || ts.isModuleDeclaration(node)) return;
|
|
249
|
+
if (ts.isVariableDeclaration(node) && !ts.isCatchClause(node.parent) && !(ts.getCombinedNodeFlags(node) & ts.NodeFlags.BlockScoped)) bindingNames(node.name, into);
|
|
250
|
+
ts.forEachChild(node, collect);
|
|
251
|
+
};
|
|
252
|
+
collect(root);
|
|
253
|
+
}
|
|
243
254
|
function functionScope(fn) {
|
|
244
255
|
const scope = /* @__PURE__ */ new Set();
|
|
245
256
|
if ((ts.isFunctionExpression(fn) || ts.isFunctionDeclaration(fn)) && fn.name) scope.add(fn.name.text);
|
|
246
257
|
for (const parameter of fn.parameters) bindingNames(parameter.name, scope);
|
|
247
|
-
function collect(node) {
|
|
248
|
-
if (ts.isVariableDeclaration(node)) bindingNames(node.name, scope);
|
|
249
|
-
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) scope.add(node.name.text);
|
|
250
|
-
if (isFunctionLike(node)) return;
|
|
251
|
-
ts.forEachChild(node, collect);
|
|
252
|
-
}
|
|
253
|
-
if (fn.body) ts.forEachChild(fn.body, collect);
|
|
254
|
-
if (fn.body && !ts.isBlock(fn.body)) collect(fn.body);
|
|
255
258
|
return scope;
|
|
256
259
|
}
|
|
260
|
+
function lexicalNames(statements) {
|
|
261
|
+
const names = /* @__PURE__ */ new Set();
|
|
262
|
+
for (const statement of statements) {
|
|
263
|
+
if (ts.isVariableStatement(statement)) {
|
|
264
|
+
if (statement.declarationList.flags & ts.NodeFlags.BlockScoped) for (const declaration of statement.declarationList.declarations) bindingNames(declaration.name, names);
|
|
265
|
+
} else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) names.add(statement.name.text);
|
|
266
|
+
}
|
|
267
|
+
return names;
|
|
268
|
+
}
|
|
269
|
+
function ownScope(node) {
|
|
270
|
+
if (ts.isBlock(node)) return lexicalNames(node.statements);
|
|
271
|
+
if (ts.isCaseBlock(node)) return lexicalNames(node.clauses.flatMap((clause) => [...clause.statements]));
|
|
272
|
+
if ((ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node)) && node.initializer && ts.isVariableDeclarationList(node.initializer)) {
|
|
273
|
+
const names = /* @__PURE__ */ new Set();
|
|
274
|
+
if (node.initializer.flags & ts.NodeFlags.BlockScoped) for (const declaration of node.initializer.declarations) bindingNames(declaration.name, names);
|
|
275
|
+
return names;
|
|
276
|
+
}
|
|
277
|
+
if (ts.isCatchClause(node) && node.variableDeclaration) {
|
|
278
|
+
const names = /* @__PURE__ */ new Set();
|
|
279
|
+
bindingNames(node.variableDeclaration.name, names);
|
|
280
|
+
return names;
|
|
281
|
+
}
|
|
282
|
+
return void 0;
|
|
283
|
+
}
|
|
257
284
|
function collectRefs(root) {
|
|
258
285
|
const refs = [];
|
|
259
286
|
const assigned = [];
|
|
@@ -268,13 +295,22 @@ function collectRefs(root) {
|
|
|
268
295
|
function walkNode(node, scopes, inAsync) {
|
|
269
296
|
if (ts.isTypeNode(node)) return;
|
|
270
297
|
if (isFunctionLike(node)) {
|
|
271
|
-
const
|
|
298
|
+
const parameterScopes = [...scopes, functionScope(node)];
|
|
272
299
|
const async = !!node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword);
|
|
273
300
|
for (const parameter of node.parameters) {
|
|
274
|
-
if (!ts.isIdentifier(parameter.name)) walkNode(parameter.name,
|
|
275
|
-
if (parameter.initializer) walkNode(parameter.initializer,
|
|
301
|
+
if (!ts.isIdentifier(parameter.name)) walkNode(parameter.name, parameterScopes, async);
|
|
302
|
+
if (parameter.initializer) walkNode(parameter.initializer, parameterScopes, async);
|
|
303
|
+
}
|
|
304
|
+
if (node.body) {
|
|
305
|
+
const hoisted = /* @__PURE__ */ new Set();
|
|
306
|
+
hoistedVarNames(node.body, hoisted);
|
|
307
|
+
walkNode(node.body, [...parameterScopes, hoisted], async);
|
|
276
308
|
}
|
|
277
|
-
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const own2 = ownScope(node);
|
|
312
|
+
if (own2) {
|
|
313
|
+
ts.forEachChild(node, (child) => walkNode(child, [...scopes, own2], inAsync));
|
|
278
314
|
return;
|
|
279
315
|
}
|
|
280
316
|
if (ts.isAwaitExpression(node) && !inAsync) awaitOutsideAsync = true;
|
|
@@ -651,6 +687,23 @@ function unboundTags(moduleSource, boundNames) {
|
|
|
651
687
|
}
|
|
652
688
|
return [...tags].sort();
|
|
653
689
|
}
|
|
690
|
+
function unboundBuiltinTags(moduleSource, boundNames, fileName) {
|
|
691
|
+
const tags = /* @__PURE__ */ new Set();
|
|
692
|
+
const values = /* @__PURE__ */ new Set();
|
|
693
|
+
for (const statement of parse(moduleSource, fileName).statements) {
|
|
694
|
+
if (ts.isImportDeclaration(statement)) continue;
|
|
695
|
+
for (const { name, kind } of collectRefs(statement).refs) {
|
|
696
|
+
if (boundNames.has(name)) continue;
|
|
697
|
+
if (kind === "tag" || kind === "member") tags.add(name);
|
|
698
|
+
else values.add(name);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const isGlobal = (name) => JS_GLOBALS.has(name) || BROWSER_IDENTIFIERS.has(name) || SERVER_ONLY_IDENTIFIERS.has(name);
|
|
702
|
+
const builtinTags = [...tags].filter(isThallyBuiltinComponent);
|
|
703
|
+
const shadowed = builtinTags.filter((name) => JS_GLOBALS.has(name) && values.has(name)).sort();
|
|
704
|
+
const builtinValues = [...values].filter((name) => isThallyBuiltinComponent(name) && !isGlobal(name));
|
|
705
|
+
return { bind: [.../* @__PURE__ */ new Set([...builtinTags.filter((name) => !shadowed.includes(name)), ...builtinValues])].sort(), shadowed };
|
|
706
|
+
}
|
|
654
707
|
var INLINE_SAFE_GLOBALS = /* @__PURE__ */ new Set([
|
|
655
708
|
"undefined",
|
|
656
709
|
"NaN",
|
|
@@ -719,12 +772,14 @@ function isSelfContainedFunction(node) {
|
|
|
719
772
|
function pageScopeNames(esmSources) {
|
|
720
773
|
const names = /* @__PURE__ */ new Set();
|
|
721
774
|
for (const source of esmSources) {
|
|
722
|
-
|
|
775
|
+
const sf = parse(source);
|
|
776
|
+
for (const statement of sf.statements) {
|
|
723
777
|
for (const local of importLocals(statement.getText())) names.add(local);
|
|
724
778
|
if (ts.isImportDeclaration(statement)) continue;
|
|
725
779
|
const target2 = ts.isExportDeclaration(statement) || ts.isExportAssignment(statement) ? void 0 : statement;
|
|
726
780
|
if (target2) for (const name of declaredNames(target2).names) names.add(name);
|
|
727
781
|
}
|
|
782
|
+
hoistedVarNames(sf, names);
|
|
728
783
|
}
|
|
729
784
|
return names;
|
|
730
785
|
}
|
|
@@ -2934,13 +2989,7 @@ function sanitizeCssModuleSelectors(css) {
|
|
|
2934
2989
|
return { css: root.toString(), omitted };
|
|
2935
2990
|
}
|
|
2936
2991
|
function sourceFile(source, filename) {
|
|
2937
|
-
return ts2.createSourceFile(
|
|
2938
|
-
filename,
|
|
2939
|
-
source,
|
|
2940
|
-
ts2.ScriptTarget.Latest,
|
|
2941
|
-
true,
|
|
2942
|
-
/\.[cm]?ts$/.test(filename) ? ts2.ScriptKind.TS : ts2.ScriptKind.TSX
|
|
2943
|
-
);
|
|
2992
|
+
return ts2.createSourceFile(filename, source, ts2.ScriptTarget.Latest, true, scriptKindFor(filename));
|
|
2944
2993
|
}
|
|
2945
2994
|
function walk(node, visitor) {
|
|
2946
2995
|
visitor(node);
|
|
@@ -2997,7 +3046,7 @@ function needsClientBoundary(source) {
|
|
|
2997
3046
|
visitNode(source);
|
|
2998
3047
|
return needed;
|
|
2999
3048
|
}
|
|
3000
|
-
function implicitReactImports(source) {
|
|
3049
|
+
function implicitReactImports(source, onShadowed) {
|
|
3001
3050
|
const bindings = /* @__PURE__ */ new Set();
|
|
3002
3051
|
const references = /* @__PURE__ */ new Set();
|
|
3003
3052
|
function bind(name) {
|
|
@@ -3015,7 +3064,9 @@ function implicitReactImports(source) {
|
|
|
3015
3064
|
}
|
|
3016
3065
|
if (ts2.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) bind(declaration.name);
|
|
3017
3066
|
if ((ts2.isFunctionDeclaration(statement) || ts2.isClassDeclaration(statement)) && statement.name) bindings.add(statement.name.text);
|
|
3067
|
+
if ((ts2.isEnumDeclaration(statement) || ts2.isModuleDeclaration(statement) || ts2.isImportEqualsDeclaration(statement)) && ts2.isIdentifier(statement.name)) bindings.add(statement.name.text);
|
|
3018
3068
|
}
|
|
3069
|
+
hoistedVarNames(source, bindings);
|
|
3019
3070
|
function visit(node) {
|
|
3020
3071
|
if (ts2.isIdentifier(node) && !(ts2.isPropertyAccessExpression(node.parent) && node.parent.name === node) && !(ts2.isPropertyAssignment(node.parent) && node.parent.name === node)) references.add(node.text);
|
|
3021
3072
|
ts2.forEachChild(node, visit);
|
|
@@ -3023,26 +3074,50 @@ function implicitReactImports(source) {
|
|
|
3023
3074
|
visit(source);
|
|
3024
3075
|
const hooks = [...REACT_GLOBALS].filter((name) => references.has(name) && !bindings.has(name)).sort();
|
|
3025
3076
|
const usesMintlifyComponents = references.has("MintlifyComponents") && !bindings.has("MintlifyComponents");
|
|
3077
|
+
const leafBound = /* @__PURE__ */ new Set();
|
|
3026
3078
|
const implicitComponents = MINTLIFY_COMPONENT_MODULES.map(({ module, names }) => {
|
|
3027
|
-
const needed = names.filter((name) => references.has(name) && !bindings.has(name));
|
|
3079
|
+
const needed = names.filter((name) => name !== "Note" && references.has(name) && !bindings.has(name));
|
|
3080
|
+
for (const name of needed) leafBound.add(name);
|
|
3028
3081
|
return needed.length ? `import { ${needed.join(", ")} } from '${module}';` : "";
|
|
3029
3082
|
}).filter(Boolean);
|
|
3083
|
+
const { bind: builtinTags, shadowed } = unboundBuiltinTags(source.text, bindings, source.fileName);
|
|
3084
|
+
const registryNames = builtinTags.filter((name) => !leafBound.has(name));
|
|
3085
|
+
if (shadowed.length) onShadowed?.(shadowed);
|
|
3030
3086
|
return [
|
|
3031
3087
|
references.has("React") && !bindings.has("React") ? "import * as React from 'react';" : "",
|
|
3032
3088
|
hooks.length ? `import { ${hooks.join(", ")} } from 'react';` : "",
|
|
3033
3089
|
...implicitComponents,
|
|
3090
|
+
registryNames.length ? builtinRegistryBindings(registryNames, identifierNames(source)) : "",
|
|
3034
3091
|
usesMintlifyComponents ? mintlifyComponentsShim() : ""
|
|
3035
3092
|
].filter(Boolean).join("\n");
|
|
3036
3093
|
}
|
|
3037
3094
|
function resolveInlineBuiltinReferences(source, moduleNames) {
|
|
3038
3095
|
const unresolved = unboundTags(source, moduleNames);
|
|
3039
3096
|
if (unresolved.length === 0) return source;
|
|
3097
|
+
return [builtinRegistryBindings(unresolved, identifierNames(sourceFile(source, "inline.jsx"))), source].join("\n");
|
|
3098
|
+
}
|
|
3099
|
+
function identifierNames(root) {
|
|
3100
|
+
const names = /* @__PURE__ */ new Set();
|
|
3101
|
+
const visit = (node) => {
|
|
3102
|
+
if (ts2.isIdentifier(node)) names.add(node.text);
|
|
3103
|
+
ts2.forEachChild(node, visit);
|
|
3104
|
+
};
|
|
3105
|
+
visit(root);
|
|
3106
|
+
return names;
|
|
3107
|
+
}
|
|
3108
|
+
function builtinRegistryBindings(names, used) {
|
|
3109
|
+
const registry = "builtinMdxComponents";
|
|
3110
|
+
let alias = registry;
|
|
3111
|
+
for (let suffix = 2; used.has(alias); suffix++) alias = `${registry}${suffix}`;
|
|
3040
3112
|
return [
|
|
3041
|
-
|
|
3042
|
-
`const { ${
|
|
3043
|
-
source
|
|
3113
|
+
`import { ${alias === registry ? registry : `${registry} as ${alias}`} } from '@/components/mdx/builtin-components';`,
|
|
3114
|
+
`const { ${names.join(", ")} } = ${alias};`
|
|
3044
3115
|
].join("\n");
|
|
3045
3116
|
}
|
|
3117
|
+
function shadowedBuiltinMessage(file, names) {
|
|
3118
|
+
const tags = names.map((name) => `<${name}>`).join(", ");
|
|
3119
|
+
return `${file} renders ${tags} as a Thally built-in component but also uses the JavaScript global of the same name, so the tag was left unbound. Rename the component or the global use in the source.`;
|
|
3120
|
+
}
|
|
3046
3121
|
function imports(statement) {
|
|
3047
3122
|
if (!ts2.isStringLiteral(statement.moduleSpecifier) || !statement.importClause) return [];
|
|
3048
3123
|
const clause = statement.importClause;
|
|
@@ -3269,6 +3344,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
3269
3344
|
function copyGraph(entry) {
|
|
3270
3345
|
const staged = /* @__PURE__ */ new Map();
|
|
3271
3346
|
const stagedWarnings = [];
|
|
3347
|
+
const stagedShadowed = [];
|
|
3272
3348
|
const modules = /* @__PURE__ */ new Map();
|
|
3273
3349
|
let stagedBytes = 0;
|
|
3274
3350
|
function stageShim(filename, content) {
|
|
@@ -3373,7 +3449,8 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
3373
3449
|
const prologue = `// @ts-nocheck
|
|
3374
3450
|
${client ? "'use client';\n" : ""}
|
|
3375
3451
|
`;
|
|
3376
|
-
|
|
3452
|
+
const implicit = implicitReactImports(ast, (names) => stagedShadowed.push({ path, names }));
|
|
3453
|
+
return shebangMatch ? shebangMatch[0] + prologue + implicit + "\n" + replaced.slice(shebangMatch[0].length) : prologue + implicit + "\n" + replaced;
|
|
3377
3454
|
}
|
|
3378
3455
|
});
|
|
3379
3456
|
}
|
|
@@ -3391,6 +3468,7 @@ ${client ? "'use client';\n" : ""}
|
|
|
3391
3468
|
}
|
|
3392
3469
|
for (const [path, module] of modules) staged.set(path, { path, content: module.render(clientModules.has(path)) });
|
|
3393
3470
|
for (const [path, file] of staged) copied.set(path, file);
|
|
3471
|
+
for (const { path, names } of stagedShadowed) warn(shadowedBuiltinMessage(relative2(root, path).replace(/\\/g, "/"), names), path);
|
|
3394
3472
|
for (const { path, omitted } of stagedWarnings) {
|
|
3395
3473
|
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);
|
|
3396
3474
|
}
|
|
@@ -3845,8 +3923,12 @@ ${inlineSource}` });
|
|
|
3845
3923
|
}`),
|
|
3846
3924
|
""
|
|
3847
3925
|
].join("\n");
|
|
3926
|
+
const implicit = implicitReactImports(
|
|
3927
|
+
sourceFile(inlineSource, "inline.jsx"),
|
|
3928
|
+
(names) => warn(shadowedBuiltinMessage(relative2(root, currentFile).replace(/\\/g, "/"), names), currentFile)
|
|
3929
|
+
);
|
|
3848
3930
|
copied.set(path, { path, content: `'use client';
|
|
3849
|
-
${
|
|
3931
|
+
${implicit}
|
|
3850
3932
|
${inlineSource}` });
|
|
3851
3933
|
copiedBytes += Buffer.byteLength(inlineSource);
|
|
3852
3934
|
for (const { node, name } of extracted) {
|
|
@@ -7196,8 +7278,49 @@ function splitOpenApiRef(raw) {
|
|
|
7196
7278
|
const specRef = match[1] ? unquote(match[1]) : void 0;
|
|
7197
7279
|
return { ...specRef ? { specRef } : {}, operation: match[2] };
|
|
7198
7280
|
}
|
|
7281
|
+
function canonicalSpecRef(specRef) {
|
|
7282
|
+
return specRef.trim().split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^(?:\.?\/+)+/, "");
|
|
7283
|
+
}
|
|
7284
|
+
function isRemoteSpecRef(specRef) {
|
|
7285
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(specRef);
|
|
7286
|
+
}
|
|
7199
7287
|
function specRefBaseName(specRef) {
|
|
7200
|
-
return specRef
|
|
7288
|
+
return canonicalSpecRef(specRef).split("/").filter(Boolean).pop()?.toLowerCase() ?? "";
|
|
7289
|
+
}
|
|
7290
|
+
function withoutSpecExtension(value) {
|
|
7291
|
+
return value.replace(/\.(?:json|ya?ml)$/i, "");
|
|
7292
|
+
}
|
|
7293
|
+
function resolveSpecRef(specRef, candidates, anyFolder = true) {
|
|
7294
|
+
const key = canonicalSpecRef(specRef);
|
|
7295
|
+
const byId = /* @__PURE__ */ new Map();
|
|
7296
|
+
for (const candidate of candidates) {
|
|
7297
|
+
const id = candidate.sourcePath ?? candidate.filename;
|
|
7298
|
+
if (!byId.has(id)) byId.set(id, candidate);
|
|
7299
|
+
}
|
|
7300
|
+
const unique = [...byId.values()];
|
|
7301
|
+
const exact = unique.find((candidate) => candidate.sourcePath === key);
|
|
7302
|
+
if (exact) return { kind: "match", spec: exact };
|
|
7303
|
+
if (!anyFolder && key.includes("/")) return { kind: "none" };
|
|
7304
|
+
const name = specRefBaseName(key);
|
|
7305
|
+
const names = unique.map((candidate) => specRefBaseName(candidate.sourcePath ?? candidate.filename));
|
|
7306
|
+
for (const fits of [unique.filter((_, index) => names[index] === name), unique.filter((_, index) => withoutSpecExtension(names[index]) === name)]) {
|
|
7307
|
+
if (fits.length === 1) return { kind: "match", spec: fits[0] };
|
|
7308
|
+
if (fits.length > 1) return { kind: "ambiguous", candidates: fits };
|
|
7309
|
+
}
|
|
7310
|
+
return { kind: "none" };
|
|
7311
|
+
}
|
|
7312
|
+
function localSpecRef(openapi) {
|
|
7313
|
+
const specRef = openapi ? splitOpenApiRef(openapi)?.specRef : void 0;
|
|
7314
|
+
return specRef && !isRemoteSpecRef(specRef) ? specRef : void 0;
|
|
7315
|
+
}
|
|
7316
|
+
function specNamedByPage(openapi, specs) {
|
|
7317
|
+
const specRef = localSpecRef(openapi);
|
|
7318
|
+
const resolved = specRef ? resolveSpecRef(specRef, specs) : void 0;
|
|
7319
|
+
return resolved?.kind === "match" ? resolved.spec : void 0;
|
|
7320
|
+
}
|
|
7321
|
+
function namesSeveralSpecs(openapi, specs) {
|
|
7322
|
+
const specRef = localSpecRef(openapi);
|
|
7323
|
+
return Boolean(specRef) && resolveSpecRef(specRef, specs).kind === "ambiguous";
|
|
7201
7324
|
}
|
|
7202
7325
|
function withSpecRef(ref, specRef) {
|
|
7203
7326
|
return `${specRef} ${ref.operation}`;
|
|
@@ -7205,25 +7328,44 @@ function withSpecRef(ref, specRef) {
|
|
|
7205
7328
|
|
|
7206
7329
|
// src/spec-exclude.ts
|
|
7207
7330
|
import { parse as parseYaml4, stringify as stringifyYaml } from "yaml";
|
|
7208
|
-
function specRefMatches(ref, specPath,
|
|
7209
|
-
const prefix =
|
|
7331
|
+
function specRefMatches(ref, specPath, isDefault) {
|
|
7332
|
+
const prefix = splitOpenApiRef(ref)?.specRef;
|
|
7210
7333
|
if (!prefix) return isDefault;
|
|
7211
|
-
const wanted = prefix.
|
|
7334
|
+
const wanted = (isRemoteSpecRef(prefix) ? prefix.trim() : canonicalSpecRef(prefix)).toLowerCase();
|
|
7212
7335
|
const lowerPath = specPath.toLowerCase();
|
|
7213
|
-
|
|
7336
|
+
const stem = withoutSpecExtension(lowerPath);
|
|
7337
|
+
return lowerPath === wanted || lowerPath.endsWith(`/${wanted}`) || stem === wanted || stem.endsWith(`/${wanted}`);
|
|
7338
|
+
}
|
|
7339
|
+
function resolvedSources(ref, specs) {
|
|
7340
|
+
const specRef = splitOpenApiRef(ref)?.specRef;
|
|
7341
|
+
if (!specRef || isRemoteSpecRef(specRef)) return { unique: false, sources: [] };
|
|
7342
|
+
const resolved = resolveSpecRef(specRef, specs);
|
|
7343
|
+
if (resolved.kind === "match") return { unique: true, sources: [resolved.spec.sourcePath] };
|
|
7344
|
+
return { unique: false, sources: resolved.kind === "ambiguous" ? resolved.candidates.map((candidate) => candidate.sourcePath) : [] };
|
|
7345
|
+
}
|
|
7346
|
+
function refWithholdsFrom(ref, spec, specs) {
|
|
7347
|
+
const { unique, sources } = resolvedSources(ref, specs);
|
|
7348
|
+
return unique ? sources[0] === spec.sourcePath : specRefMatches(ref, spec.sourcePath, true) || sources.includes(spec.sourcePath);
|
|
7349
|
+
}
|
|
7350
|
+
function refKeepsIn(ref, spec, specs) {
|
|
7351
|
+
const specRef = splitOpenApiRef(ref)?.specRef;
|
|
7352
|
+
if (!specRef) return false;
|
|
7353
|
+
if (isRemoteSpecRef(specRef)) return specRefMatches(ref, spec.sourcePath, false);
|
|
7354
|
+
const { unique, sources } = resolvedSources(ref, specs);
|
|
7355
|
+
return unique && sources[0] === spec.sourcePath;
|
|
7214
7356
|
}
|
|
7215
7357
|
function operationKey(operation) {
|
|
7216
7358
|
const [first, ...rest] = operation.trim().split(/\s+/);
|
|
7217
7359
|
return `${first.toLowerCase()} ${rest.join(" ")}`;
|
|
7218
7360
|
}
|
|
7219
|
-
function keysFor(
|
|
7361
|
+
function keysFor(refs, names) {
|
|
7220
7362
|
return [...new Set(
|
|
7221
|
-
refs.filter(
|
|
7363
|
+
refs.filter(names).map((ref) => splitOpenApiRef(ref)?.operation).filter((op) => !!op).map(operationKey)
|
|
7222
7364
|
)];
|
|
7223
7365
|
}
|
|
7224
|
-
function withheldOperationKeys(spec, withheldRefs, keptRefs) {
|
|
7225
|
-
const kept = keysFor(spec,
|
|
7226
|
-
return { withheld: keysFor(spec,
|
|
7366
|
+
function withheldOperationKeys(spec, withheldRefs, keptRefs, specs = [spec]) {
|
|
7367
|
+
const kept = keysFor(keptRefs, (ref) => refKeepsIn(ref, spec, specs));
|
|
7368
|
+
return { withheld: keysFor(withheldRefs, (ref) => refWithholdsFrom(ref, spec, specs)).filter((key) => !kept.includes(key)), kept };
|
|
7227
7369
|
}
|
|
7228
7370
|
function withoutByteOrderMark(content) {
|
|
7229
7371
|
return content.length >= 3 && content[0] === 239 && content[1] === 187 && content[2] === 191 ? content.subarray(3) : content;
|
|
@@ -10474,22 +10616,56 @@ function normalizedReferenceKey(value) {
|
|
|
10474
10616
|
function exactReferenceKey(value) {
|
|
10475
10617
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
10476
10618
|
}
|
|
10477
|
-
function
|
|
10478
|
-
const
|
|
10479
|
-
|
|
10619
|
+
function containerSpec(node) {
|
|
10620
|
+
const value = node.openapi;
|
|
10621
|
+
if (value === void 0) return void 0;
|
|
10622
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value.source : value;
|
|
10623
|
+
return typeof source === "string" && source.trim() && !splitOpenApiRef(source) ? source.trim() : null;
|
|
10624
|
+
}
|
|
10625
|
+
function navigationSpecContext(config, pageIdentity) {
|
|
10626
|
+
const context = { operations: [], pageOwners: /* @__PURE__ */ new Map(), pageIdentity };
|
|
10627
|
+
const visit = (node, owner, listed) => {
|
|
10480
10628
|
if (typeof node === "string") {
|
|
10481
|
-
if (splitOpenApiRef(node))
|
|
10482
|
-
|
|
10629
|
+
if (splitOpenApiRef(node)) context.operations.push({ ref: node, owner });
|
|
10630
|
+
else if (listed) {
|
|
10631
|
+
const page = pageIdentity(node);
|
|
10632
|
+
if (page !== null) context.pageOwners.set(page, (context.pageOwners.get(page) ?? /* @__PURE__ */ new Set()).add(owner));
|
|
10633
|
+
}
|
|
10634
|
+
} else if (Array.isArray(node)) node.forEach((child) => visit(child, owner, true));
|
|
10483
10635
|
else if (node && typeof node === "object") {
|
|
10484
10636
|
if (navigationGateReason(node)) return;
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10637
|
+
const own2 = containerSpec(node);
|
|
10638
|
+
Object.values(node).forEach((child) => visit(child, own2 === void 0 ? owner : own2, false));
|
|
10639
|
+
}
|
|
10640
|
+
};
|
|
10641
|
+
visit(config?.navigation, void 0, false);
|
|
10642
|
+
return context;
|
|
10643
|
+
}
|
|
10644
|
+
function keptOperationRefs(published, navigation, defaultSpec, specs) {
|
|
10645
|
+
const kept = [];
|
|
10646
|
+
const unowned = [];
|
|
10647
|
+
const source = (owner) => {
|
|
10648
|
+
if (!owner || isRemoteSpecRef(owner)) return owner;
|
|
10649
|
+
const resolved = resolveSpecRef(owner, specs);
|
|
10650
|
+
return resolved.kind === "match" ? resolved.spec.sourcePath ?? resolved.spec.filename : owner;
|
|
10651
|
+
};
|
|
10652
|
+
const add = (raw, owner) => {
|
|
10653
|
+
const ref = splitOpenApiRef(raw);
|
|
10654
|
+
if (!ref) return;
|
|
10655
|
+
if (ref.specRef) kept.push(raw);
|
|
10656
|
+
else if (owner) kept.push(withSpecRef(ref, owner));
|
|
10657
|
+
else unowned.push(ref.operation);
|
|
10658
|
+
};
|
|
10659
|
+
for (const { ref, file } of published) {
|
|
10660
|
+
const page = navigation.pageIdentity(file);
|
|
10661
|
+
const owners = new Set([...(page === null ? void 0 : navigation.pageOwners.get(page)) ?? [void 0]].map((owner) => source(owner === void 0 ? defaultSpec : owner)));
|
|
10662
|
+
add(ref, owners.size === 1 ? [...owners][0] : null);
|
|
10663
|
+
}
|
|
10664
|
+
for (const { ref, owner } of navigation.operations) add(ref, source(owner === void 0 ? defaultSpec : owner));
|
|
10665
|
+
return { kept, unowned };
|
|
10666
|
+
}
|
|
10667
|
+
function excludeWithheldOperations(spec, withheldRefs, keptRefs, specs) {
|
|
10668
|
+
const { withheld, kept } = withheldOperationKeys(spec, withheldRefs, keptRefs, specs);
|
|
10493
10669
|
return markExcluded(spec.content, withheld, kept);
|
|
10494
10670
|
}
|
|
10495
10671
|
var TOKEN_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\/`*!?#&]+/;
|
|
@@ -10590,6 +10766,23 @@ function specAssetPath(filename) {
|
|
|
10590
10766
|
function specAsset(filename, content) {
|
|
10591
10767
|
return { path: specAssetPath(filename), content, projectRelative: true };
|
|
10592
10768
|
}
|
|
10769
|
+
function createSpecAssetNamer() {
|
|
10770
|
+
const nameBySource = /* @__PURE__ */ new Map();
|
|
10771
|
+
const sourceByName = /* @__PURE__ */ new Map();
|
|
10772
|
+
return ({ sourcePath, filename, tabLabel }) => {
|
|
10773
|
+
let name = nameBySource.get(sourcePath);
|
|
10774
|
+
if (name === void 0) {
|
|
10775
|
+
name = filename;
|
|
10776
|
+
if (sourceByName.has(name.toLowerCase())) {
|
|
10777
|
+
name = sourcePath.replace(/\//g, "-");
|
|
10778
|
+
while (sourceByName.has(name.toLowerCase())) name = `${(tabLabel ?? "default").replace(/\W+/g, "-")}-${name}`;
|
|
10779
|
+
}
|
|
10780
|
+
nameBySource.set(sourcePath, name);
|
|
10781
|
+
sourceByName.set(name.toLowerCase(), sourcePath);
|
|
10782
|
+
}
|
|
10783
|
+
return name;
|
|
10784
|
+
};
|
|
10785
|
+
}
|
|
10593
10786
|
function mintlifyTopLevelApiReferences(config) {
|
|
10594
10787
|
const api = config?.api && typeof config.api === "object" && !Array.isArray(config.api) ? config.api : null;
|
|
10595
10788
|
if (!api) return [];
|
|
@@ -10601,19 +10794,25 @@ function mintlifyTopLevelApiReferences(config) {
|
|
|
10601
10794
|
}
|
|
10602
10795
|
return references;
|
|
10603
10796
|
}
|
|
10604
|
-
function
|
|
10797
|
+
function specFileCandidates(files) {
|
|
10798
|
+
return files.filter((file) => /\.(?:json|ya?ml)$/i.test(file.relativePath)).map((file) => ({ sourcePath: file.relativePath, filename: specRefBaseName(file.relativePath) }));
|
|
10799
|
+
}
|
|
10800
|
+
function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, nameSpec, pages = []) {
|
|
10605
10801
|
if (!mintlifyConfig) return [];
|
|
10606
10802
|
const references = [
|
|
10607
10803
|
...mintlifyTopLevelApiReferences(mintlifyConfig),
|
|
10608
10804
|
...mintlifyNavigationApiReferences(mintlifyConfig)
|
|
10609
10805
|
];
|
|
10610
|
-
const
|
|
10611
|
-
const
|
|
10806
|
+
const known = new Set(references.map((reference) => canonicalSpecRef(reference.value)));
|
|
10807
|
+
const specFiles = specFileCandidates(files);
|
|
10808
|
+
const namedFiles = /* @__PURE__ */ new Map();
|
|
10612
10809
|
for (const page of pages) {
|
|
10613
10810
|
const specRef = page.openapi ? splitOpenApiRef(page.openapi)?.specRef : void 0;
|
|
10614
10811
|
if (!specRef || /^[a-z][a-z0-9+.-]*:/i.test(specRef)) continue;
|
|
10615
|
-
const
|
|
10616
|
-
|
|
10812
|
+
const named = namedFiles.get(specRef) ?? resolveSpecRef(specRef, specFiles, false);
|
|
10813
|
+
namedFiles.set(specRef, named);
|
|
10814
|
+
const key = named.kind === "match" ? named.spec.sourcePath : "";
|
|
10815
|
+
if (!key || known.has(key)) continue;
|
|
10617
10816
|
known.add(key);
|
|
10618
10817
|
references.push({ value: key, kind: "openapi", tabLabel: `OpenAPI: ${key}`, hidden: true, pageOnly: true });
|
|
10619
10818
|
}
|
|
@@ -10663,7 +10862,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, p
|
|
|
10663
10862
|
warnings.push({ code: "unsupported-config", message: `The OpenAPI spec "${reference.value}"${tabSuffix} uses insecure HTTP and was not imported.`, source: reference.value });
|
|
10664
10863
|
continue;
|
|
10665
10864
|
}
|
|
10666
|
-
const key = reference.value
|
|
10865
|
+
const key = canonicalSpecRef(reference.value);
|
|
10667
10866
|
const match = files.find((file) => file.relativePath === key);
|
|
10668
10867
|
if (!match) {
|
|
10669
10868
|
warnings.push({
|
|
@@ -10692,21 +10891,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, p
|
|
|
10692
10891
|
directory: reference.directory
|
|
10693
10892
|
});
|
|
10694
10893
|
}
|
|
10695
|
-
const
|
|
10696
|
-
const filenameSources = /* @__PURE__ */ new Map();
|
|
10697
|
-
for (const spec of specs) {
|
|
10698
|
-
const groupKey = spec.tabLabel ?? "\0default";
|
|
10699
|
-
const owner = filenameOwners.get(spec.filename);
|
|
10700
|
-
if (owner !== void 0 && owner !== groupKey && filenameSources.get(spec.filename) !== spec.sourcePath) {
|
|
10701
|
-
let disambiguated = spec.sourcePath.replace(/\//g, "-");
|
|
10702
|
-
while (filenameOwners.has(disambiguated) && filenameOwners.get(disambiguated) !== groupKey) {
|
|
10703
|
-
disambiguated = `${groupKey.replace(/\W+/g, "-")}-${disambiguated}`;
|
|
10704
|
-
}
|
|
10705
|
-
spec.filename = disambiguated;
|
|
10706
|
-
}
|
|
10707
|
-
filenameOwners.set(spec.filename, groupKey);
|
|
10708
|
-
filenameSources.set(spec.filename, spec.sourcePath);
|
|
10709
|
-
}
|
|
10894
|
+
for (const spec of specs) spec.filename = nameSpec(spec);
|
|
10710
10895
|
return specs;
|
|
10711
10896
|
}
|
|
10712
10897
|
function rewriteMintlifyPageSpecRefs(pages, specs, remoteUrls, warnings, explainMissing = () => "no such file was found") {
|
|
@@ -10719,12 +10904,13 @@ function rewriteMintlifyPageSpecRefs(pages, specs, remoteUrls, warnings, explain
|
|
|
10719
10904
|
}
|
|
10720
10905
|
continue;
|
|
10721
10906
|
}
|
|
10722
|
-
const key = ref.specRef
|
|
10723
|
-
const
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
if (
|
|
10727
|
-
|
|
10907
|
+
const key = canonicalSpecRef(ref.specRef);
|
|
10908
|
+
const resolved = resolveSpecRef(ref.specRef, specs);
|
|
10909
|
+
if (resolved.kind === "match") {
|
|
10910
|
+
page.openapi = withSpecRef(ref, specAssetPath(resolved.spec.filename));
|
|
10911
|
+
} else if (resolved.kind === "ambiguous") {
|
|
10912
|
+
const candidates = resolved.candidates.map((spec) => spec.sourcePath ?? spec.filename).join(", ");
|
|
10913
|
+
warnings.push({ code: "unsupported-config", message: `Page "${page.id}" references the OpenAPI spec "${ref.specRef}", which fits more than one spec (${candidates}), so it was left as written and the page will not render an endpoint. Write the full path of the spec in the page's openapi frontmatter.`, source: page.source });
|
|
10728
10914
|
} else {
|
|
10729
10915
|
warnings.push({ code: "unsupported-config", message: `Page "${page.id}" references the OpenAPI spec "${ref.specRef}", which was not migrated (${explainMissing(key)}); the page will not render an endpoint.`, source: page.source });
|
|
10730
10916
|
}
|
|
@@ -12055,9 +12241,12 @@ function injectOpenApiSpecs(config, specs, warnings) {
|
|
|
12055
12241
|
const apiTab = spec.tabLabel ? tabs.find((tab) => tab.tab === spec.tabLabel) : tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
12056
12242
|
if (apiTab) {
|
|
12057
12243
|
if (apiTab.api && warnings) {
|
|
12244
|
+
const source = specAssetPath(spec.filename);
|
|
12245
|
+
const pageBound = spec.pageReferenced && !tabs.some((tab) => tab.api?.source === source);
|
|
12246
|
+
if (pageBound) insertApiTab(tabs, { tab: `OpenAPI: ${spec.sourcePath ?? spec.filename}`, hidden: true, api: { source } });
|
|
12058
12247
|
warnings.push({
|
|
12059
12248
|
code: "unsupported-config",
|
|
12060
|
-
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "${
|
|
12249
|
+
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "${source}" ${pageBound ? "gets no endpoint list of its own; the pages that name it still show their endpoint. Put it in its own tab to list all its endpoints." : "was not added. Put it in its own tab to include it."}`
|
|
12061
12250
|
});
|
|
12062
12251
|
continue;
|
|
12063
12252
|
}
|
|
@@ -12316,6 +12505,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
12316
12505
|
const gateByPath = /* @__PURE__ */ new Map();
|
|
12317
12506
|
const withheldSpecRefs = [];
|
|
12318
12507
|
const publishedSpecRefs = [];
|
|
12508
|
+
const publishedSpecPages = [];
|
|
12319
12509
|
const withheldPaths = /* @__PURE__ */ new Set();
|
|
12320
12510
|
const isDocFile = (file) => [".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase());
|
|
12321
12511
|
const isGateCandidate = (file) => isDocFile(file) && !file.relativePath.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()));
|
|
@@ -12352,6 +12542,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
12352
12542
|
const verdict = classifyPageGate(file);
|
|
12353
12543
|
gateByPath.set(file.absolutePath, verdict);
|
|
12354
12544
|
if (verdict.openapi) (verdict.reason ? withheldSpecRefs : publishedSpecRefs).push(verdict.openapi);
|
|
12545
|
+
if (verdict.openapi && !verdict.reason) publishedSpecPages.push({ ref: verdict.openapi, file: file.relativePath });
|
|
12355
12546
|
if (verdict.reason) withheldPaths.add(file.absolutePath);
|
|
12356
12547
|
}
|
|
12357
12548
|
for (const file of mintignoreFilteredFiles) {
|
|
@@ -12455,6 +12646,23 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
12455
12646
|
let discovered = files.length;
|
|
12456
12647
|
const localeConfig = docsConfig.i18n;
|
|
12457
12648
|
const defaultPageIds = new Set([...referenceMap.values()].filter((reference) => !reference.locale || reference.locale === localeConfig?.defaultLocale).map((reference) => reference.navigationId));
|
|
12649
|
+
const resolvePageIdentity = (path) => {
|
|
12650
|
+
const referenced = exactReferenceMap.get(exactReferenceKey(path)) ?? referenceMap.get(normalizedReferenceKey(path));
|
|
12651
|
+
let locale = referenced?.locale;
|
|
12652
|
+
let navigationId = referenced?.navigationId ?? pageIdFromReference(path, platform === "mintlify");
|
|
12653
|
+
if (!referenced && localeConfig) {
|
|
12654
|
+
for (const entry of localeConfig.locales) {
|
|
12655
|
+
const localized = mintlifyLocalizedReference(path, entry.code, defaultPageIds);
|
|
12656
|
+
if (localized === path) continue;
|
|
12657
|
+
locale = entry.code;
|
|
12658
|
+
navigationId = pageIdFromReference(localized, platform === "mintlify");
|
|
12659
|
+
break;
|
|
12660
|
+
}
|
|
12661
|
+
}
|
|
12662
|
+
if (!navigationId) return null;
|
|
12663
|
+
const isDefaultLocale = !locale || locale === localeConfig?.defaultLocale;
|
|
12664
|
+
return { locale, navigationId, id: isDefaultLocale ? navigationId : `${locale}/${navigationId}` };
|
|
12665
|
+
};
|
|
12458
12666
|
const routeAliases = [];
|
|
12459
12667
|
const pageFiles = [...files].sort((left, right) => {
|
|
12460
12668
|
const leftOrder = referenceOrder.get(normalizedReferenceKey(left.relativePath)) ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -12550,7 +12758,6 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
12550
12758
|
}
|
|
12551
12759
|
continue;
|
|
12552
12760
|
}
|
|
12553
|
-
const key = normalizedReferenceKey(file.relativePath);
|
|
12554
12761
|
let pageGate;
|
|
12555
12762
|
{
|
|
12556
12763
|
pageGate = gateByPath.get(file.absolutePath);
|
|
@@ -12600,25 +12807,13 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
12600
12807
|
}
|
|
12601
12808
|
}
|
|
12602
12809
|
const isRestrictedPage = Boolean(pageGate?.reason);
|
|
12603
|
-
const
|
|
12604
|
-
|
|
12605
|
-
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath, platform === "mintlify");
|
|
12606
|
-
if (!referenced && localeConfig) {
|
|
12607
|
-
for (const entry of localeConfig.locales) {
|
|
12608
|
-
const localized = mintlifyLocalizedReference(file.relativePath, entry.code, defaultPageIds);
|
|
12609
|
-
if (localized === file.relativePath) continue;
|
|
12610
|
-
locale = entry.code;
|
|
12611
|
-
navigationId = pageIdFromReference(localized, platform === "mintlify");
|
|
12612
|
-
break;
|
|
12613
|
-
}
|
|
12614
|
-
}
|
|
12615
|
-
if (!navigationId) {
|
|
12810
|
+
const identity = resolvePageIdentity(file.relativePath);
|
|
12811
|
+
if (!identity) {
|
|
12616
12812
|
skipped++;
|
|
12617
12813
|
warnings.push({ code: "invalid-page", message: "Page path could not be normalized safely.", source: file.relativePath });
|
|
12618
12814
|
continue;
|
|
12619
12815
|
}
|
|
12620
|
-
const
|
|
12621
|
-
const id = isDefaultLocale ? navigationId : `${locale}/${navigationId}`;
|
|
12816
|
+
const { locale, navigationId, id } = identity;
|
|
12622
12817
|
let raw = inlineMdxSnippets(
|
|
12623
12818
|
mintlifyProjectRoot ? importUndeclaredSnippetComponents(readFileSync6(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, mintlifyProjectRoot, snippetExports, snippetAliases, inlineGate, warnings) : injectDocusaurusGlobalImports(readFileSync6(file.absolutePath, "utf8"), globalDocusaurusImports),
|
|
12624
12819
|
file.absolutePath,
|
|
@@ -13267,8 +13462,21 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13267
13462
|
const warnSharedSpec = (path, excluded = []) => {
|
|
13268
13463
|
warnings.push({ code: "gated-page", message: sharedSpecMessage(path, excluded), source: path });
|
|
13269
13464
|
};
|
|
13270
|
-
const
|
|
13271
|
-
const allSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs, pages.filter((page) => !restrictedPages.has(page)));
|
|
13465
|
+
const nameSpec = createSpecAssetNamer();
|
|
13466
|
+
const allSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs, nameSpec, pages.filter((page) => !restrictedPages.has(page)));
|
|
13467
|
+
const navigation = navigationSpecContext(mintlifyConfig, (path) => resolvePageIdentity(path)?.id ?? null);
|
|
13468
|
+
const defaultSources = /* @__PURE__ */ new Set([
|
|
13469
|
+
...allSpecs.filter((spec) => !spec.pageOnly && spec.tabLabel === void 0).map((spec) => spec.sourcePath),
|
|
13470
|
+
...remoteApiSpecs.filter((spec) => spec.tabLabel === void 0).map((spec) => spec.url)
|
|
13471
|
+
]);
|
|
13472
|
+
const keptOperations = (fallbackSource) => {
|
|
13473
|
+
const defaultSpec = defaultSources.size === 0 ? fallbackSource : defaultSources.size === 1 ? [...defaultSources][0] : void 0;
|
|
13474
|
+
const candidates = fallbackSource ? [{ sourcePath: fallbackSource, filename: basename3(fallbackSource) }, ...allSpecs] : allSpecs;
|
|
13475
|
+
const { kept, unowned } = keptOperationRefs(publishedSpecPages, navigation, defaultSpec, candidates);
|
|
13476
|
+
return { kept: kept.filter((ref) => !namesSeveralSpecs(ref, allSpecs)), unowned };
|
|
13477
|
+
};
|
|
13478
|
+
let { kept: keptSpecRefs, unowned: unownedOperations } = keptOperations();
|
|
13479
|
+
const withheldKeys = /* @__PURE__ */ new Set();
|
|
13272
13480
|
for (const remote of remoteApiSpecs) {
|
|
13273
13481
|
const { withheld, kept } = withheldOperationKeys({ sourcePath: remote.url, filename: remote.url }, withheldSpecRefs, keptSpecRefs);
|
|
13274
13482
|
if (withheld.length > 0) {
|
|
@@ -13278,9 +13486,10 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13278
13486
|
}
|
|
13279
13487
|
const withheldOperations = /* @__PURE__ */ new Map();
|
|
13280
13488
|
for (const spec of allSpecs) {
|
|
13281
|
-
const result = excludeWithheldOperations(spec, withheldSpecRefs, keptSpecRefs);
|
|
13489
|
+
const result = excludeWithheldOperations(spec, withheldSpecRefs, keptSpecRefs, allSpecs);
|
|
13282
13490
|
spec.content = result.content;
|
|
13283
13491
|
withheldOperations.set(spec, result.excluded);
|
|
13492
|
+
result.excluded.forEach((operation) => withheldKeys.add(operationKey(operation)));
|
|
13284
13493
|
}
|
|
13285
13494
|
const resolvedSpecs = allSpecs.filter((spec) => !spec.pageOnly);
|
|
13286
13495
|
const pageOnlySpecs = allSpecs.filter((spec) => spec.pageOnly);
|
|
@@ -13288,12 +13497,14 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13288
13497
|
if (!assets.some((asset) => asset.path === specAssetPath(spec.filename))) {
|
|
13289
13498
|
assets.push(specAsset(spec.filename, spec.content));
|
|
13290
13499
|
}
|
|
13291
|
-
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath,
|
|
13500
|
+
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath, true))) warnSharedSpec(spec.sourcePath, withheldOperations.get(spec));
|
|
13292
13501
|
}
|
|
13293
13502
|
let pageSpecs = resolvedSpecs;
|
|
13294
13503
|
if (resolvedSpecs.length > 0) {
|
|
13504
|
+
const namedByPages = new Set(pages.filter((page) => !restrictedPages.has(page)).map((page) => specNamedByPage(page.openapi, resolvedSpecs)));
|
|
13295
13505
|
for (const spec of resolvedSpecs) {
|
|
13296
|
-
spec.
|
|
13506
|
+
spec.pageReferenced = namedByPages.has(spec);
|
|
13507
|
+
spec.generatedNavigation = Boolean(spec.directory) && !publishedSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath, true));
|
|
13297
13508
|
}
|
|
13298
13509
|
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
13299
13510
|
if (remoteApiSpecs.length > 0) {
|
|
@@ -13311,13 +13522,16 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13311
13522
|
const fallback = findOpenApi(files);
|
|
13312
13523
|
const fallbackContent = fallback ? readSpecFile(fallback.absolutePath, fallback.relativePath, warnings) : void 0;
|
|
13313
13524
|
if (fallback && fallbackContent) {
|
|
13314
|
-
const
|
|
13315
|
-
const matches = (ref) => specRefMatches(ref, fallback.relativePath, filename, true);
|
|
13525
|
+
const matches = (ref) => specRefMatches(ref, fallback.relativePath, true);
|
|
13316
13526
|
if (withheldSpecRefs.some(matches) && !publishedSpecRefs.some(matches)) {
|
|
13317
|
-
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(
|
|
13527
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(basename3(fallback.relativePath))}`, content: fallbackContent });
|
|
13318
13528
|
withheldAssetCount++;
|
|
13319
13529
|
} else {
|
|
13320
|
-
const
|
|
13530
|
+
const filename = nameSpec({ sourcePath: fallback.relativePath, filename: basename3(fallback.relativePath) });
|
|
13531
|
+
const fallbackSpec = { sourcePath: fallback.relativePath, filename, content: fallbackContent };
|
|
13532
|
+
({ kept: keptSpecRefs, unowned: unownedOperations } = keptOperations(fallback.relativePath));
|
|
13533
|
+
const pruned = excludeWithheldOperations(fallbackSpec, withheldSpecRefs, keptSpecRefs, [fallbackSpec, ...allSpecs]);
|
|
13534
|
+
pruned.excluded.forEach((operation) => withheldKeys.add(operationKey(operation)));
|
|
13321
13535
|
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
13322
13536
|
assets.push(specAsset(filename, pruned.content));
|
|
13323
13537
|
}
|
|
@@ -13329,13 +13543,22 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13329
13543
|
}
|
|
13330
13544
|
for (const spec of pageOnlySpecs) {
|
|
13331
13545
|
if (!assets.some((asset) => asset.path === specAssetPath(spec.filename))) assets.push(specAsset(spec.filename, spec.content));
|
|
13332
|
-
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath,
|
|
13546
|
+
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath, true))) warnSharedSpec(spec.sourcePath, withheldOperations.get(spec));
|
|
13333
13547
|
}
|
|
13334
13548
|
if (pageOnlySpecs.length > 0) {
|
|
13335
13549
|
docsConfig = injectOpenApiSpecs(docsConfig, pageOnlySpecs, warnings);
|
|
13336
13550
|
pageSpecs = [...pageSpecs, ...pageOnlySpecs];
|
|
13337
13551
|
}
|
|
13552
|
+
const notKeptPublic = [...new Set(unownedOperations.filter((operation) => withheldKeys.has(operationKey(operation))))];
|
|
13553
|
+
if (notKeptPublic.length > 0) {
|
|
13554
|
+
warnings.push({
|
|
13555
|
+
code: "gated-page",
|
|
13556
|
+
message: `${notKeptPublic.length} operation(s) withheld from a published OpenAPI spec (${notKeptPublic.join(", ")}) are also named by a published page or navigation entry that does not say which spec it means, so that reference did not keep them public. Write the spec in its openapi value (for example "openapi.json GET /x") to publish the operation.`
|
|
13557
|
+
});
|
|
13558
|
+
}
|
|
13338
13559
|
const explainMissingSpec = (key) => {
|
|
13560
|
+
const fits = resolveSpecRef(key, specFileCandidates(files), false);
|
|
13561
|
+
if (fits.kind === "ambiguous") return `several spec files fit that name (${fits.candidates.map((spec) => spec.sourcePath).join(", ")}); write the full path in the page's openapi frontmatter`;
|
|
13339
13562
|
const sameName = scannedFiles.find((file) => file.relativePath.toLowerCase() === key.toLowerCase());
|
|
13340
13563
|
if (sameName && sameName.relativePath !== key) return `paths are case-sensitive; the file is at "${sameName.relativePath}"`;
|
|
13341
13564
|
if (sameName && mintignoreMatcher?.ignores(sameName.relativePath)) return "the file is excluded by .mintignore";
|
|
@@ -13675,10 +13898,10 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
13675
13898
|
const sourceFavicon = mintlifyConfig?.favicon ?? fernRawConfig?.favicon ?? docusaurusSettings?.favicon;
|
|
13676
13899
|
const logo = brandVariants(sourceLogo);
|
|
13677
13900
|
const favicon = brandVariants(sourceFavicon);
|
|
13678
|
-
const logoLight = publicBrandPath(logo.light);
|
|
13679
13901
|
const logoDark = publicBrandPath(logo.dark);
|
|
13680
|
-
const
|
|
13902
|
+
const logoLight = publicBrandPath(logo.light) ?? logoDark;
|
|
13681
13903
|
const faviconDark = publicBrandPath(favicon.dark);
|
|
13904
|
+
const faviconLight = publicBrandPath(favicon.light) ?? faviconDark;
|
|
13682
13905
|
if (logoLight) docsConfig = {
|
|
13683
13906
|
...docsConfig,
|
|
13684
13907
|
navbar: {
|
|
@@ -13707,15 +13930,30 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
13707
13930
|
const decoration = background.decoration;
|
|
13708
13931
|
const image = publicBrandPath(background.image);
|
|
13709
13932
|
const imageDark = publicBrandPath(background.imageDark);
|
|
13710
|
-
|
|
13711
|
-
...docsConfig,
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
...image ? { image } : {},
|
|
13716
|
-
...imageDark ? { imageDark } : {}
|
|
13717
|
-
}
|
|
13933
|
+
const mapped = {
|
|
13934
|
+
...docsConfig.background,
|
|
13935
|
+
...decoration === "none" || decoration === "grid" || decoration === "gradient" ? { decoration } : {},
|
|
13936
|
+
...image ? { image } : {},
|
|
13937
|
+
...imageDark ? { imageDark } : {}
|
|
13718
13938
|
};
|
|
13939
|
+
if (Object.keys(mapped).length > 0) docsConfig = { ...docsConfig, background: mapped };
|
|
13940
|
+
const canvas = background.color && typeof background.color === "object" ? background.color : {};
|
|
13941
|
+
for (const mode of ["light", "dark"]) {
|
|
13942
|
+
if (canvas[mode] === void 0) continue;
|
|
13943
|
+
const hex = typeof canvas[mode] === "string" ? cssColorToHex(canvas[mode]) : void 0;
|
|
13944
|
+
if (!hex) {
|
|
13945
|
+
warnings.push({
|
|
13946
|
+
code: "unsupported-config",
|
|
13947
|
+
message: `background.color.${mode} ${JSON.stringify(canvas[mode])?.slice(0, 40)} is not a hex or rgb() colour (for example #121317) and was dropped, so that mode keeps Thally's default canvas. Use a hex value in docs.json to set it.`,
|
|
13948
|
+
source: "docs.json"
|
|
13949
|
+
});
|
|
13950
|
+
continue;
|
|
13951
|
+
}
|
|
13952
|
+
docsConfig = {
|
|
13953
|
+
...docsConfig,
|
|
13954
|
+
colors: { ...docsConfig.colors, [mode]: { ...docsConfig.colors?.[mode], background: hex } }
|
|
13955
|
+
};
|
|
13956
|
+
}
|
|
13719
13957
|
}
|
|
13720
13958
|
}
|
|
13721
13959
|
if (platform === "docusaurus") {
|
|
@@ -13725,13 +13963,23 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
13725
13963
|
...docsConfig,
|
|
13726
13964
|
favicon: { light: faviconLight, ...faviconDark ? { dark: faviconDark } : {} }
|
|
13727
13965
|
};
|
|
13728
|
-
const
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
];
|
|
13966
|
+
const notImported = (variants) => [variants.light, variants.dark].filter((value) => typeof value === "string" && !publicBrandPath(value));
|
|
13967
|
+
const missingLogo = notImported(logo);
|
|
13968
|
+
const missingFavicon = notImported(favicon);
|
|
13969
|
+
const missingBrand = [...missingLogo, ...missingFavicon];
|
|
13970
|
+
const substituted = [
|
|
13971
|
+
...missingLogo.length > 0 && logoLight ? [`logo (${logoLight})`] : [],
|
|
13972
|
+
...missingFavicon.length > 0 && faviconLight ? [`favicon (${faviconLight})`] : []
|
|
13973
|
+
].map((asset) => ` The imported ${asset} is used for both light and dark mode.`).join("");
|
|
13732
13974
|
if (missingBrand.length > 0) warnings.push({
|
|
13733
13975
|
code: "unsupported-config",
|
|
13734
|
-
message: `Brand asset(s) were referenced but not imported: ${[...new Set(missingBrand)].join(", ")}. Add these files under public/ or update docs.json
|
|
13976
|
+
message: `Brand asset(s) were referenced but not imported: ${[...new Set(missingBrand)].join(", ")}. Add these files under public/ or update docs.json.${substituted}`
|
|
13977
|
+
});
|
|
13978
|
+
const logoHref = sourceLogo && typeof sourceLogo === "object" ? sourceLogo.href : void 0;
|
|
13979
|
+
if (typeof logoHref === "string" && logoHref.trim() && logoHref.trim() !== "/") warnings.push({
|
|
13980
|
+
code: "unsupported-config",
|
|
13981
|
+
message: `logo.href ${JSON.stringify(logoHref.trim().slice(0, 120))} was dropped: the header logo always links to the docs home page. Add it as a navbar link in docs.json if readers need it.`,
|
|
13982
|
+
source: mintlifyConfig ? "docs.json" : "docs.yml"
|
|
13735
13983
|
});
|
|
13736
13984
|
if (platform === "mintlify") {
|
|
13737
13985
|
docsConfig = { ...docsConfig, markdown: { enabled: true } };
|
|
@@ -13764,7 +14012,7 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
13764
14012
|
message: `Fern global theme ${JSON.stringify(fernRawConfig["global-theme"])} could not be fetched during local migration; local overrides and Thally default styling tokens were applied.`,
|
|
13765
14013
|
source: "global-theme"
|
|
13766
14014
|
});
|
|
13767
|
-
if (fernRawConfig?.logo && !fernRawConfig["global-theme"] && !logoLight && !logo.light) warnings.push({
|
|
14015
|
+
if (fernRawConfig?.logo && !fernRawConfig["global-theme"] && !logoLight && !logo.light && !logo.dark) warnings.push({
|
|
13768
14016
|
code: "unsupported-config",
|
|
13769
14017
|
message: "Fern supplied a logo through its global theme without a local asset; add a logo path to docs.json after import."
|
|
13770
14018
|
});
|