@specglass/core 0.0.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/dist/config/defaults.d.ts +4 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +31 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/define-config.d.ts +17 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +55 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +74 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +366 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +109 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/content/frontmatter-schema.d.ts +53 -0
- package/dist/content/frontmatter-schema.d.ts.map +1 -0
- package/dist/content/frontmatter-schema.js +27 -0
- package/dist/content/frontmatter-schema.js.map +1 -0
- package/dist/content/mdx-loader.d.ts +93 -0
- package/dist/content/mdx-loader.d.ts.map +1 -0
- package/dist/content/mdx-loader.js +97 -0
- package/dist/content/mdx-loader.js.map +1 -0
- package/dist/content/openapi-loader.d.ts +40 -0
- package/dist/content/openapi-loader.d.ts.map +1 -0
- package/dist/content/openapi-loader.js +58 -0
- package/dist/content/openapi-loader.js.map +1 -0
- package/dist/content/rehype-code-blocks.d.ts +15 -0
- package/dist/content/rehype-code-blocks.d.ts.map +1 -0
- package/dist/content/rehype-code-blocks.js +84 -0
- package/dist/content/rehype-code-blocks.js.map +1 -0
- package/dist/errors/specglass-error.d.ts +12 -0
- package/dist/errors/specglass-error.d.ts.map +1 -0
- package/dist/errors/specglass-error.js +19 -0
- package/dist/errors/specglass-error.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +41 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +276 -0
- package/dist/integration.js.map +1 -0
- package/dist/navigation/builder.d.ts +24 -0
- package/dist/navigation/builder.d.ts.map +1 -0
- package/dist/navigation/builder.js +169 -0
- package/dist/navigation/builder.js.map +1 -0
- package/dist/navigation/index.d.ts +7 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/navigation/meta-parser.d.ts +9 -0
- package/dist/navigation/meta-parser.d.ts.map +1 -0
- package/dist/navigation/meta-parser.js +59 -0
- package/dist/navigation/meta-parser.js.map +1 -0
- package/dist/navigation/meta-schema.d.ts +77 -0
- package/dist/navigation/meta-schema.d.ts.map +1 -0
- package/dist/navigation/meta-schema.js +31 -0
- package/dist/navigation/meta-schema.js.map +1 -0
- package/dist/navigation/watcher.d.ts +44 -0
- package/dist/navigation/watcher.d.ts.map +1 -0
- package/dist/navigation/watcher.js +81 -0
- package/dist/navigation/watcher.js.map +1 -0
- package/dist/openapi/parser.d.ts +24 -0
- package/dist/openapi/parser.d.ts.map +1 -0
- package/dist/openapi/parser.js +53 -0
- package/dist/openapi/parser.js.map +1 -0
- package/dist/openapi/transformer.d.ts +19 -0
- package/dist/openapi/transformer.d.ts.map +1 -0
- package/dist/openapi/transformer.js +294 -0
- package/dist/openapi/transformer.js.map +1 -0
- package/dist/openapi/types.d.ts +109 -0
- package/dist/openapi/types.d.ts.map +1 -0
- package/dist/openapi/types.js +11 -0
- package/dist/openapi/types.js.map +1 -0
- package/dist/openapi/utils.d.ts +26 -0
- package/dist/openapi/utils.d.ts.map +1 -0
- package/dist/openapi/utils.js +34 -0
- package/dist/openapi/utils.js.map +1 -0
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/navigation.d.ts +62 -0
- package/dist/types/navigation.d.ts.map +1 -0
- package/dist/types/navigation.js +2 -0
- package/dist/types/navigation.js.map +1 -0
- package/dist/virtual/modules.d.ts +34 -0
- package/dist/virtual/modules.d.ts.map +1 -0
- package/dist/virtual/modules.js +37 -0
- package/dist/virtual/modules.js.map +1 -0
- package/package.json +51 -0
- package/src/pages/[...slug].astro +55 -0
- package/src/pages/api-reference/[...slug].astro +67 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Schema for a single OpenAPI spec source */
|
|
3
|
+
const openApiSpecSchema = z.object({
|
|
4
|
+
/** Path to the OpenAPI spec file (relative to project root) */
|
|
5
|
+
path: z.string(),
|
|
6
|
+
/** Optional display name for this spec */
|
|
7
|
+
name: z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
/** Schema for sidebar configuration */
|
|
10
|
+
const sidebarSchema = z.object({
|
|
11
|
+
/** Whether to show the sidebar on all pages */
|
|
12
|
+
enabled: z.boolean().optional(),
|
|
13
|
+
/** Whether sections are collapsed by default */
|
|
14
|
+
defaultCollapsed: z.boolean().optional(),
|
|
15
|
+
});
|
|
16
|
+
/** Schema for a header navigation link */
|
|
17
|
+
const headerLinkSchema = z.object({
|
|
18
|
+
/** Display label for the link */
|
|
19
|
+
label: z.string(),
|
|
20
|
+
/** URL the link navigates to */
|
|
21
|
+
href: z.string(),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* A color value that supports both light and dark modes.
|
|
25
|
+
*
|
|
26
|
+
* - String: applies as dark-mode override only (backward compat)
|
|
27
|
+
* - Object: explicit { light, dark } pair for full control
|
|
28
|
+
*/
|
|
29
|
+
const colorPairSchema = z.union([
|
|
30
|
+
z.string(),
|
|
31
|
+
z.object({
|
|
32
|
+
light: z.string().optional(),
|
|
33
|
+
dark: z.string().optional(),
|
|
34
|
+
}),
|
|
35
|
+
]);
|
|
36
|
+
/** Schema for semantic color tokens that map to CSS custom properties */
|
|
37
|
+
const colorsSchema = z.object({
|
|
38
|
+
/** Primary brand color — applies to both modes */
|
|
39
|
+
primary: z.string().optional(),
|
|
40
|
+
/** Secondary/accent brand color — applies to both modes */
|
|
41
|
+
accent: z.string().optional(),
|
|
42
|
+
/** Page background color (light/dark pair or dark-only string) */
|
|
43
|
+
background: colorPairSchema.optional(),
|
|
44
|
+
/** Default text color (light/dark pair or dark-only string) */
|
|
45
|
+
foreground: colorPairSchema.optional(),
|
|
46
|
+
/** Muted/secondary text color (light/dark pair or dark-only string) */
|
|
47
|
+
muted: colorPairSchema.optional(),
|
|
48
|
+
/** Border color (light/dark pair or dark-only string) */
|
|
49
|
+
border: colorPairSchema.optional(),
|
|
50
|
+
});
|
|
51
|
+
/** Schema for theme configuration */
|
|
52
|
+
const themeSchema = z.object({
|
|
53
|
+
/** Path to a custom logo image */
|
|
54
|
+
logo: z.string().optional(),
|
|
55
|
+
/** Primary brand color (CSS color value) — shorthand for colors.primary */
|
|
56
|
+
primaryColor: z.string().optional(),
|
|
57
|
+
/** Secondary/accent brand color (CSS color value) */
|
|
58
|
+
accentColor: z.string().optional(),
|
|
59
|
+
/** Font family for body text */
|
|
60
|
+
fontFamily: z.string().optional(),
|
|
61
|
+
/** Font family for code blocks */
|
|
62
|
+
codeFontFamily: z.string().optional(),
|
|
63
|
+
/** Footer text or copyright notice */
|
|
64
|
+
footer: z.string().optional(),
|
|
65
|
+
/** Path to favicon image (relative to public directory) */
|
|
66
|
+
favicon: z.string().optional(),
|
|
67
|
+
/** Path to social/OG image for link previews (relative to public directory) */
|
|
68
|
+
socialImage: z.string().optional(),
|
|
69
|
+
/** Header navigation links */
|
|
70
|
+
headerLinks: z.array(headerLinkSchema).optional(),
|
|
71
|
+
/** Semantic color tokens for CSS custom property theming */
|
|
72
|
+
colors: colorsSchema.optional(),
|
|
73
|
+
});
|
|
74
|
+
/** Schema for OpenAPI configuration */
|
|
75
|
+
const openApiSchema = z.object({
|
|
76
|
+
/** Array of OpenAPI spec sources */
|
|
77
|
+
specs: z.array(openApiSpecSchema).optional(),
|
|
78
|
+
});
|
|
79
|
+
/** Schema for build configuration */
|
|
80
|
+
const buildSchema = z.object({
|
|
81
|
+
/** Output directory for the build (relative to project root) */
|
|
82
|
+
outDir: z.string().optional(),
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Complete specglass configuration schema.
|
|
86
|
+
* All top-level fields are optional with sensible defaults.
|
|
87
|
+
*/
|
|
88
|
+
export const configSchema = z.object({
|
|
89
|
+
/** Site metadata */
|
|
90
|
+
site: z
|
|
91
|
+
.object({
|
|
92
|
+
/** Site title displayed in header and browser tab */
|
|
93
|
+
title: z.string().optional(),
|
|
94
|
+
/** Site description for SEO meta tags */
|
|
95
|
+
description: z.string().optional(),
|
|
96
|
+
/** Base URL of the deployed docs site */
|
|
97
|
+
url: z.string().url().optional(),
|
|
98
|
+
})
|
|
99
|
+
.optional(),
|
|
100
|
+
/** Sidebar navigation configuration */
|
|
101
|
+
sidebar: sidebarSchema.optional(),
|
|
102
|
+
/** OpenAPI spec configuration for auto-generated API reference pages */
|
|
103
|
+
openapi: openApiSchema.optional(),
|
|
104
|
+
/** Default theme appearance configuration */
|
|
105
|
+
theme: themeSchema.optional(),
|
|
106
|
+
/** Build output configuration */
|
|
107
|
+
build: buildSchema.optional(),
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for MDX content frontmatter.
|
|
4
|
+
*
|
|
5
|
+
* Required: title
|
|
6
|
+
* Optional: description, sidebar_label, sidebar_position, draft, custom
|
|
7
|
+
*
|
|
8
|
+
* Uses `.passthrough()` to allow Astro's internal fields (e.g., `_id`, `_collection`)
|
|
9
|
+
* through without validation errors.
|
|
10
|
+
*/
|
|
11
|
+
export declare const frontmatterSchema: z.ZodObject<{
|
|
12
|
+
/** Page title — required for all content pages */
|
|
13
|
+
title: z.ZodString;
|
|
14
|
+
/** Page description for SEO meta tags */
|
|
15
|
+
description: z.ZodOptional<z.ZodString>;
|
|
16
|
+
/** Custom sidebar label (overrides title in navigation) */
|
|
17
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
18
|
+
/** Numeric position for sidebar ordering */
|
|
19
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
/** When true, page is excluded from production builds */
|
|
21
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
22
|
+
/** User-defined custom fields, namespaced to avoid collisions */
|
|
23
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
24
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
25
|
+
/** Page title — required for all content pages */
|
|
26
|
+
title: z.ZodString;
|
|
27
|
+
/** Page description for SEO meta tags */
|
|
28
|
+
description: z.ZodOptional<z.ZodString>;
|
|
29
|
+
/** Custom sidebar label (overrides title in navigation) */
|
|
30
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
31
|
+
/** Numeric position for sidebar ordering */
|
|
32
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
/** When true, page is excluded from production builds */
|
|
34
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
35
|
+
/** User-defined custom fields, namespaced to avoid collisions */
|
|
36
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
37
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
38
|
+
/** Page title — required for all content pages */
|
|
39
|
+
title: z.ZodString;
|
|
40
|
+
/** Page description for SEO meta tags */
|
|
41
|
+
description: z.ZodOptional<z.ZodString>;
|
|
42
|
+
/** Custom sidebar label (overrides title in navigation) */
|
|
43
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
44
|
+
/** Numeric position for sidebar ordering */
|
|
45
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
/** When true, page is excluded from production builds */
|
|
47
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
48
|
+
/** User-defined custom fields, namespaced to avoid collisions */
|
|
49
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
50
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
51
|
+
/** Inferred TypeScript type for validated frontmatter */
|
|
52
|
+
export type Frontmatter = z.infer<typeof frontmatterSchema>;
|
|
53
|
+
//# sourceMappingURL=frontmatter-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-schema.d.ts","sourceRoot":"","sources":["../../src/content/frontmatter-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;IAE1B,kDAAkD;;IAGlD,yCAAyC;;IAGzC,2DAA2D;;IAG3D,4CAA4C;;IAG5C,yDAAyD;;IAGzD,iEAAiE;;;IAfjE,kDAAkD;;IAGlD,yCAAyC;;IAGzC,2DAA2D;;IAG3D,4CAA4C;;IAG5C,yDAAyD;;IAGzD,iEAAiE;;;IAfjE,kDAAkD;;IAGlD,yCAAyC;;IAGzC,2DAA2D;;IAG3D,4CAA4C;;IAG5C,yDAAyD;;IAGzD,iEAAiE;;gCAGrD,CAAC;AAEjB,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for MDX content frontmatter.
|
|
4
|
+
*
|
|
5
|
+
* Required: title
|
|
6
|
+
* Optional: description, sidebar_label, sidebar_position, draft, custom
|
|
7
|
+
*
|
|
8
|
+
* Uses `.passthrough()` to allow Astro's internal fields (e.g., `_id`, `_collection`)
|
|
9
|
+
* through without validation errors.
|
|
10
|
+
*/
|
|
11
|
+
export const frontmatterSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
/** Page title — required for all content pages */
|
|
14
|
+
title: z.string().describe("Page title (required)"),
|
|
15
|
+
/** Page description for SEO meta tags */
|
|
16
|
+
description: z.string().optional(),
|
|
17
|
+
/** Custom sidebar label (overrides title in navigation) */
|
|
18
|
+
sidebar_label: z.string().optional(),
|
|
19
|
+
/** Numeric position for sidebar ordering */
|
|
20
|
+
sidebar_position: z.number().optional(),
|
|
21
|
+
/** When true, page is excluded from production builds */
|
|
22
|
+
draft: z.boolean().optional().default(false),
|
|
23
|
+
/** User-defined custom fields, namespaced to avoid collisions */
|
|
24
|
+
custom: z.record(z.unknown()).optional(),
|
|
25
|
+
})
|
|
26
|
+
.passthrough();
|
|
27
|
+
//# sourceMappingURL=frontmatter-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter-schema.js","sourceRoot":"","sources":["../../src/content/frontmatter-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,kDAAkD;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IAEnD,yCAAyC;IACzC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAElC,2DAA2D;IAC3D,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEpC,4CAA4C;IAC5C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEvC,yDAAyD;IACzD,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAE5C,iEAAiE;IACjE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC;KACD,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SpecglassError } from "../errors/specglass-error.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options for configuring the docs content collection.
|
|
5
|
+
*/
|
|
6
|
+
export interface DocsCollectionOptions {
|
|
7
|
+
/** Base directory for MDX content files, relative to Astro project root.
|
|
8
|
+
* @default "./src/content/docs"
|
|
9
|
+
*/
|
|
10
|
+
contentDir?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Defines the Astro Content Layer collection for MDX documentation pages.
|
|
14
|
+
*
|
|
15
|
+
* Uses Astro 5.x's `glob()` loader to read `.mdx` files from the content
|
|
16
|
+
* directory. Frontmatter is validated against the `frontmatterSchema` Zod schema.
|
|
17
|
+
*
|
|
18
|
+
* This function is meant to be used in the user's `src/content/config.ts`:
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* // src/content/config.ts
|
|
23
|
+
* import { defineCollection } from "astro:content";
|
|
24
|
+
* import { defineDocsCollection } from "@specglass/core";
|
|
25
|
+
*
|
|
26
|
+
* export const collections = {
|
|
27
|
+
* docs: defineCollection(defineDocsCollection()),
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @param options - Optional configuration for the content directory
|
|
32
|
+
* @returns Collection configuration object for the docs collection
|
|
33
|
+
*/
|
|
34
|
+
export declare function defineDocsCollection(options?: DocsCollectionOptions): {
|
|
35
|
+
loader: import("astro/loaders").Loader;
|
|
36
|
+
schema: z.ZodObject<{
|
|
37
|
+
title: z.ZodString;
|
|
38
|
+
description: z.ZodOptional<z.ZodString>;
|
|
39
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
40
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
42
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
43
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
44
|
+
title: z.ZodString;
|
|
45
|
+
description: z.ZodOptional<z.ZodString>;
|
|
46
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
47
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
49
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
50
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
51
|
+
title: z.ZodString;
|
|
52
|
+
description: z.ZodOptional<z.ZodString>;
|
|
53
|
+
sidebar_label: z.ZodOptional<z.ZodString>;
|
|
54
|
+
sidebar_position: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
draft: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
56
|
+
custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
57
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Converts a content file path to a URL slug.
|
|
61
|
+
*
|
|
62
|
+
* Strips the `content/` prefix and `.mdx`/`.md` extension from the file path.
|
|
63
|
+
* Handles `index.mdx` by mapping it to the parent directory path.
|
|
64
|
+
*
|
|
65
|
+
* @param filePath - Content file path (e.g., `content/docs/getting-started.mdx`)
|
|
66
|
+
* @returns URL slug (e.g., `docs/getting-started`)
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* getSlugFromFilePath("content/docs/getting-started.mdx")
|
|
71
|
+
* // => "docs/getting-started"
|
|
72
|
+
*
|
|
73
|
+
* getSlugFromFilePath("content/docs/guides/setup.mdx")
|
|
74
|
+
* // => "docs/guides/setup"
|
|
75
|
+
*
|
|
76
|
+
* getSlugFromFilePath("content/docs/index.mdx")
|
|
77
|
+
* // => "docs"
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function getSlugFromFilePath(filePath: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* Wraps a Zod validation error into an `SpecglassError` with actionable context.
|
|
83
|
+
*
|
|
84
|
+
* Astro's Content Layer API handles Zod validation automatically, but this
|
|
85
|
+
* function provides an additional error wrapping layer for when we need to
|
|
86
|
+
* validate frontmatter outside of Astro's pipeline (e.g., in `specglass check`).
|
|
87
|
+
*
|
|
88
|
+
* @param zodError - The Zod validation error
|
|
89
|
+
* @param filePath - Path to the MDX file that failed validation
|
|
90
|
+
* @returns SpecglassError with FRONTMATTER_INVALID code and actionable hint
|
|
91
|
+
*/
|
|
92
|
+
export declare function wrapFrontmatterError(zodError: z.ZodError, filePath: string): SpecglassError;
|
|
93
|
+
//# sourceMappingURL=mdx-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdx-loader.d.ts","sourceRoot":"","sources":["../../src/content/mdx-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAOnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmB5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EACpB,QAAQ,EAAE,MAAM,GACf,cAAc,CAuBhB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { glob } from "astro/loaders";
|
|
2
|
+
import { frontmatterSchema } from "./frontmatter-schema.js";
|
|
3
|
+
import { SpecglassError } from "../errors/specglass-error.js";
|
|
4
|
+
/**
|
|
5
|
+
* Defines the Astro Content Layer collection for MDX documentation pages.
|
|
6
|
+
*
|
|
7
|
+
* Uses Astro 5.x's `glob()` loader to read `.mdx` files from the content
|
|
8
|
+
* directory. Frontmatter is validated against the `frontmatterSchema` Zod schema.
|
|
9
|
+
*
|
|
10
|
+
* This function is meant to be used in the user's `src/content/config.ts`:
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // src/content/config.ts
|
|
15
|
+
* import { defineCollection } from "astro:content";
|
|
16
|
+
* import { defineDocsCollection } from "@specglass/core";
|
|
17
|
+
*
|
|
18
|
+
* export const collections = {
|
|
19
|
+
* docs: defineCollection(defineDocsCollection()),
|
|
20
|
+
* };
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param options - Optional configuration for the content directory
|
|
24
|
+
* @returns Collection configuration object for the docs collection
|
|
25
|
+
*/
|
|
26
|
+
export function defineDocsCollection(options) {
|
|
27
|
+
const contentDir = options?.contentDir ?? "./src/content";
|
|
28
|
+
return {
|
|
29
|
+
loader: glob({ pattern: "docs/**/*.{mdx,md}", base: contentDir }),
|
|
30
|
+
schema: frontmatterSchema,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Converts a content file path to a URL slug.
|
|
35
|
+
*
|
|
36
|
+
* Strips the `content/` prefix and `.mdx`/`.md` extension from the file path.
|
|
37
|
+
* Handles `index.mdx` by mapping it to the parent directory path.
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - Content file path (e.g., `content/docs/getting-started.mdx`)
|
|
40
|
+
* @returns URL slug (e.g., `docs/getting-started`)
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* getSlugFromFilePath("content/docs/getting-started.mdx")
|
|
45
|
+
* // => "docs/getting-started"
|
|
46
|
+
*
|
|
47
|
+
* getSlugFromFilePath("content/docs/guides/setup.mdx")
|
|
48
|
+
* // => "docs/guides/setup"
|
|
49
|
+
*
|
|
50
|
+
* getSlugFromFilePath("content/docs/index.mdx")
|
|
51
|
+
* // => "docs"
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function getSlugFromFilePath(filePath) {
|
|
55
|
+
// Normalize path separators
|
|
56
|
+
let slug = filePath.replace(/\\/g, "/");
|
|
57
|
+
// Strip leading content/ prefix
|
|
58
|
+
slug = slug.replace(/^content\//, "");
|
|
59
|
+
// Strip file extension (.mdx or .md)
|
|
60
|
+
slug = slug.replace(/\.(mdx|md)$/, "");
|
|
61
|
+
// Handle index files — map to parent directory
|
|
62
|
+
slug = slug.replace(/\/index$/, "");
|
|
63
|
+
// Handle root index (just "index" after stripping content/)
|
|
64
|
+
if (slug === "index") {
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
return slug;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Wraps a Zod validation error into an `SpecglassError` with actionable context.
|
|
71
|
+
*
|
|
72
|
+
* Astro's Content Layer API handles Zod validation automatically, but this
|
|
73
|
+
* function provides an additional error wrapping layer for when we need to
|
|
74
|
+
* validate frontmatter outside of Astro's pipeline (e.g., in `specglass check`).
|
|
75
|
+
*
|
|
76
|
+
* @param zodError - The Zod validation error
|
|
77
|
+
* @param filePath - Path to the MDX file that failed validation
|
|
78
|
+
* @returns SpecglassError with FRONTMATTER_INVALID code and actionable hint
|
|
79
|
+
*/
|
|
80
|
+
export function wrapFrontmatterError(zodError, filePath) {
|
|
81
|
+
const hints = zodError.issues.map((issue) => {
|
|
82
|
+
const fieldPath = issue.path.join(".") || "unknown";
|
|
83
|
+
if (issue.code === "invalid_type" && fieldPath === "title") {
|
|
84
|
+
return `Field 'title' is required — add \`title: "Page Title"\` to your frontmatter`;
|
|
85
|
+
}
|
|
86
|
+
else if (issue.code === "invalid_type") {
|
|
87
|
+
return `Field '${fieldPath}' expected ${issue.expected}, got ${issue.received}`;
|
|
88
|
+
}
|
|
89
|
+
return `Field '${fieldPath}': ${issue.message}`;
|
|
90
|
+
});
|
|
91
|
+
const issueCount = zodError.issues.length;
|
|
92
|
+
const summary = zodError.issues
|
|
93
|
+
.map((i) => `${i.path.join(".") || "unknown"}: ${i.message}`)
|
|
94
|
+
.join("; ");
|
|
95
|
+
return new SpecglassError(`Invalid frontmatter in ${filePath}: ${issueCount} error${issueCount > 1 ? "s" : ""} (${summary})`, "FRONTMATTER_INVALID", filePath, undefined, hints.join("\n"));
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=mdx-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdx-loader.js","sourceRoot":"","sources":["../../src/content/mdx-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAY9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA+B;IAClE,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,eAAe,CAAC;IAE1D,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACjE,MAAM,EAAE,iBAAiB;KAC1B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,4BAA4B;IAC5B,IAAI,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAExC,gCAAgC;IAChC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAEtC,qCAAqC;IACrC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAEvC,+CAA+C;IAC/C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEpC,4DAA4D;IAC5D,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAoB,EACpB,QAAgB;IAEhB,MAAM,KAAK,GAAa,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACpD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;QACpD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC3D,OAAO,6EAA6E,CAAC;QACvF,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzC,OAAO,UAAU,SAAS,cAAe,KAA+B,CAAC,QAAQ,SAAU,KAA+B,CAAC,QAAQ,EAAE,CAAC;QACxI,CAAC;QACD,OAAO,UAAU,SAAS,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM;SAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;SAC5D,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,IAAI,cAAc,CACvB,0BAA0B,QAAQ,KAAK,UAAU,SAAS,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,GAAG,EAClG,qBAAqB,EACrB,QAAQ,EACR,SAAS,EACT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CACjB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro Content Layer loader for OpenAPI specifications.
|
|
3
|
+
*
|
|
4
|
+
* Parses all configured OpenAPI specs and produces content entries
|
|
5
|
+
* (one per endpoint) for the Astro content collection. This loader
|
|
6
|
+
* always re-parses on every build invocation to ensure content is
|
|
7
|
+
* never stale (FR12 / AC5).
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { ApiEndpoint, ApiEndpointError, ParsedOpenApiSpec } from "../openapi/types.js";
|
|
12
|
+
/** Config shape expected by the loader (subset of SpecglassConfig) */
|
|
13
|
+
export interface OpenApiLoaderConfig {
|
|
14
|
+
specs?: Array<{
|
|
15
|
+
path: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/** A single content entry produced by the loader */
|
|
20
|
+
export interface OpenApiContentEntry {
|
|
21
|
+
id: string;
|
|
22
|
+
data: ApiEndpoint;
|
|
23
|
+
}
|
|
24
|
+
/** Result of the OpenAPI content loading process */
|
|
25
|
+
export interface OpenApiLoaderResult {
|
|
26
|
+
entries: OpenApiContentEntry[];
|
|
27
|
+
specs: ParsedOpenApiSpec[];
|
|
28
|
+
errors: ApiEndpointError[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load and transform all configured OpenAPI specs into content entries.
|
|
32
|
+
*
|
|
33
|
+
* Each endpoint becomes a content entry with ID format:
|
|
34
|
+
* `{specName}/{method}-{sanitizedPath}`
|
|
35
|
+
*
|
|
36
|
+
* @param config - OpenAPI loader configuration (from specglass config)
|
|
37
|
+
* @returns Content entries, parsed specs, and accumulated errors
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadOpenApiContent(config: OpenApiLoaderConfig): Promise<OpenApiLoaderResult>;
|
|
40
|
+
//# sourceMappingURL=openapi-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-loader.d.ts","sourceRoot":"","sources":["../../src/content/openapi-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAE7B,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAmD9B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro Content Layer loader for OpenAPI specifications.
|
|
3
|
+
*
|
|
4
|
+
* Parses all configured OpenAPI specs and produces content entries
|
|
5
|
+
* (one per endpoint) for the Astro content collection. This loader
|
|
6
|
+
* always re-parses on every build invocation to ensure content is
|
|
7
|
+
* never stale (FR12 / AC5).
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { parseOpenApiSpec } from "../openapi/parser.js";
|
|
12
|
+
/**
|
|
13
|
+
* Load and transform all configured OpenAPI specs into content entries.
|
|
14
|
+
*
|
|
15
|
+
* Each endpoint becomes a content entry with ID format:
|
|
16
|
+
* `{specName}/{method}-{sanitizedPath}`
|
|
17
|
+
*
|
|
18
|
+
* @param config - OpenAPI loader configuration (from specglass config)
|
|
19
|
+
* @returns Content entries, parsed specs, and accumulated errors
|
|
20
|
+
*/
|
|
21
|
+
export async function loadOpenApiContent(config) {
|
|
22
|
+
const specs = config.specs ?? [];
|
|
23
|
+
if (specs.length === 0) {
|
|
24
|
+
return { entries: [], specs: [], errors: [] };
|
|
25
|
+
}
|
|
26
|
+
const allEntries = [];
|
|
27
|
+
const allSpecs = [];
|
|
28
|
+
const allErrors = [];
|
|
29
|
+
for (const specConfig of specs) {
|
|
30
|
+
const specName = specConfig.name ?? "default";
|
|
31
|
+
let parsedSpec;
|
|
32
|
+
try {
|
|
33
|
+
parsedSpec = await parseOpenApiSpec(specConfig.path);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
// Log parse-level errors as warnings — don't fail the build
|
|
37
|
+
console.warn(`[specglass] Warning: Failed to parse OpenAPI spec "${specConfig.path}": ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
allSpecs.push(parsedSpec);
|
|
41
|
+
allErrors.push(...parsedSpec.errors);
|
|
42
|
+
// Log per-endpoint errors as warnings
|
|
43
|
+
for (const endpointError of parsedSpec.errors) {
|
|
44
|
+
console.warn(`[specglass] Warning: Failed to transform ${endpointError.method.toUpperCase()} ${endpointError.path}: ${endpointError.reason}`);
|
|
45
|
+
}
|
|
46
|
+
// Produce content entries — one per endpoint
|
|
47
|
+
for (const endpoint of parsedSpec.endpoints) {
|
|
48
|
+
const sanitizedPath = endpoint.path
|
|
49
|
+
.replace(/^\//, "")
|
|
50
|
+
.replace(/\//g, "-")
|
|
51
|
+
.replace(/[{}]/g, "");
|
|
52
|
+
const id = `${specName}/${endpoint.method.toLowerCase()}-${sanitizedPath}`;
|
|
53
|
+
allEntries.push({ id, data: endpoint });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { entries: allEntries, specs: allSpecs, errors: allErrors };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=openapi-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-loader.js","sourceRoot":"","sources":["../../src/content/openapi-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAyBxD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAA2B;IAE3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IAEjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,UAAU,GAA0B,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,MAAM,SAAS,GAAuB,EAAE,CAAC;IAEzC,KAAK,MAAM,UAAU,IAAI,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,IAAI,SAAS,CAAC;QAE9C,IAAI,UAA6B,CAAC;QAClC,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4DAA4D;YAC5D,OAAO,CAAC,IAAI,CACV,sDAAsD,UAAU,CAAC,IAAI,MACnE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAC3C,EAAE,CACH,CAAC;YACF,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAErC,sCAAsC;QACtC,KAAK,MAAM,aAAa,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CACV,4CAA4C,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,aAAa,CAAC,IAAI,KAAK,aAAa,CAAC,MAAM,EAAE,CAChI,CAAC;QACJ,CAAC;QAED,6CAA6C;QAC7C,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI;iBAChC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;iBAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;iBACnB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAExB,MAAM,EAAE,GAAG,GAAG,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,aAAa,EAAE,CAAC;YAE3E,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rehype plugin that enhances Shiki-rendered code blocks with metadata
|
|
3
|
+
* for the CopyButton client-side enhancer.
|
|
4
|
+
*
|
|
5
|
+
* Astro's Shiki integration renders fenced code blocks into <pre><code> HTML.
|
|
6
|
+
* This plugin:
|
|
7
|
+
* 1. Wraps each <pre> in a .code-block container div
|
|
8
|
+
* 2. Extracts raw code text into a data-code attribute for clipboard copy
|
|
9
|
+
* 3. Parses the meta string for title and highlight info
|
|
10
|
+
*
|
|
11
|
+
* The CopyButton and visual chrome are added client-side by code-block-enhancer.ts.
|
|
12
|
+
*/
|
|
13
|
+
import type { Root } from "hast";
|
|
14
|
+
export declare function rehypeCodeBlocks(): (tree: Root) => void;
|
|
15
|
+
//# sourceMappingURL=rehype-code-blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-code-blocks.d.ts","sourceRoot":"","sources":["../../src/content/rehype-code-blocks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAiB,MAAM,MAAM,CAAC;AA4BhD,wBAAgB,gBAAgB,KACtB,MAAM,IAAI,UAgDnB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rehype plugin that enhances Shiki-rendered code blocks with metadata
|
|
3
|
+
* for the CopyButton client-side enhancer.
|
|
4
|
+
*
|
|
5
|
+
* Astro's Shiki integration renders fenced code blocks into <pre><code> HTML.
|
|
6
|
+
* This plugin:
|
|
7
|
+
* 1. Wraps each <pre> in a .code-block container div
|
|
8
|
+
* 2. Extracts raw code text into a data-code attribute for clipboard copy
|
|
9
|
+
* 3. Parses the meta string for title and highlight info
|
|
10
|
+
*
|
|
11
|
+
* The CopyButton and visual chrome are added client-side by code-block-enhancer.ts.
|
|
12
|
+
*/
|
|
13
|
+
import { visit } from "unist-util-visit";
|
|
14
|
+
/**
|
|
15
|
+
* Extract text content from a hast node tree recursively.
|
|
16
|
+
*/
|
|
17
|
+
function extractText(node) {
|
|
18
|
+
if (node.type === "text") {
|
|
19
|
+
return node.value;
|
|
20
|
+
}
|
|
21
|
+
if ("children" in node) {
|
|
22
|
+
return node.children.map(extractText).join("");
|
|
23
|
+
}
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if a <pre> is already inside a .code-block wrapper
|
|
28
|
+
* (i.e., was rendered by the explicit <CodeBlock> Astro component).
|
|
29
|
+
*/
|
|
30
|
+
function isAlreadyWrapped(parent) {
|
|
31
|
+
if (!parent)
|
|
32
|
+
return false;
|
|
33
|
+
const classes = parent.properties?.className;
|
|
34
|
+
if (Array.isArray(classes) && classes.includes("code-block-body"))
|
|
35
|
+
return true;
|
|
36
|
+
if (typeof classes === "string" && classes.includes("code-block-body"))
|
|
37
|
+
return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
export function rehypeCodeBlocks() {
|
|
41
|
+
return (tree) => {
|
|
42
|
+
// Process in reverse to avoid index shifts when splicing
|
|
43
|
+
const replacements = [];
|
|
44
|
+
visit(tree, "element", (node, index, parent) => {
|
|
45
|
+
if (node.tagName !== "pre")
|
|
46
|
+
return;
|
|
47
|
+
if (!parent || typeof index !== "number")
|
|
48
|
+
return;
|
|
49
|
+
// Skip if already inside a CodeBlock component wrapper
|
|
50
|
+
if (isAlreadyWrapped(parent))
|
|
51
|
+
return;
|
|
52
|
+
// Find the <code> child
|
|
53
|
+
const codeEl = node.children.find((child) => child.type === "element" && child.tagName === "code");
|
|
54
|
+
if (!codeEl)
|
|
55
|
+
return;
|
|
56
|
+
// Extract raw text for clipboard
|
|
57
|
+
const rawCode = extractText(codeEl).trimEnd();
|
|
58
|
+
// Build wrapper: <div class="code-block code-block--fenced">
|
|
59
|
+
const wrapper = {
|
|
60
|
+
type: "element",
|
|
61
|
+
tagName: "div",
|
|
62
|
+
properties: {
|
|
63
|
+
className: ["code-block", "code-block--fenced"],
|
|
64
|
+
"data-code": rawCode,
|
|
65
|
+
},
|
|
66
|
+
children: [
|
|
67
|
+
{
|
|
68
|
+
type: "element",
|
|
69
|
+
tagName: "div",
|
|
70
|
+
properties: { className: ["code-block-body"] },
|
|
71
|
+
children: [node],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
replacements.push({ parent: parent, index, wrapper });
|
|
76
|
+
});
|
|
77
|
+
// Apply replacements in reverse order to preserve indices
|
|
78
|
+
for (let i = replacements.length - 1; i >= 0; i--) {
|
|
79
|
+
const { parent, index, wrapper } = replacements[i];
|
|
80
|
+
parent.children.splice(index, 1, wrapper);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=rehype-code-blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-code-blocks.js","sourceRoot":"","sources":["../../src/content/rehype-code-blocks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error class for all user-facing specglass errors.
|
|
3
|
+
* Provides actionable context: error code, file path, line number, and remediation hint.
|
|
4
|
+
*/
|
|
5
|
+
export declare class SpecglassError extends Error {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly filePath?: string | undefined;
|
|
8
|
+
readonly line?: number | undefined;
|
|
9
|
+
readonly hint?: string | undefined;
|
|
10
|
+
constructor(message: string, code: string, filePath?: string | undefined, line?: number | undefined, hint?: string | undefined);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=specglass-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specglass-error.d.ts","sourceRoot":"","sources":["../../src/errors/specglass-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,MAAM;aACZ,QAAQ,CAAC,EAAE,MAAM;aACjB,IAAI,CAAC,EAAE,MAAM;aACb,IAAI,CAAC,EAAE,MAAM;gBAJ7B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,YAAA,EACjB,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,IAAI,CAAC,EAAE,MAAM,YAAA;CAKhC"}
|