diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index aea33408d8..a7f00a161d 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -75,6 +75,7 @@ "compatx": "^0.1.8", "consola": "^3.2.3", "cookie-es": "^1.2.2", + "impound": "^0.1.0", "defu": "^6.1.4", "destr": "^2.0.3", "devalue": "^5.0.0", diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 96c21242a0..72ea099e49 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -11,12 +11,13 @@ import escapeRE from 'escape-string-regexp' import { defu } from 'defu' import { dynamicEventHandler } from 'h3' import { isWindows } from 'std-env' +import { ImpoundPlugin } from 'impound' import type { Nuxt, NuxtOptions } from 'nuxt/schema' import { version as nuxtVersion } from '../../package.json' import { distDir } from '../dirs' import { toArray } from '../utils' import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon' -import { ImportProtectionPlugin, nuxtImportProtections } from './plugins/import-protection' +import { nuxtImportProtections } from './plugins/import-protection' const logLevelMapReverse = { silent: 0, @@ -358,9 +359,8 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) { nitroConfig.rollupConfig!.plugins = await nitroConfig.rollupConfig!.plugins || [] nitroConfig.rollupConfig!.plugins = toArray(nitroConfig.rollupConfig!.plugins) nitroConfig.rollupConfig!.plugins!.push( - ImportProtectionPlugin.rollup({ - rootDir: nuxt.options.rootDir, - modulesDir: nuxt.options.modulesDir, + ImpoundPlugin.rollup({ + cwd: nuxt.options.rootDir, patterns: nuxtImportProtections(nuxt, { isNitro: true }), exclude: [/core[\\/]runtime[\\/]nitro[\\/]renderer/], }), diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index f56dbf0cab..51b70f72d9 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -15,13 +15,14 @@ import { colorize } from 'consola/utils' import { updateConfig } from 'c12/update' import { formatDate, resolveCompatibilityDatesFromEnv } from 'compatx' import type { DateString } from 'compatx' - import escapeRE from 'escape-string-regexp' import { withTrailingSlash, withoutLeadingSlash } from 'ufo' - +import { ImpoundPlugin } from 'impound' +import type { ImpoundOptions } from 'impound' import defu from 'defu' import { gt, satisfies } from 'semver' import { hasTTY, isCI } from 'std-env' + import pagesModule from '../pages/module' import metaModule from '../head/module' import componentsModule from '../components/module' @@ -31,7 +32,7 @@ import { distDir, pkgDir } from '../dirs' import { version } from '../../package.json' import { scriptsStubsPreset } from '../imports/presets' import { resolveTypePath } from './utils/types' -import { ImportProtectionPlugin, nuxtImportProtections } from './plugins/import-protection' +import { nuxtImportProtections } from './plugins/import-protection' import type { UnctxTransformPluginOptions } from './plugins/unctx' import { UnctxTransformPlugin } from './plugins/unctx' import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake' @@ -245,15 +246,15 @@ async function initNuxt (nuxt: Nuxt) { addBuildPlugin(RemovePluginMetadataPlugin(nuxt)) // Add import protection - const config = { - rootDir: nuxt.options.rootDir, + const config: ImpoundOptions = { + cwd: nuxt.options.rootDir, // Exclude top-level resolutions by plugins exclude: [join(nuxt.options.srcDir, 'index.html')], patterns: nuxtImportProtections(nuxt), - modulesDir: nuxt.options.modulesDir, } - addVitePlugin(() => ImportProtectionPlugin.vite(config)) - addWebpackPlugin(() => ImportProtectionPlugin.webpack(config)) + addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...config, error: false }), { name: 'nuxt:import-protection' }), { client: false }) + addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...config, error: true }), { name: 'nuxt:import-protection' }), { server: false }) + addWebpackPlugin(() => ImpoundPlugin.webpack(config)) // add resolver for modules used in virtual files addVitePlugin(() => resolveDeepImportsPlugin(nuxt)) diff --git a/packages/nuxt/src/core/plugins/import-protection.ts b/packages/nuxt/src/core/plugins/import-protection.ts index c0940a6954..22634f09e8 100644 --- a/packages/nuxt/src/core/plugins/import-protection.ts +++ b/packages/nuxt/src/core/plugins/import-protection.ts @@ -1,7 +1,4 @@ -import { createUnplugin } from 'unplugin' -import { logger } from '@nuxt/kit' -import { resolvePath } from 'mlly' -import { isAbsolute, join, relative, resolve } from 'pathe' +import { relative, resolve } from 'pathe' import escapeRE from 'escape-string-regexp' import type { NuxtOptions } from 'nuxt/schema' @@ -53,41 +50,3 @@ export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: { return patterns } - -export const ImportProtectionPlugin = createUnplugin(function (options: ImportProtectionOptions) { - const cache: Record> = {} - const importersToExclude = options?.exclude || [] - const proxy = resolvePath('unenv/runtime/mock/proxy', { url: options.modulesDir }) - return { - name: 'nuxt:import-protection', - enforce: 'pre', - resolveId (id, importer) { - if (!importer) { return } - if (id[0] === '.') { - id = join(importer, '..', id) - } - if (isAbsolute(id)) { - id = relative(options.rootDir, id) - } - if (importersToExclude.some(p => typeof p === 'string' ? importer === p : p.test(importer))) { return } - - const invalidImports = options.patterns.filter(([pattern]) => pattern instanceof RegExp ? pattern.test(id) : pattern === id) - let matched = false - for (const match of invalidImports) { - cache[id] = cache[id] || new Map() - const [pattern, warning] = match - // Skip if already warned - if (cache[id].has(pattern)) { continue } - - const relativeImporter = isAbsolute(importer) ? relative(options.rootDir, importer) : importer - logger.error(warning || 'Invalid import', `[importing \`${id}\` from \`${relativeImporter}\`]`) - cache[id].set(pattern, true) - matched = true - } - if (matched) { - return proxy - } - return null - }, - } -}) diff --git a/packages/nuxt/test/import-protection.test.ts b/packages/nuxt/test/import-protection.test.ts index d0ac2a0ccb..110f47dfb1 100644 --- a/packages/nuxt/test/import-protection.test.ts +++ b/packages/nuxt/test/import-protection.test.ts @@ -1,7 +1,7 @@ -import { fileURLToPath } from 'node:url' import { normalize } from 'pathe' import { describe, expect, it } from 'vitest' -import { ImportProtectionPlugin, nuxtImportProtections } from '../src/core/plugins/import-protection' +import { ImpoundPlugin } from 'impound' +import { nuxtImportProtections } from '../src/core/plugins/import-protection' import type { NuxtOptions } from '../schema' const testsToTriggerOn = [ @@ -39,9 +39,8 @@ describe('import protection', () => { }) const transformWithImportProtection = (id: string, importer: string) => { - const plugin = ImportProtectionPlugin.rollup({ - rootDir: '/root', - modulesDir: [fileURLToPath(new URL('..', import.meta.url))], + const plugin = ImpoundPlugin.rollup({ + cwd: '/root', patterns: nuxtImportProtections({ options: { modules: ['some-nuxt-module'], @@ -51,5 +50,5 @@ const transformWithImportProtection = (id: string, importer: string) => { }), }) - return (plugin as any).resolveId(id, importer) + return (plugin as any).resolveId.call({ error: () => {} }, id, importer) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf48decdbb..78b476614a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -343,6 +343,9 @@ importers: ignore: specifier: ^5.3.2 version: 5.3.2 + impound: + specifier: ^0.1.0 + version: 0.1.0(rollup@4.21.1) jiti: specifier: ^1.21.6 version: 1.21.6 @@ -4703,6 +4706,9 @@ packages: importx@0.4.3: resolution: {integrity: sha512-x6E6OxmWq/SUaj7wDeDeSjyHP+rMUbEaqJ5fw0uEtC/FTX9ocxNMFJ+ONnpJIsRpFz3ya6qJAK4orwSKqw0BSQ==} + impound@0.1.0: + resolution: {integrity: sha512-F9nJgOsDc3tysjN74edE0vGPEQrU7DAje6g5nNAL5Jc9Tv4JW3mH7XMGne+EaadTniDXLeUrVR21opkNfWO1zQ==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -11778,6 +11784,16 @@ snapshots: transitivePeerDependencies: - supports-color + impound@0.1.0(rollup@4.21.1): + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + mlly: 1.7.1 + pathe: 1.1.2 + unenv: 1.10.0 + unplugin: 1.12.2 + transitivePeerDependencies: + - rollup + imurmurhash@0.1.4: {} indent-string@4.0.0: {}