Skip to content

Commit 893252b

Browse files
committed
!refactor: update tables name and schema and better checksum check
1 parent 06ba18d commit 893252b

23 files changed

+101
-85
lines changed

docs/content/docs/2.usage/1.collections.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const { data: posts } = await useAsyncData('blog', () => queryCollection('blog')
9292
<div>
9393
<h1>Blog</h1>
9494
<ul>
95-
<li v-for="post in posts" :key="post._id">
95+
<li v-for="post in posts" :key="post.id">
9696
<NuxtLink :to="post.path">{{ post.title }}</NuxtLink>
9797
</li>
9898
</ul>
@@ -138,7 +138,7 @@ type CollectionSource = {
138138
139139
Every collection includes these default fields:
140140
141-
- `_id`: Unique content identifier
141+
- `id`: Unique content identifier
142142
- `stem`: File path without extension (used for sorting and location)
143143
- `extension`: File extension
144144
- `meta`: Custom fields not defined in the collection schema

examples/blog/app/pages/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts" setup>
22
const { data: posts } = await useAsyncData(() => {
33
return queryCollection('blog')
4-
.select('title', 'description', 'path', '_id', 'date')
4+
.select('title', 'description', 'path', 'id', 'date')
55
.order('date', 'DESC')
66
.all()
77
})
@@ -12,7 +12,7 @@ const { data: posts } = await useAsyncData(() => {
1212
<h1>Blog</h1>
1313
<p
1414
v-for="post in posts"
15-
:key="post._id"
15+
:key="post.id"
1616
>
1717
>
1818
<nuxt-link :to="post.path">

src/module.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,18 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
213213
contentBuild: options.build?.markdown,
214214
})
215215

216-
const infoCollection = collections.find(c => c.name === '_info')!
216+
const infoCollection = collections.find(c => c.name === 'info')!
217217

218218
const startTime = performance.now()
219219
let filesCount = 0
220220
let cachedFilesCount = 0
221221
let parsedFilesCount = 0
222222
// Create database dump
223223
for await (const collection of collections) {
224-
if (collection.name === '_info') {
224+
if (collection.name === 'info') {
225225
continue
226226
}
227+
const collectionHash = hash(collection)
227228
collectionDump[collection.name] = []
228229
// Collection table definition
229230
collectionDump[collection.name]!.push(
@@ -249,7 +250,7 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
249250
await Promise.all(chunk.map(async (key) => {
250251
const keyInCollection = join(collection.name, collection.source?.prefix || '', key)
251252
const content = await readFile(join(cwd, key), 'utf8')
252-
const checksum = getContentChecksum(configHash + content)
253+
const checksum = getContentChecksum(configHash + collectionHash + content)
253254
const cache = databaseContents[keyInCollection]
254255

255256
let parsedContent
@@ -274,14 +275,14 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
274275

275276
collectionDump[collection.name]!.push(
276277
generateCollectionTableDefinition(infoCollection, { drop: false }),
277-
`DELETE FROM ${infoCollection.tableName} WHERE _id = 'checksum_${collection.name}'`,
278-
generateCollectionInsert(infoCollection, { _id: `checksum_${collection.name}`, version: collectionChecksum[collection.name] }),
278+
`DELETE FROM ${infoCollection.tableName} WHERE id = 'checksum_${collection.name}'`,
279+
generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, value: collectionChecksum[collection.name] }),
279280
)
280281
}
281282

282283
const sqlDumpList = Object.values(collectionDump).flatMap(a => a)
283284

284-
// Drop _info table and recreate it
285+
// Drop info table and recreate it
285286
db.exec(`DROP TABLE IF EXISTS ${infoCollection.tableName}`)
286287
for (const sql of sqlDumpList) {
287288
db.exec(sql)

src/runtime/components/ContentRenderer.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,6 @@ function findMappedTag(node: MDCElement, tags: Record<string, string>) {
214214
:prose="props.prose"
215215
:unwrap="props.unwrap"
216216
:components="componentsMap"
217-
:data-content-id="debug ? value._id : undefined"
217+
:data-content-id="debug ? value.id : undefined"
218218
/>
219219
</template>

src/runtime/internal/collection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ function findJsonFields(sql: string): string[] {
2929
}
3030

3131
function getCollectionName(table: string) {
32-
return table.replace(/^content_/, '')
32+
return table.replace(/^_content_/, '')
3333
}

src/runtime/internal/database.client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async function loadAdapter<T>(collection: T) {
5050
const dumpId = `collection_${collection}`
5151
let checksumState = 'matched'
5252
try {
53-
const dbChecksum = db.exec({ sql: `SELECT * FROM ${tables._info} where _id = '${checksumId}'`, rowMode: 'object', returnValue: 'resultRows' })
53+
const dbChecksum = db.exec({ sql: `SELECT * FROM ${tables.info} where id = '${checksumId}'`, rowMode: 'object', returnValue: 'resultRows' })
5454
.shift()
5555

5656
if (dbChecksum?.version !== checksums[String(collection)]) {
@@ -90,7 +90,7 @@ async function loadAdapter<T>(collection: T) {
9090

9191
await db.exec({ sql: `DROP TABLE IF EXISTS ${tables[String(collection)]}` })
9292
if (checksumState === 'mismatch') {
93-
await db.exec({ sql: `DELETE FROM ${tables._info} WHERE _id = '${checksumId}'` })
93+
await db.exec({ sql: `DELETE FROM ${tables.info} WHERE id = '${checksumId}'` })
9494
}
9595

9696
for (const command of dump) {

src/runtime/internal/database.server.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,35 @@ export default function loadDatabaseAdapter(config: RuntimeConfig) {
3939
}
4040

4141
const checkDatabaseIntegrity = {} as Record<string, boolean>
42-
let integrityCheckPromise: Promise<void> | null = null
42+
const integrityCheckPromise = {} as Record<string, Promise<void> | null>
4343
export async function checkAndImportDatabaseIntegrity(event: H3Event, collection: string, config: RuntimeConfig): Promise<void> {
4444
if (checkDatabaseIntegrity[String(collection)] !== false) {
4545
checkDatabaseIntegrity[String(collection)] = false
46-
integrityCheckPromise = integrityCheckPromise || _checkAndImportDatabaseIntegrity(event, collection, checksums[String(collection)], config)
46+
integrityCheckPromise[String(collection)] = integrityCheckPromise[String(collection)] || _checkAndImportDatabaseIntegrity(event, collection, checksums[String(collection)], config)
4747
.then((isValid) => { checkDatabaseIntegrity[String(collection)] = !isValid })
4848
.catch((error) => {
4949
console.log('Database integrity check failed', error)
5050
checkDatabaseIntegrity[String(collection)] = true
51-
integrityCheckPromise = null
51+
integrityCheckPromise[String(collection)] = null
5252
})
5353
}
5454

55-
if (integrityCheckPromise) {
56-
await integrityCheckPromise
55+
if (integrityCheckPromise[String(collection)]) {
56+
await integrityCheckPromise[String(collection)]
5757
}
5858
}
5959

6060
async function _checkAndImportDatabaseIntegrity(event: H3Event, collection: string, integrityVersion: string, config: RuntimeConfig) {
6161
const db = await loadDatabaseAdapter(config)
6262

63-
const before = await db.first<{ version: string }>(`select * from ${tables._info}`).catch(() => ({ version: '' }))
63+
const before = await db.first<{ version: string }>(`select * from ${tables.info} where id = 'checksum_${collection}'`).catch(() => ({ version: '' }))
64+
6465
if (before?.version) {
6566
if (before?.version === integrityVersion) {
6667
return true
6768
}
6869
// Delete old version
69-
await db.exec(`DELETE FROM ${tables._info} WHERE version = '${before.version}'`)
70+
await db.exec(`DELETE FROM ${tables.info} WHERE id = 'checksum_${collection}'`)
7071
}
7172

7273
const dump = await loadDatabaseDump(event, collection).then(decompressSQLDump)
@@ -80,7 +81,7 @@ async function _checkAndImportDatabaseIntegrity(event: H3Event, collection: stri
8081
})
8182
}, Promise.resolve())
8283

83-
const after = await db.first<{ version: string }>(`select * from ${tables._info}`).catch(() => ({ version: '' }))
84+
const after = await db.first<{ version: string }>(`SELECT * FROM ${tables.info} WHERE id = 'checksum_${collection}'`).catch(() => ({ version: '' }))
8485
return after?.version === integrityVersion
8586
}
8687

src/types/collection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export interface CollectionInfo {
7070
}
7171

7272
export interface CollectionItemBase {
73-
_id: string
73+
id: string
7474
stem: string
7575
extension: string
7676
meta: Record<string, unknown>

src/utils/collection.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { logger } from './dev'
88

99
const JSON_FIELDS_TYPES = ['ZodObject', 'ZodArray', 'ZodRecord', 'ZodIntersection', 'ZodUnion', 'ZodAny']
1010

11-
function getTableName(name: string) {
12-
return `content_${name}`
11+
export function getTableName(name: string) {
12+
return `_content_${name}`
1313
}
1414

1515
export function defineCollection<T extends ZodRawShape>(collection: Collection<T>): DefinedCollection {
@@ -45,17 +45,24 @@ export function resolveCollection(name: string, collection: DefinedCollection):
4545
name,
4646
type: collection.type || 'page',
4747
tableName: getTableName(name),
48-
private: name === '_info',
48+
private: name === 'info',
4949
}
5050
}
5151

5252
export function resolveCollections(collections: Record<string, DefinedCollection>): ResolvedCollection[] {
53-
collections._info = defineCollection({
53+
collections.info = {
5454
type: 'data',
55+
source: undefined,
5556
schema: z.object({
56-
version: z.string(),
57+
id: z.string(),
58+
value: z.string(),
5759
}),
58-
})
60+
extendedSchema: z.object({
61+
id: z.string(),
62+
value: z.string(),
63+
}),
64+
jsonFields: [],
65+
}
5966

6067
return Object.entries(collections)
6168
.map(([name, collection]) => resolveCollection(name, collection))
@@ -89,7 +96,7 @@ function resolveSource(source: string | CollectionSource | undefined): ResolvedC
8996
export function generateCollectionInsert(collection: ResolvedCollection, data: Record<string, unknown>) {
9097
const fields: string[] = []
9198
const values: Array<string | number | boolean> = []
92-
const sortedKeys = Object.keys((collection.extendedSchema).shape).sort()
99+
const sortedKeys = getOrderedColumns((collection.extendedSchema).shape)
93100

94101
sortedKeys.forEach((key) => {
95102
const value = (collection.extendedSchema).shape[key]
@@ -128,12 +135,12 @@ export function generateCollectionInsert(collection: ResolvedCollection, data: R
128135

129136
// Convert a collection with Zod schema to SQL table definition
130137
export function generateCollectionTableDefinition(collection: ResolvedCollection, opts: { drop?: boolean } = {}) {
131-
const sortedKeys = Object.keys((collection.extendedSchema).shape).sort()
138+
const sortedKeys = getOrderedColumns((collection.extendedSchema).shape)
132139
const sqlFields = sortedKeys.map((key) => {
133140
const type = (collection.extendedSchema).shape[key]!
134141
const underlyingType = getUnderlyingType(type)
135142

136-
if (key === '_id') return `${key} TEXT PRIMARY KEY`
143+
if (key === 'id') return `${key} TEXT PRIMARY KEY`
137144

138145
let sqlType: string = ZodToSqlFieldTypes[underlyingType.constructor.name as ZodFieldType]
139146

@@ -180,3 +187,13 @@ export function generateCollectionTableDefinition(collection: ResolvedCollection
180187

181188
return definition
182189
}
190+
191+
function getOrderedColumns(shape: ZodRawShape) {
192+
const keys = new Set([
193+
shape.id ? 'id' : undefined,
194+
shape.title ? 'title' : undefined,
195+
...Object.keys(shape).sort(),
196+
].filter(Boolean))
197+
198+
return Array.from(keys) as string[]
199+
}

src/utils/content/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ export async function parseContent(key: string, content: string, collection: Res
8888
},
8989
})
9090

91-
const { id: _id, ...parsedContentFields } = parsedContent
92-
const result = { _id } as typeof collection.extendedSchema._type
91+
const { id: id, ...parsedContentFields } = parsedContent
92+
const result = { id } as typeof collection.extendedSchema._type
9393
const meta = {} as Record<string, unknown>
9494

9595
const collectionKeys = Object.keys(collection.extendedSchema.shape)

0 commit comments

Comments
 (0)