@grove-dev/starlight 0.3.1 → 0.3.2

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.
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@grove-dev/starlight",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Grove's theme for Starlight (the Astro native documentation site generator)",
6
6
  "author": "grove-dev",
7
7
  "license": "MIT",
8
+ "engines": {
9
+ "node": ">=22.12.0"
10
+ },
8
11
  "readme": "README.md",
9
12
  "homepage": "https://grove.dev.mn/",
10
13
  "repository": {
@@ -18,6 +21,18 @@
18
21
  "publishConfig": {
19
22
  "access": "public"
20
23
  },
24
+ "files": [
25
+ "components",
26
+ "core",
27
+ "styles",
28
+ "global.d.ts",
29
+ "index.ts",
30
+ "schema.ts",
31
+ "user-components.ts",
32
+ "virtual.d.ts",
33
+ "README.md",
34
+ "THIRD_PARTY_LICENSES.md"
35
+ ],
21
36
  "exports": {
22
37
  ".": "./index.ts",
23
38
  "./schema": "./schema.ts",
@@ -1,191 +0,0 @@
1
- /**
2
- * @grove-dev/starlight — config / override / virtual module tests.
3
- *
4
- * Coverage (per the brief):
5
- * - plugin.ts:parseConfig: accepts empty config (all defaults),
6
- * honours user-supplied fields, throws on invalid input
7
- * - virtual module resolution: the vite plugin's `load` and
8
- * `resolveId` hooks return the canonical module id when
9
- * called with the expected arguments, and pass through for
10
- * unrelated ids
11
- * - override composition: the override() function fills in
12
- * every component override slot, warns the user on a clash,
13
- * and preserves a pre-set user override (does NOT clobber it
14
- * with the default)
15
- */
16
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
17
- import { plugin } from "../core/plugin.js";
18
- import { override, COMPONENT_OVERRIDES } from "../core/config/override.js";
19
- import { vitePlugin } from "../core/config/vite.js";
20
- import { LucodeStarlightConfigSchema } from "../core/config/schemas.js";
21
-
22
- describe("parseConfig (exercised through plugin())", () => {
23
- it("accepts an empty config and fills in all defaults", () => {
24
- // We can't import parseConfig directly (it's local to
25
- // plugin.ts), so we exercise it through `plugin(undefined)`,
26
- // which goes through the same Zod parse path. The hook
27
- // callback is called with the parsed config; we read it
28
- // back via the test.
29
- let captured: unknown;
30
- const p = plugin();
31
- // The plugin's `config:setup` hook is the only place
32
- // parseConfig's output is consumed. Invoke it with a
33
- // minimal stub of the hook parameters and capture the
34
- // addIntegration's vite config.
35
- const addIntegration = vi.fn();
36
- const updateConfig = vi.fn();
37
- const calls: Array<{ vite?: { plugins?: Array<{ load?: unknown; resolveId?: unknown }> } }> = [];
38
- addIntegration.mockImplementation((integration: { hooks: { "astro:config:setup": (args: { updateConfig: (cfg: { vite?: { plugins?: Array<{ load?: unknown; resolveId?: unknown }> } }) => void }) => void } }) => {
39
- integration.hooks["astro:config:setup"]({
40
- updateConfig: (cfg) => calls.push(cfg),
41
- });
42
- });
43
- p.hooks["config:setup"]({
44
- config: { customCss: [] },
45
- logger: { warn: () => {}, info: () => {}, error: () => {}, debug: () => {} } as never,
46
- updateConfig: updateConfig as never,
47
- addIntegration: addIntegration as never,
48
- command: "build" as never,
49
- isRestart: false,
50
- });
51
- expect(calls).toHaveLength(1);
52
- const call = calls[0];
53
- const vite = call?.vite;
54
- expect(vite?.plugins).toBeDefined();
55
- captured = vite;
56
- // Sanity: parseConfig didn't throw on the empty input.
57
- expect(captured).toBeDefined();
58
- });
59
-
60
- it("honours user-supplied docs.includeAiUtilities", () => {
61
- // Capture the vite plugin's resolved config to assert
62
- // that the user's boolean is preserved.
63
- let capturedPlugin: ReturnType<typeof vitePlugin> | undefined;
64
- const addIntegration = vi.fn((integration: { hooks: { "astro:config:setup": (args: { updateConfig: (cfg: { vite: { plugins: Array<ReturnType<typeof vitePlugin>> } }) => void }) => void } }) => {
65
- integration.hooks["astro:config:setup"]({
66
- updateConfig: (cfg) => {
67
- capturedPlugin = cfg.vite.plugins[0];
68
- },
69
- });
70
- });
71
- const p = plugin({ docs: { includeAiUtilities: true } });
72
- p.hooks["config:setup"]({
73
- config: { customCss: [] },
74
- logger: { warn: () => {}, info: () => {}, error: () => {}, debug: () => {} } as never,
75
- updateConfig: vi.fn() as never,
76
- addIntegration: addIntegration as never,
77
- command: "build" as never,
78
- isRestart: false,
79
- });
80
- expect(capturedPlugin).toBeDefined();
81
- // The vite plugin's moduleContent embeds the JSON-serialized
82
- // config. Load it to confirm `includeAiUtilities: true`
83
- // survived the parse.
84
- const loaded = capturedPlugin!.load("\0virtual:lucode-starlight-config") as string;
85
- expect(loaded).toContain('"includeAiUtilities":true');
86
- });
87
-
88
- it("falls back to the schema default for an empty docs object", () => {
89
- // The schema has `.default({ includeAiUtilities: false })`
90
- // for the docs sub-object. Pin it.
91
- const parsed = LucodeStarlightConfigSchema.parse({});
92
- expect(parsed.docs?.includeAiUtilities).toBe(false);
93
- });
94
-
95
- it("throws on invalid input (zod validation failure)", () => {
96
- // A non-string label fails the linkSchema validation.
97
- expect(() => LucodeStarlightConfigSchema.parse({ navLinks: "not-an-array" })).toThrow();
98
- });
99
- });
100
-
101
- describe("vitePlugin — virtual module resolution", () => {
102
- it("resolveId returns the resolved id for the canonical module id", () => {
103
- const p = vitePlugin(LucodeStarlightConfigSchema.parse({}));
104
- // The virtual module prefix is 'virtual:'. The plugin
105
- // matches on the un-prefixed name and returns the
106
- // '\0'-prefixed resolved id (Vite convention for virtual
107
- // modules — the null byte prevents the resolved id from
108
- // being treated as a real file path).
109
- const result = p.resolveId?.("virtual:lucode-starlight-config");
110
- expect(result).toBe("\0virtual:lucode-starlight-config");
111
- });
112
-
113
- it("resolveId returns undefined for unrelated ids (passthrough)", () => {
114
- const p = vitePlugin(LucodeStarlightConfigSchema.parse({}));
115
- expect(p.resolveId?.("virtual:some-other-module")).toBeUndefined();
116
- expect(p.resolveId?.("./relative-import")).toBeUndefined();
117
- });
118
-
119
- it("load returns the JSON-serialized config when given the resolved id", () => {
120
- const config = LucodeStarlightConfigSchema.parse({ docs: { includeAiUtilities: true } });
121
- const p = vitePlugin(config);
122
- const module = p.load?.("\0virtual:lucode-starlight-config") as string;
123
- expect(module).toContain("export default");
124
- expect(module).toContain('"includeAiUtilities":true');
125
- });
126
-
127
- it("load returns undefined for unrelated ids", () => {
128
- const p = vitePlugin(LucodeStarlightConfigSchema.parse({}));
129
- expect(p.load?.("not-the-virtual-id")).toBeUndefined();
130
- });
131
- });
132
-
133
- describe("override — component override composition", () => {
134
- let warnSpy: ReturnType<typeof vi.spyOn>;
135
-
136
- beforeEach(() => {
137
- warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
138
- });
139
-
140
- afterEach(() => {
141
- warnSpy.mockRestore();
142
- });
143
-
144
- it("fills every component override slot when the user has none", () => {
145
- // Starlight starts with no components overridden (empty
146
- // config). The override() function should set every key in
147
- // COMPONENT_OVERRIDES to point at the @grove-dev/starlight
148
- // path.
149
- const components = override(
150
- { components: {} } as never,
151
- COMPONENT_OVERRIDES,
152
- { warn: warnSpy, info: () => {}, error: () => {}, debug: () => {} } as never,
153
- );
154
- for (const key of COMPONENT_OVERRIDES) {
155
- expect(components?.[key]).toBe(`@grove-dev/starlight/components/overrides/${key}.astro`);
156
- }
157
- });
158
-
159
- it("preserves a user-supplied component override (does not clobber)", () => {
160
- // If the user already set components.Footer, we should
161
- // NOT overwrite it with the default — we warn and skip.
162
- // Pin: a previous "always overwrite" implementation would
163
- // silently clobber the user's custom footer.
164
- const userFooter = "./my-custom/Footer.astro";
165
- const components = override(
166
- { components: { Footer: userFooter } } as never,
167
- COMPONENT_OVERRIDES,
168
- { warn: warnSpy, info: () => {}, error: () => {}, debug: () => {} } as never,
169
- );
170
- expect(components?.Footer).toBe(userFooter);
171
- // The warn call names the slot and points at the override path.
172
- const warnings = warnSpy.mock.calls.map((c: unknown[]) => String(c[0]));
173
- expect(warnings.some((w: string) => w.includes("Footer"))).toBe(true);
174
- // All OTHER slots are still filled in.
175
- expect(components?.Header).toBe(`@grove-dev/starlight/components/overrides/Header.astro`);
176
- });
177
-
178
- it("handles a config with no `components` field at all (undefined)", () => {
179
- // Defensive: Starlight's config can omit `components`
180
- // entirely. override() should still produce a full set
181
- // without throwing.
182
- const components = override(
183
- {} as never,
184
- COMPONENT_OVERRIDES,
185
- { warn: warnSpy, info: () => {}, error: () => {}, debug: () => {} } as never,
186
- );
187
- for (const key of COMPONENT_OVERRIDES) {
188
- expect(components?.[key]).toBeDefined();
189
- }
190
- });
191
- });
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": ".",
5
- "noEmit": true,
6
- "types": ["node", "vitest/globals"]
7
- },
8
- "include": ["core/**/*.ts", "tests/**/*.ts", "global.d.ts", "virtual.d.ts", "index.ts", "schema.ts", "user-components.ts"]
9
- }