refactor(kit,nuxt,vite,webpack)!: remove legacy require utils (#28008)

This commit is contained in:
Daniel Roe 2024-07-03 21:12:33 +01:00 committed by GitHub
parent 128edc76fc
commit 79193561b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 76 additions and 183 deletions

View File

@ -509,6 +509,30 @@ These options have been set to their current values for some time and we do not
* `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9)
#### Removal of Deprecated Internal CJS Utils
🚦 **Impact Level**: Minimal
##### What Changed
We have now removed the following utils exported from `@nuxt/kit`:
* `resolveModule`
* `requireModule`
* `importModule`
* `tryImportModule`
* `tryRequireModule`
They were previously marked as deprecated and relied on CJS resolutions.
##### Reasons for Change
We now use [jiti](https://github.com/unjs/jiti) to resolve modules and other imports internally. It supports native ESM resolution where possible and should be less buggy.
##### Migration Steps
You can use [jiti](https://github.com/unjs/jiti) or [mlly](https://github.com/unjs/mlly) to do the same job in your own projects if you were relying on these utilities.
## Nuxt 2 vs Nuxt 3+
In the table below, there is a quick comparison between 3 versions of Nuxt:

View File

@ -1,7 +0,0 @@
/* eslint-disable no-var */
declare global {
var __NUXT_PREPATHS__: string[] | string | undefined
var __NUXT_PATHS__: string[] | string | undefined
}
export {}

View File

@ -32,13 +32,4 @@ export { addTemplate, addTypeTemplate, normalizeTemplate, updateTemplates, write
export { logger, useLogger } from './logger'
// Internal Utils
// TODO
export {
resolveModule,
requireModule,
importModule,
tryImportModule,
tryRequireModule,
} from './internal/cjs'
export type { ResolveModuleOptions, RequireModuleOptions } from './internal/cjs'
export { tryResolveModule } from './internal/esm'

View File

@ -1,121 +0,0 @@
import { pathToFileURL } from 'node:url'
import { normalize } from 'pathe'
import { interopDefault } from 'mlly'
import { createJiti } from 'jiti'
// TODO: use create-require for jest environment
const jiti = createJiti(process.cwd(), { interopDefault: true })
/** @deprecated Do not use CJS utils */
export interface ResolveModuleOptions {
paths?: string | string[]
}
/** @deprecated Do not use CJS utils */
export interface RequireModuleOptions extends ResolveModuleOptions {
// TODO: use create-require for jest environment
// native?: boolean
/** Clear the require cache (force fresh require) but only if not within `node_modules` */
clearCache?: boolean
/** Automatically de-default the result of requiring the module. */
interopDefault?: boolean
}
/** @deprecated Do not use CJS utils */
function isNodeModules (id: string) {
// TODO: Follow symlinks
return /[/\\]node_modules[/\\]/.test(id)
}
/** @deprecated Do not use CJS utils */
function clearRequireCache (id: string) {
if (isNodeModules(id)) {
return
}
const entry = getRequireCacheItem(id)
if (!entry) {
delete jiti.cache[id]
return
}
if (entry.parent) {
entry.parent.children = entry.parent.children.filter(e => e.id !== id)
}
for (const child of entry.children) {
clearRequireCache(child.id)
}
delete jiti.cache[id]
}
/** @deprecated Do not use CJS utils */
function getRequireCacheItem (id: string) {
try {
return jiti.cache[id]
} catch (e) {
// ignore issues accessing require.cache
}
}
export function getNodeModulesPaths (paths?: string[] | string) {
return ([] as Array<string | undefined>).concat(
global.__NUXT_PREPATHS__,
paths || [],
process.cwd(),
global.__NUXT_PATHS__,
).filter(Boolean) as string[]
}
/** @deprecated Do not use CJS utils */
export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
return normalize(jiti.resolve(id, {
paths: getNodeModulesPaths(opts.paths),
}))
}
/** @deprecated Do not use CJS utils */
export function requireModule (id: string, opts: RequireModuleOptions = {}) {
// Resolve id
const resolvedPath = resolveModule(id, opts)
// Clear require cache if necessary
if (opts.clearCache && !isNodeModules(id)) {
clearRequireCache(resolvedPath)
}
// Try to require
const requiredModule = jiti(resolvedPath)
return requiredModule
}
/** @deprecated Do not use CJS utils */
export function importModule (id: string, opts: RequireModuleOptions = {}) {
const resolvedPath = resolveModule(id, opts)
if (opts.interopDefault !== false) {
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
}
return import(pathToFileURL(resolvedPath).href)
}
/** @deprecated Do not use CJS utils */
export function tryImportModule (id: string, opts: RequireModuleOptions = {}) {
try {
return importModule(id, opts).catch(() => undefined)
} catch {
// intentionally empty as this is a `try-` function
}
}
/** @deprecated Do not use CJS utils */
export function tryRequireModule (id: string, opts: RequireModuleOptions = {}) {
try {
return requireModule(id, opts)
} catch {
// intentionally empty as this is a `try-` function
}
}

View File

@ -2,10 +2,9 @@ import { existsSync, promises as fsp, lstatSync } from 'node:fs'
import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema'
import { dirname, isAbsolute, join, resolve } from 'pathe'
import { defu } from 'defu'
import { createJiti } from 'jiti'
import { useNuxt } from '../context'
import { requireModule } from '../internal/cjs'
import { importModule } from '../internal/esm'
import { resolveAlias, resolvePath } from '../resolve'
import { resolveAlias } from '../resolve'
import { logger } from '../logger'
const NODE_MODULES_RE = /[/\\]node_modules[/\\]/
@ -72,15 +71,20 @@ export const normalizeModuleTranspilePath = (p: string) => {
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
let buildTimeModuleMeta: ModuleMeta = {}
const jiti = createJiti(nuxt.options.rootDir, {
interopDefault: true,
alias: nuxt.options.alias,
})
// Import if input is string
if (typeof nuxtModule === 'string') {
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule]
let error: unknown
for (const path of paths) {
try {
const src = await resolvePath(path, { fallbackToOriginal: true })
// Prefer ESM resolution if possible
nuxtModule = await importModule(src, nuxt.options.modulesDir).catch(() => null) ?? requireModule(src, { paths: nuxt.options.modulesDir })
const src = jiti.esmResolve(path)
nuxtModule = await jiti.import(src) as NuxtModule
// nuxt-module-builder generates a module.json with metadata including the version
const moduleMetadataPath = join(dirname(src), 'module.json')
@ -94,7 +98,7 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
}
}
if (typeof nuxtModule !== 'function' && error) {
logger.error(`Error while requiring module \`${nuxtModule}\`: ${error}`)
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
throw error
}
}

View File

@ -11,7 +11,6 @@ import { readPackageJSON } from 'pkg-types'
import { tryResolveModule } from './internal/esm'
import { getDirectory } from './module/install'
import { tryUseNuxt, useNuxt } from './context'
import { getNodeModulesPaths } from './internal/cjs'
import { resolveNuxtModule } from './resolve'
/**
@ -266,7 +265,7 @@ export async function _generateTypes (nuxt: Nuxt) {
await Promise.all([...nuxt.options.modules, ...nuxt.options._modules].map(async (id) => {
if (typeof id !== 'string') { return }
const pkg = await readPackageJSON(id, { url: getNodeModulesPaths(nuxt.options.modulesDir) }).catch(() => null)
const pkg = await readPackageJSON(id, { url: nuxt.options.modulesDir }).catch(() => null)
references.push(({ types: pkg?.name || id }))
}))

View File

@ -2,8 +2,6 @@
declare global {
var __NUXT_VERSION__: string
var __NUXT_ASYNC_CONTEXT__: boolean
var __NUXT_PREPATHS__: string[] | string | undefined
var __NUXT_PATHS__: string[] | string | undefined
interface Navigator {
connection?: {

View File

@ -6,7 +6,7 @@ import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from
import { joinURL, withTrailingSlash } from 'ufo'
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, writeTypes } from 'nitro'
import type { Nitro, NitroConfig, NitroOptions } from 'nitro/types'
import { findPath, logger, resolveAlias, resolveIgnorePatterns, resolveNuxtModule, resolvePath } from '@nuxt/kit'
import { findPath, logger, resolveAlias, resolveIgnorePatterns, resolveNuxtModule } from '@nuxt/kit'
import escapeRE from 'escape-string-regexp'
import { defu } from 'defu'
import { dynamicEventHandler } from 'h3'
@ -408,7 +408,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
})
const cacheDir = resolve(nuxt.options.buildDir, 'cache/nitro/prerender')
const cacheDriverPath = await resolvePath(join(distDir, 'core/runtime/nitro/cache-driver'))
const cacheDriverPath = join(distDir, 'core/runtime/nitro/cache-driver.js')
await fsp.rm(cacheDir, { recursive: true, force: true }).catch(() => {})
nitro.options._config.storage = defu(nitro.options._config.storage, {
'internal:nuxt:prerender': {

View File

@ -1,10 +1,18 @@
// @ts-check
import { defineDriver } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs-lite'
import lruCache from 'unstorage/drivers/lru-cache'
const normalizeFsKey = (item: string) => item.replaceAll(':', '_')
/**
* @param {string} item
*/
const normalizeFsKey = item => item.replaceAll(':', '_')
export default defineDriver((opts: { base: string }) => {
/**
* @param {{ base: string }} opts
*/
export default defineDriver((opts) => {
const fs = fsDriver({ base: opts.base })
const lru = lruCache({ max: 1000 })

View File

@ -47,6 +47,7 @@
"externality": "^1.0.2",
"get-port-please": "^3.1.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"jiti": "^2.0.0-beta.3",
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.1",

View File

@ -1,9 +1,7 @@
import { fileURLToPath, pathToFileURL } from 'node:url'
import { requireModule, tryResolveModule } from '@nuxt/kit'
import type { Nuxt, NuxtOptions } from '@nuxt/schema'
import type { InlineConfig as ViteConfig } from 'vite'
import { interopDefault } from 'mlly'
import type { Plugin } from 'postcss'
import { createJiti } from 'jiti'
function sortPlugins ({ plugins, order }: NuxtOptions['postcss']): string[] {
const names = Object.keys(plugins)
@ -18,27 +16,23 @@ export async function resolveCSSOptions (nuxt: Nuxt): Promise<ViteConfig['css']>
}
css.postcss.plugins = []
const postcssOptions = nuxt.options.postcss
const cwd = fileURLToPath(new URL('.', import.meta.url))
const jiti = createJiti(nuxt.options.rootDir, {
interopDefault: true,
alias: nuxt.options.alias,
})
for (const pluginName of sortPlugins(postcssOptions)) {
const pluginOptions = postcssOptions.plugins[pluginName]
if (!pluginOptions) { continue }
const path = await tryResolveModule(pluginName, nuxt.options.modulesDir)
let pluginFn: (opts: Record<string, any>) => Plugin
// TODO: use jiti v2
if (path) {
pluginFn = await import(pathToFileURL(path).href).then(interopDefault)
} else {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\` with ESM. Please report this as a bug.`)
// fall back to cjs
pluginFn = requireModule(pluginName, { paths: [cwd] })
}
const path = jiti.esmResolve(pluginName)
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
if (typeof pluginFn === 'function') {
css.postcss.plugins.push(pluginFn(pluginOptions))
} else {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
}
}

View File

@ -40,6 +40,7 @@
"globby": "^14.0.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"hash-sum": "^2.0.0",
"jiti": "^2.0.0-beta.3",
"knitwork": "^1.1.0",
"lodash-es": "4.17.21",
"magic-string": "^0.30.10",

View File

@ -1,9 +1,7 @@
import { fileURLToPath, pathToFileURL } from 'node:url'
import createResolver from 'postcss-import-resolver'
import { interopDefault } from 'mlly'
import { requireModule, tryResolveModule } from '@nuxt/kit'
import type { Nuxt, NuxtOptions } from '@nuxt/schema'
import { defu } from 'defu'
import { createJiti } from 'jiti'
import type { Plugin } from 'postcss'
const isPureObject = (obj: unknown): obj is Object => obj !== null && !Array.isArray(obj) && typeof obj === 'object'
@ -38,28 +36,25 @@ export async function getPostcssConfig (nuxt: Nuxt) {
sourceMap: nuxt.options.webpack.cssSourceMap,
})
const jiti = createJiti(nuxt.options.rootDir, {
interopDefault: true,
alias: nuxt.options.alias,
})
// Keep the order of default plugins
if (!Array.isArray(postcssOptions.plugins) && isPureObject(postcssOptions.plugins)) {
// Map postcss plugins into instances on object mode once
const cwd = fileURLToPath(new URL('.', import.meta.url))
const plugins: Plugin[] = []
for (const pluginName of sortPlugins(postcssOptions)) {
const pluginOptions = postcssOptions.plugins[pluginName]
if (!pluginOptions) { continue }
const path = await tryResolveModule(pluginName, nuxt.options.modulesDir)
let pluginFn: (opts: Record<string, any>) => Plugin
// TODO: use jiti v2
if (path) {
pluginFn = await import(pathToFileURL(path).href).then(interopDefault)
} else {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\` with ESM. Please report this as a bug.`)
// fall back to cjs
pluginFn = requireModule(pluginName, { paths: [cwd] })
}
const path = jiti.esmResolve(pluginName)
const pluginFn = (await jiti.import(path)) as (opts: Record<string, any>) => Plugin
if (typeof pluginFn === 'function') {
plugins.push(pluginFn(pluginOptions))
} else {
console.warn(`[nuxt] could not import postcss plugin \`${pluginName}\`. Please report this as a bug.`)
}
}

View File

@ -670,6 +670,9 @@ importers:
h3:
specifier: npm:h3-nightly@2.0.0-1718872656.6765a6e
version: h3-nightly@2.0.0-1718872656.6765a6e
jiti:
specifier: 2.0.0-beta.3
version: 2.0.0-beta.3
knitwork:
specifier: ^1.1.0
version: 1.1.0
@ -791,6 +794,9 @@ importers:
hash-sum:
specifier: ^2.0.0
version: 2.0.0
jiti:
specifier: 2.0.0-beta.3
version: 2.0.0-beta.3
knitwork:
specifier: ^1.1.0
version: 1.1.0