Skip to content

Commit b254bcc

Browse files
committed
wip: refactor to handle edge case for collection sources
1 parent b82cf7a commit b254bcc

File tree

10 files changed

+347
-11
lines changed

10 files changed

+347
-11
lines changed

src/app/src/composables/useContext.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import type { useTree } from './useTree'
2020
import type { useGit } from './useGit'
2121
import type { useDraftMedias } from './useDraftMedias'
2222
import { useRoute, useRouter } from 'vue-router'
23-
import { findDescendantsFileItemsFromFsPath, generateIdFromFsPath } from '../utils/tree'
23+
import { findDescendantsFileItemsFromFsPath } from '../utils/tree'
24+
import { generateIdFromFsPath } from '../utils/collection'
2425
import { joinURL } from 'ufo'
2526
import { upperFirst } from 'scule'
2627
import { generateStemFromFsPath } from '../utils/media'

src/app/src/composables/useTree.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { StudioFeature, TreeStatus, type StudioHost, type TreeItem, DraftStatus
22
import { ref, computed } from 'vue'
33
import type { useDraftDocuments } from './useDraftDocuments'
44
import type { useDraftMedias } from './useDraftMedias'
5-
import { buildTree, findItemFromFsPath, findItemFromRoute, findParentFromFsPath, generateIdFromFsPath } from '../utils/tree'
5+
import { buildTree, findItemFromFsPath, findItemFromRoute, findParentFromFsPath } from '../utils/tree'
6+
import { generateIdFromFsPath } from '../utils/collection'
67
import type { RouteLocationNormalized } from 'vue-router'
78
import { useHooks } from './useHooks'
89
import { useStudioState } from './useStudioState'

src/app/src/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { generateContentFromDocument, generateDocumentFromContent, removeReservedKeysFromDocument } from './utils/content'
2+
export { generateIdFromFsPath, parseSourceBase } from './utils/collection'

src/app/src/utils/collection.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { CollectionSource, CollectionInfo } from '@nuxt/content'
2+
import { join } from 'pathe'
3+
4+
export function parseSourceBase(source: CollectionSource) {
5+
const [fixPart, ...rest] = source.include.includes('*') ? source.include.split('*') : ['', source.include]
6+
return {
7+
fixed: fixPart || '',
8+
dynamic: '*' + rest.join('*'),
9+
}
10+
}
11+
12+
export function generateIdFromFsPath(path: string, collectionInfo: CollectionInfo) {
13+
const { fixed } = parseSourceBase(collectionInfo.source[0]!)
14+
15+
const pathWithoutFixed = path.substring(fixed.length)
16+
17+
return join(collectionInfo.name, collectionInfo.source[0]?.prefix || '', pathWithoutFixed)
18+
}

src/app/src/utils/tree.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import type { BaseItem } from '../types/item'
1111
import { isEqual } from './database'
1212
import { studioFlags } from '../composables/useStudio'
1313
import { getFileExtension, parseName } from './file'
14-
import { joinURL } from 'ufo'
1514

1615
export const COLOR_STATUS_MAP: { [key in TreeStatus]?: string } = {
1716
[TreeStatus.Created]: 'green',
@@ -173,10 +172,6 @@ TreeItem[] {
173172
return tree
174173
}
175174

176-
export function generateIdFromFsPath(fsPath: string, collectionName: string): string {
177-
return joinURL(collectionName, fsPath)
178-
}
179-
180175
export function getTreeStatus(modified?: BaseItem, original?: BaseItem): TreeStatus {
181176
if (studioFlags.dev) {
182177
return TreeStatus.Opened

src/app/test/mocks/collection.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { CollectionInfo } from '@nuxt/content'
2+
3+
export const collections: Record<string, CollectionInfo> = {
4+
landing: {
5+
name: 'landing',
6+
pascalName: 'Landing',
7+
tableName: '_content_landing',
8+
source: [
9+
{
10+
_resolved: true,
11+
prefix: '/',
12+
cwd: '/Users/larbish/Documents/nuxt/modules/studio/playground/content',
13+
include: 'index.md',
14+
},
15+
],
16+
type: 'page',
17+
fields: {
18+
id: 'string',
19+
title: 'string',
20+
body: 'json',
21+
description: 'string',
22+
extension: 'string',
23+
meta: 'json',
24+
navigation: 'json',
25+
path: 'string',
26+
seo: 'json',
27+
stem: 'string',
28+
},
29+
schema: {
30+
$schema: 'http://json-schema.org/draft-07/schema#',
31+
$ref: '#/definitions/docs',
32+
definitions: {},
33+
},
34+
tableDefinition: '',
35+
},
36+
docs: {
37+
name: 'docs',
38+
pascalName: 'Docs',
39+
tableName: '_content_docs',
40+
source: [
41+
{
42+
_resolved: true,
43+
prefix: '/',
44+
cwd: '/Users/larbish/Documents/nuxt/modules/studio/playground/content',
45+
include: '**',
46+
exclude: [
47+
'index.md',
48+
],
49+
},
50+
],
51+
type: 'page',
52+
fields: {
53+
id: 'string',
54+
title: 'string',
55+
body: 'json',
56+
description: 'string',
57+
extension: 'string',
58+
links: 'json',
59+
meta: 'json',
60+
navigation: 'json',
61+
path: 'string',
62+
seo: 'json',
63+
stem: 'string',
64+
},
65+
schema: {
66+
$schema: 'http://json-schema.org/draft-07/schema#',
67+
$ref: '#/definitions/__SCHEMA__',
68+
definitions: {},
69+
},
70+
tableDefinition: 'CREATE TABLE IF NOT EXISTS',
71+
},
72+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { generateIdFromFsPath } from '../../../src/utils/collection'
3+
import type { CollectionInfo } from '@nuxt/content'
4+
import { collections } from '../../mocks/collection'
5+
6+
describe('generateIdFromFsPath', () => {
7+
it('should generate id for single file with no prefix', () => {
8+
const path = 'index.md'
9+
const result = generateIdFromFsPath(path, collections.landing!)
10+
expect(result).toBe('landing/index.md')
11+
})
12+
13+
it('should generate id for nested file with global pattern', () => {
14+
const path = '1.getting-started/2.introduction.md'
15+
const result = generateIdFromFsPath(path, collections.docs!)
16+
expect(result).toBe('docs/1.getting-started/2.introduction.md')
17+
})
18+
19+
it('should handle deeply nested paths', () => {
20+
const path = '2.essentials/1.nested/3.components.md'
21+
const result = generateIdFromFsPath(path, collections.docs!)
22+
expect(result).toBe('docs/2.essentials/1.nested/3.components.md')
23+
})
24+
25+
it('should handle collection with custom prefix', () => {
26+
const customCollection: CollectionInfo = {
27+
name: 'docs_en',
28+
pascalName: 'DocsEn',
29+
tableName: '_content_docs_en',
30+
source: [
31+
{
32+
_resolved: true,
33+
prefix: '/en',
34+
cwd: '',
35+
include: 'en/**/*',
36+
exclude: ['en/index.md'],
37+
},
38+
],
39+
type: 'page',
40+
fields: {},
41+
schema: {
42+
$schema: 'http://json-schema.org/draft-07/schema#',
43+
$ref: '#/definitions/docs_en',
44+
definitions: {},
45+
},
46+
tableDefinition: '',
47+
}
48+
49+
const path = 'en/1.getting-started/2.introduction.md'
50+
const result = generateIdFromFsPath(path, customCollection)
51+
expect(result).toBe('docs_en/en/1.getting-started/2.introduction.md')
52+
})
53+
54+
it('should handle empty prefix correctly', () => {
55+
const customCollection: CollectionInfo = {
56+
name: 'pages',
57+
pascalName: 'Pages',
58+
tableName: '_content_pages',
59+
source: [
60+
{
61+
_resolved: true,
62+
prefix: '',
63+
cwd: '',
64+
include: 'content/**/*.md',
65+
},
66+
],
67+
type: 'page',
68+
fields: {},
69+
schema: {
70+
$schema: 'http://json-schema.org/draft-07/schema#',
71+
$ref: '#/definitions/pages',
72+
definitions: {},
73+
},
74+
tableDefinition: '',
75+
}
76+
77+
const path = 'content/about.md'
78+
const result = generateIdFromFsPath(path, customCollection)
79+
expect(result).toBe('pages/about.md')
80+
})
81+
})

src/app/test/unit/utils/tree.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest'
2-
import { buildTree, findParentFromFsPath, findItemFromRoute, findItemFromFsPath, findDescendantsFileItemsFromFsPath, getTreeStatus, generateIdFromFsPath } from '../../../src/utils/tree'
2+
import { buildTree, findParentFromFsPath, findItemFromRoute, findItemFromFsPath, findDescendantsFileItemsFromFsPath, getTreeStatus } from '../../../src/utils/tree'
33
import { tree } from '../../../test/mocks/tree'
44
import type { TreeItem } from '../../../src/types/tree'
55
import { dbItemsList, languagePrefixedDbItemsList, nestedDbItemsList } from '../../../test/mocks/database'
@@ -9,6 +9,7 @@ import { DraftStatus, TreeStatus, TreeRootId } from '../../../src/types'
99
import type { RouteLocationNormalized } from 'vue-router'
1010
import type { DatabaseItem } from '../../../src/types/database'
1111
import { joinURL, withLeadingSlash } from 'ufo'
12+
import { generateIdFromFsPath } from '../../../src/utils/collection'
1213

1314
describe('buildTree of documents with one level of depth', () => {
1415
// Result based on dbItemsList mock

src/module/src/runtime/host.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { ref } from 'vue'
22
import { ensure } from './utils/ensure'
33
import type { CollectionItemBase, CollectionSource, DatabaseAdapter } from '@nuxt/content'
44
import type { ContentDatabaseAdapter } from '../types/content'
5-
import { getCollectionByFilePath, generateIdFromFsPath, createCollectionDocument, generateRecordDeletion, generateRecordInsert, getCollectionInfo, normalizeDocument } from './utils/collection'
5+
import { getCollectionByFilePath, createCollectionDocument, generateRecordDeletion, generateRecordInsert, getCollectionInfo, normalizeDocument } from './utils/collection'
66
import { kebabCase } from 'scule'
77
import type { StudioHost, StudioUser, DatabaseItem, MediaItem, Repository } from 'nuxt-studio/app'
88
import type { RouteLocationNormalized, Router } from 'vue-router'
9-
import { generateDocumentFromContent } from 'nuxt-studio/app/utils'
9+
import { generateDocumentFromContent, generateIdFromFsPath } from 'nuxt-studio/app/utils'
1010
// @ts-expect-error queryCollection is not defined in .nuxt/imports.d.ts
1111
import { clearError, getAppManifest, queryCollection, queryCollectionItemSurroundings, queryCollectionNavigation, queryCollectionSearchSections } from '#imports'
1212
import { collections } from '#content/preview'

0 commit comments

Comments
 (0)