diff --git a/packages/vite/src/plugins/ssr-styles.ts b/packages/vite/src/plugins/ssr-styles.ts index b04c1c7420..c436c2fa8c 100644 --- a/packages/vite/src/plugins/ssr-styles.ts +++ b/packages/vite/src/plugins/ssr-styles.ts @@ -41,20 +41,19 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin { resolveId: { order: 'pre', async handler (id, importer, _options) { - // We deliberately prevent importing `#build/css` to avoid including it in the client bundle - // in its entirety. We will instead include _just_ the styles that can't be inlined, - // in the component below - if (options.mode === 'client' && id === '#build/css' && (options.shouldInline === true || (typeof options.shouldInline === 'function' && options.shouldInline(importer)))) { - return this.resolve('unenv/runtime/mock/empty', importer, _options) + // We want to remove side effects (namely, emitting CSS) from `.vue` files and explicitly imported `.css` files + // but only as long as we are going to inline that CSS. + if ((options.shouldInline === false || (typeof options.shouldInline === 'function' && !options.shouldInline(importer)))) { + return } - if (options.mode === 'client' || !id.endsWith('.vue')) { return } - - const res = await this.resolve(id, importer, { ..._options, skipSelf: true }) - if (res) { - return { - ...res, - moduleSideEffects: false + if (id === '#build/css' || id.endsWith('.vue') || isCSS(id)) { + const res = await this.resolve(id, importer, { ..._options, skipSelf: true }) + if (res) { + return { + ...res, + moduleSideEffects: false + } } } } diff --git a/test/basic.test.ts b/test/basic.test.ts index aea98b7ba7..cc28628073 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -1180,6 +1180,13 @@ describe.skipIf(isDev() || isWebpack)('inlining component styles', () => { } }) + it('should emit assets referenced in inlined CSS', async () => { + // @ts-expect-error ssssh! untyped secret property + const publicDir = useTestContext().nuxt._nitro.options.output.publicDir + const files = await readdir(join(publicDir, '_nuxt')).catch(() => []) + expect(files.map(m => m.replace(/\.\w+(\.\w+)$/, '$1'))).toContain('css-only-asset.svg') + }) + it('should not include inlined CSS in generated CSS file', async () => { const html: string = await $fetch('/styles') const cssFiles = new Set([...html.matchAll(/]*href="([^"]*\.css)">/g)].map(m => m[1])) diff --git a/test/fixtures/basic/assets/css-only-asset.svg b/test/fixtures/basic/assets/css-only-asset.svg new file mode 100644 index 0000000000..ebe18164a1 --- /dev/null +++ b/test/fixtures/basic/assets/css-only-asset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/test/fixtures/basic/assets/global.css b/test/fixtures/basic/assets/global.css index b4b49a9637..3d0b218a70 100644 --- a/test/fixtures/basic/assets/global.css +++ b/test/fixtures/basic/assets/global.css @@ -1,4 +1,4 @@ :root { --global: 'global'; - --asset: url('~/assets/logo.svg'); + --asset: url('~/assets/css-only-asset.svg'); }