@mintlify/previewing 4.0.1461 → 4.0.1463

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 (55) hide show
  1. package/dist/__tests__/listener-graphql-packages.test.d.ts +1 -0
  2. package/dist/__tests__/listener-graphql-packages.test.js +68 -0
  3. package/dist/__tests__/listener-package-manifest.test.d.ts +1 -0
  4. package/dist/__tests__/listener-package-manifest.test.js +103 -0
  5. package/dist/__tests__/packages-builder.test.d.ts +1 -0
  6. package/dist/__tests__/packages-builder.test.js +88 -0
  7. package/dist/__tests__/packages-collect.test.d.ts +1 -0
  8. package/dist/__tests__/packages-collect.test.js +387 -0
  9. package/dist/__tests__/packages-dev-snippets.test.d.ts +1 -0
  10. package/dist/__tests__/packages-dev-snippets.test.js +457 -0
  11. package/dist/__tests__/packages-dev.test.d.ts +1 -0
  12. package/dist/__tests__/packages-dev.test.js +1304 -0
  13. package/dist/__tests__/packages-flag.test.d.ts +1 -0
  14. package/dist/__tests__/packages-flag.test.js +146 -0
  15. package/dist/__tests__/packages-imports.test.d.ts +1 -0
  16. package/dist/__tests__/packages-imports.test.js +68 -0
  17. package/dist/__tests__/packages-island-errors.test.d.ts +1 -0
  18. package/dist/__tests__/packages-island-errors.test.js +128 -0
  19. package/dist/__tests__/packages-serve.test.d.ts +1 -0
  20. package/dist/__tests__/packages-serve.test.js +73 -0
  21. package/dist/__tests__/packages-setup.test.d.ts +1 -0
  22. package/dist/__tests__/packages-setup.test.js +160 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/local-preview/index.js +6 -0
  26. package/dist/local-preview/listener/index.d.ts +1 -0
  27. package/dist/local-preview/listener/index.js +10 -1
  28. package/dist/local-preview/listener/regenerateAllSnippets.js +2 -0
  29. package/dist/local-preview/listener/resolve-imports-from-import-closure.js +3 -0
  30. package/dist/local-preview/listener/resolveAllImports.js +2 -0
  31. package/dist/local-preview/packages/dev.d.ts +88 -0
  32. package/dist/local-preview/packages/dev.js +478 -0
  33. package/dist/local-preview/packages/index.d.ts +8 -0
  34. package/dist/local-preview/packages/index.js +89 -0
  35. package/dist/local-preview/packages/serve.d.ts +8 -0
  36. package/dist/local-preview/packages/serve.js +41 -0
  37. package/dist/local-preview/packages/state.d.ts +6 -0
  38. package/dist/local-preview/packages/state.js +6 -0
  39. package/dist/local-preview/run.js +8 -0
  40. package/dist/logging-state.d.ts +1 -0
  41. package/dist/logging-state.js +5 -0
  42. package/dist/packages/builder.d.ts +6 -0
  43. package/dist/packages/builder.js +17 -0
  44. package/dist/packages/collect.d.ts +61 -0
  45. package/dist/packages/collect.js +240 -0
  46. package/dist/packages/errno.d.ts +1 -0
  47. package/dist/packages/errno.js +3 -0
  48. package/dist/packages/flag.d.ts +1 -0
  49. package/dist/packages/flag.js +1 -0
  50. package/dist/packages/index.d.ts +3 -0
  51. package/dist/packages/index.js +3 -0
  52. package/dist/packages/setup.d.ts +18 -0
  53. package/dist/packages/setup.js +81 -0
  54. package/dist/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +6 -3
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { findAndRemoveImports, hasImports, getFileCategory, normalizeOverlayKey, toPosixPath, validate, stringifyTree, isMintIgnored, replaceVariables, } from '@mintlify/common';
2
+ import { findAndRemoveImports, hasImports, getFileCategory, isPackageManifestPath, normalizeOverlayKey, toPosixPath, validate, stringifyTree, isMintIgnored, replaceVariables, } from '@mintlify/common';
3
3
  import { createPage, discoverOverlayFiles, getFileListSync, getMintIgnore, MintConfigUpdater, DocsConfigUpdater, preparseMdxTree, prebuild, } from '@mintlify/prebuild';
4
4
  import { isOverlayDocument } from '@mintlify/validation';
5
5
  import Chalk from 'chalk';
@@ -12,7 +12,9 @@ import pathUtil from 'path';
12
12
  import { CMD_EXEC_PATH, NEXT_PROPS_PATH, NEXT_PUBLIC_PATH, CLIENT_PATH } from '../../constants.js';
13
13
  import { addChangeLog, addErrorLog, clearErrorLogs, getCurrentErrorLogs, } from '../../logging-state.js';
14
14
  import { AddedLog, DeletedLog, EditedLog, WarningLog, InfoLog, ErrorLog } from '../../logs.js';
15
+ import { isNativePackagesEnabled } from '../../packages/flag.js';
15
16
  import { reportPageCompileError, reportPageCompileSuccess } from '../on-demand-page.js';
17
+ import { notifyPackageInputChange, writePackageBuildProps } from '../packages/index.js';
16
18
  import { clearDocsConfigCaches, getDocsConfigApiHash, getDocsConfigNavigationHash, getVariablesHash, hasDocsConfigApiChanged, hasDocsConfigNavigationChanged, hasNavigationApiReference, hadDocsConfigApiReference, hasDocsConfigVariablesChanged, updateDocsConfigHashCache, } from './docs-config-cache.js';
17
19
  import { generateDependentSnippets } from './generateDependentSnippets.js';
18
20
  import { generatePagesWithImports } from './generatePagesWithImports.js';
@@ -185,6 +187,7 @@ const onUnlinkEvent = async (filename, triggerRefresh, options) => {
185
187
  try {
186
188
  const importedFiles = getImportedFilesFromCache();
187
189
  const potentialCategory = getFileCategory(filename, { importedFiles });
190
+ await notifyPackageInputChange(filename, potentialCategory);
188
191
  frontmatterHashes.delete(filename);
189
192
  removePageMetadataCacheEntryForFile(filename);
190
193
  if (potentialCategory === 'page') {
@@ -274,6 +277,7 @@ const onUnlinkEvent = async (filename, triggerRefresh, options) => {
274
277
  await fse.emptyDir(NEXT_PUBLIC_PATH);
275
278
  await fse.emptyDir(NEXT_PROPS_PATH);
276
279
  const prebuildResult = await prebuild(CMD_EXEC_PATH, options);
280
+ await writePackageBuildProps();
277
281
  await initializeImportCache(CMD_EXEC_PATH, prebuildResult?.fileImportsMap);
278
282
  await initializeFrontmatterHashCache();
279
283
  await refreshTrackedReferencedFiles();
@@ -384,6 +388,7 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
384
388
  clearErrorLogs();
385
389
  const filePath = pathUtil.join(CMD_EXEC_PATH, filename);
386
390
  if (shouldRegenerateGraphqlSource(filename)) {
391
+ await notifyPackageInputChange(filename, getFileCategory(filename, { importedFiles: getImportedFilesFromCache() }));
387
392
  await rebuildGraphqlSchema(triggerRefresh, options);
388
393
  return null;
389
394
  }
@@ -391,6 +396,9 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
391
396
  await syncImportedFileLocations(importChanges);
392
397
  const importedFiles = getImportedFilesFromCache();
393
398
  const potentialCategory = getFileCategory(filename, { importedFiles });
399
+ await notifyPackageInputChange(filename, potentialCategory);
400
+ if (isNativePackagesEnabled() && isPackageManifestPath(filename))
401
+ return null;
394
402
  if (hasTrackedReferencedFile(filename)) {
395
403
  if (await fse.pathExists(pathUtil.join(CMD_EXEC_PATH, 'docs.json'))) {
396
404
  return onUpdateEvent('docs.json', triggerRefresh, options);
@@ -586,6 +594,7 @@ const onUpdateEvent = async (filename, triggerRefresh, options = {}) => {
586
594
  await fse.emptyDir(NEXT_PUBLIC_PATH);
587
595
  await fse.emptyDir(NEXT_PROPS_PATH);
588
596
  const prebuildResult = await prebuild(CMD_EXEC_PATH, options);
597
+ await writePackageBuildProps();
589
598
  await initializeImportCache(CMD_EXEC_PATH, prebuildResult?.fileImportsMap);
590
599
  await initializeFrontmatterHashCache();
591
600
  await refreshTrackedReferencedFiles();
@@ -4,6 +4,7 @@ import { outputFile } from 'fs-extra';
4
4
  import { readFile } from 'fs/promises';
5
5
  import { join } from 'path';
6
6
  import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../../constants.js';
7
+ import { packageImportArgs } from '../packages/state.js';
7
8
  import { getImportedFilesFromCache } from './importCache.js';
8
9
  import { getCurrentVariables, getMintIgnoreGlobs, handleParseError } from './utils.js';
9
10
  const getV2SnippetFilenames = () => {
@@ -65,6 +66,7 @@ export const regenerateAllSnippets = async () => {
65
66
  const resolvedTree = await baseResolveAllImports({
66
67
  snippets: resolvedTrees,
67
68
  fileWithImports: importData,
69
+ ...packageImportArgs(),
68
70
  });
69
71
  await outputFile(join(NEXT_PUBLIC_PATH, targetFilename), stringifyTree(resolvedTree), {
70
72
  flag: 'w',
@@ -3,6 +3,7 @@ import { preparseMdxTree } from '@mintlify/prebuild';
3
3
  import fse from 'fs-extra';
4
4
  import { join } from 'path';
5
5
  import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../../constants.js';
6
+ import { packageImportArgs } from '../packages/state.js';
6
7
  import { getCurrentVariables, handleParseError } from './utils.js';
7
8
  const getImportSourcePath = async (filename) => {
8
9
  const generatedPath = join(NEXT_PUBLIC_PATH, filename);
@@ -70,6 +71,7 @@ const resolveSnippetImports = async (snippetsByFilename) => {
70
71
  ? await baseResolveAllImports({
71
72
  snippets: resolvedSnippets,
72
73
  fileWithImports: snippet,
74
+ ...packageImportArgs(),
73
75
  })
74
76
  : snippet.tree;
75
77
  resolvedSnippets.push({ filename: snippet.filename, tree });
@@ -94,5 +96,6 @@ export const resolveImportsFromImportClosure = async (fileWithImports) => {
94
96
  return await baseResolveAllImports({
95
97
  snippets,
96
98
  fileWithImports,
99
+ ...packageImportArgs(),
97
100
  });
98
101
  };
@@ -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
+ };