@mintlify/previewing 4.0.1462 → 4.0.1464

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.
Files changed (49) hide show
  1. package/dist/__tests__/listener-graphql-packages.test.js +68 -0
  2. package/dist/__tests__/listener-package-manifest.test.d.ts +1 -0
  3. package/dist/__tests__/listener-package-manifest.test.js +103 -0
  4. package/dist/__tests__/packages-builder.test.d.ts +1 -0
  5. package/dist/__tests__/packages-builder.test.js +88 -0
  6. package/dist/__tests__/packages-collect.test.d.ts +1 -0
  7. package/dist/__tests__/packages-collect.test.js +387 -0
  8. package/dist/__tests__/packages-dev-snippets.test.d.ts +1 -0
  9. package/dist/__tests__/packages-dev-snippets.test.js +478 -0
  10. package/dist/__tests__/packages-dev.test.d.ts +1 -0
  11. package/dist/__tests__/packages-dev.test.js +1304 -0
  12. package/dist/__tests__/packages-flag.test.d.ts +1 -0
  13. package/dist/__tests__/packages-flag.test.js +146 -0
  14. package/dist/__tests__/packages-imports.test.d.ts +1 -0
  15. package/dist/__tests__/packages-imports.test.js +68 -0
  16. package/dist/__tests__/packages-island-errors.test.d.ts +1 -0
  17. package/dist/__tests__/packages-island-errors.test.js +128 -0
  18. package/dist/__tests__/packages-serve.test.d.ts +1 -0
  19. package/dist/__tests__/packages-serve.test.js +73 -0
  20. package/dist/__tests__/packages-setup.test.js +65 -7
  21. package/dist/local-preview/index.js +6 -0
  22. package/dist/local-preview/listener/index.d.ts +1 -0
  23. package/dist/local-preview/listener/index.js +10 -1
  24. package/dist/local-preview/listener/regenerateAllSnippets.js +2 -0
  25. package/dist/local-preview/listener/resolve-imports-from-import-closure.js +3 -0
  26. package/dist/local-preview/listener/resolveAllImports.js +2 -0
  27. package/dist/local-preview/packages/dev.d.ts +88 -0
  28. package/dist/local-preview/packages/dev.js +478 -0
  29. package/dist/local-preview/packages/index.d.ts +8 -0
  30. package/dist/local-preview/packages/index.js +89 -0
  31. package/dist/local-preview/packages/serve.d.ts +8 -0
  32. package/dist/local-preview/packages/serve.js +41 -0
  33. package/dist/local-preview/packages/state.d.ts +6 -0
  34. package/dist/local-preview/packages/state.js +6 -0
  35. package/dist/local-preview/run.js +8 -0
  36. package/dist/logging-state.d.ts +1 -0
  37. package/dist/logging-state.js +5 -0
  38. package/dist/packages/builder.d.ts +2 -0
  39. package/dist/packages/builder.js +1 -0
  40. package/dist/packages/index.d.ts +0 -1
  41. package/dist/packages/index.js +0 -1
  42. package/dist/packages/setup.d.ts +2 -0
  43. package/dist/packages/setup.js +5 -1
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +4 -4
  46. package/dist/__tests__/packages-check.test.js +0 -254
  47. package/dist/packages/check.d.ts +0 -25
  48. package/dist/packages/check.js +0 -132
  49. /package/dist/__tests__/{packages-check.test.d.ts → listener-graphql-packages.test.d.ts} +0 -0
@@ -1,9 +1,11 @@
1
1
  import { resolveAllImports as baseResolveAllImports } from '@mintlify/common';
2
+ import { packageImportArgs } from '../packages/state.js';
2
3
  import { getProcessedSnippets } from './getSnippets.js';
3
4
  export const resolveAllImports = async (fileWithImports) => {
4
5
  const snippets = await getProcessedSnippets();
5
6
  return await baseResolveAllImports({
6
7
  snippets,
7
8
  fileWithImports,
9
+ ...packageImportArgs(),
8
10
  });
9
11
  };
@@ -0,0 +1,88 @@
1
+ import type { BuildResult } from '@mintlify/package-builder';
2
+ import { type PackageRouteState } from './serve.js';
3
+ export declare const PACKAGE_BUILD_PROPS_FILE = "package-build.json";
4
+ export type PackagesDevStatus = {
5
+ state: 'disabled';
6
+ } | {
7
+ state: 'building';
8
+ } | {
9
+ state: 'ready';
10
+ } | {
11
+ state: 'missing-node-modules';
12
+ } | {
13
+ state: 'failed';
14
+ message: string;
15
+ };
16
+ export type PackagesDevReporter = {
17
+ building(): void;
18
+ built(result: {
19
+ buildId: string;
20
+ islands: number;
21
+ ms: number;
22
+ warnings: string[];
23
+ }): void;
24
+ failed(errors: string[], warnings: string[]): void;
25
+ missingNodeModules(docsRoot: string): void;
26
+ islandErrors(errors: string[]): void;
27
+ configError(message: string): void;
28
+ };
29
+ export type PackagesDevOptions = {
30
+ docsRoot: string;
31
+ buildsDir: string;
32
+ propsPath: string;
33
+ onRebuilt: () => void;
34
+ regenerateSnippets?: () => Promise<unknown>;
35
+ reporter: PackagesDevReporter;
36
+ debounceMs?: number;
37
+ pollMs?: number;
38
+ build?: (input: Parameters<typeof import('@mintlify/package-builder').build>[0]) => Promise<BuildResult>;
39
+ };
40
+ export declare class PackagesDev {
41
+ private readonly options;
42
+ private setup;
43
+ private collected;
44
+ private collector;
45
+ private adopted;
46
+ private status;
47
+ private builtInputKey;
48
+ private buildingInputKey;
49
+ private builder;
50
+ private buildFiles;
51
+ private buildFilesKnown;
52
+ private changedWhileBuilding;
53
+ private debounceTimer;
54
+ private pollTimer;
55
+ private running;
56
+ private rerun;
57
+ private collectPending;
58
+ private buildCount;
59
+ private closed;
60
+ private propsWrite;
61
+ private listedIds;
62
+ constructor(options: PackagesDevOptions);
63
+ get enabled(): boolean;
64
+ getStatus(): PackagesDevStatus;
65
+ routeState: () => PackageRouteState;
66
+ init(): Promise<void>;
67
+ onFileChange(filename: string, category: string | null | undefined): Promise<void>;
68
+ private buildReads;
69
+ private finishTracking;
70
+ writeProps(): Promise<void>;
71
+ private writePropsNow;
72
+ close(): Promise<void>;
73
+ private reload;
74
+ private reportConfigError;
75
+ private importOptionsChanged;
76
+ private recollect;
77
+ private reportIslandProblems;
78
+ private inputKey;
79
+ private schedule;
80
+ private flush;
81
+ private runBuild;
82
+ private fail;
83
+ private pruneBuilds;
84
+ private nodeModulesStamp;
85
+ private watchForNodeModules;
86
+ private stopPolling;
87
+ private invalidateCompiledPages;
88
+ }
@@ -0,0 +1,478 @@
1
+ import { islandPrefixFor, isPackageManifestPath } from '@mintlify/common';
2
+ import { getFileListSync } from '@mintlify/prebuild';
3
+ import { existsSync, statSync } from 'fs';
4
+ import fse from 'fs-extra';
5
+ import path from 'path';
6
+ import { formatDiagnostic, loadPackageBuilder } from '../../packages/builder.js';
7
+ import { formatProblem, PackageIslandCollector, } from '../../packages/collect.js';
8
+ import { errnoCode } from '../../packages/errno.js';
9
+ import { DocsJsonError, InvalidPackagesError, readPackagesSetup, } from '../../packages/setup.js';
10
+ import { PACKAGE_ROUTE_PREFIX } from './serve.js';
11
+ import { setPackageImportOptions } from './state.js';
12
+ export const PACKAGE_BUILD_PROPS_FILE = 'package-build.json';
13
+ const PENDING_BUILD_ID = 'pending';
14
+ const NODE_MODULES_MARKERS = [
15
+ '.package-lock.json',
16
+ '.modules.yaml',
17
+ '.yarn-integrity',
18
+ '.yarn-state.yml',
19
+ ];
20
+ const MARKED_LOCKFILES = [
21
+ 'package-lock.json',
22
+ 'npm-shrinkwrap.json',
23
+ 'pnpm-lock.yaml',
24
+ 'yarn.lock',
25
+ ];
26
+ const PAGE_EXTENSIONS = new Set(['.mdx', '.md']);
27
+ const sameSet = (a, b) => a.size === b.size && [...a].every((value) => b.has(value));
28
+ const sameVariables = (a = {}, b = {}) => Object.keys(a).length === Object.keys(b).length &&
29
+ Object.entries(a).every(([key, value]) => b[key] === value);
30
+ const mtimeOf = (file) => {
31
+ try {
32
+ return statSync(file).mtimeMs;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ };
38
+ const RENAME_RETRY_CODES = new Set(['EPERM', 'EBUSY', 'EACCES']);
39
+ async function replaceFile(from, to, attempts = 20) {
40
+ for (let attempt = 1;; attempt++) {
41
+ try {
42
+ await fse.rename(from, to);
43
+ return;
44
+ }
45
+ catch (error) {
46
+ const code = errnoCode(error);
47
+ if (attempt >= attempts || !code || !RENAME_RETRY_CODES.has(code))
48
+ throw error;
49
+ await new Promise((resolve) => setTimeout(resolve, 10 * attempt));
50
+ }
51
+ }
52
+ }
53
+ export class PackagesDev {
54
+ constructor(options) {
55
+ this.options = options;
56
+ this.status = { state: 'disabled' };
57
+ this.buildFiles = new Set();
58
+ this.buildFilesKnown = false;
59
+ this.rerun = false;
60
+ this.collectPending = false;
61
+ this.buildCount = 0;
62
+ this.closed = false;
63
+ this.propsWrite = Promise.resolve();
64
+ this.listedIds = new Set();
65
+ this.routeState = () => ({
66
+ enabled: this.enabled,
67
+ buildsDir: this.options.buildsDir,
68
+ });
69
+ }
70
+ get enabled() {
71
+ return this.setup !== undefined;
72
+ }
73
+ getStatus() {
74
+ return this.status;
75
+ }
76
+ async init() {
77
+ await this.reload(true);
78
+ if (!this.enabled) {
79
+ await fse.remove(this.options.buildsDir);
80
+ return;
81
+ }
82
+ await fse.emptyDir(this.options.buildsDir);
83
+ await this.flush();
84
+ }
85
+ async onFileChange(filename, category) {
86
+ if (this.closed)
87
+ return;
88
+ const normalized = filename.split(path.sep).join('/');
89
+ const extension = path.extname(normalized).toLowerCase();
90
+ const manifest = isPackageManifestPath(normalized);
91
+ if (category === 'docsConfig' || manifest) {
92
+ if (await this.reload())
93
+ this.schedule(manifest);
94
+ else
95
+ this.reportIslandProblems();
96
+ return;
97
+ }
98
+ if (!this.enabled)
99
+ return;
100
+ if (category === 'mintIgnore') {
101
+ await this.writeProps();
102
+ this.collectPending = true;
103
+ this.schedule(true);
104
+ return;
105
+ }
106
+ if (category === 'page') {
107
+ await this.recollect((collector) => collector.updatePage(normalized));
108
+ this.schedule(false);
109
+ return;
110
+ }
111
+ if (category === 'snippet-v2' ||
112
+ (category === 'snippet' && PAGE_EXTENSIONS.has(extension)) ||
113
+ Object.prototype.hasOwnProperty.call(this.collected?.snippetFiles ?? {}, `/${normalized}`)) {
114
+ try {
115
+ await this.recollect();
116
+ }
117
+ finally {
118
+ this.schedule(true);
119
+ }
120
+ return;
121
+ }
122
+ this.reportIslandProblems();
123
+ const file = `/${normalized.toLowerCase()}`;
124
+ if (this.buildReads(file))
125
+ this.schedule(true);
126
+ else
127
+ this.changedWhileBuilding?.add(file);
128
+ }
129
+ buildReads(file) {
130
+ if (this.buildFiles.has(file))
131
+ return true;
132
+ if (this.buildFilesKnown || this.changedWhileBuilding)
133
+ return false;
134
+ return this.builder?.isLocalInputPath(file) ?? false;
135
+ }
136
+ finishTracking(changed, files) {
137
+ this.changedWhileBuilding = undefined;
138
+ if (files)
139
+ this.buildFiles = new Set(files.map((file) => file.toLowerCase()));
140
+ this.buildFilesKnown = files !== undefined;
141
+ if ([...changed].some((file) => this.buildReads(file)))
142
+ this.rerun = true;
143
+ }
144
+ writeProps() {
145
+ const write = this.propsWrite.then(() => this.writePropsNow());
146
+ this.propsWrite = write.catch(() => { });
147
+ return write;
148
+ }
149
+ async writePropsNow() {
150
+ const target = path.join(this.options.propsPath, PACKAGE_BUILD_PROPS_FILE);
151
+ if (!this.setup || !this.collected) {
152
+ await fse.remove(target);
153
+ return;
154
+ }
155
+ const buildId = this.adopted?.buildId ?? PENDING_BUILD_ID;
156
+ const baseUrl = `${PACKAGE_ROUTE_PREFIX}/${buildId}`;
157
+ const built = this.adopted?.manifest.islands ?? {};
158
+ const collectedIds = this.collected.islands.map((island) => island.id);
159
+ const islands = {};
160
+ for (const id of [...collectedIds, ...this.listedIds, ...Object.keys(built)]) {
161
+ if (Object.prototype.hasOwnProperty.call(islands, id))
162
+ continue;
163
+ const html = Object.prototype.hasOwnProperty.call(built, id) ? built[id]?.html : undefined;
164
+ const css = Object.prototype.hasOwnProperty.call(built, id) ? built[id]?.css : undefined;
165
+ islands[id] = {
166
+ prefix: islandPrefixFor(id),
167
+ ...(html !== undefined && { html }),
168
+ ...(css !== undefined && { css }),
169
+ };
170
+ }
171
+ for (const id of collectedIds)
172
+ this.listedIds.add(id);
173
+ const tmp = `${target}.${process.pid}.tmp`;
174
+ const props = {
175
+ baseUrl,
176
+ islands,
177
+ packageNames: [...this.setup.packageNames].sort(),
178
+ packageSnippets: [...this.collected.packageSnippets].sort(),
179
+ css: this.adopted?.manifest.css ?? [],
180
+ };
181
+ await fse.outputFile(tmp, JSON.stringify(props));
182
+ await replaceFile(tmp, target);
183
+ }
184
+ close() {
185
+ this.closed = true;
186
+ clearTimeout(this.debounceTimer);
187
+ this.stopPolling();
188
+ return this.running ?? Promise.resolve();
189
+ }
190
+ async reload(initial = false) {
191
+ const previous = this.setup;
192
+ const previousSnippets = this.collected?.packageSnippets;
193
+ let setup;
194
+ try {
195
+ setup = await readPackagesSetup(this.options.docsRoot);
196
+ }
197
+ catch (error) {
198
+ if (!(error instanceof DocsJsonError))
199
+ throw error;
200
+ if (previous) {
201
+ this.reportConfigError(error.problems, 'Keeping the last package setup until docs.json is fixed.');
202
+ if (!(error instanceof InvalidPackagesError))
203
+ return false;
204
+ if (sameVariables(previous.variables, error.variables))
205
+ return false;
206
+ setup = { ...previous, variables: error.variables };
207
+ }
208
+ else if (error instanceof InvalidPackagesError) {
209
+ this.reportConfigError(error.problems, 'Packages stay off until docs.json is fixed.');
210
+ }
211
+ }
212
+ if (previous && setup?.packageJsonError) {
213
+ this.reportConfigError([setup.packageJsonError], 'Keeping the last package setup until package.json is fixed.');
214
+ if (sameVariables(previous.variables, setup.variables))
215
+ return false;
216
+ setup = { ...previous, variables: setup.variables };
217
+ }
218
+ this.setup = setup;
219
+ if (!setup) {
220
+ this.collected = undefined;
221
+ this.collector = undefined;
222
+ this.status = { state: 'disabled' };
223
+ this.builtInputKey = undefined;
224
+ this.buildingInputKey = undefined;
225
+ this.stopPolling();
226
+ setPackageImportOptions(undefined);
227
+ await this.writeProps();
228
+ if (previous)
229
+ await this.importOptionsChanged();
230
+ return true;
231
+ }
232
+ const previousCollector = this.collector;
233
+ this.collector = new PackageIslandCollector({
234
+ docsRoot: this.options.docsRoot,
235
+ packageNames: setup.packageNames,
236
+ variables: setup.variables,
237
+ provider: setup.packages.provider,
238
+ });
239
+ if (previousCollector)
240
+ this.collector.inheritSnippetTrees(previousCollector);
241
+ await this.recollect();
242
+ const namesChanged = !previous || !sameSet(previous.packageNames, setup.packageNames);
243
+ const snippetsChanged = !previousSnippets || !sameSet(previousSnippets, this.collected?.packageSnippets ?? new Set());
244
+ if (!initial && (namesChanged || snippetsChanged))
245
+ await this.importOptionsChanged();
246
+ return true;
247
+ }
248
+ reportConfigError(problems, note) {
249
+ problems.forEach((problem, index) => this.options.reporter.configError(index === problems.length - 1 ? `${problem}. ${note}` : problem));
250
+ }
251
+ async importOptionsChanged() {
252
+ await this.options.regenerateSnippets?.();
253
+ await this.invalidateCompiledPages();
254
+ }
255
+ async recollect(collect = (collector) => collector.collectAll()) {
256
+ const { setup, collector } = this;
257
+ if (!setup || !collector)
258
+ return;
259
+ const collected = await collect(collector);
260
+ if (collector !== this.collector)
261
+ return;
262
+ const previousSnippets = this.collected?.packageSnippets;
263
+ this.collected = collected;
264
+ setPackageImportOptions({
265
+ packageNames: setup.packageNames,
266
+ packageSnippets: collected.packageSnippets,
267
+ });
268
+ if (previousSnippets && !sameSet(previousSnippets, collected.packageSnippets)) {
269
+ await this.invalidateCompiledPages();
270
+ }
271
+ this.reportIslandProblems();
272
+ await this.writeProps();
273
+ }
274
+ reportIslandProblems() {
275
+ this.options.reporter.islandErrors([
276
+ ...(this.setup?.errors ?? []),
277
+ ...(this.collected?.errors ?? []).map(formatProblem),
278
+ ]);
279
+ }
280
+ inputKey() {
281
+ return JSON.stringify({
282
+ packages: this.setup?.packages,
283
+ names: [...(this.setup?.packageNames ?? [])].sort(),
284
+ islands: (this.collected?.islands ?? []).map((island) => island.id).sort(),
285
+ snippets: this.collected?.snippetFiles,
286
+ });
287
+ }
288
+ schedule(force) {
289
+ if (!this.enabled || this.closed)
290
+ return;
291
+ if (!force && this.inputKey() === (this.buildingInputKey ?? this.builtInputKey))
292
+ return;
293
+ if (force)
294
+ this.builtInputKey = undefined;
295
+ if (this.status.state === 'ready')
296
+ this.status = { state: 'building' };
297
+ clearTimeout(this.debounceTimer);
298
+ this.debounceTimer = setTimeout(() => void this.flush(), this.options.debounceMs ?? 300);
299
+ }
300
+ async flush() {
301
+ if (this.running) {
302
+ this.rerun = true;
303
+ return this.running;
304
+ }
305
+ this.running = (async () => {
306
+ do {
307
+ this.rerun = false;
308
+ try {
309
+ await this.runBuild();
310
+ }
311
+ catch (error) {
312
+ const message = error instanceof Error ? error.message : String(error);
313
+ this.status = { state: 'failed', message };
314
+ this.options.reporter.failed([message], []);
315
+ }
316
+ finally {
317
+ this.buildingInputKey = undefined;
318
+ }
319
+ } while (this.rerun && !this.closed);
320
+ })().finally(() => {
321
+ this.running = undefined;
322
+ });
323
+ return this.running;
324
+ }
325
+ async runBuild() {
326
+ if (this.collectPending) {
327
+ this.collectPending = false;
328
+ await this.recollect();
329
+ }
330
+ const setup = this.setup;
331
+ const collected = this.collected;
332
+ if (!setup || !collected || this.closed)
333
+ return;
334
+ const inputKey = this.inputKey();
335
+ this.builtInputKey = undefined;
336
+ this.buildingInputKey = inputKey;
337
+ const started = Date.now();
338
+ const stamp = this.nodeModulesStamp();
339
+ this.status = { state: 'building' };
340
+ this.options.reporter.building();
341
+ const tmpDir = path.join(this.options.buildsDir, `.tmp-${process.pid}-${++this.buildCount}`);
342
+ this.builder ??= await loadPackageBuilder();
343
+ const build = this.options.build ?? this.builder.build;
344
+ const changed = new Set();
345
+ this.changedWhileBuilding = changed;
346
+ let result;
347
+ try {
348
+ result = await build({
349
+ docsRoot: this.options.docsRoot,
350
+ packages: setup.packages,
351
+ islands: collected.islands,
352
+ snippetFiles: collected.snippetFiles,
353
+ outDir: tmpDir,
354
+ installer: 'local',
355
+ });
356
+ }
357
+ catch (error) {
358
+ this.finishTracking(changed);
359
+ if (this.buildingInputKey !== inputKey) {
360
+ await fse.remove(tmpDir);
361
+ return;
362
+ }
363
+ return this.fail(error, tmpDir, stamp);
364
+ }
365
+ this.finishTracking(changed, [...result.localFiles, ...result.missingLocalFiles]);
366
+ if (this.buildingInputKey !== inputKey) {
367
+ await fse.remove(tmpDir);
368
+ return;
369
+ }
370
+ const diagnostics = result.manifest.diagnostics;
371
+ const errors = diagnostics.filter((d) => d.level === 'error');
372
+ const warnings = diagnostics.filter((d) => d.level === 'warning').map(formatDiagnostic);
373
+ const servable = Object.entries(result.manifest.islands).filter(([, island]) => existsSync(path.join(tmpDir, island.entry)));
374
+ const previous = this.adopted;
375
+ const servedId = `${result.manifest.buildId}-${this.buildCount}`;
376
+ const dir = path.join(this.options.buildsDir, servedId);
377
+ try {
378
+ if (servable.length > 0 || (result.ok && collected.islands.length === 0)) {
379
+ await fse.move(tmpDir, dir);
380
+ await this.pruneBuilds([servedId, previous?.buildId]);
381
+ this.adopted = { buildId: servedId, manifest: result.manifest, dir };
382
+ }
383
+ else {
384
+ await fse.remove(tmpDir);
385
+ }
386
+ this.listedIds.clear();
387
+ await this.writeProps();
388
+ }
389
+ catch (error) {
390
+ this.adopted = previous;
391
+ await fse.remove(dir);
392
+ return this.fail(error, tmpDir, stamp);
393
+ }
394
+ const dependencyDrift = diagnostics.some((d) => d.code === 'local-node-modules-drift');
395
+ if (!result.ok || dependencyDrift)
396
+ this.watchForNodeModules(stamp);
397
+ else
398
+ this.stopPolling();
399
+ if (errors.some((d) => d.code === 'install-missing-node-modules')) {
400
+ this.status = { state: 'missing-node-modules' };
401
+ this.options.reporter.missingNodeModules(this.options.docsRoot);
402
+ }
403
+ else if (!result.ok) {
404
+ const messages = errors.map(formatDiagnostic);
405
+ this.status = { state: 'failed', message: messages[0] ?? 'unknown error' };
406
+ this.options.reporter.failed(messages, warnings);
407
+ }
408
+ else {
409
+ this.status = { state: 'ready' };
410
+ this.options.reporter.built({
411
+ buildId: result.manifest.buildId,
412
+ islands: Object.keys(result.manifest.islands).length,
413
+ ms: Date.now() - started,
414
+ warnings,
415
+ });
416
+ }
417
+ if (result.ok && inputKey === this.inputKey())
418
+ this.builtInputKey = inputKey;
419
+ this.options.onRebuilt();
420
+ }
421
+ async fail(error, tmpDir, stamp) {
422
+ this.listedIds.clear();
423
+ this.watchForNodeModules(stamp);
424
+ await fse.remove(tmpDir);
425
+ const message = error instanceof Error ? error.message : String(error);
426
+ this.status = { state: 'failed', message };
427
+ this.options.reporter.failed([message], []);
428
+ await this.writeProps();
429
+ this.options.onRebuilt();
430
+ }
431
+ async pruneBuilds(keep) {
432
+ const entries = await fse.readdir(this.options.buildsDir).catch(() => []);
433
+ await Promise.all(entries
434
+ .filter((entry) => !keep.includes(entry) && !entry.startsWith('.tmp-'))
435
+ .map((entry) => fse.remove(path.join(this.options.buildsDir, entry))));
436
+ }
437
+ nodeModulesStamp() {
438
+ const { docsRoot } = this.options;
439
+ const dir = path.join(docsRoot, 'node_modules');
440
+ const markers = NODE_MODULES_MARKERS.map((marker) => mtimeOf(path.join(dir, marker)));
441
+ const stamp = MARKED_LOCKFILES.some((file) => existsSync(path.join(docsRoot, file)))
442
+ ? markers
443
+ : [mtimeOf(dir), ...markers];
444
+ return stamp.every((mtime) => mtime === undefined) ? undefined : stamp.join(',');
445
+ }
446
+ watchForNodeModules(initial) {
447
+ if (this.pollTimer || this.closed)
448
+ return;
449
+ let seen = initial;
450
+ let settling = false;
451
+ this.pollTimer = setInterval(() => {
452
+ const stamp = this.nodeModulesStamp();
453
+ if (stamp !== seen) {
454
+ seen = stamp;
455
+ settling = true;
456
+ }
457
+ else if (settling) {
458
+ settling = false;
459
+ this.schedule(true);
460
+ }
461
+ }, this.options.pollMs ?? 2000);
462
+ this.pollTimer.unref?.();
463
+ }
464
+ stopPolling() {
465
+ clearInterval(this.pollTimer);
466
+ this.pollTimer = undefined;
467
+ }
468
+ async invalidateCompiledPages() {
469
+ const { propsPath, docsRoot } = this.options;
470
+ for (const relative of getFileListSync(propsPath)) {
471
+ if (!PAGE_EXTENSIONS.has(path.extname(relative).toLowerCase()))
472
+ continue;
473
+ if (await fse.pathExists(path.join(docsRoot, relative))) {
474
+ await fse.utimes(path.join(propsPath, relative), 0, 0);
475
+ }
476
+ }
477
+ }
478
+ }
@@ -0,0 +1,8 @@
1
+ export declare const setPackagesRebuiltListener: (listener: () => void) => void;
2
+ export declare const startPackagesDev: () => Promise<void>;
3
+ export declare const notifyPackageInputChange: (filename: string, category: string | null | undefined) => Promise<void>;
4
+ export declare const writePackageBuildProps: () => Promise<void>;
5
+ export declare const packageRoute: {
6
+ prefix: string;
7
+ handler: (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction) => Promise<void>;
8
+ };
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import path from 'path';
3
+ import { CMD_EXEC_PATH, NEXT_PROPS_PATH, PREVIEW_PATH } from '../../constants.js';
4
+ import { addChangeLog, addErrorLog, getCurrentErrorLogs, removeErrorLogs, } from '../../logging-state.js';
5
+ import { ErrorLog, InfoLog, SuccessLog, WarningLog } from '../../logs.js';
6
+ import { regenerateAllSnippets } from '../listener/regenerateAllSnippets.js';
7
+ import { PackagesDev } from './dev.js';
8
+ import { createPackageRoute, PACKAGE_ROUTE_PREFIX } from './serve.js';
9
+ const MAX_LOGGED = 5;
10
+ let instance;
11
+ let shownIslandErrors = { key: '', logs: [] };
12
+ const reporter = {
13
+ building: () => {
14
+ addChangeLog(_jsx(InfoLog, { message: "building npm packages\u2026" }));
15
+ },
16
+ built: ({ buildId, islands, ms, warnings }) => {
17
+ addChangeLog(_jsx(SuccessLog, { message: `built npm packages (${islands} component${islands === 1 ? '' : 's'}, build ${buildId.slice(0, 12)}) in ${(ms / 1000).toFixed(1)}s` }));
18
+ for (const warning of warnings.slice(0, MAX_LOGGED)) {
19
+ addChangeLog(_jsx(WarningLog, { message: `npm packages: ${warning}` }));
20
+ }
21
+ },
22
+ failed: (errors, warnings) => {
23
+ for (const error of errors.slice(0, MAX_LOGGED)) {
24
+ addErrorLog(_jsx(ErrorLog, { message: `npm packages: ${error}` }));
25
+ }
26
+ for (const warning of warnings.slice(0, MAX_LOGGED)) {
27
+ addChangeLog(_jsx(WarningLog, { message: `npm packages: ${warning}` }));
28
+ }
29
+ },
30
+ missingNodeModules: (docsRoot) => {
31
+ addErrorLog(_jsx(ErrorLog, { message: `docs.json uses npm packages but ${path.join(docsRoot, 'node_modules')} is missing. Run npm install in ${docsRoot}; package components stay empty until then.` }));
32
+ },
33
+ configError: (message) => {
34
+ addErrorLog(_jsx(ErrorLog, { message: `npm packages: ${message}` }));
35
+ },
36
+ islandErrors: (errors) => {
37
+ const key = errors.join('\n');
38
+ const current = getCurrentErrorLogs();
39
+ const visible = shownIslandErrors.logs.every((log) => current.includes(log));
40
+ if (key === shownIslandErrors.key && visible)
41
+ return;
42
+ removeErrorLogs(shownIslandErrors.logs);
43
+ const logs = errors
44
+ .slice(0, MAX_LOGGED)
45
+ .map((error) => _jsx(ErrorLog, { message: `npm packages: ${error}` }));
46
+ logs.forEach(addErrorLog);
47
+ shownIslandErrors = { key, logs };
48
+ },
49
+ };
50
+ const onRebuiltRef = { current: () => { } };
51
+ export const setPackagesRebuiltListener = (listener) => {
52
+ onRebuiltRef.current = listener;
53
+ };
54
+ const reportError = (error) => addErrorLog(_jsx(ErrorLog, { message: `npm packages: ${error instanceof Error ? error.message : String(error)}` }));
55
+ export const startPackagesDev = async () => {
56
+ await instance?.close();
57
+ instance = new PackagesDev({
58
+ docsRoot: CMD_EXEC_PATH,
59
+ buildsDir: path.join(PREVIEW_PATH, 'packages'),
60
+ propsPath: NEXT_PROPS_PATH,
61
+ onRebuilt: () => onRebuiltRef.current(),
62
+ regenerateSnippets: regenerateAllSnippets,
63
+ reporter,
64
+ });
65
+ try {
66
+ await instance.init();
67
+ }
68
+ catch (error) {
69
+ reportError(error);
70
+ }
71
+ };
72
+ export const notifyPackageInputChange = async (filename, category) => {
73
+ try {
74
+ await instance?.onFileChange(filename, category);
75
+ }
76
+ catch (error) {
77
+ reportError(error);
78
+ }
79
+ };
80
+ export const writePackageBuildProps = async () => {
81
+ await instance?.writeProps();
82
+ };
83
+ export const packageRoute = {
84
+ prefix: PACKAGE_ROUTE_PREFIX,
85
+ handler: createPackageRoute(() => instance?.routeState() ?? {
86
+ enabled: false,
87
+ buildsDir: '',
88
+ }),
89
+ };
@@ -0,0 +1,8 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+ export declare const PACKAGE_ROUTE_PREFIX = "/_mintlify/pkg";
3
+ export type PackageRouteState = {
4
+ enabled: boolean;
5
+ buildsDir: string;
6
+ };
7
+ export declare const EMPTY_ISLAND_MODULE = "export function hydrate() {\n return { unmount() {} };\n}\n";
8
+ export declare const createPackageRoute: (getState: () => PackageRouteState) => (req: Request, res: Response, next: NextFunction) => Promise<void>;