@astrojs/starlight 0.41.3 → 0.41.5
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/CHANGELOG.md +16 -0
- package/components-internals/SidebarPersistState.ts +0 -1
- package/package.json +1 -1
- package/schema.ts +5 -5
- package/utils/navigation.ts +3 -3
- package/utils/url.ts +4 -0
- package/utils/zodDeepMerge.ts +144 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @astrojs/starlight
|
|
2
2
|
|
|
3
|
+
## 0.41.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4095](https://github.com/withastro/starlight/pull/4095) [`bb06434`](https://github.com/withastro/starlight/commit/bb06434a1bc199b59f9d7953d0e41c6c287b1aa2) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes a regression when using a union to [extend](https://starlight.astro.build/reference/frontmatter/#extend) Starlight’s `docsSchema()`.
|
|
8
|
+
|
|
9
|
+
## 0.41.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#3936](https://github.com/withastro/starlight/pull/3936) [`712eedd`](https://github.com/withastro/starlight/commit/712eedd8e0d28329feb361edc392438f37ba2095) Thanks [@miichom](https://github.com/miichom)! - Fixes support for modifying Zod enums when passing an [`extend` option](https://starlight.astro.build/reference/frontmatter/#extend) to Starlight’s `docsSchema()`
|
|
14
|
+
|
|
15
|
+
- [#4092](https://github.com/withastro/starlight/pull/4092) [`0896b91`](https://github.com/withastro/starlight/commit/0896b91607325b9d8494eb665cd1716a40025a5a) Thanks [@delucis](https://github.com/delucis)! - Fixes support for links containing a protocol like `mailto:` in the sidebar
|
|
16
|
+
|
|
17
|
+
- [#4088](https://github.com/withastro/starlight/pull/4088) [`4486ba4`](https://github.com/withastro/starlight/commit/4486ba432afe9e206f0b05651de24a9c25bdf6dd) Thanks [@delucis](https://github.com/delucis)! - Simplifies Starlight’s client-side sidebar state persistence script slightly
|
|
18
|
+
|
|
3
19
|
## 0.41.3
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/package.json
CHANGED
package/schema.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { FrontmatterTableOfContentsSchema } from './schemas/tableOfContents';
|
|
|
6
6
|
import { BadgeConfigSchema } from './schemas/badge';
|
|
7
7
|
import { HeroSchema } from './schemas/hero';
|
|
8
8
|
import { SidebarLinkItemHTMLAttributesSchema } from './schemas/sidebar';
|
|
9
|
+
import { deepMergeSchemas, type DeepMergedSchema } from './utils/zodDeepMerge';
|
|
9
10
|
export { i18nSchema } from './schemas/i18n';
|
|
10
11
|
|
|
11
12
|
/** Default content collection schema for Starlight’s `docs` collection. */
|
|
@@ -114,14 +115,13 @@ const StarlightFrontmatterSchema = (context: SchemaContext) =>
|
|
|
114
115
|
type DefaultSchema = ReturnType<typeof StarlightFrontmatterSchema>;
|
|
115
116
|
|
|
116
117
|
/** Base subset of Zod types that we support passing to the `extend` option. */
|
|
117
|
-
type
|
|
118
|
+
type BaseObjectSchema = z.ZodObject<z.ZodRawShape, z.core.$ZodObjectConfig>;
|
|
119
|
+
type BaseSchema = BaseObjectSchema | z.ZodUnion<readonly BaseObjectSchema[]>;
|
|
118
120
|
|
|
119
121
|
/** Type that extends Starlight’s default schema with an optional, user-defined schema. */
|
|
120
122
|
type ExtendedSchema<T extends BaseSchema = never> = [T] extends [never]
|
|
121
123
|
? DefaultSchema
|
|
122
|
-
: T
|
|
123
|
-
? z.ZodIntersection<DefaultSchema, T>
|
|
124
|
-
: DefaultSchema;
|
|
124
|
+
: DeepMergedSchema<DefaultSchema, T>;
|
|
125
125
|
|
|
126
126
|
interface DocsSchemaOpts<T extends BaseSchema> {
|
|
127
127
|
/**
|
|
@@ -160,7 +160,7 @@ export function docsSchema<T extends BaseSchema = never>(
|
|
|
160
160
|
|
|
161
161
|
return (
|
|
162
162
|
UserSchema
|
|
163
|
-
? StarlightFrontmatterSchema(context)
|
|
163
|
+
? deepMergeSchemas(StarlightFrontmatterSchema(context), UserSchema)
|
|
164
164
|
: StarlightFrontmatterSchema(context)
|
|
165
165
|
) as ExtendedSchema<T>;
|
|
166
166
|
};
|
package/utils/navigation.ts
CHANGED
|
@@ -33,7 +33,7 @@ import type {
|
|
|
33
33
|
SidebarAutogenerateRouteData,
|
|
34
34
|
} from './routing/types';
|
|
35
35
|
import { localeToLang, localizedFilePath, slugToPathname } from './slugs';
|
|
36
|
-
import {
|
|
36
|
+
import { hasProtocol } from './url';
|
|
37
37
|
import type { StarlightConfig } from './user-config';
|
|
38
38
|
|
|
39
39
|
const DirKey = Symbol('DirKey');
|
|
@@ -120,7 +120,7 @@ function entriesFromAutogenerateConfig(
|
|
|
120
120
|
/** Create a link entry from a manual link item in user config. */
|
|
121
121
|
function linkFromSidebarLinkItem(item: SidebarLinkItem, locale: string | undefined) {
|
|
122
122
|
let href = item.link;
|
|
123
|
-
if (!
|
|
123
|
+
if (!hasProtocol(href)) {
|
|
124
124
|
href = ensureLeadingSlash(href);
|
|
125
125
|
// Inject current locale into link.
|
|
126
126
|
if (locale) href = '/' + locale + href;
|
|
@@ -188,7 +188,7 @@ function makeSidebarLink(
|
|
|
188
188
|
opts: MakeLinkOptions & { autogenerate: SidebarAutogenerateRouteData }
|
|
189
189
|
): SidebarAutoLink;
|
|
190
190
|
function makeSidebarLink({ attrs, badge, href, label, autogenerate }: MakeLinkOptions) {
|
|
191
|
-
if (!
|
|
191
|
+
if (!hasProtocol(href)) {
|
|
192
192
|
href = formatPath(href);
|
|
193
193
|
}
|
|
194
194
|
return makeLink({ label, href, badge, attrs, autogenerate });
|
package/utils/url.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
const HTTPProtocolRegEx = /^https?:\/\//;
|
|
2
|
+
const ProtocolRegEx = /^[a-z][a-z\d+.-]*:/i;
|
|
2
3
|
|
|
3
4
|
/** Check if a string starts with one of `http://` or `https://`. */
|
|
4
5
|
export const isAbsoluteUrl = (link: string) => HTTPProtocolRegEx.test(link);
|
|
6
|
+
|
|
7
|
+
/** Check if a string contains a protocol (e.g., `http:`, `https:`, `mailto:`). */
|
|
8
|
+
export const hasProtocol = (link: string) => ProtocolRegEx.test(link);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { z } from 'astro/zod';
|
|
2
|
+
|
|
3
|
+
/** Type-level equivalent of {@link mergeWithDefaultSchema}. */
|
|
4
|
+
export type DeepMergedSchema<Default extends z.core.$ZodType, User extends z.core.$ZodType> =
|
|
5
|
+
User extends z.ZodOptional<infer WrappedSchema extends z.core.$ZodType>
|
|
6
|
+
? z.ZodOptional<DeepMergedSchema<UnwrappedSchema<Default>, WrappedSchema>>
|
|
7
|
+
: User extends z.ZodNullable<infer WrappedSchema extends z.core.$ZodType>
|
|
8
|
+
? z.ZodNullable<DeepMergedSchema<UnwrappedSchema<Default>, WrappedSchema>>
|
|
9
|
+
: User extends z.ZodDefault<infer WrappedSchema extends z.core.$ZodType>
|
|
10
|
+
? z.ZodDefault<DeepMergedSchema<UnwrappedSchema<Default>, WrappedSchema>>
|
|
11
|
+
: User extends z.ZodPrefault<infer WrappedSchema extends z.core.$ZodType>
|
|
12
|
+
? z.ZodPrefault<DeepMergedSchema<UnwrappedSchema<Default>, WrappedSchema>>
|
|
13
|
+
: User extends z.ZodUnion<infer Users extends readonly z.core.$ZodType[]>
|
|
14
|
+
? z.ZodUnion<DeepMergedUnionMembers<UnwrappedSchema<Default>, Users>>
|
|
15
|
+
: UnwrappedSchema<Default> extends z.ZodObject<infer DefaultShape>
|
|
16
|
+
? User extends z.ZodObject<infer UserShape, infer UserConfig>
|
|
17
|
+
? z.ZodObject<DeepMergedShape<DefaultShape, UserShape>, UserConfig>
|
|
18
|
+
: User
|
|
19
|
+
: UnwrappedSchema<Default> extends z.ZodArray<
|
|
20
|
+
infer DefaultItemSchema extends z.core.$ZodType
|
|
21
|
+
>
|
|
22
|
+
? User extends z.ZodArray<infer UserItemSchema extends z.core.$ZodType>
|
|
23
|
+
? z.ZodArray<DeepMergedSchema<DefaultItemSchema, UserItemSchema>>
|
|
24
|
+
: User
|
|
25
|
+
: User;
|
|
26
|
+
|
|
27
|
+
/** Type-level equivalent of {@link unwrapSchema}. */
|
|
28
|
+
type UnwrappedSchema<T extends z.core.$ZodType> =
|
|
29
|
+
T extends z.ZodOptional<infer Wrapped extends z.core.$ZodType>
|
|
30
|
+
? UnwrappedSchema<Wrapped>
|
|
31
|
+
: T extends z.ZodNullable<infer Wrapped extends z.core.$ZodType>
|
|
32
|
+
? UnwrappedSchema<Wrapped>
|
|
33
|
+
: T extends z.ZodDefault<infer Wrapped extends z.core.$ZodType>
|
|
34
|
+
? UnwrappedSchema<Wrapped>
|
|
35
|
+
: T extends z.ZodPrefault<infer Wrapped extends z.core.$ZodType>
|
|
36
|
+
? UnwrappedSchema<Wrapped>
|
|
37
|
+
: T;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Merged Zod object shapes, preserving default properties, adding user-only properties, and
|
|
41
|
+
* recursively merging shared properties.
|
|
42
|
+
*/
|
|
43
|
+
type DeepMergedShape<Default extends z.ZodRawShape, User extends z.ZodRawShape> = {
|
|
44
|
+
[Key in keyof Default | keyof User]: Key extends keyof User
|
|
45
|
+
? Key extends keyof Default
|
|
46
|
+
? DeepMergedSchema<Default[Key], User[Key]>
|
|
47
|
+
: User[Key]
|
|
48
|
+
: Key extends keyof Default
|
|
49
|
+
? Default[Key]
|
|
50
|
+
: never;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Merged union schemas where each union member is deep-merged with the default schema. */
|
|
54
|
+
type DeepMergedUnionMembers<
|
|
55
|
+
Default extends z.core.$ZodType,
|
|
56
|
+
Users extends readonly z.core.$ZodType[],
|
|
57
|
+
> = {
|
|
58
|
+
[Key in keyof Users]: Users[Key] extends z.core.$ZodType
|
|
59
|
+
? DeepMergedSchema<Default, Users[Key]>
|
|
60
|
+
: Users[Key];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function deepMergeSchemas(defaultSchema: z.ZodType, userSchema: z.ZodType): z.ZodType {
|
|
64
|
+
const unwrappedDefaultSchema = unwrapSchema(defaultSchema);
|
|
65
|
+
|
|
66
|
+
if (isWrappedSchema(userSchema)) {
|
|
67
|
+
return userSchema.clone({
|
|
68
|
+
...userSchema._zod.def,
|
|
69
|
+
innerType: deepMergeSchemas(unwrappedDefaultSchema, userSchema._zod.def.innerType),
|
|
70
|
+
} as Parameters<typeof userSchema.clone>[0]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isZodUnion(userSchema)) {
|
|
74
|
+
return userSchema.clone({
|
|
75
|
+
...userSchema._zod.def,
|
|
76
|
+
options: userSchema.options.map((schema) => deepMergeSchemas(unwrappedDefaultSchema, schema)),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isZodObject(unwrappedDefaultSchema) && isZodObject(userSchema)) {
|
|
81
|
+
const shape: z.core.$ZodLooseShape = { ...unwrappedDefaultSchema.shape };
|
|
82
|
+
|
|
83
|
+
for (const key of Object.keys(userSchema.shape)) {
|
|
84
|
+
const userFieldSchema = userSchema.shape[key] as z.ZodType;
|
|
85
|
+
const defaultFieldSchema = shape[key] as z.ZodType | undefined;
|
|
86
|
+
|
|
87
|
+
shape[key] = defaultFieldSchema
|
|
88
|
+
? deepMergeSchemas(defaultFieldSchema, userFieldSchema)
|
|
89
|
+
: userFieldSchema;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return userSchema.safeExtend(shape);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isZodArray(unwrappedDefaultSchema) && isZodArray(userSchema)) {
|
|
96
|
+
return userSchema.clone({
|
|
97
|
+
...userSchema._zod.def,
|
|
98
|
+
element: deepMergeSchemas(unwrappedDefaultSchema.element, userSchema.element),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return userSchema;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Unwrap any Zod schema that wraps other schemas, such as `z.optional()`, `z.nullable()`,
|
|
107
|
+
* `z.default()`, etc. until we reach the innermost schema.
|
|
108
|
+
*/
|
|
109
|
+
function unwrapSchema(schema: z.ZodType): z.ZodType {
|
|
110
|
+
let current = schema;
|
|
111
|
+
|
|
112
|
+
while (isWrappedSchema(current)) {
|
|
113
|
+
current = current._zod.def.innerType;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return current;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if a schema wraps another schema which can happen when using modifiers like
|
|
121
|
+
* `z.optional()`, `z.nullable()`, `z.default()`, etc.
|
|
122
|
+
*/
|
|
123
|
+
function isWrappedSchema(schema: z.ZodType): schema is z.ZodType & {
|
|
124
|
+
_zod: { def: z.ZodType['_zod']['def'] & { innerType: z.ZodType } };
|
|
125
|
+
} {
|
|
126
|
+
return (
|
|
127
|
+
schema._zod.def.type === 'optional' ||
|
|
128
|
+
schema._zod.def.type === 'nullable' ||
|
|
129
|
+
schema._zod.def.type === 'default' ||
|
|
130
|
+
schema._zod.def.type === 'prefault'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isZodObject(schema: z.ZodType): schema is z.ZodObject<z.ZodRawShape> {
|
|
135
|
+
return schema._zod.def.type === 'object';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isZodArray(schema: z.ZodType): schema is z.ZodArray<z.ZodType> {
|
|
139
|
+
return schema._zod.def.type === 'array';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isZodUnion(schema: z.ZodType): schema is z.ZodUnion<readonly z.ZodType[]> {
|
|
143
|
+
return schema._zod.def.type === 'union';
|
|
144
|
+
}
|