mirror of
https://github.com/nuxt/nuxt.git
synced 2025-03-18 23:41:18 +00:00
perf(nuxt): migrate to use exsolve
for module resolution (#31124)
This commit is contained in:
parent
241e04081b
commit
798f050bc7
@ -32,6 +32,7 @@
|
||||
"defu": "^6.1.4",
|
||||
"destr": "^2.0.3",
|
||||
"errx": "^0.1.0",
|
||||
"exsolve": "^0.4.4",
|
||||
"globby": "^14.1.0",
|
||||
"ignore": "^7.0.3",
|
||||
"jiti": "^2.4.2",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { interopDefault, resolvePath, resolvePathSync } from 'mlly'
|
||||
import { interopDefault } from 'mlly'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { createJiti } from 'jiti'
|
||||
import { captureStackTrace } from 'errx'
|
||||
|
||||
@ -21,17 +22,19 @@ export function directoryToURL (dir: string): URL {
|
||||
*/
|
||||
export async function tryResolveModule (id: string, url: URL | URL[]): Promise<string | undefined>
|
||||
/** @deprecated pass URLs pointing at files */
|
||||
export async function tryResolveModule (id: string, url: string | string[]): Promise<string | undefined>
|
||||
export async function tryResolveModule (id: string, url: string | string[] | URL | URL[] = import.meta.url) {
|
||||
try {
|
||||
return await resolvePath(id, { url })
|
||||
} catch {
|
||||
// intentionally empty as this is a `try-` function
|
||||
}
|
||||
export function tryResolveModule (id: string, url: string | string[]): Promise<string | undefined>
|
||||
export function tryResolveModule (id: string, url: string | string[] | URL | URL[] = import.meta.url) {
|
||||
return Promise.resolve(resolveModulePath(id, {
|
||||
from: url,
|
||||
suffixes: ['', 'index'],
|
||||
try: true,
|
||||
}))
|
||||
}
|
||||
|
||||
export function resolveModule (id: string, options?: ResolveModuleOptions) {
|
||||
return resolvePathSync(id, { url: options?.url ?? options?.paths ?? [import.meta.url] })
|
||||
return resolveModulePath(id, {
|
||||
from: options?.url ?? options?.paths ?? [import.meta.url],
|
||||
})
|
||||
}
|
||||
|
||||
export interface ImportModuleOptions extends ResolveModuleOptions {
|
||||
|
@ -8,8 +8,9 @@ import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
|
||||
import { globby } from 'globby'
|
||||
import defu from 'defu'
|
||||
import { basename, join, relative } from 'pathe'
|
||||
import { isWindows } from 'std-env'
|
||||
import { directoryToURL, tryResolveModule } from '../internal/esm'
|
||||
import { resolveModuleURL } from 'exsolve'
|
||||
|
||||
import { directoryToURL } from '../internal/esm'
|
||||
|
||||
export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig>, 'overrides'> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
@ -110,10 +111,10 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
|
||||
async function loadNuxtSchema (cwd: string) {
|
||||
const url = directoryToURL(cwd)
|
||||
const urls = [url]
|
||||
const nuxtPath = await tryResolveModule('nuxt', url) ?? await tryResolveModule('nuxt-nightly', url)
|
||||
const nuxtPath = resolveModuleURL('nuxt', { try: true, from: url }) ?? resolveModuleURL('nuxt-nightly', { try: true, from: url })
|
||||
if (nuxtPath) {
|
||||
urls.unshift(pathToFileURL(nuxtPath))
|
||||
}
|
||||
const schemaPath = await tryResolveModule('@nuxt/schema', urls) ?? '@nuxt/schema'
|
||||
return await import(isWindows ? pathToFileURL(schemaPath).href : schemaPath).then(r => r.NuxtConfigSchema)
|
||||
const schemaPath = resolveModuleURL('@nuxt/schema', { try: true, from: urls }) ?? '@nuxt/schema'
|
||||
return await import(schemaPath).then(r => r.NuxtConfigSchema)
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { existsSync, promises as fsp, lstatSync } from 'node:fs'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema'
|
||||
import { dirname, isAbsolute, join, resolve } from 'pathe'
|
||||
import { dirname, isAbsolute, resolve } from 'pathe'
|
||||
import { defu } from 'defu'
|
||||
import { createJiti } from 'jiti'
|
||||
import { parseNodeModulePath, resolve as resolveModule } from 'mlly'
|
||||
import { parseNodeModulePath } from 'mlly'
|
||||
import { resolveModuleURL } from 'exsolve'
|
||||
import { isRelative } from 'ufo'
|
||||
import { directoryToURL } from '../internal/esm'
|
||||
import { useNuxt } from '../context'
|
||||
import { resolveAlias, resolvePath } from '../resolve'
|
||||
import { logger } from '../logger'
|
||||
import { resolveAlias } from '../resolve'
|
||||
|
||||
const NODE_MODULES_RE = /[/\\]node_modules[/\\]/
|
||||
|
||||
@ -81,84 +81,63 @@ export const normalizeModuleTranspilePath = (p: string) => {
|
||||
|
||||
const MissingModuleMatcher = /Cannot find module\s+['"]?([^'")\s]+)['"]?/i
|
||||
|
||||
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
||||
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()): Promise<{ nuxtModule: NuxtModule<any>, buildTimeModuleMeta: ModuleMeta, resolvedModulePath?: string }> {
|
||||
let buildTimeModuleMeta: ModuleMeta = {}
|
||||
let resolvedModulePath: string | undefined
|
||||
|
||||
if (typeof nuxtModule === 'function') {
|
||||
return {
|
||||
nuxtModule,
|
||||
buildTimeModuleMeta,
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof nuxtModule !== 'string') {
|
||||
throw new TypeError(`Nuxt module should be a function or a string to import. Received: ${nuxtModule}.`)
|
||||
}
|
||||
|
||||
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })
|
||||
|
||||
let resolvedNuxtModule: NuxtModule<any> | undefined
|
||||
let hadNonFunctionPath = false
|
||||
// Import if input is string
|
||||
if (typeof nuxtModule === 'string') {
|
||||
const paths = new Set<string>()
|
||||
nuxtModule = resolveAlias(nuxtModule, nuxt.options.alias)
|
||||
nuxtModule = resolveAlias(nuxtModule, nuxt.options.alias)
|
||||
|
||||
if (isRelative(nuxtModule)) {
|
||||
nuxtModule = resolve(nuxt.options.rootDir, nuxtModule)
|
||||
}
|
||||
|
||||
paths.add(nuxtModule)
|
||||
paths.add(join(nuxtModule, 'module'))
|
||||
paths.add(join(nuxtModule, 'nuxt'))
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
const src = isAbsolute(path)
|
||||
? pathToFileURL(await resolvePath(path, { fallbackToOriginal: false, extensions: nuxt.options.extensions })).href
|
||||
: await resolveModule(path, {
|
||||
url: nuxt.options.modulesDir.map(m => directoryToURL(m.replace(/\/node_modules\/?$/, '/'))),
|
||||
extensions: nuxt.options.extensions,
|
||||
})
|
||||
resolvedModulePath = fileURLToPath(new URL(src))
|
||||
if (!existsSync(resolvedModulePath)) {
|
||||
continue
|
||||
}
|
||||
const instance = await jiti.import(src, { default: true }) as NuxtModule
|
||||
// ignore possible barrel exports
|
||||
if (typeof instance !== 'function') {
|
||||
hadNonFunctionPath = true
|
||||
continue
|
||||
}
|
||||
resolvedNuxtModule = instance
|
||||
|
||||
// nuxt-module-builder generates a module.json with metadata including the version
|
||||
const moduleMetadataPath = new URL('module.json', src)
|
||||
if (existsSync(moduleMetadataPath)) {
|
||||
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
|
||||
}
|
||||
break
|
||||
} catch (error: unknown) {
|
||||
const code = (error as Error & { code?: string }).code
|
||||
if (code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || code === 'ERR_UNSUPPORTED_DIR_IMPORT' || code === 'ENOTDIR') {
|
||||
continue
|
||||
}
|
||||
if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {
|
||||
const module = MissingModuleMatcher.exec((error as Error).message)?.[1]
|
||||
// verify that it's missing the nuxt module otherwise it may be a sub dependency of the module itself
|
||||
// i.e module is importing a module that is missing
|
||||
if (!module || module.includes(nuxtModule as string)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolvedNuxtModule = nuxtModule
|
||||
if (isRelative(nuxtModule)) {
|
||||
nuxtModule = resolve(nuxt.options.rootDir, nuxtModule)
|
||||
}
|
||||
|
||||
if (!resolvedNuxtModule) {
|
||||
// Module was resolvable but returned a non-function
|
||||
if (hadNonFunctionPath) {
|
||||
try {
|
||||
const src = resolveModuleURL(nuxtModule, {
|
||||
from: nuxt.options.modulesDir.map(m => directoryToURL(m.replace(/\/node_modules\/?$/, '/'))),
|
||||
suffixes: ['nuxt', 'nuxt/index', 'module', 'module/index', '', 'index'],
|
||||
extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'],
|
||||
})
|
||||
const resolvedModulePath = fileURLToPath(src)
|
||||
const resolvedNuxtModule = await jiti.import<NuxtModule<any>>(src, { default: true })
|
||||
|
||||
if (typeof resolvedNuxtModule !== 'function') {
|
||||
throw new TypeError(`Nuxt module should be a function: ${nuxtModule}.`)
|
||||
}
|
||||
// Throw error if module could not be found
|
||||
if (typeof nuxtModule === 'string') {
|
||||
|
||||
// nuxt-module-builder generates a module.json with metadata including the version
|
||||
const moduleMetadataPath = new URL('module.json', src)
|
||||
if (existsSync(moduleMetadataPath)) {
|
||||
buildTimeModuleMeta = JSON.parse(await fsp.readFile(moduleMetadataPath, 'utf-8'))
|
||||
}
|
||||
|
||||
return { nuxtModule: resolvedNuxtModule, buildTimeModuleMeta, resolvedModulePath }
|
||||
} catch (error: unknown) {
|
||||
const code = (error as Error & { code?: string }).code
|
||||
if (code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || code === 'ERR_UNSUPPORTED_DIR_IMPORT' || code === 'ENOTDIR') {
|
||||
throw new TypeError(`Could not load \`${nuxtModule}\`. Is it installed?`)
|
||||
}
|
||||
if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {
|
||||
const module = MissingModuleMatcher.exec((error as Error).message)?.[1]
|
||||
// verify that it's missing the nuxt module otherwise it may be a sub dependency of the module itself
|
||||
// i.e module is importing a module that is missing
|
||||
if (module && !module.includes(nuxtModule as string)) {
|
||||
throw new TypeError(`Error while importing module \`${nuxtModule}\`: ${error}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { nuxtModule: resolvedNuxtModule, buildTimeModuleMeta, resolvedModulePath } as { nuxtModule: NuxtModule<any>, buildTimeModuleMeta: ModuleMeta, resolvedModulePath?: string }
|
||||
throw new TypeError(`Could not load \`${nuxtModule}\`. Is it installed?`)
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { isAbsolute } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { normalize } from 'pathe'
|
||||
import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema'
|
||||
import { resolvePathSync } from 'mlly'
|
||||
import { isWindows } from 'std-env'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { MODE_RE, filterInPlace } from './utils'
|
||||
import { tryUseNuxt, useNuxt } from './context'
|
||||
import { addTemplate } from './template'
|
||||
@ -35,7 +33,9 @@ export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
|
||||
|
||||
if (!existsSync(plugin.src) && isAbsolute(plugin.src)) {
|
||||
try {
|
||||
plugin.src = resolvePathSync(isWindows ? pathToFileURL(plugin.src).href : plugin.src, { extensions: tryUseNuxt()?.options.extensions })
|
||||
plugin.src = resolveModulePath(plugin.src, {
|
||||
extensions: tryUseNuxt()?.options.extensions ?? ['.js', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts'],
|
||||
})
|
||||
} catch {
|
||||
// ignore errors as the file may be in the nuxt vfs
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { promises as fsp } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { basename, dirname, isAbsolute, join, normalize, resolve } from 'pathe'
|
||||
import { globby } from 'globby'
|
||||
import { resolvePath as _resolvePath } from 'mlly'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { resolveAlias as _resolveAlias } from 'pathe/utils'
|
||||
import { directoryToURL } from './internal/esm'
|
||||
import { tryUseNuxt } from './context'
|
||||
@ -172,7 +172,7 @@ async function _resolvePathGranularly (path: string, opts: ResolvePathOptions =
|
||||
const modulesDir = nuxt ? nuxt.options.modulesDir : []
|
||||
|
||||
// Resolve aliases
|
||||
path = resolveAlias(path)
|
||||
path = _resolveAlias(path, opts.alias ?? nuxt?.options.alias ?? {})
|
||||
|
||||
// Resolve relative to cwd
|
||||
if (!isAbsolute(path)) {
|
||||
@ -200,7 +200,11 @@ async function _resolvePathGranularly (path: string, opts: ResolvePathOptions =
|
||||
}
|
||||
|
||||
// Try to resolve as module id
|
||||
const resolvedModulePath = await _resolvePath(_path, { url: [cwd, ...modulesDir].map(d => directoryToURL(d)) }).catch(() => null)
|
||||
const resolvedModulePath = resolveModulePath(_path, {
|
||||
try: true,
|
||||
suffixes: ['', 'index'],
|
||||
from: [cwd, ...modulesDir].map(d => directoryToURL(d)),
|
||||
})
|
||||
if (resolvedModulePath) {
|
||||
return {
|
||||
path: resolvedModulePath,
|
||||
|
@ -7,9 +7,10 @@ import { defu } from 'defu'
|
||||
import type { TSConfig } from 'pkg-types'
|
||||
import { gte } from 'semver'
|
||||
import { readPackageJSON } from 'pkg-types'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
|
||||
import { filterInPlace } from './utils'
|
||||
import { directoryToURL, tryResolveModule } from './internal/esm'
|
||||
import { directoryToURL } from './internal/esm'
|
||||
import { getDirectory } from './module/install'
|
||||
import { tryUseNuxt, useNuxt } from './context'
|
||||
import { resolveNuxtModule } from './resolve'
|
||||
@ -250,7 +251,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
||||
let absolutePath = resolve(basePath, aliases[alias]!)
|
||||
let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
|
||||
if (!stats) {
|
||||
const resolvedModule = await tryResolveModule(aliases[alias]!, importPaths)
|
||||
const resolvedModule = resolveModulePath(aliases[alias]!, { try: true, from: importPaths })
|
||||
if (resolvedModule) {
|
||||
absolutePath = resolvedModule
|
||||
stats = await fsp.stat(resolvedModule).catch(() => null)
|
||||
|
@ -89,6 +89,7 @@
|
||||
"esbuild": "^0.25.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"exsolve": "^0.4.4",
|
||||
"globby": "^14.1.0",
|
||||
"h3": "npm:h3-nightly@1.15.1-20250222-111608-d1c00fc",
|
||||
"hookable": "^5.5.3",
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { existsSync, statSync, writeFileSync } from 'node:fs'
|
||||
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
||||
import { addBuildPlugin, addPluginTemplate, addTemplate, addTypeTemplate, addVitePlugin, defineNuxtModule, findPath, resolveAlias, resolvePath } from '@nuxt/kit'
|
||||
import { addBuildPlugin, addPluginTemplate, addTemplate, addTypeTemplate, addVitePlugin, defineNuxtModule, findPath, resolveAlias } from '@nuxt/kit'
|
||||
import type { Component, ComponentsDir, ComponentsOptions } from 'nuxt/schema'
|
||||
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { distDir } from '../dirs'
|
||||
import { logger } from '../utils'
|
||||
import { componentNamesTemplate, componentsIslandsTemplate, componentsMetadataTemplate, componentsPluginTemplate, componentsTypeTemplate } from './templates'
|
||||
@ -175,7 +176,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
for (const component of newComponents) {
|
||||
if (!(component as any /* untyped internal property */)._scanned && !(component.filePath in nuxt.vfs) && isAbsolute(component.filePath) && !existsSync(component.filePath)) {
|
||||
// attempt to resolve component path
|
||||
component.filePath = await resolvePath(component.filePath, { fallbackToOriginal: true })
|
||||
component.filePath = resolveModulePath(resolveAlias(component.filePath), { try: true, extensions: nuxt.options.extensions }) ?? component.filePath
|
||||
}
|
||||
if (component.mode === 'client' && !newComponents.some(c => c.pascalName === component.pascalName && c.mode === 'server')) {
|
||||
newComponents.push({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { promises as fsp, mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { dirname, join, relative, resolve } from 'pathe'
|
||||
import { defu } from 'defu'
|
||||
import { findPath, normalizePlugin, normalizeTemplate, resolveAlias, resolveFiles, resolvePath } from '@nuxt/kit'
|
||||
import { findPath, normalizePlugin, normalizeTemplate, resolveFiles, resolvePath } from '@nuxt/kit'
|
||||
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema'
|
||||
|
||||
import type { PluginMeta } from 'nuxt/app'
|
||||
@ -215,8 +215,8 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
||||
}
|
||||
|
||||
// Normalize and de-duplicate plugins and middleware
|
||||
app.middleware = uniqueBy(await resolvePaths([...app.middleware].reverse(), 'path'), 'name').reverse()
|
||||
app.plugins = uniqueBy(await resolvePaths(app.plugins, 'src'), 'src')
|
||||
app.middleware = uniqueBy(await resolvePaths(nuxt, [...app.middleware].reverse(), 'path'), 'name').reverse()
|
||||
app.plugins = uniqueBy(await resolvePaths(nuxt, app.plugins, 'src'), 'src')
|
||||
|
||||
// Resolve app.config
|
||||
app.configs = []
|
||||
@ -231,16 +231,21 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
||||
await nuxt.callHook('app:resolve', app)
|
||||
|
||||
// Normalize and de-duplicate plugins and middleware
|
||||
app.middleware = uniqueBy(await resolvePaths(app.middleware, 'path'), 'name')
|
||||
app.plugins = uniqueBy(await resolvePaths(app.plugins, 'src'), 'src')
|
||||
app.middleware = uniqueBy(await resolvePaths(nuxt, app.middleware, 'path'), 'name')
|
||||
app.plugins = uniqueBy(await resolvePaths(nuxt, app.plugins, 'src'), 'src')
|
||||
}
|
||||
|
||||
function resolvePaths<Item extends Record<string, any>> (items: Item[], key: { [K in keyof Item]: Item[K] extends string ? K : never }[keyof Item]) {
|
||||
function resolvePaths<Item extends Record<string, any>> (nuxt: Nuxt, items: Item[], key: { [K in keyof Item]: Item[K] extends string ? K : never }[keyof Item]) {
|
||||
return Promise.all(items.map(async (item) => {
|
||||
if (!item[key]) { return item }
|
||||
return {
|
||||
...item,
|
||||
[key]: await resolvePath(resolveAlias(item[key])),
|
||||
[key]: await resolvePath(item[key], {
|
||||
alias: nuxt.options.alias,
|
||||
extensions: nuxt.options.extensions,
|
||||
fallbackToOriginal: true,
|
||||
virtual: true,
|
||||
}),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { EventType } from '@parcel/watcher'
|
||||
import type { FSWatcher } from 'chokidar'
|
||||
import { watch as chokidarWatch } from 'chokidar'
|
||||
import { createIsIgnored, directoryToURL, importModule, isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit'
|
||||
import { createIsIgnored, directoryToURL, importModule, isIgnored, useNuxt } from '@nuxt/kit'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { normalize, relative, resolve } from 'pathe'
|
||||
import type { Nuxt, NuxtBuilder } from 'nuxt/schema'
|
||||
@ -193,36 +193,35 @@ async function createParcelWatcher () {
|
||||
// eslint-disable-next-line no-console
|
||||
console.time('[nuxt] builder:parcel:watch')
|
||||
}
|
||||
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)))
|
||||
if (!watcherPath) {
|
||||
try {
|
||||
const { subscribe } = await importModule<typeof import('@parcel/watcher')>('@parcel/watcher', { url: [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)) })
|
||||
for (const layer of nuxt.options._layers) {
|
||||
if (!layer.config.srcDir) { continue }
|
||||
const watcher = subscribe(layer.config.srcDir, (err, events) => {
|
||||
if (err) { return }
|
||||
for (const event of events) {
|
||||
if (isIgnored(event.path)) { continue }
|
||||
nuxt.callHook('builder:watch', watchEvents[event.type], normalize(event.path))
|
||||
}
|
||||
}, {
|
||||
ignore: [
|
||||
...nuxt.options.ignore,
|
||||
'node_modules',
|
||||
],
|
||||
})
|
||||
watcher.then((subscription) => {
|
||||
if (nuxt.options.debug && nuxt.options.debug.watchers) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.timeEnd('[nuxt] builder:parcel:watch')
|
||||
}
|
||||
nuxt.hook('close', () => subscription.unsubscribe())
|
||||
})
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
logger.warn('Falling back to `chokidar-granular` as `@parcel/watcher` cannot be resolved in your project.')
|
||||
return false
|
||||
}
|
||||
|
||||
const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath)
|
||||
for (const layer of nuxt.options._layers) {
|
||||
if (!layer.config.srcDir) { continue }
|
||||
const watcher = subscribe(layer.config.srcDir, (err, events) => {
|
||||
if (err) { return }
|
||||
for (const event of events) {
|
||||
if (isIgnored(event.path)) { continue }
|
||||
nuxt.callHook('builder:watch', watchEvents[event.type], normalize(event.path))
|
||||
}
|
||||
}, {
|
||||
ignore: [
|
||||
...nuxt.options.ignore,
|
||||
'node_modules',
|
||||
],
|
||||
})
|
||||
watcher.then((subscription) => {
|
||||
if (nuxt.options.debug && nuxt.options.debug.watchers) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.timeEnd('[nuxt] builder:parcel:watch')
|
||||
}
|
||||
nuxt.hook('close', () => subscription.unsubscribe())
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function bundle (nuxt: Nuxt) {
|
||||
@ -244,10 +243,9 @@ async function bundle (nuxt: Nuxt) {
|
||||
}
|
||||
|
||||
async function loadBuilder (nuxt: Nuxt, builder: string): Promise<NuxtBuilder> {
|
||||
const builderPath = await tryResolveModule(builder, [directoryToURL(nuxt.options.rootDir), new URL(import.meta.url)])
|
||||
|
||||
if (!builderPath) {
|
||||
try {
|
||||
return await importModule(builder, { url: [directoryToURL(nuxt.options.rootDir), new URL(import.meta.url)] })
|
||||
} catch {
|
||||
throw new Error(`Loading \`${builder}\` builder failed. You can read more about the nuxt \`builder\` option at: \`https://nuxt.com/docs/api/nuxt-config#builder\``)
|
||||
}
|
||||
return importModule(builderPath)
|
||||
}
|
||||
|
@ -6,7 +6,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, directoryToURL, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, runWithNuxtContext, tryResolveModule, useNitro } from '@nuxt/kit'
|
||||
import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, directoryToURL, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, runWithNuxtContext, useNitro } from '@nuxt/kit'
|
||||
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
|
||||
import type { PackageJson } from 'pkg-types'
|
||||
import { readPackageJSON } from 'pkg-types'
|
||||
@ -24,6 +24,7 @@ import defu from 'defu'
|
||||
import { gt, satisfies } from 'semver'
|
||||
import { hasTTY, isCI } from 'std-env'
|
||||
import { genImport } from 'knitwork'
|
||||
import { resolveModulePath, resolveModuleURL } from 'exsolve'
|
||||
|
||||
import { installNuxtModule } from '../core/features'
|
||||
import pagesModule from '../pages/module'
|
||||
@ -390,14 +391,14 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
}))
|
||||
}
|
||||
|
||||
nuxt.hook('modules:done', async () => {
|
||||
nuxt.hook('modules:done', () => {
|
||||
const importPaths = nuxt.options.modulesDir.map(dir => directoryToURL((dir)))
|
||||
// Add unctx transform
|
||||
addBuildPlugin(UnctxTransformPlugin({
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
transformerOptions: {
|
||||
...nuxt.options.optimization.asyncTransforms,
|
||||
helperModule: await tryResolveModule('unctx', importPaths) ?? 'unctx',
|
||||
helperModule: resolveModuleURL('unctx', { try: true, from: importPaths }) ?? 'unctx',
|
||||
},
|
||||
}))
|
||||
|
||||
@ -482,8 +483,14 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
for (const _mod of nuxt.options.modules) {
|
||||
const mod = Array.isArray(_mod) ? _mod[0] : _mod
|
||||
if (typeof mod !== 'string') { continue }
|
||||
const modPath = await resolvePath(resolveAlias(mod), { fallbackToOriginal: true })
|
||||
specifiedModules.add(modPath)
|
||||
const modAlias = resolveAlias(mod)
|
||||
const modPath = resolveModulePath(modAlias, {
|
||||
try: true,
|
||||
from: nuxt.options.modulesDir.map(m => directoryToURL(m.replace(/\/node_modules\/?$/, '/'))),
|
||||
suffixes: ['nuxt', 'nuxt/index', 'module', 'module/index', '', 'index'],
|
||||
extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'],
|
||||
})
|
||||
specifiedModules.add(modPath || modAlias)
|
||||
}
|
||||
|
||||
// Automatically register user modules
|
||||
@ -920,9 +927,9 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
||||
}
|
||||
|
||||
export async function checkDependencyVersion (name: string, nuxtVersion: string): Promise<void> {
|
||||
const path = await resolvePath(name, { fallbackToOriginal: true }).catch(() => null)
|
||||
const path = resolveModulePath(name, { try: true })
|
||||
|
||||
if (!path || path === name) { return }
|
||||
if (!path) { return }
|
||||
const { version } = await readPackageJSON(path)
|
||||
|
||||
if (version && gt(nuxtVersion, version)) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { parseNodeModulePath, resolvePath } from 'mlly'
|
||||
import { parseNodeModulePath } from 'mlly'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { isAbsolute, normalize } from 'pathe'
|
||||
import type { Plugin } from 'vite'
|
||||
import { directoryToURL, resolveAlias } from '@nuxt/kit'
|
||||
@ -36,13 +37,24 @@ export function resolveDeepImportsPlugin (nuxt: Nuxt): Plugin {
|
||||
const normalisedImporter = importer.replace(/^\0?virtual:(?:nuxt:)?/, '')
|
||||
const dir = parseNodeModulePath(normalisedImporter).dir || pkgDir
|
||||
|
||||
return await this.resolve?.(normalisedId, dir, { skipSelf: true }) ?? await resolvePath(id, {
|
||||
url: [dir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)),
|
||||
const res = await this.resolve?.(normalisedId, dir, { skipSelf: true })
|
||||
if (res !== undefined && res !== null) {
|
||||
return res
|
||||
}
|
||||
|
||||
const path = resolveModulePath(id, {
|
||||
from: [dir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)),
|
||||
suffixes: ['', 'index'],
|
||||
conditions,
|
||||
}).catch(() => {
|
||||
try: true,
|
||||
})
|
||||
|
||||
if (!path) {
|
||||
logger.debug('Could not resolve id', id, importer)
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
return normalize(path)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { resolve } from 'pathe'
|
||||
import { watch } from 'chokidar'
|
||||
import { defu } from 'defu'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { createIsIgnored, createResolver, defineNuxtModule, directoryToURL, importModule, tryResolveModule } from '@nuxt/kit'
|
||||
import { createIsIgnored, createResolver, defineNuxtModule, directoryToURL, importModule } from '@nuxt/kit'
|
||||
import { generateTypes, resolveSchema as resolveUntypedSchema } from 'untyped'
|
||||
import type { Schema, SchemaDefinition } from 'untyped'
|
||||
import untypedPlugin from 'untyped/babel-plugin'
|
||||
@ -54,9 +54,10 @@ export default defineNuxtModule({
|
||||
})
|
||||
|
||||
if (nuxt.options.experimental.watcher === 'parcel') {
|
||||
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(dir => directoryToURL(dir)))
|
||||
if (watcherPath) {
|
||||
const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath)
|
||||
try {
|
||||
const { subscribe } = await importModule<typeof import('@parcel/watcher')>('@parcel/watcher', {
|
||||
url: [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(dir => directoryToURL(dir)),
|
||||
})
|
||||
for (const layer of nuxt.options._layers) {
|
||||
const subscription = await subscribe(layer.config.rootDir, onChange, {
|
||||
ignore: ['!nuxt.schema.*'],
|
||||
@ -64,8 +65,9 @@ export default defineNuxtModule({
|
||||
nuxt.hook('close', () => subscription.unsubscribe())
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
logger.warn('Falling back to `chokidar` as `@parcel/watcher` cannot be resolved in your project.')
|
||||
}
|
||||
logger.warn('Falling back to `chokidar` as `@parcel/watcher` cannot be resolved in your project.')
|
||||
}
|
||||
|
||||
const isIgnored = createIsIgnored(nuxt)
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { resolvePackageJSON } from 'pkg-types'
|
||||
import { resolvePath as _resolvePath } from 'mlly'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { dirname } from 'pathe'
|
||||
import { directoryToURL, tryUseNuxt } from '@nuxt/kit'
|
||||
|
||||
export async function resolveTypePath (path: string, subpath: string, searchPaths = tryUseNuxt()?.options.modulesDir) {
|
||||
try {
|
||||
const r = await _resolvePath(path, { url: searchPaths?.map(d => directoryToURL(d)), conditions: ['types', 'import', 'require'] })
|
||||
const r = resolveModulePath(path, {
|
||||
from: searchPaths?.map(d => directoryToURL(d)),
|
||||
conditions: ['types', 'import', 'require'],
|
||||
})
|
||||
if (subpath) {
|
||||
return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, '')
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { addComponent, addImportsSources, addPlugin, addTemplate, defineNuxtModule, directoryToURL, tryResolveModule } from '@nuxt/kit'
|
||||
import { addComponent, addImportsSources, addPlugin, addTemplate, defineNuxtModule, directoryToURL } from '@nuxt/kit'
|
||||
import type { NuxtOptions } from '@nuxt/schema'
|
||||
import { resolveModulePath } from 'exsolve'
|
||||
import { distDir } from '../dirs'
|
||||
|
||||
const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body']
|
||||
@ -10,7 +11,7 @@ export default defineNuxtModule<NuxtOptions['unhead']>({
|
||||
name: 'nuxt:meta',
|
||||
configKey: 'unhead',
|
||||
},
|
||||
async setup (options, nuxt) {
|
||||
setup (options, nuxt) {
|
||||
const runtimeDir = resolve(distDir, 'head/runtime')
|
||||
|
||||
// Transpile @unhead/vue
|
||||
@ -53,7 +54,7 @@ export default defineNuxtModule<NuxtOptions['unhead']>({
|
||||
|
||||
// Opt-out feature allowing dependencies using @vueuse/head to work
|
||||
const importPaths = nuxt.options.modulesDir.map(d => directoryToURL(d))
|
||||
const unheadVue = await tryResolveModule('@unhead/vue', importPaths) || '@unhead/vue'
|
||||
const unheadVue = resolveModulePath('@unhead/vue', { try: true, from: importPaths }) || '@unhead/vue'
|
||||
|
||||
addTemplate({
|
||||
filename: 'unhead-plugins.mjs',
|
||||
|
@ -247,6 +247,9 @@ importers:
|
||||
errx:
|
||||
specifier: ^0.1.0
|
||||
version: 0.1.0
|
||||
exsolve:
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
globby:
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0
|
||||
@ -398,6 +401,9 @@ importers:
|
||||
estree-walker:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
exsolve:
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
globby:
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0
|
||||
@ -3808,9 +3814,6 @@ packages:
|
||||
uWebSockets.js:
|
||||
optional: true
|
||||
|
||||
crossws@0.3.2:
|
||||
resolution: {integrity: sha512-S2PpQHRcgYABOS2465b34wqTOn5dbLL+iSvyweJYGGFLDsKq88xrjDXUiEhfYkhWZq1HuS6of3okRHILbkrqxw==}
|
||||
|
||||
crossws@0.3.4:
|
||||
resolution: {integrity: sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==}
|
||||
|
||||
@ -4388,8 +4391,8 @@ packages:
|
||||
resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
exsolve@0.4.2:
|
||||
resolution: {integrity: sha512-sLGq+3R6ISovHNxuENnZ69paPIQm8h2bfeSefXIKvBLNCgh4pc+luHtgTLC4ij2zN6KFm1aMG8efQXcjyNy3lw==}
|
||||
exsolve@0.4.4:
|
||||
resolution: {integrity: sha512-74RiT9i1G0eyFyE9n5f6mdX8+AicZFnhJ0CHB9VrkIl3Sy8vmW49ODbpwevdLswST7fhp3jvfPzD4DArTfjnsA==}
|
||||
|
||||
extend@3.0.2:
|
||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||
@ -10519,7 +10522,7 @@ snapshots:
|
||||
confbox: 0.1.8
|
||||
defu: 6.1.4
|
||||
dotenv: 16.4.7
|
||||
exsolve: 0.4.2
|
||||
exsolve: 0.4.4
|
||||
giget: 2.0.0
|
||||
jiti: 2.4.2
|
||||
ohash: 2.0.5
|
||||
@ -10810,10 +10813,6 @@ snapshots:
|
||||
|
||||
crossws@0.2.4: {}
|
||||
|
||||
crossws@0.3.2:
|
||||
dependencies:
|
||||
uncrypto: 0.1.3
|
||||
|
||||
crossws@0.3.4:
|
||||
dependencies:
|
||||
uncrypto: 0.1.3
|
||||
@ -11550,7 +11549,7 @@ snapshots:
|
||||
|
||||
expect-type@1.1.0: {}
|
||||
|
||||
exsolve@0.4.2: {}
|
||||
exsolve@0.4.4: {}
|
||||
|
||||
extend@3.0.2: {}
|
||||
|
||||
@ -12506,7 +12505,7 @@ snapshots:
|
||||
citty: 0.1.6
|
||||
clipboardy: 4.0.0
|
||||
consola: 3.4.0
|
||||
crossws: 0.3.2
|
||||
crossws: 0.3.4
|
||||
defu: 6.1.4
|
||||
get-port-please: 3.1.2
|
||||
h3: h3-nightly@1.15.1-20250222-111608-d1c00fc
|
||||
|
2
test/fixtures/basic/nuxt.config.ts
vendored
2
test/fixtures/basic/nuxt.config.ts
vendored
@ -35,7 +35,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
})
|
||||
},
|
||||
'~/modules/subpath',
|
||||
'~/custom-modules/subpath',
|
||||
'./modules/test',
|
||||
'~/modules/example',
|
||||
function (_, nuxt) {
|
||||
|
Loading…
Reference in New Issue
Block a user