@sveltejs/vite-plugin-svelte 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,206 @@
1
+ import { log } from './log';
2
+ //eslint-disable-next-line node/no-missing-import
3
+ import { findClosestPkgJsonPath } from 'vitefu';
4
+ import { readFileSync } from 'fs';
5
+ import { performance } from 'perf_hooks';
6
+
7
+ interface Stat {
8
+ file: string;
9
+ pkg?: string;
10
+ start: number;
11
+ end: number;
12
+ }
13
+
14
+ export interface StatCollection {
15
+ name: string;
16
+ options: CollectionOptions;
17
+ //eslint-disable-next-line no-unused-vars
18
+ start: (file: string) => () => void;
19
+ stats: Stat[];
20
+ packageStats?: PackageStats[];
21
+ collectionStart: number;
22
+ duration?: number;
23
+ finish: () => Promise<void> | void;
24
+ finished: boolean;
25
+ }
26
+
27
+ interface PackageStats {
28
+ pkg: string;
29
+ files: number;
30
+ duration: number;
31
+ }
32
+
33
+ export interface CollectionOptions {
34
+ //eslint-disable-next-line no-unused-vars
35
+ logInProgress: (collection: StatCollection, now: number) => boolean;
36
+ //eslint-disable-next-line no-unused-vars
37
+ logResult: (collection: StatCollection) => boolean;
38
+ }
39
+
40
+ const defaultCollectionOptions: CollectionOptions = {
41
+ // log after 500ms and more than one file processed
42
+ logInProgress: (c, now) => now - c.collectionStart > 500 && c.stats.length > 1,
43
+ // always log results
44
+ logResult: () => true
45
+ };
46
+
47
+ function humanDuration(n: number) {
48
+ // 99.9ms 0.10s
49
+ return n < 100 ? `${n.toFixed(1)}ms` : `${(n / 1000).toFixed(2)}s`;
50
+ }
51
+
52
+ function formatPackageStats(pkgStats: PackageStats[]) {
53
+ const statLines = pkgStats.map((pkgStat) => {
54
+ const duration = pkgStat.duration;
55
+ const avg = duration / pkgStat.files;
56
+ return [pkgStat.pkg, `${pkgStat.files}`, humanDuration(duration), humanDuration(avg)];
57
+ });
58
+ statLines.unshift(['package', 'files', 'time', 'avg']);
59
+ const columnWidths = statLines.reduce(
60
+ (widths: number[], row) => {
61
+ for (let i = 0; i < row.length; i++) {
62
+ const cell = row[i];
63
+ if (widths[i] < cell.length) {
64
+ widths[i] = cell.length;
65
+ }
66
+ }
67
+ return widths;
68
+ },
69
+ statLines[0].map(() => 0)
70
+ );
71
+
72
+ const table = statLines
73
+ .map((row: string[]) =>
74
+ row
75
+ .map((cell: string, i: number) => {
76
+ if (i === 0) {
77
+ return cell.padEnd(columnWidths[i], ' ');
78
+ } else {
79
+ return cell.padStart(columnWidths[i], ' ');
80
+ }
81
+ })
82
+ .join('\t')
83
+ )
84
+ .join('\n');
85
+ return table;
86
+ }
87
+
88
+ export class VitePluginSvelteStats {
89
+ // package directory -> package name
90
+ private _packages: { path: string; name: string }[] = [];
91
+ private _collections: StatCollection[] = [];
92
+ startCollection(name: string, opts?: Partial<CollectionOptions>) {
93
+ const options = {
94
+ ...defaultCollectionOptions,
95
+ ...opts
96
+ };
97
+ const stats: Stat[] = [];
98
+ const collectionStart = performance.now();
99
+ const _this = this;
100
+ let hasLoggedProgress = false;
101
+ const collection: StatCollection = {
102
+ name,
103
+ options,
104
+ stats,
105
+ collectionStart,
106
+ finished: false,
107
+ start(file) {
108
+ if (collection.finished) {
109
+ throw new Error('called after finish() has been used');
110
+ }
111
+ const start = performance.now();
112
+ const stat: Stat = { file, start, end: start };
113
+ return () => {
114
+ const now = performance.now();
115
+ stat.end = now;
116
+ stats.push(stat);
117
+ if (!hasLoggedProgress && options.logInProgress(collection, now)) {
118
+ hasLoggedProgress = true;
119
+ log.info(`${name} in progress ...`);
120
+ }
121
+ };
122
+ },
123
+ async finish() {
124
+ await _this._finish(collection);
125
+ }
126
+ };
127
+ _this._collections.push(collection);
128
+ return collection;
129
+ }
130
+
131
+ public async finishAll() {
132
+ await Promise.all(this._collections.map((c) => c.finish()));
133
+ }
134
+
135
+ 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 = [];
152
+ }
153
+ collection.start = () => () => {};
154
+ collection.finish = () => {};
155
+ }
156
+
157
+ private async _aggregateStatsResult(collection: StatCollection) {
158
+ const stats = collection.stats;
159
+ for (const stat of stats) {
160
+ let pkg = this._packages.find((p) => stat.file.startsWith(p.path));
161
+ if (!pkg) {
162
+ // check for package.json first
163
+ let pkgPath = await findClosestPkgJsonPath(stat.file);
164
+ if (pkgPath) {
165
+ let path = pkgPath?.replace(/package.json$/, '');
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(/package.json$/, '');
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
+ }
180
+ }
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';
184
+ }
185
+
186
+ // group stats
187
+ const grouped: { [key: string]: PackageStats } = {};
188
+ stats.forEach((stat) => {
189
+ const pkg = stat.pkg!;
190
+ let group = grouped[pkg];
191
+ if (!group) {
192
+ group = grouped[pkg] = {
193
+ files: 0,
194
+ duration: 0,
195
+ pkg
196
+ };
197
+ }
198
+ group.files += 1;
199
+ group.duration += stat.end - stat.start;
200
+ });
201
+
202
+ const groups = Object.values(grouped);
203
+ groups.sort((a, b) => b.duration - a.duration);
204
+ collection.packageStats = groups;
205
+ }
206
+ }
@@ -1,43 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { findRootSvelteDependencies, needsOptimization } from '../dependencies';
3
- import * as path from 'path';
4
- import { createRequire } from 'module';
5
- import { fileURLToPath } from 'url';
6
- const __dir = path.dirname(fileURLToPath(import.meta.url));
7
- const e2eTestRoot = path.resolve(__dir, '../../../../../packages/e2e-tests');
8
-
9
- describe('dependencies', () => {
10
- describe('findRootSvelteDependencies', () => {
11
- it('should find svelte dependencies in packages/e2e-test/hmr', () => {
12
- const deps = findRootSvelteDependencies(path.resolve('packages/e2e-tests/hmr'));
13
- expect(deps).toHaveLength(1);
14
- expect(deps[0].name).toBe('e2e-test-dep-svelte-simple');
15
- expect(deps[0].path).toEqual([]);
16
- });
17
- it('should find nested svelte dependencies in packages/e2e-test/package-json-svelte-field', () => {
18
- const deps = findRootSvelteDependencies(path.join(e2eTestRoot, 'package-json-svelte-field'));
19
- expect(deps).toHaveLength(3);
20
- const hybrid = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-hybrid');
21
- expect(hybrid).toBeTruthy();
22
- expect(hybrid.path).toHaveLength(0);
23
- const nested = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-nested');
24
- expect(nested).toBeTruthy();
25
- expect(nested.path).toHaveLength(0);
26
- const simple = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-simple');
27
- expect(simple).toBeTruthy();
28
- expect(simple.path).toHaveLength(1);
29
- expect(simple.path[0]).toBe('e2e-test-dep-svelte-nested');
30
- });
31
- });
32
- describe('needsOptimization', () => {
33
- it('should optimize cjs deps only', () => {
34
- const testDepsPath = path.join(e2eTestRoot, 'dependencies/package.json');
35
- const localRequire = createRequire(testDepsPath);
36
- expect(needsOptimization('e2e-test-dep-cjs-and-esm', localRequire)).toBe(false);
37
- expect(needsOptimization('e2e-test-dep-cjs-only', localRequire)).toBe(true);
38
- expect(needsOptimization('e2e-test-dep-esm-only', localRequire)).toBe(false);
39
- expect(needsOptimization('e2e-test-dep-index-only', localRequire)).toBe(true);
40
- expect(needsOptimization('e2e-test-dep-types-only', localRequire)).toBe(false);
41
- });
42
- });
43
- });