@mintlify/cli 4.0.1409 → 4.0.1411
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__/brokenLinks.test.ts +2 -3
- package/__test__/resolveFiles.test.ts +70 -0
- package/bin/cli.js +1 -39
- package/bin/{deslop/resolveFiles.js → resolveFiles.js} +0 -74
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/cli.tsx +1 -51
- package/src/{deslop/resolveFiles.ts → resolveFiles.ts} +0 -77
- package/__test__/deslop/client.test.ts +0 -167
- package/__test__/deslop/resolveFiles.test.ts +0 -183
- package/__test__/deslop/whitespace.test.ts +0 -94
- package/bin/deslop/client.js +0 -74
- package/bin/deslop/index.js +0 -242
- package/bin/deslop/types.js +0 -1
- package/bin/deslop/whitespace.js +0 -144
- package/src/deslop/client.ts +0 -77
- package/src/deslop/index.tsx +0 -286
- package/src/deslop/types.ts +0 -39
- package/src/deslop/whitespace.ts +0 -155
|
@@ -4,8 +4,8 @@ import { addLog, clearLogs } from '@mintlify/previewing';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { mockProcessExit } from 'vitest-mock-process';
|
|
6
6
|
|
|
7
|
-
import { resolveExplicitFiles } from '../src/deslop/resolveFiles.js';
|
|
8
7
|
import { checkForMintJson, CMD_EXEC_PATH, findDocsRoot } from '../src/helpers.js';
|
|
8
|
+
import { resolveExplicitFiles } from '../src/resolveFiles.js';
|
|
9
9
|
import { runCommand } from './utils.js';
|
|
10
10
|
|
|
11
11
|
vi.mock('@mintlify/previewing', async (importOriginal) => {
|
|
@@ -22,8 +22,7 @@ vi.mock('../src/helpers.js', async (importOriginal) => {
|
|
|
22
22
|
};
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
vi.mock('../src/
|
|
26
|
-
resolveChangedFiles: vi.fn(),
|
|
25
|
+
vi.mock('../src/resolveFiles.js', () => ({
|
|
27
26
|
resolveExplicitFiles: vi.fn(),
|
|
28
27
|
}));
|
|
29
28
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { resolveExplicitFiles } from '../src/resolveFiles.js';
|
|
6
|
+
|
|
7
|
+
let baseDir: string;
|
|
8
|
+
|
|
9
|
+
async function writeFixture(rel: string) {
|
|
10
|
+
const filePath = path.join(baseDir, rel);
|
|
11
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
12
|
+
await fs.writeFile(filePath, '# fixture\n');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
baseDir = await fs.mkdtemp(path.join(os.tmpdir(), 'resolve-files-test-'));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await fs.rm(baseDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('resolveExplicitFiles', () => {
|
|
24
|
+
it('resolves literal paths', async () => {
|
|
25
|
+
await writeFixture('docs/a.mdx');
|
|
26
|
+
await expect(resolveExplicitFiles(baseDir, ['docs/a.mdx'])).resolves.toEqual(['docs/a.mdx']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('normalizes leading ./', async () => {
|
|
30
|
+
await writeFixture('docs/a.md');
|
|
31
|
+
await expect(resolveExplicitFiles(baseDir, ['./docs/a.md'])).resolves.toEqual(['docs/a.md']);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('errors listing paths that matched nothing', async () => {
|
|
35
|
+
await writeFixture('docs/a.mdx');
|
|
36
|
+
await expect(
|
|
37
|
+
resolveExplicitFiles(baseDir, ['docs/a.mdx', 'docs/missing.mdx', 'nope/*.mdx'])
|
|
38
|
+
).rejects.toThrow('No matching files for: docs/missing.mdx, nope/*.mdx');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('accepts existing literal paths regardless of extension', async () => {
|
|
42
|
+
await writeFixture('README.txt');
|
|
43
|
+
await expect(resolveExplicitFiles(baseDir, ['README.txt'])).resolves.toEqual(['README.txt']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('rejects a directory passed as a literal path', async () => {
|
|
47
|
+
await fs.mkdir(path.join(baseDir, 'docs'), { recursive: true });
|
|
48
|
+
await expect(resolveExplicitFiles(baseDir, ['docs'])).rejects.toThrow('No matching files');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('expands globs recursively and keeps only docs files', async () => {
|
|
52
|
+
await writeFixture('docs/a.mdx');
|
|
53
|
+
await writeFixture('docs/nested/deep/b.mdx');
|
|
54
|
+
await writeFixture('docs/nested/notes.txt');
|
|
55
|
+
await writeFixture('top.md');
|
|
56
|
+
|
|
57
|
+
await expect(resolveExplicitFiles(baseDir, ['docs/**/*.mdx'])).resolves.toEqual([
|
|
58
|
+
'docs/a.mdx',
|
|
59
|
+
'docs/nested/deep/b.mdx',
|
|
60
|
+
]);
|
|
61
|
+
await expect(resolveExplicitFiles(baseDir, ['*.md'])).resolves.toEqual(['top.md']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('dedupes overlapping literal and glob matches', async () => {
|
|
65
|
+
await writeFixture('docs/a.mdx');
|
|
66
|
+
await expect(resolveExplicitFiles(baseDir, ['docs/a.mdx', 'docs/*.mdx'])).resolves.toEqual([
|
|
67
|
+
'docs/a.mdx',
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
});
|
package/bin/cli.js
CHANGED
|
@@ -21,8 +21,6 @@ import { comingSoon } from './comingSoon.js';
|
|
|
21
21
|
import { setTelemetryEnabled } from './config.js';
|
|
22
22
|
import { getConfigValue, setConfigValue, clearConfigValue } from './config.js';
|
|
23
23
|
import { API_URL } from './constants.js';
|
|
24
|
-
import { deslopHandler } from './deslop/index.js';
|
|
25
|
-
import { resolveExplicitFiles } from './deslop/resolveFiles.js';
|
|
26
24
|
import { formatHandler } from './format.js';
|
|
27
25
|
import { CMD_EXEC_PATH, checkPort, checkNodeVersion, autoUpgradeIfNeeded, findDocsRoot, getVersions, isAI, suppressConsoleWarnings, terminate, } from './helpers.js';
|
|
28
26
|
import { runIndexSetup } from './indexSetup/setup.js';
|
|
@@ -31,9 +29,9 @@ import { getAccessToken } from './keyring.js';
|
|
|
31
29
|
import { login } from './login.js';
|
|
32
30
|
import { logout } from './logout.js';
|
|
33
31
|
import { mdxLinter } from './mdxLinter.js';
|
|
34
|
-
import { subdomainMiddleware } from './middlewares/subdomainMiddleware.js';
|
|
35
32
|
import { createTelemetryMiddleware } from './middlewares/telemetryMiddleware.js';
|
|
36
33
|
import { checkOpenApiFile, getOpenApiFilenamesFromDocsConfig } from './openApiCheck.js';
|
|
34
|
+
import { resolveExplicitFiles } from './resolveFiles.js';
|
|
37
35
|
import { scoreHandler } from './score/index.js';
|
|
38
36
|
import { signup } from './signup.js';
|
|
39
37
|
import { status, getCliSubdomains } from './status.js';
|
|
@@ -546,42 +544,6 @@ export const cli = ({ packageName = 'mint' }) => {
|
|
|
546
544
|
})
|
|
547
545
|
.example('mint score', 'Run agent readiness checks on your default subdomain')
|
|
548
546
|
.example('mint score docs.example.com', 'Run agent readiness checks on a URL'), scoreHandler)
|
|
549
|
-
.command('deslop [files..]', 'Detect AI-sounding prose and get humanization suggestions', (yargs) => yargs
|
|
550
|
-
.middleware(subdomainMiddleware)
|
|
551
|
-
.positional('files', {
|
|
552
|
-
type: 'string',
|
|
553
|
-
array: true,
|
|
554
|
-
description: 'Files or globs to check (defaults to git-changed pages)',
|
|
555
|
-
})
|
|
556
|
-
.option('format', {
|
|
557
|
-
type: 'string',
|
|
558
|
-
choices: ['table', 'plain', 'json'],
|
|
559
|
-
description: 'Output format',
|
|
560
|
-
})
|
|
561
|
-
.option('subdomain', {
|
|
562
|
-
type: 'string',
|
|
563
|
-
description: 'Documentation subdomain (default: mint config set subdomain)',
|
|
564
|
-
})
|
|
565
|
-
.option('threshold', {
|
|
566
|
-
type: 'number',
|
|
567
|
-
default: 0.5,
|
|
568
|
-
description: 'Fail a page when its AI-written fraction meets this value (0-1)',
|
|
569
|
-
})
|
|
570
|
-
.option('fix-whitespace', {
|
|
571
|
-
type: 'boolean',
|
|
572
|
-
default: false,
|
|
573
|
-
description: 'Normalize prose whitespace in the checked files (skips code and frontmatter)',
|
|
574
|
-
})
|
|
575
|
-
.check((argv) => {
|
|
576
|
-
if (typeof argv.threshold === 'number' &&
|
|
577
|
-
(argv.threshold < 0 || argv.threshold > 1 || Number.isNaN(argv.threshold))) {
|
|
578
|
-
throw new Error('--threshold must be a number between 0 and 1');
|
|
579
|
-
}
|
|
580
|
-
return true;
|
|
581
|
-
})
|
|
582
|
-
.example('mint deslop', 'Check git-changed pages for AI-sounding prose')
|
|
583
|
-
.example('mint deslop docs/guide.mdx', 'Check a specific page')
|
|
584
|
-
.example('mint deslop "docs/**/*.mdx" --format json', 'Check pages by glob with agent-friendly JSON output'), deslopHandler)
|
|
585
547
|
.command('format', 'Format MDX files in the current directory', (yargs) => yargs
|
|
586
548
|
.usage('usage: mint format')
|
|
587
549
|
.example('mint format', 'format all MDX files in the current directory'), formatHandler)
|
|
@@ -7,8 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { categorizeFilePaths, getMintIgnore } from '@mintlify/prebuild';
|
|
11
|
-
import { execFile } from 'node:child_process';
|
|
12
10
|
import fs from 'node:fs/promises';
|
|
13
11
|
import path from 'node:path';
|
|
14
12
|
const GLOB_CHARS = /[*?]/;
|
|
@@ -53,17 +51,6 @@ function globToRegExp(pattern) {
|
|
|
53
51
|
}
|
|
54
52
|
return new RegExp(`^${source}$`);
|
|
55
53
|
}
|
|
56
|
-
function exists(filePath) {
|
|
57
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
-
try {
|
|
59
|
-
yield fs.access(filePath);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
catch (_a) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
54
|
function isFile(filePath) {
|
|
68
55
|
return __awaiter(this, void 0, void 0, function* () {
|
|
69
56
|
try {
|
|
@@ -123,64 +110,3 @@ export function resolveExplicitFiles(baseDir, args) {
|
|
|
123
110
|
return [...new Set(matched)];
|
|
124
111
|
});
|
|
125
112
|
}
|
|
126
|
-
function runGit(args, cwd) {
|
|
127
|
-
return new Promise((resolve, reject) => {
|
|
128
|
-
execFile('git', args, { cwd }, (error, stdout) => {
|
|
129
|
-
if (error)
|
|
130
|
-
reject(error);
|
|
131
|
-
else
|
|
132
|
-
resolve(stdout);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
function parseGitLines(stdout) {
|
|
137
|
-
return stdout
|
|
138
|
-
.split('\n')
|
|
139
|
-
.map((line) => line.trim())
|
|
140
|
-
.filter(Boolean);
|
|
141
|
-
}
|
|
142
|
-
export function resolveChangedFiles(baseDir) {
|
|
143
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
-
try {
|
|
145
|
-
yield runGit(['rev-parse', '--is-inside-work-tree'], baseDir);
|
|
146
|
-
}
|
|
147
|
-
catch (_a) {
|
|
148
|
-
throw new Error('Not a git repository, so changed pages cannot be detected. Pass explicit paths: `mint deslop <files..>`.');
|
|
149
|
-
}
|
|
150
|
-
let diff;
|
|
151
|
-
let untracked;
|
|
152
|
-
try {
|
|
153
|
-
[diff, untracked] = yield Promise.all([
|
|
154
|
-
runGit(['-c', 'core.quotePath=false', 'diff', '--name-only', '--relative', 'HEAD'], baseDir),
|
|
155
|
-
runGit(['-c', 'core.quotePath=false', 'ls-files', '--others', '--exclude-standard'], baseDir),
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
catch (_b) {
|
|
159
|
-
throw new Error('Could not detect changed pages (does the repository have any commits?). Pass explicit paths: `mint deslop <files..>`.');
|
|
160
|
-
}
|
|
161
|
-
const candidates = [...new Set([...parseGitLines(diff), ...parseGitLines(untracked)])]
|
|
162
|
-
.map(normalizeArg)
|
|
163
|
-
.filter((file) => DOCS_FILE.test(file));
|
|
164
|
-
const existing = [];
|
|
165
|
-
for (const file of candidates) {
|
|
166
|
-
if (yield exists(path.join(baseDir, file)))
|
|
167
|
-
existing.push(file);
|
|
168
|
-
}
|
|
169
|
-
if (existing.length === 0)
|
|
170
|
-
return [];
|
|
171
|
-
if (!(yield exists(path.join(baseDir, 'docs.json')))) {
|
|
172
|
-
return existing;
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const mintIgnore = yield getMintIgnore(baseDir);
|
|
176
|
-
const { contentFilenames } = yield categorizeFilePaths(baseDir, mintIgnore);
|
|
177
|
-
const contentSet = new Set(contentFilenames.map((file) => toPosix(file).replace(/^\//, '')));
|
|
178
|
-
return existing.filter((file) => contentSet.has(file));
|
|
179
|
-
}
|
|
180
|
-
catch (_c) {
|
|
181
|
-
// docs.json is present but its navigation could not be resolved. Fail closed
|
|
182
|
-
// rather than sending possibly-ignored files to the credit-consuming API.
|
|
183
|
-
throw new Error('Could not read docs.json to determine which pages to check. Pass explicit paths: `mint deslop <files..>`.');
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
}
|