feat(kit,nuxt,vite): directoryToURL to normalise paths (#30986)

This commit is contained in:
Daniel Roe 2025-02-14 12:14:28 +01:00
parent 9ba51dba6f
commit 62cb4a6ec1
No known key found for this signature in database
GPG Key ID: CBC814C393D93268
15 changed files with 60 additions and 41 deletions

View File

@ -32,6 +32,6 @@ export { addTemplate, addServerTemplate, addTypeTemplate, normalizeTemplate, upd
export { logger, useLogger } from './logger' export { logger, useLogger } from './logger'
// Internal Utils // Internal Utils
export { resolveModule, tryResolveModule, importModule, tryImportModule, requireModule, tryRequireModule } from './internal/esm' export { directoryToURL, resolveModule, tryResolveModule, importModule, tryImportModule, requireModule, tryRequireModule } from './internal/esm'
export type { ImportModuleOptions, ResolveModuleOptions } from './internal/esm' export type { ImportModuleOptions, ResolveModuleOptions } from './internal/esm'
export * from './internal/template' export * from './internal/template'

View File

@ -3,7 +3,13 @@ import { interopDefault, resolvePath, resolvePathSync } from 'mlly'
import { createJiti } from 'jiti' import { createJiti } from 'jiti'
export interface ResolveModuleOptions { export interface ResolveModuleOptions {
/** @deprecated use `url` with URLs pointing at a file - never a directory */
paths?: string | string[] paths?: string | string[]
url?: URL | URL[]
}
export function directoryToURL (dir: string): URL {
return pathToFileURL(dir + '/')
} }
/** /**
@ -12,7 +18,10 @@ export interface ResolveModuleOptions {
* *
* @internal * @internal
*/ */
export async function tryResolveModule (id: string, url: string | string[] = import.meta.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 { try {
return await resolvePath(id, { url }) return await resolvePath(id, { url })
} catch { } catch {
@ -21,7 +30,7 @@ export async function tryResolveModule (id: string, url: string | string[] = imp
} }
export function resolveModule (id: string, options?: ResolveModuleOptions) { export function resolveModule (id: string, options?: ResolveModuleOptions) {
return resolvePathSync(id, { url: options?.paths ?? [import.meta.url] }) return resolvePathSync(id, { url: options?.url ?? options?.paths ?? [import.meta.url] })
} }
export interface ImportModuleOptions extends ResolveModuleOptions { export interface ImportModuleOptions extends ResolveModuleOptions {
@ -30,8 +39,8 @@ export interface ImportModuleOptions extends ResolveModuleOptions {
} }
export async function importModule<T = unknown> (id: string, opts?: ImportModuleOptions) { export async function importModule<T = unknown> (id: string, opts?: ImportModuleOptions) {
const resolvedPath = await resolveModule(id, opts) const resolvedPath = resolveModule(id, opts)
return import(pathToFileURL(resolvedPath).href).then(r => opts?.interopDefault !== false ? interopDefault(r) : r) as Promise<T> return await import(pathToFileURL(resolvedPath).href).then(r => opts?.interopDefault !== false ? interopDefault(r) : r) as Promise<T>
} }
export function tryImportModule<T = unknown> (id: string, opts?: ImportModuleOptions) { export function tryImportModule<T = unknown> (id: string, opts?: ImportModuleOptions) {

View File

@ -9,7 +9,7 @@ import { globby } from 'globby'
import defu from 'defu' import defu from 'defu'
import { basename, join, relative } from 'pathe' import { basename, join, relative } from 'pathe'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
import { tryResolveModule } from '../internal/esm' import { directoryToURL, tryResolveModule } from '../internal/esm'
export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig>, 'overrides'> { export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig>, 'overrides'> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
@ -108,11 +108,12 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
} }
async function loadNuxtSchema (cwd: string) { async function loadNuxtSchema (cwd: string) {
const paths = [cwd] const url = directoryToURL(cwd)
const nuxtPath = await tryResolveModule('nuxt', cwd) ?? await tryResolveModule('nuxt-nightly', cwd) const urls = [url]
const nuxtPath = await tryResolveModule('nuxt', url) ?? await tryResolveModule('nuxt-nightly', url)
if (nuxtPath) { if (nuxtPath) {
paths.unshift(nuxtPath) urls.unshift(pathToFileURL(nuxtPath))
} }
const schemaPath = await tryResolveModule('@nuxt/schema', paths) ?? '@nuxt/schema' const schemaPath = await tryResolveModule('@nuxt/schema', urls) ?? '@nuxt/schema'
return await import(isWindows ? pathToFileURL(schemaPath).href : schemaPath).then(r => r.NuxtConfigSchema) return await import(isWindows ? pathToFileURL(schemaPath).href : schemaPath).then(r => r.NuxtConfigSchema)
} }

View File

@ -1,8 +1,6 @@
import { pathToFileURL } from 'node:url'
import { readPackageJSON, resolvePackageJSON } from 'pkg-types' import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import type { Nuxt } from '@nuxt/schema' import type { Nuxt } from '@nuxt/schema'
import { withTrailingSlash } from 'ufo' import { directoryToURL, importModule, tryImportModule } from '../internal/esm'
import { importModule, tryImportModule } from '../internal/esm'
import { runWithNuxtContext } from '../context' import { runWithNuxtContext } from '../context'
import type { LoadNuxtConfigOptions } from './config' import type { LoadNuxtConfigOptions } from './config'
@ -28,10 +26,10 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
// Apply dev as config override // Apply dev as config override
opts.overrides.dev = !!opts.dev opts.overrides.dev = !!opts.dev
const rootDir = withTrailingSlash(pathToFileURL(opts.cwd!).href) const rootURL = directoryToURL(opts.cwd!)
const nearestNuxtPkg = await Promise.all(['nuxt-nightly', 'nuxt3', 'nuxt', 'nuxt-edge'] const nearestNuxtPkg = await Promise.all(['nuxt-nightly', 'nuxt3', 'nuxt', 'nuxt-edge']
.map(pkg => resolvePackageJSON(pkg, { url: rootDir }).catch(() => null))) .map(pkg => resolvePackageJSON(pkg, { url: rootURL }).catch(() => null)))
.then(r => (r.filter(Boolean) as string[]).sort((a, b) => b.length - a.length)[0]) .then(r => (r.filter(Boolean) as string[]).sort((a, b) => b.length - a.length)[0])
if (!nearestNuxtPkg) { if (!nearestNuxtPkg) {
throw new Error(`Cannot find any nuxt version from ${opts.cwd}`) throw new Error(`Cannot find any nuxt version from ${opts.cwd}`)
@ -41,13 +39,13 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
// Nuxt 3 // Nuxt 3
if (majorVersion && majorVersion >= 3) { if (majorVersion && majorVersion >= 3) {
const { loadNuxt } = await importModule<typeof import('nuxt')>((pkg as any)._name || pkg.name, { paths: rootDir }) const { loadNuxt } = await importModule<typeof import('nuxt')>((pkg as any)._name || pkg.name, { url: rootURL })
const nuxt = await loadNuxt(opts) const nuxt = await loadNuxt(opts)
return nuxt return nuxt
} }
// Nuxt 2 // Nuxt 2
const { loadNuxt } = await tryImportModule<{ loadNuxt: any }>('nuxt-edge', { paths: rootDir }) || await importModule<{ loadNuxt: any }>('nuxt', { paths: rootDir }) const { loadNuxt } = await tryImportModule<{ loadNuxt: any }>('nuxt-edge', { url: rootURL }) || await importModule<{ loadNuxt: any }>('nuxt', { url: rootURL })
const nuxt = await loadNuxt({ const nuxt = await loadNuxt({
rootDir: opts.cwd, rootDir: opts.cwd,
for: opts.dev ? 'dev' : 'build', for: opts.dev ? 'dev' : 'build',
@ -73,15 +71,15 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
} }
export async function buildNuxt (nuxt: Nuxt): Promise<any> { export async function buildNuxt (nuxt: Nuxt): Promise<any> {
const rootDir = withTrailingSlash(pathToFileURL(nuxt.options.rootDir).href) const rootURL = directoryToURL(nuxt.options.rootDir)
// Nuxt 3 // Nuxt 3
if (nuxt.options._majorVersion === 3) { if (nuxt.options._majorVersion === 3) {
const { build } = await tryImportModule<typeof import('nuxt')>('nuxt-nightly', { paths: rootDir }) || await tryImportModule<typeof import('nuxt')>('nuxt3', { paths: rootDir }) || await importModule<typeof import('nuxt')>('nuxt', { paths: rootDir }) const { build } = await tryImportModule<typeof import('nuxt')>('nuxt-nightly', { url: rootURL }) || await tryImportModule<typeof import('nuxt')>('nuxt3', { url: rootURL }) || await importModule<typeof import('nuxt')>('nuxt', { url: rootURL })
return runWithNuxtContext(nuxt, () => build(nuxt)) return runWithNuxtContext(nuxt, () => build(nuxt))
} }
// Nuxt 2 // Nuxt 2
const { build } = await tryImportModule<{ build: any }>('nuxt-edge', { paths: rootDir }) || await importModule<{ build: any }>('nuxt', { paths: rootDir }) const { build } = await tryImportModule<{ build: any }>('nuxt-edge', { url: rootURL }) || await importModule<{ build: any }>('nuxt', { url: rootURL })
return runWithNuxtContext(nuxt, () => build(nuxt)) return runWithNuxtContext(nuxt, () => build(nuxt))
} }

View File

@ -7,6 +7,7 @@ import { createJiti } from 'jiti'
import { parseNodeModulePath, resolve as resolveModule } from 'mlly' import { parseNodeModulePath, resolve as resolveModule } from 'mlly'
import { isRelative } from 'ufo' import { isRelative } from 'ufo'
import { isNuxt2 } from '../compatibility' import { isNuxt2 } from '../compatibility'
import { directoryToURL } from '../internal/esm'
import { useNuxt } from '../context' import { useNuxt } from '../context'
import { resolveAlias, resolvePath } from '../resolve' import { resolveAlias, resolvePath } from '../resolve'
import { logger } from '../logger' import { logger } from '../logger'
@ -107,7 +108,10 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
try { try {
const src = isAbsolute(path) const src = isAbsolute(path)
? pathToFileURL(await resolvePath(path, { fallbackToOriginal: false, extensions: nuxt.options.extensions })).href ? pathToFileURL(await resolvePath(path, { fallbackToOriginal: false, extensions: nuxt.options.extensions })).href
: await resolveModule(path, { url: nuxt.options.modulesDir.map(m => pathToFileURL(m.replace(/\/node_modules\/?$/, ''))), extensions: nuxt.options.extensions }) : await resolveModule(path, {
url: nuxt.options.modulesDir.map(m => directoryToURL(m.replace(/\/node_modules\/?$/, '/'))),
extensions: nuxt.options.extensions,
})
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule nuxtModule = await jiti.import(src, { default: true }) as NuxtModule
resolvedModulePath = fileURLToPath(new URL(src)) resolvedModulePath = fileURLToPath(new URL(src))

View File

@ -4,6 +4,7 @@ import { basename, dirname, isAbsolute, join, normalize, resolve } from 'pathe'
import { globby } from 'globby' import { globby } from 'globby'
import { resolvePath as _resolvePath } from 'mlly' import { resolvePath as _resolvePath } from 'mlly'
import { resolveAlias as _resolveAlias } from 'pathe/utils' import { resolveAlias as _resolveAlias } from 'pathe/utils'
import { directoryToURL } from './internal/esm'
import { tryUseNuxt } from './context' import { tryUseNuxt } from './context'
import { isIgnored } from './ignore' import { isIgnored } from './ignore'
import { toArray } from './utils' import { toArray } from './utils'
@ -201,7 +202,7 @@ async function _resolvePathGranularly (path: string, opts: ResolvePathOptions =
} }
// Try to resolve as module id // Try to resolve as module id
const resolvedModulePath = await _resolvePath(_path, { url: [cwd, ...modulesDir] }).catch(() => null) const resolvedModulePath = await _resolvePath(_path, { url: [cwd, ...modulesDir].map(d => directoryToURL(d)) }).catch(() => null)
if (resolvedModulePath) { if (resolvedModulePath) {
return { return {
path: resolvedModulePath, path: resolvedModulePath,

View File

@ -9,7 +9,7 @@ import { gte } from 'semver'
import { readPackageJSON } from 'pkg-types' import { readPackageJSON } from 'pkg-types'
import { filterInPlace } from './utils' import { filterInPlace } from './utils'
import { tryResolveModule } from './internal/esm' import { directoryToURL, tryResolveModule } from './internal/esm'
import { getDirectory } from './module/install' import { getDirectory } from './module/install'
import { tryUseNuxt, useNuxt } from './context' import { tryUseNuxt, useNuxt } from './context'
import { resolveNuxtModule } from './resolve' import { resolveNuxtModule } from './resolve'
@ -244,6 +244,8 @@ export async function _generateTypes (nuxt: Nuxt) {
tsConfig.compilerOptions.paths ||= {} tsConfig.compilerOptions.paths ||= {}
tsConfig.include ||= [] tsConfig.include ||= []
const importPaths = nuxt.options.modulesDir.map(d => directoryToURL(d))
for (const alias in aliases) { for (const alias in aliases) {
if (excludedAlias.some(re => re.test(alias))) { if (excludedAlias.some(re => re.test(alias))) {
continue continue
@ -251,7 +253,7 @@ export async function _generateTypes (nuxt: Nuxt) {
let absolutePath = resolve(basePath, aliases[alias]!) let absolutePath = resolve(basePath, aliases[alias]!)
let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */) let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
if (!stats) { if (!stats) {
const resolvedModule = await tryResolveModule(aliases[alias]!, nuxt.options.modulesDir) const resolvedModule = await tryResolveModule(aliases[alias]!, importPaths)
if (resolvedModule) { if (resolvedModule) {
absolutePath = resolvedModule absolutePath = resolvedModule
stats = await fsp.stat(resolvedModule).catch(() => null) stats = await fsp.stat(resolvedModule).catch(() => null)

View File

@ -1,7 +1,7 @@
import type { EventType } from '@parcel/watcher' import type { EventType } from '@parcel/watcher'
import type { FSWatcher } from 'chokidar' import type { FSWatcher } from 'chokidar'
import { watch as chokidarWatch } from 'chokidar' import { watch as chokidarWatch } from 'chokidar'
import { createIsIgnored, importModule, isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit' import { createIsIgnored, directoryToURL, importModule, isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { normalize, relative, resolve } from 'pathe' import { normalize, relative, resolve } from 'pathe'
import type { Nuxt, NuxtBuilder } from 'nuxt/schema' import type { Nuxt, NuxtBuilder } from 'nuxt/schema'
@ -196,7 +196,7 @@ async function createParcelWatcher () {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.time('[nuxt] builder:parcel:watch') console.time('[nuxt] builder:parcel:watch')
} }
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir]) const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)))
if (!watcherPath) { if (!watcherPath) {
logger.warn('Falling back to `chokidar-granular` as `@parcel/watcher` cannot be resolved in your project.') logger.warn('Falling back to `chokidar-granular` as `@parcel/watcher` cannot be resolved in your project.')
return false return false
@ -248,7 +248,7 @@ async function bundle (nuxt: Nuxt) {
} }
async function loadBuilder (nuxt: Nuxt, builder: string): Promise<NuxtBuilder> { async function loadBuilder (nuxt: Nuxt, builder: string): Promise<NuxtBuilder> {
const builderPath = await tryResolveModule(builder, [nuxt.options.rootDir, import.meta.url]) const builderPath = await tryResolveModule(builder, [directoryToURL(nuxt.options.rootDir), new URL(import.meta.url)])
if (!builderPath) { if (!builderPath) {
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\``) 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\``)

View File

@ -6,7 +6,7 @@ import { join, normalize, relative, resolve } from 'pathe'
import { createDebugger, createHooks } from 'hookable' import { createDebugger, createHooks } from 'hookable'
import ignore from 'ignore' import ignore from 'ignore'
import type { LoadNuxtOptions } from '@nuxt/kit' import type { LoadNuxtOptions } from '@nuxt/kit'
import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, 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, resolvePath, runWithNuxtContext, tryResolveModule, useNitro } from '@nuxt/kit'
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema' import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
import type { PackageJson } from 'pkg-types' import type { PackageJson } from 'pkg-types'
import { readPackageJSON } from 'pkg-types' import { readPackageJSON } from 'pkg-types'
@ -390,12 +390,13 @@ async function initNuxt (nuxt: Nuxt) {
} }
nuxt.hook('modules:done', async () => { nuxt.hook('modules:done', async () => {
const importPaths = nuxt.options.modulesDir.map(dir => directoryToURL((dir)))
// Add unctx transform // Add unctx transform
addBuildPlugin(UnctxTransformPlugin({ addBuildPlugin(UnctxTransformPlugin({
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
transformerOptions: { transformerOptions: {
...nuxt.options.optimization.asyncTransforms, ...nuxt.options.optimization.asyncTransforms,
helperModule: await tryResolveModule('unctx', nuxt.options.modulesDir) ?? 'unctx', helperModule: await tryResolveModule('unctx', importPaths) ?? 'unctx',
}, },
})) }))

View File

@ -1,7 +1,7 @@
import { parseNodeModulePath, resolvePath } from 'mlly' import { parseNodeModulePath, resolvePath } from 'mlly'
import { isAbsolute, normalize } from 'pathe' import { isAbsolute, normalize } from 'pathe'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import { resolveAlias } from '@nuxt/kit' import { directoryToURL, resolveAlias } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema' import type { Nuxt } from '@nuxt/schema'
import { pkgDir } from '../../dirs' import { pkgDir } from '../../dirs'
@ -37,7 +37,7 @@ export function resolveDeepImportsPlugin (nuxt: Nuxt): Plugin {
const dir = parseNodeModulePath(normalisedImporter).dir || pkgDir const dir = parseNodeModulePath(normalisedImporter).dir || pkgDir
return await this.resolve?.(normalisedId, dir, { skipSelf: true }) ?? await resolvePath(id, { return await this.resolve?.(normalisedId, dir, { skipSelf: true }) ?? await resolvePath(id, {
url: [dir, ...nuxt.options.modulesDir], url: [dir, ...nuxt.options.modulesDir].map(d => directoryToURL(d)),
conditions, conditions,
}).catch(() => { }).catch(() => {
logger.debug('Could not resolve id', id, importer) logger.debug('Could not resolve id', id, importer)

View File

@ -5,7 +5,7 @@ import { resolve } from 'pathe'
import { watch } from 'chokidar' import { watch } from 'chokidar'
import { defu } from 'defu' import { defu } from 'defu'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { createIsIgnored, createResolver, defineNuxtModule, importModule, tryResolveModule } from '@nuxt/kit' import { createIsIgnored, createResolver, defineNuxtModule, directoryToURL, importModule, tryResolveModule } from '@nuxt/kit'
import { generateTypes, resolveSchema as resolveUntypedSchema } from 'untyped' import { generateTypes, resolveSchema as resolveUntypedSchema } from 'untyped'
import type { Schema, SchemaDefinition } from 'untyped' import type { Schema, SchemaDefinition } from 'untyped'
import untypedPlugin from 'untyped/babel-plugin' import untypedPlugin from 'untyped/babel-plugin'
@ -57,7 +57,7 @@ export default defineNuxtModule({
}) })
if (nuxt.options.experimental.watcher === 'parcel') { if (nuxt.options.experimental.watcher === 'parcel') {
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir]) const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir].map(dir => directoryToURL(dir)))
if (watcherPath) { if (watcherPath) {
const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath) const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath)
for (const layer of nuxt.options._layers) { for (const layer of nuxt.options._layers) {

View File

@ -1,11 +1,11 @@
import { resolvePackageJSON } from 'pkg-types' import { resolvePackageJSON } from 'pkg-types'
import { resolvePath as _resolvePath } from 'mlly' import { resolvePath as _resolvePath } from 'mlly'
import { dirname } from 'pathe' import { dirname } from 'pathe'
import { tryUseNuxt } from '@nuxt/kit' import { directoryToURL, tryUseNuxt } from '@nuxt/kit'
export async function resolveTypePath (path: string, subpath: string, searchPaths = tryUseNuxt()?.options.modulesDir) { export async function resolveTypePath (path: string, subpath: string, searchPaths = tryUseNuxt()?.options.modulesDir) {
try { try {
const r = await _resolvePath(path, { url: searchPaths, conditions: ['types', 'import', 'require'] }) const r = await _resolvePath(path, { url: searchPaths?.map(d => directoryToURL(d)), conditions: ['types', 'import', 'require'] })
if (subpath) { if (subpath) {
return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, '') return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, '')
} }

View File

@ -1,5 +1,5 @@
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { addComponent, addImportsSources, addPlugin, addTemplate, defineNuxtModule, tryResolveModule } from '@nuxt/kit' import { addComponent, addImportsSources, addPlugin, addTemplate, defineNuxtModule, directoryToURL, tryResolveModule } from '@nuxt/kit'
import type { NuxtOptions } from '@nuxt/schema' import type { NuxtOptions } from '@nuxt/schema'
import { distDir } from '../dirs' import { distDir } from '../dirs'
@ -52,7 +52,8 @@ export default defineNuxtModule<NuxtOptions['unhead']>({
}) })
// Opt-out feature allowing dependencies using @vueuse/head to work // Opt-out feature allowing dependencies using @vueuse/head to work
const unheadVue = await tryResolveModule('@unhead/vue', nuxt.options.modulesDir) || '@unhead/vue' const importPaths = nuxt.options.modulesDir.map(d => directoryToURL(d))
const unheadVue = await tryResolveModule('@unhead/vue', importPaths) || '@unhead/vue'
if (nuxt.options.experimental.polyfillVueUseHead) { if (nuxt.options.experimental.polyfillVueUseHead) {
// backwards compatibility // backwards compatibility
nuxt.options.alias['@vueuse/head'] = unheadVue nuxt.options.alias['@vueuse/head'] = unheadVue

View File

@ -1,5 +1,5 @@
import { existsSync } from 'node:fs' import { existsSync } from 'node:fs'
import { addBuildPlugin, addTemplate, addTypeTemplate, createIsIgnored, defineNuxtModule, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit' import { addBuildPlugin, addTemplate, addTypeTemplate, createIsIgnored, defineNuxtModule, directoryToURL, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
import { isAbsolute, join, normalize, relative, resolve } from 'pathe' import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
import type { Import, Unimport } from 'unimport' import type { Import, Unimport } from 'unimport'
import { createUnimport, scanDirExports, toExports } from 'unimport' import { createUnimport, scanDirExports, toExports } from 'unimport'
@ -181,6 +181,8 @@ function addDeclarationTemplates (ctx: Unimport, options: Partial<ImportsOptions
const SUPPORTED_EXTENSION_RE = new RegExp(`\\.(${nuxt.options.extensions.map(i => i.replace('.', '')).join('|')})$`) const SUPPORTED_EXTENSION_RE = new RegExp(`\\.(${nuxt.options.extensions.map(i => i.replace('.', '')).join('|')})$`)
const importPaths = nuxt.options.modulesDir.map(dir => directoryToURL(dir))
async function cacheImportPaths (imports: Import[]) { async function cacheImportPaths (imports: Import[]) {
const importSource = Array.from(new Set(imports.map(i => i.from))) const importSource = Array.from(new Set(imports.map(i => i.from)))
// skip relative import paths for node_modules that are explicitly installed // skip relative import paths for node_modules that are explicitly installed
@ -190,7 +192,7 @@ function addDeclarationTemplates (ctx: Unimport, options: Partial<ImportsOptions
} }
let path = resolveAlias(from) let path = resolveAlias(from)
if (!isAbsolute(path)) { if (!isAbsolute(path)) {
path = await tryResolveModule(from, nuxt.options.modulesDir).then(async (r) => { path = await tryResolveModule(from, importPaths).then(async (r) => {
if (!r) { return r } if (!r) { return r }
const { dir, name } = parseNodeModulePath(r) const { dir, name } = parseNodeModulePath(r)

View File

@ -2,7 +2,7 @@ import { resolve } from 'pathe'
import * as vite from 'vite' import * as vite from 'vite'
import vuePlugin from '@vitejs/plugin-vue' import vuePlugin from '@vitejs/plugin-vue'
import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import { logger, resolvePath, tryImportModule } from '@nuxt/kit' import { directoryToURL, logger, resolvePath, tryImportModule } from '@nuxt/kit'
import { joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo' import { joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo'
import type { ViteConfig } from '@nuxt/schema' import type { ViteConfig } from '@nuxt/schema'
import type { PackageJson } from 'pkg-types' import type { PackageJson } from 'pkg-types'
@ -117,7 +117,7 @@ export async function buildServer (ctx: ViteBuildContext) {
if (!ctx.nuxt.options.dev) { if (!ctx.nuxt.options.dev) {
const runtimeDependencies = await tryImportModule<PackageJson>('nitropack/package.json', { const runtimeDependencies = await tryImportModule<PackageJson>('nitropack/package.json', {
paths: ctx.nuxt.options.modulesDir, url: ctx.nuxt.options.modulesDir.map(d => directoryToURL(d)),
})?.then(r => r?.dependencies ? Object.keys(r.dependencies) : []).catch(() => []) || [] })?.then(r => r?.dependencies ? Object.keys(r.dependencies) : []).catch(() => []) || []
if (Array.isArray(serverConfig.ssr!.external)) { if (Array.isArray(serverConfig.ssr!.external)) {
serverConfig.ssr!.external.push( serverConfig.ssr!.external.push(