diff --git a/src/runtime/composables/query.ts b/src/runtime/composables/query.ts index def5f171c..75a2a2009 100644 --- a/src/runtime/composables/query.ts +++ b/src/runtime/composables/query.ts @@ -39,7 +39,9 @@ export function queryContent(query: string, ...pathParts: str export function queryContent (query: QueryBuilderParams): QueryBuilder; export function queryContent (query?: string | QueryBuilderParams, ...pathParts: string[]) { if (typeof query === 'string') { - const path = withLeadingSlash(withoutTrailingSlash(joinURL(query, ...pathParts))) + let path = withLeadingSlash(withoutTrailingSlash(joinURL(query, ...pathParts))) + // escape regex special chars + path = path.replace(/[-[\]{}()*+.,^$\s]/g, '\\$&') return createQuery(queryFetch).where({ _path: new RegExp(`^${path}`) }) } diff --git a/src/runtime/server/storage.ts b/src/runtime/server/storage.ts index bd97dffe6..22b138d58 100644 --- a/src/runtime/server/storage.ts +++ b/src/runtime/server/storage.ts @@ -128,6 +128,9 @@ export function serverQueryContent (event: CompatibilityEvent let params = (path || {}) as Partial if (typeof path === 'string') { path = withLeadingSlash(joinURL(path, ...pathParts)) + // escape regex special chars + path = path.replace(/[-[\]{}()*+.,^$\s]/g, '\\$&') + params = { where: [{ _path: new RegExp(`^${path}`) }] } diff --git a/test/basic.test.ts b/test/basic.test.ts index e4f74fdc8..3be5742f9 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -99,6 +99,10 @@ describe('fixtures:basic', async () => { expect(html).not.contains('') }) + test('partial:specials-chars', async () => { + const html = await $fetch('/_partial/content-(v2)') + expect(html).contains('Content (v2)') + }) testNavigation() testMarkdownParser() diff --git a/test/fixtures/basic/content/_partial/content (v2).md b/test/fixtures/basic/content/_partial/content (v2).md new file mode 100644 index 000000000..27d810e8f --- /dev/null +++ b/test/fixtures/basic/content/_partial/content (v2).md @@ -0,0 +1 @@ +# Content (v2) \ No newline at end of file