Skip to content

Commit 50790fa

Browse files
committed
feat: migrating content from docs.coveo.com to typedoc
1 parent 98f2577 commit 50790fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4640
-33
lines changed

.cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"metadatas",
8888
"Modernizr",
8989
"myapp",
90+
"coveoua",
9091
"mycoveocloudorganizationg",
9192
"mycoveoorganization",
9293
"mycoveoorganizationg",

packages/documentation/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# @coveo/documentation
2+
3+
This is a typedoc plugin. It ensure that the documentation generated adheres to our styling guidelines.
4+
5+
It is used by
6+
- `@coveo/headless`
7+
8+
## Navigation
9+
This plugin also deviates from the standard typedoc navigation patterns. It allows for top level documents that are ungrouped and it removes the standard
10+
`Documents` tab for ungrouped documents. It also allows for groups to have a mixed of documents and folders based on Category, as opposed to forcing
11+
each document in a group that has categories to _be_ categorized or otherwise lumped together into an `Others` folder.
12+
13+
The sorting of the navigation be specified, both for the top level and optionally by for groups.
14+
15+
NOTE: when specifying the sorting for groups, the key must be in lowercase not the actual casing of the document title.

packages/documentation/lib/formatTypeDocToolbar.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function formatTypeDocToolbar() {
1+
export const formatTypeDocToolbar = () => {
22
document.addEventListener('DOMContentLoaded', () => {
33
const header = document.getElementsByTagName('header')[0] as HTMLElement;
44
if (header) {
@@ -12,4 +12,4 @@ export function formatTypeDocToolbar() {
1212
typedocThemeSelector.style.display = 'none';
1313
}
1414
});
15-
}
15+
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {normalize} from './normalize.js';
2+
import type {NavNode} from './types.js';
3+
4+
/**
5+
* Hoists any child whose title equals `fallbackCategory` so its children are promoted
6+
* to the *parent* level, at the exact position where the bucket appeared.
7+
* Runs depth-first so descendants are processed as well.
8+
*/
9+
export const hoistOtherCategoryInNav = (
10+
root: NavNode,
11+
fallbackCategory: string
12+
) => {
13+
if (!root) return;
14+
15+
const stack: NavNode[] = [root];
16+
while (stack.length) {
17+
const node = stack.pop()!;
18+
const kids = node.children;
19+
if (!Array.isArray(kids) || kids.length === 0) continue;
20+
21+
const nextChildren: NavNode[] = [];
22+
for (const child of kids) {
23+
const title = typeof child.text === 'string' ? child.text : undefined;
24+
if (title && normalize(title) === normalize(fallbackCategory)) {
25+
if (Array.isArray(child.children) && child.children.length) {
26+
// Promote grandchildren to the parent's level at this position
27+
nextChildren.push(...child.children);
28+
}
29+
// Drop the bucket itself
30+
} else {
31+
nextChildren.push(child);
32+
}
33+
}
34+
node.children = nextChildren;
35+
36+
// Recurse
37+
for (const c of node.children) stack.push(c);
38+
}
39+
};
40+
41+
/**
42+
* Top-level helper for themes that return navigation as an array of nodes.
43+
* If an element named like the fallback group exists at the root, its children are
44+
* spliced into the root array at the same index (i.e., promoted to the top level),
45+
* and the bucket is removed. Also applies recursive hoisting within all nodes.
46+
*/
47+
export const hoistOtherCategoryInArray = (
48+
rootItems: NavNode[],
49+
fallbackCategory: string,
50+
topLevelGroup: string
51+
) => {
52+
if (!Array.isArray(rootItems) || rootItems.length === 0) return;
53+
54+
// First pass: recursively hoist 'Other' within each item
55+
for (const item of rootItems) {
56+
hoistOtherCategoryInNav(item, fallbackCategory);
57+
}
58+
59+
// Second pass: hoist any top-level bucket matching either fallbackCategory ('Other')
60+
// or the requested top-level group (e.g., 'Documents').
61+
let i = 0;
62+
while (i < rootItems.length) {
63+
const item = rootItems[i];
64+
const title = typeof item.text === 'string' ? item.text : undefined;
65+
if (
66+
title &&
67+
(normalize(title) === normalize(fallbackCategory) ||
68+
normalize(title) === normalize(topLevelGroup))
69+
) {
70+
const replacement = Array.isArray(item.children) ? item.children : [];
71+
// Replace the bucket node with its children (promote to top level)
72+
rootItems.splice(i, 1, ...replacement);
73+
// Continue at same index to handle multiple merges
74+
continue;
75+
}
76+
i++;
77+
}
78+
};

packages/documentation/lib/index.tsx

Lines changed: 147 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,151 @@
11
import {cpSync} from 'node:fs';
22
import {dirname, resolve} from 'node:path';
33
import {fileURLToPath} from 'node:url';
4-
// following docs https://typedoc.org/guides/development/#plugins
5-
// eslint-disable-next-line n/no-unpublished-import
6-
import {type Application, Converter, JSX, RendererEvent} from 'typedoc';
4+
import {
5+
type Application,
6+
Converter,
7+
type DefaultTheme,
8+
JSX,
9+
type NavigationElement,
10+
ParameterType,
11+
type ProjectReflection,
12+
RendererEvent,
13+
} from 'typedoc';
714
import {formatTypeDocToolbar} from './formatTypeDocToolbar.js';
15+
import {hoistOtherCategoryInArray, hoistOtherCategoryInNav} from './hoist.js';
816
import {insertAtomicSearchBox} from './insertAtomicSearchBox.js';
917
import {insertBetaNote} from './insertBetaNote.js';
1018
import {insertCustomComments} from './insertCustomComments.js';
1119
import {insertMetaTags} from './insertMetaTags.js';
1220
import {insertSiteHeaderBar} from './insertSiteHeaderBar.js';
21+
import {applyTopLevelRenameArray} from './renaming.js';
22+
import {
23+
applyNestedOrderingArray,
24+
applyNestedOrderingNode,
25+
applyTopLevelOrderingArray,
26+
applyTopLevelOrderingNode,
27+
} from './sortNodes.js';
28+
import type {NavNode} from './types.js';
1329

1430
const __dirname = dirname(fileURLToPath(import.meta.url));
1531

1632
/**
1733
* Called by TypeDoc when loaded as a plugin.
1834
*/
19-
export function load(app: Application) {
35+
export const load = (app: Application) => {
36+
app.options.addDeclaration({
37+
name: 'hoistOther.fallbackCategory',
38+
help: "Name of the fallback category to hoist (defaults to defaultCategory or 'Other').",
39+
type: ParameterType.String,
40+
});
41+
42+
app.options.addDeclaration({
43+
name: 'hoistOther.topLevelGroup',
44+
help: "Name of the top-level group whose children should be promoted to root (default 'Documents').",
45+
type: ParameterType.String,
46+
});
47+
48+
app.options.addDeclaration({
49+
name: 'hoistOther.topLevelOrder',
50+
help: 'An array to sort the top level nav by.',
51+
type: ParameterType.Array,
52+
});
53+
54+
app.options.addDeclaration({
55+
name: 'hoistOther.nestedOrder',
56+
help: "Object mapping parent title -> ordering array for its children. Use '*' for a default. If omitted, children are sorted alphabetically.",
57+
type: ParameterType.Mixed,
58+
});
59+
60+
app.options.addDeclaration({
61+
name: 'hoistOther.renameModulesTo',
62+
help: "If set, rename any top-level group titled 'Modules' to this string.",
63+
type: ParameterType.String,
64+
});
65+
66+
const originalMethodName = 'getNavigation';
67+
let originalMethod: (
68+
project: ProjectReflection
69+
) => NavigationElement[] | null = null;
70+
app.renderer.on('beginRender', () => {
71+
const theme = app.renderer.theme as DefaultTheme | undefined;
72+
if (!theme) return;
73+
74+
originalMethod = theme.getNavigation;
75+
76+
if (!originalMethod) return;
77+
78+
const opts = app.options;
79+
const fallback =
80+
(opts.getValue('hoistOther.fallbackCategory') as string) ||
81+
(opts.getValue('defaultCategory') as string) ||
82+
'Other';
83+
84+
const topLevelGroup =
85+
(opts.getValue('hoistOther.topLevelGroup') as string) || 'Documents';
86+
87+
const topLevelOrder =
88+
(opts.getValue('hoistOther.topLevelOrder') as string[] | undefined) ||
89+
undefined;
90+
91+
let nestedOrder = opts.getValue('hoistOther.nestedOrder') as
92+
| Record<string, string[]>
93+
| string
94+
| undefined;
95+
if (typeof nestedOrder === 'string') {
96+
try {
97+
nestedOrder = JSON.parse(nestedOrder);
98+
} catch {}
99+
}
100+
101+
const renameModulesTo =
102+
(opts.getValue('hoistOther.renameModulesTo') as string | undefined) ||
103+
undefined;
104+
105+
const typedNestedOrder = nestedOrder as Record<string, string[]>;
106+
107+
theme.getNavigation = function wrappedNavigation(
108+
this: unknown,
109+
...args: unknown[]
110+
) {
111+
const nav = originalMethod!.apply(this, args);
112+
113+
// The nav shape can be an array of nodes or a single root with children
114+
if (Array.isArray(nav)) {
115+
if (renameModulesTo?.trim()) {
116+
applyTopLevelRenameArray(nav, 'Modules', renameModulesTo.trim());
117+
}
118+
119+
hoistOtherCategoryInArray(nav as NavNode[], fallback, topLevelGroup);
120+
121+
if (topLevelOrder?.length) {
122+
applyTopLevelOrderingArray(nav as NavNode[], topLevelOrder);
123+
}
124+
125+
applyNestedOrderingArray(nav as NavNode[], typedNestedOrder);
126+
} else if (nav && typeof nav === 'object') {
127+
if (renameModulesTo?.trim() && Array.isArray(nav.children)) {
128+
applyTopLevelRenameArray(
129+
nav.children,
130+
'Modules',
131+
renameModulesTo.trim()
132+
);
133+
}
134+
135+
hoistOtherCategoryInNav(nav as NavNode, fallback);
136+
if (
137+
(nav as NavNode).children &&
138+
topLevelOrder &&
139+
topLevelOrder.length
140+
) {
141+
applyTopLevelOrderingNode(nav as NavNode, topLevelOrder);
142+
}
143+
applyNestedOrderingNode(nav as NavNode, typedNestedOrder);
144+
}
145+
return nav;
146+
};
147+
});
148+
20149
// Need the Meta Tags to be inserted first, or it causes issues with the navigation sidebar
21150
app.renderer.hooks.on('head.begin', () => (
22151
<>
@@ -119,14 +248,8 @@ export function load(app: Application) {
119248
</>
120249
));
121250

122-
const baseAssetsPath = '../../documentation/assets';
123-
124-
const createFileCopyEntry = (sourcePath: string) => ({
125-
from: resolve(__dirname, `${baseAssetsPath}/${sourcePath}`),
126-
to: resolve(app.options.getValue('out'), `assets/${sourcePath}`),
127-
});
128-
129-
const onRenderEnd = () => {
251+
app.renderer.on(RendererEvent.END, () => {
252+
const baseAssetsPath = '../../documentation/assets';
130253
const filesToCopy = [
131254
'css/docs-style.css',
132255
'css/main-new.css',
@@ -145,19 +268,26 @@ export function load(app: Application) {
145268
];
146269

147270
filesToCopy.forEach((filePath) => {
148-
const file = createFileCopyEntry(filePath);
271+
const file = {
272+
from: resolve(__dirname, `${baseAssetsPath}/${filePath}`),
273+
to: resolve(app.options.getValue('out'), `assets/${filePath}`),
274+
};
149275
cpSync(file.from, file.to);
150276
});
151277

152278
const darkModeJs = {
153279
from: resolve(__dirname, '../../documentation/dist/dark-mode.js'),
154280
to: resolve(app.options.getValue('out'), 'assets/vars/dark-mode.js'),
155281
};
282+
// Restore original to avoid side effects
283+
const theme = app.renderer.theme as DefaultTheme | undefined;
284+
if (theme && originalMethodName && originalMethod) {
285+
theme[originalMethodName] = originalMethod;
286+
}
287+
originalMethod = null;
156288

157289
cpSync(darkModeJs.from, darkModeJs.to);
158-
};
159-
160-
app.renderer.on(RendererEvent.END, onRenderEnd);
290+
});
161291

162292
app.converter.on(Converter.EVENT_CREATE_DECLARATION, insertCustomComments);
163-
}
293+
};

packages/documentation/lib/insertAtomicSearchBox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ declare global {
1313
}
1414
}
1515

16-
export function insertAtomicSearchBox() {
16+
export const insertAtomicSearchBox = () => {
1717
const areFunctionalCookiesEnabled = (): boolean => {
1818
return document.cookie
1919
.split('; ')
@@ -54,4 +54,4 @@ export function insertAtomicSearchBox() {
5454
})();
5555
}
5656
});
57-
}
57+
};

packages/documentation/lib/insertBetaNote.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function insertBetaNote() {
1+
export const insertBetaNote = () => {
22
document.addEventListener('DOMContentLoaded', () => {
33
const breadcrumbs = document.querySelector('ul.tsd-breadcrumb');
44
if (breadcrumbs) {
@@ -14,4 +14,4 @@ export function insertBetaNote() {
1414
}
1515
}
1616
});
17-
}
17+
};

packages/documentation/lib/insertCoveoLogo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function insertCoveoLogo(imagePath: string) {
1+
export const insertCoveoLogo = (imagePath: string) => {
22
document.addEventListener('DOMContentLoaded', () => {
33
const toolbarContents = document.getElementsByClassName(
44
'tsd-toolbar-contents'
@@ -24,4 +24,4 @@ export function insertCoveoLogo(imagePath: string) {
2424
faviconLink.rel = 'icon';
2525
faviconLink.href = `${imagePath}/favicon.ico`;
2626
document.head.appendChild(faviconLink);
27-
}
27+
};

packages/documentation/lib/insertCustomComments.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const comments = [
1717
},
1818
];
1919

20+
// NOTE: cannot be converted into an arrow function `this`
2021
export function insertCustomComments(
2122
this: undefined,
2223
_ctx: Context,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function insertMetaTags() {
1+
export const insertMetaTags = () => {
22
const head = document.getElementsByTagName('head')[0];
33
if (head) {
44
head.innerHTML += `
@@ -8,4 +8,4 @@ export function insertMetaTags() {
88
<meta name="docsSiteBaseUrl" content="/en">
99
`;
1010
}
11-
}
11+
};

0 commit comments

Comments
 (0)