feat(nuxt): resolve unresolved paths within node_modules (#22478)

This commit is contained in:
Daniel Roe 2023-08-07 23:05:29 +01:00 committed by GitHub
parent ffd0223583
commit b5c9a81d68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 57 additions and 6 deletions

View File

@ -25,6 +25,10 @@ export async function installModule (moduleToInstall: string | NuxtModule, inlin
if (typeof moduleToInstall === 'string') { if (typeof moduleToInstall === 'string') {
nuxt.options.build.transpile.push(normalizeModuleTranspilePath(moduleToInstall)) nuxt.options.build.transpile.push(normalizeModuleTranspilePath(moduleToInstall))
const directory = getDirectory(moduleToInstall)
if (directory !== moduleToInstall) {
nuxt.options.modulesDir.push(getDirectory(moduleToInstall))
}
} }
nuxt.options._installedModules = nuxt.options._installedModules || [] nuxt.options._installedModules = nuxt.options._installedModules || []
@ -37,15 +41,19 @@ export async function installModule (moduleToInstall: string | NuxtModule, inlin
// --- Internal --- // --- Internal ---
export const normalizeModuleTranspilePath = (p: string) => { function getDirectory (p: string) {
try { try {
// we need to target directories instead of module file paths themselves // we need to target directories instead of module file paths themselves
// /home/user/project/node_modules/module/index.js -> /home/user/project/node_modules/module // /home/user/project/node_modules/module/index.js -> /home/user/project/node_modules/module
p = isAbsolute(p) && lstatSync(p).isFile() ? dirname(p) : p return isAbsolute(p) && lstatSync(p).isFile() ? dirname(p) : p
} catch (e) { } catch (e) {
// maybe the path is absolute but does not exist, allow this to bubble up // maybe the path is absolute but does not exist, allow this to bubble up
} }
return p.split('node_modules/').pop() as string return p
}
export const normalizeModuleTranspilePath = (p: string) => {
return getDirectory(p).split('node_modules/').pop() as string
} }
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) { export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {

View File

@ -90,7 +90,7 @@ async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
) )
} }
if (!app.mainComponent) { if (!app.mainComponent) {
app.mainComponent = (await tryResolveModule('@nuxt/ui-templates/templates/welcome.vue', nuxt.options.modulesDir))! app.mainComponent = (await tryResolveModule('@nuxt/ui-templates/templates/welcome.vue', nuxt.options.modulesDir)) ?? '@nuxt/ui-templates/templates/welcome.vue'
} }
// Resolve root component // Resolve root component

View File

@ -25,6 +25,7 @@ import { addModuleTranspiles } from './modules'
import { initNitro } from './nitro' import { initNitro } from './nitro'
import schemaModule from './schema' import schemaModule from './schema'
import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata' import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata'
import { resolveDeepImportsPlugin } from './plugins/resolve-deep-imports'
export function createNuxt (options: NuxtOptions): Nuxt { export function createNuxt (options: NuxtOptions): Nuxt {
const hooks = createHooks<NuxtHooks>() const hooks = createHooks<NuxtHooks>()
@ -86,6 +87,9 @@ async function initNuxt (nuxt: Nuxt) {
addVitePlugin(() => ImportProtectionPlugin.vite(config)) addVitePlugin(() => ImportProtectionPlugin.vite(config))
addWebpackPlugin(() => ImportProtectionPlugin.webpack(config)) addWebpackPlugin(() => ImportProtectionPlugin.webpack(config))
// add resolver for modules used in virtual files
addVitePlugin(() => resolveDeepImportsPlugin(nuxt))
if (nuxt.options.experimental.localLayerAliases) { if (nuxt.options.experimental.localLayerAliases) {
// Add layer aliasing support for ~, ~~, @ and @@ aliases // Add layer aliasing support for ~, ~~, @ and @@ aliases
addVitePlugin(() => LayerAliasingPlugin.vite({ addVitePlugin(() => LayerAliasingPlugin.vite({

View File

@ -0,0 +1,30 @@
import { parseNodeModulePath, resolvePath } from 'mlly'
import { isAbsolute, normalize } from 'pathe'
import type { Plugin } from 'vite'
import { resolveAlias } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import { pkgDir } from '../../dirs'
export function resolveDeepImportsPlugin (nuxt: Nuxt): Plugin {
return {
name: 'nuxt:resolve-bare-imports',
enforce: 'post',
async resolveId (id, importer, options) {
if (!importer || isAbsolute(id) || !isAbsolute(importer) || id.startsWith('virtual:') || id.startsWith('/__skip_vite')) {
return
}
id = normalize(id)
id = resolveAlias(id, nuxt.options.alias)
const { dir } = parseNodeModulePath(importer)
return await this.resolve?.(id, dir || pkgDir, { skipSelf: true }) ?? await resolvePath(id, {
url: [dir || pkgDir, ...nuxt.options.modulesDir],
// TODO: respect nitro runtime conditions
conditions: options.ssr ? ['node', 'import', 'require'] : ['import', 'require']
}).catch(() => {
console.log('[nuxt] Could not resolve id', id, importer)
return null
})
}
}
}

View File

@ -58,7 +58,13 @@ export async function buildServer (ctx: ViteBuildContext) {
} }
}, },
ssr: { ssr: {
external: ['#internal/nitro', '#internal/nitro/utils', ...nitroDependencies], external: [
'#internal/nitro', '#internal/nitro/utils',
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
'unhead', '@unhead/ssr', '@unhead/vue', 'unctx', 'h3', 'devalue', '@nuxt/devalue', 'radix3', 'unstorage',
// dependencies we might share with nitro - these can be inlined (if necessary) in the nitro build
...nitroDependencies
],
noExternal: [ noExternal: [
...transpile({ isServer: true, isDev: ctx.nuxt.options.dev }), ...transpile({ isServer: true, isDev: ctx.nuxt.options.dev }),
'/__vue-jsx', '/__vue-jsx',

View File

@ -132,7 +132,10 @@ function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = ne
node.shouldExternalize = async (id: string) => { node.shouldExternalize = async (id: string) => {
const result = await isExternal(id) const result = await isExternal(id)
if (result?.external) { if (result?.external) {
return resolveModule(result.id, { url: ctx.nuxt.options.modulesDir }) return resolveModule(result.id, {
url: ctx.nuxt.options.modulesDir,
conditions: ['node', 'import', 'require']
}).catch(() => false)
} }
return false return false
} }