From db9ef1268238431298975c1ff258c50f08d4d2fe Mon Sep 17 00:00:00 2001 From: httphypixelnet Date: Sun, 19 Jul 2026 22:46:35 -0700 Subject: [PATCH 01/13] fixed glossary (probably), removed un-typesafe code --- astro.config.mjs | 1 + package.json | 1 + src/data/glossary.ts | 22 ++++-- src/plugins/remark-glossary.ts | 68 ++++++++----------- src/plugins/remark-mdx-global-imports.ts | 1 + ...es.mjs => remark-no-inline-code-fences.ts} | 6 +- tsconfig.json | 3 +- 7 files changed, 54 insertions(+), 48 deletions(-) rename src/plugins/{remark-no-inline-code-fences.mjs => remark-no-inline-code-fences.ts} (84%) diff --git a/astro.config.mjs b/astro.config.mjs index f6a16bf1..4c1e998d 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,3 +1,4 @@ +// @ts-check import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import remarkGlossary from './src/plugins/remark-glossary'; diff --git a/package.json b/package.json index 6993cd56..5fb5cb22 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "astro-eslint-parser": "^1.4.0", "eslint": "^10.5.0", "eslint-plugin-astro": "^1.7.0", + "mdast-util-mdx": "^3.0.0", "prettier": "^3.8.4", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-sentences-per-line": "^0.2.3", diff --git a/src/data/glossary.ts b/src/data/glossary.ts index f05fcc9d..6e976367 100644 --- a/src/data/glossary.ts +++ b/src/data/glossary.ts @@ -6,16 +6,27 @@ * * Definitions should not have a period at the end * - * Format: - * { - * term: "TERM", // The word/abbreviation to match (case-insensitive) - * definition: "..." // The explanation shown on hover - * } + * @property definition: The explanation shown on hover + * @property autoDetect: Optional, defaults to true. If true, automatically highlight this term throughout site content. If false the term will only be highlighted when explicitly wrapped in component. + * @property term: The word/abbreviation to match (case-insensitive) */ export interface GlossaryTerm { + /** + * The word/abbreviation to match (case-insensitive) + * @example "SystemCore" + */ term: string; + /** + * The explanation shown on hover + * @example "Main processor for robot code, contains various IO" + */ definition: string; + /** + * Whether to automatically detect this term throughout the site. Set to false for common terms (eg: CAN) to avoid false positives. + * @default true + */ + autoDetect?: boolean; } export const glossaryTerms: GlossaryTerm[] = [ @@ -40,6 +51,7 @@ export const glossaryTerms: GlossaryTerm[] = [ term: 'CAN', definition: 'Controller Area Network: typically yellow and green cable used to communicate with motor controllers and sensors, can be run in various topographies instead of each cable needing to connect to SystemCore', + autoDetect: false, }, { term: 'PWM', diff --git a/src/plugins/remark-glossary.ts b/src/plugins/remark-glossary.ts index 804732dc..e1f388ea 100644 --- a/src/plugins/remark-glossary.ts +++ b/src/plugins/remark-glossary.ts @@ -1,20 +1,19 @@ -import { visit } from 'unist-util-visit'; -import type { Root, Text } from 'mdast'; +import { visit, SKIP } from 'unist-util-visit'; +import type { Root, RootContent, Text } from 'mdast'; import type { VFile } from 'vfile'; -import { glossaryTerms } from '../data/glossary'; +import type { MdxJsxTextElement } from 'mdast-util-mdx-jsx'; -const sortedTerms = [...glossaryTerms].sort( - (a, b) => b.term.length - a.term.length, -); +import { glossaryTerms } from '../data/glossary'; -const termMap = new Map(); -glossaryTerms.forEach(({ term, definition }) => { - termMap.set(term.toLowerCase(), definition); -}); +const sortedTerms = [...glossaryTerms] + .sort((a, b) => b.term.length - a.term.length) + .filter((term) => term.autoDetect !== false); const pattern = new RegExp( - `\\b(${sortedTerms.map((t) => escapeRegex(t.term)).join('|')})\\b`, - 'gi', + `(? escapeRegex(t.term)) + .join('|')})(?![\\p{L}\\p{N}_])`, + 'giu', ); function escapeRegex(str: string): string { @@ -28,17 +27,7 @@ export function remarkGlossary() { visit(tree, 'text', (node: Text, index, parent) => { if (!parent || index === undefined) return; - if ( - parent.type === 'link' || - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (parent as any).type === 'code' || - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (parent as any).type === 'inlineCode' || - (parent as unknown as { tagName?: string }).tagName === - 'abbr' || - (parent as unknown as { tagName?: string }).tagName === 'a' || - (parent as unknown as { tagName?: string }).tagName === 'code' - ) { + if (parent.type === 'link' || parent.type === 'mdxJsxTextElement') { return; } @@ -47,15 +36,13 @@ export function remarkGlossary() { if (matches.length === 0) return; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const newNodes: any[] = []; + const newNodes: RootContent[] = []; let lastIndex = 0; matches.forEach((match) => { - const matchStart = match.index!; + const matchStart = match.index; const matchEnd = matchStart + match[0].length; const matchedTerm = match[0]; - const definition = termMap.get(matchedTerm.toLowerCase()); if (matchStart > lastIndex) { newNodes.push({ @@ -64,10 +51,19 @@ export function remarkGlossary() { }); } - newNodes.push({ - type: 'html', - value: `${escapeHtml(matchedTerm)}`, - }); + const glossaryNode: MdxJsxTextElement = { + type: 'mdxJsxTextElement', + name: 'Glossary', + attributes: [ + { + type: 'mdxJsxAttribute', + name: 'term', + value: matchedTerm, + }, + ], + children: [], + }; + newNodes.push(glossaryNode); lastIndex = matchEnd; }); @@ -80,17 +76,9 @@ export function remarkGlossary() { } parent.children.splice(index, 1, ...newNodes); + return [SKIP, index + newNodes.length]; }); }; } -function escapeHtml(str: string): string { - return str - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - export default remarkGlossary; diff --git a/src/plugins/remark-mdx-global-imports.ts b/src/plugins/remark-mdx-global-imports.ts index adad4fbf..6b68c71e 100644 --- a/src/plugins/remark-mdx-global-imports.ts +++ b/src/plugins/remark-mdx-global-imports.ts @@ -26,6 +26,7 @@ const GLOBAL_IMPORTS = [ { name: 'Aside', path: '@components/Aside.astro' }, { name: 'Slides', path: '@components/Slides.astro' }, { name: 'LinkButton', path: '@components/LinkButton.astro' }, + { name: 'Glossary', path: '@components/Glossary.astro' }, // { name: 'ImageTable', path: '@components/ImageTable.astro' }, ]; diff --git a/src/plugins/remark-no-inline-code-fences.mjs b/src/plugins/remark-no-inline-code-fences.ts similarity index 84% rename from src/plugins/remark-no-inline-code-fences.mjs rename to src/plugins/remark-no-inline-code-fences.ts index d123c981..e8c31b2f 100644 --- a/src/plugins/remark-no-inline-code-fences.mjs +++ b/src/plugins/remark-no-inline-code-fences.ts @@ -1,9 +1,13 @@ +/// +import type { Root } from 'mdast'; import { visit } from 'unist-util-visit'; +import type { VFile } from 'vfile'; + const IGNORE_RE = /rli:\s*ignore/; export default function remarkNoInlineCodeFences() { - return (tree, file) => { + return (tree: Root, file: VFile) => { visit(tree, 'code', (node, index, parent) => { if (index !== undefined && parent) { const prev = parent.children[index - 1]; diff --git a/tsconfig.json b/tsconfig.json index 82c8c25c..ee797fa8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,9 +3,8 @@ "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist"], "compilerOptions": { - "baseUrl": ".", "paths": { - "@components/*": ["src/components/*"] + "@components/*": ["./src/components/*"] } } } From e0430aad2d8abed4dec2185406330447d90ba7b1 Mon Sep 17 00:00:00 2001 From: httphypixelnet Date: Sun, 19 Jul 2026 23:42:56 -0700 Subject: [PATCH 02/13] lint and fix type error --- .remarkrc.mjs => .remarkrc.ts | 2 +- astro.config.mjs => astro.config.ts | 11 +++++------ src/plugins/remark-no-inline-code-fences.ts | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) rename .remarkrc.mjs => .remarkrc.ts (94%) rename astro.config.mjs => astro.config.ts (90%) diff --git a/.remarkrc.mjs b/.remarkrc.ts similarity index 94% rename from .remarkrc.mjs rename to .remarkrc.ts index 175ab2ff..48495c4e 100644 --- a/.remarkrc.mjs +++ b/.remarkrc.ts @@ -1,7 +1,7 @@ import remarkPresetLintRecommended from 'remark-preset-lint-recommended'; import remarkFrontmatter from 'remark-frontmatter'; import remarkMdx from 'remark-mdx'; -import remarkNoInlineCodeFences from './src/plugins/remark-no-inline-code-fences.mjs'; +import remarkNoInlineCodeFences from './src/plugins/remark-no-inline-code-fences'; export default { plugins: [ diff --git a/astro.config.mjs b/astro.config.ts similarity index 90% rename from astro.config.mjs rename to astro.config.ts index 4c1e998d..76713da2 100644 --- a/astro.config.mjs +++ b/astro.config.ts @@ -1,12 +1,11 @@ -// @ts-check import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; -import remarkGlossary from './src/plugins/remark-glossary'; -import remarkCenter from './src/plugins/remark-center'; -import remarkFigure from './src/plugins/remark-figure'; -import remarkImageAttributes from './src/plugins/remark-image-attributes'; +import remarkGlossary from './src/plugins/remark-glossary.ts'; +import remarkCenter from './src/plugins/remark-center.ts'; +import remarkFigure from './src/plugins/remark-figure.ts'; +import remarkImageAttributes from './src/plugins/remark-image-attributes.ts'; import { remarkMdxGlobalImports } from './src/plugins/remark-mdx-global-imports.ts'; -import remarkCodeRegion from './src/plugins/remark-code-region'; +import remarkCodeRegion from './src/plugins/remark-code-region.ts'; export default defineConfig({ site: 'https://frcsoftware.org', diff --git a/src/plugins/remark-no-inline-code-fences.ts b/src/plugins/remark-no-inline-code-fences.ts index e8c31b2f..fff5b265 100644 --- a/src/plugins/remark-no-inline-code-fences.ts +++ b/src/plugins/remark-no-inline-code-fences.ts @@ -3,7 +3,6 @@ import type { Root } from 'mdast'; import { visit } from 'unist-util-visit'; import type { VFile } from 'vfile'; - const IGNORE_RE = /rli:\s*ignore/; export default function remarkNoInlineCodeFences() { From 54c5991434acf7421395374988882be8e436b23a Mon Sep 17 00:00:00 2001 From: httphypixelnet <91695346+httphypixelnet@users.noreply.github.com> Date: Tue, 21 Jul 2026 01:28:48 +0000 Subject: [PATCH 03/13] revert type fixes --- .remarkrc.ts | 13 ---- astro.config.ts | 74 --------------------- package.json | 3 +- pnpm-lock.yaml | 6 ++ src/plugins/remark-no-inline-code-fences.ts | 32 --------- 5 files changed, 8 insertions(+), 120 deletions(-) delete mode 100644 .remarkrc.ts delete mode 100644 astro.config.ts delete mode 100644 src/plugins/remark-no-inline-code-fences.ts diff --git a/.remarkrc.ts b/.remarkrc.ts deleted file mode 100644 index 48495c4e..00000000 --- a/.remarkrc.ts +++ /dev/null @@ -1,13 +0,0 @@ -import remarkPresetLintRecommended from 'remark-preset-lint-recommended'; -import remarkFrontmatter from 'remark-frontmatter'; -import remarkMdx from 'remark-mdx'; -import remarkNoInlineCodeFences from './src/plugins/remark-no-inline-code-fences'; - -export default { - plugins: [ - remarkFrontmatter, - remarkMdx, - remarkPresetLintRecommended, - remarkNoInlineCodeFences, - ], -}; diff --git a/astro.config.ts b/astro.config.ts deleted file mode 100644 index 76713da2..00000000 --- a/astro.config.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { defineConfig } from 'astro/config'; -import starlight from '@astrojs/starlight'; -import remarkGlossary from './src/plugins/remark-glossary.ts'; -import remarkCenter from './src/plugins/remark-center.ts'; -import remarkFigure from './src/plugins/remark-figure.ts'; -import remarkImageAttributes from './src/plugins/remark-image-attributes.ts'; -import { remarkMdxGlobalImports } from './src/plugins/remark-mdx-global-imports.ts'; -import remarkCodeRegion from './src/plugins/remark-code-region.ts'; - -export default defineConfig({ - site: 'https://frcsoftware.org', - prefetch: true, - - markdown: { - remarkPlugins: [ - remarkCenter, - remarkFigure, - remarkGlossary, - remarkImageAttributes, - remarkMdxGlobalImports, - remarkCodeRegion, - ], - rehypePlugins: [], - }, - - integrations: [ - starlight({ - title: 'FRCSoftware.org', - favicon: '/favicon.svg', - head: [ - { - tag: 'meta', - attrs: { - property: 'og:image', - content: 'https://frcsoftware.org/favicon.svg', - }, - }, - { - tag: 'meta', - attrs: { - property: 'og:image:alt', - content: 'FRCSoftware.org logo icon', - }, - }, - { - tag: 'meta', - attrs: { - property: 'og:description', - content: - 'The comprehensive learning guide for FRC programming', - }, - }, - ], - logo: { - src: './src/assets/universal/favicon-white.svg', - }, - customCss: ['./src/styles/global.css'], - components: { - Header: './src/starlightOverrides/Header.astro', - Footer: './src/starlightOverrides/Footer.astro', - Sidebar: './src/starlightOverrides/Sidebar.astro', - Pagination: './src/starlightOverrides/Pagination.astro', - Hero: './src/starlightOverrides/Hero.astro', - TableOfContents: - './src/starlightOverrides/TableOfContents.astro', - }, - // TOC is disabled globally but can be enabled per-directory in src/config/tocConfig.ts - // or per-page via frontmatter (tableOfContents: true) - tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 }, - // Sidebar configuration is now managed in src/config/sidebarConfig.ts - // This allows different sidebars per top-level navigation section - }), - ], -}); diff --git a/package.json b/package.json index 2b22f574..e77f0ba2 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,11 @@ "astro-eslint-parser": "^1.4.0", "eslint": "^10.5.0", "eslint-plugin-astro": "^1.7.0", - "mdast-util-mdx": "^3.0.0", "husky": "^9.1.7", "lint-staged": "^17.0.8", + "mdast-util-mdx": "^3.0.0", "prettier": "^3.8.4", + "mdast-util-mdx-jsx": "^3.2.0", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-sentences-per-line": "^0.2.3", "remark-cli": "^12.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a05ffc62..1a865fc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: astro: specifier: ^6.3.7 version: 6.3.7(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0) + mdast-util-mdx-jsx: + specifier: ^3.2.0 + version: 3.2.0 sharp: specifier: ^0.34.5 version: 0.34.5 @@ -48,6 +51,9 @@ importers: lint-staged: specifier: ^17.0.8 version: 17.0.8 + mdast-util-mdx: + specifier: ^3.0.0 + version: 3.0.0 prettier: specifier: ^3.8.4 version: 3.8.4 diff --git a/src/plugins/remark-no-inline-code-fences.ts b/src/plugins/remark-no-inline-code-fences.ts deleted file mode 100644 index fff5b265..00000000 --- a/src/plugins/remark-no-inline-code-fences.ts +++ /dev/null @@ -1,32 +0,0 @@ -/// -import type { Root } from 'mdast'; -import { visit } from 'unist-util-visit'; -import type { VFile } from 'vfile'; - -const IGNORE_RE = /rli:\s*ignore/; - -export default function remarkNoInlineCodeFences() { - return (tree: Root, file: VFile) => { - visit(tree, 'code', (node, index, parent) => { - if (index !== undefined && parent) { - const prev = parent.children[index - 1]; - if ( - prev?.type === 'mdxFlowExpression' && - IGNORE_RE.test(prev.value) - ) { - return; - } - } - - if (node.meta?.trim()) return; - - if (node.value?.trim()) { - const msg = file.message( - 'Code fences must use the RLI system: ```language path/to/file#regionName', - node, - ); - msg.fatal = true; - } - }); - }; -} From a1315fa4c0209dae079bab52788025a107bed790 Mon Sep 17 00:00:00 2001 From: httphypixelnet <91695346+httphypixelnet@users.noreply.github.com> Date: Tue, 21 Jul 2026 01:29:59 +0000 Subject: [PATCH 04/13] revert type fixes --- .remarkrc.mjs | 13 ++++ astro.config.mjs | 74 ++++++++++++++++++++ pnpm-workspace.yaml | 3 + src/plugins/remark-no-inline-code-fences.mjs | 29 ++++++++ 4 files changed, 119 insertions(+) create mode 100644 .remarkrc.mjs create mode 100644 astro.config.mjs create mode 100644 pnpm-workspace.yaml create mode 100644 src/plugins/remark-no-inline-code-fences.mjs diff --git a/.remarkrc.mjs b/.remarkrc.mjs new file mode 100644 index 00000000..175ab2ff --- /dev/null +++ b/.remarkrc.mjs @@ -0,0 +1,13 @@ +import remarkPresetLintRecommended from 'remark-preset-lint-recommended'; +import remarkFrontmatter from 'remark-frontmatter'; +import remarkMdx from 'remark-mdx'; +import remarkNoInlineCodeFences from './src/plugins/remark-no-inline-code-fences.mjs'; + +export default { + plugins: [ + remarkFrontmatter, + remarkMdx, + remarkPresetLintRecommended, + remarkNoInlineCodeFences, + ], +}; diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 00000000..76713da2 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,74 @@ +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; +import remarkGlossary from './src/plugins/remark-glossary.ts'; +import remarkCenter from './src/plugins/remark-center.ts'; +import remarkFigure from './src/plugins/remark-figure.ts'; +import remarkImageAttributes from './src/plugins/remark-image-attributes.ts'; +import { remarkMdxGlobalImports } from './src/plugins/remark-mdx-global-imports.ts'; +import remarkCodeRegion from './src/plugins/remark-code-region.ts'; + +export default defineConfig({ + site: 'https://frcsoftware.org', + prefetch: true, + + markdown: { + remarkPlugins: [ + remarkCenter, + remarkFigure, + remarkGlossary, + remarkImageAttributes, + remarkMdxGlobalImports, + remarkCodeRegion, + ], + rehypePlugins: [], + }, + + integrations: [ + starlight({ + title: 'FRCSoftware.org', + favicon: '/favicon.svg', + head: [ + { + tag: 'meta', + attrs: { + property: 'og:image', + content: 'https://frcsoftware.org/favicon.svg', + }, + }, + { + tag: 'meta', + attrs: { + property: 'og:image:alt', + content: 'FRCSoftware.org logo icon', + }, + }, + { + tag: 'meta', + attrs: { + property: 'og:description', + content: + 'The comprehensive learning guide for FRC programming', + }, + }, + ], + logo: { + src: './src/assets/universal/favicon-white.svg', + }, + customCss: ['./src/styles/global.css'], + components: { + Header: './src/starlightOverrides/Header.astro', + Footer: './src/starlightOverrides/Footer.astro', + Sidebar: './src/starlightOverrides/Sidebar.astro', + Pagination: './src/starlightOverrides/Pagination.astro', + Hero: './src/starlightOverrides/Hero.astro', + TableOfContents: + './src/starlightOverrides/TableOfContents.astro', + }, + // TOC is disabled globally but can be enabled per-directory in src/config/tocConfig.ts + // or per-page via frontmatter (tableOfContents: true) + tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 }, + // Sidebar configuration is now managed in src/config/sidebarConfig.ts + // This allows different sidebars per top-level navigation section + }), + ], +}); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..cd792a12 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - esbuild + - sharp diff --git a/src/plugins/remark-no-inline-code-fences.mjs b/src/plugins/remark-no-inline-code-fences.mjs new file mode 100644 index 00000000..d123c981 --- /dev/null +++ b/src/plugins/remark-no-inline-code-fences.mjs @@ -0,0 +1,29 @@ +import { visit } from 'unist-util-visit'; + +const IGNORE_RE = /rli:\s*ignore/; + +export default function remarkNoInlineCodeFences() { + return (tree, file) => { + visit(tree, 'code', (node, index, parent) => { + if (index !== undefined && parent) { + const prev = parent.children[index - 1]; + if ( + prev?.type === 'mdxFlowExpression' && + IGNORE_RE.test(prev.value) + ) { + return; + } + } + + if (node.meta?.trim()) return; + + if (node.value?.trim()) { + const msg = file.message( + 'Code fences must use the RLI system: ```language path/to/file#regionName', + node, + ); + msg.fatal = true; + } + }); + }; +} From 8a69f187b1263562e72a0e9776259ee3572f412d Mon Sep 17 00:00:00 2001 From: httphypixelnet <91695346+httphypixelnet@users.noreply.github.com> Date: Tue, 21 Jul 2026 02:51:18 +0000 Subject: [PATCH 05/13] locking that file real good --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a865fc6..be328363 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,6 @@ importers: astro: specifier: ^6.3.7 version: 6.3.7(@types/node@24.12.4)(rollup@4.59.0)(tsx@4.22.4)(yaml@2.9.0) - mdast-util-mdx-jsx: - specifier: ^3.2.0 - version: 3.2.0 sharp: specifier: ^0.34.5 version: 0.34.5 @@ -54,6 +51,9 @@ importers: mdast-util-mdx: specifier: ^3.0.0 version: 3.0.0 + mdast-util-mdx-jsx: + specifier: ^3.2.0 + version: 3.2.0 prettier: specifier: ^3.8.4 version: 3.8.4 From e8b0f3f2d8e4f95af267db45bdf133d1c54ddc83 Mon Sep 17 00:00:00 2001 From: httphypixelnet <91695346+httphypixelnet@users.noreply.github.com> Date: Tue, 21 Jul 2026 03:42:35 +0000 Subject: [PATCH 06/13] fix trailing whitespace --- src/components/Glossary.astro | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Glossary.astro b/src/components/Glossary.astro index 5a746714..1439de25 100644 --- a/src/components/Glossary.astro +++ b/src/components/Glossary.astro @@ -21,9 +21,7 @@ const definition = slotContent?.trim() || glossaryEntry?.definition || 'Definition not found'; --- - - {term} - +{term.trim()}