@f5-sales-demo/docs-theme 4.4.7 → 4.4.9
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/canonical-route-middleware.ts +3 -2
- package/components/ProviderReference.astro +4 -1
- package/config.ts +6 -1
- package/package.json +1 -1
- package/src/utils/availability.test.ts +25 -0
- package/src/utils/canonical-provider.ts +19 -2
- package/src/utils/compact-scopes.mjs +20 -0
- package/src/utils/compact-scopes.test.ts +22 -0
- package/src/utils/provider-sections.ts +17 -0
- package/src/utils/share-provider-assets.mjs +3 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
|
|
3
3
|
import type { ProviderPage } from './src/utils/canonical-provider.ts';
|
|
4
|
-
import { collectionNavigation
|
|
4
|
+
import { collectionNavigation } from './src/utils/canonical-provider.ts';
|
|
5
|
+
import { stagedProviderSections } from './src/utils/provider-sections.ts';
|
|
5
6
|
|
|
6
7
|
let providerNavigation: { collections: Record<string, ProviderPage[]> };
|
|
7
8
|
export const onRequest = defineRouteMiddleware(async (context, next) => {
|
|
@@ -12,7 +13,7 @@ export const onRequest = defineRouteMiddleware(async (context, next) => {
|
|
|
12
13
|
providerNavigation ||= JSON.parse(fs.readFileSync(process.env.PROVIDER_NAVIGATION || '', 'utf8'));
|
|
13
14
|
const metadata = entry.data.xcsh_docs;
|
|
14
15
|
const base = import.meta.env.BASE_URL;
|
|
15
|
-
const sections =
|
|
16
|
+
const sections = stagedProviderSections().map((section) => ({
|
|
16
17
|
type: 'link' as const,
|
|
17
18
|
label: section.label,
|
|
18
19
|
href: `${base.replace(/\/$/, '')}/${section.path}/`,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { CardGrid } from '@astrojs/starlight/components';
|
|
3
|
-
import {
|
|
3
|
+
import { stagedProviderSections } from '../src/utils/provider-sections';
|
|
4
|
+
|
|
5
|
+
const providerSections = stagedProviderSections();
|
|
6
|
+
|
|
4
7
|
import LinkCard from './LinkCard.astro';
|
|
5
8
|
|
|
6
9
|
const { entry } = Astro.locals.starlightRoute;
|
package/config.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
import { sidebarTranslations } from './src/i18n/translations.ts';
|
|
33
33
|
import remarkMermaid from './src/plugins/remark-mermaid.mjs';
|
|
34
34
|
import { providerMegaMenu } from './src/utils/canonical-provider.ts';
|
|
35
|
+
import { stagedProviderSections } from './src/utils/provider-sections.ts';
|
|
35
36
|
import { resolveMegaMenuIcon } from './src/utils/resolve-icon.ts';
|
|
36
37
|
import providerSharedAssets from './src/utils/share-provider-assets.mjs';
|
|
37
38
|
import { buildSubcategorySidebar } from './src/utils/subcategory-sidebar.ts';
|
|
@@ -712,7 +713,11 @@ export function createF5xcDocsConfig(options: F5xcDocsConfigOptions = {}) {
|
|
|
712
713
|
console.warn('[docs-theme] OPENAPI_SPECS_CONFIG contains invalid JSON; skipping OpenAPI plugin.', e);
|
|
713
714
|
}
|
|
714
715
|
}
|
|
715
|
-
const megaMenuItems =
|
|
716
|
+
const megaMenuItems =
|
|
717
|
+
options.megaMenuItems ||
|
|
718
|
+
(canonicalProvider
|
|
719
|
+
? providerMegaMenu(base, stagedProviderSections(canonicalProvider.manifest))
|
|
720
|
+
: defaultMegaMenuItems);
|
|
716
721
|
const head = options.head || (canonicalProvider ? [] : defaultHead);
|
|
717
722
|
const logo = options.logo || { src: '@f5-sales-demo/docs-theme/assets/f5-distributed-cloud.svg' };
|
|
718
723
|
const additionalRemarkPlugins = options.additionalRemarkPlugins || [];
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
import { availableProviderSections } from './canonical-provider';
|
|
3
|
+
|
|
4
|
+
it('omits missing historical setup while retaining existing type families', () => {
|
|
5
|
+
const paths = [
|
|
6
|
+
'documentation/resources/demo/index.md',
|
|
7
|
+
'documentation/data-sources/demo/index.md',
|
|
8
|
+
'documentation/actions/demo/index.md',
|
|
9
|
+
'documentation/ephemeral-resources/demo/index.md',
|
|
10
|
+
'documentation/guides/example/index.md',
|
|
11
|
+
];
|
|
12
|
+
expect(availableProviderSections(paths).map((section) => section.path)).toEqual([
|
|
13
|
+
'guides',
|
|
14
|
+
'resources',
|
|
15
|
+
'data-sources',
|
|
16
|
+
'actions',
|
|
17
|
+
'ephemeral-resources',
|
|
18
|
+
]);
|
|
19
|
+
expect(availableProviderSections([...paths, 'documentation/provider/setup/index.md'])[0].path).toBe('provider/setup');
|
|
20
|
+
});
|
|
21
|
+
it('does not infer setup from a different provider page or an unrelated folder', () => {
|
|
22
|
+
expect(
|
|
23
|
+
availableProviderSections(['documentation/provider/index.md', 'documentation/resources-other/example.md']),
|
|
24
|
+
).toEqual([]);
|
|
25
|
+
});
|
|
@@ -8,6 +8,23 @@ export const providerSections = [
|
|
|
8
8
|
{ label: 'Ephemeral resources', path: 'ephemeral-resources', icon: 'random' },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
|
+
export function availableProviderSections(paths: string[]) {
|
|
12
|
+
const slugs = paths
|
|
13
|
+
.filter((path) => path.startsWith('documentation/') && /\.mdx?$/.test(path))
|
|
14
|
+
.map((path) =>
|
|
15
|
+
path
|
|
16
|
+
.slice('documentation/'.length)
|
|
17
|
+
.replace(/(^|\/)index\.mdx?$/, '')
|
|
18
|
+
.replace(/\.mdx?$/, '')
|
|
19
|
+
.replace(/\/$/, ''),
|
|
20
|
+
);
|
|
21
|
+
return providerSections.filter((section) =>
|
|
22
|
+
section.path === 'provider/setup'
|
|
23
|
+
? slugs.includes(section.path)
|
|
24
|
+
: slugs.some((slug) => slug === section.path || slug.startsWith(`${section.path}/`)),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
11
28
|
export interface ProviderPage {
|
|
12
29
|
id: string;
|
|
13
30
|
collection_id: string;
|
|
@@ -54,7 +71,7 @@ export function collectionNavigation(pages: ProviderPage[], collection: string,
|
|
|
54
71
|
return (children.get(null) || []).map((page) => tree(page));
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
export function providerMegaMenu(base: string) {
|
|
74
|
+
export function providerMegaMenu(base: string, sections = providerSections) {
|
|
58
75
|
return [
|
|
59
76
|
{
|
|
60
77
|
label: 'Provider documentation',
|
|
@@ -64,7 +81,7 @@ export function providerMegaMenu(base: string) {
|
|
|
64
81
|
categories: [
|
|
65
82
|
{
|
|
66
83
|
title: 'Terraform xcsh',
|
|
67
|
-
items:
|
|
84
|
+
items: sections.map((section) => ({
|
|
68
85
|
label: section.label,
|
|
69
86
|
href: `${base.replace(/\/$/, '')}/${section.path}/`,
|
|
70
87
|
})),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export async function compactProviderScopes(root) {
|
|
4
|
+
const files = (await readdir(root, { recursive: true })).filter((name) => /\.(html|css|js)$/.test(name));
|
|
5
|
+
const names = new Set();
|
|
6
|
+
for (const file of files) {
|
|
7
|
+
const text = await readFile(join(root, file), 'utf8');
|
|
8
|
+
for (const match of text.matchAll(/\bastro-[a-z0-9]{8}\b/g)) names.add(match[0]);
|
|
9
|
+
}
|
|
10
|
+
const aliases = new Map([...names].sort().map((name, index) => [name, `f5p${index.toString(36)}`]));
|
|
11
|
+
let savedBytes = 0;
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
const path = join(root, file);
|
|
14
|
+
const text = await readFile(path, 'utf8');
|
|
15
|
+
const compact = text.replace(/\bastro-[a-z0-9]{8}\b/g, (name) => aliases.get(name));
|
|
16
|
+
savedBytes += Buffer.byteLength(text) - Buffer.byteLength(compact);
|
|
17
|
+
if (text !== compact) await writeFile(path, compact);
|
|
18
|
+
}
|
|
19
|
+
return { aliases: aliases.size, savedBytes };
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { expect, it } from 'vitest';
|
|
5
|
+
import { compactProviderScopes } from './compact-scopes.mjs';
|
|
6
|
+
|
|
7
|
+
it('keeps generated scope selectors consistent across HTML CSS and JS', async () => {
|
|
8
|
+
const root = await mkdtemp(join(tmpdir(), 'provider-scope-'));
|
|
9
|
+
try {
|
|
10
|
+
await writeFile(join(root, 'page.html'), '<div class="astro-abcdefgh">Reference</div>');
|
|
11
|
+
await writeFile(join(root, 'page.css'), '.astro-abcdefgh{color:red}');
|
|
12
|
+
await writeFile(join(root, 'page.js'), 'document.querySelector(".astro-abcdefgh")');
|
|
13
|
+
const result = await compactProviderScopes(root);
|
|
14
|
+
expect(result.aliases).toBe(1);
|
|
15
|
+
expect(await readFile(join(root, 'page.html'), 'utf8')).toBe('<div class="f5p0">Reference</div>');
|
|
16
|
+
expect(await readFile(join(root, 'page.css'), 'utf8')).toBe('.f5p0{color:red}');
|
|
17
|
+
expect(await readFile(join(root, 'page.js'), 'utf8')).toContain('.f5p0');
|
|
18
|
+
expect((await compactProviderScopes(root)).savedBytes).toBe(0);
|
|
19
|
+
} finally {
|
|
20
|
+
await rm(root, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { availableProviderSections } from './canonical-provider.ts';
|
|
3
|
+
|
|
4
|
+
const sectionsByManifest = new Map<string, ReturnType<typeof availableProviderSections>>();
|
|
5
|
+
export function stagedProviderSections(manifest = process.env.CANONICAL_MANIFEST || '') {
|
|
6
|
+
if (!manifest) throw new Error('Canonical provider navigation requires a manifest');
|
|
7
|
+
let sections = sectionsByManifest.get(manifest);
|
|
8
|
+
if (!sections) {
|
|
9
|
+
const data = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
10
|
+
if (!data.files || typeof data.files !== 'object' || Array.isArray(data.files)) {
|
|
11
|
+
throw new Error('Canonical provider manifest requires file receipts');
|
|
12
|
+
}
|
|
13
|
+
sections = availableProviderSections(Object.keys(data.files));
|
|
14
|
+
sectionsByManifest.set(manifest, sections);
|
|
15
|
+
}
|
|
16
|
+
return sections;
|
|
17
|
+
}
|
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join, posix } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { compactProviderScopes } from './compact-scopes.mjs';
|
|
5
6
|
|
|
6
7
|
const hash = (value) => createHash('sha256').update(value).digest('hex').slice(0, 24);
|
|
7
8
|
// Input is Astro-generated HTML. Keep each asset's original execution position
|
|
@@ -69,7 +70,8 @@ export async function shareProviderAssets(root, base = '/') {
|
|
|
69
70
|
await writeFile(join(assetRoot, name), body);
|
|
70
71
|
assetBytes += Buffer.byteLength(body);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
const scopes = await compactProviderScopes(root);
|
|
74
|
+
return { pages: files.length, assets: assets.size, savedBytes: saved - assetBytes + scopes.savedBytes };
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
export default function providerSharedAssets() {
|