From e89240c9b5e48c54a73ba8a3d7ad50a29bbee880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Fri, 15 Mar 2024 23:02:17 +0100 Subject: [PATCH 01/17] fix(nuxt): avoid warning about unused page/layout components when mounted asynchronously --- .../src/app/plugins/check-if-layout-used.ts | 4 ++- packages/nuxt/src/components/loader.ts | 14 +++++++++- packages/nuxt/src/components/module.ts | 28 +++++++++++++++++-- .../runtime/plugins/check-if-page-unused.ts | 5 +++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/packages/nuxt/src/app/plugins/check-if-layout-used.ts b/packages/nuxt/src/app/plugins/check-if-layout-used.ts index 9cf7192ea98c..247429681c05 100644 --- a/packages/nuxt/src/app/plugins/check-if-layout-used.ts +++ b/packages/nuxt/src/app/plugins/check-if-layout-used.ts @@ -5,6 +5,8 @@ import { useError } from '../composables/error' // @ts-expect-error virtual file import layouts from '#build/layouts' +// @ts-expect-error virtual file +import { isNuxtLayoutUsed } from '#build/detected-nuxt-layout-usage' export default defineNuxtPlugin({ name: 'nuxt:checkIfLayoutUsed', @@ -12,7 +14,7 @@ export default defineNuxtPlugin({ const error = useError() function checkIfLayoutUsed () { - if (!error.value && !nuxtApp._isNuxtLayoutUsed && Object.keys(layouts).length > 0) { + if (!error.value && !nuxtApp._isNuxtLayoutUsed && !isNuxtLayoutUsed && Object.keys(layouts).length > 0) { console.warn('[nuxt] Your project has layouts but the `` component has not been used.') } } diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 1f372adfb35e..17daf155c4c0 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -5,7 +5,7 @@ import { pascalCase } from 'scule' import { resolve } from 'pathe' import type { Component, ComponentsOptions } from 'nuxt/schema' -import { logger, tryUseNuxt } from '@nuxt/kit' +import { logger, tryUseNuxt, updateTemplates } from '@nuxt/kit' import { distDir } from '../dirs' import { isVue } from '../core/utils' @@ -15,6 +15,7 @@ interface LoaderOptions { sourcemap?: boolean transform?: ComponentsOptions['transform'] experimentalComponentIslands?: boolean + detectedComponentsUsage: Record } export const loaderPlugin = createUnplugin((options: LoaderOptions) => { @@ -54,6 +55,8 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { let identifier = map.get(component) || `__nuxt_component_${num++}` map.set(component, identifier) + detectComponentUsage(component, options.detectedComponentsUsage) + const isServerOnly = !component._raw && component.mode === 'server' && !components.some(c => c.pascalName === component.pascalName && c.mode === 'client') if (isServerOnly) { @@ -123,3 +126,12 @@ function findComponent (components: Component[], name: string, mode: LoaderOptio // and createServerComponent above return otherModeComponent } + +function detectComponentUsage (component: Component, detectedComponentsUsage: LoaderOptions['detectedComponentsUsage']) { + if (process.env.NODE_ENV !== 'development' || !(component.pascalName in detectedComponentsUsage)) { return } + + detectedComponentsUsage[component.pascalName] = true + const virtualFilename = `detected-${component.kebabName}-usage.mjs` + + updateTemplates({ filter: template => template.filename === virtualFilename }) +} diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts index 0394c5fb1b63..082034a9c11e 100644 --- a/packages/nuxt/src/components/module.ts +++ b/packages/nuxt/src/components/module.ts @@ -212,6 +212,28 @@ export default defineNuxtModule({ } }) + const detectedComponentsUsage = { + NuxtLayout: false, + NuxtPage: false + } + + if (process.env.NODE_ENV === 'development') { + addTemplate({ + filename: 'detected-nuxt-layout-usage.mjs', + options: detectedComponentsUsage, + getContents: data => ` + export const isNuxtLayoutUsed = ${data.options.NuxtLayout} + ` + }) + addTemplate({ + filename: 'detected-nuxt-page-usage.mjs', + options: detectedComponentsUsage, + getContents: data => ` + export const isNuxtPageUsed = ${data.options.NuxtPage} + ` + }) + } + nuxt.hook('vite:extendConfig', (config, { isClient, isServer }) => { const mode = isClient ? 'client' : 'server' @@ -231,7 +253,8 @@ export default defineNuxtModule({ getComponents, mode, transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined, - experimentalComponentIslands: !!nuxt.options.experimental.componentIslands + experimentalComponentIslands: !!nuxt.options.experimental.componentIslands, + detectedComponentsUsage })) if (nuxt.options.experimental.componentIslands) { @@ -300,7 +323,8 @@ export default defineNuxtModule({ getComponents, mode, transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined, - experimentalComponentIslands: !!nuxt.options.experimental.componentIslands + experimentalComponentIslands: !!nuxt.options.experimental.componentIslands, + detectedComponentsUsage })) if (nuxt.options.experimental.componentIslands) { diff --git a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts b/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts index 57ae91fe6e63..ff747425082f 100644 --- a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts +++ b/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts @@ -3,13 +3,16 @@ import { defineNuxtPlugin } from '#app/nuxt' import { onNuxtReady } from '#app/composables/ready' import { useError } from '#app/composables/error' +// @ts-expect-error virtual file +import { isNuxtPageUsed } from '#build/detected-nuxt-page-usage' + export default defineNuxtPlugin({ name: 'nuxt:checkIfPageUnused', setup (nuxtApp) { const error = useError() function checkIfPageUnused () { - if (!error.value && !nuxtApp._isNuxtPageUsed) { + if (!error.value && !nuxtApp._isNuxtPageUsed && !isNuxtPageUsed) { console.warn( '[nuxt] Your project has pages but the `` component has not been used.' + ' You might be using the `` component instead, which will not work correctly in Nuxt.' + From dc46308c828566a854591ba500ceeb7c4f8771bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 11:03:15 +0100 Subject: [PATCH 02/17] Revert "fix(nuxt): avoid warning about unused page/layout components when mounted asynchronously" This reverts commit 92974b40e58e7ba5b28677b803998b4e3968b906. --- .../src/app/plugins/check-if-layout-used.ts | 4 +-- packages/nuxt/src/components/loader.ts | 14 +--------- packages/nuxt/src/components/module.ts | 28 ++----------------- .../runtime/plugins/check-if-page-unused.ts | 5 +--- 4 files changed, 5 insertions(+), 46 deletions(-) diff --git a/packages/nuxt/src/app/plugins/check-if-layout-used.ts b/packages/nuxt/src/app/plugins/check-if-layout-used.ts index 247429681c05..9cf7192ea98c 100644 --- a/packages/nuxt/src/app/plugins/check-if-layout-used.ts +++ b/packages/nuxt/src/app/plugins/check-if-layout-used.ts @@ -5,8 +5,6 @@ import { useError } from '../composables/error' // @ts-expect-error virtual file import layouts from '#build/layouts' -// @ts-expect-error virtual file -import { isNuxtLayoutUsed } from '#build/detected-nuxt-layout-usage' export default defineNuxtPlugin({ name: 'nuxt:checkIfLayoutUsed', @@ -14,7 +12,7 @@ export default defineNuxtPlugin({ const error = useError() function checkIfLayoutUsed () { - if (!error.value && !nuxtApp._isNuxtLayoutUsed && !isNuxtLayoutUsed && Object.keys(layouts).length > 0) { + if (!error.value && !nuxtApp._isNuxtLayoutUsed && Object.keys(layouts).length > 0) { console.warn('[nuxt] Your project has layouts but the `` component has not been used.') } } diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 17daf155c4c0..1f372adfb35e 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -5,7 +5,7 @@ import { pascalCase } from 'scule' import { resolve } from 'pathe' import type { Component, ComponentsOptions } from 'nuxt/schema' -import { logger, tryUseNuxt, updateTemplates } from '@nuxt/kit' +import { logger, tryUseNuxt } from '@nuxt/kit' import { distDir } from '../dirs' import { isVue } from '../core/utils' @@ -15,7 +15,6 @@ interface LoaderOptions { sourcemap?: boolean transform?: ComponentsOptions['transform'] experimentalComponentIslands?: boolean - detectedComponentsUsage: Record } export const loaderPlugin = createUnplugin((options: LoaderOptions) => { @@ -55,8 +54,6 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { let identifier = map.get(component) || `__nuxt_component_${num++}` map.set(component, identifier) - detectComponentUsage(component, options.detectedComponentsUsage) - const isServerOnly = !component._raw && component.mode === 'server' && !components.some(c => c.pascalName === component.pascalName && c.mode === 'client') if (isServerOnly) { @@ -126,12 +123,3 @@ function findComponent (components: Component[], name: string, mode: LoaderOptio // and createServerComponent above return otherModeComponent } - -function detectComponentUsage (component: Component, detectedComponentsUsage: LoaderOptions['detectedComponentsUsage']) { - if (process.env.NODE_ENV !== 'development' || !(component.pascalName in detectedComponentsUsage)) { return } - - detectedComponentsUsage[component.pascalName] = true - const virtualFilename = `detected-${component.kebabName}-usage.mjs` - - updateTemplates({ filter: template => template.filename === virtualFilename }) -} diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts index 082034a9c11e..0394c5fb1b63 100644 --- a/packages/nuxt/src/components/module.ts +++ b/packages/nuxt/src/components/module.ts @@ -212,28 +212,6 @@ export default defineNuxtModule({ } }) - const detectedComponentsUsage = { - NuxtLayout: false, - NuxtPage: false - } - - if (process.env.NODE_ENV === 'development') { - addTemplate({ - filename: 'detected-nuxt-layout-usage.mjs', - options: detectedComponentsUsage, - getContents: data => ` - export const isNuxtLayoutUsed = ${data.options.NuxtLayout} - ` - }) - addTemplate({ - filename: 'detected-nuxt-page-usage.mjs', - options: detectedComponentsUsage, - getContents: data => ` - export const isNuxtPageUsed = ${data.options.NuxtPage} - ` - }) - } - nuxt.hook('vite:extendConfig', (config, { isClient, isServer }) => { const mode = isClient ? 'client' : 'server' @@ -253,8 +231,7 @@ export default defineNuxtModule({ getComponents, mode, transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined, - experimentalComponentIslands: !!nuxt.options.experimental.componentIslands, - detectedComponentsUsage + experimentalComponentIslands: !!nuxt.options.experimental.componentIslands })) if (nuxt.options.experimental.componentIslands) { @@ -323,8 +300,7 @@ export default defineNuxtModule({ getComponents, mode, transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined, - experimentalComponentIslands: !!nuxt.options.experimental.componentIslands, - detectedComponentsUsage + experimentalComponentIslands: !!nuxt.options.experimental.componentIslands })) if (nuxt.options.experimental.componentIslands) { diff --git a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts b/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts index ff747425082f..57ae91fe6e63 100644 --- a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts +++ b/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts @@ -3,16 +3,13 @@ import { defineNuxtPlugin } from '#app/nuxt' import { onNuxtReady } from '#app/composables/ready' import { useError } from '#app/composables/error' -// @ts-expect-error virtual file -import { isNuxtPageUsed } from '#build/detected-nuxt-page-usage' - export default defineNuxtPlugin({ name: 'nuxt:checkIfPageUnused', setup (nuxtApp) { const error = useError() function checkIfPageUnused () { - if (!error.value && !nuxtApp._isNuxtPageUsed && !isNuxtPageUsed) { + if (!error.value && !nuxtApp._isNuxtPageUsed) { console.warn( '[nuxt] Your project has pages but the `` component has not been used.' + ' You might be using the `` component instead, which will not work correctly in Nuxt.' + From e94e25e1df61f10cd9fe725ce6e2d5aed35673bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 11:08:21 +0100 Subject: [PATCH 03/17] feat(nuxt): add _hasLayouts property on NuxtApp --- packages/nuxt/src/core/app.ts | 1 + packages/schema/src/types/nuxt.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/nuxt/src/core/app.ts b/packages/nuxt/src/core/app.ts index ccfffa8abf57..0c27e556feb8 100644 --- a/packages/nuxt/src/core/app.ts +++ b/packages/nuxt/src/core/app.ts @@ -124,6 +124,7 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { for (const config of layerConfigs) { const layoutDir = (config.rootDir === nuxt.options.rootDir ? nuxt.options : config).dir?.layouts || 'layouts' const layoutFiles = await resolveFiles(config.srcDir, `${layoutDir}/**/*{${nuxt.options.extensions.join(',')}}`) + app._hasLayouts = layoutFiles.length > 0 for (const file of layoutFiles) { const name = getNameFromPath(file, resolve(config.srcDir, layoutDir)) if (!name) { diff --git a/packages/schema/src/types/nuxt.ts b/packages/schema/src/types/nuxt.ts index b37c37d9001f..0daa4fe1c1a3 100644 --- a/packages/schema/src/types/nuxt.ts +++ b/packages/schema/src/types/nuxt.ts @@ -69,6 +69,9 @@ export interface NuxtApp { templates: NuxtTemplate[] configs: string[] pages?: NuxtPage[] + + // Private fields + _hasLayouts?: boolean } export interface Nuxt { From b8ea5ded649c690747813a58b5b5be5e33fbb2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 11:20:15 +0100 Subject: [PATCH 04/17] refactor(nuxt): emit component usage warnings at compile time --- packages/nuxt/src/core/nuxt.ts | 47 ++++++++++++++++++- .../core/plugins/detect-component-usage.ts | 39 +++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 packages/nuxt/src/core/plugins/detect-component-usage.ts diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index ab8f93c79475..939a40f4e627 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -1,4 +1,4 @@ -import { dirname, join, normalize, relative, resolve } from 'pathe' +import { dirname, join, normalize, relative, resolve, sep } from 'pathe' import { createDebugger, createHooks } from 'hookable' import type { LoadNuxtOptions } from '@nuxt/kit' import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' @@ -25,6 +25,7 @@ import { UnctxTransformPlugin } from './plugins/unctx' import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake' import { TreeShakeComposablesPlugin } from './plugins/tree-shake' import { DevOnlyPlugin } from './plugins/dev-only' +import { DetectComponentUsagePlugin } from './plugins/detect-component-usage' import { LayerAliasingPlugin } from './plugins/layer-aliasing' import { addModuleTranspiles } from './modules' import { initNitro } from './nitro' @@ -196,6 +197,50 @@ async function initNuxt (nuxt: Nuxt) { addWebpackPlugin(() => DevOnlyPlugin.webpack({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client })) } + if (nuxt.options.dev) { + // Add component usage detection + let hasBeenInitialized = false + const detectedComponents = new Set() + const cache = new Set() + + const componentDetectionConfig = { + rootDir: nuxt.options.rootDir, + exclude: [ + // Exclude top-level resolutions by plugins + join(nuxt.options.rootDir, 'index.html'), + // Keep only imports coming from the user's project (inside the rootDir) + new RegExp(`^(?!${escapeRE(nuxt.options.rootDir)}${escapeRE(sep)}).+[^\n]+$`) + ], + detectedComponents + } + + addVitePlugin(() => DetectComponentUsagePlugin.vite(componentDetectionConfig)) + addWebpackPlugin(() => DetectComponentUsagePlugin.webpack(componentDetectionConfig)) + + nuxt.hook('app:templates', (app) => { + // Skip first hook call + if (!hasBeenInitialized) { + hasBeenInitialized = true + return + } + + if (app._hasLayouts) { + if (!detectedComponents.has('NuxtLayout') && !cache.has('NuxtLayout')) { + logger.warn('[nuxt] Your project has layouts but the `` component has not been used.') + } + cache.add('NuxtLayout') + } + if (nuxt.options.pages) { + if (!detectedComponents.has('NuxtPage') && !cache.has('NuxtPage')) { + logger.warn('[nuxt] Your project has pages but the `` component has not been used.' + + ' You might be using the `` component instead, which will not work correctly in Nuxt.' + + ' You can set `pages: false` in `nuxt.config` if you do not wish to use the Nuxt `vue-router` integration.') + } + cache.add('NuxtPage') + } + }) + } + if (nuxt.options.dev) { // Add plugin to check if layouts are defined without NuxtLayout being instantiated addPlugin(resolve(nuxt.options.appDir, 'plugins/check-if-layout-used')) diff --git a/packages/nuxt/src/core/plugins/detect-component-usage.ts b/packages/nuxt/src/core/plugins/detect-component-usage.ts new file mode 100644 index 000000000000..adca3f91fc08 --- /dev/null +++ b/packages/nuxt/src/core/plugins/detect-component-usage.ts @@ -0,0 +1,39 @@ +import { createUnplugin } from 'unplugin' +import { isAbsolute, join, relative } from 'pathe' + +interface DetectComponentUsageOptions { + rootDir: string + exclude?: Array + detectedComponents: Set +} + +export const DetectComponentUsagePlugin = createUnplugin((options: DetectComponentUsageOptions) => { + const importersToExclude = options?.exclude || [] + + const detectComponentUsagePatterns: Array<[importPattern: string | RegExp, name: string]> = [ + ['../packages/nuxt/src/pages/runtime/page', 'NuxtPage'], + ['../packages/nuxt/src/app/components/nuxt-layout', 'NuxtLayout'] + ] + + return { + name: 'nuxt:detect-component-usage', + 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 } + + for (const [pattern, name] of detectComponentUsagePatterns) { + if (pattern instanceof RegExp ? pattern.test(id) : pattern === id) { + options.detectedComponents.add(name) + } + } + return null + } + } +}) From cefa4c0968b6d8488d8fcb1f25352303b19d691c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 11:23:09 +0100 Subject: [PATCH 05/17] refactor(nuxt): remove plugins with runtime component usage checks --- .../src/app/plugins/check-if-layout-used.ts | 28 ----------------- packages/nuxt/src/core/nuxt.ts | 5 ---- packages/nuxt/src/pages/module.ts | 5 ---- .../runtime/plugins/check-if-page-unused.ts | 30 ------------------- 4 files changed, 68 deletions(-) delete mode 100644 packages/nuxt/src/app/plugins/check-if-layout-used.ts delete mode 100644 packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts diff --git a/packages/nuxt/src/app/plugins/check-if-layout-used.ts b/packages/nuxt/src/app/plugins/check-if-layout-used.ts deleted file mode 100644 index 9cf7192ea98c..000000000000 --- a/packages/nuxt/src/app/plugins/check-if-layout-used.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { nextTick } from 'vue' -import { defineNuxtPlugin } from '../nuxt' -import { onNuxtReady } from '../composables/ready' -import { useError } from '../composables/error' - -// @ts-expect-error virtual file -import layouts from '#build/layouts' - -export default defineNuxtPlugin({ - name: 'nuxt:checkIfLayoutUsed', - setup (nuxtApp) { - const error = useError() - - function checkIfLayoutUsed () { - if (!error.value && !nuxtApp._isNuxtLayoutUsed && Object.keys(layouts).length > 0) { - console.warn('[nuxt] Your project has layouts but the `` component has not been used.') - } - } - if (import.meta.server) { - nuxtApp.hook('app:rendered', ({ renderResult }) => { renderResult?.html && nextTick(checkIfLayoutUsed) }) - } else { - onNuxtReady(checkIfLayoutUsed) - } - }, - env: { - islands: false - } -}) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 939a40f4e627..f6d066e9bed7 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -241,11 +241,6 @@ async function initNuxt (nuxt: Nuxt) { }) } - if (nuxt.options.dev) { - // Add plugin to check if layouts are defined without NuxtLayout being instantiated - addPlugin(resolve(nuxt.options.appDir, 'plugins/check-if-layout-used')) - } - if (nuxt.options.dev && nuxt.options.features.devLogs) { addPlugin(resolve(nuxt.options.appDir, 'plugins/dev-server-logs.client')) addServerPlugin(resolve(distDir, 'core/runtime/nitro/dev-server-logs')) diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index 25d5c95ab9cf..885fe27f96fd 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -71,11 +71,6 @@ export default defineNuxtModule({ } nuxt.options.pages = await isPagesEnabled() - if (nuxt.options.dev && nuxt.options.pages) { - // Add plugin to check if pages are enabled without NuxtPage being instantiated - addPlugin(resolve(runtimeDir, 'plugins/check-if-page-unused')) - } - nuxt.hook('app:templates', async (app) => { app.pages = await resolvePagesRoutes() await nuxt.callHook('pages:extend', app.pages) diff --git a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts b/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts deleted file mode 100644 index 57ae91fe6e63..000000000000 --- a/packages/nuxt/src/pages/runtime/plugins/check-if-page-unused.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { nextTick } from 'vue' -import { defineNuxtPlugin } from '#app/nuxt' -import { onNuxtReady } from '#app/composables/ready' -import { useError } from '#app/composables/error' - -export default defineNuxtPlugin({ - name: 'nuxt:checkIfPageUnused', - setup (nuxtApp) { - const error = useError() - - function checkIfPageUnused () { - if (!error.value && !nuxtApp._isNuxtPageUsed) { - console.warn( - '[nuxt] Your project has pages but the `` component has not been used.' + - ' You might be using the `` component instead, which will not work correctly in Nuxt.' + - ' You can set `pages: false` in `nuxt.config` if you do not wish to use the Nuxt `vue-router` integration.' - ) - } - } - - if (import.meta.server) { - nuxtApp.hook('app:rendered', ({ renderResult }) => { renderResult?.html && nextTick(checkIfPageUnused) }) - } else { - onNuxtReady(checkIfPageUnused) - } - }, - env: { - islands: false - } -}) From 488f2bba6167857856c558c5584b88c0b8482f0e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:27:32 +0000 Subject: [PATCH 06/17] [autofix.ci] apply automated fixes --- packages/nuxt/test/app.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nuxt/test/app.test.ts b/packages/nuxt/test/app.test.ts index 47bbdbc28aa9..011e9c151ea0 100644 --- a/packages/nuxt/test/app.test.ts +++ b/packages/nuxt/test/app.test.ts @@ -17,6 +17,7 @@ describe('resolveApp', () => { const app = await getResolvedApp([]) expect(app).toMatchInlineSnapshot(` { + "_hasLayouts": false, "components": [], "configs": [], "dir": "", From 6f54ebb0f5c78eb47f6dc025e4946bcbe7abecdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 13:22:11 +0100 Subject: [PATCH 07/17] refactor(nuxt): use addBuildPlugin to add DetectComponentUsagePlugin --- packages/nuxt/src/core/nuxt.ts | 3 +-- packages/nuxt/src/core/plugins/detect-component-usage.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index f6d066e9bed7..c9fc1a0847b6 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -214,8 +214,7 @@ async function initNuxt (nuxt: Nuxt) { detectedComponents } - addVitePlugin(() => DetectComponentUsagePlugin.vite(componentDetectionConfig)) - addWebpackPlugin(() => DetectComponentUsagePlugin.webpack(componentDetectionConfig)) + addBuildPlugin(DetectComponentUsagePlugin(componentDetectionConfig)) nuxt.hook('app:templates', (app) => { // Skip first hook call diff --git a/packages/nuxt/src/core/plugins/detect-component-usage.ts b/packages/nuxt/src/core/plugins/detect-component-usage.ts index adca3f91fc08..d30c02862923 100644 --- a/packages/nuxt/src/core/plugins/detect-component-usage.ts +++ b/packages/nuxt/src/core/plugins/detect-component-usage.ts @@ -7,7 +7,7 @@ interface DetectComponentUsageOptions { detectedComponents: Set } -export const DetectComponentUsagePlugin = createUnplugin((options: DetectComponentUsageOptions) => { +export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) => createUnplugin(() => { const importersToExclude = options?.exclude || [] const detectComponentUsagePatterns: Array<[importPattern: string | RegExp, name: string]> = [ From 2330bc9b13e27076597bb438a99137539e074647 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 18 Mar 2024 15:26:16 +0000 Subject: [PATCH 08/17] chore: remove old code --- packages/nuxt/src/app/components/client-only.ts | 7 ------- packages/nuxt/src/app/components/nuxt-layout.ts | 4 ---- packages/nuxt/src/pages/runtime/page.ts | 4 ---- 3 files changed, 15 deletions(-) diff --git a/packages/nuxt/src/app/components/client-only.ts b/packages/nuxt/src/app/components/client-only.ts index ec002d460d27..d3f19cd5a268 100644 --- a/packages/nuxt/src/app/components/client-only.ts +++ b/packages/nuxt/src/app/components/client-only.ts @@ -1,6 +1,5 @@ import { cloneVNode, createElementBlock, createStaticVNode, defineComponent, getCurrentInstance, h, onMounted, provide, ref } from 'vue' import type { ComponentInternalInstance, ComponentOptions, InjectionKey } from 'vue' -import { useNuxtApp } from '../nuxt' import { getFragmentHTML } from './utils' export const clientOnlySymbol: InjectionKey = Symbol.for('nuxt:client-only') @@ -13,12 +12,6 @@ export default defineComponent({ setup (_, { slots, attrs }) { const mounted = ref(false) onMounted(() => { mounted.value = true }) - // Bail out of checking for pages/layouts as they might be included under `` 🤷‍♂️ - if (import.meta.dev) { - const nuxtApp = useNuxtApp() - nuxtApp._isNuxtPageUsed = true - nuxtApp._isNuxtLayoutUsed = true - } provide(clientOnlySymbol, true) return (props: any) => { if (mounted.value) { return slots.default?.() } diff --git a/packages/nuxt/src/app/components/nuxt-layout.ts b/packages/nuxt/src/app/components/nuxt-layout.ts index 7509f2f9ff4f..64e7c840eeab 100644 --- a/packages/nuxt/src/app/components/nuxt-layout.ts +++ b/packages/nuxt/src/app/components/nuxt-layout.ts @@ -76,10 +76,6 @@ export default defineComponent({ useRouter().beforeEach(removeErrorHook) } - if (import.meta.dev) { - nuxtApp._isNuxtLayoutUsed = true - } - return () => { const hasLayout = layout.value && layout.value in layouts const transitionProps = route.meta.layoutTransition ?? defaultLayoutTransition diff --git a/packages/nuxt/src/pages/runtime/page.ts b/packages/nuxt/src/pages/runtime/page.ts index aafaeb8e893a..66c7ba69c4da 100644 --- a/packages/nuxt/src/pages/runtime/page.ts +++ b/packages/nuxt/src/pages/runtime/page.ts @@ -62,10 +62,6 @@ export default defineComponent({ }) } - if (import.meta.dev) { - nuxtApp._isNuxtPageUsed = true - } - return () => { return h(RouterView, { name: props.name, route: props.route, ...attrs }, { default: (routeProps: RouterViewSlotProps) => { From 10fbc4cfe36fdd9d434f2562fe2b87daef54e9e8 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 18 Mar 2024 15:27:43 +0000 Subject: [PATCH 09/17] fix: use `app.layouts` as source of truth --- packages/nuxt/src/core/app.ts | 1 - packages/nuxt/src/core/nuxt.ts | 2 +- packages/nuxt/test/app.test.ts | 1 - packages/schema/src/types/nuxt.ts | 3 --- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/nuxt/src/core/app.ts b/packages/nuxt/src/core/app.ts index 0c27e556feb8..ccfffa8abf57 100644 --- a/packages/nuxt/src/core/app.ts +++ b/packages/nuxt/src/core/app.ts @@ -124,7 +124,6 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) { for (const config of layerConfigs) { const layoutDir = (config.rootDir === nuxt.options.rootDir ? nuxt.options : config).dir?.layouts || 'layouts' const layoutFiles = await resolveFiles(config.srcDir, `${layoutDir}/**/*{${nuxt.options.extensions.join(',')}}`) - app._hasLayouts = layoutFiles.length > 0 for (const file of layoutFiles) { const name = getNameFromPath(file, resolve(config.srcDir, layoutDir)) if (!name) { diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index c9fc1a0847b6..90d3f069ebd9 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -223,7 +223,7 @@ async function initNuxt (nuxt: Nuxt) { return } - if (app._hasLayouts) { + if (Object.keys(app.layouts).length > 0) { if (!detectedComponents.has('NuxtLayout') && !cache.has('NuxtLayout')) { logger.warn('[nuxt] Your project has layouts but the `` component has not been used.') } diff --git a/packages/nuxt/test/app.test.ts b/packages/nuxt/test/app.test.ts index 011e9c151ea0..47bbdbc28aa9 100644 --- a/packages/nuxt/test/app.test.ts +++ b/packages/nuxt/test/app.test.ts @@ -17,7 +17,6 @@ describe('resolveApp', () => { const app = await getResolvedApp([]) expect(app).toMatchInlineSnapshot(` { - "_hasLayouts": false, "components": [], "configs": [], "dir": "", diff --git a/packages/schema/src/types/nuxt.ts b/packages/schema/src/types/nuxt.ts index 0daa4fe1c1a3..b37c37d9001f 100644 --- a/packages/schema/src/types/nuxt.ts +++ b/packages/schema/src/types/nuxt.ts @@ -69,9 +69,6 @@ export interface NuxtApp { templates: NuxtTemplate[] configs: string[] pages?: NuxtPage[] - - // Private fields - _hasLayouts?: boolean } export interface Nuxt { From 8163fe4d0aa5d87ffb3bd0039d62afa5e8782d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 16:40:38 +0100 Subject: [PATCH 10/17] refactor(nuxt): use check-component-usage plugin to log warnings at runtime --- .../src/app/plugins/check-component-usage.ts | 26 ++++++++++++ packages/nuxt/src/core/nuxt.ts | 40 +++++++------------ .../core/plugins/detect-component-usage.ts | 2 + 3 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 packages/nuxt/src/app/plugins/check-component-usage.ts diff --git a/packages/nuxt/src/app/plugins/check-component-usage.ts b/packages/nuxt/src/app/plugins/check-component-usage.ts new file mode 100644 index 000000000000..be923c2c9578 --- /dev/null +++ b/packages/nuxt/src/app/plugins/check-component-usage.ts @@ -0,0 +1,26 @@ +import { defineNuxtPlugin } from '../nuxt' +// @ts-expect-error virtual file +import { hasPages, isNuxtLayoutUsed, isNuxtPageUsed } from '#build/detected-component-usage.mjs' +// @ts-expect-error virtual file +import layouts from '#build/layouts' + +export default defineNuxtPlugin({ + name: 'nuxt:check-component-usage', + setup (nuxtApp) { + const cache = new Set() + + nuxtApp.hook('app:mounted', () => { + if (Object.keys(layouts).length > 0 && !isNuxtLayoutUsed && !cache.has('NuxtLayout')) { + console.warn('[nuxt] Your project has layouts but the `` component has not been used') + cache.add('NuxtLayout') + } + + if (hasPages && !isNuxtPageUsed && !cache.has('NuxtPage')) { + console.warn('[nuxt] Your project has pages but the `` component has not been used.' + + ' You might be using the `` component instead, which will not work correctly in Nuxt.' + + ' You can set `pages: false` in `nuxt.config` if you do not wish to use the Nuxt `vue-router` integration.') + cache.add('NuxtPage') + } + }) + } +}) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 90d3f069ebd9..cc82062476cb 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -1,7 +1,7 @@ import { dirname, join, normalize, relative, resolve, sep } from 'pathe' import { createDebugger, createHooks } from 'hookable' import type { LoadNuxtOptions } from '@nuxt/kit' -import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' +import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' import { resolvePath as _resolvePath } from 'mlly' import type { Nuxt, NuxtHooks, NuxtOptions } from 'nuxt/schema' import type { PackageJson } from 'pkg-types' @@ -199,14 +199,12 @@ async function initNuxt (nuxt: Nuxt) { if (nuxt.options.dev) { // Add component usage detection - let hasBeenInitialized = false const detectedComponents = new Set() - const cache = new Set() const componentDetectionConfig = { rootDir: nuxt.options.rootDir, exclude: [ - // Exclude top-level resolutions by plugins + // Exclude top-level resolutions by plugins join(nuxt.options.rootDir, 'index.html'), // Keep only imports coming from the user's project (inside the rootDir) new RegExp(`^(?!${escapeRE(nuxt.options.rootDir)}${escapeRE(sep)}).+[^\n]+$`) @@ -216,28 +214,20 @@ async function initNuxt (nuxt: Nuxt) { addBuildPlugin(DetectComponentUsagePlugin(componentDetectionConfig)) - nuxt.hook('app:templates', (app) => { - // Skip first hook call - if (!hasBeenInitialized) { - hasBeenInitialized = true - return - } - - if (Object.keys(app.layouts).length > 0) { - if (!detectedComponents.has('NuxtLayout') && !cache.has('NuxtLayout')) { - logger.warn('[nuxt] Your project has layouts but the `` component has not been used.') - } - cache.add('NuxtLayout') - } - if (nuxt.options.pages) { - if (!detectedComponents.has('NuxtPage') && !cache.has('NuxtPage')) { - logger.warn('[nuxt] Your project has pages but the `` component has not been used.' + - ' You might be using the `` component instead, which will not work correctly in Nuxt.' + - ' You can set `pages: false` in `nuxt.config` if you do not wish to use the Nuxt `vue-router` integration.') - } - cache.add('NuxtPage') - } + addTemplate({ + filename: 'detected-component-usage.mjs', + options: { detectedComponents, nuxtOptions: nuxt.options }, + getContents: data => + `export const hasPages = ${data.options.nuxtOptions.pages}; + export const isNuxtLayoutUsed = ${data.options.detectedComponents.has( + 'NuxtLayout' + )}; + export const isNuxtPageUsed = ${data.options.detectedComponents.has( + 'NuxtPage' + )};` }) + + addPlugin(resolve(nuxt.options.appDir, 'plugins/check-component-usage')) } if (nuxt.options.dev && nuxt.options.features.devLogs) { diff --git a/packages/nuxt/src/core/plugins/detect-component-usage.ts b/packages/nuxt/src/core/plugins/detect-component-usage.ts index d30c02862923..01547a6c9bdc 100644 --- a/packages/nuxt/src/core/plugins/detect-component-usage.ts +++ b/packages/nuxt/src/core/plugins/detect-component-usage.ts @@ -1,5 +1,6 @@ import { createUnplugin } from 'unplugin' import { isAbsolute, join, relative } from 'pathe' +import { updateTemplates } from '@nuxt/kit' interface DetectComponentUsageOptions { rootDir: string @@ -31,6 +32,7 @@ export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) for (const [pattern, name] of detectComponentUsagePatterns) { if (pattern instanceof RegExp ? pattern.test(id) : pattern === id) { options.detectedComponents.add(name) + updateTemplates({ filter: template => template.filename === 'detected-component-usage.mjs' }) } } return null From 06ff4dfd0776b7783523cae471946d2034d0eb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 16:58:42 +0100 Subject: [PATCH 11/17] refactor(nuxt): use absolute path with distDir for detect-component-usage --- packages/nuxt/src/app/plugins/check-component-usage.ts | 2 +- .../nuxt/src/core/plugins/detect-component-usage.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/nuxt/src/app/plugins/check-component-usage.ts b/packages/nuxt/src/app/plugins/check-component-usage.ts index be923c2c9578..8c8da20123f3 100644 --- a/packages/nuxt/src/app/plugins/check-component-usage.ts +++ b/packages/nuxt/src/app/plugins/check-component-usage.ts @@ -11,7 +11,7 @@ export default defineNuxtPlugin({ nuxtApp.hook('app:mounted', () => { if (Object.keys(layouts).length > 0 && !isNuxtLayoutUsed && !cache.has('NuxtLayout')) { - console.warn('[nuxt] Your project has layouts but the `` component has not been used') + console.warn('[nuxt] Your project has layouts but the `` component has not been used.') cache.add('NuxtLayout') } diff --git a/packages/nuxt/src/core/plugins/detect-component-usage.ts b/packages/nuxt/src/core/plugins/detect-component-usage.ts index 01547a6c9bdc..362ed3aa0126 100644 --- a/packages/nuxt/src/core/plugins/detect-component-usage.ts +++ b/packages/nuxt/src/core/plugins/detect-component-usage.ts @@ -1,6 +1,7 @@ import { createUnplugin } from 'unplugin' -import { isAbsolute, join, relative } from 'pathe' +import { join, resolve } from 'pathe' import { updateTemplates } from '@nuxt/kit' +import { distDir } from '../../dirs' interface DetectComponentUsageOptions { rootDir: string @@ -12,8 +13,8 @@ export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) const importersToExclude = options?.exclude || [] const detectComponentUsagePatterns: Array<[importPattern: string | RegExp, name: string]> = [ - ['../packages/nuxt/src/pages/runtime/page', 'NuxtPage'], - ['../packages/nuxt/src/app/components/nuxt-layout', 'NuxtLayout'] + [resolve(distDir, 'pages/runtime/page'), 'NuxtPage'], + [resolve(distDir, 'app/components/nuxt-layout'), 'NuxtLayout'] ] return { @@ -24,9 +25,6 @@ export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) 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 } for (const [pattern, name] of detectComponentUsagePatterns) { From aa4eda9fffc4d55dc9ebc1e487b8cd749e0ad3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 17:01:35 +0100 Subject: [PATCH 12/17] refactor(nuxt): hardcode sep character --- packages/nuxt/src/core/nuxt.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index cc82062476cb..34c16953582d 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -1,4 +1,4 @@ -import { dirname, join, normalize, relative, resolve, sep } from 'pathe' +import { dirname, join, normalize, relative, resolve } from 'pathe' import { createDebugger, createHooks } from 'hookable' import type { LoadNuxtOptions } from '@nuxt/kit' import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerPlugin, addTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' @@ -207,7 +207,7 @@ async function initNuxt (nuxt: Nuxt) { // Exclude top-level resolutions by plugins join(nuxt.options.rootDir, 'index.html'), // Keep only imports coming from the user's project (inside the rootDir) - new RegExp(`^(?!${escapeRE(nuxt.options.rootDir)}${escapeRE(sep)}).+[^\n]+$`) + new RegExp(`^(?!${escapeRE(nuxt.options.rootDir)}/).+[^\n]+$`) ], detectedComponents } From 9e58137614076c89028fe31ab619c9ea3963864a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 23:10:20 +0100 Subject: [PATCH 13/17] test: fix runtime-compiler fixture --- test/fixtures/runtime-compiler/app.vue | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/fixtures/runtime-compiler/app.vue diff --git a/test/fixtures/runtime-compiler/app.vue b/test/fixtures/runtime-compiler/app.vue new file mode 100644 index 000000000000..8f62b8bf9218 --- /dev/null +++ b/test/fixtures/runtime-compiler/app.vue @@ -0,0 +1,3 @@ + From 0f986b006445b536f052db6c7d53b3b3c668e9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Mon, 18 Mar 2024 23:41:43 +0100 Subject: [PATCH 14/17] test: fix basic fixture --- test/fixtures/basic/app.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/app.vue diff --git a/test/fixtures/basic/app.vue b/test/fixtures/basic/app.vue new file mode 100644 index 000000000000..f8eacfa737ec --- /dev/null +++ b/test/fixtures/basic/app.vue @@ -0,0 +1,5 @@ + From 4a9609dfe29f57fc4a82e59560db46ce2f94865a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Tue, 19 Mar 2024 15:09:06 +0100 Subject: [PATCH 15/17] Revert "test: fix runtime-compiler fixture" and "test: fix basic fixture" This reverts the following commits: fe0c830c58c337e6e9ce793cc5967a02031129dc eb80c313d01a825f1f615a35ea7b1e0be6a41833 --- test/fixtures/basic/app.vue | 5 ----- test/fixtures/runtime-compiler/app.vue | 3 --- 2 files changed, 8 deletions(-) delete mode 100644 test/fixtures/basic/app.vue delete mode 100644 test/fixtures/runtime-compiler/app.vue diff --git a/test/fixtures/basic/app.vue b/test/fixtures/basic/app.vue deleted file mode 100644 index f8eacfa737ec..000000000000 --- a/test/fixtures/basic/app.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/test/fixtures/runtime-compiler/app.vue b/test/fixtures/runtime-compiler/app.vue deleted file mode 100644 index 8f62b8bf9218..000000000000 --- a/test/fixtures/runtime-compiler/app.vue +++ /dev/null @@ -1,3 +0,0 @@ - From 4132d38b523aca282849a97dfdf121622d9b9412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Labreuil?= Date: Tue, 19 Mar 2024 15:11:27 +0100 Subject: [PATCH 16/17] fix(nuxt): include the generated runtime app.vue for component usage detection --- packages/nuxt/src/core/nuxt.ts | 4 ++++ packages/nuxt/src/core/plugins/detect-component-usage.ts | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 34c16953582d..94cc2b689565 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -209,6 +209,10 @@ async function initNuxt (nuxt: Nuxt) { // Keep only imports coming from the user's project (inside the rootDir) new RegExp(`^(?!${escapeRE(nuxt.options.rootDir)}/).+[^\n]+$`) ], + include: [ + // Keep the imports coming from the auto-generated runtime app.vue + resolve(distDir, 'pages/runtime/app.vue') + ], detectedComponents } diff --git a/packages/nuxt/src/core/plugins/detect-component-usage.ts b/packages/nuxt/src/core/plugins/detect-component-usage.ts index 362ed3aa0126..84711a6eb090 100644 --- a/packages/nuxt/src/core/plugins/detect-component-usage.ts +++ b/packages/nuxt/src/core/plugins/detect-component-usage.ts @@ -6,11 +6,13 @@ import { distDir } from '../../dirs' interface DetectComponentUsageOptions { rootDir: string exclude?: Array + include?: Array detectedComponents: Set } export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) => createUnplugin(() => { const importersToExclude = options?.exclude || [] + const importersToInclude = options?.include || [] const detectComponentUsagePatterns: Array<[importPattern: string | RegExp, name: string]> = [ [resolve(distDir, 'pages/runtime/page'), 'NuxtPage'], @@ -25,7 +27,9 @@ export const DetectComponentUsagePlugin = (options: DetectComponentUsageOptions) if (id[0] === '.') { id = join(importer, '..', id) } - if (importersToExclude.some(p => typeof p === 'string' ? importer === p : p.test(importer))) { return } + const isExcludedImporter = importersToExclude.some(p => typeof p === 'string' ? importer === p : p.test(importer)) + const isIncludedImporter = importersToInclude.some(p => typeof p === 'string' ? importer === p : p.test(importer)) + if (isExcludedImporter && !isIncludedImporter) { return } for (const [pattern, name] of detectComponentUsagePatterns) { if (pattern instanceof RegExp ? pattern.test(id) : pattern === id) { From 8145b408765b76f1f9b7326cd2f1693cfeafd9ed Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 27 Mar 2024 09:24:47 +0000 Subject: [PATCH 17/17] refactor: simplify template --- packages/nuxt/src/core/nuxt.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 94cc2b689565..effbd1cae4ad 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -201,7 +201,7 @@ async function initNuxt (nuxt: Nuxt) { // Add component usage detection const detectedComponents = new Set() - const componentDetectionConfig = { + addBuildPlugin(DetectComponentUsagePlugin({ rootDir: nuxt.options.rootDir, exclude: [ // Exclude top-level resolutions by plugins @@ -214,21 +214,16 @@ async function initNuxt (nuxt: Nuxt) { resolve(distDir, 'pages/runtime/app.vue') ], detectedComponents - } - - addBuildPlugin(DetectComponentUsagePlugin(componentDetectionConfig)) + })) addTemplate({ filename: 'detected-component-usage.mjs', - options: { detectedComponents, nuxtOptions: nuxt.options }, - getContents: data => - `export const hasPages = ${data.options.nuxtOptions.pages}; - export const isNuxtLayoutUsed = ${data.options.detectedComponents.has( - 'NuxtLayout' - )}; - export const isNuxtPageUsed = ${data.options.detectedComponents.has( - 'NuxtPage' - )};` + getContents: ({ nuxt }) => + [ + `export const hasPages = ${nuxt.options.pages}`, + `export const isNuxtLayoutUsed = ${detectedComponents.has('NuxtLayout')}`, + `export const isNuxtPageUsed = ${detectedComponents.has('NuxtPage')}` + ].join('\n') }) addPlugin(resolve(nuxt.options.appDir, 'plugins/check-component-usage'))