@nu-appdev/northwestern-starlight-theme 1.4.0 → 1.5.0

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,97 @@
1
+ import type { AstroIntegration } from "astro";
2
+ import type { AstroMermaidOptions } from "astro-mermaid";
3
+ /**
4
+ * Configuration options for the Northwestern Mermaid integration.
5
+ *
6
+ * Extends `astro-mermaid` options with a `toolbar` toggle that controls
7
+ * the hover toolbar (fullscreen, download, copy) on rendered diagrams.
8
+ *
9
+ * @see {@link northwesternMermaid} for the integration factory
10
+ */
11
+ export interface NorthwesternMermaidOptions extends AstroMermaidOptions {
12
+ /**
13
+ * Show the hover toolbar (fullscreen, download SVG, copy source) on diagrams.
14
+ *
15
+ * @default true
16
+ */
17
+ toolbar?: boolean;
18
+ }
19
+ /**
20
+ * Theme mode for Mermaid color palette generation.
21
+ *
22
+ * Maps to the `data-theme` attribute on `<html>`: `"light"` for the default
23
+ * palette, `"dark"` for the inverted palette with lighter primary colors
24
+ * and darker canvas backgrounds.
25
+ */
26
+ export type NorthwesternMermaidMode = "light" | "dark";
27
+ /**
28
+ * Generate a complete `astro-mermaid` config with Northwestern-branded colors
29
+ * for the given theme mode.
30
+ *
31
+ * Builds a Mermaid `themeVariables` object from the Northwestern brand palette,
32
+ * deriving all node, edge, label, chart, and diagram-specific colors from a
33
+ * small set of brand primaries via `khroma` color manipulation.
34
+ *
35
+ * @param mode - `"light"` or `"dark"`. Controls canvas, text, and primary color values.
36
+ * @returns A complete `AstroMermaidOptions` object ready to pass to `astro-mermaid`.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { createNorthwesternMermaidConfig } from "@nu-appdev/northwestern-starlight-theme/mermaid";
41
+ *
42
+ * const darkConfig = createNorthwesternMermaidConfig("dark");
43
+ * // darkConfig.mermaidConfig.themeVariables contains all colors
44
+ * ```
45
+ */
46
+ export declare function createNorthwesternMermaidConfig(mode: NorthwesternMermaidMode): AstroMermaidOptions;
47
+ /**
48
+ * Pre-built light-mode Mermaid config with Northwestern brand colors.
49
+ *
50
+ * Used as the default when Mermaid is auto-detected. Override individual
51
+ * `themeVariables` by passing a `mermaid` object to {@link northwesternTheme}
52
+ * in `index.ts`, or use {@link createNorthwesternMermaidConfig} for full control.
53
+ */
54
+ export declare const defaultMermaidConfig: AstroMermaidOptions;
55
+ /**
56
+ * Pre-built dark-mode Mermaid config with Northwestern brand colors.
57
+ *
58
+ * Applied at runtime when the user switches to dark mode. The toolbar script
59
+ * re-renders diagrams with this config via `window.__NU_MERMAID_CONFIGS__.dark`.
60
+ */
61
+ export declare const darkMermaidConfig: AstroMermaidOptions;
62
+ /**
63
+ * Create an Astro integration that registers Northwestern-branded Mermaid diagrams.
64
+ *
65
+ * Wraps `astro-mermaid` with Northwestern color palettes for both light and dark
66
+ * modes, and injects the toolbar script (fullscreen viewer, download, copy).
67
+ *
68
+ * **Note:** `defineNorthwesternConfig` handles Mermaid integration ordering.
69
+ * This function is only needed for manual setups.
70
+ *
71
+ * **Must be added before `starlight()` in the `integrations` array.** The
72
+ * `astro-mermaid` remark plugin needs to register before Starlight's rehype
73
+ * processing, and Astro processes integrations in order. Placing it after
74
+ * `starlight()` causes Mermaid code blocks to be treated as plain code.
75
+ *
76
+ * @param options - Merged with Northwestern defaults. Set `toolbar: false` to
77
+ * disable the hover toolbar.
78
+ * @returns An Astro integration to add to `integrations` in your Astro config.
79
+ *
80
+ * @example Manual setup
81
+ * ```ts
82
+ * import { northwesternMermaid } from "@nu-appdev/northwestern-starlight-theme/mermaid";
83
+ * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
84
+ *
85
+ * export default defineConfig({
86
+ * integrations: [
87
+ * northwesternMermaid(), // Must come before starlight()
88
+ * starlight({
89
+ * plugins: [northwesternTheme()],
90
+ * title: "My Docs",
91
+ * }),
92
+ * ],
93
+ * });
94
+ * ```
95
+ */
96
+ export declare function northwesternMermaid(options?: NorthwesternMermaidOptions): AstroIntegration;
97
+ //# sourceMappingURL=mermaid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../mermaid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAIzD;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA2B,SAAQ,mBAAmB;IACnE;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,MAAM,CAAC;AAwUvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB,CAmBlG;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,mBAA8D,CAAC;AAElG;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,mBAA6D,CAAC;AAE9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,gBAAgB,CAqD9F"}
@@ -0,0 +1,141 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Non-empty string helper used when reading titles from mixed Starlight config shapes.
4
+ *
5
+ * Trims surrounding whitespace and rejects empty strings after trimming.
6
+ */
7
+ export declare const nonEmptyStringSchema: z.ZodString;
8
+ /**
9
+ * Top-level configuration for the Northwestern Starlight theme plugin.
10
+ *
11
+ * With `defineNorthwesternConfig`, pass this object as the `theme` key.
12
+ * With `northwesternTheme()` directly, pass it as the function argument.
13
+ */
14
+ export declare const northwesternThemeConfigSchema: z.ZodObject<{
15
+ /**
16
+ * Homepage hero layout configuration.
17
+ */
18
+ homepage: z.ZodOptional<z.ZodObject<{
19
+ layout: z.ZodOptional<z.ZodEnum<{
20
+ centered: "centered";
21
+ split: "split";
22
+ }>>;
23
+ showTitle: z.ZodOptional<z.ZodBoolean>;
24
+ imageWidth: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strict>>;
26
+ /**
27
+ * Mermaid diagram support.
28
+ *
29
+ * - `true`: auto-detect Mermaid packages and enable support when available
30
+ * - `false`: disable Mermaid integration entirely
31
+ * - `object`: merge custom Mermaid options with Northwestern defaults
32
+ */
33
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
34
+ /**
35
+ * Open Graph image generation.
36
+ *
37
+ * Generates a branded 1200x630 image for each docs page when `site`
38
+ * is configured in `astro.config.ts`.
39
+ */
40
+ ogImage: z.ZodOptional<z.ZodBoolean>;
41
+ }, z.core.$strict>;
42
+ /**
43
+ * Configuration options for the standalone Northwestern Mermaid integration.
44
+ *
45
+ * This intentionally validates only the Northwestern-owned wrapper surface and
46
+ * allows additional upstream `astro-mermaid` options to pass through unchanged.
47
+ */
48
+ export declare const northwesternMermaidOptionsSchema: z.ZodObject<{
49
+ /**
50
+ * Show the hover toolbar on rendered diagrams.
51
+ *
52
+ * The toolbar provides fullscreen, download, and copy-source actions.
53
+ */
54
+ toolbar: z.ZodOptional<z.ZodBoolean>;
55
+ }, z.core.$loose>;
56
+ /**
57
+ * Configuration options for `defineNorthwesternConfig()`.
58
+ *
59
+ * This schema validates the Northwestern-owned wrapper surface while allowing
60
+ * the rest of Astro's top-level config to pass through untouched.
61
+ */
62
+ export declare const northwesternConfigOptionsSchema: z.ZodObject<{
63
+ /**
64
+ * Full Starlight configuration object.
65
+ *
66
+ * This is forwarded to `starlight()` after Northwestern defaults and
67
+ * helper-managed integration ordering are applied.
68
+ */
69
+ starlight: z.ZodObject<{}, z.core.$loose>;
70
+ /**
71
+ * Northwestern theme plugin options.
72
+ */
73
+ theme: z.ZodOptional<z.ZodObject<{
74
+ /**
75
+ * Homepage hero layout configuration.
76
+ */
77
+ homepage: z.ZodOptional<z.ZodObject<{
78
+ layout: z.ZodOptional<z.ZodEnum<{
79
+ centered: "centered";
80
+ split: "split";
81
+ }>>;
82
+ showTitle: z.ZodOptional<z.ZodBoolean>;
83
+ imageWidth: z.ZodOptional<z.ZodString>;
84
+ }, z.core.$strict>>;
85
+ /**
86
+ * Mermaid diagram support.
87
+ *
88
+ * - `true`: auto-detect Mermaid packages and enable support when available
89
+ * - `false`: disable Mermaid integration entirely
90
+ * - `object`: merge custom Mermaid options with Northwestern defaults
91
+ */
92
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
93
+ /**
94
+ * Open Graph image generation.
95
+ *
96
+ * Generates a branded 1200x630 image for each docs page when `site`
97
+ * is configured in `astro.config.ts`.
98
+ */
99
+ ogImage: z.ZodOptional<z.ZodBoolean>;
100
+ }, z.core.$strict>>;
101
+ /**
102
+ * Mermaid diagram support.
103
+ *
104
+ * - `true`: add Northwestern Mermaid with defaults
105
+ * - `false`: disable Mermaid
106
+ * - `object`: add Northwestern Mermaid with custom options
107
+ */
108
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
109
+ /**
110
+ * Show the hover toolbar on rendered diagrams.
111
+ *
112
+ * The toolbar provides fullscreen, download, and copy-source actions.
113
+ */
114
+ toolbar: z.ZodOptional<z.ZodBoolean>;
115
+ }, z.core.$loose>]>>;
116
+ /**
117
+ * Additional Starlight plugins to register after the Northwestern theme.
118
+ */
119
+ plugins: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
120
+ /**
121
+ * Escape hatch for advanced integration ordering.
122
+ *
123
+ * Use `before` for integrations that must run before Mermaid/Starlight,
124
+ * and `after` for integrations that should be appended after Starlight.
125
+ */
126
+ integrations: z.ZodOptional<z.ZodObject<{
127
+ /**
128
+ * Integrations added before Mermaid and Starlight.
129
+ */
130
+ before: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
131
+ /**
132
+ * Integrations added after Starlight.
133
+ */
134
+ after: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
135
+ }, z.core.$strict>>;
136
+ }, z.core.$loose>;
137
+ /**
138
+ * Validate a public config object and throw a friendly package-scoped error on failure.
139
+ */
140
+ export declare function validateSchema<T>(schema: z.ZodType<T>, value: unknown, scope: string): T;
141
+ //# sourceMappingURL=config-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../src/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,aAIoD,CAAC;AA6DtF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B;IAElC;;OAEG;;;;;;;;;IAKH;;;;;;OAMG;;IASH;;;;;OAKG;;kBAOL,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC;IAErC;;;;OAIG;;iBAQL,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B;IAEpC;;;;;OAKG;;IAKH;;OAEG;;QA9EH;;WAEG;;;;;;;;;QAKH;;;;;;WAMG;;QASH;;;;;WAKG;;;IAwDH;;;;;;OAMG;;QA7CH;;;;WAIG;;;IA+CH;;OAEG;;IAKH;;;;;OAKG;;QAGK;;WAEG;;QAKH;;WAEG;;;iBAYb,CAAC;AA2BP;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAQxF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Rehype plugin that wraps `<table>` elements in a scrollable container at build time.
3
+ *
4
+ * Inserts a `<div class="nu-table-scroll">` around each table with `tabindex="0"`,
5
+ * `role="region"`, and `aria-label` for keyboard scrollability. Runs during the
6
+ * Astro build so the HTML arrives with the wrapper already in place.
7
+ */
8
+ import type { Root } from "hast";
9
+ export default function rehypeTableScroll(): (tree: Root) => void;
10
+ //# sourceMappingURL=rehype-table-scroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-table-scroll.d.ts","sourceRoot":"","sources":["../../src/rehype-table-scroll.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAkB,IAAI,EAAE,MAAM,MAAM,CAAC;AAgCjD,MAAM,CAAC,OAAO,UAAU,iBAAiB,KAC7B,MAAM,IAAI,UACrB"}
@@ -0,0 +1,35 @@
1
+ import type { StarlightExpressiveCodeOptions } from "@astrojs/starlight/expressive-code";
2
+ import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
3
+
4
+ /**
5
+ * Pre-configured Expressive Code options for the Northwestern Starlight theme.
6
+ *
7
+ * Adds the line-numbers plugin and sets sensible defaults. Pass your own
8
+ * options to merge with the Northwestern defaults.
9
+ *
10
+ * **Note:** `defineNorthwesternConfig` includes these options.
11
+ * This helper is only needed for manual setups using `northwesternTheme()` directly.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // ec.config.mjs (manual setup only)
16
+ * import { defineEcConfig } from "@astrojs/starlight/expressive-code";
17
+ * import { northwesternExpressiveCode } from "@nu-appdev/northwestern-starlight-theme/expressive-code";
18
+ *
19
+ * export default defineEcConfig(northwesternExpressiveCode());
20
+ * ```
21
+ */
22
+ export function northwesternExpressiveCode(
23
+ config: StarlightExpressiveCodeOptions = {},
24
+ ): StarlightExpressiveCodeOptions {
25
+ const { defaultProps, plugins, ...rest } = config;
26
+
27
+ return {
28
+ ...rest,
29
+ plugins: [pluginLineNumbers(), ...(plugins ?? [])],
30
+ defaultProps: {
31
+ showLineNumbers: false,
32
+ ...(defaultProps ?? {}),
33
+ },
34
+ };
35
+ }
package/index.ts CHANGED
@@ -4,7 +4,8 @@ import { createRequire } from "node:module";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import type { StarlightPlugin } from "@astrojs/starlight/types";
7
- import rehypeTableScroll from "./src/rehype-table-scroll.ts";
7
+ import { nonEmptyStringSchema, northwesternThemeConfigSchema, validateSchema } from "./src/config-schema";
8
+ import rehypeTableScroll from "./src/rehype-table-scroll";
8
9
 
9
10
  /**
10
11
  * Configuration for the homepage hero section.
@@ -54,21 +55,16 @@ export interface NorthwesternHomepageConfig {
54
55
  /**
55
56
  * Top-level configuration for the Northwestern Starlight theme plugin.
56
57
  *
57
- * Pass to {@link northwesternTheme} when registering the plugin in your
58
- * Starlight config. All properties are optional and have sensible defaults.
58
+ * With `defineNorthwesternConfig`, pass these as the `theme` key.
59
+ * With `northwesternTheme()` directly, pass them as the function argument.
60
+ * All properties are optional.
59
61
  *
60
62
  * @example
61
63
  * ```ts
62
- * // astro.config.ts
63
- * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
64
- *
65
- * export default defineConfig({
66
- * integrations: [
67
- * starlight({
68
- * plugins: [northwesternTheme({ homepage: { layout: "split" } })],
69
- * }),
70
- * ],
71
- * });
64
+ * defineNorthwesternConfig({
65
+ * starlight: { title: "My Docs" },
66
+ * theme: { homepage: { layout: "split" } },
67
+ * })
72
68
  * ```
73
69
  *
74
70
  * @see {@link NorthwesternHomepageConfig} for homepage hero options
@@ -87,8 +83,7 @@ export interface NorthwesternThemeConfig {
87
83
  * - `true` (default) — auto-detect: enables Mermaid if `astro-mermaid` and `mermaid`
88
84
  * are installed, skips silently if they are not
89
85
  * - `false` — disables Mermaid entirely
90
- * - `object` — enables Mermaid with custom {@link https://mermaid.js.org/config/schema-docs/config.html | MermaidConfig}
91
- * merged with Northwestern defaults
86
+ * - `object` — enables Mermaid with custom Mermaid config merged with Northwestern defaults
92
87
  *
93
88
  * @default true
94
89
  */
@@ -107,6 +102,18 @@ export interface NorthwesternThemeConfig {
107
102
  ogImage?: boolean;
108
103
  }
109
104
 
105
+ function getSiteTitle(title: unknown): string {
106
+ const parsedTitle = nonEmptyStringSchema.safeParse(title);
107
+ if (parsedTitle.success) return parsedTitle.data;
108
+ if (title && typeof title === "object") {
109
+ for (const value of Object.values(title as Record<string, unknown>)) {
110
+ const parsedValue = nonEmptyStringSchema.safeParse(value);
111
+ if (parsedValue.success) return parsedValue.data;
112
+ }
113
+ }
114
+ return "";
115
+ }
116
+
110
117
  /**
111
118
  * Try to resolve a module. Returns true if it can be imported.
112
119
  */
@@ -129,10 +136,14 @@ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
129
136
  * component overrides (Hero, ThemeToggle, EditLink), and optional Mermaid diagram
130
137
  * support with branded color schemes.
131
138
  *
139
+ * **Recommended:** Use {@link https://github.com/NUAppDev/northwestern-starlight-theme | defineNorthwesternConfig}
140
+ * from `@nu-appdev/northwestern-starlight-theme/config` instead. It handles
141
+ * integration ordering, Mermaid, and Expressive Code (line numbers, GitHub themes).
142
+ *
132
143
  * @param config - Theme configuration. All properties optional.
133
144
  * @returns A Starlight plugin to pass to `plugins` in your Starlight config.
134
145
  *
135
- * @example Basic usage (all defaults)
146
+ * @example Manual setup
136
147
  * ```ts
137
148
  * starlight({ plugins: [northwesternTheme()] })
138
149
  * ```
@@ -150,7 +161,11 @@ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
150
161
  * ```
151
162
  */
152
163
  export default function northwesternTheme(config: NorthwesternThemeConfig = {}): StarlightPlugin {
153
- const { homepage = {}, mermaid = true, ogImage = true } = config;
164
+ const {
165
+ homepage = {},
166
+ mermaid = true,
167
+ ogImage = true,
168
+ } = validateSchema(northwesternThemeConfigSchema, config, "theme config");
154
169
  const themeConfig = {
155
170
  homepage: {
156
171
  layout: homepage.layout ?? "centered",
@@ -161,6 +176,7 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
161
176
  enabled: false, // set to true in config:setup when ogImage is enabled
162
177
  siteTitle: "",
163
178
  logoPath: "",
179
+ resvgWasmPath: "",
164
180
  },
165
181
  };
166
182
 
@@ -268,36 +284,18 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
268
284
  }
269
285
  }
270
286
 
271
- // OG image generation (bundled via astro-og-canvas)
287
+ // OG image generation (bundled via satori + @resvg/resvg-wasm)
272
288
  if (ogImage) {
273
- // canvaskit-wasm uses __dirname to locate its WASM binary, which
274
- // doesn't exist in ESM. Under pnpm's strict symlinks the binary
275
- // can't be found unless the consumer installs canvaskit-wasm
276
- // directly. Probe resolution from the consumer's project root
277
- // (not the theme package) so a missing install skips OG
278
- // gracefully instead of crashing the build.
279
- let canvaskitUsable = true;
280
- try {
281
- const require = createRequire(join(process.cwd(), "package.json"));
282
- require.resolve("canvaskit-wasm");
283
- } catch {
284
- canvaskitUsable = false;
285
- }
289
+ const siteTitle = getSiteTitle(starlightConfig.title);
286
290
 
287
- if (!canvaskitUsable) {
291
+ if (!siteTitle) {
288
292
  logger.warn(
289
- "OG image generation requires canvaskit-wasm, which could not be loaded.\n" +
290
- " pnpm users: run `pnpm add canvaskit-wasm`\n" +
291
- " The build will continue without OG images.",
293
+ "Open Graph image generation was disabled because Starlight `title` is empty.\n" +
294
+ " Set `title` in starlight({...}) so OG images and structured data have a site name.",
292
295
  );
293
296
  } else {
294
- themeConfig.ogImage.enabled = true;
295
297
  themeConfig.ogImage.logoPath = faviconPath;
296
- const rawTitle = starlightConfig.title;
297
- themeConfig.ogImage.siteTitle =
298
- typeof rawTitle === "string"
299
- ? rawTitle
300
- : (Object.values(rawTitle as Record<string, string>)[0] ?? "");
298
+ themeConfig.ogImage.siteTitle = siteTitle;
301
299
 
302
300
  addRouteMiddleware({
303
301
  entrypoint: "@nu-appdev/northwestern-starlight-theme/src/og/route-middleware",
@@ -307,16 +305,29 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
307
305
  addIntegration({
308
306
  name: "northwestern-theme-og-image",
309
307
  hooks: {
310
- "astro:config:setup": ({ injectRoute }) => {
308
+ "astro:config:setup": ({ config: astroConfig, injectRoute }) => {
309
+ if (!astroConfig.site) {
310
+ logger.warn(
311
+ "Open Graph image generation was disabled because `site` is not set.\n" +
312
+ " Set `site` in astro.config.ts to enable absolute OG image URLs.",
313
+ );
314
+ return;
315
+ }
316
+
317
+ themeConfig.ogImage.enabled = true;
318
+ const require = createRequire(import.meta.url);
319
+ themeConfig.ogImage.resvgWasmPath = require.resolve(
320
+ "@resvg/resvg-wasm/index_bg.wasm",
321
+ );
322
+
311
323
  injectRoute({
312
324
  pattern: "/og/[...slug]",
313
325
  entrypoint: "@nu-appdev/northwestern-starlight-theme/src/og/endpoint.ts",
314
326
  });
327
+ logger.info("OG image generation enabled");
315
328
  },
316
329
  },
317
330
  });
318
-
319
- logger.info("OG image generation enabled");
320
331
  }
321
332
  }
322
333
 
package/mermaid.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { AstroIntegration } from "astro";
2
- import mermaid, { type AstroMermaidOptions } from "astro-mermaid";
2
+ import type { AstroMermaidOptions } from "astro-mermaid";
3
3
  import { darken, isDark, lighten, mix, transparentize } from "khroma";
4
+ import { northwesternMermaidOptionsSchema, validateSchema } from "./src/config-schema";
4
5
 
5
6
  /**
6
7
  * Configuration options for the Northwestern Mermaid integration.
@@ -417,6 +418,9 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
417
418
  * Wraps `astro-mermaid` with Northwestern color palettes for both light and dark
418
419
  * modes, and injects the toolbar script (fullscreen viewer, download, copy).
419
420
  *
421
+ * **Note:** `defineNorthwesternConfig` handles Mermaid integration ordering.
422
+ * This function is only needed for manual setups.
423
+ *
420
424
  * **Must be added before `starlight()` in the `integrations` array.** The
421
425
  * `astro-mermaid` remark plugin needs to register before Starlight's rehype
422
426
  * processing, and Astro processes integrations in order. Placing it after
@@ -426,7 +430,7 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
426
430
  * disable the hover toolbar.
427
431
  * @returns An Astro integration to add to `integrations` in your Astro config.
428
432
  *
429
- * @example
433
+ * @example Manual setup
430
434
  * ```ts
431
435
  * import { northwesternMermaid } from "@nu-appdev/northwestern-starlight-theme/mermaid";
432
436
  * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
@@ -443,7 +447,11 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
443
447
  * ```
444
448
  */
445
449
  export function northwesternMermaid(options: NorthwesternMermaidOptions = {}): AstroIntegration {
446
- const { toolbar = true, ...overrides } = options;
450
+ const { toolbar = true, ...overrides } = validateSchema(
451
+ northwesternMermaidOptionsSchema,
452
+ options,
453
+ "Mermaid config",
454
+ );
447
455
  const lightConfig = createNorthwesternMermaidConfig("light");
448
456
  const darkConfig = createNorthwesternMermaidConfig("dark");
449
457
 
@@ -463,19 +471,21 @@ export function northwesternMermaid(options: NorthwesternMermaidOptions = {}): A
463
471
 
464
472
  const mergedLightMermaidConfig = mergeWithOverrides(lightConfig.mermaidConfig as Record<string, unknown>);
465
473
  const mergedDarkMermaidConfig = mergeWithOverrides(darkConfig.mermaidConfig as Record<string, unknown>);
466
-
467
- const mermaidIntegration = mermaid({
468
- ...lightConfig,
469
- ...overrides,
470
- enableLog: false,
471
- mermaidConfig: mergedLightMermaidConfig,
472
- });
474
+ const mermaidModulePromise = import("astro-mermaid");
473
475
 
474
476
  return {
475
477
  name: "northwestern-mermaid",
476
478
  hooks: {
477
- "astro:config:setup"(params) {
478
- mermaidIntegration.hooks["astro:config:setup"]?.(params);
479
+ async "astro:config:setup"(params) {
480
+ const { default: mermaid } = await mermaidModulePromise;
481
+ const mermaidIntegration = mermaid({
482
+ ...lightConfig,
483
+ ...overrides,
484
+ enableLog: false,
485
+ mermaidConfig: mergedLightMermaidConfig,
486
+ });
487
+
488
+ await mermaidIntegration.hooks["astro:config:setup"]?.(params);
479
489
 
480
490
  params.injectScript(
481
491
  "page",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-appdev/northwestern-starlight-theme",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A Northwestern-branded theme for Astro Starlight",
5
5
  "license": "MIT",
6
6
  "author": "Danny Foster <danny@northwestern.edu>",
@@ -28,14 +28,19 @@
28
28
  },
29
29
  "exports": {
30
30
  ".": {
31
- "types": "./index.ts",
31
+ "types": "./dist/index.d.ts",
32
32
  "import": "./index.ts"
33
33
  },
34
34
  "./expressive-code": {
35
- "import": "./expressive-code.mjs"
35
+ "types": "./dist/expressive-code.d.ts",
36
+ "import": "./expressive-code.ts"
37
+ },
38
+ "./config": {
39
+ "types": "./dist/config.d.ts",
40
+ "import": "./config.ts"
36
41
  },
37
42
  "./mermaid": {
38
- "types": "./mermaid.ts",
43
+ "types": "./dist/mermaid.d.ts",
39
44
  "import": "./mermaid.ts"
40
45
  },
41
46
  "./components": {
@@ -50,15 +55,19 @@
50
55
  },
51
56
  "files": [
52
57
  "CHANGELOG.md",
53
- "expressive-code.mjs",
58
+ "dist/",
59
+ "config.ts",
60
+ "expressive-code.ts",
54
61
  "index.ts",
55
62
  "mermaid.ts",
56
63
  "src/"
57
64
  ],
58
65
  "dependencies": {
59
66
  "@expressive-code/plugin-line-numbers": "^0.41.7",
60
- "astro-og-canvas": "^0.10.1",
61
- "khroma": "^2.1.0"
67
+ "@resvg/resvg-wasm": "^2.6.2",
68
+ "khroma": "^2.1.0",
69
+ "satori": "^0.26.0",
70
+ "zod": "^4.3.6"
62
71
  },
63
72
  "peerDependencies": {
64
73
  "@astrojs/starlight": ">=0.32.0",
@@ -75,6 +84,10 @@
75
84
  }
76
85
  },
77
86
  "devDependencies": {
78
- "@types/hast": "^3.0.4"
87
+ "@types/hast": "^3.0.4",
88
+ "typescript": "^6.0.2"
89
+ },
90
+ "scripts": {
91
+ "build": "tsc -p tsconfig.build.json --noCheck"
79
92
  }
80
93
  }