From 693642ff204ee10d3d9e660c433c5312c73da1ed Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 14 Jan 2025 13:59:20 +0800 Subject: [PATCH] feat: use async localStorage instead --- packages/kit/package.json | 1 - packages/kit/src/module/install.ts | 44 +++---------------- packages/nuxt/package.json | 1 + packages/nuxt/src/core/nuxt.ts | 50 +++++++++++++++++++++- packages/schema/src/config/experimental.ts | 9 ++++ packages/schema/src/config/internal.ts | 2 +- packages/schema/src/types/debug.ts | 5 ++- packages/schema/src/types/nuxt.ts | 4 ++ pnpm-lock.yaml | 6 +-- 9 files changed, 77 insertions(+), 45 deletions(-) diff --git a/packages/kit/package.json b/packages/kit/package.json index c435d4c657..3a318286f2 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -39,7 +39,6 @@ "klona": "^2.0.6", "mlly": "^1.7.3", "ohash": "^1.1.4", - "on-change": "^5.0.1", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "scule": "^1.3.0", diff --git a/packages/kit/src/module/install.ts b/packages/kit/src/module/install.ts index 7bbd4fddfc..d9a149cd96 100644 --- a/packages/kit/src/module/install.ts +++ b/packages/kit/src/module/install.ts @@ -1,6 +1,6 @@ import { existsSync, promises as fsp, lstatSync } from 'node:fs' import { fileURLToPath, pathToFileURL } from 'node:url' -import type { ModuleMeta, Nuxt, NuxtConfig, NuxtDebugModuleMutationRecord, NuxtModule } from '@nuxt/schema' +import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema' import { dirname, isAbsolute, join, resolve } from 'pathe' import { defu } from 'defu' import { createJiti } from 'jiti' @@ -27,44 +27,11 @@ export async function installModule< } } - let _nuxt = nuxt - if (nuxt.options.debug) { - const onChange = await import('on-change').then(r => r.default) - const moduleName = (await nuxtModule.getMeta?.())?.name || buildTimeModuleMeta?.name || resolvedModulePath - - // Unwrap onChange proxy if already wrapped - nuxt = onChange.target(nuxt) - - nuxt._debug ||= {} - nuxt._debug.moduleMutationRecords ||= [] - - _nuxt = onChange( - nuxt, - (keys, value, _, applyData) => { - // We only listen to changes in the `options` object - if (keys[0] !== 'options') { - return - } - const record: NuxtDebugModuleMutationRecord = { - module: moduleName, - keys: keys.slice(1), - value, - timestamp: Date.now(), - } - if (applyData?.name) { - record.method = applyData.name - } - nuxt._debug!.moduleMutationRecords!.push(record) - }, { - ignoreUnderscores: true, - ignoreSymbols: true, - pathAsArray: true, - }, - ) - } - // Call module - const res = await nuxtModule(inlineOptions || {}, _nuxt) ?? {} + const res = await nuxt._asyncLocalStorageModule.run( + nuxtModule, + () => nuxtModule(inlineOptions || {}, nuxt), + ) ?? {} if (res === false /* setup aborted */) { return } @@ -89,6 +56,7 @@ export async function installModule< nuxt.options._installedModules.push({ meta: defu(await nuxtModule.getMeta?.(), buildTimeModuleMeta), + module: nuxtModule, timings: res.timings, entryPath, }) diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 5d8d666f56..5c10475a4c 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -92,6 +92,7 @@ "globby": "^14.0.2", "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e", "hookable": "^5.5.3", + "on-change": "^5.0.1", "ignore": "^7.0.0", "impound": "^0.2.0", "jiti": "^2.4.2", diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 957da98314..064ff2325a 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -1,5 +1,6 @@ import { existsSync } from 'node:fs' import { rm } from 'node:fs/promises' +import { AsyncLocalStorage } from 'node:async_hooks' import { join, normalize, relative, resolve } from 'pathe' import { createDebugger, createHooks } from 'hookable' import ignore from 'ignore' @@ -10,6 +11,7 @@ import type { PackageJson } from 'pkg-types' import { readPackageJSON } from 'pkg-types' import { hash } from 'ohash' import consola from 'consola' +import onChange from 'on-change' import { colorize } from 'consola/utils' import { updateConfig } from 'c12/update' import { formatDate, resolveCompatibilityDatesFromEnv } from 'compatx' @@ -51,9 +53,11 @@ import { VirtualFSPlugin } from './plugins/virtual' export function createNuxt (options: NuxtOptions): Nuxt { const hooks = createHooks() + const proxiedOptions = new WeakMap() + const nuxt: Nuxt = { _version: version, - options, + _asyncLocalStorageModule: new AsyncLocalStorage(), hooks, callHook: hooks.callHook, addHooks: hooks.addHooks, @@ -62,6 +66,50 @@ export function createNuxt (options: NuxtOptions): Nuxt { close: () => hooks.callHook('close', nuxt), vfs: {}, apps: {}, + options, + } + + if (options.experimental.debugModuleMutation) { + Object.defineProperty(nuxt, 'options', { + get () { + const currentModule = nuxt._asyncLocalStorageModule.getStore() + if (!currentModule) { + return options + } + + if (proxiedOptions.has(currentModule)) { + return proxiedOptions.get(currentModule)! + } + + nuxt._debug ||= {} + nuxt._debug.moduleMutationRecords ||= [] + + const proxied = onChange( + options, + (keys, value, previousValue, applyData) => { + if (value === previousValue && !applyData) { + return + } + nuxt._debug!.moduleMutationRecords!.push({ + module: currentModule, + keys, + target: 'nuxt.options', + value: applyData?.args ?? value, + timestamp: Date.now(), + method: applyData?.name, + }) + }, { + ignoreUnderscores: true, + ignoreSymbols: true, + pathAsArray: true, + }, + ) + + proxiedOptions.set(currentModule, proxied) + return proxied + }, + }, + ) } hooks.hookOnce('close', () => { hooks.removeAllHooks() }) diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 90c0bc4add..fc89c0174b 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -437,5 +437,14 @@ export default defineUntypedSchema({ browserDevtoolsTiming: { $resolve: async (val, get) => val ?? await get('dev'), }, + + /** + * Record mutations to `nuxt.options` in module context + */ + debugModuleMutation: { + $resolve: async (val, get) => { + return val ?? await get('debug') + }, + }, }, }) diff --git a/packages/schema/src/config/internal.ts b/packages/schema/src/config/internal.ts index 77c79bd5bc..0207964f64 100644 --- a/packages/schema/src/config/internal.ts +++ b/packages/schema/src/config/internal.ts @@ -25,7 +25,7 @@ export default defineUntypedSchema({ appDir: '', /** * @private - * @type {Array<{ meta: ModuleMeta; timings?: Record; entryPath?: string }>} + * @type {Array<{ meta: ModuleMeta; module: NuxtModule, timings?: Record; entryPath?: string }>} */ _installedModules: [], /** @private */ diff --git a/packages/schema/src/types/debug.ts b/packages/schema/src/types/debug.ts index c034d8f513..ba8edb2b5a 100644 --- a/packages/schema/src/types/debug.ts +++ b/packages/schema/src/types/debug.ts @@ -1,3 +1,5 @@ +import type { NuxtModule } from './module' + export interface NuxtDebugContext { /** * Module mutation records to the `nuxt` instance. @@ -6,8 +8,9 @@ export interface NuxtDebugContext { } export interface NuxtDebugModuleMutationRecord { - module: string | undefined + module: NuxtModule keys: (string | symbol)[] + target: 'nuxt.options' value: any method?: string timestamp: number diff --git a/packages/schema/src/types/nuxt.ts b/packages/schema/src/types/nuxt.ts index 3f16cfdf23..f6d52a20e9 100644 --- a/packages/schema/src/types/nuxt.ts +++ b/packages/schema/src/types/nuxt.ts @@ -1,5 +1,7 @@ +import type { AsyncLocalStorage } from 'node:async_hooks' import type { Hookable } from 'hookable' import type { Ignore } from 'ignore' +import type { NuxtModule } from './module' import type { NuxtHooks, NuxtLayout, NuxtMiddleware, NuxtPage } from './hooks' import type { Component } from './components' import type { NuxtOptions } from './config' @@ -85,6 +87,8 @@ export interface Nuxt { _ignore?: Ignore _dependencies?: Set _debug?: NuxtDebugContext + /** Async local storage for current running Nuxt module instance. */ + _asyncLocalStorageModule: AsyncLocalStorage /** The resolved Nuxt configuration. */ options: NuxtOptions diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 542a70d3ac..8c1914335c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,9 +230,6 @@ importers: ohash: specifier: 1.1.4 version: 1.1.4 - on-change: - specifier: ^5.0.1 - version: 5.0.1 pathe: specifier: ^2.0.1 version: 2.0.1 @@ -405,6 +402,9 @@ importers: ohash: specifier: 1.1.4 version: 1.1.4 + on-change: + specifier: ^5.0.1 + version: 5.0.1 pathe: specifier: ^2.0.1 version: 2.0.1