From 5ec049d3eae339d53a1e070d2352ed8535d905c0 Mon Sep 17 00:00:00 2001 From: Farnabaz Date: Tue, 18 Oct 2022 18:03:28 +0200 Subject: [PATCH 1/2] fix(`queryContent`): use path argument as prefix if there is another condition (#1612) --- src/runtime/composables/query.ts | 4 +- test/features/content-query.ts | 43 +++++++++++++++++-- .../content/_partial/mdc-props-inline.md | 2 + .../basic/content/_partial/mdc-props.md | 2 + .../basic/pages/features/query-content.vue | 20 ++++++--- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/runtime/composables/query.ts b/src/runtime/composables/query.ts index fe6198193..5b28cdf94 100644 --- a/src/runtime/composables/query.ts +++ b/src/runtime/composables/query.ts @@ -11,7 +11,9 @@ import { addPrerenderPath, shouldUseClientDB, withContentBase } from './utils' */ export const createQueryFetch = (path?: string) => async (query: QueryBuilder) => { if (path) { - if (query.params().first) { + if (query.params().first && (query.params().where || []).length === 0) { + // If query contains `path` and does not contain any `where` condition + // Then can use `path` as `where` condition to find exact match query.where({ _path: withoutTrailingSlash(path) }) } else { query.where({ _path: new RegExp(`^${path.replace(/[-[\]{}()*+.,^$\s/]/g, '\\$&')}`) }) diff --git a/test/features/content-query.ts b/test/features/content-query.ts index ef0d0dc3c..d81383ed5 100644 --- a/test/features/content-query.ts +++ b/test/features/content-query.ts @@ -11,33 +11,68 @@ export const testContentQuery = () => { }) test('exact match foo not found', async () => { - const content = await $fetch('/features/query-content?path=/foo&findOne=1') + const content = await $fetch('/features/query-content?path=/prefix/foo&findOne=1') // empty expect(content).includes('$$$$') }) + // test `queryContent( PREFIX ).findOne()` test('exact match foo/bar found', async () => { - const content = await $fetch('/features/query-content?path=/foo/bar&findOne=1') + const content = await $fetch('/features/query-content?path=/prefix/foo/bar&findOne=1') // empty expect(content).includes('prefix:foo:bar.md$$') }) + // test `queryContent( PREFIX ).find()` test('prefix queries', async () => { - const content = await $fetch('/features/query-content?path=/foo') + const content = await $fetch('/features/query-content?path=/prefix/foo') expect(content).includes('prefix:foo:bar.md') expect(content).includes('prefix:foo:baz.md') expect(content).includes('prefix:foobarbaz.md') }) + // test `queryContent( PREFIX ).find()` with trailing slash test('directory listing', async () => { - const content = await $fetch('/features/query-content?path=/foo/') + const content = await $fetch('/features/query-content?path=/prefix/foo/') expect(content).includes('prefix:foo:bar.md') expect(content).includes('prefix:foo:baz.md') expect(content).not.includes('prefix:foobarbaz.md') }) + + // test `queryContent( PREFIX ).where( CONDITION ).find()` + test('list contents with tag', async () => { + const content = await $fetch('/features/query-content?where={"tags": { "$contains": "mdc" } }') + + expect(content).includes(':mdc-props-inline.md') + expect(content).includes(':mdc-props.md') + }) + + // test `queryContent( PREFIX ).where( CONDITION ).findOne()` + test('find contents with tag', async () => { + const content = await $fetch('/features/query-content?where={"tags": { "$contains": "mdc" } }&findOne=1') + + expect(content).includes(':mdc-props-inline.md') + expect(content).not.includes(':mdc-props.md') + }) + + // test `queryContent().where( CONDITION ).find()` + test('find contents with tag', async () => { + const content = await $fetch('/features/query-content?prefix=&where={"tags": { "$contains": "mdc" } }') + + expect(content).includes(':mdc-props-inline.md') + expect(content).includes(':mdc-props.md') + }) + + // test `queryContent().where( CONDITION ).findOne()` + test('find contents with tag', async () => { + const content = await $fetch('/features/query-content?prefix=&where={"tags": { "$contains": "mdc" } }&findOne=1') + + expect(content).includes(':mdc-props-inline.md') + expect(content).not.includes(':mdc-props.md') + }) }) } diff --git a/test/fixtures/basic/content/_partial/mdc-props-inline.md b/test/fixtures/basic/content/_partial/mdc-props-inline.md index a4aba1b33..c26aeb023 100644 --- a/test/fixtures/basic/content/_partial/mdc-props-inline.md +++ b/test/fixtures/basic/content/_partial/mdc-props-inline.md @@ -1,4 +1,6 @@ --- +tags: +- mdc categories: - Art - History diff --git a/test/fixtures/basic/content/_partial/mdc-props.md b/test/fixtures/basic/content/_partial/mdc-props.md index 5bf80fa16..4d1af97e2 100644 --- a/test/fixtures/basic/content/_partial/mdc-props.md +++ b/test/fixtures/basic/content/_partial/mdc-props.md @@ -1,4 +1,6 @@ --- +tags: +- mdc categories: - Art - History diff --git a/test/fixtures/basic/pages/features/query-content.vue b/test/fixtures/basic/pages/features/query-content.vue index 3b937d13f..b985bdcf3 100644 --- a/test/fixtures/basic/pages/features/query-content.vue +++ b/test/fixtures/basic/pages/features/query-content.vue @@ -6,15 +6,21 @@ From 98271a74d976448cf437996f4b12f206935e0499 Mon Sep 17 00:00:00 2001 From: Farnabaz Date: Tue, 18 Oct 2022 18:03:51 +0200 Subject: [PATCH 2/2] feat(parser): introduce `_dir` field in contents (#1613) --- src/runtime/transformers/path-meta.ts | 9 ++++--- test/features/transformer-path-meta.ts | 33 ++++++++++++++++++------- test/fixtures/basic/server/api/parse.ts | 6 ++--- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/runtime/transformers/path-meta.ts b/src/runtime/transformers/path-meta.ts index a01baa0dd..ae4b39e50 100644 --- a/src/runtime/transformers/path-meta.ts +++ b/src/runtime/transformers/path-meta.ts @@ -32,12 +32,13 @@ export default defineTransformer({ // Check first part for locale name const _locale = locales.includes(parts[0]) ? parts.shift() : defaultLocale - const filePath = parts.join('/') + const filePath = generatePath(parts.join('/')) return { - _path: generatePath(filePath), - _draft: isDraft(filePath), - _partial: isPartial(filePath), + _path: filePath, + _dir: filePath.split('/').slice(-2)[0], + _draft: isDraft(_path), + _partial: isPartial(_path), _locale, ...content, // TODO: move title to Markdown parser diff --git a/test/features/transformer-path-meta.ts b/test/features/transformer-path-meta.ts index bfbd8c099..dc610efa6 100644 --- a/test/features/transformer-path-meta.ts +++ b/test/features/transformer-path-meta.ts @@ -7,28 +7,32 @@ const testCases = { title: '', _draft: false, _partial: false, - _path: '/' + _path: '/', + _dir: '' }, 'content:3.index.draft.md': { __description: 'Index file with position [Draft]', title: '', _draft: true, _partial: false, - _path: '/' + _path: '/', + _dir: '' }, 'content:1.blog:3.index.draft.md': { __description: 'Blog Index file with position [Draft]', title: '', _draft: true, _partial: false, - _path: '/blog' + _path: '/blog', + _dir: '' }, 'content:1.blog:_4.the-post.md': { __description: 'Blog post file with position [Partial]', title: '4The Post', _draft: false, _partial: true, - _path: '/blog/_4.the-post' + _path: '/blog/_4.the-post', + _dir: 'blog' }, ...['1.0.0', '1.1', '1', '1.x', '1.0.x', '1.0.0.x'].reduce((map, semver) => { map[`content:${semver}:doc.md`] = { @@ -36,7 +40,8 @@ const testCases = { _draft: false, _partial: false, _path: `/${semver}/doc`, - _source: 'content' + _source: 'content', + _dir: semver } return map }, {}), @@ -45,28 +50,32 @@ const testCases = { title: 'Doc', _draft: false, _partial: false, - _path: '/one/two/three/four/five/doc' + _path: '/one/two/three/four/five/doc', + _dir: 'five' }, 'content:1.one:file?param=value#hash.md': { __description: 'Handle special chars in file name', title: 'File?param=value#hash', _draft: false, _partial: false, - _path: '/one/fileparamvaluehash' + _path: '/one/fileparamvaluehash', + _dir: 'one' }, 'content:indexer.md': { __description: 'non-index file with index substring', title: 'Indexer', _draft: false, _partial: false, - _path: '/indexer' + _path: '/indexer', + _dir: '' }, 'content:indexer.draft.md': { __description: 'non-index file with index substring', title: 'Indexer', _draft: true, _partial: false, - _path: '/indexer' + _path: '/indexer', + _dir: '' } } @@ -110,6 +119,12 @@ export const testPathMetaTransformer = () => { `source is not equal, recieved: ${transformed._source}` ) + expect(transformed).toHaveProperty('_dir') + assert( + transformed._dir === expected._dir, + `directory is not equal, recieved: ${transformed._dir}` + ) + expect(transformed).toHaveProperty('_path') assert( fullPath.startsWith(`${transformed._source}/${transformed._file}`), diff --git a/test/fixtures/basic/server/api/parse.ts b/test/fixtures/basic/server/api/parse.ts index 4665378cb..95ba9f33c 100644 --- a/test/fixtures/basic/server/api/parse.ts +++ b/test/fixtures/basic/server/api/parse.ts @@ -1,8 +1,8 @@ -import { defineEventHandler, useBody } from 'h3' +import { eventHandler, readBody } from 'h3' import { parseContent } from '#content/server' -export default defineEventHandler(async (event) => { - const { id, content, options } = await useBody(event) +export default eventHandler(async (event) => { + const { id, content, options } = await readBody(event) // @ts-ignore const parsedContent = await parseContent(id, content, options)