Repository: nuxt/nuxt
File: packages/kit/src/module/install.ts (TypeScript)

Code before:
/** Installs a module on a Nuxt instance. */
export async function installModule<
  T extends string | NuxtModule,
  Config extends Extract<NonNullable<NuxtConfig['modules']>[number], [T, any]>,
> (moduleToInstall: T, inlineOptions?: [Config] extends [never] ? any : Config[1], nuxt: Nuxt = useNuxt()) {
  const { nuxtModule, buildTimeModuleMeta, resolvedModulePath } = await loadNuxtModuleInstance(moduleToInstall, nuxt)

  const localLayerModuleDirs: string[] = []
  for (const l of nuxt.options._layers) {
    const srcDir = l.config.srcDir || l.cwd
    if (!NODE_MODULES_RE.test(srcDir)) {
      localLayerModuleDirs.push(resolve(srcDir, l.config?.dir?.modules || 'modules').replace(/\/?$/, '/'))
    }
  }

  const meta = await nuxtModule?.getMeta?.()

  if (meta?.name && meta?.version) {
    const rc = readRc({ dir: nuxt.options.rootDir, name: '.nuxtrc' })
    const previousVersion = rc?.setups?.[meta.name]

    if (!previousVersion) {
      await nuxtModule?.onInstall?.(nuxt)
    } else if (semver.gt(meta.version, previousVersion)) {
      await nuxtModule?.onUpgrade?.(inlineOptions, nuxt, previousVersion)
    }

    updateRc(
      { setups: { [meta.name]: meta?.version } },
      { dir: nuxt.options.rootDir, name: '.nuxtrc' },
    )
  }

  // Call module
  const res = nuxt.options.experimental?.debugModuleMutation && nuxt._asyncLocalStorageModule
    ? await nuxt._asyncLocalStorageModule.run(nuxtModule, () => nuxtModule(inlineOptions || {}, nuxt)) ?? {}
    : await nuxtModule(inlineOptions || {}, nuxt) ?? {}
  if (res === false /* setup aborted */) {
    return
  }

  const modulePath = resolvedModulePath || moduleToInstall
  if (typeof modulePath === 'string') {
    const parsed = parseNodeModulePath(modulePath)
    const moduleRoot = parsed.dir
      ? parsed.dir + parsed.name
      : await resolvePackageJSON(modulePath, { try: true }).then(r => r ? dirname(r) : modulePath)
    nuxt.options.build.transpile.push(normalizeModuleTranspilePath(moduleRoot))
    const directory = moduleRoot.replace(/\/?$/, '/')
    if (moduleRoot !== moduleToInstall && !localLayerModuleDirs.some(dir => directory.startsWith(dir))) {
      nuxt.options.modulesDir.push(join(moduleRoot, 'node_modules'))

Review comment (bug):
Maybe try catching error here and tell the user what went wrong, then exit later?

Code after:

/** Installs a module on a Nuxt instance. */
export async function installModule<
  T extends string | NuxtModule,
  Config extends Extract<NonNullable<NuxtConfig['modules']>[number], [T, any]>,
> (moduleToInstall: T, inlineOptions?: [Config] extends [never] ? any : Config[1], nuxt: Nuxt = useNuxt()) {
  const { nuxtModule, buildTimeModuleMeta, resolvedModulePath } = await loadNuxtModuleInstance(moduleToInstall, nuxt)

  const localLayerModuleDirs: string[] = []
  for (const l of nuxt.options._layers) {
    const srcDir = l.config.srcDir || l.cwd
    if (!NODE_MODULES_RE.test(srcDir)) {
      localLayerModuleDirs.push(resolve(srcDir, l.config?.dir?.modules || 'modules').replace(/\/?$/, '/'))
    }
  }

  // module lifecycle hooks
  if (nuxtModule.getMeta && (nuxtModule.onInstall || nuxtModule.onUpgrade)) {
    const meta = await nuxtModule.getMeta?.()

    if (meta && meta.name && meta.version) {
      const rc = readRc({ dir: nuxt.options.rootDir, name: '.nuxtrc' })
      const previousVersion = rc?.setups?.[meta.name]

      try {
        if (!previousVersion) {
          await nuxtModule.onInstall?.(nuxt)
        } else if (semver.gt(meta.version, previousVersion)) {
          await nuxtModule.onUpgrade?.(inlineOptions, nuxt, previousVersion)
        }
        if (previousVersion !== meta.version) {
          updateRc(
            { setups: { [meta.name]: meta?.version } },
            { dir: nuxt.options.rootDir, name: '.nuxtrc' },
          )
        }
      } catch (e) {
        logger.error(
          `Error while executing ${!previousVersion ? 'install' : 'upgrade'} hook for module \`${meta.name}\`: ${e}`,
        )
      }
    }
  }

  // Call module
  const res = nuxt.options.experimental?.debugModuleMutation && nuxt._asyncLocalStorageModule
    ? await nuxt._asyncLocalStorageModule.run(nuxtModule, () => nuxtModule(inlineOptions || {}, nuxt)) ?? {}
    : await nuxtModule(inlineOptions || {}, nuxt) ?? {}
  if (res === false /* setup aborted */) {
    return
  }