Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(kit,nuxi): resolve module paths using node algorithm #19537

Merged
merged 8 commits into from
Mar 10, 2023
10 changes: 9 additions & 1 deletion packages/kit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,13 @@ export * from './logger'

// Internal Utils
// TODO
export * from './internal/cjs'
export {
resolveModule,
requireModule,
importModule,
tryImportModule,
tryRequireModule
} from './internal/cjs'
export type { ResolveModuleOptions, RequireModuleOptions } from './internal/cjs'
export { tryResolveModule } from './internal/esm'
export * from './internal/template'
7 changes: 1 addition & 6 deletions packages/kit/src/internal/cjs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pathToFileURL } from 'node:url'
import { join, normalize } from 'pathe'
import { normalize } from 'pathe'
import { interopDefault } from 'mlly'
import jiti from 'jiti'

Expand Down Expand Up @@ -82,11 +82,6 @@ export function getRequireCacheItem (id: string) {
}
}

/** Resolve the `package.json` file for a given module. */
export function requireModulePkg (id: string, opts: RequireModuleOptions = {}) {
return requireModule(join(id, 'package.json'), opts)
}

/** @deprecated Do not use CJS utils */
export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
return normalize(_require.resolve(id, {
Expand Down
25 changes: 25 additions & 0 deletions packages/kit/src/internal/esm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { pathToFileURL } from 'node:url'
import { interopDefault, resolvePath } from 'mlly'

/**
* Resolve a module from a given root path using an algorithm patterned on
* the upcoming `import.meta.resolve`. It returns a file URL
*
* @internal
*/
export async function tryResolveModule (id: string, url = import.meta.url) {
try {
return await resolvePath(id, { url })
} catch { }
}

export async function importModule (id: string, url = import.meta.url) {
const resolvedPath = await resolvePath(id, { url })
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
}

export function tryImportModule (id: string, url = import.meta.url) {
try {
return importModule(id, url).catch(() => undefined)
} catch { }
}
18 changes: 9 additions & 9 deletions packages/kit/src/loader/nuxt.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { pathToFileURL } from 'node:url'
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import type { Nuxt } from '@nuxt/schema'
import type { RequireModuleOptions } from '../internal/cjs'
import { importModule, tryImportModule } from '../internal/cjs'
import { importModule, tryImportModule } from '../internal/esm'
import type { LoadNuxtConfigOptions } from './config'

export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
Expand All @@ -23,8 +23,6 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
opts.cwd = opts.cwd || opts.rootDir
opts.overrides = opts.overrides || opts.config || {}

const resolveOpts: RequireModuleOptions = { paths: opts.cwd }

// Apply dev as config override
opts.overrides.dev = !!opts.dev

Expand All @@ -37,15 +35,17 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
const pkg = await readPackageJSON(nearestNuxtPkg)
const majorVersion = parseInt((pkg.version || '').split('.')[0])

const rootDir = pathToFileURL(opts.cwd || process.cwd()).href

// Nuxt 3
if (majorVersion === 3) {
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, resolveOpts)
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, rootDir)
const nuxt = await loadNuxt(opts)
return nuxt
}

// Nuxt 2
const { loadNuxt } = await tryImportModule('nuxt-edge', resolveOpts) || await importModule('nuxt', resolveOpts)
const { loadNuxt } = await tryImportModule('nuxt-edge', rootDir) || await importModule('nuxt', rootDir)
const nuxt = await loadNuxt({
rootDir: opts.cwd,
for: opts.dev ? 'dev' : 'build',
Expand All @@ -58,15 +58,15 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
}

export async function buildNuxt (nuxt: Nuxt): Promise<any> {
const resolveOpts: RequireModuleOptions = { paths: nuxt.options.rootDir }
const rootDir = pathToFileURL(nuxt.options.rootDir).href

// Nuxt 3
if (nuxt.options._majorVersion === 3) {
const { build } = await tryImportModule('nuxt3', resolveOpts) || await importModule('nuxt', resolveOpts)
const { build } = await tryImportModule('nuxt3', rootDir) || await importModule('nuxt', rootDir)
return build(nuxt)
}

// Nuxt 2
const { build } = await tryImportModule('nuxt-edge', resolveOpts) || await importModule('nuxt', resolveOpts)
const { build } = await tryImportModule('nuxt-edge', rootDir) || await importModule('nuxt', rootDir)
return build(nuxt)
}
5 changes: 3 additions & 2 deletions packages/kit/src/module/install.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Nuxt, NuxtModule } from '@nuxt/schema'
import { useNuxt } from '../context'
import { resolveModule, requireModule, importModule } from '../internal/cjs'
import { resolveModule, requireModule } from '../internal/cjs'
import { importModule } from '../internal/esm'
import { resolveAlias } from '../resolve'

/** Installs a module on a Nuxt instance. */
Expand Down Expand Up @@ -38,7 +39,7 @@ async function normalizeModule (nuxtModule: string | NuxtModule, inlineOptions?:
const isESM = _src.endsWith('.mjs')

try {
nuxtModule = isESM ? await importModule(_src) : requireModule(_src)
nuxtModule = isESM ? await importModule(_src, nuxt.options.rootDir) : requireModule(_src)
} catch (error: unknown) {
console.error(`Error while requiring module \`${nuxtModule}\`: ${error}`)
throw error
Expand Down
4 changes: 2 additions & 2 deletions packages/nuxi/src/commands/build-module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { execa } from 'execa'
import consola from 'consola'
import { resolve } from 'pathe'
import { tryResolveModule } from '../utils/cjs'
import { tryResolveModule } from '../utils/esm'
import { defineNuxtCommand } from './index'

const MODULE_BUILDER_PKG = '@nuxt/module-builder'
Expand All @@ -15,7 +15,7 @@ export default defineNuxtCommand({
async invoke (args) {
// Find local installed version
const rootDir = resolve(args._[0] || '.')
const hasLocal = tryResolveModule(`${MODULE_BUILDER_PKG}/package.json`, rootDir)
const hasLocal = await tryResolveModule(`${MODULE_BUILDER_PKG}/package.json`, rootDir)

const execArgs = Object.entries({
'--stub': args.stub
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxi/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { setupDotenv } from 'c12'
import { showBanner, showVersions } from '../utils/banner'
import { writeTypes } from '../utils/prepare'
import { loadKit } from '../utils/kit'
import { importModule } from '../utils/cjs'
import { importModule } from '../utils/esm'
import { overrideEnv } from '../utils/env'
import { writeNuxtManifest, loadNuxtManifest, cleanupNuxtDirs } from '../utils/nuxt'
import { defineNuxtCommand } from './index'
Expand Down
4 changes: 2 additions & 2 deletions packages/nuxi/src/commands/typecheck.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { execa } from 'execa'
import { resolve } from 'pathe'
import { tryResolveModule } from '../utils/cjs'
import { tryResolveModule } from '../utils/esm'

import { loadKit } from '../utils/kit'
import { writeTypes } from '../utils/prepare'
Expand Down Expand Up @@ -31,7 +31,7 @@ export default defineNuxtCommand({
await nuxt.close()

// Prefer local install if possible
const hasLocalInstall = tryResolveModule('typescript', rootDir) && tryResolveModule('vue-tsc/package.json', rootDir)
const hasLocalInstall = await tryResolveModule('typescript', rootDir) && await tryResolveModule('vue-tsc/package.json', rootDir)
if (hasLocalInstall) {
await execa('vue-tsc', ['--noEmit'], { preferLocal: true, stdio: 'inherit', cwd: rootDir })
} else {
Expand Down
12 changes: 0 additions & 12 deletions packages/nuxi/src/utils/cjs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createRequire } from 'node:module'
import { pathToFileURL } from 'node:url'
import { normalize, dirname } from 'pathe'

export function getModulePaths (paths?: string | string[]): string[] {
Expand All @@ -21,12 +20,6 @@ export function resolveModule (id: string, paths?: string | string[]) {
return normalize(_require.resolve(id, { paths: getModulePaths(paths) }))
}

export function tryResolveModule (id: string, paths?: string | string[]) {
try {
return resolveModule(id, paths)
} catch { return null }
}

export function requireModule (id: string, paths?: string | string[]) {
return _require(resolveModule(id, paths))
}
Expand All @@ -35,11 +28,6 @@ export function tryRequireModule (id: string, paths?: string | string[]) {
try { return requireModule(id, paths) } catch { return null }
}

export function importModule (id: string, paths?: string | string[]) {
const resolvedPath = resolveModule(id, paths)
return import(pathToFileURL(resolvedPath).href)
}

export function getNearestPackage (id: string, paths?: string | string[]) {
while (dirname(id) !== id) {
try { return requireModule(id + '/package.json', paths) } catch { }
Expand Down
19 changes: 19 additions & 0 deletions packages/nuxi/src/utils/esm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { pathToFileURL } from 'node:url'
import { interopDefault, resolvePath } from 'mlly'

export async function tryResolveModule (id: string, url = import.meta.url) {
try {
return await resolvePath(id, { url })
} catch { }
}

export async function importModule (id: string, url = import.meta.url) {
const resolvedPath = await resolvePath(id, { url })
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
}

export function tryImportModule (id: string, url = import.meta.url) {
try {
return importModule(id, url).catch(() => undefined)
} catch { }
}
2 changes: 1 addition & 1 deletion packages/nuxi/src/utils/kit.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { importModule } from './cjs'
import { importModule } from './esm'

export const loadKit = async (rootDir: string): Promise<typeof import('@nuxt/kit')> => {
try {
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
)
}
if (!app.mainComponent) {
app.mainComponent = tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')
app.mainComponent = (await tryResolveModule('@nuxt/ui-templates/templates/welcome.vue'))!
}

// Resolve root component
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ async function initNuxt (nuxt: Nuxt) {
addComponent({
name: 'NuxtWelcome',
priority: 10, // built-in that we do not expect the user to override
filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')!
filePath: (await tryResolveModule('@nuxt/ui-templates/templates/welcome.vue'))!
})

addComponent({
Expand Down
4 changes: 2 additions & 2 deletions packages/nuxt/src/head/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default defineNuxtModule({
meta: {
name: 'head'
},
setup (options, nuxt) {
async setup (options, nuxt) {
const runtimeDir = resolve(distDir, 'head/runtime')

// Transpile @unhead/vue
Expand Down Expand Up @@ -53,7 +53,7 @@ export default defineNuxtModule({
// Opt-out feature allowing dependencies using @vueuse/head to work
if (nuxt.options.experimental.polyfillVueUseHead) {
// backwards compatibility
nuxt.options.alias['@vueuse/head'] = tryResolveModule('@unhead/vue') || '@unhead/vue'
nuxt.options.alias['@vueuse/head'] = await tryResolveModule('@unhead/vue') || '@unhead/vue'
addPlugin({ src: resolve(runtimeDir, 'plugins/vueuse-head-polyfill') })
}

Expand Down