Skip to content

Commit 2241c6e

Browse files
committed
improved API
1 parent 6fc7e6f commit 2241c6e

File tree

17 files changed

+511
-520
lines changed

17 files changed

+511
-520
lines changed

.yarn/releases/yarn-3.2.2.cjs renamed to .yarn/releases/yarn-3.2.3.cjs

+266-266
Large diffs are not rendered by default.

.yarnrc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ plugins:
1616
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
1717
spec: "@yarnpkg/plugin-version"
1818

19-
yarnPath: .yarn/releases/yarn-3.2.2.cjs
19+
yarnPath: .yarn/releases/yarn-3.2.3.cjs

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@
4747
"next-contentlayer": "workspace:*",
4848
"rxjs": "^7.1.0"
4949
},
50-
"packageManager": "[email protected].2"
50+
"packageManager": "[email protected].3"
5151
}

packages/@contentlayer/cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentlayer/cli",
3-
"version": "0.2.8-dev.5",
3+
"version": "0.2.8-dev.8",
44
"type": "module",
55
"exports": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/@contentlayer/client/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentlayer/client",
3-
"version": "0.2.8-dev.5",
3+
"version": "0.2.8-dev.8",
44
"type": "module",
55
"exports": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/@contentlayer/core/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentlayer/core",
3-
"version": "0.2.8-dev.5",
3+
"version": "0.2.8-dev.8",
44
"type": "module",
55
"exports": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/@contentlayer/experimental-source-files-stackbit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentlayer/experimental-source-files-stackbit",
3-
"version": "0.2.8-dev.5",
3+
"version": "0.2.8-dev.8",
44
"type": "module",
55
"exports": {
66
".": {

packages/@contentlayer/experimental-source-files-stackbit/src/addComputedFields.ts

-46
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,54 @@
1-
import * as SourceFiles from '@contentlayer/source-files'
2-
import { casesHandled, isReadonlyArray, not, notImplemented, partition, pick } from '@contentlayer/utils'
3-
import { identity } from '@contentlayer/utils/effect'
1+
import type { GetDocumentTypeNamesGen } from '@contentlayer/core'
2+
import type * as SourceFiles from '@contentlayer/source-files'
3+
import { defineDocumentType } from '@contentlayer/source-files'
4+
import { not, partition } from '@contentlayer/utils'
45
import * as Stackbit from '@stackbit/sdk'
56
import { validateAndNormalizeConfig } from '@stackbit/sdk/dist/config/config-loader.js'
67

7-
export { addComputedFields } from './addComputedFields.js'
8+
import type { SharedCtx } from './mapping.js'
9+
import { stackbitDocumentLikeModelToDocumentType, stackbitObjectModelToDocumentType } from './mapping.js'
810

9-
type DocumentTypeMap = Record<string, SourceFiles.DocumentType>
10-
type NestedTypeMap = Record<string, SourceFiles.NestedType>
11-
type SharedCtx = {
12-
documentTypeMap: DocumentTypeMap
13-
nestedTypeMap: NestedTypeMap
11+
export type ContentlayerOverrideArgs<TDocumentTypeNames extends string> = {
12+
documentTypes: Partial<{
13+
[TDocumentTypeName in TDocumentTypeNames]: ContentlayerOverrideDocumentType<TDocumentTypeName>
14+
}>
15+
}
16+
17+
export type ContentlayerOverrideDocumentType<TDocumentTypeName extends string> = {
18+
filePathPattern?: string
19+
computedFields?: SourceFiles.ComputedFields<TDocumentTypeName>
1420
}
1521

1622
/**
1723
* @example
1824
* ```ts
25+
* // contentlayer.config.ts
1926
* import { makeSource } from 'contentlayer/source-files'
2027
* import { loadStackbitConfigAsDocumentTypes } from '@contentlayer/experimental-source-files-stackbit'
2128
*
29+
* // Looks for `stackbit.yaml` in the current directory
2230
* export default loadStackbitConfigAsDocumentTypes().then((documentTypes) => {
2331
* return makeSource({ contentDirPath: 'content', documentTypes })
2432
* })
2533
* ```
2634
*/
27-
export const loadStackbitConfigAsDocumentTypes = (
35+
export const loadStackbitConfigAsDocumentTypes = <TDocumentTypeNames extends GetDocumentTypeNamesGen>(
2836
options: Stackbit.ConfigLoaderOptions = { dirPath: '' },
37+
overrideArgs: ContentlayerOverrideArgs<TDocumentTypeNames> = { documentTypes: {} },
2938
): Promise<SourceFiles.DocumentType[]> =>
3039
Stackbit.loadConfig(options).then((configResult) => {
3140
if (configResult.errors.length > 0) {
3241
throw new Error(configResult.errors.join('\n'))
3342
}
3443

35-
return stackbitConfigToDocumentTypes(configResult.config!)
44+
return stackbitConfigToDocumentTypes(configResult.config!, overrideArgs)
3645
})
3746

3847
/**
3948
*
4049
* @example
4150
* ```ts
51+
* // contentlayer.config.ts
4252
* import { makeSource } from 'contentlayer/source-files'
4353
* import { stackbitConfigToDocumentTypes } from '@contentlayer/source-files-stackbit'
4454
* import stackbitConfig from './stackbit.config.js'
@@ -48,8 +58,9 @@ export const loadStackbitConfigAsDocumentTypes = (
4858
* export default makeSource({ contentDirPath: 'content', documentTypes })
4959
* ```
5060
*/
51-
export const stackbitConfigToDocumentTypes = (
61+
export const stackbitConfigToDocumentTypes = <TDocumentTypeNames extends GetDocumentTypeNamesGen>(
5262
stackbitConfig: Stackbit.Config | Stackbit.YamlConfig,
63+
overrideArgs: ContentlayerOverrideArgs<TDocumentTypeNames> = { documentTypes: {} },
5364
): SourceFiles.DocumentType[] => {
5465
const validatedStackbitConfig = validateStackbitConfig(stackbitConfig)
5566

@@ -68,12 +79,26 @@ export const stackbitConfigToDocumentTypes = (
6879
})
6980

7081
documentTypes.forEach((documentType) => {
71-
ctx.documentTypeMap[documentType.def().name] = documentType
82+
const documentTypeName = documentType.def().name
83+
ctx.documentTypeMap[documentTypeName] = documentType
84+
85+
const documentOverride = (overrideArgs.documentTypes as any)[documentTypeName]
86+
if (documentOverride) {
87+
patchDocumentType(documentType, documentOverride)
88+
}
7289
})
7390

7491
return documentTypes
7592
}
7693

94+
const patchDocumentType = (
95+
documentType: SourceFiles.DocumentType,
96+
patch: Partial<SourceFiles.DocumentTypeDef>,
97+
): void => {
98+
const previousDef = documentType.def()
99+
documentType.def = defineDocumentType(() => ({ ...previousDef, ...patch })).def
100+
}
101+
77102
const validateStackbitConfig = (stackbitConfig: Stackbit.Config | Stackbit.YamlConfig): Stackbit.Config => {
78103
if (Array.isArray(stackbitConfig.models)) {
79104
return stackbitConfig as Stackbit.Config
@@ -88,190 +113,9 @@ const validateStackbitConfig = (stackbitConfig: Stackbit.Config | Stackbit.YamlC
88113
return stackbitConfigResult.config
89114
}
90115

91-
const stackbitDocumentLikeModelToDocumentType =
92-
(ctx: SharedCtx) =>
93-
(stackbitModel: Stackbit.PageModel | Stackbit.DataModel | Stackbit.ConfigModel): SourceFiles.DocumentType => {
94-
return SourceFiles.defineDocumentType(() => ({
95-
name: stackbitModel.name,
96-
description: stackbitModel.description,
97-
fields: (stackbitModel.fields ?? []).map(stackbitFieldToField(ctx)),
98-
isSingleton: stackbitModel.type === 'config' || stackbitModel.singleInstance === true,
99-
}))
100-
}
101-
102-
const stackbitObjectModelToDocumentType =
103-
(ctx: SharedCtx) =>
104-
(stackbitModel: Stackbit.ObjectModel): SourceFiles.NestedType => {
105-
return SourceFiles.defineNestedType(() => ({
106-
name: stackbitModel.name,
107-
description: stackbitModel.description,
108-
fields: (stackbitModel.fields ?? []).map(stackbitFieldToField(ctx)),
109-
}))
110-
}
111-
112-
const stackbitFieldToField =
113-
(ctx: SharedCtx) =>
114-
(stackbitField: Stackbit.Field): SourceFiles.FieldDefWithName => {
115-
const commonFields = {
116-
...pick(stackbitField, ['name', 'description', 'required']),
117-
default: stackbitField.default as any,
118-
}
119-
120-
type WithName<T> = T & { name: string }
121-
122-
switch (stackbitField.type) {
123-
case 'boolean':
124-
case 'number':
125-
type FieldDef = SourceFiles.BooleanFieldDef | SourceFiles.NumberFieldDef
126-
return identity<WithName<FieldDef>>({ ...commonFields, type: stackbitField.type })
127-
case 'enum':
128-
return identity<WithName<SourceFiles.EnumFieldDef>>({
129-
...commonFields,
130-
type: 'enum',
131-
options: stackbitField.options.map(mapStackbitEnumOption),
132-
})
133-
case 'style':
134-
return identity<WithName<SourceFiles.JSONFieldDef>>({ ...commonFields, type: 'json' })
135-
case 'list': {
136-
const of = stackbitListItemToListFieldDef(ctx)(stackbitField.items!)
137-
return isReadonlyArray(of)
138-
? identity<WithName<SourceFiles.ListPolymorphicFieldDef>>({
139-
...commonFields,
140-
type: 'list',
141-
of,
142-
typeField: 'type',
143-
})
144-
: identity<WithName<SourceFiles.ListFieldDef>>({ ...commonFields, type: 'list', of })
145-
}
146-
case 'reference': {
147-
const of = stackbitField.models.map((modelName) => ctx.documentTypeMap[modelName]!)
148-
if (of.length === 1) {
149-
return identity<WithName<SourceFiles.ReferenceFieldDef>>({ ...commonFields, type: 'reference', of: of[0]! })
150-
}
151-
return identity<WithName<SourceFiles.ReferencePolymorphicFieldDef>>({
152-
...commonFields,
153-
type: 'reference',
154-
of,
155-
typeField: 'type',
156-
})
157-
}
158-
case 'model': {
159-
const of = stackbitField.models.map((modelName) => ctx.nestedTypeMap[modelName]!)
160-
if (of.length === 1) {
161-
return identity<WithName<SourceFiles.NestedFieldDef>>({ ...commonFields, type: 'nested', of: of[0]! })
162-
}
163-
return identity<WithName<SourceFiles.NestedPolymorphicFieldDef>>({
164-
...commonFields,
165-
type: 'nested',
166-
of,
167-
typeField: 'type',
168-
})
169-
}
170-
case 'object': {
171-
const unnamedNestedTypeDef = identity<SourceFiles.NestedUnnamedTypeDef>({
172-
fields: stackbitField.fields.map(stackbitFieldToField(ctx)),
173-
})
174-
return identity<WithName<SourceFiles.NestedFieldDef>>({
175-
...commonFields,
176-
type: 'nested',
177-
of: { type: 'nested', def: () => unnamedNestedTypeDef },
178-
})
179-
}
180-
case 'markdown':
181-
return identity<WithName<SourceFiles.MarkdownFieldDef>>({ ...commonFields, type: 'markdown' })
182-
case 'json':
183-
return identity<WithName<SourceFiles.JSONFieldDef>>({ ...commonFields, type: 'json' })
184-
case 'image':
185-
return identity<WithName<SourceFiles.ImageFieldDef>>({ ...commonFields, type: 'image' })
186-
case 'datetime':
187-
case 'date':
188-
return identity<WithName<SourceFiles.DateFieldDef>>({ ...commonFields, type: 'date' })
189-
case 'string':
190-
case 'url':
191-
case 'text':
192-
case 'color':
193-
case 'slug':
194-
case 'html':
195-
case 'file':
196-
return identity<WithName<SourceFiles.StringFieldDef>>({ ...commonFields, type: 'string' })
197-
case 'richText':
198-
notImplemented(`richText doesn't exist in the "files" content source`)
199-
default:
200-
casesHandled(stackbitField)
201-
}
202-
}
203-
204-
const stackbitListItemToListFieldDef =
205-
(ctx: SharedCtx) =>
206-
(
207-
stackbitListItem: Stackbit.FieldListItems,
208-
): SourceFiles.ListFieldDefItem.Item | readonly SourceFiles.ListFieldDefItem.Item[] => {
209-
switch (stackbitListItem.type) {
210-
case 'boolean':
211-
case 'string':
212-
case 'number':
213-
type Item =
214-
| SourceFiles.ListFieldDefItem.ItemString
215-
| SourceFiles.ListFieldDefItem.ItemBoolean
216-
| SourceFiles.ListFieldDefItem.ItemNumber
217-
return identity<Item>({ type: stackbitListItem.type })
218-
case 'enum':
219-
return identity<SourceFiles.ListFieldDefItem.ItemEnum>({
220-
type: 'enum',
221-
options: stackbitListItem.options.map(mapStackbitEnumOption),
222-
})
223-
case 'reference':
224-
return firstArrayItemIfOne(
225-
stackbitListItem.models.map((modelName) =>
226-
identity<SourceFiles.ListFieldDefItem.ItemDocumentReference>(ctx.documentTypeMap[modelName]!),
227-
),
228-
)
229-
case 'model':
230-
return firstArrayItemIfOne(
231-
stackbitListItem.models.map((modelName) =>
232-
identity<SourceFiles.ListFieldDefItem.ItemNestedType>(ctx.nestedTypeMap[modelName]!),
233-
),
234-
)
235-
case 'object':
236-
return identity<SourceFiles.ListFieldDefItem.ItemNestedType>({
237-
type: 'nested',
238-
def: () => ({ fields: stackbitListItem.fields.map(stackbitFieldToField(ctx)) }),
239-
})
240-
case 'date':
241-
case 'datetime':
242-
return identity<SourceFiles.ListFieldDefItem.ItemDate>({ type: 'date' })
243-
case 'json':
244-
return identity<SourceFiles.ListFieldDefItem.ItemJSON>({ type: 'json' })
245-
case 'markdown':
246-
return identity<SourceFiles.ListFieldDefItem.ItemMarkdown>({ type: 'markdown' })
247-
case 'image':
248-
return identity<SourceFiles.ListFieldDefItem.ItemImage>({ type: 'image' })
249-
case 'url':
250-
case 'text':
251-
case 'color':
252-
case 'slug':
253-
case 'html':
254-
case 'file':
255-
return identity<SourceFiles.ListFieldDefItem.ItemString>({ type: 'string' })
256-
case 'richText':
257-
notImplemented(`richText doesn't exist in the "files" content source`)
258-
default:
259-
casesHandled(stackbitListItem)
260-
}
261-
}
262-
263-
const mapStackbitEnumOption = (option: Stackbit.FieldEnumOptionValue | Stackbit.FieldEnumOptionObject): string => {
264-
if (typeof option === 'string' || typeof option === 'number') {
265-
return option.toString()
266-
}
267-
return option.value.toString()
268-
}
269-
270116
const isDocumentLikeModel = (
271117
model: Stackbit.Model,
272118
): model is Stackbit.PageModel | Stackbit.DataModel | Stackbit.ConfigModel =>
273119
model.type === 'data' || model.type === 'page' || model.type === 'config'
274120

275121
const isImageModel = (model: Stackbit.Model): model is Stackbit.ImageModel => model.type === 'image'
276-
277-
const firstArrayItemIfOne = <T>(array: readonly T[]): T | readonly T[] => (array.length === 1 ? array[0]! : array)

0 commit comments

Comments
 (0)