Skip to content

Commit acfbe96

Browse files
committed
feat: multi source collection
1 parent c96fd9d commit acfbe96

File tree

8 files changed

+97
-77
lines changed

8 files changed

+97
-77
lines changed

src/module.ts

+45-44
Original file line numberDiff line numberDiff line change
@@ -274,53 +274,54 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
274274
if (!collection.source) {
275275
continue
276276
}
277+
for await (const source of collection.source) {
278+
if (source.prepare) {
279+
await source.prepare(nuxt)
280+
}
277281

278-
if (collection.source.prepare) {
279-
await collection.source.prepare(nuxt)
280-
}
281-
282-
const { fixed } = parseSourceBase(collection.source)
283-
const cwd = collection.source.cwd
284-
const _keys = await fastGlob(collection.source.include, { cwd, ignore: collection.source!.exclude || [], dot: true })
285-
.catch(() => [])
286-
287-
filesCount += _keys.length
288-
289-
const list: Array<Array<string>> = []
290-
for await (const chunk of chunks(_keys, 25)) {
291-
await Promise.all(chunk.map(async (key) => {
292-
key = key.substring(fixed.length)
293-
const keyInCollection = join(collection.name, collection.source?.prefix || '', key)
294-
295-
const content = await readFile(join(cwd, fixed, key), 'utf8')
296-
const checksum = getContentChecksum(configHash + collectionHash + content)
297-
const cache = databaseContents[keyInCollection]
298-
299-
let parsedContent
300-
if (cache && cache.checksum === checksum) {
301-
cachedFilesCount += 1
302-
parsedContent = JSON.parse(cache.parsedContent)
303-
}
304-
else {
305-
parsedFilesCount += 1
306-
parsedContent = await parseContent(keyInCollection, content, collection, nuxt)
307-
db.insertDevelopmentCache(keyInCollection, checksum, JSON.stringify(parsedContent))
308-
}
309-
310-
list.push([key, generateCollectionInsert(collection, parsedContent)])
311-
}))
312-
}
313-
// Sort by file name to ensure consistent order
314-
list.sort((a, b) => String(a[0]).localeCompare(String(b[0])))
315-
collectionDump[collection.name]!.push(...list.map(([, sql]) => sql!))
282+
const { fixed } = parseSourceBase(source)
283+
const cwd = source.cwd
284+
const _keys = await fastGlob(source.include, { cwd, ignore: source!.exclude || [], dot: true })
285+
.catch(() => [])
286+
287+
filesCount += _keys.length
288+
289+
const list: Array<Array<string>> = []
290+
for await (const chunk of chunks(_keys, 25)) {
291+
await Promise.all(chunk.map(async (key) => {
292+
key = key.substring(fixed.length)
293+
const keyInCollection = join(collection.name, source?.prefix || '', key)
294+
295+
const content = await readFile(join(cwd, fixed, key), 'utf8')
296+
const checksum = getContentChecksum(configHash + collectionHash + content)
297+
const cache = databaseContents[keyInCollection]
298+
299+
let parsedContent
300+
if (cache && cache.checksum === checksum) {
301+
cachedFilesCount += 1
302+
parsedContent = JSON.parse(cache.parsedContent)
303+
}
304+
else {
305+
parsedFilesCount += 1
306+
parsedContent = await parseContent(keyInCollection, content, collection, nuxt)
307+
db.insertDevelopmentCache(keyInCollection, checksum, JSON.stringify(parsedContent))
308+
}
309+
310+
list.push([key, generateCollectionInsert(collection, parsedContent)])
311+
}))
312+
}
313+
// Sort by file name to ensure consistent order
314+
list.sort((a, b) => String(a[0]).localeCompare(String(b[0])))
315+
collectionDump[collection.name]!.push(...list.map(([, sql]) => sql!))
316316

317-
collectionChecksum[collection.name] = hash(collectionDump[collection.name])
317+
collectionChecksum[collection.name] = hash(collectionDump[collection.name])
318318

319-
collectionDump[collection.name]!.push(
320-
generateCollectionTableDefinition(infoCollection, { drop: false }),
321-
`DELETE FROM ${infoCollection.tableName} WHERE id = 'checksum_${collection.name}'`,
322-
generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, version: collectionChecksum[collection.name] }),
323-
)
319+
collectionDump[collection.name]!.push(
320+
generateCollectionTableDefinition(infoCollection, { drop: false }),
321+
`DELETE FROM ${infoCollection.tableName} WHERE id = 'checksum_${collection.name}'`,
322+
generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, version: collectionChecksum[collection.name] }),
323+
)
324+
}
324325
}
325326

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

src/runtime/internal/schema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ZodRawShape } from 'zod'
22

3-
export function getOrderedSchemaKeys(shape: ZodRawShape) {
3+
export function getOrderedSchemaKeys(shape: ZodRawShape | Record<string, unknown>) {
44
const keys = new Set([
55
shape.id ? 'id' : undefined,
66
shape.title ? 'title' : undefined,

src/types/collection.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,21 @@ export interface ResolvedCollectionSource extends CollectionSource {
2424

2525
export interface PageCollection<T extends ZodRawShape = ZodRawShape> {
2626
type: 'page'
27-
source?: string | CollectionSource
27+
source?: string | CollectionSource | CollectionSource[]
2828
schema?: ZodObject<T>
2929
}
3030

3131
export interface DataCollection<T extends ZodRawShape = ZodRawShape> {
3232
type: 'data'
33-
source?: string | CollectionSource
33+
source?: string | CollectionSource | CollectionSource[]
3434
schema: ZodObject<T>
3535
}
3636

3737
export type Collection<T extends ZodRawShape = ZodRawShape> = PageCollection<T> | DataCollection<T>
3838

3939
export interface DefinedCollection<T extends ZodRawShape = ZodRawShape> {
4040
type: CollectionType
41-
source: ResolvedCollectionSource | undefined
41+
source: ResolvedCollectionSource[] | undefined
4242
schema: ZodObject<T>
4343
extendedSchema: ZodObject<T>
4444
jsonFields: string[]
@@ -48,7 +48,7 @@ export interface ResolvedCollection<T extends ZodRawShape = ZodRawShape> {
4848
name: string
4949
tableName: string
5050
type: CollectionType
51-
source: ResolvedCollectionSource | undefined
51+
source: ResolvedCollectionSource[] | undefined
5252
schema: ZodObject<T>
5353
extendedSchema: ZodObject<T>
5454
jsonFields: string[]
@@ -63,7 +63,7 @@ export interface CollectionInfo {
6363
name: string
6464
pascalName: string
6565
tableName: string
66-
source: CollectionSource
66+
source: CollectionSource[]
6767
type: CollectionType
6868
schema: JsonSchema7Type & {
6969
$schema?: string

src/types/module.ts

+7
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,10 @@ export interface RuntimeConfig {
178178
localDatabase: SqliteDatabaseConfig
179179
}
180180
}
181+
182+
export interface PublicRuntimeConfig {
183+
studio: {
184+
apiURL?: string
185+
iframeMessagingAllowedOrigins?: string
186+
}
187+
}

src/utils/collection.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,28 @@ export function resolveCollections(collections: Record<string, DefinedCollection
7373
/**
7474
* Process collection source and return refined source
7575
*/
76-
function resolveSource(source: string | CollectionSource | undefined): ResolvedCollectionSource | undefined {
76+
function resolveSource(source: string | CollectionSource | CollectionSource[] | undefined): ResolvedCollectionSource[] | undefined {
7777
if (!source) {
7878
return undefined
7979
}
8080

8181
if (typeof source === 'string') {
82-
return defineLocalSource({ include: source })
82+
return [defineLocalSource({ include: source })]
8383
}
8484

85-
if ((source as ResolvedCollectionSource)._resolved) {
86-
return source as ResolvedCollectionSource
87-
}
85+
const sources: CollectionSource[] = Array.isArray(source) ? source : [source]
8886

89-
if (source.repository) {
90-
return defineGitHubSource(source)
91-
}
87+
return sources.map((source) => {
88+
if ((source as ResolvedCollectionSource)._resolved) {
89+
return source as ResolvedCollectionSource
90+
}
9291

93-
return defineLocalSource(source)
92+
if (source.repository) {
93+
return defineGitHubSource(source)
94+
}
95+
96+
return defineLocalSource(source)
97+
})
9498
}
9599

96100
// Convert collection data to SQL insert statement

src/utils/dev.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ export async function watchContents(nuxt: Nuxt, options: ModuleOptions, manifest
2626
const db = localDatabase(options._localDatabase!.filename)
2727
const collections = manifest.collections
2828

29-
const localCollections = collections.filter(c => c.source && !c.source.repository)
29+
const sourceMap = collections.flatMap((c) => {
30+
return c.source
31+
? c.source.filter(s => !s.repository).map(s => ({ collection: c, source: s }))
32+
: []
33+
})
34+
// const localCollections = collections.filter(c => c.source && !c.source.repository)
3035

3136
const watcher = chokidar.watch('.', { ignoreInitial: true, cwd })
3237

@@ -85,13 +90,14 @@ export async function watchContents(nuxt: Nuxt, options: ModuleOptions, manifest
8590
}
8691

8792
async function onChange(path: string) {
88-
const collection = localCollections.find(({ source }) => micromatch.isMatch(path, source!.include, { ignore: source!.exclude || [], dot: true }))
89-
if (collection) {
93+
const match = sourceMap.find(({ source }) => micromatch.isMatch(path, source!.include, { ignore: source!.exclude || [], dot: true }))
94+
if (match) {
95+
const { collection, source } = match
9096
logger.info(`File \`${path}\` changed on \`${collection.name}\` collection`)
91-
const { fixed } = parseSourceBase(collection.source!)
97+
const { fixed } = parseSourceBase(source)
9298

9399
const filePath = path.substring(fixed.length)
94-
const keyInCollection = join(collection.name, collection.source?.prefix || '', filePath)
100+
const keyInCollection = join(collection.name, source?.prefix || '', filePath)
95101

96102
const content = await readFile(join(nuxt.options.rootDir, 'content', path), 'utf8')
97103
const checksum = getContentChecksum(content)
@@ -113,13 +119,14 @@ export async function watchContents(nuxt: Nuxt, options: ModuleOptions, manifest
113119
}
114120

115121
async function onRemove(path: string) {
116-
const collection = localCollections.find(({ source }) => micromatch.isMatch(path, source!.include, { ignore: source!.exclude || [], dot: true }))
117-
if (collection) {
122+
const match = sourceMap.find(({ source }) => micromatch.isMatch(path, source!.include, { ignore: source!.exclude || [], dot: true }))
123+
if (match) {
124+
const { collection, source } = match
118125
logger.info(`File \`${path}\` removed from \`${collection.name}\` collection`)
119-
const { fixed } = parseSourceBase(collection.source!)
126+
const { fixed } = parseSourceBase(source)
120127

121128
const filePath = path.substring(fixed.length)
122-
const keyInCollection = join(collection.name, collection.source?.prefix || '', filePath)
129+
const keyInCollection = join(collection.name, source?.prefix || '', filePath)
123130

124131
await db.deleteDevelopmentCache(keyInCollection)
125132

test/base.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ describe('empty', async () => {
4343
expect(pagesCollection).toBeDefined()
4444
expect(pagesCollection?.type).toBe('page')
4545
expect(pagesCollection?.source).toBeDefined()
46-
expect(pagesCollection?.source?.include).toBe('**/*')
46+
expect(pagesCollection?.source[0]).toBeDefined()
47+
expect(pagesCollection?.source[0].include).toBe('**/*')
4748
})
4849
})
4950

test/unit/defineCollection.test.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ describe('defineCollection', () => {
1717
})
1818
expect(collection).toMatchObject({
1919
type: 'page',
20-
source: {
20+
source: [{
2121
_resolved: true,
2222
include: 'pages/**',
2323
cwd: '',
24-
},
24+
}],
2525
})
2626

2727
expect(collection.schema.shape).not.ownProperty('title')
@@ -61,12 +61,12 @@ describe('defineCollection', () => {
6161

6262
expect(collection).toMatchObject({
6363
type: 'page',
64-
source: {
64+
source: [{
6565
include: 'pages/**',
6666
prefix: 'blog',
6767
exclude: ['pages/blog/index.md'],
6868
cwd: '',
69-
},
69+
}],
7070
})
7171

7272
expect(collection.schema.shape).ownProperty('customField')
@@ -86,11 +86,11 @@ describe('defineCollection', () => {
8686

8787
expect(collection).toMatchObject({
8888
type: 'data',
89-
source: {
89+
source: [{
9090
_resolved: true,
9191
include: 'data/**',
9292
cwd: '',
93-
},
93+
}],
9494
})
9595

9696
expect(collection.schema.shape).toHaveProperty('customField')
@@ -115,12 +115,12 @@ describe('defineCollection', () => {
115115

116116
expect(collection).toMatchObject({
117117
type: 'data',
118-
source: {
118+
source: [{
119119
include: 'data/**',
120120
cwd: '',
121121
prefix: 'blog',
122122
exclude: ['data/blog/index.md'],
123-
},
123+
}],
124124
})
125125
})
126126
})

0 commit comments

Comments
 (0)