@mintlify/cli 4.0.1338 → 4.0.1339

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.
@@ -1,9 +1,11 @@
1
1
  import { buildGraph, getBrokenExternalLinks } from '@mintlify/link-rot';
2
2
  import { MdxPath } from '@mintlify/link-rot/dist/graph.js';
3
3
  import { addLog, clearLogs } from '@mintlify/previewing';
4
+ import path from 'node:path';
4
5
  import { mockProcessExit } from 'vitest-mock-process';
5
6
 
6
- import { checkForMintJson } from '../src/helpers.js';
7
+ import { resolveExplicitFiles } from '../src/deslop/resolveFiles.js';
8
+ import { checkForMintJson, CMD_EXEC_PATH } from '../src/helpers.js';
7
9
  import { runCommand } from './utils.js';
8
10
 
9
11
  vi.mock('@mintlify/previewing', async (importOriginal) => {
@@ -19,6 +21,11 @@ vi.mock('../src/helpers.js', async (importOriginal) => {
19
21
  };
20
22
  });
21
23
 
24
+ vi.mock('../src/deslop/resolveFiles.js', () => ({
25
+ resolveChangedFiles: vi.fn(),
26
+ resolveExplicitFiles: vi.fn(),
27
+ }));
28
+
22
29
  const mockGraph = {
23
30
  precomputeFileResolutions: vi.fn(),
24
31
  getBrokenInternalLinks: vi.fn().mockReturnValue([]),
@@ -42,6 +49,7 @@ describe('brokenLinks', () => {
42
49
  beforeEach(() => {
43
50
  vi.clearAllMocks();
44
51
  vi.mocked(buildGraph).mockResolvedValue(mockGraph as never);
52
+ vi.mocked(resolveExplicitFiles).mockResolvedValue([]);
45
53
  mockGraph.getBrokenInternalLinks.mockReturnValue([]);
46
54
  });
47
55
 
@@ -94,6 +102,42 @@ describe('brokenLinks', () => {
94
102
  expect(processExitMock).toHaveBeenCalledWith(1);
95
103
  });
96
104
 
105
+ it('checks only files matching the provided option', async () => {
106
+ vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
107
+ vi.mocked(resolveExplicitFiles).mockResolvedValueOnce(['guides/selected.mdx']);
108
+ mockGraph.getBrokenInternalLinks.mockReturnValue([
109
+ {
110
+ relativeDir: 'guides',
111
+ filename: 'selected.mdx',
112
+ originalPath: '/api/selected-invalid',
113
+ pathType: 'internal',
114
+ } as MdxPath,
115
+ {
116
+ relativeDir: 'guides',
117
+ filename: 'other.mdx',
118
+ originalPath: '/api/other-invalid',
119
+ pathType: 'internal',
120
+ } as MdxPath,
121
+ ]);
122
+
123
+ await runCommand('broken-links', '--files', 'guides/selected.mdx', 'guides/**/*.mdx');
124
+
125
+ expect(resolveExplicitFiles).toHaveBeenCalledWith(CMD_EXEC_PATH, [
126
+ 'guides/selected.mdx',
127
+ 'guides/**/*.mdx',
128
+ ]);
129
+ expect(addLogSpy).toHaveBeenCalledWith(
130
+ expect.objectContaining({
131
+ props: {
132
+ brokenLinksByFile: {
133
+ [path.join('guides', 'selected.mdx')]: ['/api/selected-invalid'],
134
+ },
135
+ },
136
+ })
137
+ );
138
+ expect(processExitMock).toHaveBeenCalledWith(1);
139
+ });
140
+
97
141
  it('fails when checking throws error', async () => {
98
142
  vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
99
143
  vi.mocked(buildGraph).mockRejectedValueOnce(new Error('some error'));
@@ -118,6 +162,7 @@ describe('brokenLinks --check-external', () => {
118
162
  beforeEach(() => {
119
163
  vi.clearAllMocks();
120
164
  vi.mocked(buildGraph).mockResolvedValue(mockGraph as never);
165
+ vi.mocked(resolveExplicitFiles).mockResolvedValue([]);
121
166
  mockGraph.getBrokenInternalLinks.mockReturnValue([]);
122
167
  });
123
168
 
@@ -160,6 +205,19 @@ describe('brokenLinks --check-external', () => {
160
205
  expect(processExitMock).toHaveBeenCalledWith(1);
161
206
  });
162
207
 
208
+ it('checks external links only from matching files', async () => {
209
+ vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
210
+ vi.mocked(resolveExplicitFiles).mockResolvedValueOnce(['selected.mdx']);
211
+ vi.mocked(getBrokenExternalLinks).mockResolvedValueOnce([]);
212
+
213
+ await runCommand('broken-links', '--files', 'selected.mdx', '--check-external');
214
+
215
+ expect(getBrokenExternalLinks).toHaveBeenCalledWith(mockGraph, {
216
+ sourceFiles: new Set(['selected.mdx']),
217
+ });
218
+ expect(processExitMock).toHaveBeenCalledWith(0);
219
+ });
220
+
163
221
  it('fails when external link checking throws error', async () => {
164
222
  vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
165
223
  vi.mocked(getBrokenExternalLinks).mockRejectedValueOnce(new Error('network error'));
package/bin/cli.js CHANGED
@@ -21,6 +21,7 @@ import { setTelemetryEnabled } from './config.js';
21
21
  import { getConfigValue, setConfigValue, clearConfigValue } from './config.js';
22
22
  import { API_URL } from './constants.js';
23
23
  import { deslopHandler } from './deslop/index.js';
24
+ import { resolveExplicitFiles } from './deslop/resolveFiles.js';
24
25
  import { CMD_EXEC_PATH, checkPort, checkNodeVersion, autoUpgradeIfNeeded, getVersions, isAI, suppressConsoleWarnings, terminate, } from './helpers.js';
25
26
  import { init } from './init.js';
26
27
  import { getAccessToken } from './keyring.js';
@@ -232,6 +233,11 @@ export const cli = ({ packageName = 'mint' }) => {
232
233
  yield terminate(valid ? 0 : 1);
233
234
  }))
234
235
  .command('broken-links', 'Check for broken links', (yargs) => yargs
236
+ .option('files', {
237
+ type: 'string',
238
+ array: true,
239
+ description: 'Files or globs to check (defaults to the whole site)',
240
+ })
235
241
  .option('check-anchors', {
236
242
  type: 'boolean',
237
243
  default: false,
@@ -251,17 +257,26 @@ export const cli = ({ packageName = 'mint' }) => {
251
257
  type: 'boolean',
252
258
  default: false,
253
259
  description: 'also check that docs.json redirect destinations resolve to valid paths',
254
- }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
260
+ })
261
+ .example('mint broken-links --files introduction.mdx', 'Check a specific page')
262
+ .example('mint broken-links --files "guides/**/*.mdx"', 'Check pages matching a glob'), (argv) => __awaiter(void 0, void 0, void 0, function* () {
263
+ var _a;
255
264
  yield autoUpgradeIfNeeded();
256
265
  addLog(_jsx(SpinnerLog, { message: "checking for broken links..." }));
257
266
  try {
267
+ const fileArgs = ((_a = argv.files) !== null && _a !== void 0 ? _a : []).map(String).filter(Boolean);
268
+ const sourceFiles = fileArgs.length > 0
269
+ ? new Set((yield resolveExplicitFiles(CMD_EXEC_PATH, fileArgs)).map((file) => path.normalize(file)))
270
+ : undefined;
258
271
  const graph = yield buildGraph(undefined, {
259
272
  checkSnippets: argv['check-snippets'],
260
273
  });
261
274
  graph.precomputeFileResolutions();
262
- const brokenInternalLinks = graph.getBrokenInternalLinks({
275
+ const brokenInternalLinks = graph
276
+ .getBrokenInternalLinks({
263
277
  checkAnchors: argv['check-anchors'],
264
- });
278
+ })
279
+ .filter(({ relativeDir, filename }) => !sourceFiles || sourceFiles.has(path.join(relativeDir, filename)));
265
280
  const brokenLinksByFile = {};
266
281
  brokenInternalLinks.forEach((mdxPath) => {
267
282
  const filename = path.join(mdxPath.relativeDir, mdxPath.filename);
@@ -274,7 +289,7 @@ export const cli = ({ packageName = 'mint' }) => {
274
289
  }
275
290
  });
276
291
  if (argv['check-external']) {
277
- const brokenExternalLinks = yield getBrokenExternalLinks(graph);
292
+ const brokenExternalLinks = yield getBrokenExternalLinks(graph, sourceFiles ? { sourceFiles } : undefined);
278
293
  for (const result of brokenExternalLinks) {
279
294
  for (const source of result.sources) {
280
295
  const label = result.status