Skip to content

Commit 7cff2b9

Browse files
authored
fix(ssr): local content components on Cloudflare Workers (#3704)
1 parent 9f8402a commit 7cff2b9

File tree

5 files changed

+64
-39
lines changed

5 files changed

+64
-39
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@
7575
"hookable": "^5.5.3",
7676
"isomorphic-git": "^1.36.1",
7777
"jiti": "^2.6.1",
78-
"json-schema-to-typescript-lite": "^15.0.0",
79-
"knitwork": "^1.3.0",
78+
"json-schema-to-typescript": "^15.0.4",
8079
"mdast-util-to-hast": "^13.2.1",
8180
"mdast-util-to-string": "^4.0.0",
8281
"micromark": "^4.0.2",

pnpm-lock.yaml

Lines changed: 38 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/runtime/components/ContentRenderer.vue

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<script setup lang="ts">
22
import { kebabCase, pascalCase } from 'scule'
33
import { resolveComponent, toRaw, defineAsyncComponent, computed } from 'vue'
4-
import type { AsyncComponentLoader } from 'vue'
54
import type { MDCComment, MDCElement, MDCRoot, MDCText } from '@nuxtjs/mdc'
65
import htmlTags from '@nuxtjs/mdc/runtime/parser/utils/html-tags-list'
76
import MDCRenderer from '@nuxtjs/mdc/runtime/components/MDCRenderer.vue'
87
import { toHast } from 'minimark/hast'
9-
import { globalComponents, localComponents } from '#content/components'
8+
import { globalComponents, localComponents, localComponentLoaders } from '#content/components'
109
import { useRuntimeConfig } from '#imports'
1110
1211
interface Renderable {
@@ -123,14 +122,8 @@ function resolveVueComponent(component: string | Renderable) {
123122
_component = resolveComponent(component, false)
124123
}
125124
else if (localComponents.includes(pascalCase(component))) {
126-
const loader: AsyncComponentLoader = () => {
127-
return import('#content/components')
128-
.then((m) => {
129-
const comp = m[pascalCase(component) as keyof typeof m] as unknown as () => unknown
130-
return comp ? comp() : undefined
131-
})
132-
}
133-
_component = defineAsyncComponent(loader)
125+
const loader = localComponentLoaders[pascalCase(component) as keyof typeof localComponentLoaders]
126+
_component = loader ? defineAsyncComponent(loader) : undefined
134127
}
135128
if (typeof _component === 'string') {
136129
return _component

src/types/global.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ declare module '#content/manifest' {
1818
declare module '#content/components' {
1919
export const globalComponents: string[]
2020
export const localComponents: string[]
21+
export const localComponentLoaders: Record<string, import('vue').AsyncComponentLoader>
2122
}
2223

2324
declare module '#content/dump' {

src/utils/templates.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { gzip } from 'node:zlib'
22
import type { NuxtTemplate } from '@nuxt/schema'
33
import { isAbsolute, join, relative } from 'pathe'
4-
import { genDynamicImport } from 'knitwork'
5-
import { compile as jsonSchemaToTypescript } from 'json-schema-to-typescript-lite'
6-
import type { JSONSchema } from 'json-schema-to-typescript-lite'
4+
import { compile as jsonSchemaToTypescript } from 'json-schema-to-typescript'
5+
import type { JSONSchema } from 'json-schema-to-typescript'
76
import { pascalCase } from 'scule'
87
import type { Schema } from 'untyped'
98
import type { CollectionInfo, ResolvedCollection } from '../types/collection'
@@ -137,21 +136,32 @@ export const componentsManifestTemplate = (manifest: Manifest) => {
137136
return nuxt.options.dev || options.manifest.components.includes(c.pascalName) || c.global
138137
})
139138
.reduce((map, c) => {
140-
map[c.pascalName] = map[c.pascalName] || [
141-
c.pascalName,
142-
`${genDynamicImport(isAbsolute(c.filePath)
143-
? './' + relative(join(nuxt.options.buildDir, 'content'), c.filePath).replace(/\b\.(?!vue)\w+$/g, '')
144-
: c.filePath.replace(/\b\.(?!vue)\w+$/g, ''), { wrapper: false, singleQuotes: true })}`,
145-
c.global,
146-
]
139+
const importPath = isAbsolute(c.filePath)
140+
? './' + relative(join(nuxt.options.buildDir, 'content'), c.filePath).replace(/\b\.(?!vue)\w+$/g, '')
141+
: c.filePath.replace(/\b\.(?!vue)\w+$/g, '')
142+
map[c.pascalName] = map[c.pascalName] || [c.pascalName, importPath, c.global, c.export || 'default']
147143
return map
148144
}, {} as Record<string, unknown[]>)
149145

150146
const componentsList = Object.values(componentsMap)
151147
const globalComponents = componentsList.filter(c => c[2]).map(c => c[0])
152148
const localComponents = componentsList.filter(c => !c[2])
153149
return [
154-
...localComponents.map(([pascalName, type]) => `export const ${pascalName} = () => ${type}`),
150+
'const pickExport = (mod, exportName, componentName, path) => {',
151+
' const resolved = exportName === \'default\' ? mod?.default : mod?.[exportName]',
152+
' if (!resolved) {',
153+
' throw new Error(`[nuxt-content] Missing export "${exportName}" for component "${componentName}" in "${path}".`)',
154+
' }',
155+
' return resolved',
156+
'}',
157+
'export const localComponentLoaders = {',
158+
...localComponents.map(([pascalName, path, , exp]) => {
159+
const pathLiteral = JSON.stringify(path)
160+
const exportLiteral = JSON.stringify(exp)
161+
const nameLiteral = JSON.stringify(pascalName)
162+
return ` ${pascalName}: () => import(${pathLiteral}).then(m => pickExport(m, ${exportLiteral}, ${nameLiteral}, ${pathLiteral})),`
163+
}),
164+
'}',
155165
`export const globalComponents: string[] = ${JSON.stringify(globalComponents)}`,
156166
`export const localComponents: string[] = ${JSON.stringify(localComponents.map(c => c[0]))}`,
157167
].join('\n')

0 commit comments

Comments
 (0)