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

This commit is contained in:
Daniel Roe 2024-08-29 20:50:01 +01:00 committed by GitHub
parent d2ef3145f6
commit 77e36ee274
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 36 additions and 60 deletions

View File

@ -75,6 +75,7 @@
"compatx": "^0.1.8", "compatx": "^0.1.8",
"consola": "^3.2.3", "consola": "^3.2.3",
"cookie-es": "^1.2.2", "cookie-es": "^1.2.2",
"impound": "^0.1.0",
"defu": "^6.1.4", "defu": "^6.1.4",
"destr": "^2.0.3", "destr": "^2.0.3",
"devalue": "^5.0.0", "devalue": "^5.0.0",

View File

@ -11,12 +11,13 @@ import escapeRE from 'escape-string-regexp'
import { defu } from 'defu' import { defu } from 'defu'
import { dynamicEventHandler } from 'h3' import { dynamicEventHandler } from 'h3'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
import { ImpoundPlugin } from 'impound'
import type { Nuxt, NuxtOptions } from 'nuxt/schema' import type { Nuxt, NuxtOptions } from 'nuxt/schema'
import { version as nuxtVersion } from '../../package.json' import { version as nuxtVersion } from '../../package.json'
import { distDir } from '../dirs' import { distDir } from '../dirs'
import { toArray } from '../utils' import { toArray } from '../utils'
import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon' 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 = { const logLevelMapReverse = {
silent: 0, silent: 0,
@ -358,9 +359,8 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
nitroConfig.rollupConfig!.plugins = await nitroConfig.rollupConfig!.plugins || [] nitroConfig.rollupConfig!.plugins = await nitroConfig.rollupConfig!.plugins || []
nitroConfig.rollupConfig!.plugins = toArray(nitroConfig.rollupConfig!.plugins) nitroConfig.rollupConfig!.plugins = toArray(nitroConfig.rollupConfig!.plugins)
nitroConfig.rollupConfig!.plugins!.push( nitroConfig.rollupConfig!.plugins!.push(
ImportProtectionPlugin.rollup({ ImpoundPlugin.rollup({
rootDir: nuxt.options.rootDir, cwd: nuxt.options.rootDir,
modulesDir: nuxt.options.modulesDir,
patterns: nuxtImportProtections(nuxt, { isNitro: true }), patterns: nuxtImportProtections(nuxt, { isNitro: true }),
exclude: [/core[\\/]runtime[\\/]nitro[\\/]renderer/], exclude: [/core[\\/]runtime[\\/]nitro[\\/]renderer/],
}), }),

View File

@ -15,13 +15,14 @@ import { colorize } from 'consola/utils'
import { updateConfig } from 'c12/update' import { updateConfig } from 'c12/update'
import { formatDate, resolveCompatibilityDatesFromEnv } from 'compatx' import { formatDate, resolveCompatibilityDatesFromEnv } from 'compatx'
import type { DateString } from 'compatx' import type { DateString } from 'compatx'
import escapeRE from 'escape-string-regexp' import escapeRE from 'escape-string-regexp'
import { withTrailingSlash, withoutLeadingSlash } from 'ufo' import { withTrailingSlash, withoutLeadingSlash } from 'ufo'
import { ImpoundPlugin } from 'impound'
import type { ImpoundOptions } from 'impound'
import defu from 'defu' import defu from 'defu'
import { gt, satisfies } from 'semver' import { gt, satisfies } from 'semver'
import { hasTTY, isCI } from 'std-env' import { hasTTY, isCI } from 'std-env'
import pagesModule from '../pages/module' import pagesModule from '../pages/module'
import metaModule from '../head/module' import metaModule from '../head/module'
import componentsModule from '../components/module' import componentsModule from '../components/module'
@ -31,7 +32,7 @@ import { distDir, pkgDir } from '../dirs'
import { version } from '../../package.json' import { version } from '../../package.json'
import { scriptsStubsPreset } from '../imports/presets' import { scriptsStubsPreset } from '../imports/presets'
import { resolveTypePath } from './utils/types' 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 type { UnctxTransformPluginOptions } from './plugins/unctx'
import { UnctxTransformPlugin } from './plugins/unctx' import { UnctxTransformPlugin } from './plugins/unctx'
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake' import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
@ -245,15 +246,15 @@ async function initNuxt (nuxt: Nuxt) {
addBuildPlugin(RemovePluginMetadataPlugin(nuxt)) addBuildPlugin(RemovePluginMetadataPlugin(nuxt))
// Add import protection // Add import protection
const config = { const config: ImpoundOptions = {
rootDir: nuxt.options.rootDir, cwd: nuxt.options.rootDir,
// Exclude top-level resolutions by plugins // Exclude top-level resolutions by plugins
exclude: [join(nuxt.options.srcDir, 'index.html')], exclude: [join(nuxt.options.srcDir, 'index.html')],
patterns: nuxtImportProtections(nuxt), patterns: nuxtImportProtections(nuxt),
modulesDir: nuxt.options.modulesDir,
} }
addVitePlugin(() => ImportProtectionPlugin.vite(config)) addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...config, error: false }), { name: 'nuxt:import-protection' }), { client: false })
addWebpackPlugin(() => ImportProtectionPlugin.webpack(config)) 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 // add resolver for modules used in virtual files
addVitePlugin(() => resolveDeepImportsPlugin(nuxt)) addVitePlugin(() => resolveDeepImportsPlugin(nuxt))

View File

@ -1,7 +1,4 @@
import { createUnplugin } from 'unplugin' import { relative, resolve } from 'pathe'
import { logger } from '@nuxt/kit'
import { resolvePath } from 'mlly'
import { isAbsolute, join, relative, resolve } from 'pathe'
import escapeRE from 'escape-string-regexp' import escapeRE from 'escape-string-regexp'
import type { NuxtOptions } from 'nuxt/schema' import type { NuxtOptions } from 'nuxt/schema'
@ -53,41 +50,3 @@ export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: {
return patterns 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 { normalize } from 'pathe'
import { describe, expect, it } from 'vitest' 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' import type { NuxtOptions } from '../schema'
const testsToTriggerOn = [ const testsToTriggerOn = [
@ -39,9 +39,8 @@ describe('import protection', () => {
}) })
const transformWithImportProtection = (id: string, importer: string) => { const transformWithImportProtection = (id: string, importer: string) => {
const plugin = ImportProtectionPlugin.rollup({ const plugin = ImpoundPlugin.rollup({
rootDir: '/root', cwd: '/root',
modulesDir: [fileURLToPath(new URL('..', import.meta.url))],
patterns: nuxtImportProtections({ patterns: nuxtImportProtections({
options: { options: {
modules: ['some-nuxt-module'], 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

@ -348,6 +348,9 @@ importers:
ignore: ignore:
specifier: ^5.3.2 specifier: ^5.3.2
version: 5.3.2 version: 5.3.2
impound:
specifier: ^0.1.0
version: 0.1.0(rollup@4.21.1)
jiti: jiti:
specifier: 2.0.0-beta.3 specifier: 2.0.0-beta.3
version: 2.0.0-beta.3 version: 2.0.0-beta.3
@ -4661,6 +4664,9 @@ packages:
importx@0.4.3: importx@0.4.3:
resolution: {integrity: sha512-x6E6OxmWq/SUaj7wDeDeSjyHP+rMUbEaqJ5fw0uEtC/FTX9ocxNMFJ+ONnpJIsRpFz3ya6qJAK4orwSKqw0BSQ==} resolution: {integrity: sha512-x6E6OxmWq/SUaj7wDeDeSjyHP+rMUbEaqJ5fw0uEtC/FTX9ocxNMFJ+ONnpJIsRpFz3ya6qJAK4orwSKqw0BSQ==}
impound@0.1.0:
resolution: {integrity: sha512-F9nJgOsDc3tysjN74edE0vGPEQrU7DAje6g5nNAL5Jc9Tv4JW3mH7XMGne+EaadTniDXLeUrVR21opkNfWO1zQ==}
imurmurhash@0.1.4: imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'} engines: {node: '>=0.8.19'}
@ -11803,6 +11809,16 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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: {} imurmurhash@0.1.4: {}
indent-string@4.0.0: {} indent-string@4.0.0: {}