jamdesk 1.1.25 → 1.1.26
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__/integration/validate.integration.test.js +40 -1
- package/dist/__tests__/integration/validate.integration.test.js.map +1 -1
- package/dist/__tests__/unit/templates-consistency.test.d.ts +2 -0
- package/dist/__tests__/unit/templates-consistency.test.d.ts.map +1 -0
- package/dist/__tests__/unit/templates-consistency.test.js +19 -0
- package/dist/__tests__/unit/templates-consistency.test.js.map +1 -0
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +9 -6
- package/dist/commands/validate.js.map +1 -1
- package/dist/lib/navigation-validator.d.ts +39 -17
- package/dist/lib/navigation-validator.d.ts.map +1 -1
- package/dist/lib/navigation-validator.js +65 -36
- package/dist/lib/navigation-validator.js.map +1 -1
- package/package.json +1 -1
- package/vendored/lib/docs-types.ts +14 -4
- package/vendored/lib/extract-highlights.ts +2 -2
- package/vendored/lib/validate-config.ts +118 -13
- package/vendored/schema/docs-schema.json +0 -3
- package/vendored/shared/navigation-validator.ts +103 -53
- package/vendored/shared/status-reporter.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { validateConfig } from '../../lib/docs-config.js';
|
|
@@ -67,4 +67,43 @@ describe('validate command integration', () => {
|
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
|
+
describe('validate command — missing navigation pages', () => {
|
|
71
|
+
let originalCwd;
|
|
72
|
+
let exitSpy;
|
|
73
|
+
let logSpy;
|
|
74
|
+
let errSpy;
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
originalCwd = process.cwd();
|
|
77
|
+
exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code) => {
|
|
78
|
+
throw new Error(`__exit__${code ?? 0}`);
|
|
79
|
+
}));
|
|
80
|
+
logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
81
|
+
errSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
82
|
+
});
|
|
83
|
+
afterEach(() => {
|
|
84
|
+
process.chdir(originalCwd);
|
|
85
|
+
exitSpy.mockRestore();
|
|
86
|
+
logSpy.mockRestore();
|
|
87
|
+
errSpy.mockRestore();
|
|
88
|
+
});
|
|
89
|
+
it('emits a missing_page warning for a page without an MDX file', async () => {
|
|
90
|
+
process.chdir(path.join(fixturesDir, 'missing-nav-pages'));
|
|
91
|
+
// Dynamic import so the process.exit/console spies in beforeEach are
|
|
92
|
+
// installed before validate.ts's module side effects run.
|
|
93
|
+
const { validate } = await import('../../commands/validate.js');
|
|
94
|
+
try {
|
|
95
|
+
await validate({ verbose: false, skipMdx: true });
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
// validate() calls process.exit on completion — catch the thrown marker
|
|
99
|
+
expect(e.message).toMatch(/^__exit__/);
|
|
100
|
+
}
|
|
101
|
+
const output = [
|
|
102
|
+
...logSpy.mock.calls.map((c) => c.join(' ')),
|
|
103
|
+
...errSpy.mock.calls.map((c) => c.join(' ')),
|
|
104
|
+
].join('\n');
|
|
105
|
+
expect(output).toContain('missing-page.mdx');
|
|
106
|
+
expect(output.toLowerCase()).toContain('missing navigation');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
70
109
|
//# sourceMappingURL=validate.integration.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/validate.integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/validate.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates-consistency.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/templates-consistency.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { validateNavigationPages } from '../../lib/navigation-validator.js';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const templatesDir = path.resolve(__dirname, '../../../templates');
|
|
9
|
+
describe('bundled init templates', () => {
|
|
10
|
+
it('contains a docs.json', () => {
|
|
11
|
+
expect(fs.existsSync(path.join(templatesDir, 'docs.json'))).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
it('every page referenced in docs.json navigation exists as an MDX or MD file', async () => {
|
|
14
|
+
const config = JSON.parse(fs.readFileSync(path.join(templatesDir, 'docs.json'), 'utf-8'));
|
|
15
|
+
const warnings = await validateNavigationPages(config, templatesDir);
|
|
16
|
+
expect(warnings).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=templates-consistency.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates-consistency.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/templates-consistency.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAEnE,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAC/D,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACrE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAUD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAUD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+MtE"}
|
|
@@ -10,7 +10,7 @@ import { validateConfig } from '../lib/docs-config.js';
|
|
|
10
10
|
import { output } from '../lib/output.js';
|
|
11
11
|
import { validateOpenApiSpec, formatErrorForCli, clearSpecCache } from '../lib/openapi.js';
|
|
12
12
|
import { normalizeConfig } from '../lib/normalize-config.js';
|
|
13
|
-
import {
|
|
13
|
+
import { validateNavigationPages } from '../lib/navigation-validator.js';
|
|
14
14
|
import { extractAllPages } from '../lib/mdx-validator.js';
|
|
15
15
|
import { validateBrandingAssets } from '../lib/validate-branding.js';
|
|
16
16
|
import { ensureDependencies } from '../lib/deps.js';
|
|
@@ -39,13 +39,16 @@ export async function validate(options) {
|
|
|
39
39
|
console.log('');
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
// Step 2: Validate navigation
|
|
42
|
+
// Step 2: Validate navigation pages exist
|
|
43
43
|
if (result.config) {
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
44
|
+
const navWarnings = await validateNavigationPages(result.config, projectDir);
|
|
45
|
+
if (navWarnings.length > 0) {
|
|
46
46
|
console.log('');
|
|
47
|
-
output.warn('Missing navigation
|
|
48
|
-
|
|
47
|
+
output.warn('Missing navigation pages:');
|
|
48
|
+
for (const w of navWarnings) {
|
|
49
|
+
const detail = w.message ? ` — ${w.message}` : '';
|
|
50
|
+
console.log(` - ${w.file}${detail}`);
|
|
51
|
+
}
|
|
49
52
|
console.log('');
|
|
50
53
|
output.hint('Create these files or remove them from docs.json navigation');
|
|
51
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAuB,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAgBpD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAwB;IACrD,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAEjC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAC3C,CAAC;IAED,6BAA6B;IAC7B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;IAEjD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,CAAC,KAAK,CAAC,sBAAsB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAErC,uCAAuC;IACvC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,MAAa,CAAC,CAAC;QAC3D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;YACjE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,26 +6,48 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Used by both CLI (validate command) and build-service (build-time warnings).
|
|
8
8
|
*/
|
|
9
|
-
interface PageObject {
|
|
10
|
-
page?: string;
|
|
11
|
-
title?: string;
|
|
12
|
-
}
|
|
13
|
-
interface NavigationGroup {
|
|
14
|
-
group?: string;
|
|
15
|
-
pages?: (string | PageObject | NavigationGroup)[];
|
|
16
|
-
}
|
|
17
|
-
interface DocsConfig {
|
|
18
|
-
navigation?: NavigationGroup[];
|
|
19
|
-
}
|
|
20
9
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
10
|
+
* Structural copy of `BuildWarning` from ./status-reporter.ts. Kept local so
|
|
11
|
+
* this file can be copied byte-identically to all four tracked locations
|
|
12
|
+
* (build-service/shared, functions/src/shared, cli/vendored/shared,
|
|
13
|
+
* cli/src/lib) — the CLI src/lib copy has no sibling status-reporter.ts, and
|
|
14
|
+
* an `import type { BuildWarning } from './status-reporter.js'` would fail to
|
|
15
|
+
* resolve under the CLI's NodeNext tsconfig. TypeScript structural typing
|
|
16
|
+
* means callers in build-service/functions can still assign these to their
|
|
17
|
+
* imported BuildWarning[] without issue. Keep the shape in sync with
|
|
18
|
+
* builder/shared/status-reporter.ts.
|
|
23
19
|
*/
|
|
24
|
-
|
|
20
|
+
type BuildWarning = {
|
|
21
|
+
type: 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
|
|
22
|
+
file: string;
|
|
23
|
+
line?: number;
|
|
24
|
+
link?: string;
|
|
25
|
+
message?: string;
|
|
26
|
+
};
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
+
* Extract all local page paths referenced anywhere in a navigation config.
|
|
29
|
+
*
|
|
30
|
+
* Walks every nav container shape (groups, tabs, anchors, dropdowns,
|
|
31
|
+
* products, versions, languages, global) recursively. Skips external URLs.
|
|
32
|
+
* Deduplicates — a page referenced from multiple containers is returned once.
|
|
33
|
+
*
|
|
34
|
+
* Input may be the full `navigation` object from docs.json, or any nested
|
|
35
|
+
* container. Returns a sorted unique list.
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractNavigationPages(nav: unknown): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Boundary type for `validateNavigationPages` — any object with an optional
|
|
40
|
+
* `navigation` key. Callers' stricter `DocsConfig` types assign here.
|
|
41
|
+
*/
|
|
42
|
+
export type NavValidationInput = {
|
|
43
|
+
navigation?: unknown;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Validate that all pages referenced in docs.json navigation exist on disk
|
|
48
|
+
* as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
|
|
49
|
+
* merging into `reporter.reportCompletion({ warnings })`.
|
|
28
50
|
*/
|
|
29
|
-
export declare function
|
|
51
|
+
export declare function validateNavigationPages(config: NavValidationInput | null | undefined, projectDir: string): Promise<BuildWarning[]>;
|
|
30
52
|
export {};
|
|
31
53
|
//# sourceMappingURL=navigation-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-validator.d.ts","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,
|
|
1
|
+
{"version":3,"file":"navigation-validator.d.ts","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;GAUG;AACH,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,cAAc,CAAC;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA6BF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CA0C7D;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAElF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,EAC7C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAwBzB"}
|
|
@@ -21,55 +21,84 @@ async function fileExists(filePath) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Extract all page paths
|
|
25
|
-
*
|
|
24
|
+
* Extract all local page paths referenced anywhere in a navigation config.
|
|
25
|
+
*
|
|
26
|
+
* Walks every nav container shape (groups, tabs, anchors, dropdowns,
|
|
27
|
+
* products, versions, languages, global) recursively. Skips external URLs.
|
|
28
|
+
* Deduplicates — a page referenced from multiple containers is returned once.
|
|
29
|
+
*
|
|
30
|
+
* Input may be the full `navigation` object from docs.json, or any nested
|
|
31
|
+
* container. Returns a sorted unique list.
|
|
26
32
|
*/
|
|
27
|
-
export function extractNavigationPages(
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
export function extractNavigationPages(nav) {
|
|
34
|
+
if (!nav || typeof nav !== 'object')
|
|
35
|
+
return [];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
function addPage(value) {
|
|
38
|
+
if (typeof value !== 'string')
|
|
39
|
+
return;
|
|
40
|
+
if (value.startsWith('http://') || value.startsWith('https://'))
|
|
41
|
+
return;
|
|
42
|
+
seen.add(value);
|
|
43
|
+
}
|
|
44
|
+
function walkItems(items) {
|
|
45
|
+
if (!Array.isArray(items))
|
|
46
|
+
return;
|
|
30
47
|
for (const item of items) {
|
|
31
48
|
if (typeof item === 'string') {
|
|
32
|
-
|
|
33
|
-
if (!item.startsWith('http://') && !item.startsWith('https://')) {
|
|
34
|
-
pages.push(item);
|
|
35
|
-
}
|
|
49
|
+
addPage(item);
|
|
36
50
|
}
|
|
37
51
|
else if (item && typeof item === 'object') {
|
|
38
|
-
|
|
39
|
-
pages.push(item.page);
|
|
40
|
-
}
|
|
41
|
-
if ('pages' in item && Array.isArray(item.pages)) {
|
|
42
|
-
processPages(item.pages);
|
|
43
|
-
}
|
|
52
|
+
walkContainer(item);
|
|
44
53
|
}
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
}
|
|
56
|
+
function walkContainer(container) {
|
|
57
|
+
if (!container || typeof container !== 'object')
|
|
58
|
+
return;
|
|
59
|
+
// `page` property on an item (e.g., { page: "foo", title: "..." })
|
|
60
|
+
if ('page' in container)
|
|
61
|
+
addPage(container.page);
|
|
62
|
+
walkItems(container.pages);
|
|
63
|
+
walkItems(container.groups);
|
|
64
|
+
walkItems(container.tabs);
|
|
65
|
+
walkItems(container.anchors);
|
|
66
|
+
walkItems(container.dropdowns);
|
|
67
|
+
walkItems(container.products);
|
|
68
|
+
walkItems(container.versions);
|
|
69
|
+
walkItems(container.languages);
|
|
70
|
+
if (container.global)
|
|
71
|
+
walkContainer(container.global);
|
|
51
72
|
}
|
|
52
|
-
|
|
73
|
+
walkContainer(nav);
|
|
74
|
+
return Array.from(seen).sort();
|
|
53
75
|
}
|
|
54
76
|
/**
|
|
55
|
-
* Validate that all navigation
|
|
56
|
-
*
|
|
77
|
+
* Validate that all pages referenced in docs.json navigation exist on disk
|
|
78
|
+
* as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
|
|
79
|
+
* merging into `reporter.reportCompletion({ warnings })`.
|
|
57
80
|
*/
|
|
58
|
-
export async function
|
|
59
|
-
if (!config
|
|
81
|
+
export async function validateNavigationPages(config, projectDir) {
|
|
82
|
+
if (!config || typeof config !== 'object')
|
|
60
83
|
return [];
|
|
61
|
-
}
|
|
62
84
|
const pagePaths = extractNavigationPages(config.navigation);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
85
|
+
if (pagePaths.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
// Short-circuits to `.md` only if `.mdx` doesn't exist, avoiding a second
|
|
88
|
+
// stat when the first hits.
|
|
89
|
+
const results = await Promise.all(pagePaths.map(async (pagePath) => {
|
|
90
|
+
const mdxExists = await fileExists(path.join(projectDir, `${pagePath}.mdx`));
|
|
91
|
+
if (mdxExists)
|
|
92
|
+
return { pagePath, exists: true };
|
|
93
|
+
const mdExists = await fileExists(path.join(projectDir, `${pagePath}.md`));
|
|
94
|
+
return { pagePath, exists: mdExists };
|
|
95
|
+
}));
|
|
96
|
+
return results
|
|
97
|
+
.filter((r) => !r.exists)
|
|
98
|
+
.map(({ pagePath }) => ({
|
|
99
|
+
type: 'missing_page',
|
|
100
|
+
file: `${pagePath}.mdx`,
|
|
101
|
+
message: `Page "${pagePath}" is referenced in navigation but no ${pagePath}.mdx or ${pagePath}.md file exists`,
|
|
102
|
+
}));
|
|
74
103
|
}
|
|
75
104
|
//# sourceMappingURL=navigation-validator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-validator.js","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"navigation-validator.js","sourceRoot":"","sources":["../../src/lib/navigation-validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.26",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -214,9 +214,13 @@ export interface TabConfig {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
/**
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
217
|
+
* Legacy anchor configuration (top-level navigation sections).
|
|
218
|
+
*
|
|
219
|
+
* Retained so NavigationConfig / DropdownConfig / ProductConfig /
|
|
220
|
+
* VersionConfig / LanguageConfig can continue to type customer configs
|
|
221
|
+
* that still carry the legacy shape. New configs should use TabConfig
|
|
222
|
+
* for internal sections and top-level ExternalAnchorConfig for external
|
|
223
|
+
* links; `validateConfig` rejects `navigation.anchors` at build time.
|
|
220
224
|
*/
|
|
221
225
|
export interface AnchorConfig {
|
|
222
226
|
anchor: string;
|
|
@@ -792,9 +796,15 @@ export interface DocsConfig {
|
|
|
792
796
|
// Required fields
|
|
793
797
|
theme: ThemeName;
|
|
794
798
|
name: string;
|
|
795
|
-
colors: ColorsConfig;
|
|
796
799
|
navigation: NavigationConfig;
|
|
797
800
|
|
|
801
|
+
// Boundary escape hatch — docs.json is user-authored and may carry fields
|
|
802
|
+
// not represented in this interface. Unknown keys read as `unknown`.
|
|
803
|
+
[key: string]: unknown;
|
|
804
|
+
|
|
805
|
+
// Optional — themes provide defaults at render time
|
|
806
|
+
colors?: ColorsConfig;
|
|
807
|
+
|
|
798
808
|
// Optional fields
|
|
799
809
|
description?: string;
|
|
800
810
|
favicon?: Favicon;
|
|
@@ -12,7 +12,7 @@ import type { DocsConfig } from './docs-types.js';
|
|
|
12
12
|
export interface ExtractedHighlights {
|
|
13
13
|
siteName: string;
|
|
14
14
|
theme: 'jam' | 'nebula' | 'pulsar';
|
|
15
|
-
primaryColor
|
|
15
|
+
primaryColor?: string;
|
|
16
16
|
seoIndexable: boolean;
|
|
17
17
|
analyticsIntegrations: string[];
|
|
18
18
|
apiPlaygroundType: 'interactive' | 'simple' | 'hidden' | null;
|
|
@@ -118,7 +118,7 @@ export function extractConfigHighlights(config: DocsConfig, pageCount: number =
|
|
|
118
118
|
return {
|
|
119
119
|
siteName: config.name,
|
|
120
120
|
theme: config.theme,
|
|
121
|
-
primaryColor: config.colors
|
|
121
|
+
primaryColor: config.colors?.primary,
|
|
122
122
|
seoIndexable,
|
|
123
123
|
analyticsIntegrations,
|
|
124
124
|
apiPlaygroundType,
|
|
@@ -19,7 +19,7 @@ const Ajv = (AjvModule as any).default || AjvModule;
|
|
|
19
19
|
import JSON5 from 'json5';
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
import path from 'path';
|
|
22
|
-
import {
|
|
22
|
+
import { BUILD_SERVICE_DIR } from './paths.js';
|
|
23
23
|
|
|
24
24
|
// Types
|
|
25
25
|
export interface DocsConfig {
|
|
@@ -76,8 +76,11 @@ export async function getSchema(): Promise<Schema | null> {
|
|
|
76
76
|
return cachedSchema;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Try to load bundled schema first
|
|
80
|
-
|
|
79
|
+
// Try to load bundled schema first. Resolve from BUILD_SERVICE_DIR, not
|
|
80
|
+
// LIB_DIR — in compiled CJS `__dirname` is `dist/lib`, so the old
|
|
81
|
+
// `LIB_DIR/../schema` path pointed at `dist/schema` (doesn't exist) and
|
|
82
|
+
// Cloud Run silently fell back to fetching the live marketing schema.
|
|
83
|
+
const bundledSchemaPath = path.join(BUILD_SERVICE_DIR, 'schema/docs-schema.json');
|
|
81
84
|
if (fs.existsSync(bundledSchemaPath)) {
|
|
82
85
|
const schemaContent = fs.readFileSync(bundledSchemaPath, 'utf-8');
|
|
83
86
|
cachedSchema = JSON.parse(schemaContent) as Schema;
|
|
@@ -117,6 +120,97 @@ export function parseConfig(content: string): DocsConfig {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Filter out noise from anyOf validation errors.
|
|
125
|
+
*
|
|
126
|
+
* The master schema uses a 3-branch anyOf (one per theme) and Ajv emits errors
|
|
127
|
+
* from every branch it tried, producing messages like `must be "nebula"` when
|
|
128
|
+
* the user typed `theme: "jam"`. This helper:
|
|
129
|
+
*
|
|
130
|
+
* 1. Groups errors by their `#/anyOf/<N>` branch.
|
|
131
|
+
* 2. Finds the matching branch by the absence of a `/theme` const error
|
|
132
|
+
* (a passing branch can't emit a theme mismatch).
|
|
133
|
+
* 3. Drops all `/theme` const errors (just noise from wrong branches).
|
|
134
|
+
* 4. Drops all top-level `anyOf` meta-errors (never actionable).
|
|
135
|
+
* 5. Keeps only errors from the matching branch, plus errors not anchored
|
|
136
|
+
* to any anyOf branch (e.g., `#/definitions/...`).
|
|
137
|
+
* 6. Deduplicates by (instancePath, keyword, params).
|
|
138
|
+
*
|
|
139
|
+
* Ported from `builder/cli/src/lib/docs-config.ts`, which has used this logic
|
|
140
|
+
* in production since the CLI shipped. The `filterAnyOfNoise` bodies match;
|
|
141
|
+
* `formatValidationErrors` deliberately differs — build-service adds an
|
|
142
|
+
* `effectiveErrors` fallback the CLI version doesn't need.
|
|
143
|
+
*/
|
|
144
|
+
export function filterAnyOfNoise(errors: ErrorObject[]): ErrorObject[] {
|
|
145
|
+
// First pass: identify the correct theme branch based on which branch
|
|
146
|
+
// does NOT have a const error for /theme
|
|
147
|
+
const branchErrors = new Map<string, ErrorObject[]>();
|
|
148
|
+
for (const err of errors) {
|
|
149
|
+
const match = err.schemaPath.match(/#\/anyOf\/(\d+)/);
|
|
150
|
+
if (match) {
|
|
151
|
+
const branch = match[1];
|
|
152
|
+
if (!branchErrors.has(branch)) {
|
|
153
|
+
branchErrors.set(branch, []);
|
|
154
|
+
}
|
|
155
|
+
branchErrors.get(branch)!.push(err);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Find the branch that matches the user's theme (no theme const error)
|
|
160
|
+
let matchingBranch: string | null = null;
|
|
161
|
+
for (const [branch, errs] of branchErrors) {
|
|
162
|
+
const hasThemeConstError = errs.some((e) =>
|
|
163
|
+
e.keyword === 'const' && e.instancePath === '/theme',
|
|
164
|
+
);
|
|
165
|
+
if (!hasThemeConstError) {
|
|
166
|
+
matchingBranch = branch;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Filter to only show errors from the matching branch
|
|
172
|
+
// Also filter out generic "anyOf" errors which are not actionable
|
|
173
|
+
const filtered = errors.filter((err) => {
|
|
174
|
+
// Always filter out theme const errors (just noise from wrong branches)
|
|
175
|
+
if (err.keyword === 'const' && err.instancePath === '/theme') {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Filter out generic anyOf errors (not actionable, user needs specific errors)
|
|
180
|
+
if (err.keyword === 'anyOf') {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If we identified a matching branch, only show errors from that branch
|
|
185
|
+
// (or errors not from any anyOf branch)
|
|
186
|
+
if (matchingBranch !== null) {
|
|
187
|
+
const match = err.schemaPath.match(/#\/anyOf\/(\d+)/);
|
|
188
|
+
if (match && match[1] !== matchingBranch) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return true;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return deduplicateErrors(filtered);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Remove duplicate errors that share path + keyword + params.
|
|
201
|
+
*/
|
|
202
|
+
function deduplicateErrors(errors: ErrorObject[]): ErrorObject[] {
|
|
203
|
+
const seen = new Set<string>();
|
|
204
|
+
return errors.filter((err) => {
|
|
205
|
+
const key = `${err.instancePath}|${err.keyword}|${JSON.stringify(err.params)}`;
|
|
206
|
+
if (seen.has(key)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
seen.add(key);
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
120
214
|
/**
|
|
121
215
|
* Format validation errors into user-friendly messages
|
|
122
216
|
*/
|
|
@@ -124,8 +218,16 @@ export function formatValidationErrors(errors: ErrorObject[] | null | undefined)
|
|
|
124
218
|
if (!errors || errors.length === 0) {
|
|
125
219
|
return 'Unknown validation error';
|
|
126
220
|
}
|
|
127
|
-
|
|
128
|
-
|
|
221
|
+
|
|
222
|
+
// Fall back to raw errors if the filter empties the list — prevents
|
|
223
|
+
// degrading to "Unknown validation error" for a config Ajv rejected.
|
|
224
|
+
// Safe only because validateConfig pre-checks `config.theme` before Ajv,
|
|
225
|
+
// so a fully-noise error list is unreachable in practice. A new caller
|
|
226
|
+
// that skips the theme pre-check would break this invariant.
|
|
227
|
+
const filteredErrors = filterAnyOfNoise(errors);
|
|
228
|
+
const effectiveErrors = filteredErrors.length > 0 ? filteredErrors : errors;
|
|
229
|
+
|
|
230
|
+
const messages = effectiveErrors.slice(0, 3).map(err => {
|
|
129
231
|
const path = err.instancePath || 'root';
|
|
130
232
|
|
|
131
233
|
switch (err.keyword) {
|
|
@@ -150,10 +252,10 @@ export function formatValidationErrors(errors: ErrorObject[] | null | undefined)
|
|
|
150
252
|
}
|
|
151
253
|
});
|
|
152
254
|
|
|
153
|
-
if (
|
|
154
|
-
messages.push(`...and ${
|
|
255
|
+
if (effectiveErrors.length > 3) {
|
|
256
|
+
messages.push(`...and ${effectiveErrors.length - 3} more error(s)`);
|
|
155
257
|
}
|
|
156
|
-
|
|
258
|
+
|
|
157
259
|
return messages.join('. ');
|
|
158
260
|
}
|
|
159
261
|
|
|
@@ -210,9 +312,13 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
|
|
|
210
312
|
};
|
|
211
313
|
}
|
|
212
314
|
|
|
213
|
-
// Validate navigation has content
|
|
315
|
+
// Validate navigation has content. navigation.anchors is a deprecated
|
|
316
|
+
// field that we reject below, but it still counts as "has content" for
|
|
317
|
+
// this gate so legacy configs get the precise migration error rather
|
|
318
|
+
// than the generic "must have at least one of" message.
|
|
214
319
|
const nav = config.navigation;
|
|
215
|
-
const
|
|
320
|
+
const legacyAnchors = (nav as { anchors?: unknown }).anchors;
|
|
321
|
+
const hasContent = legacyAnchors || nav.tabs || nav.groups || nav.pages ||
|
|
216
322
|
nav.products || nav.dropdowns || nav.versions || nav.languages;
|
|
217
323
|
if (!hasContent) {
|
|
218
324
|
return {
|
|
@@ -220,7 +326,7 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
|
|
|
220
326
|
error: 'Invalid docs.json: Navigation must have at least one of: anchors, tabs, groups, pages, products, dropdowns, versions, or languages.',
|
|
221
327
|
};
|
|
222
328
|
}
|
|
223
|
-
|
|
329
|
+
|
|
224
330
|
// Validate theme
|
|
225
331
|
const validThemes = ['jam', 'nebula', 'pulsar'];
|
|
226
332
|
if (config.theme && !validThemes.includes(config.theme)) {
|
|
@@ -230,8 +336,7 @@ export async function validateConfig(configPath: string, docsPath?: string): Pro
|
|
|
230
336
|
};
|
|
231
337
|
}
|
|
232
338
|
|
|
233
|
-
|
|
234
|
-
if (nav.anchors) {
|
|
339
|
+
if (legacyAnchors) {
|
|
235
340
|
return {
|
|
236
341
|
valid: false,
|
|
237
342
|
error: 'Invalid docs.json: "anchors" is not supported within "navigation". Use "tabs" instead. Run `jamdesk migrate` to update your docs.json.',
|
|
@@ -1604,7 +1604,6 @@
|
|
|
1604
1604
|
"required": [
|
|
1605
1605
|
"theme",
|
|
1606
1606
|
"name",
|
|
1607
|
-
"colors",
|
|
1608
1607
|
"navigation"
|
|
1609
1608
|
],
|
|
1610
1609
|
"additionalProperties": false
|
|
@@ -1726,7 +1725,6 @@
|
|
|
1726
1725
|
"required": [
|
|
1727
1726
|
"theme",
|
|
1728
1727
|
"name",
|
|
1729
|
-
"colors",
|
|
1730
1728
|
"navigation"
|
|
1731
1729
|
],
|
|
1732
1730
|
"additionalProperties": false
|
|
@@ -1848,7 +1846,6 @@
|
|
|
1848
1846
|
"required": [
|
|
1849
1847
|
"theme",
|
|
1850
1848
|
"name",
|
|
1851
|
-
"colors",
|
|
1852
1849
|
"navigation"
|
|
1853
1850
|
],
|
|
1854
1851
|
"additionalProperties": false
|
|
@@ -10,18 +10,38 @@
|
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Structural copy of `BuildWarning` from ./status-reporter.ts. Kept local so
|
|
15
|
+
* this file can be copied byte-identically to all four tracked locations
|
|
16
|
+
* (build-service/shared, functions/src/shared, cli/vendored/shared,
|
|
17
|
+
* cli/src/lib) — the CLI src/lib copy has no sibling status-reporter.ts, and
|
|
18
|
+
* an `import type { BuildWarning } from './status-reporter.js'` would fail to
|
|
19
|
+
* resolve under the CLI's NodeNext tsconfig. TypeScript structural typing
|
|
20
|
+
* means callers in build-service/functions can still assign these to their
|
|
21
|
+
* imported BuildWarning[] without issue. Keep the shape in sync with
|
|
22
|
+
* builder/shared/status-reporter.ts.
|
|
23
|
+
*/
|
|
24
|
+
type BuildWarning = {
|
|
25
|
+
type: 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
|
|
26
|
+
file: string;
|
|
27
|
+
line?: number;
|
|
28
|
+
link?: string;
|
|
29
|
+
message?: string;
|
|
30
|
+
};
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Minimal shape for anything that can nest pages.
|
|
34
|
+
*/
|
|
35
|
+
interface NavContainer {
|
|
36
|
+
pages?: unknown[];
|
|
37
|
+
groups?: unknown[];
|
|
38
|
+
tabs?: unknown[];
|
|
39
|
+
anchors?: unknown[];
|
|
40
|
+
dropdowns?: unknown[];
|
|
41
|
+
products?: unknown[];
|
|
42
|
+
versions?: unknown[];
|
|
43
|
+
languages?: unknown[];
|
|
44
|
+
global?: NavContainer;
|
|
25
45
|
}
|
|
26
46
|
|
|
27
47
|
/**
|
|
@@ -37,65 +57,95 @@ async function fileExists(filePath: string): Promise<boolean> {
|
|
|
37
57
|
}
|
|
38
58
|
|
|
39
59
|
/**
|
|
40
|
-
* Extract all page paths
|
|
41
|
-
*
|
|
60
|
+
* Extract all local page paths referenced anywhere in a navigation config.
|
|
61
|
+
*
|
|
62
|
+
* Walks every nav container shape (groups, tabs, anchors, dropdowns,
|
|
63
|
+
* products, versions, languages, global) recursively. Skips external URLs.
|
|
64
|
+
* Deduplicates — a page referenced from multiple containers is returned once.
|
|
65
|
+
*
|
|
66
|
+
* Input may be the full `navigation` object from docs.json, or any nested
|
|
67
|
+
* container. Returns a sorted unique list.
|
|
42
68
|
*/
|
|
43
|
-
export function extractNavigationPages(
|
|
44
|
-
|
|
69
|
+
export function extractNavigationPages(nav: unknown): string[] {
|
|
70
|
+
if (!nav || typeof nav !== 'object') return [];
|
|
71
|
+
|
|
72
|
+
const seen = new Set<string>();
|
|
73
|
+
|
|
74
|
+
function addPage(value: unknown): void {
|
|
75
|
+
if (typeof value !== 'string') return;
|
|
76
|
+
if (value.startsWith('http://') || value.startsWith('https://')) return;
|
|
77
|
+
seen.add(value);
|
|
78
|
+
}
|
|
45
79
|
|
|
46
|
-
function
|
|
80
|
+
function walkItems(items: unknown): void {
|
|
81
|
+
if (!Array.isArray(items)) return;
|
|
47
82
|
for (const item of items) {
|
|
48
83
|
if (typeof item === 'string') {
|
|
49
|
-
|
|
50
|
-
if (!item.startsWith('http://') && !item.startsWith('https://')) {
|
|
51
|
-
pages.push(item);
|
|
52
|
-
}
|
|
84
|
+
addPage(item);
|
|
53
85
|
} else if (item && typeof item === 'object') {
|
|
54
|
-
|
|
55
|
-
pages.push(item.page);
|
|
56
|
-
}
|
|
57
|
-
if ('pages' in item && Array.isArray(item.pages)) {
|
|
58
|
-
processPages(item.pages);
|
|
59
|
-
}
|
|
86
|
+
walkContainer(item as NavContainer);
|
|
60
87
|
}
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
90
|
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
}
|
|
91
|
+
function walkContainer(container: NavContainer): void {
|
|
92
|
+
if (!container || typeof container !== 'object') return;
|
|
93
|
+
|
|
94
|
+
// `page` property on an item (e.g., { page: "foo", title: "..." })
|
|
95
|
+
if ('page' in container) addPage((container as { page?: unknown }).page);
|
|
96
|
+
|
|
97
|
+
walkItems(container.pages);
|
|
98
|
+
walkItems(container.groups);
|
|
99
|
+
walkItems(container.tabs);
|
|
100
|
+
walkItems(container.anchors);
|
|
101
|
+
walkItems(container.dropdowns);
|
|
102
|
+
walkItems(container.products);
|
|
103
|
+
walkItems(container.versions);
|
|
104
|
+
walkItems(container.languages);
|
|
105
|
+
if (container.global) walkContainer(container.global);
|
|
68
106
|
}
|
|
69
107
|
|
|
70
|
-
|
|
108
|
+
walkContainer(nav as NavContainer);
|
|
109
|
+
|
|
110
|
+
return Array.from(seen).sort();
|
|
71
111
|
}
|
|
72
112
|
|
|
73
113
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
114
|
+
* Boundary type for `validateNavigationPages` — any object with an optional
|
|
115
|
+
* `navigation` key. Callers' stricter `DocsConfig` types assign here.
|
|
76
116
|
*/
|
|
77
|
-
export
|
|
78
|
-
config: DocsConfig,
|
|
79
|
-
projectDir: string
|
|
80
|
-
): Promise<string[]> {
|
|
81
|
-
if (!config.navigation || !Array.isArray(config.navigation)) {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const pagePaths = extractNavigationPages(config.navigation);
|
|
86
|
-
const missing: string[] = [];
|
|
117
|
+
export type NavValidationInput = { navigation?: unknown; [key: string]: unknown };
|
|
87
118
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Validate that all pages referenced in docs.json navigation exist on disk
|
|
121
|
+
* as either `.mdx` or `.md` files. Returns a BuildWarning[] suitable for
|
|
122
|
+
* merging into `reporter.reportCompletion({ warnings })`.
|
|
123
|
+
*/
|
|
124
|
+
export async function validateNavigationPages(
|
|
125
|
+
config: NavValidationInput | null | undefined,
|
|
126
|
+
projectDir: string,
|
|
127
|
+
): Promise<BuildWarning[]> {
|
|
128
|
+
if (!config || typeof config !== 'object') return [];
|
|
91
129
|
|
|
92
|
-
|
|
93
|
-
|
|
130
|
+
const pagePaths = extractNavigationPages(config.navigation);
|
|
131
|
+
if (pagePaths.length === 0) return [];
|
|
94
132
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
133
|
+
// Short-circuits to `.md` only if `.mdx` doesn't exist, avoiding a second
|
|
134
|
+
// stat when the first hits.
|
|
135
|
+
const results = await Promise.all(
|
|
136
|
+
pagePaths.map(async (pagePath) => {
|
|
137
|
+
const mdxExists = await fileExists(path.join(projectDir, `${pagePath}.mdx`));
|
|
138
|
+
if (mdxExists) return { pagePath, exists: true };
|
|
139
|
+
const mdExists = await fileExists(path.join(projectDir, `${pagePath}.md`));
|
|
140
|
+
return { pagePath, exists: mdExists };
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
99
143
|
|
|
100
|
-
return
|
|
144
|
+
return results
|
|
145
|
+
.filter((r) => !r.exists)
|
|
146
|
+
.map(({ pagePath }) => ({
|
|
147
|
+
type: 'missing_page',
|
|
148
|
+
file: `${pagePath}.mdx`,
|
|
149
|
+
message: `Page "${pagePath}" is referenced in navigation but no ${pagePath}.mdx or ${pagePath}.md file exists`,
|
|
150
|
+
}));
|
|
101
151
|
}
|
|
@@ -22,7 +22,7 @@ export interface ProgressUpdate {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** Warning types that can occur during builds (non-blocking) */
|
|
25
|
-
export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset';
|
|
25
|
+
export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page';
|
|
26
26
|
|
|
27
27
|
/** Build warning structure */
|
|
28
28
|
export interface BuildWarning {
|