@imjp/writenex-astro 1.3.6 → 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.
- package/dist/chunk-E4PQLKAH.js +282 -0
- package/dist/chunk-E4PQLKAH.js.map +1 -0
- package/dist/{chunk-NSW7AIVF.js → chunk-EEUN46Q2.js} +3 -3
- package/dist/{chunk-YBCPOLMY.js → chunk-P5KMSHFP.js} +2 -1
- package/dist/{chunk-YBCPOLMY.js.map → chunk-P5KMSHFP.js.map} +1 -1
- package/dist/{chunk-JMNCPNQX.js → chunk-XVQNYPOI.js} +65 -3
- package/dist/chunk-XVQNYPOI.js.map +1 -0
- package/dist/chunk-YRSIZLHE.js +1 -0
- package/dist/{chunk-N37EPLKG.js → chunk-ZWUGHWHD.js} +79 -10
- package/dist/chunk-ZWUGHWHD.js.map +1 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.js +114 -114
- package/dist/client/index.js.map +1 -1
- package/dist/config/index.d.ts +3 -2
- package/dist/config/index.js +11 -3
- package/dist/{config-CliL0CoN.d.ts → config-B7t8CjL1.d.ts} +38 -60
- package/dist/{content-TuL3GT66.d.ts → content-CwcgR8P6.d.ts} +1 -1
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +2 -2
- package/dist/fields/index.d.ts +16 -0
- package/dist/fields/index.js +13 -0
- package/dist/fields-DUSm13nm.d.ts +223 -0
- package/dist/filesystem/index.d.ts +2 -2
- package/dist/filesystem/index.js +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/{loader-53VVP2IN.js → loader-B5WZCVBC.js} +3 -3
- package/dist/loader-B5WZCVBC.js.map +1 -0
- package/dist/{schema-DDJyoVkj.d.ts → schema-nLMfZ9-o.d.ts} +79 -53
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +3 -3
- package/package.json +5 -1
- package/src/client/components/FrontmatterForm/FrontmatterForm.css +104 -0
- package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +452 -26
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +3 -0
- package/src/config/schema.ts +114 -73
- package/src/discovery/schema.ts +120 -1
- package/src/fields/collection.ts +67 -0
- package/src/fields/fields.ts +150 -0
- package/src/fields/index.ts +42 -0
- package/src/fields/resolve.ts +203 -0
- package/src/fields/types.ts +179 -0
- package/src/index.ts +3 -0
- package/src/types/config.ts +87 -63
- package/src/types/index.ts +3 -0
- package/dist/chunk-JMNCPNQX.js.map +0 -1
- package/dist/chunk-KIKIPIFA.js +0 -1
- package/dist/chunk-N37EPLKG.js.map +0 -1
- /package/dist/{chunk-NSW7AIVF.js.map → chunk-EEUN46Q2.js.map} +0 -0
- /package/dist/{chunk-KIKIPIFA.js.map → chunk-YRSIZLHE.js.map} +0 -0
- /package/dist/{loader-53VVP2IN.js.map → fields/index.js.map} +0 -0
package/src/config/schema.ts
CHANGED
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import type { WritenexConfig } from "@/types";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Schema for field type definitions
|
|
15
|
-
*/
|
|
16
13
|
const fieldTypeSchema = z.enum([
|
|
17
14
|
"string",
|
|
18
15
|
"number",
|
|
@@ -21,41 +18,124 @@ const fieldTypeSchema = z.enum([
|
|
|
21
18
|
"array",
|
|
22
19
|
"image",
|
|
23
20
|
"object",
|
|
21
|
+
"file",
|
|
22
|
+
"blocks",
|
|
23
|
+
"relationship",
|
|
24
|
+
"markdoc",
|
|
25
|
+
"mdx",
|
|
26
|
+
"child",
|
|
27
|
+
"slug",
|
|
28
|
+
"url",
|
|
29
|
+
"integer",
|
|
30
|
+
"select",
|
|
31
|
+
"multiselect",
|
|
32
|
+
"datetime",
|
|
33
|
+
"cloud-image",
|
|
34
|
+
"path-reference",
|
|
35
|
+
"conditional",
|
|
36
|
+
"empty",
|
|
37
|
+
"empty-content",
|
|
38
|
+
"empty-document",
|
|
39
|
+
"ignored",
|
|
40
|
+
"checkbox",
|
|
24
41
|
]);
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
})
|
|
43
|
+
const validationSchema = z
|
|
44
|
+
.object({
|
|
45
|
+
isRequired: z.boolean().optional(),
|
|
46
|
+
min: z.number().optional(),
|
|
47
|
+
max: z.number().optional(),
|
|
48
|
+
minLength: z.number().optional(),
|
|
49
|
+
maxLength: z.number().optional(),
|
|
50
|
+
pattern: z.string().optional(),
|
|
51
|
+
patternDescription: z.string().optional(),
|
|
52
|
+
})
|
|
53
|
+
.optional();
|
|
54
|
+
|
|
55
|
+
type SchemaFieldInput = {
|
|
56
|
+
type: z.infer<typeof fieldTypeSchema>;
|
|
57
|
+
required?: boolean;
|
|
58
|
+
default?: unknown;
|
|
59
|
+
items?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
label?: string;
|
|
62
|
+
options?: string[];
|
|
63
|
+
directory?: string;
|
|
64
|
+
publicPath?: string;
|
|
65
|
+
validation?: z.infer<typeof validationSchema>;
|
|
66
|
+
fields?: Record<string, SchemaFieldInput>;
|
|
67
|
+
itemField?: SchemaFieldInput;
|
|
68
|
+
blockTypes?: Record<string, SchemaFieldInput>;
|
|
69
|
+
collection?: string;
|
|
70
|
+
multiline?: boolean;
|
|
71
|
+
format?: string;
|
|
72
|
+
itemLabel?: string;
|
|
73
|
+
matchField?: string;
|
|
74
|
+
matchValue?: unknown;
|
|
75
|
+
showField?: SchemaFieldInput;
|
|
76
|
+
accept?: string;
|
|
77
|
+
allowExternal?: boolean;
|
|
78
|
+
inline?: boolean;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function createSchemaFieldSchema(): z.ZodType<SchemaFieldInput> {
|
|
82
|
+
let schemaFieldSchema: z.ZodType<SchemaFieldInput>;
|
|
83
|
+
|
|
84
|
+
const baseFields = {
|
|
85
|
+
type: fieldTypeSchema,
|
|
86
|
+
required: z.boolean().optional(),
|
|
87
|
+
default: z.unknown().optional(),
|
|
88
|
+
items: z.string().optional(),
|
|
89
|
+
description: z.string().optional(),
|
|
90
|
+
label: z.string().optional(),
|
|
91
|
+
options: z.array(z.string()).optional(),
|
|
92
|
+
directory: z.string().optional(),
|
|
93
|
+
publicPath: z.string().optional(),
|
|
94
|
+
validation: validationSchema,
|
|
95
|
+
collection: z.string().optional(),
|
|
96
|
+
multiline: z.boolean().optional(),
|
|
97
|
+
format: z.string().optional(),
|
|
98
|
+
itemLabel: z.string().optional(),
|
|
99
|
+
matchField: z.string().optional(),
|
|
100
|
+
matchValue: z.unknown().optional(),
|
|
101
|
+
accept: z.string().optional(),
|
|
102
|
+
allowExternal: z.boolean().optional(),
|
|
103
|
+
inline: z.boolean().optional(),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
schemaFieldSchema = z.object({
|
|
107
|
+
...baseFields,
|
|
108
|
+
fields: z
|
|
109
|
+
.record(
|
|
110
|
+
z.string(),
|
|
111
|
+
z.lazy(() => schemaFieldSchema)
|
|
112
|
+
)
|
|
113
|
+
.optional(),
|
|
114
|
+
itemField: z.lazy(() => schemaFieldSchema).optional(),
|
|
115
|
+
blockTypes: z
|
|
116
|
+
.record(
|
|
117
|
+
z.string(),
|
|
118
|
+
z.lazy(() => schemaFieldSchema)
|
|
119
|
+
)
|
|
120
|
+
.optional(),
|
|
121
|
+
showField: z.lazy(() => schemaFieldSchema).optional(),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return schemaFieldSchema;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const schemaFieldSchema = createSchemaFieldSchema();
|
|
36
128
|
|
|
37
|
-
/**
|
|
38
|
-
* Schema for collection schema (record of field definitions)
|
|
39
|
-
*/
|
|
40
129
|
const collectionSchemaSchema = z.record(z.string(), schemaFieldSchema);
|
|
41
130
|
|
|
42
|
-
/**
|
|
43
|
-
* Schema for image strategy
|
|
44
|
-
*/
|
|
45
131
|
const imageStrategySchema = z.enum(["colocated", "public", "custom"]);
|
|
46
132
|
|
|
47
|
-
/**
|
|
48
|
-
* Schema for image configuration
|
|
49
|
-
*/
|
|
50
133
|
const imageConfigSchema = z.object({
|
|
51
134
|
strategy: imageStrategySchema,
|
|
52
135
|
publicPath: z.string().optional(),
|
|
53
136
|
storagePath: z.string().optional(),
|
|
54
137
|
});
|
|
55
138
|
|
|
56
|
-
/**
|
|
57
|
-
* Schema for collection configuration
|
|
58
|
-
*/
|
|
59
139
|
const collectionConfigSchema = z.object({
|
|
60
140
|
name: z.string().min(1, "Collection name is required"),
|
|
61
141
|
path: z.string().min(1, "Collection path is required"),
|
|
@@ -65,77 +145,44 @@ const collectionConfigSchema = z.object({
|
|
|
65
145
|
images: imageConfigSchema.optional(),
|
|
66
146
|
});
|
|
67
147
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
148
|
+
const singletonConfigSchema = z.object({
|
|
149
|
+
name: z.string().min(1, "Singleton name is required"),
|
|
150
|
+
path: z.string().min(1, "Singleton path is required"),
|
|
151
|
+
previewUrl: z.string().optional(),
|
|
152
|
+
schema: collectionSchemaSchema.optional(),
|
|
153
|
+
images: imageConfigSchema.optional(),
|
|
154
|
+
});
|
|
155
|
+
|
|
71
156
|
const discoveryConfigSchema = z.object({
|
|
72
157
|
enabled: z.boolean(),
|
|
73
158
|
ignore: z.array(z.string()).optional(),
|
|
74
159
|
});
|
|
75
160
|
|
|
76
|
-
/**
|
|
77
|
-
* Schema for editor configuration
|
|
78
|
-
*/
|
|
79
161
|
const editorConfigSchema = z.object({
|
|
80
162
|
autosave: z.boolean().optional(),
|
|
81
163
|
autosaveInterval: z.number().positive().optional(),
|
|
82
164
|
});
|
|
83
165
|
|
|
84
|
-
/**
|
|
85
|
-
* Schema for version history configuration
|
|
86
|
-
*/
|
|
87
166
|
const versionHistoryConfigSchema = z.object({
|
|
88
167
|
enabled: z.boolean().optional(),
|
|
89
168
|
maxVersions: z.number().int().positive().optional(),
|
|
90
169
|
storagePath: z.string().optional(),
|
|
91
170
|
});
|
|
92
171
|
|
|
93
|
-
/**
|
|
94
|
-
* Main Writenex configuration schema
|
|
95
|
-
*/
|
|
96
172
|
export const writenexConfigSchema = z.object({
|
|
97
173
|
collections: z.array(collectionConfigSchema).optional(),
|
|
174
|
+
singletons: z.array(singletonConfigSchema).optional(),
|
|
98
175
|
images: imageConfigSchema.optional(),
|
|
99
176
|
editor: editorConfigSchema.optional(),
|
|
100
177
|
discovery: discoveryConfigSchema.optional(),
|
|
101
178
|
versionHistory: versionHistoryConfigSchema.optional(),
|
|
102
179
|
});
|
|
103
180
|
|
|
104
|
-
/**
|
|
105
|
-
* Schema for integration options
|
|
106
|
-
*/
|
|
107
181
|
export const writenexOptionsSchema = z.object({
|
|
108
182
|
allowProduction: z.boolean().optional(),
|
|
109
183
|
});
|
|
110
184
|
|
|
111
|
-
/**
|
|
112
|
-
* Helper function for defining type-safe Writenex configuration.
|
|
113
|
-
*
|
|
114
|
-
* This function provides IDE autocompletion and type checking for
|
|
115
|
-
* the configuration object. It's the recommended way to create
|
|
116
|
-
* a writenex.config.ts file.
|
|
117
|
-
*
|
|
118
|
-
* @param config - The Writenex configuration object
|
|
119
|
-
* @returns The same configuration object (identity function for type safety)
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```typescript
|
|
123
|
-
* // writenex.config.ts
|
|
124
|
-
* import { defineConfig } from '@writenex/astro';
|
|
125
|
-
*
|
|
126
|
-
* export default defineConfig({
|
|
127
|
-
* collections: [
|
|
128
|
-
* {
|
|
129
|
-
* name: 'blog',
|
|
130
|
-
* path: 'src/content/blog',
|
|
131
|
-
* filePattern: '{slug}.md',
|
|
132
|
-
* },
|
|
133
|
-
* ],
|
|
134
|
-
* });
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
185
|
export function defineConfig(config: WritenexConfig): WritenexConfig {
|
|
138
|
-
// Validate the configuration
|
|
139
186
|
const result = writenexConfigSchema.safeParse(config);
|
|
140
187
|
|
|
141
188
|
if (!result.success) {
|
|
@@ -148,12 +195,6 @@ export function defineConfig(config: WritenexConfig): WritenexConfig {
|
|
|
148
195
|
return config;
|
|
149
196
|
}
|
|
150
197
|
|
|
151
|
-
/**
|
|
152
|
-
* Validate a Writenex configuration object
|
|
153
|
-
*
|
|
154
|
-
* @param config - The configuration to validate
|
|
155
|
-
* @returns Validation result with success status and parsed data or errors
|
|
156
|
-
*/
|
|
157
198
|
export function validateConfig(
|
|
158
199
|
config: unknown
|
|
159
200
|
):
|
package/src/discovery/schema.ts
CHANGED
|
@@ -60,6 +60,69 @@ const IMAGE_EXTENSIONS = [
|
|
|
60
60
|
".svg",
|
|
61
61
|
];
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* URL pattern detection
|
|
65
|
+
*/
|
|
66
|
+
const URL_PATTERN = /^https?:\/\//;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Slug pattern detection (lowercase, hyphenated)
|
|
70
|
+
*/
|
|
71
|
+
const SLUG_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Datetime pattern detection (ISO datetime)
|
|
75
|
+
*/
|
|
76
|
+
const DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if a string looks like a URL
|
|
80
|
+
*/
|
|
81
|
+
function isUrlValue(value: unknown): boolean {
|
|
82
|
+
if (typeof value !== "string") return false;
|
|
83
|
+
return URL_PATTERN.test(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if a string looks like a slug
|
|
88
|
+
*/
|
|
89
|
+
function isSlugValue(value: unknown): boolean {
|
|
90
|
+
if (typeof value !== "string") return false;
|
|
91
|
+
if (value.length < 2 || value.length > 100) return false;
|
|
92
|
+
return SLUG_PATTERN.test(value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if a string looks like a datetime
|
|
97
|
+
*/
|
|
98
|
+
function isDatetimeValue(value: unknown): boolean {
|
|
99
|
+
if (typeof value !== "string") return false;
|
|
100
|
+
return DATETIME_PATTERN.test(value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if a string looks like a file path (non-image)
|
|
105
|
+
*/
|
|
106
|
+
function isFilePath(value: unknown): boolean {
|
|
107
|
+
if (typeof value !== "string") return false;
|
|
108
|
+
if (isImagePath(value)) return false;
|
|
109
|
+
return /\.\w{2,5}$/.test(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if an array looks like a multiselect (array of strings from a small set)
|
|
114
|
+
*/
|
|
115
|
+
function isMultiselectArray(values: unknown[]): boolean {
|
|
116
|
+
if (values.length === 0) return false;
|
|
117
|
+
const allStrings = values.every((v) => typeof v === "string" && v.length > 0);
|
|
118
|
+
if (!allStrings) return false;
|
|
119
|
+
const uniqueValues = [...new Set(values)];
|
|
120
|
+
return (
|
|
121
|
+
uniqueValues.length <= ENUM_MAX_VALUES &&
|
|
122
|
+
values.length > uniqueValues.length
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
63
126
|
/**
|
|
64
127
|
* Field analysis data collected from samples
|
|
65
128
|
*/
|
|
@@ -74,6 +137,18 @@ interface FieldAnalysis {
|
|
|
74
137
|
hasImagePaths: boolean;
|
|
75
138
|
/** Whether values look like dates */
|
|
76
139
|
hasDateValues: boolean;
|
|
140
|
+
/** Whether values look like URLs */
|
|
141
|
+
hasUrlValues: boolean;
|
|
142
|
+
/** Whether values look like slugs */
|
|
143
|
+
hasSlugValues: boolean;
|
|
144
|
+
/** Whether values look like datetimes */
|
|
145
|
+
hasDatetimeValues: boolean;
|
|
146
|
+
/** Whether values look like file paths */
|
|
147
|
+
hasFilePaths: boolean;
|
|
148
|
+
/** Whether all number values are integers */
|
|
149
|
+
allIntegers: boolean;
|
|
150
|
+
/** Whether array looks like multiselect */
|
|
151
|
+
isMultiselect: boolean;
|
|
77
152
|
/** For arrays, analysis of item types */
|
|
78
153
|
arrayItemTypes: Set<string>;
|
|
79
154
|
}
|
|
@@ -153,9 +228,29 @@ function inferFieldType(analysis: FieldAnalysis): FieldType {
|
|
|
153
228
|
// If it has image paths, it's an image field
|
|
154
229
|
if (analysis.hasImagePaths) return "image";
|
|
155
230
|
|
|
231
|
+
// If it has datetime values, it's a datetime field
|
|
232
|
+
if (analysis.hasDatetimeValues) return "datetime";
|
|
233
|
+
|
|
156
234
|
// If it has date values, it's a date field
|
|
157
235
|
if (analysis.hasDateValues) return "date";
|
|
158
236
|
|
|
237
|
+
// If it has file paths, it's a file field
|
|
238
|
+
if (analysis.hasFilePaths) return "file";
|
|
239
|
+
|
|
240
|
+
// If it has URL values, it's a url field
|
|
241
|
+
if (analysis.hasUrlValues) return "url";
|
|
242
|
+
|
|
243
|
+
// If it has slug values and is a single string type
|
|
244
|
+
if (analysis.hasSlugValues && analysis.types.size === 1) {
|
|
245
|
+
const typesArr = [...analysis.types];
|
|
246
|
+
if (typesArr.length === 1 && typesArr[0] === "string") {
|
|
247
|
+
return "slug";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check for multiselect arrays
|
|
252
|
+
if (analysis.isMultiselect) return "multiselect";
|
|
253
|
+
|
|
159
254
|
// Check detected types (excluding null)
|
|
160
255
|
const nonNullTypes = new Set([...analysis.types].filter((t) => t !== "null"));
|
|
161
256
|
|
|
@@ -166,7 +261,7 @@ function inferFieldType(analysis: FieldAnalysis): FieldType {
|
|
|
166
261
|
case "boolean":
|
|
167
262
|
return "boolean";
|
|
168
263
|
case "number":
|
|
169
|
-
return "number";
|
|
264
|
+
return analysis.allIntegers ? "integer" : "number";
|
|
170
265
|
case "array":
|
|
171
266
|
return "array";
|
|
172
267
|
case "object":
|
|
@@ -290,6 +385,12 @@ export async function detectSchema(
|
|
|
290
385
|
values: [],
|
|
291
386
|
hasImagePaths: false,
|
|
292
387
|
hasDateValues: false,
|
|
388
|
+
hasUrlValues: false,
|
|
389
|
+
hasSlugValues: false,
|
|
390
|
+
hasDatetimeValues: false,
|
|
391
|
+
hasFilePaths: false,
|
|
392
|
+
allIntegers: true,
|
|
393
|
+
isMultiselect: false,
|
|
293
394
|
arrayItemTypes: new Set(),
|
|
294
395
|
};
|
|
295
396
|
fieldAnalyses.set(fieldName, analysis);
|
|
@@ -307,12 +408,30 @@ export async function detectSchema(
|
|
|
307
408
|
if (isDateValue(value)) {
|
|
308
409
|
analysis.hasDateValues = true;
|
|
309
410
|
}
|
|
411
|
+
if (isUrlValue(value)) {
|
|
412
|
+
analysis.hasUrlValues = true;
|
|
413
|
+
}
|
|
414
|
+
if (isSlugValue(value)) {
|
|
415
|
+
analysis.hasSlugValues = true;
|
|
416
|
+
}
|
|
417
|
+
if (isDatetimeValue(value)) {
|
|
418
|
+
analysis.hasDatetimeValues = true;
|
|
419
|
+
}
|
|
420
|
+
if (isFilePath(value)) {
|
|
421
|
+
analysis.hasFilePaths = true;
|
|
422
|
+
}
|
|
423
|
+
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
424
|
+
analysis.allIntegers = false;
|
|
425
|
+
}
|
|
310
426
|
|
|
311
427
|
// Analyze array items
|
|
312
428
|
if (Array.isArray(value)) {
|
|
313
429
|
for (const item of value) {
|
|
314
430
|
analysis.arrayItemTypes.add(detectValueType(item));
|
|
315
431
|
}
|
|
432
|
+
if (isMultiselectArray(value)) {
|
|
433
|
+
analysis.isMultiselect = true;
|
|
434
|
+
}
|
|
316
435
|
}
|
|
317
436
|
}
|
|
318
437
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Collection and Singleton schema helpers
|
|
3
|
+
*
|
|
4
|
+
* These helpers provide a typed way to define collection and singleton
|
|
5
|
+
* schemas using the fields builder API. They convert FieldDefinition
|
|
6
|
+
* objects to internal SchemaField format.
|
|
7
|
+
*
|
|
8
|
+
* @module @writenex/astro/fields/collection
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { CollectionConfig, ImageConfig } from "@/types";
|
|
12
|
+
import { resolveFieldDefinition } from "./resolve";
|
|
13
|
+
import type { FieldDefinition } from "./types";
|
|
14
|
+
|
|
15
|
+
export interface CollectionSchemaConfig {
|
|
16
|
+
name: string;
|
|
17
|
+
path: string;
|
|
18
|
+
filePattern?: string;
|
|
19
|
+
previewUrl?: string;
|
|
20
|
+
schema: Record<string, FieldDefinition>;
|
|
21
|
+
images?: ImageConfig;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SingletonSchemaConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
path: string;
|
|
27
|
+
previewUrl?: string;
|
|
28
|
+
schema: Record<string, FieldDefinition>;
|
|
29
|
+
images?: ImageConfig;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function collection(config: CollectionSchemaConfig): CollectionConfig {
|
|
33
|
+
const schema: Record<string, import("@/types").SchemaField> = {};
|
|
34
|
+
for (const [key, fieldDef] of Object.entries(config.schema)) {
|
|
35
|
+
schema[key] = resolveFieldDefinition(fieldDef);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
name: config.name,
|
|
40
|
+
path: config.path,
|
|
41
|
+
filePattern: config.filePattern,
|
|
42
|
+
previewUrl: config.previewUrl,
|
|
43
|
+
schema,
|
|
44
|
+
images: config.images,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function singleton(config: SingletonSchemaConfig): {
|
|
49
|
+
name: string;
|
|
50
|
+
path: string;
|
|
51
|
+
previewUrl?: string;
|
|
52
|
+
schema: Record<string, import("@/types").SchemaField>;
|
|
53
|
+
images?: ImageConfig;
|
|
54
|
+
} {
|
|
55
|
+
const schema: Record<string, import("@/types").SchemaField> = {};
|
|
56
|
+
for (const [key, fieldDef] of Object.entries(config.schema)) {
|
|
57
|
+
schema[key] = resolveFieldDefinition(fieldDef);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
name: config.name,
|
|
62
|
+
path: config.path,
|
|
63
|
+
previewUrl: config.previewUrl,
|
|
64
|
+
schema,
|
|
65
|
+
images: config.images,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fields builder API for @writenex/astro
|
|
3
|
+
*
|
|
4
|
+
* This module provides a TypeScript-first builder pattern for defining
|
|
5
|
+
* content schema fields. Each field type is a method on the `fields` object.
|
|
6
|
+
*
|
|
7
|
+
* @module @writenex/astro/fields/fields
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ArrayFieldConfig,
|
|
12
|
+
BaseFieldConfig,
|
|
13
|
+
BlocksFieldConfig,
|
|
14
|
+
CheckboxFieldConfig,
|
|
15
|
+
ChildFieldConfig,
|
|
16
|
+
CloudImageFieldConfig,
|
|
17
|
+
ConditionalFieldConfig,
|
|
18
|
+
DateFieldConfig,
|
|
19
|
+
DatetimeFieldConfig,
|
|
20
|
+
FieldDefinition,
|
|
21
|
+
FileFieldConfig,
|
|
22
|
+
ImageFieldConfig,
|
|
23
|
+
IntegerFieldConfig,
|
|
24
|
+
MarkdocFieldConfig,
|
|
25
|
+
MdxFieldConfig,
|
|
26
|
+
MultiselectFieldConfig,
|
|
27
|
+
NumberFieldConfig,
|
|
28
|
+
ObjectFieldConfig,
|
|
29
|
+
PathReferenceFieldConfig,
|
|
30
|
+
RelationshipFieldConfig,
|
|
31
|
+
SelectFieldConfig,
|
|
32
|
+
SlugFieldConfig,
|
|
33
|
+
TextFieldConfig,
|
|
34
|
+
UrlFieldConfig,
|
|
35
|
+
} from "./types";
|
|
36
|
+
|
|
37
|
+
function createField<K extends FieldDefinition["fieldKind"]>(
|
|
38
|
+
fieldKind: K,
|
|
39
|
+
config: Omit<Extract<FieldDefinition, { fieldKind: K }>, "fieldKind">
|
|
40
|
+
): Extract<FieldDefinition, { fieldKind: K }> {
|
|
41
|
+
return { fieldKind, ...config } as Extract<FieldDefinition, { fieldKind: K }>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const fields = {
|
|
45
|
+
text(config: TextFieldConfig = {}): FieldDefinition {
|
|
46
|
+
return createField("text", config);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
slug(config: SlugFieldConfig = {}): FieldDefinition {
|
|
50
|
+
return createField("slug", config);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
url(config: UrlFieldConfig = {}): FieldDefinition {
|
|
54
|
+
return createField("url", config);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
number(config: NumberFieldConfig = {}): FieldDefinition {
|
|
58
|
+
return createField("number", config);
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
integer(config: IntegerFieldConfig = {}): FieldDefinition {
|
|
62
|
+
return createField("integer", config);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
select(config: SelectFieldConfig): FieldDefinition {
|
|
66
|
+
return createField("select", config);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
multiselect(config: MultiselectFieldConfig): FieldDefinition {
|
|
70
|
+
return createField("multiselect", config);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
checkbox(config: CheckboxFieldConfig = {}): FieldDefinition {
|
|
74
|
+
return createField("checkbox", config);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
date(config: DateFieldConfig = {}): FieldDefinition {
|
|
78
|
+
return createField("date", config);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
datetime(config: DatetimeFieldConfig = {}): FieldDefinition {
|
|
82
|
+
return createField("datetime", config);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
image(config: ImageFieldConfig = {}): FieldDefinition {
|
|
86
|
+
return createField("image", config);
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
file(config: FileFieldConfig = {}): FieldDefinition {
|
|
90
|
+
return createField("file", config);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
object(config: ObjectFieldConfig): FieldDefinition {
|
|
94
|
+
return createField("object", config);
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
array(config: ArrayFieldConfig): FieldDefinition {
|
|
98
|
+
return createField("array", config);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
blocks(config: BlocksFieldConfig): FieldDefinition {
|
|
102
|
+
return createField("blocks", config);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
relationship(config: RelationshipFieldConfig): FieldDefinition {
|
|
106
|
+
return createField("relationship", config);
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
pathReference(config: PathReferenceFieldConfig = {}): FieldDefinition {
|
|
110
|
+
return createField("path-reference", config);
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
markdoc(config: MarkdocFieldConfig = {}): FieldDefinition {
|
|
114
|
+
return createField("markdoc", config);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
mdx(config: MdxFieldConfig = {}): FieldDefinition {
|
|
118
|
+
return createField("mdx", config);
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
conditional(config: ConditionalFieldConfig): FieldDefinition {
|
|
122
|
+
return createField("conditional", config);
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
child(config: ChildFieldConfig = {}): FieldDefinition {
|
|
126
|
+
return createField("child", config);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
cloudImage(config: CloudImageFieldConfig = {}): FieldDefinition {
|
|
130
|
+
return createField("cloud-image", config);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
empty(config: BaseFieldConfig = {}): FieldDefinition {
|
|
134
|
+
return createField("empty", config);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
emptyContent(config: BaseFieldConfig = {}): FieldDefinition {
|
|
138
|
+
return createField("empty-content", config);
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
emptyDocument(config: BaseFieldConfig = {}): FieldDefinition {
|
|
142
|
+
return createField("empty-document", config);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
ignored(config: BaseFieldConfig = {}): FieldDefinition {
|
|
146
|
+
return createField("ignored", config);
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type Fields = typeof fields;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fields API barrel exports
|
|
3
|
+
*
|
|
4
|
+
* @module @writenex/astro/fields
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
CollectionSchemaConfig,
|
|
9
|
+
SingletonSchemaConfig,
|
|
10
|
+
} from "./collection";
|
|
11
|
+
export { collection, singleton } from "./collection";
|
|
12
|
+
export type { Fields } from "./fields";
|
|
13
|
+
export { fields } from "./fields";
|
|
14
|
+
export { resolveFieldDefinition } from "./resolve";
|
|
15
|
+
export type {
|
|
16
|
+
ArrayFieldConfig,
|
|
17
|
+
BaseFieldConfig,
|
|
18
|
+
BlocksFieldConfig,
|
|
19
|
+
CheckboxFieldConfig,
|
|
20
|
+
ChildFieldConfig,
|
|
21
|
+
CloudImageFieldConfig,
|
|
22
|
+
ConditionalFieldConfig,
|
|
23
|
+
DateFieldConfig,
|
|
24
|
+
DatetimeFieldConfig,
|
|
25
|
+
FieldDefinition,
|
|
26
|
+
FieldKind,
|
|
27
|
+
FileFieldConfig,
|
|
28
|
+
ImageFieldConfig,
|
|
29
|
+
IntegerFieldConfig,
|
|
30
|
+
MarkdocFieldConfig,
|
|
31
|
+
MdxFieldConfig,
|
|
32
|
+
MultiselectFieldConfig,
|
|
33
|
+
NumberFieldConfig,
|
|
34
|
+
ObjectFieldConfig,
|
|
35
|
+
PathReferenceFieldConfig,
|
|
36
|
+
RelationshipFieldConfig,
|
|
37
|
+
SelectFieldConfig,
|
|
38
|
+
SlugFieldConfig,
|
|
39
|
+
TextFieldConfig,
|
|
40
|
+
UrlFieldConfig,
|
|
41
|
+
ValidationOptions,
|
|
42
|
+
} from "./types";
|