Skip to content

Commit 7835ee3

Browse files
committed
build(docs): generate sassdoc nav items
1 parent 6ccc997 commit 7835ee3

File tree

7 files changed

+186
-254
lines changed

7 files changed

+186
-254
lines changed

apps/docs/scripts/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ export const GENERATED_DIR = join(
1717
"generated"
1818
);
1919
export const GENERATED_SASSDOC_FILE = join(GENERATED_DIR, "sassdoc.ts");
20+
export const GENERATED_SASSDOC_NAV_ITEMS_FILE = join(
21+
GENERATED_DIR,
22+
"sassdocNavItems.ts"
23+
);
2024
export const ALIASED_SASSDOC_FILE = aliased(GENERATED_SASSDOC_FILE);
25+
export const ALIASED_SASSDOC_NAV_ITEMS_FILE = aliased(
26+
GENERATED_SASSDOC_NAV_ITEMS_FILE
27+
);
2128

2229
export const GENERATED_STACKBLITZ_FILE = join(GENERATED_DIR, "stackblitz.ts");
2330
export const ALIASED_STACKBLITZ_FILE = aliased(GENERATED_SASSDOC_FILE);

apps/docs/scripts/createScssLookup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { watch } from "chokidar";
33
import { createScssLookup } from "docs-generator/scripts/create-scss-lookup";
44
import { logComplete } from "docs-generator/utils/log";
55

6+
import { ensureGeneratedDir } from "./ensureGeneratedDir.js";
7+
68
if (!process.argv.includes("--watch")) {
9+
await ensureGeneratedDir();
710
await createScssLookup();
811
process.exit(0);
912
}

apps/docs/scripts/sassdoc.ts

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
import { type NavigationItem } from "@react-md/core/navigation/types";
2+
import { alphaNumericSort } from "@react-md/core/utils/alphaNumericSort";
13
import { log, logComplete } from "docs-generator/utils/log";
24
import { existsSync } from "node:fs";
35
import { writeFile } from "node:fs/promises";
46
import { format } from "prettier";
5-
import { type GeneratedSassDocWithOrder, generate } from "sassdoc-generator";
7+
import {
8+
type GeneratedSassDoc,
9+
type GeneratedSassDocWithOrder,
10+
generate,
11+
} from "sassdoc-generator";
612
import { type FormattedSassDocItem } from "sassdoc-generator/types";
713

14+
import { titleCase } from "../src/utils/strings.js";
815
import {
916
ALIASED_SASSDOC_FILE,
17+
ALIASED_SASSDOC_NAV_ITEMS_FILE,
1018
CORE_SRC,
1119
GENERATED_FILE_BANNER,
1220
GENERATED_SASSDOC_FILE,
21+
GENERATED_SASSDOC_NAV_ITEMS_FILE,
1322
} from "./constants.js";
1423
import { ensureGeneratedDir } from "./ensureGeneratedDir.js";
1524

@@ -50,25 +59,104 @@ export const SASSDOC_VARIABLES: Record<string, FormattedVariableItem | undefined
5059
GENERATED_SASSDOC_FILE,
5160
await format(contents, { parser: "typescript" })
5261
);
62+
logComplete(`Generated "${ALIASED_SASSDOC_FILE}"`);
63+
}
64+
65+
async function generateNavItems(options: GeneratedSassDoc): Promise<void> {
66+
const { mixins, functions, variables } = options;
67+
const groups = new Set<string>();
68+
mixins.forEach((item) => groups.add(item.group));
69+
functions.forEach((item) => groups.add(item.group));
70+
variables.forEach((item) => groups.add(item.group));
71+
72+
const components: (NavigationItem & { children: string })[] = [];
73+
const remainingItems: (NavigationItem & { children: string })[] = [];
74+
groups.forEach((group) => {
75+
if (group.startsWith("core.")) {
76+
const withoutCore = group.replace("core.", "");
77+
if (withoutCore === "form") {
78+
components.push({
79+
type: "route",
80+
href: "/form",
81+
children: "Form",
82+
});
83+
} else {
84+
remainingItems.push({
85+
type: "route",
86+
href: `/${withoutCore}`,
87+
children: titleCase(withoutCore, "-"),
88+
});
89+
}
90+
} else {
91+
components.push({
92+
type: "route",
93+
href: `/${group}`,
94+
children: titleCase(group, "-"),
95+
});
96+
}
97+
});
98+
99+
await writeFile(
100+
GENERATED_SASSDOC_NAV_ITEMS_FILE,
101+
await format(
102+
`${GENERATED_FILE_BANNER}
103+
import { type NavigationItem } from "@react-md/core/navigation/types";
104+
105+
export const SASSDOC_NAV_ITEMS: readonly NavigationItem[] = [
106+
{
107+
type: "group",
108+
href: "/sassdoc",
109+
children: "Sass API Docs",
110+
items: [
111+
{ type: "subheader", children: "Core" },
112+
${alphaNumericSort(remainingItems, { extractor: (item) => item.children })
113+
.map((item) => JSON.stringify(item))
114+
.join(",")},
115+
{ type: "subheader", children: "Components" },
116+
${alphaNumericSort(components, { extractor: (item) => item.children })
117+
.map((item) => JSON.stringify(item))
118+
.join(",")},
119+
]
120+
},
121+
]
122+
`,
123+
{ filepath: GENERATED_SASSDOC_NAV_ITEMS_FILE }
124+
)
125+
);
126+
logComplete(`Generated "${ALIASED_SASSDOC_NAV_ITEMS_FILE}"`);
53127
}
54128

55129
if (process.argv.includes("--touch")) {
130+
const empty: GeneratedSassDocWithOrder = {
131+
mixins: new Map(),
132+
functions: new Map(),
133+
variables: new Map(),
134+
mixinsOrder: new Map(),
135+
functionsOrder: new Map(),
136+
variablesOrder: new Map(),
137+
};
138+
const update = "Run `pnpm --filter docs sassdoc` to update.";
139+
140+
if (!existsSync(GENERATED_SASSDOC_NAV_ITEMS_FILE)) {
141+
await log(
142+
generateNavItems(empty),
143+
"",
144+
`Created an empty "${ALIASED_SASSDOC_NAV_ITEMS_FILE}". ${update}`
145+
);
146+
} else {
147+
logComplete(
148+
`Skipped creating "${ALIASED_SASSDOC_NAV_ITEMS_FILE}" since it already exists. ${update}`
149+
);
150+
}
56151
if (!existsSync(GENERATED_SASSDOC_FILE)) {
57152
await log(
58-
createSassDocFile({
59-
mixins: new Map(),
60-
functions: new Map(),
61-
variables: new Map(),
62-
mixinsOrder: new Map(),
63-
functionsOrder: new Map(),
64-
variablesOrder: new Map(),
65-
}),
153+
createSassDocFile(empty),
66154
"",
67-
`Created an empty "${ALIASED_SASSDOC_FILE}". Run \`pnpm --filter docs sassdoc\` to update.`
155+
`Created an empty "${ALIASED_SASSDOC_FILE}". ${update}`
68156
);
69157
} else {
70158
logComplete(
71-
`Skipped creating "${ALIASED_SASSDOC_FILE}" since it already exists. Run \`pnpm --filter docs sassdoc\` to update.`
159+
`Skipped creating "${ALIASED_SASSDOC_FILE}" since it already exists. ${update}`
72160
);
73161
}
74162

@@ -77,7 +165,11 @@ if (process.argv.includes("--touch")) {
77165

78166
async function run(): Promise<void> {
79167
await ensureGeneratedDir();
80-
await createSassDocFile(await generate({ src: CORE_SRC }));
168+
const generated = await generate({ src: CORE_SRC });
169+
await Promise.all([
170+
createSassDocFile(generated),
171+
generateNavItems(generated),
172+
]);
81173
}
82174

83-
await log(run(), "", `Created "${ALIASED_SASSDOC_FILE}"`);
175+
await log(run(), "", "sassdoc script completed");
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use client";
2+
3+
import { Button } from "@react-md/core/button/Button";
4+
import { Dialog } from "@react-md/core/dialog/Dialog";
5+
import { useToggle } from "@react-md/core/useToggle";
6+
import CloseIcon from "@react-md/material-icons/CloseIcon";
7+
import { type ReactElement, type ReactNode, useEffect } from "react";
8+
9+
export interface DevRegenDialogProps {
10+
children: ReactNode;
11+
}
12+
13+
export function DevRegenDialog({
14+
children,
15+
}: DevRegenDialogProps): ReactElement {
16+
const { toggled, enable, disable } = useToggle();
17+
useEffect(() => {
18+
enable();
19+
}, [enable]);
20+
21+
return (
22+
<Dialog
23+
visible={toggled}
24+
onRequestClose={disable}
25+
aria-label="Regenerate SassDoc"
26+
type="custom"
27+
disableOverlay
28+
disableTransition
29+
disableScrollLock
30+
isFocusTypeDisabled={() => true}
31+
style={{ position: "relative" }}
32+
containerProps={{
33+
style: {
34+
position: "fixed",
35+
bottom: 0,
36+
left: "50%",
37+
transform: "translate3d(-50%, 0, 0)",
38+
},
39+
}}
40+
>
41+
<Button
42+
onClick={disable}
43+
buttonType="icon"
44+
style={{ position: "absolute", right: 0 }}
45+
>
46+
<CloseIcon />
47+
</Button>
48+
{children}
49+
</Dialog>
50+
);
51+
}

apps/docs/src/app/(main)/(markdown)/(apidocs)/sassdoc/[group]/page.tsx

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import { Box } from "@react-md/core/box/Box";
2-
import { dialog } from "@react-md/core/dialog/styles";
3-
import { Portal } from "@react-md/core/portal/Portal";
4-
import { TextContainer } from "@react-md/core/typography/TextContainer";
51
import { notFound } from "next/navigation.js";
62
import { type ReactElement } from "react";
73

84
import { Markdown } from "@/components/Markdown.jsx";
95
import { PageNotFound } from "@/components/PageNotFound/PageNotFound.jsx";
106
import { TableOfContents } from "@/components/TableOfContents/TableOfContents.jsx";
117

8+
import { DevRegenDialog } from "./DevRegenDialog.jsx";
129
import { SassDocSection } from "./SassDocSection.jsx";
1310
import { SASSDOC_GROUP, SASSDOC_GROUP_NAMES } from "./constants.js";
1411
import { createTOC } from "./utils.js";
@@ -60,32 +57,16 @@ pnpm --filter docs sassdoc
6057
<SassDocSection items={lookup.mixins}>Mixins</SassDocSection>
6158
<SassDocSection items={lookup.functions}>Functions</SassDocSection>
6259
{process.env.NODE_ENV !== "production" && (
63-
<Portal>
64-
<Box
65-
style={{
66-
position: "fixed",
67-
bottom: 0,
68-
left: 0,
69-
right: 0,
70-
zIndex: 999,
71-
paddingBottom: 0,
72-
}}
73-
>
74-
<TextContainer
75-
style={{ paddingBlock: 0 }}
76-
className={dialog({ fixed: true, type: "centered" })}
77-
>
78-
<Markdown
79-
source={`> !Info! If the sassdoc does not match the \`@react-md/core\` source code, run the following command:
60+
<DevRegenDialog>
61+
<Markdown
62+
source={`> !Info! If the sassdoc does not match the \`@react-md/core\` source code, run the following command:
8063
8164
\`\`\`sh
8265
pnpm --filter docs sassdoc
8366
\`\`\`
8467
`}
85-
/>
86-
</TextContainer>
87-
</Box>
88-
</Portal>
68+
/>
69+
</DevRegenDialog>
8970
)}
9071
</>
9172
);

0 commit comments

Comments
 (0)