@mandujs/core 0.24.0 → 0.25.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/package.json +3 -1
- package/src/bundler/build.ts +26 -3
- package/src/bundler/dev.ts +18 -4
- package/src/client/globals.ts +61 -44
- package/src/client/router.ts +93 -15
- package/src/client/use-fetch.ts +243 -239
- package/src/config/mandu.ts +64 -0
- package/src/config/validate.ts +35 -0
- package/src/content/collection.ts +506 -0
- package/src/content/frontmatter.ts +189 -0
- package/src/content/generate-types.ts +168 -0
- package/src/content/index.ts +206 -168
- package/src/content/llms-txt.ts +196 -0
- package/src/content/prebuild.test.ts +249 -0
- package/src/content/prebuild.ts +400 -0
- package/src/content/schema.ts +20 -0
- package/src/content/sidebar.ts +212 -0
- package/src/content/slug.ts +110 -0
- package/src/guard/check.ts +5 -2
- package/src/observability/index.ts +37 -18
- package/src/observability/metrics.ts +334 -0
- package/src/runtime/index.ts +11 -0
- package/src/runtime/registry.ts +171 -0
- package/src/runtime/server.ts +104 -4
- package/src/runtime/ssr.ts +118 -1
- package/src/runtime/streaming-ssr.ts +11 -1
- package/src/utils/__tests__/lru-cache.test.ts +186 -0
- package/src/utils/lru-cache.ts +172 -75
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Types Generator (Issue #199)
|
|
3
|
+
*
|
|
4
|
+
* Emits `.mandu/generated/content-types.d.ts` with typed aliases for
|
|
5
|
+
* every collection declared in `content.config.ts`. This gives users
|
|
6
|
+
* full autocomplete on `docs.all()`, `docs.get(slug)`, etc. without
|
|
7
|
+
* having to thread generics through every call site:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // user writes
|
|
11
|
+
* import type { CollectionMap } from '.mandu/generated/content-types';
|
|
12
|
+
* const entry = (await collections.docs.all())[0];
|
|
13
|
+
* entry.data.title; // typed as string when schema has title: z.string()
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* # Contract
|
|
17
|
+
*
|
|
18
|
+
* - Input: a map of collection-name → Collection instance (already
|
|
19
|
+
* returned from `defineCollection({ path, schema })`). We DO NOT
|
|
20
|
+
* re-execute `content.config.ts` here — the caller (the CLI
|
|
21
|
+
* generator in #196) is responsible for running the config in
|
|
22
|
+
* a scratch context and passing the live map to this function.
|
|
23
|
+
*
|
|
24
|
+
* - Output: a single `.d.ts` file with:
|
|
25
|
+
* 1. Re-exports of `Collection`, `CollectionEntry` types
|
|
26
|
+
* 2. A `CollectionMap` interface keyed by collection name
|
|
27
|
+
* 3. A `CollectionName` union type
|
|
28
|
+
* 4. Per-collection `Entry{Name}` aliases
|
|
29
|
+
*
|
|
30
|
+
* - Determinism: output order is stable (sorted by collection name)
|
|
31
|
+
* so regenerating without content changes produces a no-op diff.
|
|
32
|
+
*
|
|
33
|
+
* Schema-level type extraction is intentionally shallow for the MVP —
|
|
34
|
+
* we emit `CollectionEntry<Record<string, unknown>>` for collections
|
|
35
|
+
* without a schema, and `CollectionEntry<z.infer<typeof X>>` only
|
|
36
|
+
* when the user exports the schema alongside the collection. A future
|
|
37
|
+
* pass (tracked in a follow-up) will parse the Zod schema at build
|
|
38
|
+
* time to emit concrete interfaces.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import * as fs from "fs";
|
|
42
|
+
import * as path from "path";
|
|
43
|
+
import type { Collection } from "./collection";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Structural view of a Collection accepted by the type emitter. The
|
|
47
|
+
* generator only reads the collection's name — it never calls methods
|
|
48
|
+
* — so the empty interface lets callers pass typed
|
|
49
|
+
* `Collection<{title: string}>` without running into TypeScript's
|
|
50
|
+
* invariance on generic classes.
|
|
51
|
+
*/
|
|
52
|
+
// biome-ignore lint/suspicious/noEmptyInterface: marker type for variance
|
|
53
|
+
interface CollectionLike {}
|
|
54
|
+
|
|
55
|
+
/** Input map: `{ docs: docsCollection, blog: blogCollection }`. */
|
|
56
|
+
export type CollectionRegistry = Record<string, CollectionLike>;
|
|
57
|
+
|
|
58
|
+
// Re-export the Collection type so users of @mandujs/core/content still
|
|
59
|
+
// see it alongside the registry.
|
|
60
|
+
export type { Collection };
|
|
61
|
+
|
|
62
|
+
/** Options for the type-emitter. */
|
|
63
|
+
export interface GenerateTypesOptions {
|
|
64
|
+
/**
|
|
65
|
+
* Destination path for the generated `.d.ts` file. Defaults to
|
|
66
|
+
* `.mandu/generated/content-types.d.ts` relative to `root`.
|
|
67
|
+
*/
|
|
68
|
+
outFile?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Project root. Used to resolve `outFile` when that option is
|
|
71
|
+
* relative. Defaults to `process.cwd()`.
|
|
72
|
+
*/
|
|
73
|
+
root?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Banner comment prepended to the file. Defaults to an
|
|
76
|
+
* auto-generated warning so authors know not to hand-edit.
|
|
77
|
+
*/
|
|
78
|
+
banner?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const DEFAULT_BANNER = `/**
|
|
82
|
+
* AUTO-GENERATED by @mandujs/core/content — do not edit by hand.
|
|
83
|
+
* Regenerated on every \`mandu build\` / \`mandu dev\` bootstrap.
|
|
84
|
+
*/`;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Generate the `content-types.d.ts` source string. Exposed separately
|
|
88
|
+
* from the filesystem writer so tests can assert on the output without
|
|
89
|
+
* touching disk.
|
|
90
|
+
*/
|
|
91
|
+
export function renderContentTypes(
|
|
92
|
+
collections: CollectionRegistry
|
|
93
|
+
): string {
|
|
94
|
+
const names = Object.keys(collections).sort();
|
|
95
|
+
const lines: string[] = [];
|
|
96
|
+
lines.push(DEFAULT_BANNER);
|
|
97
|
+
lines.push("");
|
|
98
|
+
lines.push(`import type { Collection, CollectionEntry } from "@mandujs/core/content";`);
|
|
99
|
+
lines.push("");
|
|
100
|
+
lines.push("// Per-collection entry aliases");
|
|
101
|
+
for (const name of names) {
|
|
102
|
+
// We can't introspect Zod at runtime without an ICU-sized schema
|
|
103
|
+
// crawler, so we emit `Record<string, unknown>` here. Users who
|
|
104
|
+
// want full field typing can import their schema directly:
|
|
105
|
+
// type DocEntry = CollectionEntry<z.infer<typeof docsSchema>>;
|
|
106
|
+
lines.push(
|
|
107
|
+
`export type Entry${capitalize(name)} = CollectionEntry<Record<string, unknown>>;`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
lines.push("");
|
|
111
|
+
lines.push("export interface CollectionMap {");
|
|
112
|
+
for (const name of names) {
|
|
113
|
+
lines.push(` ${JSON.stringify(name)}: Collection<Record<string, unknown>>;`);
|
|
114
|
+
}
|
|
115
|
+
lines.push("}");
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push(
|
|
118
|
+
`export type CollectionName = ${names.length > 0 ? names.map((n) => JSON.stringify(n)).join(" | ") : "never"};`
|
|
119
|
+
);
|
|
120
|
+
lines.push("");
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Write the generated types file to disk. Creates the directory
|
|
126
|
+
* tree if necessary and is a no-op if the file already contains the
|
|
127
|
+
* exact same contents (prevents unnecessary filewatcher churn during
|
|
128
|
+
* `mandu dev`).
|
|
129
|
+
*/
|
|
130
|
+
export function generateContentTypes(
|
|
131
|
+
collections: CollectionRegistry,
|
|
132
|
+
options: GenerateTypesOptions = {}
|
|
133
|
+
): { outFile: string; wrote: boolean } {
|
|
134
|
+
const root = options.root ?? process.cwd();
|
|
135
|
+
const outFile = path.isAbsolute(options.outFile ?? "")
|
|
136
|
+
? (options.outFile as string)
|
|
137
|
+
: path.resolve(
|
|
138
|
+
root,
|
|
139
|
+
options.outFile ?? ".mandu/generated/content-types.d.ts"
|
|
140
|
+
);
|
|
141
|
+
const banner = options.banner ?? DEFAULT_BANNER;
|
|
142
|
+
const body = renderContentTypes(collections);
|
|
143
|
+
const content = banner === DEFAULT_BANNER ? body : banner + "\n\n" + body.slice(body.indexOf("\n\n") + 2);
|
|
144
|
+
|
|
145
|
+
let existing: string | null = null;
|
|
146
|
+
try {
|
|
147
|
+
existing = fs.readFileSync(outFile, "utf8");
|
|
148
|
+
} catch {
|
|
149
|
+
existing = null;
|
|
150
|
+
}
|
|
151
|
+
if (existing === content) {
|
|
152
|
+
return { outFile, wrote: false };
|
|
153
|
+
}
|
|
154
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
155
|
+
fs.writeFileSync(outFile, content, "utf8");
|
|
156
|
+
return { outFile, wrote: true };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function capitalize(value: string): string {
|
|
160
|
+
// Collection names might contain dashes/underscores (`my-docs`),
|
|
161
|
+
// which are not valid identifiers. Strip non-word chars and
|
|
162
|
+
// PascalCase the remaining segments so `my-docs` becomes `MyDocs`.
|
|
163
|
+
return value
|
|
164
|
+
.split(/[^A-Za-z0-9]+/)
|
|
165
|
+
.filter((part) => part.length > 0)
|
|
166
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
167
|
+
.join("");
|
|
168
|
+
}
|
package/src/content/index.ts
CHANGED
|
@@ -1,168 +1,206 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Content Layer
|
|
3
|
-
*
|
|
4
|
-
* Astro Content Layer에서 영감받은 빌드 타임 콘텐츠 로딩 시스템
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* // content.config.ts
|
|
9
|
-
* import { defineContentConfig, glob, file, api } from '@mandujs/core/content';
|
|
10
|
-
* import { z } from 'zod';
|
|
11
|
-
*
|
|
12
|
-
* const postSchema = z.object({
|
|
13
|
-
* title: z.string(),
|
|
14
|
-
* date: z.coerce.date(),
|
|
15
|
-
* tags: z.array(z.string()).default([]),
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* export default defineContentConfig({
|
|
19
|
-
* collections: {
|
|
20
|
-
* posts: {
|
|
21
|
-
* loader: glob({ pattern: 'content/posts/**\/*.md' }),
|
|
22
|
-
* schema: postSchema,
|
|
23
|
-
* },
|
|
24
|
-
* settings: {
|
|
25
|
-
* loader: file({ path: 'data/settings.json' }),
|
|
26
|
-
* },
|
|
27
|
-
* products: {
|
|
28
|
-
* loader: api({ url: 'https://api.example.com/products' }),
|
|
29
|
-
* },
|
|
30
|
-
* },
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* ```ts
|
|
35
|
-
* // 페이지에서 사용
|
|
36
|
-
* import { getCollection, getEntry } from '@mandujs/core/content';
|
|
37
|
-
*
|
|
38
|
-
* const posts = await getCollection('posts');
|
|
39
|
-
* const post = await getEntry('posts', 'hello-world');
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
// ============================================================================
|
|
44
|
-
// Core exports
|
|
45
|
-
// ============================================================================
|
|
46
|
-
|
|
47
|
-
export {
|
|
48
|
-
ContentLayer,
|
|
49
|
-
createContentLayer,
|
|
50
|
-
getCollection,
|
|
51
|
-
getEntry,
|
|
52
|
-
setGlobalContentLayer,
|
|
53
|
-
getGlobalContentLayer,
|
|
54
|
-
} from "./content-layer";
|
|
55
|
-
|
|
56
|
-
export type { ContentLayerOptions } from "./content-layer";
|
|
57
|
-
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// Loaders
|
|
60
|
-
// ============================================================================
|
|
61
|
-
|
|
62
|
-
export { file, glob, api } from "./loaders";
|
|
63
|
-
|
|
64
|
-
export type {
|
|
65
|
-
Loader,
|
|
66
|
-
FileLoaderOptions,
|
|
67
|
-
GlobLoaderOptions,
|
|
68
|
-
ApiLoaderOptions,
|
|
69
|
-
PaginationConfig,
|
|
70
|
-
ParsedMarkdown,
|
|
71
|
-
LoaderEntry,
|
|
72
|
-
} from "./loaders";
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
// Stores
|
|
76
|
-
// ============================================================================
|
|
77
|
-
|
|
78
|
-
export { ContentDataStore, createDataStore } from "./data-store";
|
|
79
|
-
export type { DataStoreOptions } from "./data-store";
|
|
80
|
-
|
|
81
|
-
export { ContentMetaStore, createMetaStore } from "./meta-store";
|
|
82
|
-
export type { MetaStoreOptions } from "./meta-store";
|
|
83
|
-
|
|
84
|
-
// ============================================================================
|
|
85
|
-
// Utilities
|
|
86
|
-
// ============================================================================
|
|
87
|
-
|
|
88
|
-
export {
|
|
89
|
-
generateDigest,
|
|
90
|
-
generateFileDigest,
|
|
91
|
-
combineDigests,
|
|
92
|
-
digestsMatch,
|
|
93
|
-
hasChanged,
|
|
94
|
-
} from "./digest";
|
|
95
|
-
|
|
96
|
-
export type { DigestOptions } from "./digest";
|
|
97
|
-
|
|
98
|
-
export { createLoaderContext, createSimpleMarkdownRenderer } from "./loader-context";
|
|
99
|
-
export type { CreateLoaderContextOptions } from "./loader-context";
|
|
100
|
-
|
|
101
|
-
export { createContentWatcher } from "./watcher";
|
|
102
|
-
export type { ContentWatcherOptions } from "./watcher";
|
|
103
|
-
|
|
104
|
-
// ============================================================================
|
|
105
|
-
// Types
|
|
106
|
-
// ============================================================================
|
|
107
|
-
|
|
108
|
-
export type {
|
|
109
|
-
// Core types
|
|
110
|
-
DataEntry,
|
|
111
|
-
RenderedContent,
|
|
112
|
-
ContentHeading,
|
|
113
|
-
CollectionConfig,
|
|
114
|
-
ContentConfig,
|
|
115
|
-
|
|
116
|
-
// Loader types
|
|
117
|
-
LoaderContext,
|
|
118
|
-
ParseDataOptions,
|
|
119
|
-
|
|
120
|
-
// Store interfaces
|
|
121
|
-
DataStore,
|
|
122
|
-
MetaStore,
|
|
123
|
-
|
|
124
|
-
// Logger & Watcher
|
|
125
|
-
ContentLogger,
|
|
126
|
-
ContentWatcher,
|
|
127
|
-
|
|
128
|
-
// Config
|
|
129
|
-
ManduContentConfig,
|
|
130
|
-
|
|
131
|
-
// Helper types
|
|
132
|
-
InferEntryData,
|
|
133
|
-
CollectionEntry
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Content Layer
|
|
3
|
+
*
|
|
4
|
+
* Astro Content Layer에서 영감받은 빌드 타임 콘텐츠 로딩 시스템
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // content.config.ts
|
|
9
|
+
* import { defineContentConfig, glob, file, api } from '@mandujs/core/content';
|
|
10
|
+
* import { z } from 'zod';
|
|
11
|
+
*
|
|
12
|
+
* const postSchema = z.object({
|
|
13
|
+
* title: z.string(),
|
|
14
|
+
* date: z.coerce.date(),
|
|
15
|
+
* tags: z.array(z.string()).default([]),
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export default defineContentConfig({
|
|
19
|
+
* collections: {
|
|
20
|
+
* posts: {
|
|
21
|
+
* loader: glob({ pattern: 'content/posts/**\/*.md' }),
|
|
22
|
+
* schema: postSchema,
|
|
23
|
+
* },
|
|
24
|
+
* settings: {
|
|
25
|
+
* loader: file({ path: 'data/settings.json' }),
|
|
26
|
+
* },
|
|
27
|
+
* products: {
|
|
28
|
+
* loader: api({ url: 'https://api.example.com/products' }),
|
|
29
|
+
* },
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* // 페이지에서 사용
|
|
36
|
+
* import { getCollection, getEntry } from '@mandujs/core/content';
|
|
37
|
+
*
|
|
38
|
+
* const posts = await getCollection('posts');
|
|
39
|
+
* const post = await getEntry('posts', 'hello-world');
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Core exports
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
ContentLayer,
|
|
49
|
+
createContentLayer,
|
|
50
|
+
getCollection,
|
|
51
|
+
getEntry,
|
|
52
|
+
setGlobalContentLayer,
|
|
53
|
+
getGlobalContentLayer,
|
|
54
|
+
} from "./content-layer";
|
|
55
|
+
|
|
56
|
+
export type { ContentLayerOptions } from "./content-layer";
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Loaders
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export { file, glob, api } from "./loaders";
|
|
63
|
+
|
|
64
|
+
export type {
|
|
65
|
+
Loader,
|
|
66
|
+
FileLoaderOptions,
|
|
67
|
+
GlobLoaderOptions,
|
|
68
|
+
ApiLoaderOptions,
|
|
69
|
+
PaginationConfig,
|
|
70
|
+
ParsedMarkdown,
|
|
71
|
+
LoaderEntry,
|
|
72
|
+
} from "./loaders";
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Stores
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
export { ContentDataStore, createDataStore } from "./data-store";
|
|
79
|
+
export type { DataStoreOptions } from "./data-store";
|
|
80
|
+
|
|
81
|
+
export { ContentMetaStore, createMetaStore } from "./meta-store";
|
|
82
|
+
export type { MetaStoreOptions } from "./meta-store";
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Utilities
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
generateDigest,
|
|
90
|
+
generateFileDigest,
|
|
91
|
+
combineDigests,
|
|
92
|
+
digestsMatch,
|
|
93
|
+
hasChanged,
|
|
94
|
+
} from "./digest";
|
|
95
|
+
|
|
96
|
+
export type { DigestOptions } from "./digest";
|
|
97
|
+
|
|
98
|
+
export { createLoaderContext, createSimpleMarkdownRenderer } from "./loader-context";
|
|
99
|
+
export type { CreateLoaderContextOptions } from "./loader-context";
|
|
100
|
+
|
|
101
|
+
export { createContentWatcher } from "./watcher";
|
|
102
|
+
export type { ContentWatcherOptions } from "./watcher";
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Types
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
export type {
|
|
109
|
+
// Core types
|
|
110
|
+
DataEntry,
|
|
111
|
+
RenderedContent,
|
|
112
|
+
ContentHeading,
|
|
113
|
+
CollectionConfig,
|
|
114
|
+
ContentConfig,
|
|
115
|
+
|
|
116
|
+
// Loader types
|
|
117
|
+
LoaderContext,
|
|
118
|
+
ParseDataOptions,
|
|
119
|
+
|
|
120
|
+
// Store interfaces
|
|
121
|
+
DataStore,
|
|
122
|
+
MetaStore,
|
|
123
|
+
|
|
124
|
+
// Logger & Watcher
|
|
125
|
+
ContentLogger,
|
|
126
|
+
ContentWatcher,
|
|
127
|
+
|
|
128
|
+
// Config
|
|
129
|
+
ManduContentConfig,
|
|
130
|
+
|
|
131
|
+
// Helper types
|
|
132
|
+
InferEntryData,
|
|
133
|
+
// Legacy ContentLayer CollectionEntry — the MVP Collection API
|
|
134
|
+
// (Issue #199) exports a different `CollectionEntry` shape further
|
|
135
|
+
// below ({ slug, data, content, filePath }). The MVP wins the
|
|
136
|
+
// unqualified name since it is the first-class public surface; the
|
|
137
|
+
// legacy ContentLayer alias is preserved here for back-compat.
|
|
138
|
+
CollectionEntry as LegacyCollectionEntry,
|
|
139
|
+
} from "./types";
|
|
140
|
+
|
|
141
|
+
// Errors
|
|
142
|
+
export {
|
|
143
|
+
ContentError,
|
|
144
|
+
LoaderError,
|
|
145
|
+
ParseError,
|
|
146
|
+
ValidationError,
|
|
147
|
+
} from "./types";
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Config helper
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Content 설정 정의 헬퍼
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* export default defineContentConfig({
|
|
159
|
+
* collections: {
|
|
160
|
+
* posts: { loader: glob({ pattern: 'content/posts/**\/*.md' }) },
|
|
161
|
+
* },
|
|
162
|
+
* });
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
import type { ContentConfig as ContentConfigType } from "./types";
|
|
166
|
+
|
|
167
|
+
export function defineContentConfig<T extends ContentConfigType>(config: T): T {
|
|
168
|
+
return config;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// MVP Collection API (Issue #199)
|
|
173
|
+
// ============================================================================
|
|
174
|
+
//
|
|
175
|
+
// `defineCollection` now supports BOTH the legacy `{ loader, schema }` shape
|
|
176
|
+
// (for backwards compat with ContentLayer projects) and the new
|
|
177
|
+
// `{ path, schema, ... }` shape that returns a Collection instance with
|
|
178
|
+
// `.load()/.all()/.get()/.getCompiled()`. The overload dispatch lives in
|
|
179
|
+
// `./collection.ts` — it detects the shape at runtime via the presence
|
|
180
|
+
// of the `loader` property.
|
|
181
|
+
|
|
182
|
+
export { defineCollection, Collection } from "./collection";
|
|
183
|
+
export type {
|
|
184
|
+
CollectionEntry,
|
|
185
|
+
CompiledCollectionEntry,
|
|
186
|
+
CollectionSort,
|
|
187
|
+
DefineCollectionOptions,
|
|
188
|
+
} from "./collection";
|
|
189
|
+
|
|
190
|
+
export { z } from "./schema";
|
|
191
|
+
export type { ZodSchema, ZodError, ZodType, ZodTypeAny, Infer } from "./schema";
|
|
192
|
+
|
|
193
|
+
export { parseFrontmatter, parseSimpleYaml, parseScalar } from "./frontmatter";
|
|
194
|
+
export type { ParsedFrontmatter } from "./frontmatter";
|
|
195
|
+
|
|
196
|
+
export { slugFromPath } from "./slug";
|
|
197
|
+
export type { SlugFromPathOptions } from "./slug";
|
|
198
|
+
|
|
199
|
+
export { generateSidebar } from "./sidebar";
|
|
200
|
+
export type { SidebarNode, GenerateSidebarOptions } from "./sidebar";
|
|
201
|
+
|
|
202
|
+
export { generateLLMSTxt } from "./llms-txt";
|
|
203
|
+
export type { LLMSTxtInput, GenerateLLMSTxtOptions } from "./llms-txt";
|
|
204
|
+
|
|
205
|
+
export { generateContentTypes, renderContentTypes } from "./generate-types";
|
|
206
|
+
export type { CollectionRegistry, GenerateTypesOptions } from "./generate-types";
|