From 705e221bbd28bf831fac1e6329b3468f16495963 Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Thu, 9 Jan 2025 15:04:13 +0100 Subject: [PATCH] feat: provide fallback instance for backward compatibility --- packages/kit/src/context.ts | 14 +++++++++- packages/nuxt/src/core/nuxt.ts | 40 +++++++++++++++++----------- packages/nuxt/test/load-nuxt.test.ts | 12 ++++++++- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/kit/src/context.ts b/packages/kit/src/context.ts index 873b388b26..dd4481dfdd 100644 --- a/packages/kit/src/context.ts +++ b/packages/kit/src/context.ts @@ -1,9 +1,11 @@ import { getContext } from 'unctx' import type { Nuxt } from '@nuxt/schema' import { asyncNameStorage } from './utils' +import { logger } from './logger' /** Direct access to the Nuxt context - see https://github.com/unjs/unctx. */ export const nuxtCtx = () => getContext(asyncNameStorage.getStore()!) +export const fallbackNuxtCtx = getContext('nuxt-fallback') // TODO: Use use/tryUse from unctx. https://github.com/unjs/unctx/issues/6 @@ -19,6 +21,11 @@ export const nuxtCtx = () => getContext(asyncNameStorage.getStore()!) export function useNuxt (): Nuxt { const instance = nuxtCtx().tryUse() if (!instance) { + const fallbackInstance = fallbackNuxtCtx.tryUse() + if (fallbackInstance) { + logger.warn('Using fallback global Nuxt instance. You may be using a @nuxt/kit composable outside of a Nuxt context, this behavior is deprecated and will be removed in v4.') + return fallbackInstance } + throw new Error('Nuxt instance is unavailable!') } return instance @@ -37,5 +44,10 @@ export function useNuxt (): Nuxt { * ``` */ export function tryUseNuxt (): Nuxt | null { - return nuxtCtx().tryUse() + const nuxt = nuxtCtx().tryUse() + if(!nuxt) { + logger.warn('Using fallback global Nuxt instance. You may be using a @nuxt/kit composable outside of a Nuxt context, this behavior is deprecated and will be removed in v4.') + return fallbackNuxtCtx.tryUse() + } + return nuxt } diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 7efa9b4eec..98004c9247 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -4,7 +4,7 @@ import { join, normalize, relative, resolve } from 'pathe' import { createDebugger, createHooks } from 'hookable' import ignore from 'ignore' import type { LoadNuxtOptions } from '@nuxt/kit' -import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro, asyncNameStorage } from '@nuxt/kit' +import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro, asyncNameStorage, fallbackNuxtCtx } from '@nuxt/kit' import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema' import type { PackageJson } from 'pkg-types' import { readPackageJSON } from 'pkg-types' @@ -49,7 +49,7 @@ import { PrehydrateTransformPlugin } from './plugins/prehydrate' import { VirtualFSPlugin } from './plugins/virtual' import { randomUUID } from 'uncrypto' -export function createNuxt (options: NuxtOptions): Nuxt { +export function createNuxt(options: NuxtOptions): Nuxt { const hooks = createHooks() const name = randomUUID() const nuxt: Nuxt = { @@ -59,7 +59,7 @@ export function createNuxt (options: NuxtOptions): Nuxt { callHook: hooks.callHook, addHooks: hooks.addHooks, hook: hooks.hook, - ready: () => asyncNameStorage.run(name, () => initNuxt(nuxt)) , + ready: () => asyncNameStorage.run(name, () => initNuxt(nuxt)), close: () => hooks.callHook('close', nuxt), vfs: {}, apps: {}, @@ -90,7 +90,7 @@ const keyDependencies = [ let warnedAboutCompatDate = false -async function initNuxt (nuxt: Nuxt) { +async function initNuxt(nuxt: Nuxt) { // Register user hooks for (const config of nuxt.options._layers.map(layer => layer.config).reverse()) { if (config.hooks) { @@ -110,7 +110,7 @@ async function initNuxt (nuxt: Nuxt) { logger.info(`Using \`${fallbackCompatibilityDate}\` as fallback compatibility date.`) } - async function promptAndUpdate () { + async function promptAndUpdate() { const result = await consola.prompt(`Do you want to update your ${colorize('cyan', 'nuxt.config')} to set ${colorize('cyan', `compatibilityDate: '${todaysDate}'`)}?`, { type: 'confirm', default: true, @@ -124,7 +124,7 @@ async function initNuxt (nuxt: Nuxt) { const res = await updateConfig({ configFile: 'nuxt.config', cwd: nuxt.options.rootDir, - async onCreate ({ configFile }) { + async onCreate({ configFile }) { const shallCreate = await consola.prompt(`Do you want to create ${colorize('cyan', relative(nuxt.options.rootDir, configFile))}?`, { type: 'confirm', default: true, @@ -134,7 +134,7 @@ async function initNuxt (nuxt: Nuxt) { } return _getDefaultNuxtConfig() }, - onUpdate (config) { + onUpdate(config) { config.compatibilityDate = todaysDate }, }) @@ -175,10 +175,18 @@ async function initNuxt (nuxt: Nuxt) { } } }) - + if (!fallbackNuxtCtx.tryUse()) { + // backward compatibility with 3.x + fallbackNuxtCtx.set(nuxt) + nuxt.hook('close', () => { + fallbackNuxtCtx.unset() + }) + } // Set nuxt instance for useNuxt nuxtCtx().set(nuxt) - nuxt.hook('close', () => nuxtCtx().unset()) + nuxt.hook('close', () => { + nuxtCtx().unset() + }) const coreTypePackages = nuxt.options.typescript.hoist || [] @@ -696,7 +704,7 @@ export default defineNuxtPlugin({ nuxt.options.build.transpile = nuxt.options.build.transpile.map(t => typeof t === 'string' ? normalize(t) : t) addModuleTranspiles() - + // Init nitro await initNitro(nuxt) @@ -723,7 +731,7 @@ export default defineNuxtPlugin({ await nuxt.callHook('ready', nuxt) } -export async function loadNuxt (opts: LoadNuxtOptions): Promise { +export async function loadNuxt(opts: LoadNuxtOptions): Promise { const options = await loadNuxtConfig(opts) // Temporary until finding better placement for each @@ -796,7 +804,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise { configurable: false, enumerable: true, get: () => nitroOptions, - set (value) { + set(value) { Object.assign(nitroOptions, value) }, }, @@ -824,7 +832,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise { return nuxt } -async function checkDependencyVersion (name: string, nuxtVersion: string): Promise { +async function checkDependencyVersion(name: string, nuxtVersion: string): Promise { const path = await resolvePath(name, { fallbackToOriginal: true }).catch(() => null) if (!path || path === name) { return } @@ -837,7 +845,7 @@ async function checkDependencyVersion (name: string, nuxtVersion: string): Promi const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i -function deduplicateArray (maybeArray: T): T { +function deduplicateArray(maybeArray: T): T { if (!Array.isArray(maybeArray)) { return maybeArray } const fresh: any[] = [] @@ -852,7 +860,7 @@ function deduplicateArray (maybeArray: T): T { return fresh as T } -function createPortalProperties (sourceValue: any, options: NuxtOptions, paths: string[]) { +function createPortalProperties(sourceValue: any, options: NuxtOptions, paths: string[]) { let sharedValue = sourceValue for (const path of paths) { @@ -872,7 +880,7 @@ function createPortalProperties (sourceValue: any, options: NuxtOptions, paths: configurable: false, enumerable: true, get: () => sharedValue, - set (value) { + set(value) { sharedValue = value }, }, diff --git a/packages/nuxt/test/load-nuxt.test.ts b/packages/nuxt/test/load-nuxt.test.ts index dffcdf7130..2da31eccea 100644 --- a/packages/nuxt/test/load-nuxt.test.ts +++ b/packages/nuxt/test/load-nuxt.test.ts @@ -35,7 +35,7 @@ describe('loadNuxt', () => { ready: true, overrides: { hooks: { - ready () { + ready() { hookRan = true }, }, @@ -44,6 +44,16 @@ describe('loadNuxt', () => { await nuxt.close() expect(hookRan).toBe(true) }) + it('load multiple nuxt', async () => { + await Promise.all([ + loadNuxt({ + cwd: repoRoot, + }), + loadNuxt({ + cwd: repoRoot, + }) + ]) + }) }) describe('dependency mismatch', () => {