Skip to content

Commit 99f25df

Browse files
committed
refactor: simplify collection definition
1 parent 8f06edd commit 99f25df

File tree

8 files changed

+61
-64
lines changed

8 files changed

+61
-64
lines changed

src/module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
215215
}
216216
collectionDump[collection.name] = []
217217
// Collection table definition
218-
collectionDump[collection.name].push(...collection.tableDefinition.split('\n'))
218+
collectionDump[collection.name].push(
219+
...generateCollectionTableDefinition(collection, { drop: true }).split('\n'),
220+
)
219221

220222
if (!collection.source) {
221223
continue
@@ -262,7 +264,7 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
262264
collectionChecksum[collection.name] = hash(collectionDump[collection.name])
263265

264266
collectionDump[collection.name].push(
265-
generateCollectionTableDefinition(infoCollection.name, infoCollection, { drop: false }),
267+
generateCollectionTableDefinition(infoCollection, { drop: false }),
266268
`DELETE FROM ${infoCollection.tableName} WHERE _id = 'checksum_${collection.name}'`,
267269
generateCollectionInsert(infoCollection, { _id: `checksum_${collection.name}`, version: collectionChecksum[collection.name] }),
268270
)

src/runtime/adapters/sqlite.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default createDatabaseAdapter<{ filename: string }>((opts) => {
77
if (!db) {
88
const filename = !opts || isAbsolute(opts?.filename || '')
99
? opts?.filename
10+
// @ts-expect-error - `_importMeta_` is defined in nitro runtime
1011
: new URL(opts.filename, globalThis._importMeta_.url).pathname
1112
db = new Database(filename)
1213
}

src/types/collection.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,16 @@ export interface DefinedCollection<T extends ZodRawShape = ZodRawShape> {
4141
source: ResolvedCollectionSource | undefined
4242
schema: ZodObject<T>
4343
extendedSchema: ZodObject<T>
44+
jsonFields: string[]
4445
}
4546

4647
export interface ResolvedCollection<T extends ZodRawShape = ZodRawShape> {
4748
name: string
48-
pascalName: string
49+
tableName: string
4950
type: CollectionType
5051
source: ResolvedCollectionSource | undefined
5152
schema: ZodObject<T>
5253
extendedSchema: ZodObject<T>
53-
tableName: string
54-
tableDefinition: string
55-
generatedFields: {
56-
raw: boolean
57-
body: boolean
58-
path: boolean
59-
}
6054
jsonFields: string[]
6155
/**
6256
* Whether the collection is private or not.

src/utils/collection.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { pascalCase } from 'scule'
21
import type { ZodObject, ZodOptionalDef, ZodRawShape, ZodStringDef, ZodType } from 'zod'
32
import type { Collection, ResolvedCollection, CollectionSource, DefinedCollection, ResolvedCollectionSource } from '../types/collection'
43
import { defineLocalSource, defineGitHubSource } from './source'
54
import { metaSchema, pageSchema } from './schema'
65
import type { ZodFieldType } from './zod'
7-
import { getUnderlyingType, ZodToSqlFieldTypes, z } from './zod'
6+
import { getUnderlyingType, ZodToSqlFieldTypes, z, getUnderlyingTypeName } from './zod'
87
import { logger } from './dev'
98

109
const JSON_FIELDS_TYPES = ['ZodObject', 'ZodArray', 'ZodRecord', 'ZodIntersection', 'ZodUnion', 'ZodAny']
1110

12-
const getTableName = (name: string) => `content_${name}`
11+
function getTableName(name: string) {
12+
return `content_${name}`
13+
}
1314

1415
export function defineCollection<T extends ZodRawShape>(collection: Collection<T>): DefinedCollection {
1516
let schema = collection.schema || z.object({})
@@ -24,6 +25,9 @@ export function defineCollection<T extends ZodRawShape>(collection: Collection<T
2425
source: resolveSource(collection.source),
2526
schema: collection.schema || z.object({}),
2627
extendedSchema: schema,
28+
jsonFields: Object.keys(schema.shape)
29+
.filter(key => JSON_FIELDS_TYPES
30+
.includes(getUnderlyingTypeName(schema.shape[key as keyof typeof schema.shape]))),
2731
}
2832
}
2933

@@ -40,19 +44,7 @@ export function resolveCollection(name: string, collection: DefinedCollection):
4044
...collection,
4145
name,
4246
type: collection.type || 'page',
43-
pascalName: pascalCase(name),
44-
source: collection.source,
4547
tableName: getTableName(name),
46-
tableDefinition: generateCollectionTableDefinition(name, collection, { drop: true }),
47-
generatedFields: {
48-
raw: typeof collection.schema.shape.raw !== 'undefined',
49-
body: typeof collection.schema.shape.body !== 'undefined',
50-
path: typeof collection.schema.shape.path !== 'undefined',
51-
},
52-
jsonFields: Object.keys(collection.extendedSchema.shape || {})
53-
.filter(key => JSON_FIELDS_TYPES
54-
.includes(getUnderlyingType(collection.extendedSchema.shape[key]).constructor.name)),
55-
5648
private: name === '_info',
5749
}
5850
}
@@ -139,7 +131,7 @@ export function generateCollectionInsert(collection: ResolvedCollection, data: R
139131
}
140132

141133
// Convert a collection with Zod schema to SQL table definition
142-
export function generateCollectionTableDefinition(name: string, collection: DefinedCollection, opts: { drop?: boolean } = {}) {
134+
export function generateCollectionTableDefinition(collection: ResolvedCollection, opts: { drop?: boolean } = {}) {
143135
const sortedKeys = Object.keys((collection.extendedSchema).shape).sort()
144136
const sqlFields = sortedKeys.map((key) => {
145137
const type = (collection.extendedSchema).shape[key]
@@ -180,10 +172,10 @@ export function generateCollectionTableDefinition(name: string, collection: Defi
180172
return `"${key}" ${sqlType}${constraints.join(' ')}`
181173
})
182174

183-
let definition = `CREATE TABLE IF NOT EXISTS ${getTableName(name)} (${sqlFields.join(', ')});`
175+
let definition = `CREATE TABLE IF NOT EXISTS ${collection.tableName} (${sqlFields.join(', ')});`
184176

185177
if (opts.drop) {
186-
definition = `DROP TABLE IF EXISTS ${getTableName(name)};\n${definition}`
178+
definition = `DROP TABLE IF EXISTS ${collection.tableName};\n${definition}`
187179
}
188180

189181
return definition

src/utils/content/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export async function parseContent(key: string, content: string, collection: Res
8282
})
8383

8484
const { id: _id, ...parsedContentFields } = parsedContent
85-
const result = { _id } as typeof collection.schema._type
85+
const result = { _id } as typeof collection.extendedSchema._type
8686
const meta = {} as Record<string, unknown>
8787

8888
const collectionKeys = Object.keys(collection.extendedSchema.shape)
@@ -97,5 +97,9 @@ export async function parseContent(key: string, content: string, collection: Res
9797

9898
result.meta = meta
9999

100+
// Storing `content` into `rawbody` field
101+
// This allow users to define `rowbody` field in collection schema and access to raw content
102+
result.rawbody = content
103+
100104
return result
101105
}

src/utils/templates.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { NuxtTemplate } from '@nuxt/schema'
44
import { isAbsolute, join, relative } from 'pathe'
55
import { genDynamicImport } from 'knitwork'
66
import { deflate } from 'pako'
7+
import { pascalCase } from 'scule'
78
import type { ResolvedCollection } from '../types/collection'
89
import type { Manifest } from '../types/manifest'
910

@@ -36,15 +37,15 @@ export const contentTypesTemplate = (collections: ResolvedCollection[]) => ({
3637
'',
3738
'declare module \'@nuxt/content\' {',
3839
...publicCollections.map(c =>
39-
indentLines(`interface ${c.pascalName}CollectionItem extends ${parentInterface(c)} ${printNode(zodToTs(c.schema, c.pascalName).node)}`),
40+
indentLines(`interface ${pascalCase(c.name)}CollectionItem extends ${parentInterface(c)} ${printNode(zodToTs(c.schema, pascalCase(c.name)).node)}`),
4041
),
4142
'',
4243
' interface PageCollections {',
43-
...pagesCollections.map(c => indentLines(`${c.name}: ${c.pascalName}CollectionItem`, 4)),
44+
...pagesCollections.map(c => indentLines(`${c.name}: ${pascalCase(c.name)}CollectionItem`, 4)),
4445
' }',
4546
'',
4647
' interface Collections {',
47-
...publicCollections.map(c => indentLines(`${c.name}: ${c.pascalName}CollectionItem`, 4)),
48+
...publicCollections.map(c => indentLines(`${c.name}: ${pascalCase(c.name)}CollectionItem`, 4)),
4849
' }',
4950
'}',
5051
'',
@@ -61,7 +62,6 @@ export const collectionsTemplate = (collections: ResolvedCollection[]) => ({
6162
const collectionsMeta = options.collections.reduce((acc, collection) => {
6263
acc[collection.name] = {
6364
name: collection.name,
64-
pascalName: collection.pascalName,
6565
tableName: collection.tableName,
6666
// Remove source from collection meta if it's a remote collection
6767
source: collection.source?.repository ? undefined : collection.source,

src/utils/zod.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ export function getUnderlyingType(zodType: ZodType): ZodType {
2525
}
2626
return zodType
2727
}
28+
29+
export function getUnderlyingTypeName(zodType: ZodType): string {
30+
return getUnderlyingType(zodType).constructor.name
31+
}

test/unit/generateCollectionTableDefinition.test.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { describe, expect, test } from 'vitest'
22
import { z } from 'zod'
3-
import { generateCollectionTableDefinition, defineCollection } from '../../src/utils/collection'
3+
import { generateCollectionTableDefinition, defineCollection, resolveCollection } from '../../src/utils/collection'
44
import { getTableName } from '../utils/database'
55

66
describe('generateCollectionTableDefinition', () => {
77
test('Page without custom schema', () => {
8-
const collection = defineCollection({
8+
const collection = resolveCollection('content', defineCollection({
99
type: 'page',
1010
source: 'pages/**',
11-
})
12-
const sql = generateCollectionTableDefinition('content', collection)
11+
}))!
12+
const sql = generateCollectionTableDefinition(collection)
1313

1414
expect(sql).toBe([
1515
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -28,14 +28,14 @@ describe('generateCollectionTableDefinition', () => {
2828
})
2929

3030
test('Page with custom schema', () => {
31-
const collection = defineCollection({
31+
const collection = resolveCollection('content', defineCollection({
3232
type: 'page',
3333
source: 'pages/**',
3434
schema: z.object({
3535
customField: z.string(),
3636
}),
37-
})
38-
const sql = generateCollectionTableDefinition('content', collection)
37+
}))!
38+
const sql = generateCollectionTableDefinition(collection)
3939

4040
expect(sql).toBe([
4141
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -55,14 +55,14 @@ describe('generateCollectionTableDefinition', () => {
5555
})
5656

5757
test('Data with schema', () => {
58-
const collection = defineCollection({
58+
const collection = resolveCollection('content', defineCollection({
5959
type: 'data',
6060
source: 'data/**',
6161
schema: z.object({
6262
customField: z.string(),
6363
}),
64-
})
65-
const sql = generateCollectionTableDefinition('content', collection)
64+
}))!
65+
const sql = generateCollectionTableDefinition(collection)
6666

6767
expect(sql).toBe([
6868
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -77,14 +77,14 @@ describe('generateCollectionTableDefinition', () => {
7777

7878
// Columns
7979
test('String with max length', () => {
80-
const collection = defineCollection({
80+
const collection = resolveCollection('content', defineCollection({
8181
type: 'data',
8282
source: 'data/**',
8383
schema: z.object({
8484
customField: z.string().max(64).default('foo'),
8585
}),
86-
})
87-
const sql = generateCollectionTableDefinition('content', collection)
86+
}))!
87+
const sql = generateCollectionTableDefinition(collection)
8888

8989
expect(sql).toBe([
9090
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -98,14 +98,14 @@ describe('generateCollectionTableDefinition', () => {
9898
})
9999

100100
test('Number', () => {
101-
const collection = defineCollection({
101+
const collection = resolveCollection('content', defineCollection({
102102
type: 'data',
103103
source: 'data/**',
104104
schema: z.object({
105105
customField: z.number().default(13),
106106
}),
107-
})
108-
const sql = generateCollectionTableDefinition('content', collection)
107+
}))!
108+
const sql = generateCollectionTableDefinition(collection)
109109

110110
expect(sql).toBe([
111111
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -119,14 +119,14 @@ describe('generateCollectionTableDefinition', () => {
119119
})
120120

121121
test('Boolean', () => {
122-
const collection = defineCollection({
122+
const collection = resolveCollection('content', defineCollection({
123123
type: 'data',
124124
source: 'data/**',
125125
schema: z.object({
126126
customField: z.boolean().default(false),
127127
}),
128-
})
129-
const sql = generateCollectionTableDefinition('content', collection)
128+
}))!
129+
const sql = generateCollectionTableDefinition(collection)
130130

131131
expect(sql).toBe([
132132
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -140,14 +140,14 @@ describe('generateCollectionTableDefinition', () => {
140140
})
141141

142142
test('Date', () => {
143-
const collection = defineCollection({
143+
const collection = resolveCollection('content', defineCollection({
144144
type: 'data',
145145
source: 'data/**',
146146
schema: z.object({
147147
customField: z.date(),
148148
}),
149-
})
150-
const sql = generateCollectionTableDefinition('content', collection)
149+
}))!
150+
const sql = generateCollectionTableDefinition(collection)
151151

152152
expect(sql).toBe([
153153
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -161,7 +161,7 @@ describe('generateCollectionTableDefinition', () => {
161161
})
162162

163163
test('Object', () => {
164-
const collection = defineCollection({
164+
const collection = resolveCollection('content', defineCollection({
165165
type: 'data',
166166
source: 'data/**',
167167
schema: z.object({
@@ -170,8 +170,8 @@ describe('generateCollectionTableDefinition', () => {
170170
f2: z.string(),
171171
}),
172172
}),
173-
})
174-
const sql = generateCollectionTableDefinition('content', collection)
173+
}))!
174+
const sql = generateCollectionTableDefinition(collection)
175175

176176
expect(sql).toBe([
177177
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -185,7 +185,7 @@ describe('generateCollectionTableDefinition', () => {
185185
})
186186

187187
test('Array', () => {
188-
const collection = defineCollection({
188+
const collection = resolveCollection('content', defineCollection({
189189
type: 'data',
190190
source: 'data/**',
191191
schema: z.object({
@@ -194,8 +194,8 @@ describe('generateCollectionTableDefinition', () => {
194194
f2: z.string(),
195195
})),
196196
}),
197-
})
198-
const sql = generateCollectionTableDefinition('content', collection)
197+
}))!
198+
const sql = generateCollectionTableDefinition(collection)
199199

200200
expect(sql).toBe([
201201
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,
@@ -209,7 +209,7 @@ describe('generateCollectionTableDefinition', () => {
209209
})
210210

211211
test('Nullable', () => {
212-
const collection = defineCollection({
212+
const collection = resolveCollection('content', defineCollection({
213213
type: 'data',
214214
source: 'data/**',
215215
schema: z.object({
@@ -225,8 +225,8 @@ describe('generateCollectionTableDefinition', () => {
225225
}).nullable(),
226226
f6: z.array(z.any()).nullable(),
227227
}),
228-
})
229-
const sql = generateCollectionTableDefinition('content', collection)
228+
}))!
229+
const sql = generateCollectionTableDefinition(collection)
230230

231231
expect(sql).toBe([
232232
`CREATE TABLE IF NOT EXISTS ${getTableName('content')} (`,

0 commit comments

Comments
 (0)