@@ -2,7 +2,9 @@ import { log } from './log';
22//eslint-disable-next-line node/no-missing-import
33import { findClosestPkgJsonPath } from 'vitefu' ;
44import { readFileSync } from 'fs' ;
5+ import { dirname } from 'path' ;
56import { performance } from 'perf_hooks' ;
7+ import { normalizePath } from 'vite' ;
68
79interface Stat {
810 file : string ;
@@ -85,6 +87,27 @@ function formatPackageStats(pkgStats: PackageStats[]) {
8587 return table ;
8688}
8789
90+ /**
91+ * utility to get the package name a file belongs to
92+ *
93+ * @param {string } file to find package for
94+ * @returns {path:string,name:string } tuple of path,name where name is the parsed package name and path is the normalized path to it
95+ */
96+ async function getClosestNamedPackage ( file : string ) : Promise < { name : string ; path : string } > {
97+ let name = '$unknown' ;
98+ let path = await findClosestPkgJsonPath ( file , ( pkgPath ) => {
99+ const pkg = JSON . parse ( readFileSync ( pkgPath , 'utf-8' ) ) ;
100+ if ( pkg . name != null ) {
101+ name = pkg . name ;
102+ return true ;
103+ }
104+ return false ;
105+ } ) ;
106+ // return normalized path with appended '/' so .startsWith works for future file checks
107+ path = normalizePath ( dirname ( path ?? file ) ) + '/' ;
108+ return { name, path } ;
109+ }
110+
88111export class VitePluginSvelteStats {
89112 // package directory -> package name
90113 private _packages : { path : string ; name : string } [ ] = [ ] ;
@@ -108,6 +131,7 @@ export class VitePluginSvelteStats {
108131 if ( collection . finished ) {
109132 throw new Error ( 'called after finish() has been used' ) ;
110133 }
134+ file = normalizePath ( file ) ;
111135 const start = performance . now ( ) ;
112136 const stat : Stat = { file, start, end : start } ;
113137 return ( ) => {
@@ -133,54 +157,41 @@ export class VitePluginSvelteStats {
133157 }
134158
135159 private async _finish ( collection : StatCollection ) {
136- collection . finished = true ;
137- const now = performance . now ( ) ;
138- collection . duration = now - collection . collectionStart ;
139- const logResult = collection . options . logResult ( collection ) ;
140- if ( logResult ) {
141- await this . _aggregateStatsResult ( collection ) ;
142- log . info ( `${ collection . name } done.` , formatPackageStats ( collection . packageStats ! ) ) ;
143- }
144- // cut some ties to free it for garbage collection
145- const index = this . _collections . indexOf ( collection ) ;
146- this . _collections . splice ( index , 1 ) ;
147- collection . stats . length = 0 ;
148- collection . stats = [ ] ;
149- if ( collection . packageStats ) {
150- collection . packageStats . length = 0 ;
151- collection . packageStats = [ ] ;
160+ try {
161+ collection . finished = true ;
162+ const now = performance . now ( ) ;
163+ collection . duration = now - collection . collectionStart ;
164+ const logResult = collection . options . logResult ( collection ) ;
165+ if ( logResult ) {
166+ await this . _aggregateStatsResult ( collection ) ;
167+ log . info ( `${ collection . name } done.` , formatPackageStats ( collection . packageStats ! ) ) ;
168+ }
169+ // cut some ties to free it for garbage collection
170+ const index = this . _collections . indexOf ( collection ) ;
171+ this . _collections . splice ( index , 1 ) ;
172+ collection . stats . length = 0 ;
173+ collection . stats = [ ] ;
174+ if ( collection . packageStats ) {
175+ collection . packageStats . length = 0 ;
176+ collection . packageStats = [ ] ;
177+ }
178+ collection . start = ( ) => ( ) => { } ;
179+ collection . finish = ( ) => { } ;
180+ } catch ( e ) {
181+ // this should not happen, but stats taking also should not break the process
182+ log . debug . once ( `failed to finish stats for ${ collection . name } ` , e ) ;
152183 }
153- collection . start = ( ) => ( ) => { } ;
154- collection . finish = ( ) => { } ;
155184 }
156185
157186 private async _aggregateStatsResult ( collection : StatCollection ) {
158187 const stats = collection . stats ;
159188 for ( const stat of stats ) {
160189 let pkg = this . _packages . find ( ( p ) => stat . file . startsWith ( p . path ) ) ;
161190 if ( ! pkg ) {
162- // check for package.json first
163- let pkgPath = await findClosestPkgJsonPath ( stat . file ) ;
164- if ( pkgPath ) {
165- let path = pkgPath ?. replace ( / p a c k a g e .j s o n $ / , '' ) ;
166- let name = JSON . parse ( readFileSync ( pkgPath , 'utf-8' ) ) . name ;
167- if ( ! name ) {
168- // some packages have nameless nested package.json
169- pkgPath = await findClosestPkgJsonPath ( path ) ;
170- if ( pkgPath ) {
171- path = pkgPath ?. replace ( / p a c k a g e .j s o n $ / , '' ) ;
172- name = JSON . parse ( readFileSync ( pkgPath , 'utf-8' ) ) . name ;
173- }
174- }
175- if ( path && name ) {
176- pkg = { path, name } ;
177- this . _packages . push ( pkg ) ;
178- }
179- }
191+ pkg = await getClosestNamedPackage ( stat . file ) ;
192+ this . _packages . push ( pkg ) ;
180193 }
181- // TODO is it possible that we want to track files where there is no named packge.json as parent?
182- // what do we want to do for that, try to find common root paths for different stats?
183- stat . pkg = pkg ?. name ?? '$unknown' ;
194+ stat . pkg = pkg . name ;
184195 }
185196
186197 // group stats
0 commit comments