diff --git a/packages/nuxt/src/components/client-fallback-auto-id.ts b/packages/nuxt/src/components/client-fallback-auto-id.ts index 9d642d55f7..5b2d00f059 100644 --- a/packages/nuxt/src/components/client-fallback-auto-id.ts +++ b/packages/nuxt/src/components/client-fallback-auto-id.ts @@ -3,7 +3,7 @@ import type { ComponentsOptions } from '@nuxt/schema' import MagicString from 'magic-string' import { isAbsolute, relative } from 'pathe' import { hash } from 'ohash' -import { isVueTemplate } from './helpers' +import { isVue } from '../core/utils' interface LoaderOptions { sourcemap?: boolean transform?: ComponentsOptions['transform'], @@ -25,7 +25,7 @@ export const clientFallbackAutoIdPlugin = createUnplugin((options: LoaderOptions if (include.some(pattern => id.match(pattern))) { return true } - return isVueTemplate(id) + return isVue(id, { type: ['template'] }) }, transform (code, id) { if (!CLIENT_FALLBACK_RE.test(code)) { return } diff --git a/packages/nuxt/src/components/helpers.ts b/packages/nuxt/src/components/helpers.ts deleted file mode 100644 index ebd4ccf5fe..0000000000 --- a/packages/nuxt/src/components/helpers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { pathToFileURL } from 'node:url' -import { parseQuery, parseURL } from 'ufo' - -export function isVueTemplate (id: string) { - // Bare `.vue` file (in Vite) - if (id.endsWith('.vue')) { - return true - } - - const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - if (!search) { - return false - } - - const query = parseQuery(search) - - // Macro - if (query.macro) { - return true - } - - // Non-Vue or Styles - if (!('vue' in query) || query.type === 'style') { - return false - } - - // Query `?vue&type=template` (in Webpack or external template) - return true -} diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 9b01961493..b496494016 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -6,7 +6,7 @@ import { resolve } from 'pathe' import type { Component, ComponentsOptions } from 'nuxt/schema' import { distDir } from '../dirs' -import { isVueTemplate } from './helpers' +import { isVue } from '../core/utils' interface LoaderOptions { getComponents (): Component[] @@ -31,7 +31,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { if (include.some(pattern => id.match(pattern))) { return true } - return isVueTemplate(id) + return isVue(id, { type: ['template', 'script'] }) }, transform (code) { const components = options.getComponents() diff --git a/packages/nuxt/src/core/plugins/tree-shake.ts b/packages/nuxt/src/core/plugins/tree-shake.ts index 6b0c3c611b..2a852e9c43 100644 --- a/packages/nuxt/src/core/plugins/tree-shake.ts +++ b/packages/nuxt/src/core/plugins/tree-shake.ts @@ -1,8 +1,7 @@ -import { pathToFileURL } from 'node:url' import { stripLiteral } from 'strip-literal' -import { parseQuery, parseURL } from 'ufo' import MagicString from 'magic-string' import { createUnplugin } from 'unplugin' +import { isJS, isVue } from '../utils' type ImportPath = string @@ -22,18 +21,7 @@ export const TreeShakeComposablesPlugin = createUnplugin((options: TreeShakeComp name: 'nuxt:tree-shake-composables:transform', enforce: 'post', transformInclude (id) { - const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - const { type } = parseQuery(search) - - // vue files - if (pathname.endsWith('.vue') && (type === 'script' || !search)) { - return true - } - - // js files - if (pathname.match(/\.((c|m)?j|t)sx?$/g)) { - return true - } + return isVue(id, { type: ['script'] }) || isJS(id) }, transform (code) { if (!code.match(COMPOSABLE_RE)) { return } diff --git a/packages/nuxt/src/core/plugins/unctx.ts b/packages/nuxt/src/core/plugins/unctx.ts index 6a93a72a44..19ce781e94 100644 --- a/packages/nuxt/src/core/plugins/unctx.ts +++ b/packages/nuxt/src/core/plugins/unctx.ts @@ -1,9 +1,9 @@ -import { pathToFileURL } from 'node:url' -import { parseQuery, parseURL } from 'ufo' import type { TransformerOptions } from 'unctx/transform' import { createTransformer } from 'unctx/transform' import { createUnplugin } from 'unplugin' +import { isJS, isVue } from '../utils' + const TRANSFORM_MARKER = '/* _processed_nuxt_unctx_transform */\n' interface UnctxTransformPluginOptions { @@ -17,22 +17,7 @@ export const UnctxTransformPlugin = createUnplugin((options: UnctxTransformPlugi name: 'unctx:transform', enforce: 'post', transformInclude (id) { - const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - const query = parseQuery(search) - - // Vue files - if ( - pathname.endsWith('.vue') || - 'macro' in query || - ('vue' in query && (query.type === 'template' || query.type === 'script' || 'setup' in query)) - ) { - return true - } - - // JavaScript files - if (pathname.match(/\.((c|m)?j|t)sx?$/g)) { - return true - } + return isVue(id) || isJS(id) }, transform (code) { // TODO: needed for webpack - update transform in unctx/unplugin? diff --git a/packages/nuxt/src/core/utils/index.ts b/packages/nuxt/src/core/utils/index.ts new file mode 100644 index 0000000000..9ce54f0f06 --- /dev/null +++ b/packages/nuxt/src/core/utils/index.ts @@ -0,0 +1,13 @@ +export * from './names' +export * from './plugins' + +export function uniqueBy (arr: T[], key: K) { + const res: T[] = [] + const seen = new Set() + for (const item of arr) { + if (seen.has(item[key])) { continue } + seen.add(item[key]) + res.push(item) + } + return res +} diff --git a/packages/nuxt/src/core/utils.ts b/packages/nuxt/src/core/utils/names.ts similarity index 65% rename from packages/nuxt/src/core/utils.ts rename to packages/nuxt/src/core/utils/names.ts index 46757ec20b..4684e21353 100644 --- a/packages/nuxt/src/core/utils.ts +++ b/packages/nuxt/src/core/utils/names.ts @@ -5,17 +5,6 @@ export function getNameFromPath (path: string) { return kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '') } -export function uniqueBy (arr: T[], key: K) { - const res: T[] = [] - const seen = new Set() - for (const item of arr) { - if (seen.has(item[key])) { continue } - seen.add(item[key]) - res.push(item) - } - return res -} - export function hasSuffix (path: string, suffix: string) { return basename(path).replace(extname(path), '').endsWith(suffix) } diff --git a/packages/nuxt/src/core/utils/plugins.ts b/packages/nuxt/src/core/utils/plugins.ts new file mode 100644 index 0000000000..9002a13369 --- /dev/null +++ b/packages/nuxt/src/core/utils/plugins.ts @@ -0,0 +1,38 @@ +import { pathToFileURL } from 'node:url' +import { parseQuery, parseURL } from 'ufo' + +export function isVue (id: string, opts: { type?: Array<'template' | 'script' | 'style'> } = {}) { + // Bare `.vue` file (in Vite) + const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) + if (id.endsWith('.vue') && !search) { + return true + } + + if (!search) { + return false + } + + const query = parseQuery(search) + + // Macro + if (query.macro && (!opts.type || opts.type.includes('script'))) { + return true + } + + // Non-Vue or Styles + const type = 'setup' in query ? 'script' : query.type as 'script' | 'template' | 'style' + if (!('vue' in query) || (opts.type && !opts.type.includes(type))) { + return false + } + + // Query `?vue&type=template` (in Webpack or external template) + return true +} + +const JS_RE = /\.((c|m)?j|t)sx?$/ + +export function isJS (id: string) { + // JavaScript files + const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href)) + return JS_RE.test(pathname) +} diff --git a/packages/nuxt/src/imports/transform.ts b/packages/nuxt/src/imports/transform.ts index ab2c08b0af..e623d81d49 100644 --- a/packages/nuxt/src/imports/transform.ts +++ b/packages/nuxt/src/imports/transform.ts @@ -1,18 +1,14 @@ -import { pathToFileURL } from 'node:url' import { createUnplugin } from 'unplugin' -import { parseQuery, parseURL } from 'ufo' import type { Unimport } from 'unimport' import { normalize } from 'pathe' import type { ImportsOptions } from 'nuxt/schema' +import { isJS, isVue } from '../core/utils' export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: { ctx: Unimport, options: Partial, sourcemap?: boolean }) => { return { name: 'nuxt:imports-transform', enforce: 'post', transformInclude (id) { - const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - const query = parseQuery(search) - // Included if (options.transform?.include?.some(pattern => id.match(pattern))) { return true @@ -23,18 +19,12 @@ export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: { ct } // Vue files - if ( - id.endsWith('.vue') || - 'macro' in query || - ('vue' in query && (query.type === 'template' || query.type === 'script' || 'setup' in query)) - ) { + if (isVue(id, { type: ['script', 'template'] })) { return true } // JavaScript files - if (pathname.match(/\.((c|m)?j|t)sx?$/g)) { - return true - } + return isJS(id) }, async transform (code, id) { id = normalize(id) diff --git a/packages/vite/src/plugins/pure-annotations.ts b/packages/vite/src/plugins/pure-annotations.ts index 3d604dfb8d..6f79b7ffbd 100644 --- a/packages/vite/src/plugins/pure-annotations.ts +++ b/packages/vite/src/plugins/pure-annotations.ts @@ -1,8 +1,7 @@ -import { pathToFileURL } from 'node:url' import MagicString from 'magic-string' -import { parseQuery, parseURL } from 'ufo' import { createUnplugin } from 'unplugin' import { stripLiteral } from 'strip-literal' +import { isJS, isVue } from '../../../nuxt/src/core/utils/plugins' export interface PureAnnotationsOptions { sourcemap: boolean @@ -16,18 +15,7 @@ export const pureAnnotationsPlugin = createUnplugin((options: PureAnnotationsOpt name: 'nuxt:pure-annotations', enforce: 'post', transformInclude (id) { - const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - const { type } = parseQuery(search) - - // vue files - if (pathname.endsWith('.vue') && (type === 'script' || !search)) { - return true - } - - // js files - if (pathname.match(/\.((c|m)?j|t)sx?$/g)) { - return true - } + return isVue(id, { type: ['script'] }) || isJS(id) }, transform (code) { if (!FUNCTION_RE_SINGLE.test(code)) { return } diff --git a/test/fixtures/basic/pages/index.vue b/test/fixtures/basic/pages/index.vue index b2e39ed571..7b6c4bb734 100644 --- a/test/fixtures/basic/pages/index.vue +++ b/test/fixtures/basic/pages/index.vue @@ -46,6 +46,11 @@ const config = useRuntimeConfig() const someValue = useState('val', () => 1) +const NestedSugarCounter = resolveComponent('NestedSugarCounter') +if (!NestedSugarCounter) { + throw new Error('Component not found') +} + definePageMeta({ alias: '/some-alias', other: ref('test'),