Skip to content

Commit

Permalink
feat: refactor to use nitro hooks (#57)
Browse files Browse the repository at this point in the history
* feat: refactor to use nitro hooks (#56)
  • Loading branch information
dulnan authored Jul 5, 2024
1 parent f265986 commit 9bd9d49
Show file tree
Hide file tree
Showing 52 changed files with 1,462 additions and 1,276 deletions.
16 changes: 10 additions & 6 deletions .github/workflows/test.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ jobs:
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: |
npm ci
npx playwright-core install chromium
- name: Vitest
- name: Module Builder
run: |
npm run dev:prepare
npm run prepack
- name: Vitest
run: |
# For some reason this has to be removed or else the test fails.
rm -rf .nuxt
npm run test:ci
- name: Prettier
Expand All @@ -37,4 +41,4 @@ jobs:
- name: nuxi typecheck
run: |
npm run prettier
npm run typecheck
2 changes: 1 addition & 1 deletion build.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
externals: ['unstorage', 'defu', 'h3'],
externals: ['unstorage', 'defu', 'h3', 'pathe'],
})
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 0 additions & 16 deletions playground/pages/cachedPageFromDisk.vue

This file was deleted.

6 changes: 5 additions & 1 deletion playground/pages/cachedPageWithRandomNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<script lang="ts" setup>
import { useAsyncData } from 'nuxt/app'
import { useRouteCache } from '#imports'
import { useCDNHeaders, useRouteCache } from '#imports'
const { data: random } = await useAsyncData(() => {
return Promise.resolve(Math.round(Math.random() * 1000000000).toString())
Expand All @@ -13,4 +13,8 @@ const { data: random } = await useAsyncData(() => {
useRouteCache((helper) => {
helper.setCacheable().setMaxAge(9000)
})
useCDNHeaders((v) => {
v.set('maxStale', true).public().addTags(['one', 'two', 'three'])
})
</script>
90 changes: 43 additions & 47 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { fileURLToPath } from 'url'
import type { NuxtModule } from '@nuxt/schema'
import { defu } from 'defu'
import { relative } from 'pathe'
import {
addServerHandler,
createResolver,
defineNuxtModule,
addComponent,
addImports,
addTemplate,
addServerPlugin,
addServerImports,
} from '@nuxt/kit'
import type {
MultiCacheApp,
MultiCacheServerOptions,
NuxtMultiCacheOptions,
} from './runtime/types'
Expand Down Expand Up @@ -66,13 +70,16 @@ export default defineNuxtModule<ModuleOptions>({
const metaUrl = import.meta.url
const { resolve } = createResolver(metaUrl)
const rootDir = nuxt.options.rootDir
const srcDir = nuxt.options.srcDir
const srcResolver = createResolver(srcDir).resolve

const runtimeDir = fileURLToPath(new URL('./runtime', metaUrl))
nuxt.options.build.transpile.push(runtimeDir)
nuxt.options.runtimeConfig.multiCache = {
debug: !!options.debug,
rootDir,
cdn: {
enabled: !!options.cdn?.enabled,
cacheControlHeader:
options.cdn?.cacheControlHeader || DEFAULT_CDN_CONTROL_HEADER,
cacheTagHeader: options.cdn?.cacheTagHeader || DEFAULT_CDN_TAG_HEADER,
Expand Down Expand Up @@ -104,19 +111,19 @@ export default defineNuxtModule<ModuleOptions>({
})

// Add composables.
if (options.data) {
if (options.data || nuxt.options._prepare) {
addImports({
from: resolve('./runtime/composables/useDataCache'),
name: 'useDataCache',
})
}
if (options.route) {
if (options.route || nuxt.options._prepare) {
addImports({
from: resolve('./runtime/composables/useRouteCache'),
name: 'useRouteCache',
})
}
if (options.cdn) {
if (options.cdn || nuxt.options._prepare) {
addImports({
from: resolve('./runtime/composables/useCDNHeaders'),
name: 'useCDNHeaders',
Expand All @@ -128,44 +135,21 @@ export default defineNuxtModule<ModuleOptions>({
)

// Add RenderCacheable component if feature is enabled.
if (options.component) {
if (options.component || nuxt.options._prepare) {
await addComponent({
filePath: resolve('./runtime/components/RenderCacheable/index'),
name: 'RenderCacheable',
global: true,
})
}

if (options.component || options.route || options.data) {
// Add the event handler that attaches the SSR context object to the
// request event.
addServerHandler({
handler: resolve('./runtime/serverHandler/cacheContext'),
middleware: true,
})
}

// Adds the CDN helper to the event context.
if (options.cdn?.enabled) {
addServerHandler({
handler: resolve('./runtime/serverHandler/cdnHeaders'),
middleware: true,
})
}

// Serves cached routes.
if (options.route?.enabled) {
addServerHandler({
handler: resolve('./runtime/serverHandler/serveCachedRoute'),
})
}

// Hooks into sending the response and adds route to cache and adds CDN
// headers.
if (options.cdn?.enabled || options.route?.enabled) {
addServerHandler({
handler: resolve('./runtime/serverHandler/responseSend'),
})
if (
options.route?.enabled ||
options.cdn?.enabled ||
nuxt.options._prepare
) {
addServerPlugin(resolve('./runtime/server/plugins/multiCache'))
}

// Shamelessly copied and adapted from:
Expand All @@ -181,19 +165,24 @@ export default defineNuxtModule<ModuleOptions>({

const maybeUserFile = fileExists(resolvedPath, extensions)

if (maybeUserFile) {
return addTemplate({
filename: resolvedFilename,
write: true,
getContents: () => `export { default } from '${resolvedPath}'`,
})
}
const moduleTypesPath = relative(
nuxt.options.buildDir,
resolve('./runtime/types.ts'),
)

const serverOptionsLine = maybeUserFile
? `import serverOptions from '${relative(nuxt.options.buildDir, srcResolver(resolvedPath))}'`
: `const serverOptions: MultiCacheServerOptions = {}`

// Else provide `undefined` fallback
return addTemplate({
filename: resolvedFilename,
write: true,
getContents: () => 'export default {}',
getContents: () => `
import type { MultiCacheServerOptions } from '${moduleTypesPath}'
${serverOptionsLine}
export { serverOptions }
`,
})
})()

Expand All @@ -208,6 +197,13 @@ export default defineNuxtModule<ModuleOptions>({
nitroConfig.alias['#multi-cache-server-options'] = template.dst
})

addServerImports([
{
from: resolve('./runtime/server/utils/useMultiCacheApp'),
name: 'useMultiCacheApp',
},
])

// Add cache management API if enabled.
if (options.api?.enabled) {
// Prefix is defined in default config.
Expand All @@ -218,27 +214,27 @@ export default defineNuxtModule<ModuleOptions>({

// Add the server API handlers for cache management.
addServerHandler({
handler: resolve('./runtime/serverHandler/api/purgeAll'),
handler: resolve('./runtime/server/api/purgeAll'),
method: 'post',
route: prefix('purge/all'),
})
addServerHandler({
handler: resolve('./runtime/serverHandler/api/purgeTags'),
handler: resolve('./runtime/server/api/purgeTags'),
method: 'post',
route: prefix('purge/tags'),
})
addServerHandler({
handler: resolve('./runtime/serverHandler/api/purgeItem'),
handler: resolve('./runtime/server/api/purgeItem'),
method: 'post',
route: prefix('purge/:cacheName'),
})
addServerHandler({
handler: resolve('./runtime/serverHandler/api/stats'),
handler: resolve('./runtime/server/api/stats'),
method: 'get',
route: prefix('stats/:cacheName'),
})
addServerHandler({
handler: resolve('./runtime/serverHandler/api/inspectItem'),
handler: resolve('./runtime/server/api/inspectItem'),
method: 'get',
route: prefix('inspect/:cacheName'),
})
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/components/RenderCacheable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ export default defineComponent({
const defaultSlot = slots.default()
const first = defaultSlot[0]

const isServer = import.meta.server || import.meta.env.VITEST

// Wrap all server-side code in an if statement so that it gets properly
// removed from the client bundles.
if (process.server && !props.noCache) {
if (isServer && !props.noCache) {
const { debug } = useRuntimeConfig().multiCache || {}
const cacheKey = getCacheKey(props as any, first, debug)

Expand Down
4 changes: 3 additions & 1 deletion src/runtime/composables/useCDNHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export function useCDNHeaders(
cb: (helper: NuxtMultiCacheCDNHelper) => void,
providedEvent?: H3Event,
): void {
if (process.client) {
const isServer =
import.meta.env.VITEST_SERVER === 'true' || import.meta.server
if (!isServer) {
return
}

Expand Down
7 changes: 5 additions & 2 deletions src/runtime/composables/useDataCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ export function useDataCache<T>(
// Dummy argument for the callback used when something goes wrong accessing
// the cache or on client side.
const dummy: CallbackContext<T> = {
addToCache: (v: T) => {
addToCache: (_v: T) => {
return Promise.resolve()
},
cacheTags: [] as string[],
}

const isServer =
import.meta.env.VITEST_SERVER === 'true' || import.meta.server

// Code only available on server side.
if (process.client) {
if (!isServer) {
return Promise.resolve(dummy)
}
const { debug } = useRuntimeConfig().multiCache || {}
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/composables/useRouteCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export function useRouteCache(
cb: (helper: NuxtMultiCacheRouteCacheHelper) => void,
providedEvent?: H3Event,
): void {
if (process.client) {
const isServer =
import.meta.env.VITEST_SERVER === 'true' || import.meta.server
if (!isServer) {
return
}

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/helpers/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function getMultiCacheContext(

export function getMultiCacheRouteHelper(
event: H3Event,
): NuxtMultiCacheRouteCacheHelper {
): NuxtMultiCacheRouteCacheHelper | undefined {
return event?.context?.[MULTI_CACHE_ROUTE_CONTEXT_KEY]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import type { H3Event } from 'h3'
import { createError, getHeader } from 'h3'
import type { Storage } from 'unstorage'
import type {
MultiCacheRuntimeConfig,
MultiCacheServerOptions,
NuxtMultiCacheSSRContext,
} from './../../../types'
import { loadCacheContext } from './../../helpers/storage'
import { useRuntimeConfig } from '#imports'
import serverOptions from '#multi-cache-server-options'
import type { NuxtMultiCacheSSRContext } from './../../../types'
import { useMultiCacheApp } from '../../utils/useMultiCacheApp'

const AUTH_HEADER = 'x-nuxt-multi-cache-token'

export function getCacheInstance(event: H3Event): Storage {
const cacheContext = loadCacheContext()
const multiCache = useMultiCacheApp()

const cacheName = event.context.params?.cacheName as
| keyof NuxtMultiCacheSSRContext
| undefined

if (cacheName) {
const cache = cacheContext[cacheName]
const cache = multiCache.cache[cacheName]
if (cache) {
return cache
}
Expand All @@ -37,14 +31,9 @@ export function getCacheInstance(event: H3Event): Storage {
*
* Throws an error if authorization failed.
*/
export async function checkAuth(
event: H3Event,
providedRuntimeConfig?: MultiCacheRuntimeConfig,
providedServerOptions?: MultiCacheServerOptions,
) {
const runtimeConfig = useRuntimeConfig()
const { authorizationDisabled, authorizationToken } =
(providedRuntimeConfig || runtimeConfig.multiCache).api || {}
export async function checkAuth(event: H3Event) {
const { serverOptions, config } = useMultiCacheApp()
const { authorizationDisabled, authorizationToken } = config.api || {}

// Allow if authorization is explicitly disabled.
if (authorizationDisabled) {
Expand All @@ -64,8 +53,7 @@ export async function checkAuth(
})
}

const authorization = (providedServerOptions || serverOptions).api
?.authorization
const authorization = serverOptions.api?.authorization

// At this stage if this method is missing, we throw an error to indicate
// that the module is not configured properly.
Expand Down
File renamed without changes.
Loading

0 comments on commit 9bd9d49

Please sign in to comment.