diff --git a/packages/bridge/package.json b/packages/bridge/package.json index efc09d4cc4..6ea35cf189 100644 --- a/packages/bridge/package.json +++ b/packages/bridge/package.json @@ -17,6 +17,7 @@ "@nuxt/postcss8": "^1.1.3", "@vue/composition-api": "^1.2.2", "acorn": "^8.5.0", + "enhanced-resolve": "^5.8.3", "estree-walker": "^2.0.2", "fs-extra": "^10.0.0", "magic-string": "^0.25.7", diff --git a/packages/bridge/src/module.ts b/packages/bridge/src/module.ts index d5dfe35b0e..51c9465028 100644 --- a/packages/bridge/src/module.ts +++ b/packages/bridge/src/module.ts @@ -2,6 +2,7 @@ import { defineNuxtModule, installModule } from '@nuxt/kit' import { setupNitroBridge } from './nitro' import { setupAppBridge } from './app' import { setupCAPIBridge } from './capi' +import { setupBetterResolve } from './resolve' export default defineNuxtModule({ name: 'nuxt-bridge', @@ -13,7 +14,8 @@ export default defineNuxtModule({ capi: {}, // TODO: Remove from 2.16 postcss8: true, - swc: true + swc: true, + resolve: true }, async setup (opts, nuxt) { if (opts.nitro) { @@ -37,5 +39,8 @@ export default defineNuxtModule({ if (opts.swc) { await installModule(nuxt, require.resolve('nuxt-swc')) } + if (opts.resolve) { + setupBetterResolve() + } } }) diff --git a/packages/bridge/src/resolve.ts b/packages/bridge/src/resolve.ts new file mode 100644 index 0000000000..416dbc3283 --- /dev/null +++ b/packages/bridge/src/resolve.ts @@ -0,0 +1,79 @@ +import fs from 'fs' +import { promisify } from 'util' +import defu from 'defu' + +import { CachedInputFileSystem, ResolveContext, ResolverFactory } from 'enhanced-resolve' +import { ResolveOptions } from 'webpack/types' +import { extendWebpackConfig, useNuxt } from '@nuxt/kit' + +type UserResolveOptions = Parameters[0] +type ResolverOptions = Omit & { fileSystem?: CachedInputFileSystem } + +const DEFAULTS: UserResolveOptions = { + fileSystem: new CachedInputFileSystem(fs, 4000), + extensions: ['.ts', '.mjs', '.cjs', '.js', '.json'], + mainFields: ['module', 'main'] +} + +// Abstracted resolver factory which can be used in rollup, webpack, etc. +const createResolver = (resolveOptions: ResolverOptions) => { + const options = defu(resolveOptions, DEFAULTS) as UserResolveOptions + const resolver = ResolverFactory.createResolver(options) + + const root = options.roots?.[0] || '.' + + const promisifiedResolve = promisify(resolver.resolve.bind(resolver)) as (context: object, path: string, request: string, resolveContext: ResolveContext) => Promise + + const resolve = (id: string, importer?: string) => promisifiedResolve({}, importer || root, id, {}) + + return { resolve, resolver } +} + +// Webpack plugin to add first-level support for subpath exports, etc. +class EnhancedResolverPlugin { + resolver: ReturnType + + constructor (options: ResolverOptions) { + this.resolver = createResolver(options) + } + + apply (defaultResolver: any) { + const enhancedResolver = this.resolver + + defaultResolver.getHook('resolve').tapPromise('EnhancedResolverPlugin', async (request) => { + const id = request.request + // Fall back to default webpack4 resolver if not a node_modules import + if (!id || !defaultResolver.isModule(id)) { return } + + const importer = request.context?.issuer + try { + const result = await enhancedResolver.resolve(id, importer) + // Fall back to default webpack4 resolver if we can't resolve id + if (!result) { return } + request.path = result + return request + } catch { + // Fall back to default webpack4 resolver in the event of error + } + }) + } +} + +export function setupBetterResolve () { + const nuxt = useNuxt() + + extendWebpackConfig((config) => { + const isServer = config.name === 'server' + + config.resolve = config.resolve || {} + config.resolve.plugins = config.resolve.plugins || [] + + config.resolve.plugins.push(new EnhancedResolverPlugin({ + conditionNames: ['import', ...isServer ? ['node'] : []], + alias: config.resolve.alias, + modules: config.resolve.modules, + plugins: config.resolve.plugins as Array>, + roots: config.resolve.roots || [nuxt.options.rootDir] + })) + }) +} diff --git a/yarn.lock b/yarn.lock index 55ff7749d1..d1509124ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1443,6 +1443,7 @@ __metadata: "@types/node-fetch": ^3.0.2 "@vue/composition-api": ^1.2.2 acorn: ^8.5.0 + enhanced-resolve: ^5.8.3 estree-walker: ^2.0.2 fs-extra: ^10.0.0 magic-string: ^0.25.7