Skip to content

Commit 53a86ae

Browse files
committed
refactor: improve collection source management
1 parent 79629dc commit 53a86ae

18 files changed

+78
-45
lines changed

src/presets/cloudflare-pages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default definePreset({
2929
nitroConfig.publicAssets.push({ dir: join(nitroConfig.buildDir!, 'content', 'raw'), maxAge: 60 })
3030
nitroConfig.handlers.push({
3131
route: '/api/content/:collection/database.sql',
32-
handler: resolver.resolve('./runtime/presets/cloudflare-pages/database.sql'),
32+
handler: resolver.resolve('./runtime/presets/cloudflare-pages/database-handler'),
3333
})
3434
},
3535

src/presets/node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default definePreset({
1111
nitroConfig.alias['#content/dump'] = addTemplate(fullDatabaseCompressedDumpTemplate(manifest)).dst
1212
nitroConfig.handlers.push({
1313
route: '/api/content/:collection/database.sql',
14-
handler: resolver.resolve('./runtime/presets/node/database.sql'),
14+
handler: resolver.resolve('./runtime/presets/node/database-handler'),
1515
})
1616
},
1717
})

src/types/collection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type CollectionSource = {
1717
}
1818

1919
export interface ResolvedCollectionSource extends CollectionSource {
20+
_resolved: true
2021
prepare?: (nuxt: Nuxt) => Promise<void>
2122
cwd: string
2223
}
@@ -37,7 +38,7 @@ export type Collection<T extends ZodRawShape = ZodRawShape> = PageCollection<T>
3738

3839
export interface DefinedCollection<T extends ZodRawShape = ZodRawShape> {
3940
type: CollectionType
40-
source: CollectionSource | string | undefined
41+
source: ResolvedCollectionSource | undefined
4142
schema: ZodObject<T>
4243
extendedSchema: ZodObject<T>
4344
}

src/utils/collection.ts

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
import { join } from 'node:path'
21
import { pascalCase } from 'scule'
32
import type { ZodObject, ZodOptionalDef, ZodRawShape, ZodStringDef, ZodType } from 'zod'
43
import type { Collection, ResolvedCollection, CollectionSource, DefinedCollection, ResolvedCollectionSource } from '../types/collection'
4+
import { defineLocalSource, defineGitHubSource } from './source'
55
import { metaSchema, pageSchema } from './schema'
66
import type { ZodFieldType } from './zod'
77
import { getUnderlyingType, ZodToSqlFieldTypes, z } from './zod'
8-
import { downloadRepository, parseGitHubUrl } from './git'
98
import { logger } from './dev'
109

11-
interface ResolveOptions {
12-
rootDir: string
13-
}
14-
1510
const JSON_FIELDS_TYPES = ['ZodObject', 'ZodArray', 'ZodRecord', 'ZodIntersection', 'ZodUnion', 'ZodAny']
1611

1712
const getTableName = (name: string) => `content_${name}`
@@ -26,13 +21,13 @@ export function defineCollection<T extends ZodRawShape>(collection: Collection<T
2621

2722
return {
2823
type: collection.type,
29-
source: collection.source,
24+
source: resolveSource(collection.source),
3025
schema: collection.schema || z.object({}),
3126
extendedSchema: schema,
3227
}
3328
}
3429

35-
export function resolveCollection(name: string, collection: DefinedCollection, opts: ResolveOptions): ResolvedCollection | undefined {
30+
export function resolveCollection(name: string, collection: DefinedCollection): ResolvedCollection | undefined {
3631
if (/^[a-z_]\w*$/i.test(name) === false) {
3732
logger.warn([
3833
`Collection name "${name}" is invalid. Collection names must be valid JavaScript identifiers. This collection will be ignored.`,
@@ -46,7 +41,7 @@ export function resolveCollection(name: string, collection: DefinedCollection, o
4641
name,
4742
type: collection.type || 'page',
4843
pascalName: pascalCase(name),
49-
source: resolveSource(collection.source, opts),
44+
source: collection.source,
5045
tableName: getTableName(name),
5146
tableDefinition: generateCollectionTableDefinition(name, collection, { drop: true }),
5247
generatedFields: {
@@ -62,7 +57,7 @@ export function resolveCollection(name: string, collection: DefinedCollection, o
6257
}
6358
}
6459

65-
export function resolveCollections(collections: Record<string, DefinedCollection>, opts: ResolveOptions): ResolvedCollection[] {
60+
export function resolveCollections(collections: Record<string, DefinedCollection>): ResolvedCollection[] {
6661
collections._info = defineCollection({
6762
type: 'data',
6863
schema: z.object({
@@ -71,39 +66,31 @@ export function resolveCollections(collections: Record<string, DefinedCollection
7166
})
7267

7368
return Object.entries(collections)
74-
.map(([name, collection]) => resolveCollection(name, collection, opts))
69+
.map(([name, collection]) => resolveCollection(name, collection))
7570
.filter(Boolean) as ResolvedCollection[]
7671
}
7772

7873
/**
7974
* Process collection source and return refined source
8075
*/
81-
function resolveSource(source: string | CollectionSource | undefined, opts: ResolveOptions): ResolvedCollectionSource | undefined {
76+
function resolveSource(source: string | CollectionSource | undefined): ResolvedCollectionSource | undefined {
8277
if (!source) {
8378
return undefined
8479
}
8580

86-
const result: ResolvedCollectionSource = {
87-
cwd: '',
88-
...(typeof source === 'string' ? { path: source } : source),
81+
if (typeof source === 'string') {
82+
return defineLocalSource({ path: source })
8983
}
9084

91-
const repository = result?.repository && parseGitHubUrl(result.repository!)
92-
if (repository) {
93-
const { org, repo, branch } = repository
94-
result.cwd = join(opts.rootDir, '.data', 'content', `github-${org}-${repo}-${branch}`)
95-
96-
result.prepare = async () => {
97-
await downloadRepository(
98-
`https://github.com/${org}/${repo}/archive/refs/heads/${branch}.tar.gz`,
99-
result.cwd!,
100-
)
101-
}
85+
if ((source as ResolvedCollectionSource)._resolved) {
86+
return source as ResolvedCollectionSource
10287
}
10388

104-
result.cwd = result.cwd || join(opts.rootDir, 'content')
89+
if (source.repository) {
90+
return defineGitHubSource(source)
91+
}
10592

106-
return result
93+
return defineLocalSource(source)
10794
}
10895

10996
export function parseSourceBase(source: CollectionSource) {

src/utils/config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ const defaultConfig = {
1414
export async function loadContentConfig(rootDir: string, opts: { defaultFallback?: boolean } = {}) {
1515
const { config, configFile } = await loadConfig({ name: 'content', cwd: rootDir })
1616

17-
if (!configFile && opts?.defaultFallback) {
17+
if ((!configFile || configFile === 'content.config') && opts.defaultFallback) {
1818
logger.warn('`content.config.ts` is not found, falling back to default collection. In order to have full control over your collections, create the config file in project root. See: https://content.nuxt.com/getting-started/installation')
1919
return {
20-
collections: resolveCollections(defaultConfig.collections, { rootDir }),
20+
collections: resolveCollections(defaultConfig.collections),
2121
}
2222
}
2323

2424
return {
25-
collections: resolveCollections(config.collections || {}, { rootDir }),
25+
collections: resolveCollections(config.collections || {}),
2626
}
2727
}

src/utils/source.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { join } from 'pathe'
2+
import type { CollectionSource, ResolvedCollectionSource } from '../types/collection'
3+
import { downloadRepository, parseGitHubUrl } from './git'
4+
5+
export function defineLocalSource(source: CollectionSource): ResolvedCollectionSource {
6+
const resolvedSource: ResolvedCollectionSource = {
7+
_resolved: true,
8+
...source,
9+
cwd: '',
10+
prepare: async (nuxt) => {
11+
resolvedSource.cwd = source.cwd || join(nuxt.options.rootDir, 'content')
12+
},
13+
}
14+
return resolvedSource
15+
}
16+
17+
export function defineGitHubSource(source: CollectionSource): ResolvedCollectionSource {
18+
const resolvedSource: ResolvedCollectionSource = {
19+
_resolved: true,
20+
...source,
21+
cwd: '',
22+
prepare: async (nuxt) => {
23+
const repository = source?.repository && parseGitHubUrl(source.repository!)
24+
if (repository) {
25+
const { org, repo, branch } = repository
26+
resolvedSource.cwd = join(nuxt.options.rootDir, '.data', 'content', `github-${org}-${repo}-${branch}`)
27+
28+
await downloadRepository(
29+
`https://github.com/${org}/${repo}/archive/refs/heads/${branch}.tar.gz`,
30+
resolvedSource.cwd!,
31+
)
32+
}
33+
},
34+
}
35+
return resolvedSource
36+
}

test/base.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe('empty', async () => {
3333
test('Default collection is defined', async () => {
3434
const rootDir = fileURLToPath(new URL('./fixtures/empty', import.meta.url))
3535
const config = await loadContentConfig(rootDir, { defaultFallback: true })
36+
console.log(config.collections)
3637

3738
// Pages collection + _info collection
3839
expect(config.collections.length).toBe(2)

test/unit/defineCollection.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ describe('defineCollection', () => {
1717
})
1818
expect(collection).toMatchObject({
1919
type: 'page',
20-
source: 'pages/**',
20+
source: {
21+
_resolved: true,
22+
cwd: '',
23+
path: 'pages/**',
24+
},
2125
})
2226

2327
expect(collection.schema.shape).not.ownProperty('title')
@@ -81,7 +85,11 @@ describe('defineCollection', () => {
8185

8286
expect(collection).toMatchObject({
8387
type: 'data',
84-
source: 'data/**',
88+
source: {
89+
_resolved: true,
90+
cwd: '',
91+
path: 'data/**',
92+
},
8593
})
8694

8795
expect(collection.schema.shape).toHaveProperty('customField')

test/unit/generateCollectionInsert.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('generateCollectionInsert', () => {
1414
otherField2: z.boolean().default(true),
1515
date: z.date().default(new Date('2022-01-01')),
1616
}),
17-
}), { rootDir: '~' })!
17+
}))!
1818
const sql = generateCollectionInsert(collection, {
1919
_id: 'foo.md',
2020
stem: 'foo',
@@ -39,7 +39,7 @@ describe('generateCollectionInsert', () => {
3939
otherField2: z.boolean().default(true),
4040
date: z.date().default(new Date('2022-01-01')),
4141
}),
42-
}), { rootDir: '~' })!
42+
}))!
4343
const sql = generateCollectionInsert(collection, {
4444
_id: 'foo.md',
4545
stem: 'foo',

test/unit/parseContent.csv.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ describe('Parser (.csv)', () => {
7272
schema: z.object({
7373
body: z.any(),
7474
}),
75-
}), { rootDir: '~' })!
75+
}))!
7676
for (const csv of csvs) {
7777
test(`${csv.replace(/\n/g, '-')}`, async () => {
7878
const parsed = await parseContent('content/index.csv', csv, collection)

test/unit/parseContent.json.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('Parser (json)', () => {
2020
schema: z.object({
2121
body: z.any(),
2222
}),
23-
}), { rootDir: '~' })!
23+
}))!
2424
test('key:value', async () => {
2525
const parsed = await parseContent('content/index.json', json, collection)
2626

test/unit/parseContent.md-highlighter.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Highlighter', () => {
2828
source: 'content/**',
2929
schema: z.object({
3030
}),
31-
}), { rootDir: '~' })!
31+
}))!
3232

3333
test('themed', async () => {
3434
const parsed = await parseContent('content/index.md', [

test/unit/parseContent.md.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('Parser (.md)', () => {
2222
source: 'content/**',
2323
schema: z.object({
2424
}),
25-
}), { rootDir: '~' })!
25+
}))!
2626

2727
test('Index file', async () => {
2828
const parsed = await parseContent('content/index.md', '# Index', collection, nuxtMock)

test/unit/parseContent.path-meta.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ describe('Transformer (path-meta)', () => {
7979
source: 'content/**',
8080
schema: z.object({
8181
}),
82-
}), { rootDir: '~' })!
82+
}))!
8383

8484
Object.entries(testCases).forEach(([id, expected]) => {
8585
test(id, async () => {

test/unit/parseContent.yaml.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('Parser (.yml)', () => {
1111
schema: z.object({
1212
body: z.any(),
1313
}),
14-
}), { rootDir: '~' })!
14+
}))!
1515
test('key:value', async () => {
1616
const parsed = await parseContent('content/index.yml', 'key: value', collection)
1717

test/unit/resolveCollection.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ describe('resolveCollection', () => {
77
type: 'page',
88
source: 'pages/**',
99
})
10-
const resolvedCollection = resolveCollection('invalid-name', collection, { rootDir: '' })
10+
const resolvedCollection = resolveCollection('invalid-name', collection)
1111
expect(resolvedCollection).toBeUndefined()
1212
})
1313
})

0 commit comments

Comments
 (0)