@f5-sales-demo/docs-theme 4.4.2 → 4.4.3

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.
@@ -0,0 +1,6 @@
1
+ ---
2
+ import ProviderSidebarTree from './ProviderSidebarTree.astro';
3
+
4
+ const { sidebar } = Astro.locals.starlightRoute;
5
+ ---
6
+ <nav aria-label="Provider reference"><ProviderSidebarTree entries={sidebar} /></nav>
@@ -0,0 +1,21 @@
1
+ ---
2
+ import type { StarlightRouteData } from '@astrojs/starlight/route-data';
3
+
4
+ interface Props {
5
+ entries: StarlightRouteData['sidebar'];
6
+ }
7
+ const { entries } = Astro.props;
8
+ ---
9
+ <ul>{entries.map(entry => <li>{entry.type === 'link'
10
+ ? <a href={entry.href} aria-current={entry.isCurrent ? 'page' : undefined}>{entry.label}</a>
11
+ : <details open={!entry.collapsed}><summary>{entry.label}</summary><Astro.self entries={entry.entries} /></details>}
12
+ </li>)}</ul>
13
+ <style>
14
+ ul { list-style: none; padding: 0; margin: 0; }
15
+ ul ul { padding-inline-start: .75rem; border-inline-start: 1px solid var(--sl-color-gray-5); }
16
+ li { margin: .3rem 0; overflow-wrap: anywhere; }
17
+ a { display: block; padding: .2rem .4rem; color: var(--sl-color-gray-2); text-decoration: none; font-size: var(--sl-text-sm); }
18
+ a:hover { color: var(--sl-color-white); }
19
+ a[aria-current] { background: var(--sl-color-accent-low); color: var(--sl-color-accent-high); border-inline-start: 2px solid var(--sl-color-accent); }
20
+ summary { cursor: pointer; padding: .25rem; font-weight: 600; font-size: var(--sl-text-sm); }
21
+ </style>
package/config.ts CHANGED
@@ -33,6 +33,7 @@ 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
35
  import { resolveMegaMenuIcon } from './src/utils/resolve-icon.ts';
36
+ import providerSharedAssets from './src/utils/share-provider-assets.mjs';
36
37
  import { buildSubcategorySidebar } from './src/utils/subcategory-sidebar.ts';
37
38
 
38
39
  export type { LocaleConfig } from './src/i18n/locales.ts';
@@ -922,6 +923,7 @@ export function createF5xcDocsConfig(options: F5xcDocsConfigOptions = {}) {
922
923
  ],
923
924
  }),
924
925
  react(),
926
+ ...(canonicalProvider ? [providerSharedAssets()] : []),
925
927
  ...additionalIntegrations,
926
928
  ],
927
929
  });
package/index.ts CHANGED
@@ -24,6 +24,9 @@ export default function f5xcDocsTheme(): StarlightPlugin {
24
24
  ],
25
25
  components: {
26
26
  ...config.components,
27
+ ...(process.env.DOCS_PROFILE === 'canonical-provider'
28
+ ? { Sidebar: '@f5-sales-demo/docs-theme/components/ProviderSidebar.astro' }
29
+ : {}),
27
30
  Banner: '@f5-sales-demo/docs-theme/components/Banner.astro',
28
31
  EditLink: '@f5-sales-demo/docs-theme/components/EditLink.astro',
29
32
  Footer: '@f5-sales-demo/docs-theme/components/Footer.astro',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f5-sales-demo/docs-theme",
3
- "version": "4.4.2",
3
+ "version": "4.4.3",
4
4
  "description": "F5 Distributed Cloud branded Starlight documentation theme",
5
5
  "type": "module",
6
6
  "engines": {
@@ -41,7 +41,8 @@
41
41
  "./src/i18n/translator.ts": "./src/i18n/translator.ts",
42
42
  "./src/i18n/prompt.ts": "./src/i18n/prompt.ts",
43
43
  "./src/utils/localize-ecosystem-href.ts": "./src/utils/localize-ecosystem-href.ts",
44
- "./canonical-route-middleware": "./canonical-route-middleware.ts"
44
+ "./canonical-route-middleware": "./canonical-route-middleware.ts",
45
+ "./components/ProviderSidebar.astro": "./components/ProviderSidebar.astro"
45
46
  },
46
47
  "files": [
47
48
  "index.ts",
@@ -0,0 +1,61 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const hash = (value) => createHash('sha256').update(value).digest('hex').slice(0, 24);
7
+ // Input is Astro-generated HTML. Keep each asset's original execution position
8
+ // and SVG attributes; only identical generated bytes share a file.
9
+ export async function shareProviderAssets(root, base = '/') {
10
+ const files = (await readdir(root, { recursive: true })).filter((file) => file.endsWith('.html'));
11
+ const assetRoot = join(root, '_shared');
12
+ const assets = new Map();
13
+ const prefix = `${base.replace(/\/$/, '')}/_shared/`;
14
+ let saved = 0;
15
+ await mkdir(assetRoot, { recursive: true });
16
+ for (const file of files) {
17
+ const path = join(root, file);
18
+ const before = await readFile(path, 'utf8');
19
+ let html = before.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/g, (original, attrs, body) => {
20
+ if (/\bsrc\s*=/.test(attrs) || !body.trim() || /type=["'](?:application\/|text\/template)/.test(attrs))
21
+ return original;
22
+ const name = `${hash(body)}.js`;
23
+ assets.set(name, body);
24
+ return `<script${attrs} src="${prefix}${name}"></script>`;
25
+ });
26
+ const islands = [];
27
+ html = html.replace(/<astro-island\b[\s\S]*?<\/astro-island>/g, (island) => {
28
+ islands.push(island);
29
+ return `<!--provider-island-${islands.length - 1}-->`;
30
+ });
31
+ html = html.replace(/<svg\b([^>]*)>([\s\S]*?)<\/svg>/g, (original, attrs, body) => {
32
+ // Sprite definitions and interactive SVG content must remain inline.
33
+ if (/<(?:symbol|script|foreignObject)\b/.test(body) || /\bid=/.test(attrs) || /\bon\w+=/.test(attrs))
34
+ return original;
35
+ const name = `${hash(body)}.svg`;
36
+ assets.set(name, `<svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon">${body}</symbol></svg>`);
37
+ return `<svg${attrs}><use href="${prefix}${name}#icon"></use></svg>`;
38
+ });
39
+ html = html.replace(/<!--provider-island-(\d+)-->/g, (_, index) => islands[Number(index)]);
40
+ saved += Buffer.byteLength(before) - Buffer.byteLength(html);
41
+ await writeFile(path, html);
42
+ }
43
+ let assetBytes = 0;
44
+ for (const [name, body] of assets) {
45
+ await writeFile(join(assetRoot, name), body);
46
+ assetBytes += Buffer.byteLength(body);
47
+ }
48
+ return { pages: files.length, assets: assets.size, savedBytes: saved - assetBytes };
49
+ }
50
+
51
+ export default function providerSharedAssets() {
52
+ return {
53
+ name: 'f5-canonical-shared-assets',
54
+ hooks: {
55
+ 'astro:build:done': async ({ dir, logger }) => {
56
+ const result = await shareProviderAssets(fileURLToPath(dir), process.env.DOCS_BASE || '/');
57
+ logger.info(`Shared canonical assets: ${JSON.stringify(result)}`);
58
+ },
59
+ },
60
+ };
61
+ }
@@ -0,0 +1,25 @@
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 { shareProviderAssets } from './share-provider-assets.mjs';
6
+
7
+ it('shares identical generated assets, preserving script order and SVG dimensions', async () => {
8
+ const root = await mkdtemp(join(tmpdir(), 'shared-provider-'));
9
+ try {
10
+ const text =
11
+ '<h1>Reference</h1><script>window.example=1;</script><svg width="16" viewBox="0 0 24 24"><path d="M1 2"/></svg><script src="keep.js"></script>';
12
+ await writeFile(join(root, 'one.html'), text);
13
+ await writeFile(join(root, 'two.html'), text);
14
+ const result = await shareProviderAssets(root, '/provider/version/');
15
+ expect(result.assets).toBe(2);
16
+ const html = await readFile(join(root, 'one.html'), 'utf8');
17
+ expect(html).toContain('<h1>Reference</h1>');
18
+ expect(html).toMatch(/<script src="\/provider\/version\/_shared\/[a-f0-9]+\.js"><\/script>/);
19
+ expect(html).toContain('<svg width="16" viewBox="0 0 24 24"><use');
20
+ expect(html).toContain('<script src="keep.js"></script>');
21
+ expect(await readFile(join(root, 'two.html'), 'utf8')).toBe(html);
22
+ } finally {
23
+ await rm(root, { recursive: true, force: true });
24
+ }
25
+ });