@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.
- package/dist/__tests__/listener-graphql-packages.test.d.ts +1 -0
- package/dist/__tests__/listener-graphql-packages.test.js +68 -0
- package/dist/__tests__/listener-package-manifest.test.d.ts +1 -0
- package/dist/__tests__/listener-package-manifest.test.js +103 -0
- package/dist/__tests__/packages-builder.test.d.ts +1 -0
- package/dist/__tests__/packages-builder.test.js +88 -0
- package/dist/__tests__/packages-collect.test.d.ts +1 -0
- package/dist/__tests__/packages-collect.test.js +387 -0
- package/dist/__tests__/packages-dev-snippets.test.d.ts +1 -0
- package/dist/__tests__/packages-dev-snippets.test.js +457 -0
- package/dist/__tests__/packages-dev.test.d.ts +1 -0
- package/dist/__tests__/packages-dev.test.js +1304 -0
- package/dist/__tests__/packages-flag.test.d.ts +1 -0
- package/dist/__tests__/packages-flag.test.js +146 -0
- package/dist/__tests__/packages-imports.test.d.ts +1 -0
- package/dist/__tests__/packages-imports.test.js +68 -0
- package/dist/__tests__/packages-island-errors.test.d.ts +1 -0
- package/dist/__tests__/packages-island-errors.test.js +128 -0
- package/dist/__tests__/packages-serve.test.d.ts +1 -0
- package/dist/__tests__/packages-serve.test.js +73 -0
- package/dist/__tests__/packages-setup.test.d.ts +1 -0
- package/dist/__tests__/packages-setup.test.js +160 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/local-preview/index.js +6 -0
- package/dist/local-preview/listener/index.d.ts +1 -0
- package/dist/local-preview/listener/index.js +10 -1
- package/dist/local-preview/listener/regenerateAllSnippets.js +2 -0
- package/dist/local-preview/listener/resolve-imports-from-import-closure.js +3 -0
- package/dist/local-preview/listener/resolveAllImports.js +2 -0
- package/dist/local-preview/packages/dev.d.ts +88 -0
- package/dist/local-preview/packages/dev.js +478 -0
- package/dist/local-preview/packages/index.d.ts +8 -0
- package/dist/local-preview/packages/index.js +89 -0
- package/dist/local-preview/packages/serve.d.ts +8 -0
- package/dist/local-preview/packages/serve.js +41 -0
- package/dist/local-preview/packages/state.d.ts +6 -0
- package/dist/local-preview/packages/state.js +6 -0
- package/dist/local-preview/run.js +8 -0
- package/dist/logging-state.d.ts +1 -0
- package/dist/logging-state.js +5 -0
- package/dist/packages/builder.d.ts +6 -0
- package/dist/packages/builder.js +17 -0
- package/dist/packages/collect.d.ts +61 -0
- package/dist/packages/collect.js +240 -0
- package/dist/packages/errno.d.ts +1 -0
- package/dist/packages/errno.js +3 -0
- package/dist/packages/flag.d.ts +1 -0
- package/dist/packages/flag.js +1 -0
- package/dist/packages/index.d.ts +3 -0
- package/dist/packages/index.js +3 -0
- package/dist/packages/setup.d.ts +18 -0
- package/dist/packages/setup.js +81 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -3
|
@@ -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>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fse from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export const PACKAGE_ROUTE_PREFIX = '/_mintlify/pkg';
|
|
4
|
+
const BUILD_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
5
|
+
const ISLAND_ENTRY = /^client\/islands\/([A-Za-z0-9_-]{1,64})\.js$/;
|
|
6
|
+
export const EMPTY_ISLAND_MODULE = 'export function hydrate() {\n return { unmount() {} };\n}\n';
|
|
7
|
+
const resolveInside = (root, relative) => {
|
|
8
|
+
const resolved = path.resolve(root, relative);
|
|
9
|
+
return resolved.startsWith(root + path.sep) ? resolved : undefined;
|
|
10
|
+
};
|
|
11
|
+
export const createPackageRoute = (getState) => async (req, res, next) => {
|
|
12
|
+
const state = getState();
|
|
13
|
+
if (!state.enabled || (req.method !== 'GET' && req.method !== 'HEAD')) {
|
|
14
|
+
next();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const [buildId, ...rest] = req.path.replace(/^\/+/, '').split('/');
|
|
18
|
+
const relative = rest.join('/');
|
|
19
|
+
const clientDir = buildId && BUILD_ID.test(buildId) ? path.join(state.buildsDir, buildId, 'client') : '';
|
|
20
|
+
const inClient = relative.startsWith('client/') ? relative.slice('client/'.length) : '';
|
|
21
|
+
const file = clientDir && inClient ? resolveInside(clientDir, inClient) : undefined;
|
|
22
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
23
|
+
if (file &&
|
|
24
|
+
(await fse.stat(file).then((stat) => stat.isFile(), () => false))) {
|
|
25
|
+
res.sendFile(path.relative(clientDir, file), {
|
|
26
|
+
root: clientDir,
|
|
27
|
+
dotfiles: 'deny',
|
|
28
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (ISLAND_ENTRY.test(relative)) {
|
|
33
|
+
res
|
|
34
|
+
.status(200)
|
|
35
|
+
.type('text/javascript')
|
|
36
|
+
.setHeader('Cache-Control', 'no-store')
|
|
37
|
+
.send(EMPTY_ISLAND_MODULE);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
res.status(404).type('text/plain').send('Not found');
|
|
41
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PackageImportOptions } from '@mintlify/common';
|
|
2
|
+
export declare const getPackageImportOptions: () => PackageImportOptions | undefined;
|
|
3
|
+
export declare const setPackageImportOptions: (options: PackageImportOptions | undefined) => void;
|
|
4
|
+
export declare const packageImportArgs: () => {
|
|
5
|
+
packages?: PackageImportOptions;
|
|
6
|
+
};
|
|
@@ -6,12 +6,14 @@ import { Server as SocketServer } from 'socket.io';
|
|
|
6
6
|
import { CMD_EXEC_PATH, NEXT_PUBLIC_PATH } from '../constants.js';
|
|
7
7
|
import { addLog, removeSpinnerLogs } from '../logging-state.js';
|
|
8
8
|
import { LaunchLog, SearchDisabledLog, UpdateLog } from '../logs.js';
|
|
9
|
+
import { isNativePackagesEnabled } from '../packages/flag.js';
|
|
9
10
|
import { maybeFixMissingWindowsEnvVar } from '../util.js';
|
|
10
11
|
import { addDevTimingLog, startDevTiming, timeDev } from './dev-timing.js';
|
|
11
12
|
import listener, { initializeDocsConfigHashCache, initializeFrontmatterHashCache, initializeImportCache, } from './listener/index.js';
|
|
12
13
|
import { refreshTrackedReferencedFiles } from './listener/referencedFiles.js';
|
|
13
14
|
import { getLocalNetworkIp } from './network.js';
|
|
14
15
|
import { ensurePageCompiled, getFailedCompileEvents, markOnDemandPagesReady, setCompileListener, } from './on-demand-page.js';
|
|
16
|
+
import { packageRoute, setPackagesRebuiltListener } from './packages/index.js';
|
|
15
17
|
import { setupNext } from './setupNext.js';
|
|
16
18
|
const PROXY_RESPONSE_HEADER_DENYLIST = new Set([
|
|
17
19
|
'connection',
|
|
@@ -53,6 +55,7 @@ export const run = async (argv) => {
|
|
|
53
55
|
process.env.LOCAL_OG_PREVIEW = 'true';
|
|
54
56
|
const requestHandler = await timeDev('setup Next handler', () => setupNext());
|
|
55
57
|
const localIp = getLocalNetworkIp();
|
|
58
|
+
const nativePackages = isNativePackagesEnabled();
|
|
56
59
|
const apiUrl = argv.apiUrl ?? process.env.MINTLIFY_API_URL ?? 'http://localhost:5000';
|
|
57
60
|
const accessToken = argv.accessToken;
|
|
58
61
|
const subdomain = argv.subdomain;
|
|
@@ -60,6 +63,8 @@ export const run = async (argv) => {
|
|
|
60
63
|
process.env.MINTLIFY_API_URL = apiUrl;
|
|
61
64
|
if (subdomain)
|
|
62
65
|
process.env.MINTLIFY_CLI_SUBDOMAIN = subdomain;
|
|
66
|
+
if (nativePackages)
|
|
67
|
+
app.use(packageRoute.prefix, packageRoute.handler);
|
|
63
68
|
// next-server is bugged, public files added after starting aren't served
|
|
64
69
|
app.use('/', express.static(NEXT_PUBLIC_PATH));
|
|
65
70
|
app.use((req, _res, next) => {
|
|
@@ -133,6 +138,8 @@ export const run = async (argv) => {
|
|
|
133
138
|
const onChange = () => {
|
|
134
139
|
io.emit('reload');
|
|
135
140
|
};
|
|
141
|
+
if (nativePackages)
|
|
142
|
+
setPackagesRebuiltListener(onChange);
|
|
136
143
|
setCompileListener((event) => {
|
|
137
144
|
io.emit('compiling', event);
|
|
138
145
|
});
|
|
@@ -184,6 +191,7 @@ export const run = async (argv) => {
|
|
|
184
191
|
disablePrefetch: argv.disablePrefetch,
|
|
185
192
|
allowSourceRefs: true,
|
|
186
193
|
lazyPages: true,
|
|
194
|
+
nativePackages,
|
|
187
195
|
}, watcherGate);
|
|
188
196
|
await timeDev('initialize import cache', () => initializeImportCache(CMD_EXEC_PATH, argv.fileImportsMap));
|
|
189
197
|
markOnDemandPagesReady();
|
package/dist/logging-state.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const addChangeLog: (log: React.ReactNode) => void;
|
|
|
6
6
|
export declare const addErrorLog: (log: React.ReactNode) => void;
|
|
7
7
|
export declare const addRawErrorLog: (message: string) => void;
|
|
8
8
|
export declare const clearErrorLogs: () => void;
|
|
9
|
+
export declare const removeErrorLogs: (targets: readonly React.ReactNode[]) => void;
|
|
9
10
|
export declare const clearLogs: () => void;
|
|
10
11
|
export declare const removeLastLog: () => void;
|
|
11
12
|
export declare const removeLog: (target: React.ReactNode) => void;
|
package/dist/logging-state.js
CHANGED
|
@@ -47,6 +47,11 @@ export const clearErrorLogs = () => {
|
|
|
47
47
|
currentErrorLogs = [];
|
|
48
48
|
setErrorLogsCallback?.(currentErrorLogs);
|
|
49
49
|
};
|
|
50
|
+
export const removeErrorLogs = (targets) => {
|
|
51
|
+
ensureLoggingInstance();
|
|
52
|
+
currentErrorLogs = currentErrorLogs.filter((log) => !targets.includes(log));
|
|
53
|
+
setErrorLogsCallback?.(currentErrorLogs);
|
|
54
|
+
};
|
|
50
55
|
export const clearLogs = () => {
|
|
51
56
|
ensureLoggingInstance();
|
|
52
57
|
currentLogs = [];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BuildDiagnostic } from '@mintlify/package-builder';
|
|
2
|
+
type PackageBuilder = typeof import('@mintlify/package-builder');
|
|
3
|
+
export declare const PACKAGE_BUILDER_MISSING = "The optional @mintlify/package-builder dependency is not installed, so npm packages are unavailable. npm skips it under --omit=optional or when esbuild fails to install; reinstall mint with --include=optional --foreground-scripts to see why.";
|
|
4
|
+
export declare const loadPackageBuilder: (load?: () => Promise<PackageBuilder>) => Promise<PackageBuilder>;
|
|
5
|
+
export declare const formatDiagnostic: ({ pkg, message }: BuildDiagnostic) => string;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { errnoCode } from './errno.js';
|
|
2
|
+
export const PACKAGE_BUILDER_MISSING = 'The optional @mintlify/package-builder dependency is not installed, so npm packages are unavailable. npm skips it under --omit=optional or when esbuild fails to install; reinstall mint with --include=optional --foreground-scripts to see why.';
|
|
3
|
+
const NOT_FOUND_CODES = new Set(['ERR_MODULE_NOT_FOUND', 'MODULE_NOT_FOUND']);
|
|
4
|
+
export const loadPackageBuilder = async (load = () => import('@mintlify/package-builder')) => {
|
|
5
|
+
try {
|
|
6
|
+
return await load();
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
if (NOT_FOUND_CODES.has(errnoCode(error) ?? '') &&
|
|
10
|
+
error instanceof Error &&
|
|
11
|
+
error.message.includes("'@mintlify/package-builder'")) {
|
|
12
|
+
throw new Error(PACKAGE_BUILDER_MISSING);
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export const formatDiagnostic = ({ pkg, message }) => `${pkg ? `${pkg}: ` : ''}${message}`;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type FileWithImports, type IslandSpec, type PackageImportOptions, type PackageSourceProblem } from '@mintlify/common';
|
|
2
|
+
export type PackageIslandProblem = PackageSourceProblem;
|
|
3
|
+
export declare const formatProblem: ({ file, line, message }: PackageIslandProblem) => string;
|
|
4
|
+
export type CollectPackageIslandsInput = {
|
|
5
|
+
docsRoot: string;
|
|
6
|
+
packageNames: ReadonlySet<string>;
|
|
7
|
+
variables?: Record<string, string>;
|
|
8
|
+
provider?: string;
|
|
9
|
+
mintIgnore?: string[];
|
|
10
|
+
};
|
|
11
|
+
export type PageIslands = {
|
|
12
|
+
ids: string[];
|
|
13
|
+
errors: PackageIslandProblem[];
|
|
14
|
+
};
|
|
15
|
+
export type CollectedIslands = {
|
|
16
|
+
islands: IslandSpec[];
|
|
17
|
+
errors: PackageIslandProblem[];
|
|
18
|
+
warnings: PackageIslandProblem[];
|
|
19
|
+
packageSnippets: Set<string>;
|
|
20
|
+
snippetFiles: Record<string, string>;
|
|
21
|
+
pages: Record<string, PageIslands>;
|
|
22
|
+
};
|
|
23
|
+
type SourceFile = {
|
|
24
|
+
filename: string;
|
|
25
|
+
content: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const resolvePageContent: ({ page, docsRoot, snippets, packages, onWarning, }: {
|
|
28
|
+
page: SourceFile;
|
|
29
|
+
docsRoot: string;
|
|
30
|
+
snippets: FileWithImports[];
|
|
31
|
+
packages: PackageImportOptions;
|
|
32
|
+
onWarning?: (message: string) => void;
|
|
33
|
+
}) => Promise<string | undefined>;
|
|
34
|
+
export declare class PackageIslandCollector {
|
|
35
|
+
private readonly input;
|
|
36
|
+
private snippetSources;
|
|
37
|
+
private snippets;
|
|
38
|
+
private snippetTreeMap;
|
|
39
|
+
private packageSnippets;
|
|
40
|
+
private packageSnippetTrees;
|
|
41
|
+
private pageEntries;
|
|
42
|
+
private warnings;
|
|
43
|
+
private providerErrors;
|
|
44
|
+
private snippetErrors;
|
|
45
|
+
private snippetFiles;
|
|
46
|
+
private queue;
|
|
47
|
+
constructor(input: CollectPackageIslandsInput);
|
|
48
|
+
private get packages();
|
|
49
|
+
private warn;
|
|
50
|
+
inheritSnippetTrees(previous: PackageIslandCollector): void;
|
|
51
|
+
collectAll(): Promise<CollectedIslands>;
|
|
52
|
+
updatePage(filename: string): Promise<CollectedIslands>;
|
|
53
|
+
private enqueue;
|
|
54
|
+
private classify;
|
|
55
|
+
private collectAllNow;
|
|
56
|
+
private updatePageNow;
|
|
57
|
+
result(): CollectedIslands;
|
|
58
|
+
private extractPage;
|
|
59
|
+
}
|
|
60
|
+
export declare const collectPackageIslands: (input: CollectPackageIslandsInput) => Promise<CollectedIslands>;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { classifyPackageSources, extractImportSources, extractPagePackageIslands, findAndRemoveImports, findPackageSnippets, getAST, hasImports, isPackageSnippetPath, isSnippetExtension, mergePackageIslands, parseFrontmatter, replaceVariables, resolveAllImports, resolveImportPath, resolveSnippetImports, stringifyTree, } from '@mintlify/common';
|
|
2
|
+
import { createPage, getFileListSync, getMintIgnore } from '@mintlify/prebuild';
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
4
|
+
import { extname, join, sep } from 'path';
|
|
5
|
+
export const formatProblem = ({ file, line, message }) => `${file}${line === undefined ? '' : `:${line}`} ${message}`;
|
|
6
|
+
const withSlash = (filename) => (filename.startsWith('/') ? filename : `/${filename}`);
|
|
7
|
+
const displayPath = (filename) => filename.replace(/^\/+/, '');
|
|
8
|
+
const mayImport = (content) => /\bimport\b/.test(content);
|
|
9
|
+
const parseOrUndefined = (content, filename) => {
|
|
10
|
+
try {
|
|
11
|
+
return getAST(content, filename);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const readSources = async (docsRoot, mintIgnore) => {
|
|
18
|
+
const filenames = getFileListSync(docsRoot, docsRoot, mintIgnore)
|
|
19
|
+
.filter((filename) => isSnippetExtension(extname(filename).slice(1)))
|
|
20
|
+
.sort();
|
|
21
|
+
const sources = await Promise.all(filenames.map(async (filename) => {
|
|
22
|
+
const content = await readFile(join(docsRoot, filename), 'utf8').catch(() => undefined);
|
|
23
|
+
return content === undefined ? [] : [{ path: displayPath(filename), content }];
|
|
24
|
+
}));
|
|
25
|
+
return sources.flat();
|
|
26
|
+
};
|
|
27
|
+
const createLegacySnippetTreeMap = async (snippets, variables) => {
|
|
28
|
+
if (snippets.length === 0)
|
|
29
|
+
return {};
|
|
30
|
+
const { createPackageSnippetTreeMap } = await import('@mintlify/common/server-mdx');
|
|
31
|
+
return createPackageSnippetTreeMap(snippets, variables);
|
|
32
|
+
};
|
|
33
|
+
const resolveSnippets = async (snippets, variables, packages, onWarning) => {
|
|
34
|
+
const parsed = [];
|
|
35
|
+
for (const { filename, content } of snippets) {
|
|
36
|
+
const tree = parseOrUndefined(replaceVariables(content, variables), filename);
|
|
37
|
+
if (tree)
|
|
38
|
+
parsed.push({ ...(await findAndRemoveImports(tree)), filename });
|
|
39
|
+
}
|
|
40
|
+
return resolveSnippetImports(parsed, {
|
|
41
|
+
packages,
|
|
42
|
+
onWarning: (warning, filename) => onWarning(filename, warning.message),
|
|
43
|
+
skip: (filename) => packages.packageSnippets?.has(filename) ?? false,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
export const resolvePageContent = async ({ page, docsRoot, snippets, packages, onWarning, }) => {
|
|
47
|
+
const tree = parseOrUndefined(page.content, page.filename);
|
|
48
|
+
if (!tree)
|
|
49
|
+
return undefined;
|
|
50
|
+
const importsResponse = await findAndRemoveImports(tree);
|
|
51
|
+
const content = hasImports(importsResponse)
|
|
52
|
+
? stringifyTree(await resolveAllImports({
|
|
53
|
+
snippets,
|
|
54
|
+
fileWithImports: { ...importsResponse, filename: page.filename },
|
|
55
|
+
onWarning: (warning) => onWarning?.(warning.message),
|
|
56
|
+
packages,
|
|
57
|
+
}))
|
|
58
|
+
: page.content;
|
|
59
|
+
const { pageContent } = await createPage(displayPath(page.filename), content, docsRoot, [], [], () => { });
|
|
60
|
+
return parseFrontmatter(pageContent).body;
|
|
61
|
+
};
|
|
62
|
+
export class PackageIslandCollector {
|
|
63
|
+
constructor(input) {
|
|
64
|
+
this.input = input;
|
|
65
|
+
this.snippetSources = [];
|
|
66
|
+
this.snippets = [];
|
|
67
|
+
this.snippetTreeMap = {};
|
|
68
|
+
this.packageSnippets = new Set();
|
|
69
|
+
this.packageSnippetTrees = new Map();
|
|
70
|
+
this.pageEntries = new Map();
|
|
71
|
+
this.warnings = [];
|
|
72
|
+
this.providerErrors = [];
|
|
73
|
+
this.snippetErrors = [];
|
|
74
|
+
this.snippetFiles = {};
|
|
75
|
+
this.queue = Promise.resolve();
|
|
76
|
+
this.warn = (file, message) => {
|
|
77
|
+
if (!this.warnings.some((warning) => warning.message === message)) {
|
|
78
|
+
this.warnings.push({ file: displayPath(file), message });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
get packages() {
|
|
83
|
+
return { packageNames: this.input.packageNames, packageSnippets: this.packageSnippets };
|
|
84
|
+
}
|
|
85
|
+
inheritSnippetTrees(previous) {
|
|
86
|
+
if (previous.input.docsRoot !== this.input.docsRoot)
|
|
87
|
+
return;
|
|
88
|
+
this.queue = Promise.all([this.queue, previous.queue]).then(() => {
|
|
89
|
+
this.packageSnippetTrees = new Map(previous.packageSnippetTrees);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
collectAll() {
|
|
93
|
+
return this.enqueue(() => this.collectAllNow());
|
|
94
|
+
}
|
|
95
|
+
updatePage(filename) {
|
|
96
|
+
return this.enqueue(() => this.updatePageNow(filename));
|
|
97
|
+
}
|
|
98
|
+
enqueue(operation) {
|
|
99
|
+
const run = this.queue.then(operation);
|
|
100
|
+
this.queue = run.catch(() => { });
|
|
101
|
+
return run;
|
|
102
|
+
}
|
|
103
|
+
classify(files) {
|
|
104
|
+
return classifyPackageSources(files, {
|
|
105
|
+
packageNames: this.input.packageNames,
|
|
106
|
+
variables: this.input.variables,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async collectAllNow() {
|
|
110
|
+
const { docsRoot, variables, provider } = this.input;
|
|
111
|
+
const files = await readSources(docsRoot, this.input.mintIgnore ?? (await getMintIgnore(docsRoot)));
|
|
112
|
+
const classified = this.classify(files);
|
|
113
|
+
const snippetPaths = new Set([
|
|
114
|
+
...classified.snippets.map(({ path }) => path),
|
|
115
|
+
...classified.unparsed.flatMap(({ path, category }) => (category === 'snippet' ? path : [])),
|
|
116
|
+
]);
|
|
117
|
+
this.snippetSources = files
|
|
118
|
+
.filter(({ path }) => snippetPaths.has(path))
|
|
119
|
+
.map(({ path, content }) => ({ filename: withSlash(path), content }));
|
|
120
|
+
this.snippetTreeMap = await createLegacySnippetTreeMap(classified.legacySnippets, variables);
|
|
121
|
+
const previousTrees = this.packageSnippetTrees;
|
|
122
|
+
const parsedTrees = new Map(classified.snippets.map(({ path, tree }) => [path, tree]));
|
|
123
|
+
this.packageSnippetTrees = new Map();
|
|
124
|
+
for (const { filename } of this.snippetSources) {
|
|
125
|
+
if (!isPackageSnippetPath(filename))
|
|
126
|
+
continue;
|
|
127
|
+
const tree = parsedTrees.get(displayPath(filename)) ?? previousTrees.get(filename);
|
|
128
|
+
if (tree)
|
|
129
|
+
this.packageSnippetTrees.set(filename, tree);
|
|
130
|
+
}
|
|
131
|
+
this.packageSnippets = findPackageSnippets([...this.packageSnippetTrees].map(([filename, tree]) => ({ filename, tree })));
|
|
132
|
+
this.warnings = [];
|
|
133
|
+
this.snippets = await resolveSnippets(this.snippetSources, variables, this.packages, this.warn);
|
|
134
|
+
const sourceErrors = new Map();
|
|
135
|
+
for (const error of classified.errors) {
|
|
136
|
+
sourceErrors.set(error.file, [...(sourceErrors.get(error.file) ?? []), error]);
|
|
137
|
+
}
|
|
138
|
+
this.pageEntries = new Map();
|
|
139
|
+
for (const page of classified.pages)
|
|
140
|
+
await this.extractPage(page, sourceErrors.get(page.path));
|
|
141
|
+
for (const { path, content, category } of classified.unparsed) {
|
|
142
|
+
if (category === 'page')
|
|
143
|
+
await this.extractPage({ path, content }, sourceErrors.get(path));
|
|
144
|
+
}
|
|
145
|
+
this.snippetErrors = classified.errors.filter(({ file }) => !this.pageEntries.has(file));
|
|
146
|
+
this.snippetFiles = {};
|
|
147
|
+
for (const path of [...this.packageSnippets].sort()) {
|
|
148
|
+
const source = this.snippetSources.find(({ filename }) => filename === path);
|
|
149
|
+
if (source)
|
|
150
|
+
this.snippetFiles[path] = source.content;
|
|
151
|
+
}
|
|
152
|
+
this.providerErrors = [];
|
|
153
|
+
if (provider !== undefined) {
|
|
154
|
+
try {
|
|
155
|
+
this.snippetFiles[provider] = await readFile(join(docsRoot, provider), 'utf8');
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
this.providerErrors.push({
|
|
159
|
+
file: 'docs.json',
|
|
160
|
+
message: `packages.provider ${provider} was not found.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return this.result();
|
|
165
|
+
}
|
|
166
|
+
async updatePageNow(filename) {
|
|
167
|
+
const path = displayPath(filename.split(sep).join('/'));
|
|
168
|
+
if (!this.pageEntries.has(path))
|
|
169
|
+
return this.collectAllNow();
|
|
170
|
+
let content;
|
|
171
|
+
try {
|
|
172
|
+
content = await readFile(join(this.input.docsRoot, path), 'utf8');
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
this.pageEntries.delete(path);
|
|
176
|
+
return this.result();
|
|
177
|
+
}
|
|
178
|
+
const tree = parseOrUndefined(content, withSlash(path));
|
|
179
|
+
const importsPages = (tree ? extractImportSources(tree) : []).some((source) => {
|
|
180
|
+
const resolved = resolveImportPath(source, withSlash(path));
|
|
181
|
+
return resolved !== null && this.pageEntries.has(displayPath(resolved));
|
|
182
|
+
});
|
|
183
|
+
if (importsPages)
|
|
184
|
+
return this.collectAllNow();
|
|
185
|
+
await this.extractPage({ path, content }, this.classify([{ path, content }]).errors);
|
|
186
|
+
return this.result();
|
|
187
|
+
}
|
|
188
|
+
result() {
|
|
189
|
+
const entries = [...this.pageEntries.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
190
|
+
const pages = {};
|
|
191
|
+
for (const [file, entry] of entries)
|
|
192
|
+
pages[file] = { ids: entry.ids, errors: entry.errors };
|
|
193
|
+
return {
|
|
194
|
+
islands: mergePackageIslands(entries.map(([, entry]) => entry)).islands,
|
|
195
|
+
errors: [
|
|
196
|
+
...entries.flatMap(([, entry]) => entry.errors),
|
|
197
|
+
...this.snippetErrors,
|
|
198
|
+
...this.providerErrors,
|
|
199
|
+
],
|
|
200
|
+
warnings: [...this.warnings],
|
|
201
|
+
packageSnippets: new Set(this.packageSnippets),
|
|
202
|
+
snippetFiles: { ...this.snippetFiles },
|
|
203
|
+
pages,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async extractPage(page, sourceErrors = []) {
|
|
207
|
+
const { path } = page;
|
|
208
|
+
const content = replaceVariables(page.content, this.input.variables);
|
|
209
|
+
const noIslands = { ids: [], islands: [], errors: sourceErrors };
|
|
210
|
+
if (!mayImport(content) && !content.includes('<Snippet')) {
|
|
211
|
+
this.pageEntries.set(path, noIslands);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const body = await resolvePageContent({
|
|
215
|
+
page: { filename: withSlash(path), content: page.content },
|
|
216
|
+
docsRoot: this.input.docsRoot,
|
|
217
|
+
snippets: this.snippets,
|
|
218
|
+
packages: this.packages,
|
|
219
|
+
onWarning: (message) => this.warn(path, message),
|
|
220
|
+
});
|
|
221
|
+
if (body === undefined) {
|
|
222
|
+
this.pageEntries.set(path, noIslands);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const extraction = extractPagePackageIslands({
|
|
226
|
+
path,
|
|
227
|
+
content: body,
|
|
228
|
+
packageNames: this.input.packageNames,
|
|
229
|
+
packageSnippets: this.packageSnippets,
|
|
230
|
+
variables: this.input.variables,
|
|
231
|
+
snippetTreeMap: this.snippetTreeMap,
|
|
232
|
+
});
|
|
233
|
+
this.pageEntries.set(path, {
|
|
234
|
+
ids: extraction.ids,
|
|
235
|
+
islands: extraction.islands,
|
|
236
|
+
errors: [...sourceErrors, ...extraction.errors],
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export const collectPackageIslands = (input) => new PackageIslandCollector(input).collectAll();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const errnoCode: (error: unknown) => string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isNativePackagesEnabled: () => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isNativePackagesEnabled = () => process.env.MINTLIFY_NATIVE_NPM_PACKAGES === '1';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { collectPackageIslands, PackageIslandCollector, resolvePageContent, type CollectedIslands, type CollectPackageIslandsInput, type PackageIslandProblem, type PageIslands, } from './collect.js';
|
|
2
|
+
export { isNativePackagesEnabled } from './flag.js';
|
|
3
|
+
export { normalizeProviderPath, readPackagesSetup, type PackagesSetup } from './setup.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type PackagesConfig } from '@mintlify/validation';
|
|
2
|
+
export type PackagesSetup = {
|
|
3
|
+
packages: PackagesConfig;
|
|
4
|
+
packageNames: Set<string>;
|
|
5
|
+
variables?: Record<string, string>;
|
|
6
|
+
errors: string[];
|
|
7
|
+
packageJsonError?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare class DocsJsonError extends Error {
|
|
10
|
+
readonly problems: string[];
|
|
11
|
+
constructor(problems: string[]);
|
|
12
|
+
}
|
|
13
|
+
export declare class InvalidPackagesError extends DocsJsonError {
|
|
14
|
+
readonly variables: Record<string, string> | undefined;
|
|
15
|
+
constructor(problems: string[], variables: Record<string, string> | undefined);
|
|
16
|
+
}
|
|
17
|
+
export declare const normalizeProviderPath: (provider: string) => string;
|
|
18
|
+
export declare const readPackagesSetup: (docsRoot: string) => Promise<PackagesSetup | undefined>;
|