@mintlify/previewing 4.0.1462 → 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 (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 +457 -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 +2 -2
  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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/previewing",
3
- "version": "4.0.1462",
3
+ "version": "4.0.1463",
4
4
  "description": "Preview Mintlify docs locally",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -86,5 +86,5 @@
86
86
  "vitest": "2.1.9",
87
87
  "vitest-mock-process": "1.0.4"
88
88
  },
89
- "gitHead": "d7f4b348efcebe273004ba634700ac4018560404"
89
+ "gitHead": "688752e942ccb49213dddf7fa163b025016a48eb"
90
90
  }
@@ -1,254 +0,0 @@
1
- import fse from 'fs-extra';
2
- import { mkdtemp, rm } from 'fs/promises';
3
- import { tmpdir } from 'os';
4
- import { basename, join } from 'path';
5
- import { checkPackages } from '../packages/check.js';
6
- const PAGE = `import { LineChart } from "recharts";
7
-
8
- <LineChart width={400} height={200} data={[{ v: 1 }]} />
9
- `;
10
- const result = (input) => {
11
- const id = input.islands[0]?.id ?? '';
12
- return {
13
- ok: true,
14
- manifest: {
15
- version: 1,
16
- builderVersion: '0.0.1',
17
- buildId: 'f'.repeat(32),
18
- depsHash: 'd'.repeat(64),
19
- islandsHash: 'e'.repeat(64),
20
- react: { version: '18.3.1', domVersion: '18.3.1' },
21
- packages: { react: { version: '18.3.1' }, recharts: { version: '2.15.4' } },
22
- css: [],
23
- clientAccess: {
24
- version: 1,
25
- files: { [`client/islands/${id}.js`]: [id] },
26
- globalFiles: [],
27
- },
28
- islands: {
29
- [id]: { entry: `client/islands/${id}.js`, prefix: `mi-${id}-`, html: '<svg/>', ssrMs: 2 },
30
- },
31
- files: [
32
- { path: `client/islands/${id}.js`, bytes: 2048, gzipBytes: 512, integrity: 'sha384-abc' },
33
- ],
34
- diagnostics: [
35
- { level: 'warning', code: 'peer-range', message: 'peer warning', pkg: 'recharts' },
36
- ],
37
- },
38
- stats: {
39
- installMs: 400,
40
- bundleMs: 300,
41
- renderMs: 100,
42
- totalMs: 850,
43
- clientBytes: 2048,
44
- clientGzipBytes: 512,
45
- serverBytes: 4096,
46
- islandClientGzipBytes: { [id]: 512 },
47
- reactDirs: { react: [], 'react-dom': [] },
48
- },
49
- localFiles: [],
50
- missingLocalFiles: [],
51
- };
52
- };
53
- describe('checkPackages', () => {
54
- let docsRoot;
55
- beforeEach(async () => {
56
- docsRoot = await mkdtemp(join(tmpdir(), 'packages-check-'));
57
- await fse.outputJson(join(docsRoot, 'docs.json'), { name: 'Docs', packages: {} });
58
- await fse.outputJson(join(docsRoot, 'package.json'), { dependencies: { recharts: '2.15.4' } });
59
- await fse.outputFile(join(docsRoot, 'charts.mdx'), PAGE);
60
- });
61
- afterEach(async () => {
62
- await rm(docsRoot, { recursive: true, force: true });
63
- });
64
- it('builds with npm ci in a temp folder and prints hashes, sizes and diagnostics', async () => {
65
- let input;
66
- const check = await checkPackages({
67
- docsRoot,
68
- build: async (received) => {
69
- input = received;
70
- await fse.ensureDir(received.outDir);
71
- return result(received);
72
- },
73
- });
74
- expect(input?.installer).toBe('npm-ci');
75
- expect(input?.islands).toHaveLength(1);
76
- expect(await fse.pathExists(input?.outDir ?? '')).toBe(false);
77
- expect(check.ok).toBe(true);
78
- const text = check.lines.map((line) => `${line.level} ${line.message}`);
79
- expect(text.some((line) => /[0-9a-f]{32}|sha384-|client\/islands/.test(line))).toBe(false);
80
- expect(text).toEqual(expect.arrayContaining([
81
- 'info 1 package component on 1 page',
82
- 'info installed react 18.3.1, react-dom 18.3.1, recharts 2.15.4',
83
- 'info install 400 ms, bundle 300 ms, render 100 ms, total 850 ms',
84
- 'info 0.5 KB of browser JS (gzip) across all pages',
85
- 'info charts.mdx: <LineChart> from recharts, 0.5 KB JS (gzip), renders on the server',
86
- 'warning recharts: peer warning',
87
- ]));
88
- });
89
- it('keeps the output when --out is given', async () => {
90
- const out = join(docsRoot, '..', `${basename(docsRoot)}-out`);
91
- await checkPackages({
92
- docsRoot,
93
- outDir: out,
94
- build: async (received) => {
95
- await fse.ensureDir(received.outDir);
96
- return result(received);
97
- },
98
- });
99
- expect(await fse.pathExists(out)).toBe(true);
100
- await rm(out, { recursive: true, force: true });
101
- });
102
- it('refuses to replace a non-empty --out folder unless forced', async () => {
103
- const out = join(docsRoot, '..', `${basename(docsRoot)}-keep`);
104
- await fse.outputFile(join(out, 'notes.txt'), 'keep me');
105
- const build = vi.fn(async (received) => {
106
- await fse.emptyDir(received.outDir);
107
- return result(received);
108
- });
109
- try {
110
- const check = await checkPackages({ docsRoot, outDir: out, build });
111
- expect(build).not.toHaveBeenCalled();
112
- expect(check.ok).toBe(false);
113
- expect(check.lines).toEqual([
114
- {
115
- level: 'error',
116
- message: expect.stringContaining(`${out} already exists and is not an empty folder`),
117
- },
118
- ]);
119
- expect(await fse.readFile(join(out, 'notes.txt'), 'utf8')).toBe('keep me');
120
- const forced = await checkPackages({ docsRoot, outDir: out, force: true, build });
121
- expect(build).toHaveBeenCalledTimes(1);
122
- expect(forced.ok).toBe(true);
123
- }
124
- finally {
125
- await rm(out, { recursive: true, force: true });
126
- }
127
- });
128
- it('refuses an --out path that is a file', async () => {
129
- const out = join(docsRoot, 'charts.mdx');
130
- const build = vi.fn();
131
- const check = await checkPackages({ docsRoot, outDir: out, build });
132
- expect(build).not.toHaveBeenCalled();
133
- expect(check.ok).toBe(false);
134
- expect(check.lines[0]?.message).toContain(`${out} already exists and is not an empty folder`);
135
- expect(await fse.readFile(out, 'utf8')).toBe(PAGE);
136
- });
137
- it('builds the package components that <Snippet file> injects', async () => {
138
- await fse.outputFile(join(docsRoot, 'charts.mdx'), '<Snippet file="chart.mdx" />\n');
139
- await fse.outputFile(join(docsRoot, '_snippets/chart.mdx'), PAGE);
140
- const build = vi.fn(async (received) => result(received));
141
- const check = await checkPackages({ docsRoot, build });
142
- expect(build.mock.calls[0]?.[0].islands).toEqual([
143
- expect.objectContaining({ pagePath: 'charts', kind: 'inline' }),
144
- ]);
145
- expect(check.lines).toContainEqual({
146
- level: 'info',
147
- message: '1 package component on 1 page',
148
- });
149
- });
150
- it('fails before building when a page has island errors', async () => {
151
- await fse.outputFile(join(docsRoot, 'bad.mdx'), PAGE.replace('/>', 'onClick={() => {}} />'));
152
- const build = vi.fn();
153
- const check = await checkPackages({ docsRoot, build });
154
- expect(build).not.toHaveBeenCalled();
155
- expect(check.ok).toBe(false);
156
- expect(check.lines.some((line) => line.level === 'error' && line.message.startsWith('bad.mdx:3'))).toBe(true);
157
- });
158
- it('fails before building when a snippet a page imports has errors', async () => {
159
- await fse.outputFile(join(docsRoot, 'charts.mdx'), 'import { Chart } from "/snippets/chart.jsx";\n\n<Chart />\n');
160
- await fse.outputFile(join(docsRoot, 'snippets/chart.jsx'), 'import { LineChart } from "recharts";\n\nexport const Chart = () => <LineChart\n');
161
- const build = vi.fn();
162
- const check = await checkPackages({ docsRoot, build });
163
- expect(build).not.toHaveBeenCalled();
164
- expect(check.ok).toBe(false);
165
- expect(check.lines.some((line) => line.level === 'error' && line.message.startsWith('snippets/chart.jsx'))).toBe(true);
166
- });
167
- it('reports a docs.json that does not parse instead of a missing packages key', async () => {
168
- const raw = '{ "name": "Docs", "packages": {}, }';
169
- await fse.outputFile(join(docsRoot, 'docs.json'), raw);
170
- const parserMessage = (() => {
171
- try {
172
- JSON.parse(raw);
173
- return '';
174
- }
175
- catch (error) {
176
- return error instanceof Error ? error.message : String(error);
177
- }
178
- })();
179
- const build = vi.fn();
180
- const check = await checkPackages({ docsRoot, build });
181
- expect(build).not.toHaveBeenCalled();
182
- expect(check.ok).toBe(false);
183
- expect(check.lines).toEqual([
184
- { level: 'error', message: `docs.json could not be loaded: ${parserMessage}` },
185
- ]);
186
- });
187
- it.each([
188
- ['', 'docs.json could not be loaded: Unexpected end of JSON input'],
189
- ['["packages"]', 'docs.json is not a JSON object'],
190
- ])('fails on docs.json %j without building', async (raw, message) => {
191
- await fse.outputFile(join(docsRoot, 'docs.json'), raw);
192
- const build = vi.fn();
193
- expect(await checkPackages({ docsRoot, build })).toEqual({
194
- ok: false,
195
- lines: [{ level: 'error', message }],
196
- });
197
- expect(build).not.toHaveBeenCalled();
198
- });
199
- it.each([
200
- ['', 'package.json is not valid JSON: Unexpected end of JSON input'],
201
- ['{ "dependencies": ', 'package.json is not valid JSON: Unexpected end of JSON input'],
202
- ['["recharts"]', 'package.json is not a JSON object'],
203
- ['null', 'package.json is not a JSON object'],
204
- ])('fails on package.json %j without building', async (raw, message) => {
205
- await fse.outputFile(join(docsRoot, 'package.json'), raw);
206
- const build = vi.fn();
207
- expect(await checkPackages({ docsRoot, build })).toEqual({
208
- ok: false,
209
- lines: [{ level: 'error', message }],
210
- });
211
- expect(build).not.toHaveBeenCalled();
212
- });
213
- it.each([
214
- ['a string', 'nope', ['docs.json packages: Expected object, received string']],
215
- ['null', null, ['docs.json packages: Expected object, received null']],
216
- ['an array', [], ['docs.json packages: Expected object, received array']],
217
- [
218
- 'an http registry',
219
- { registries: { '@acme': 'http://npm.acme.com/' } },
220
- [
221
- 'docs.json packages.registries.@acme: Registry URLs must start with `https://` and then the host',
222
- ],
223
- ],
224
- [
225
- 'a provider without a leading slash',
226
- { provider: 'snippets/provider.jsx' },
227
- [
228
- 'docs.json packages.provider: Provider path must start with `/`, such as `/snippets/provider.jsx`',
229
- ],
230
- ],
231
- [
232
- 'a provider outside the docs',
233
- { provider: '../provider.jsx', isolation: 'none' },
234
- [
235
- 'docs.json packages.provider: Provider path cannot contain `..` segments',
236
- "docs.json packages.isolation: Invalid enum value. Expected 'page' | 'frame', received 'none'",
237
- ],
238
- ],
239
- ])('fails on %s for packages with only the schema errors and no build', async (_, packages, errors) => {
240
- await fse.outputJson(join(docsRoot, 'docs.json'), { name: 'Docs', packages });
241
- const build = vi.fn();
242
- expect(await checkPackages({ docsRoot, build })).toEqual({
243
- ok: false,
244
- lines: errors.map((message) => ({ level: 'error', message })),
245
- });
246
- expect(build).not.toHaveBeenCalled();
247
- });
248
- it('fails without a packages key', async () => {
249
- await fse.outputJson(join(docsRoot, 'docs.json'), { name: 'Docs' });
250
- const check = await checkPackages({ docsRoot, build: vi.fn() });
251
- expect(check.ok).toBe(false);
252
- expect(check.lines[0]?.message).toContain('has no "packages" key');
253
- });
254
- });
@@ -1,25 +0,0 @@
1
- import type { BuildDiagnostic, BuildResult, BuildStats, PackageBuildManifest } from '@mintlify/package-builder';
2
- import { type CollectedIslands } from './collect.js';
3
- export type PackagesCheckLine = {
4
- level: 'info' | 'warning' | 'error';
5
- message: string;
6
- };
7
- export type PackagesCheckResult = {
8
- ok: boolean;
9
- lines: PackagesCheckLine[];
10
- collected?: CollectedIslands;
11
- manifest?: PackageBuildManifest;
12
- stats?: BuildStats;
13
- };
14
- export type PackagesCheckOptions = {
15
- docsRoot: string;
16
- outDir?: string;
17
- force?: boolean;
18
- build?: (input: Parameters<typeof import('@mintlify/package-builder').build>[0]) => Promise<BuildResult>;
19
- };
20
- export declare const formatDiagnostic: ({ pkg, message }: BuildDiagnostic) => string;
21
- export declare const describeBuild: ({ manifest, stats }: {
22
- manifest: PackageBuildManifest;
23
- stats: BuildStats;
24
- }, collected: CollectedIslands) => PackagesCheckLine[];
25
- export declare const checkPackages: ({ docsRoot, outDir, force, build, }: PackagesCheckOptions) => Promise<PackagesCheckResult>;
@@ -1,132 +0,0 @@
1
- import { mkdtemp, readdir, rm } from 'fs/promises';
2
- import { tmpdir } from 'os';
3
- import { join, resolve } from 'path';
4
- import { loadPackageBuilder } from './builder.js';
5
- import { collectPackageIslands, formatProblem } from './collect.js';
6
- import { errnoCode } from './errno.js';
7
- import { DocsJsonError, readPackagesSetup } from './setup.js';
8
- const kb = (bytes) => `${(bytes / 1024).toFixed(1)} KB`;
9
- const ms = (value) => `${Math.round(value)} ms`;
10
- const occupied = async (dir) => {
11
- try {
12
- return (await readdir(dir)).length > 0;
13
- }
14
- catch (error) {
15
- return errnoCode(error) !== 'ENOENT';
16
- }
17
- };
18
- export const formatDiagnostic = ({ pkg, message }) => `${pkg ? `${pkg}: ` : ''}${message}`;
19
- const describeImports = (island) => {
20
- const byPackage = new Map();
21
- for (const { local, pkg } of island?.imports ?? []) {
22
- byPackage.set(pkg, [...(byPackage.get(pkg) ?? []), `<${local}>`]);
23
- }
24
- return [...byPackage].map(([pkg, names]) => `${names.join(', ')} from ${pkg}`).join('; ');
25
- };
26
- export const describeBuild = ({ manifest, stats }, collected) => {
27
- const info = (message) => ({ level: 'info', message });
28
- const specs = new Map(collected.islands.map((island) => [island.id, island]));
29
- const lines = [
30
- info(`installed ${[
31
- `react ${manifest.react.version || '?'}`,
32
- `react-dom ${manifest.react.domVersion || '?'}`,
33
- ...Object.entries(manifest.packages)
34
- .filter(([name]) => name !== 'react' && name !== 'react-dom')
35
- .map(([name, { version }]) => `${name} ${version}`),
36
- ].join(', ')}`),
37
- info(`install ${ms(stats.installMs)}, bundle ${ms(stats.bundleMs)}, render ${ms(stats.renderMs)}, total ${ms(stats.totalMs)}`),
38
- info(`${kb(stats.clientGzipBytes)} of browser JS (gzip) across all pages`),
39
- ];
40
- for (const [page, { ids }] of Object.entries(collected.pages)) {
41
- for (const id of new Set(ids)) {
42
- const island = manifest.islands[id];
43
- if (!island)
44
- continue;
45
- const gzip = stats.islandClientGzipBytes[id];
46
- const parts = [
47
- describeImports(specs.get(id)) || 'package component',
48
- ...(gzip === undefined ? [] : [`${kb(gzip)} JS (gzip)`]),
49
- island.html !== undefined ? 'renders on the server' : 'renders in the browser only',
50
- ];
51
- lines.push(info(`${page}: ${parts.join(', ')}`));
52
- }
53
- }
54
- for (const d of manifest.diagnostics) {
55
- lines.push({ level: d.level, message: formatDiagnostic(d) });
56
- }
57
- return lines;
58
- };
59
- export const checkPackages = async ({ docsRoot, outDir, force, build, }) => {
60
- if (outDir && !force && (await occupied(resolve(outDir)))) {
61
- return {
62
- ok: false,
63
- lines: [
64
- {
65
- level: 'error',
66
- message: `${resolve(outDir)} already exists and is not an empty folder; use an empty folder or pass --force to replace it.`,
67
- },
68
- ],
69
- };
70
- }
71
- let setup;
72
- try {
73
- setup = await readPackagesSetup(docsRoot);
74
- }
75
- catch (error) {
76
- if (!(error instanceof DocsJsonError))
77
- throw error;
78
- return { ok: false, lines: error.problems.map((message) => ({ level: 'error', message })) };
79
- }
80
- if (!setup) {
81
- return {
82
- ok: false,
83
- lines: [{ level: 'error', message: `${join(docsRoot, 'docs.json')} has no "packages" key.` }],
84
- };
85
- }
86
- const lines = setup.errors.map((message) => ({ level: 'error', message }));
87
- if (setup.packageJsonError)
88
- return { ok: false, lines };
89
- const collected = await collectPackageIslands({
90
- docsRoot,
91
- packageNames: setup.packageNames,
92
- variables: setup.variables,
93
- provider: setup.packages.provider,
94
- });
95
- const pageCount = Object.values(collected.pages).filter(({ ids }) => ids.length > 0).length;
96
- lines.push({
97
- level: 'info',
98
- message: `${collected.islands.length} package component${collected.islands.length === 1 ? '' : 's'} on ${pageCount} page${pageCount === 1 ? '' : 's'}`,
99
- });
100
- for (const warning of collected.warnings) {
101
- lines.push({ level: 'warning', message: formatProblem(warning) });
102
- }
103
- for (const error of collected.errors) {
104
- lines.push({ level: 'error', message: formatProblem(error) });
105
- }
106
- if (lines.some((line) => line.level === 'error'))
107
- return { ok: false, lines, collected };
108
- const run = build ?? (await loadPackageBuilder()).build;
109
- const target = outDir ? resolve(outDir) : await mkdtemp(join(tmpdir(), 'mint-packages-check-'));
110
- try {
111
- const result = await run({
112
- docsRoot,
113
- packages: setup.packages,
114
- islands: collected.islands,
115
- snippetFiles: collected.snippetFiles,
116
- outDir: target,
117
- installer: 'npm-ci',
118
- });
119
- lines.push(...describeBuild(result, collected));
120
- return {
121
- ok: result.ok,
122
- lines,
123
- collected,
124
- manifest: result.manifest,
125
- stats: result.stats,
126
- };
127
- }
128
- finally {
129
- if (!outDir)
130
- await rm(target, { recursive: true, force: true });
131
- }
132
- };