mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
feat(nuxt3): add import protection patterns (#2834)
This commit is contained in:
parent
d5cbe09ced
commit
7553849371
@ -1,6 +1,7 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { wpfs, getNitroContext, createDevServer, resolveMiddleware, build, prepare, generate, writeTypes, scanMiddleware } from '@nuxt/nitro'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { ImportProtectionPlugin } from './plugins/import-protection'
|
||||
|
||||
export function initNitro (nuxt: Nuxt) {
|
||||
// Create contexts
|
||||
@ -27,6 +28,17 @@ export function initNitro (nuxt: Nuxt) {
|
||||
nuxt.hook('close', () => nitroDevContext._internal.hooks.callHook('close'))
|
||||
nitroDevContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||
|
||||
// Register nuxt3 protection patterns
|
||||
nitroDevContext._internal.hooks.hook('nitro:rollup:before', (ctx) => {
|
||||
ctx.rollupConfig.plugins.push(ImportProtectionPlugin.rollup({
|
||||
rootDir: nuxt.options.rootDir,
|
||||
patterns: [
|
||||
...['#app', /^#build(\/|$)/]
|
||||
.map(p => [p, 'Vue app aliases are not allowed in server routes.']) as [RegExp | string, string][]
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
// Add typed route responses
|
||||
nuxt.hook('prepare:types', (opts) => {
|
||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'nitro.d.ts') })
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { createHooks } from 'hookable'
|
||||
import type { Nuxt, NuxtOptions, NuxtConfig, ModuleContainer, NuxtHooks } from '@nuxt/schema'
|
||||
import { loadNuxtConfig, LoadNuxtOptions, nuxtCtx, installModule, addComponent } from '@nuxt/kit'
|
||||
import { loadNuxtConfig, LoadNuxtOptions, nuxtCtx, installModule, addComponent, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
|
||||
import pagesModule from '../pages/module'
|
||||
import metaModule from '../meta/module'
|
||||
import componentsModule from '../components/module'
|
||||
import autoImportsModule from '../auto-imports/module'
|
||||
import { distDir, pkgDir } from '../dirs'
|
||||
import { version } from '../../package.json'
|
||||
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
|
||||
import { initNitro } from './nitro'
|
||||
import { addModuleTranspiles } from './modules'
|
||||
|
||||
@ -50,6 +51,15 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
}
|
||||
})
|
||||
|
||||
// Add import protection
|
||||
|
||||
const config = {
|
||||
rootDir: nuxt.options.rootDir,
|
||||
patterns: vueAppPatterns(nuxt)
|
||||
}
|
||||
addVitePlugin(ImportProtectionPlugin.vite(config))
|
||||
addWebpackPlugin(ImportProtectionPlugin.webpack(config))
|
||||
|
||||
// Init user modules
|
||||
await nuxt.callHook('modules:before', { nuxt } as ModuleContainer)
|
||||
const modulesToInstall = [
|
||||
|
54
packages/nuxt3/src/core/plugins/import-protection.ts
Normal file
54
packages/nuxt3/src/core/plugins/import-protection.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { createRequire } from 'module'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import consola from 'consola'
|
||||
import { isAbsolute, relative, resolve } from 'pathe'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
|
||||
const _require = createRequire(import.meta.url)
|
||||
|
||||
interface ImportProtectionOptions {
|
||||
rootDir: string
|
||||
patterns: [importPattern: string | RegExp, warning?: string][]
|
||||
}
|
||||
|
||||
const escapeRE = (str: string) => str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
||||
|
||||
export const vueAppPatterns = (nuxt: Nuxt) => [
|
||||
[/(^|node_modules\/)(nuxt3|nuxt)/, '`nuxt3`/`nuxt` cannot be imported directly. Instead, import runtime Nuxt composables from `#app` or `#imports`.'],
|
||||
[/nuxt\.config/, 'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.'],
|
||||
[/(^|node_modules\/)@vue\/composition-api/],
|
||||
...nuxt.options.modules.filter(m => typeof m === 'string').map((m: string) =>
|
||||
[new RegExp(`^${escapeRE(m)}$`), 'Importing directly from module entrypoints is not allowed.']),
|
||||
...['#static-assets', '#static', '#config', '#assets', '#storage', '#server-middleware']
|
||||
.map(i => [i, 'Nitro aliases cannot be imported in the Vue part of your app.']),
|
||||
...[/(^|node_modules\/)@nuxt\/kit/, /(^|node_modules\/)@nuxt\/nitro/]
|
||||
.map(i => [i, 'This module cannot be imported in the Vue part of your app.']),
|
||||
[new RegExp(escapeRE(resolve(nuxt.options.srcDir, (nuxt.options.dir as any).server || 'server'))), 'Importing from server middleware is not allowed in the Vue part of your app.']
|
||||
] as ImportProtectionOptions['patterns']
|
||||
|
||||
export const ImportProtectionPlugin = createUnplugin(function (options: ImportProtectionOptions) {
|
||||
const cache: Record<string, Map<string | RegExp, boolean>> = {}
|
||||
return {
|
||||
name: 'nuxt:import-protection',
|
||||
enforce: 'pre',
|
||||
resolveId (id, importer) {
|
||||
const invalidImports = options.patterns.filter(([pattern]) => pattern instanceof RegExp ? pattern.test(id) : pattern === id)
|
||||
let matched: boolean
|
||||
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
|
||||
consola.error(warning || 'Invalid import', `[importing \`${id}\` from \`${relativeImporter}\`]`)
|
||||
cache[id].set(pattern, true)
|
||||
matched = true
|
||||
}
|
||||
if (matched) {
|
||||
return _require.resolve('unenv/runtime/mock/proxy')
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user