@@ -13,6 +13,7 @@ import { resolveClientEntry } from '../utils/config'
1313import { useNitro } from '@nuxt/kit'
1414
1515const SUPPORTED_FILES_RE = / \. (?: v u e | (?: [ c m ] ? j | t ) s x ? ) $ /
16+ const QUERY_RE = / \? .+ $ /
1617
1718export function SSRStylesPlugin ( nuxt :Nuxt ) :Plugin | undefined {
1819if ( nuxt . options . dev ) { return }
@@ -49,7 +50,16 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
4950globalCSS :nuxt . options . css ,
5051}
5152
52- const relativeToSrcDir = ( path :string ) => relative ( nuxt . options . srcDir , path )
53+ // relative file lookup has duplicate checks
54+ const relativeCache = new Map < string , string > ( )
55+ const relativeToSrcDir = ( path :string ) => {
56+ let cached = relativeCache . get ( path )
57+ if ( cached === undefined ) {
58+ cached = relative ( nuxt . options . srcDir , path )
59+ relativeCache . set ( path , cached )
60+ }
61+ return cached
62+ }
5363
5464const warnCache = new Set < string > ( )
5565const components = nuxt . apps . default ! . components || [ ]
@@ -58,6 +68,7 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
5868// .server components without a corresponding .client component will need to be rendered as an island
5969( component . mode === 'server' && ! components . some ( c => c . pascalName === component . pascalName && c . mode === 'client' ) ) ,
6070)
71+ const islandPaths = new Set ( islands . map ( c => c . filePath ) )
6172
6273let entry :string
6374
@@ -192,7 +203,7 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
192203
193204const relativePath = relativeToSrcDir ( moduleId )
194205if ( relativePath in cssMap ) {
195- cssMap [ relativePath ] ! . inBundle = cssMap [ relativePath ] ! . inBundle ?? ( ( isVue ( moduleId ) && ! ! relativeToSrcDir ( moduleId ) ) || isEntry )
206+ cssMap [ relativePath ] ! . inBundle = cssMap [ relativePath ] ! . inBundle ?? ( ( isVue ( moduleId ) && ! ! relativePath ) || isEntry )
196207}
197208}
198209
@@ -232,37 +243,40 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
232243
233244const { pathname, search} = parseURL ( decodeURIComponent ( pathToFileURL ( id ) . href ) )
234245
235- if ( ! ( id in clientCSSMap ) && ! islands . some ( c => c . filePath === pathname ) ) { return }
246+ if ( ! ( id in clientCSSMap ) && ! islandPaths . has ( pathname ) ) { return }
236247
237248const query = parseQuery ( search )
238249if ( query . macro || query . nuxt_component ) { return }
239250
240- if ( ! islands . some ( c => c . filePath === pathname ) ) {
251+ if ( ! islandPaths . has ( pathname ) ) {
241252if ( options . shouldInline === false || ( typeof options . shouldInline === 'function' && ! options . shouldInline ( id ) ) ) { return }
242253}
243254
244255const relativeId = relativeToSrcDir ( id )
245256const idMap = cssMap [ relativeId ] ||= { files :[ ] }
246257
247258const emittedIds = new Set < string > ( )
259+ const idFilename = filename ( id )
248260
249261let styleCtr = 0
250262const ids = clientCSSMap [ id ] || [ ]
251263for ( const file of ids ) {
264+ if ( emittedIds . has ( file ) ) { continue }
265+ const fileInline = file + '?inline&used'
252266const resolved = await this . resolve ( file ) ?? await this . resolve ( file , id )
253- const res = await this . resolve ( file + '?inline&used' ) ?? await this . resolve ( file + '?inline&used' , id )
267+ const res = await this . resolve ( fileInline ) ?? await this . resolve ( fileInline , id )
254268if ( ! resolved || ! res ) {
255269if ( ! warnCache . has ( file ) ) {
256270warnCache . add ( file )
257271this . warn ( `[nuxt] Cannot extract styles for \`${ file } \`. Its styles will not be inlined when server-rendering.` )
258272}
259273continue
260274}
261- if ( emittedIds . has ( file ) ) { continue }
275+ emittedIds . add ( file )
262276const ref = this . emitFile ( {
263277type :'chunk' ,
264- name :`${ filename ( id ) } -styles-${ ++ styleCtr } .mjs` ,
265- id :file + '?inline&used' ,
278+ name :`${ idFilename } -styles-${ ++ styleCtr } .mjs` ,
279+ id :fileInline ,
266280} )
267281
268282idRefMap [ relativeToSrcDir ( file ) ] = ref
@@ -272,12 +286,12 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
272286if ( ! SUPPORTED_FILES_RE . test ( pathname ) ) { return }
273287
274288for ( const i of findStaticImports ( code ) ) {
275- const { type} = parseQuery ( i . specifier )
276- if ( type !== 'style' && ! i . specifier . endsWith ( '.css' ) ) { continue }
289+ if ( ! i . specifier . endsWith ( '.css' ) && parseQuery ( i . specifier ) . type !== 'style' ) { continue }
277290
278291const resolved = await this . resolve ( i . specifier , id )
279292if ( ! resolved ) { continue }
280- if ( ! ( await this . resolve ( resolved . id + '?inline&used' ) ) ) {
293+ const resolvedIdInline = resolved . id + '?inline&used'
294+ if ( ! ( await this . resolve ( resolvedIdInline ) ) ) {
281295if ( ! warnCache . has ( resolved . id ) ) {
282296warnCache . add ( resolved . id )
283297this . warn ( `[nuxt] Cannot extract styles for \`${ i . specifier } \`. Its styles will not be inlined when server-rendering.` )
@@ -288,8 +302,8 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
288302if ( emittedIds . has ( resolved . id ) ) { continue }
289303const ref = this . emitFile ( {
290304type :'chunk' ,
291- name :`${ filename ( id ) } -styles-${ ++ styleCtr } .mjs` ,
292- id :resolved . id + '?inline&used' ,
305+ name :`${ idFilename } -styles-${ ++ styleCtr } .mjs` ,
306+ id :resolvedIdInline ,
293307} )
294308
295309idRefMap [ relativeToSrcDir ( resolved . id ) ] = ref
@@ -302,5 +316,5 @@ export function SSRStylesPlugin (nuxt: Nuxt): Plugin | undefined {
302316}
303317
304318function filename ( name :string ) {
305- return _filename ( name . replace ( / \? . + $ / , '' ) )
319+ return _filename ( name . replace ( QUERY_RE , '' ) )
306320}