@@ -12,6 +12,7 @@ import type { json, logging } from '@angular-devkit/core';
12
12
import type { Plugin } from 'esbuild' ;
13
13
import { lookup as lookupMimeType } from 'mrmime' ;
14
14
import assert from 'node:assert' ;
15
+ import { randomUUID } from 'node:crypto' ;
15
16
import { readFile } from 'node:fs/promises' ;
16
17
import { ServerResponse } from 'node:http' ;
17
18
import type { AddressInfo } from 'node:net' ;
@@ -182,7 +183,7 @@ export async function* serveWithVite(
182
183
}
183
184
184
185
if ( server ) {
185
- handleUpdate ( generatedFiles , server , serverOptions , context . logger ) ;
186
+ handleUpdate ( normalizePath , generatedFiles , server , serverOptions , context . logger ) ;
186
187
} else {
187
188
const projectName = context . target ?. project ;
188
189
if ( ! projectName ) {
@@ -230,6 +231,7 @@ export async function* serveWithVite(
230
231
}
231
232
232
233
function handleUpdate (
234
+ normalizePath : ( id : string ) => string ,
233
235
generatedFiles : Map < string , OutputFileRecord > ,
234
236
server : ViteDevServer ,
235
237
serverOptions : NormalizedDevServerOptions ,
@@ -241,7 +243,9 @@ function handleUpdate(
241
243
for ( const [ file , record ] of generatedFiles ) {
242
244
if ( record . updated ) {
243
245
updatedFiles . push ( file ) ;
244
- const updatedModules = server . moduleGraph . getModulesByFile ( file ) ;
246
+ const updatedModules = server . moduleGraph . getModulesByFile (
247
+ normalizePath ( path . join ( server . config . root , file ) ) ,
248
+ ) ;
245
249
updatedModules ?. forEach ( ( m ) => server ?. moduleGraph . invalidateModule ( m ) ) ;
246
250
}
247
251
}
@@ -255,9 +259,7 @@ function handleUpdate(
255
259
const timestamp = Date . now ( ) ;
256
260
server . ws . send ( {
257
261
type : 'update' ,
258
- updates : updatedFiles . map ( ( f ) => {
259
- const filePath = f . slice ( 1 ) ; // Remove leading slash.
260
-
262
+ updates : updatedFiles . map ( ( filePath ) => {
261
263
return {
262
264
type : 'css-update' ,
263
265
timestamp,
@@ -298,7 +300,7 @@ function analyzeResultFiles(
298
300
// This mimics the Webpack dev-server behavior.
299
301
filePath = '/index.html' ;
300
302
} else {
301
- filePath = '/' + normalizePath ( file . path ) ;
303
+ filePath = normalizePath ( file . path ) ;
302
304
}
303
305
seen . add ( filePath ) ;
304
306
@@ -365,11 +367,16 @@ export async function setupServer(
365
367
// dynamically import Vite for ESM compatibility
366
368
const { normalizePath } = await import ( 'vite' ) ;
367
369
370
+ // Path will not exist on disk and only used to provide separate path for Vite requests
371
+ const virtualProjectRoot = normalizePath (
372
+ path . join ( serverOptions . workspaceRoot , `.angular/vite-root/${ randomUUID ( ) } /` ) ,
373
+ ) ;
374
+
368
375
const configuration : InlineConfig = {
369
376
configFile : false ,
370
377
envFile : false ,
371
378
cacheDir : path . join ( serverOptions . cacheOptions . path , 'vite' ) ,
372
- root : serverOptions . workspaceRoot ,
379
+ root : virtualProjectRoot ,
373
380
publicDir : false ,
374
381
esbuild : false ,
375
382
mode : 'development' ,
@@ -399,7 +406,7 @@ export async function setupServer(
399
406
} ,
400
407
ssr : {
401
408
// Exclude any provided dependencies (currently build defined externals)
402
- external : externalMetadata . implicit ,
409
+ external : externalMetadata . explicit ,
403
410
} ,
404
411
plugins : [
405
412
createAngularLocaleDataPlugin ( ) ,
@@ -415,27 +422,32 @@ export async function setupServer(
415
422
return source ;
416
423
}
417
424
418
- if ( importer && source . startsWith ( '.' ) ) {
425
+ if ( importer && source [ 0 ] === '.' && importer . startsWith ( virtualProjectRoot ) ) {
419
426
// Remove query if present
420
427
const [ importerFile ] = importer . split ( '?' , 1 ) ;
421
428
422
- source = normalizePath ( path . join ( path . dirname ( importerFile ) , source ) ) ;
429
+ source = normalizePath (
430
+ path . join ( path . dirname ( path . relative ( virtualProjectRoot , importerFile ) ) , source ) ,
431
+ ) ;
432
+ }
433
+ if ( source [ 0 ] === '/' ) {
434
+ source = source . slice ( 1 ) ;
423
435
}
424
-
425
436
const [ file ] = source . split ( '?' , 1 ) ;
426
437
if ( outputFiles . has ( file ) ) {
427
- return source ;
438
+ return path . join ( virtualProjectRoot , source ) ;
428
439
}
429
440
} ,
430
441
load ( id ) {
431
442
const [ file ] = id . split ( '?' , 1 ) ;
432
- const codeContents = outputFiles . get ( file ) ?. contents ;
443
+ const relativeFile = normalizePath ( path . relative ( virtualProjectRoot , file ) ) ;
444
+ const codeContents = outputFiles . get ( relativeFile ) ?. contents ;
433
445
if ( codeContents === undefined ) {
434
446
return ;
435
447
}
436
448
437
449
const code = Buffer . from ( codeContents ) . toString ( 'utf-8' ) ;
438
- const mapContents = outputFiles . get ( file + '.map' ) ?. contents ;
450
+ const mapContents = outputFiles . get ( relativeFile + '.map' ) ?. contents ;
439
451
440
452
return {
441
453
// Remove source map URL comments from the code if a sourcemap is present.
@@ -532,7 +544,7 @@ export async function setupServer(
532
544
return ;
533
545
}
534
546
535
- const rawHtml = outputFiles . get ( '/ index.server.html' ) ?. contents ;
547
+ const rawHtml = outputFiles . get ( 'index.server.html' ) ?. contents ;
536
548
if ( ! rawHtml ) {
537
549
next ( ) ;
538
550
0 commit comments