fix(nuxt): improve error logging in import protections (#28753)

This commit is contained in:
Daniel Roe 2024-08-29 20:50:01 +01:00
parent 29fbb2d61a
commit 98630820b1
No known key found for this signature in database
GPG Key ID: 3714AB03996F442B
6 changed files with 36 additions and 60 deletions

View File

@ -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",

View File

@ -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/],
}),

View File

@ -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))

View File

@ -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<string, Map<string | RegExp, boolean>> = {}
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
},
}
})

View File

@ -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)
}

View File

@ -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: {}