@mintlify/previewing 4.0.1216 → 4.0.1218

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.
@@ -11,6 +11,7 @@ import pathUtil from 'path';
11
11
  import { CMD_EXEC_PATH, NEXT_PROPS_PATH, NEXT_PUBLIC_PATH, CLIENT_PATH } from '../../constants.js';
12
12
  import { addChangeLog, addErrorLog, clearErrorLogs, getCurrentErrorLogs, } from '../../logging-state.js';
13
13
  import { AddedLog, DeletedLog, EditedLog, WarningLog, InfoLog, ErrorLog } from '../../logs.js';
14
+ import { reportPageCompileError, reportPageCompileSuccess } from '../on-demand-page.js';
14
15
  import { clearDocsConfigCaches, getDocsConfigApiHash, getDocsConfigNavigationHash, getVariablesHash, hasDocsConfigApiChanged, hasDocsConfigNavigationChanged, hasNavigationApiReference, hasDocsConfigVariablesChanged, updateDocsConfigHashCache, } from './docs-config-cache.js';
15
16
  import { generateDependentSnippets } from './generateDependentSnippets.js';
16
17
  import { generatePagesWithImports } from './generatePagesWithImports.js';
@@ -154,6 +155,9 @@ const onUnlinkEvent = async (filename, triggerRefresh, options) => {
154
155
  const potentialCategory = getFileCategory(filename, { importedFiles });
155
156
  frontmatterHashes.delete(filename);
156
157
  removePageMetadataCacheEntryForFile(filename);
158
+ if (potentialCategory === 'page') {
159
+ reportPageCompileSuccess(filename);
160
+ }
157
161
  if (hasTrackedReferencedFile(filename)) {
158
162
  try {
159
163
  const { mintConfig, openApiFiles, docsConfig } = await getDocsState(handleParseError);
@@ -359,16 +363,36 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
359
363
  case 'page': {
360
364
  let contentStr = (await readFile(filePath)).toString();
361
365
  regenerateNavFromPageMetadataCache = await shouldRegenerateNavForPage(filename, contentStr, frontmatterHashes, handleParseError);
362
- const tree = await preparseMdxTree(contentStr, CMD_EXEC_PATH, filePath, suppressParseError);
363
- const importsResponse = await findAndRemoveImports(tree);
364
- if (hasImports(importsResponse)) {
365
- contentStr = stringifyTree(await resolvePageImports({ ...importsResponse, filename }));
366
+ let pageFailed = false;
367
+ const capturePageError = () => {
368
+ pageFailed = true;
369
+ };
370
+ const capturePageErrorAndLog = (message) => {
371
+ pageFailed = true;
372
+ handleParseError(message);
373
+ };
374
+ try {
375
+ const tree = await preparseMdxTree(contentStr, CMD_EXEC_PATH, filePath, capturePageError);
376
+ const importsResponse = await findAndRemoveImports(tree);
377
+ if (hasImports(importsResponse)) {
378
+ contentStr = stringifyTree(await resolvePageImports({ ...importsResponse, filename }));
379
+ }
380
+ const { pageContent, pageMetadata, slug } = await createPage(filename, contentStr, CMD_EXEC_PATH, [], [], capturePageErrorAndLog);
381
+ await fse.outputFile(targetPath, pageContent, {
382
+ flag: 'w',
383
+ });
384
+ upsertPageMetadataCacheEntry(slug, pageMetadata);
385
+ }
386
+ catch (error) {
387
+ reportPageCompileError(filename);
388
+ throw error;
389
+ }
390
+ if (pageFailed) {
391
+ reportPageCompileError(filename);
392
+ }
393
+ else {
394
+ reportPageCompileSuccess(filename);
366
395
  }
367
- const { pageContent, pageMetadata, slug } = await createPage(filename, contentStr, CMD_EXEC_PATH, [], [], handleParseError);
368
- await fse.outputFile(targetPath, pageContent, {
369
- flag: 'w',
370
- });
371
- upsertPageMetadataCacheEntry(slug, pageMetadata);
372
396
  break;
373
397
  }
374
398
  case 'snippet': {
@@ -2,9 +2,13 @@ export declare const markOnDemandPagesReady: () => void;
2
2
  export type CompileEvent = {
3
3
  path: string;
4
4
  active: boolean;
5
+ error?: boolean;
5
6
  };
6
7
  type CompileListener = (event: CompileEvent) => void;
7
8
  export declare const setCompileListener: (listener: CompileListener) => void;
9
+ export declare const getFailedCompileEvents: () => CompileEvent[];
10
+ export declare const reportPageCompileSuccess: (filename: string) => void;
11
+ export declare const reportPageCompileError: (filename: string) => void;
8
12
  export declare const ensurePageCompiled: (pathname: string, options?: {
9
13
  silent?: boolean;
10
14
  }) => Promise<void>;
@@ -8,7 +8,7 @@ import { startDevTiming } from './dev-timing.js';
8
8
  import { getImportedFilesFromCache } from './listener/importCache.js';
9
9
  import { upsertPageMetadataCacheEntry } from './listener/page-metadata-cache.js';
10
10
  import { resolvePageImports } from './listener/resolve-page-imports.js';
11
- import { getMintIgnoreGlobs, handleParseError, suppressParseError } from './listener/utils.js';
11
+ import { getMintIgnoreGlobs, handleParseError } from './listener/utils.js';
12
12
  const { readFile } = _promises;
13
13
  let resolveReady = () => { };
14
14
  const ready = new Promise((resolve) => {
@@ -27,11 +27,12 @@ const pagePathForFilename = (filename) => {
27
27
  const trackCompile = async (filename, task) => {
28
28
  const path = pagePathForFilename(filename);
29
29
  compileListener?.({ path, active: true });
30
+ let failed = false;
30
31
  try {
31
- return await task();
32
+ failed = await task();
32
33
  }
33
34
  finally {
34
- compileListener?.({ path, active: false });
35
+ compileListener?.({ path, active: false, error: failed || undefined });
35
36
  }
36
37
  };
37
38
  const inflight = new Map();
@@ -69,25 +70,50 @@ const candidateFilenames = (slug) => [
69
70
  `${slug}/index.md`,
70
71
  ];
71
72
  const failedCompiles = new Map();
73
+ export const getFailedCompileEvents = () => Array.from(failedCompiles.keys()).map((filename) => ({
74
+ path: pagePathForFilename(filename),
75
+ active: false,
76
+ error: true,
77
+ }));
78
+ export const reportPageCompileSuccess = (filename) => {
79
+ if (!failedCompiles.delete(filename))
80
+ return;
81
+ compileListener?.({ path: pagePathForFilename(filename), active: false });
82
+ };
83
+ export const reportPageCompileError = (filename) => {
84
+ failedCompiles.set(filename, 0);
85
+ compileListener?.({ path: pagePathForFilename(filename), active: false, error: true });
86
+ };
72
87
  const compilePage = async (filename, sourcePath, targetPath, sourceMtimeMs) => {
73
88
  const finish = startDevTiming(`compile ${filename}`);
89
+ let hasParseError = false;
90
+ const captureParseError = () => {
91
+ hasParseError = true;
92
+ };
74
93
  try {
75
94
  let contentStr = (await readFile(sourcePath)).toString();
76
- const tree = await preparseMdxTree(contentStr, CMD_EXEC_PATH, sourcePath, suppressParseError);
95
+ const tree = await preparseMdxTree(contentStr, CMD_EXEC_PATH, sourcePath, captureParseError);
77
96
  const importsResponse = await findAndRemoveImports(tree);
78
97
  if (hasImports(importsResponse)) {
79
98
  contentStr = stringifyTree(await resolvePageImports({ ...importsResponse, filename }));
80
99
  }
81
- const { pageContent, pageMetadata, slug } = await createPage(filename, contentStr, CMD_EXEC_PATH, [], [], suppressParseError);
100
+ const { pageContent, pageMetadata, slug } = await createPage(filename, contentStr, CMD_EXEC_PATH, [], [], captureParseError);
82
101
  await fse.outputFile(targetPath, pageContent, { flag: 'w' });
83
102
  upsertPageMetadataCacheEntry(slug, pageMetadata);
103
+ if (hasParseError) {
104
+ failedCompiles.set(filename, sourceMtimeMs);
105
+ finish('failed');
106
+ return true;
107
+ }
84
108
  failedCompiles.delete(filename);
85
109
  finish();
110
+ return false;
86
111
  }
87
112
  catch (error) {
88
113
  failedCompiles.set(filename, sourceMtimeMs);
89
114
  handleParseError(`Failed to compile ${filename}: ${error instanceof Error ? error.message : String(error)}`);
90
115
  finish('failed');
116
+ return true;
91
117
  }
92
118
  };
93
119
  export const ensurePageCompiled = async (pathname, options = {}) => {
@@ -106,6 +132,16 @@ export const ensurePageCompiled = async (pathname, options = {}) => {
106
132
  const sourceStat = await fse.stat(sourcePath);
107
133
  const targetPath = pathUtil.join(NEXT_PROPS_PATH, filename);
108
134
  const targetStat = await fse.stat(targetPath).catch(() => undefined);
135
+ if (failedCompiles.get(filename) === sourceStat.mtimeMs) {
136
+ if (!options.silent) {
137
+ compileListener?.({
138
+ path: pagePathForFilename(filename),
139
+ active: false,
140
+ error: true,
141
+ });
142
+ }
143
+ return;
144
+ }
109
145
  if (targetStat !== undefined && targetStat.mtimeMs >= sourceStat.mtimeMs) {
110
146
  return;
111
147
  }
@@ -113,14 +149,15 @@ export const ensurePageCompiled = async (pathname, options = {}) => {
113
149
  if (getFileCategory(filename, { importedFiles: getImportedFilesFromCache() }) !== 'page') {
114
150
  continue;
115
151
  }
116
- if (failedCompiles.get(filename) === sourceStat.mtimeMs) {
117
- continue;
118
- }
119
152
  const pending = inflight.get(filename) ??
120
153
  compilePage(filename, sourcePath, targetPath, sourceStat.mtimeMs).finally(() => {
121
154
  inflight.delete(filename);
122
155
  });
123
156
  inflight.set(filename, pending);
124
- return options.silent ? pending : trackCompile(filename, () => pending);
157
+ if (options.silent) {
158
+ await pending;
159
+ return;
160
+ }
161
+ return trackCompile(filename, () => pending);
125
162
  }
126
163
  };
@@ -11,7 +11,7 @@ import { addDevTimingLog, startDevTiming, timeDev } from './dev-timing.js';
11
11
  import listener, { initializeDocsConfigHashCache, initializeFrontmatterHashCache, initializeImportCache, } from './listener/index.js';
12
12
  import { refreshTrackedReferencedFiles } from './listener/referencedFiles.js';
13
13
  import { getLocalNetworkIp } from './network.js';
14
- import { ensurePageCompiled, markOnDemandPagesReady, setCompileListener } from './on-demand-page.js';
14
+ import { ensurePageCompiled, getFailedCompileEvents, markOnDemandPagesReady, setCompileListener, } from './on-demand-page.js';
15
15
  import { setupNext } from './setupNext.js';
16
16
  const PROXY_RESPONSE_HEADER_DENYLIST = new Set([
17
17
  'connection',
@@ -135,6 +135,11 @@ export const run = async (argv) => {
135
135
  setCompileListener((event) => {
136
136
  io.emit('compiling', event);
137
137
  });
138
+ io.on('connection', (socket) => {
139
+ for (const event of getFailedCompileEvents()) {
140
+ socket.emit('compiling', event);
141
+ }
142
+ });
138
143
  const finishServerListenTiming = startDevTiming('server ready');
139
144
  server.listen(currentPort, () => {
140
145
  finishServerListenTiming();