@mintlify/cli 4.0.1763 → 4.0.1765
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/__test__/migrate.test.ts +186 -0
- package/__test__/validateRules.test.ts +36 -0
- package/bin/cli.js +35 -0
- package/bin/migrate.js +54 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/bin/validate/files.js +16 -4
- package/bin/validate/mdx.js +1 -1
- package/migrate/index.md +56 -0
- package/migrate/sources/confluence.md +120 -0
- package/migrate/sources/docsify.md +128 -0
- package/migrate/sources/document360.md +117 -0
- package/migrate/sources/docus.md +136 -0
- package/migrate/sources/docusaurus.md +143 -0
- package/migrate/sources/fern.md +130 -0
- package/migrate/sources/fumadocs.md +141 -0
- package/migrate/sources/gitbook.md +120 -0
- package/migrate/sources/github-wiki.md +90 -0
- package/migrate/sources/jekyll.md +104 -0
- package/migrate/sources/mdbook.md +98 -0
- package/migrate/sources/mkdocs.md +113 -0
- package/migrate/sources/nextra.md +144 -0
- package/migrate/sources/notion.md +99 -0
- package/migrate/sources/nuxt.md +113 -0
- package/migrate/sources/pdf.md +99 -0
- package/migrate/sources/readme.md +118 -0
- package/migrate/sources/redocly.md +114 -0
- package/migrate/sources/starlight.md +136 -0
- package/migrate/sources/vitepress.md +143 -0
- package/migrate/sources/vuepress.md +153 -0
- package/migrate/topics/api.md +76 -0
- package/migrate/topics/changelog.md +47 -0
- package/migrate/topics/conversion.md +194 -0
- package/migrate/topics/discovery.md +93 -0
- package/migrate/topics/navigation.md +82 -0
- package/migrate/topics/verify.md +58 -0
- package/package.json +10 -10
- package/src/cli.tsx +47 -0
- package/src/migrate.ts +76 -0
- package/src/validate/files.ts +23 -3
- package/src/validate/mdx.ts +1 -1
package/src/cli.tsx
CHANGED
|
@@ -42,6 +42,13 @@ import { login } from './login.js';
|
|
|
42
42
|
import { logout } from './logout.js';
|
|
43
43
|
import { mdxLinter } from './mdxLinter.js';
|
|
44
44
|
import { createTelemetryMiddleware } from './middlewares/telemetryMiddleware.js';
|
|
45
|
+
import {
|
|
46
|
+
agents,
|
|
47
|
+
listMigrationDocs,
|
|
48
|
+
migrateWithAgent,
|
|
49
|
+
migrationInstructions,
|
|
50
|
+
type AgentName,
|
|
51
|
+
} from './migrate.js';
|
|
45
52
|
import { ogCommand } from './og.js';
|
|
46
53
|
import { buildOverlayRegistryForCheck, checkOpenApiFile } from './openApiCheck.js';
|
|
47
54
|
import { scoreHandler } from './score/index.js';
|
|
@@ -930,6 +937,46 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
|
|
|
930
937
|
.example('mint format', 'format all MDX files in the current directory'),
|
|
931
938
|
formatHandler
|
|
932
939
|
)
|
|
940
|
+
.command(
|
|
941
|
+
'migrate',
|
|
942
|
+
'Print instructions for migrating a docs site to Mintlify',
|
|
943
|
+
(yargs: Argv) =>
|
|
944
|
+
yargs
|
|
945
|
+
.option('from', {
|
|
946
|
+
type: 'string',
|
|
947
|
+
choices: listMigrationDocs('sources'),
|
|
948
|
+
description: 'Include instructions for a source platform',
|
|
949
|
+
})
|
|
950
|
+
.option('topic', {
|
|
951
|
+
type: 'string',
|
|
952
|
+
choices: listMigrationDocs('topics'),
|
|
953
|
+
description: 'Print a single topic',
|
|
954
|
+
})
|
|
955
|
+
.option('agent', {
|
|
956
|
+
type: 'string',
|
|
957
|
+
choices: Object.keys(agents) as AgentName[],
|
|
958
|
+
description: 'Run the migration with a coding agent',
|
|
959
|
+
})
|
|
960
|
+
.option('claude', {
|
|
961
|
+
type: 'boolean',
|
|
962
|
+
description: 'Alias for --agent claude',
|
|
963
|
+
}),
|
|
964
|
+
async (argv) => {
|
|
965
|
+
const agent = argv.agent ?? (argv.claude ? 'claude' : undefined);
|
|
966
|
+
if (agent) {
|
|
967
|
+
try {
|
|
968
|
+
return await terminate(await migrateWithAgent(agent, argv.from));
|
|
969
|
+
} catch (error) {
|
|
970
|
+
addLog(<ErrorLog message={error instanceof Error ? error.message : String(error)} />);
|
|
971
|
+
return await terminate(1);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
await new Promise<void>((resolve) =>
|
|
975
|
+
process.stdout.write(migrationInstructions(argv), () => resolve())
|
|
976
|
+
);
|
|
977
|
+
await terminate(0);
|
|
978
|
+
}
|
|
979
|
+
)
|
|
933
980
|
// Coming soon commands — visible in help, tracked via telemetry to gauge interest.
|
|
934
981
|
.command(
|
|
935
982
|
'ai',
|
package/src/migrate.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import { windowsCompatibleSpawnOptions } from './helpers.js';
|
|
7
|
+
|
|
8
|
+
type Section = 'topics' | 'sources';
|
|
9
|
+
|
|
10
|
+
const root = fileURLToPath(new URL('../migrate', import.meta.url));
|
|
11
|
+
|
|
12
|
+
const read = (...segments: string[]) => readFileSync(path.join(root, ...segments), 'utf8');
|
|
13
|
+
|
|
14
|
+
export const listMigrationDocs = (section: Section) =>
|
|
15
|
+
readdirSync(path.join(root, section))
|
|
16
|
+
.filter((file) => file.endsWith('.md'))
|
|
17
|
+
.map((file) => file.slice(0, -3))
|
|
18
|
+
.sort();
|
|
19
|
+
|
|
20
|
+
const overview = () =>
|
|
21
|
+
[
|
|
22
|
+
read('index.md').trimEnd(),
|
|
23
|
+
`Topics: ${listMigrationDocs('topics').join(', ')}. Run \`mint migrate --topic <name>\`.`,
|
|
24
|
+
`Sources: ${listMigrationDocs('sources').join(', ')}. Run \`mint migrate --from <name>\`.`,
|
|
25
|
+
].join('\n\n');
|
|
26
|
+
|
|
27
|
+
export const migrationInstructions = ({ from, topic }: { from?: string; topic?: string }) => {
|
|
28
|
+
if (topic) return read('topics', `${topic}.md`);
|
|
29
|
+
if (from) return `${overview()}\n\n${read('sources', `${from}.md`)}`;
|
|
30
|
+
return `${overview()}\n`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const prompt = (from?: string) =>
|
|
34
|
+
`Migrate a documentation site to Mintlify in the current directory. Run \`mint migrate${from ? ` --from ${from}` : ''}\` and follow the instructions it prints.`;
|
|
35
|
+
|
|
36
|
+
export const agents = {
|
|
37
|
+
claude: {
|
|
38
|
+
bin: 'claude',
|
|
39
|
+
args: (prompt: string) => [prompt],
|
|
40
|
+
install: 'npm install -g @anthropic-ai/claude-code',
|
|
41
|
+
},
|
|
42
|
+
codex: {
|
|
43
|
+
bin: 'codex',
|
|
44
|
+
args: (prompt: string) => [prompt],
|
|
45
|
+
install: 'npm install -g @openai/codex',
|
|
46
|
+
},
|
|
47
|
+
cursor: {
|
|
48
|
+
bin: 'cursor-agent',
|
|
49
|
+
args: (prompt: string) => [prompt],
|
|
50
|
+
install: 'curl https://cursor.com/install -fsS | bash',
|
|
51
|
+
},
|
|
52
|
+
gemini: {
|
|
53
|
+
bin: 'gemini',
|
|
54
|
+
args: (prompt: string) => ['-i', prompt],
|
|
55
|
+
install: 'npm install -g @google/gemini-cli',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type AgentName = keyof typeof agents;
|
|
60
|
+
|
|
61
|
+
export const agentArgs = (name: AgentName, from?: string) => agents[name].args(prompt(from));
|
|
62
|
+
|
|
63
|
+
export const migrateWithAgent = (name: AgentName, from?: string): Promise<number> =>
|
|
64
|
+
new Promise((resolve, reject) => {
|
|
65
|
+
const { bin, install } = agents[name];
|
|
66
|
+
const args = agentArgs(name, from).map((arg) =>
|
|
67
|
+
windowsCompatibleSpawnOptions.shell ? `"${arg}"` : arg
|
|
68
|
+
);
|
|
69
|
+
const child = spawn(bin, args, { stdio: 'inherit', ...windowsCompatibleSpawnOptions });
|
|
70
|
+
child.on('error', (error: NodeJS.ErrnoException) =>
|
|
71
|
+
reject(
|
|
72
|
+
error.code === 'ENOENT' ? new Error(`${bin} not found. Install it with: ${install}`) : error
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
76
|
+
});
|
package/src/validate/files.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
parseContent,
|
|
14
14
|
relocate,
|
|
15
15
|
selectEdits,
|
|
16
|
+
toOffset,
|
|
16
17
|
unmapEdit,
|
|
17
18
|
} from './mdx.js';
|
|
18
19
|
import { PARSE_ERROR } from './rules/parseError.js';
|
|
@@ -56,16 +57,35 @@ const readFrontmatter = (content: string): Record<string, unknown> => {
|
|
|
56
57
|
type ParseError = { line?: number; column?: number; message: string };
|
|
57
58
|
|
|
58
59
|
const REASON_POSITION = /\s*\(\d+:\d+(?:-\d+:\d+)?\)$/;
|
|
60
|
+
const TAG_NAME_RULES = new Set(['unexpected-character', 'unexpected-eof']);
|
|
61
|
+
const ESCAPE_HINT = "; if this < isn't a tag, escape it as \\<";
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
const isStrayLessThan = (
|
|
64
|
+
error: unknown,
|
|
65
|
+
content: string,
|
|
66
|
+
position: { line?: number; column?: number } | null
|
|
67
|
+
) => {
|
|
68
|
+
const { source, ruleId } = (error ?? {}) as { source?: unknown; ruleId?: unknown };
|
|
69
|
+
return (
|
|
70
|
+
source === 'micromark-extension-mdx-jsx' &&
|
|
71
|
+
typeof ruleId === 'string' &&
|
|
72
|
+
TAG_NAME_RULES.has(ruleId) &&
|
|
73
|
+
position?.line != null &&
|
|
74
|
+
position.column != null &&
|
|
75
|
+
content[toOffset(content, position.line, position.column) - 1] === '<'
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const toParseError = (error: unknown, content = ''): ParseError => {
|
|
61
80
|
const reason = (error as { reason?: unknown } | null)?.reason;
|
|
62
81
|
const fallback = error instanceof Error ? error.message : 'could not parse this file';
|
|
63
82
|
const text = typeof reason === 'string' ? reason : fallback;
|
|
64
83
|
const position = error && typeof error === 'object' ? getErrorPosition(error) : null;
|
|
84
|
+
const hint = isStrayLessThan(error, content, position) ? ESCAPE_HINT : '';
|
|
65
85
|
return {
|
|
66
86
|
...(position?.line != null && { line: position.line }),
|
|
67
87
|
...(position?.column != null && { column: position.column }),
|
|
68
|
-
message: text.replace(REASON_POSITION, ''),
|
|
88
|
+
message: text.replace(REASON_POSITION, '') + hint,
|
|
69
89
|
};
|
|
70
90
|
};
|
|
71
91
|
|
|
@@ -102,7 +122,7 @@ export const parseFile = (
|
|
|
102
122
|
try {
|
|
103
123
|
return { context: { ...context, tree: getAST(content, file) }, approximate: true };
|
|
104
124
|
} catch (error) {
|
|
105
|
-
return { error: toParseError(error) };
|
|
125
|
+
return { error: toParseError(error, content) };
|
|
106
126
|
}
|
|
107
127
|
};
|
|
108
128
|
|
package/src/validate/mdx.ts
CHANGED
|
@@ -186,7 +186,7 @@ export const applyEdits = (content: string, edits: Edit[]): string =>
|
|
|
186
186
|
content
|
|
187
187
|
);
|
|
188
188
|
|
|
189
|
-
const toOffset = (content: string, line: number, column = 1) => {
|
|
189
|
+
export const toOffset = (content: string, line: number, column = 1) => {
|
|
190
190
|
let offset = 0;
|
|
191
191
|
for (let current = 1; current < line; current++) {
|
|
192
192
|
const next = content.indexOf('\n', offset);
|