Graduation
/
ui
/node_modules
/@sveltejs
/vite-plugin-svelte
/src
/utils
/vite-plugin-svelte-stats.js
| import { log } from './log.js'; | |
| import { performance } from 'node:perf_hooks'; | |
| import { normalizePath } from 'vite'; | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').CollectionOptions} */ | |
| const defaultCollectionOptions = { | |
| // log after 500ms and more than one file processed | |
| logInProgress: (c, now) => now - c.collectionStart > 500 && c.stats.length > 1, | |
| // always log results | |
| logResult: () => true | |
| }; | |
| /** | |
| * @param {number} n | |
| * @returns | |
| */ | |
| function humanDuration(n) { | |
| // 99.9ms 0.10s | |
| return n < 100 ? `${n.toFixed(1)}ms` : `${(n / 1000).toFixed(2)}s`; | |
| } | |
| /** | |
| * @param {import('../types/vite-plugin-svelte-stats.d.ts').PackageStats[]} pkgStats | |
| * @returns {string} | |
| */ | |
| function formatPackageStats(pkgStats) { | |
| const statLines = pkgStats.map((pkgStat) => { | |
| const duration = pkgStat.duration; | |
| const avg = duration / pkgStat.files; | |
| return [pkgStat.pkg, `${pkgStat.files}`, humanDuration(duration), humanDuration(avg)]; | |
| }); | |
| statLines.unshift(['package', 'files', 'time', 'avg']); | |
| const columnWidths = statLines.reduce( | |
| (widths, row) => { | |
| for (let i = 0; i < row.length; i++) { | |
| const cell = row[i]; | |
| if (widths[i] < cell.length) { | |
| widths[i] = cell.length; | |
| } | |
| } | |
| return widths; | |
| }, | |
| statLines[0].map(() => 0) | |
| ); | |
| const table = statLines | |
| .map((row) => | |
| row | |
| .map((cell, i) => { | |
| if (i === 0) { | |
| return cell.padEnd(columnWidths[i], ' '); | |
| } else { | |
| return cell.padStart(columnWidths[i], ' '); | |
| } | |
| }) | |
| .join('\t') | |
| ) | |
| .join('\n'); | |
| return table; | |
| } | |
| /** | |
| * @class | |
| */ | |
| export class VitePluginSvelteStats { | |
| // package directory -> package name | |
| /** @type {import('./vite-plugin-svelte-cache.js').VitePluginSvelteCache} */ | |
| #cache; | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection[]} */ | |
| #collections = []; | |
| /** | |
| * @param {import('./vite-plugin-svelte-cache.js').VitePluginSvelteCache} cache | |
| */ | |
| constructor(cache) { | |
| this.#cache = cache; | |
| } | |
| /** | |
| * @param {string} name | |
| * @param {Partial<import('../types/vite-plugin-svelte-stats.d.ts').CollectionOptions>} [opts] | |
| * @returns {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} | |
| */ | |
| startCollection(name, opts) { | |
| const options = { | |
| ...defaultCollectionOptions, | |
| ...opts | |
| }; | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').Stat[]} */ | |
| const stats = []; | |
| const collectionStart = performance.now(); | |
| const _this = this; | |
| let hasLoggedProgress = false; | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} */ | |
| const collection = { | |
| name, | |
| options, | |
| stats, | |
| collectionStart, | |
| finished: false, | |
| start(file) { | |
| if (collection.finished) { | |
| throw new Error('called after finish() has been used'); | |
| } | |
| file = normalizePath(file); | |
| const start = performance.now(); | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').Stat} */ | |
| const stat = { file, start, end: start }; | |
| return () => { | |
| const now = performance.now(); | |
| stat.end = now; | |
| stats.push(stat); | |
| if (!hasLoggedProgress && options.logInProgress(collection, now)) { | |
| hasLoggedProgress = true; | |
| log.debug(`${name} in progress ...`, undefined, 'stats'); | |
| } | |
| }; | |
| }, | |
| async finish() { | |
| await _this.#finish(collection); | |
| } | |
| }; | |
| _this.#collections.push(collection); | |
| return collection; | |
| } | |
| async finishAll() { | |
| await Promise.all(this.#collections.map((c) => c.finish())); | |
| } | |
| /** | |
| * @param {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} collection | |
| */ | |
| async #finish(collection) { | |
| try { | |
| collection.finished = true; | |
| const now = performance.now(); | |
| collection.duration = now - collection.collectionStart; | |
| const logResult = collection.options.logResult(collection); | |
| if (logResult) { | |
| await this.#aggregateStatsResult(collection); | |
| log.debug( | |
| `${collection.name} done.\n${formatPackageStats( | |
| /** @type {import('../types/vite-plugin-svelte-stats.d.ts').PackageStats[]}*/ ( | |
| collection.packageStats | |
| ) | |
| )}`, | |
| undefined, | |
| 'stats' | |
| ); | |
| } | |
| // cut some ties to free it for garbage collection | |
| const index = this.#collections.indexOf(collection); | |
| this.#collections.splice(index, 1); | |
| collection.stats.length = 0; | |
| collection.stats = []; | |
| if (collection.packageStats) { | |
| collection.packageStats.length = 0; | |
| collection.packageStats = []; | |
| } | |
| collection.start = () => () => {}; | |
| collection.finish = () => {}; | |
| } catch (e) { | |
| // this should not happen, but stats taking also should not break the process | |
| log.debug.once(`failed to finish stats for ${collection.name}\n`, e, 'stats'); | |
| } | |
| } | |
| /** | |
| * @param {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} collection | |
| */ | |
| async #aggregateStatsResult(collection) { | |
| const stats = collection.stats; | |
| for (const stat of stats) { | |
| stat.pkg = (await this.#cache.getPackageInfo(stat.file)).name; | |
| } | |
| // group stats | |
| /** @type {Record<string, import('../types/vite-plugin-svelte-stats.d.ts').PackageStats>} */ | |
| const grouped = {}; | |
| stats.forEach((stat) => { | |
| const pkg = /** @type {string} */ (stat.pkg); | |
| let group = grouped[pkg]; | |
| if (!group) { | |
| group = grouped[pkg] = { | |
| files: 0, | |
| duration: 0, | |
| pkg | |
| }; | |
| } | |
| group.files += 1; | |
| group.duration += stat.end - stat.start; | |
| }); | |
| const groups = Object.values(grouped); | |
| groups.sort((a, b) => b.duration - a.duration); | |
| collection.packageStats = groups; | |
| } | |
| } | |