@tramvai/cli 6.68.0 → 6.68.2
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.
- package/lib/api/benchmark/build.d.ts +1 -6
- package/lib/api/benchmark/build.d.ts.map +1 -1
- package/lib/api/benchmark/build.js +59 -23
- package/lib/api/benchmark/build.js.map +1 -1
- package/lib/api/benchmark/const.d.ts +2 -0
- package/lib/api/benchmark/const.d.ts.map +1 -0
- package/lib/api/benchmark/const.js +5 -0
- package/lib/api/benchmark/const.js.map +1 -0
- package/lib/api/benchmark/index.d.ts +4 -6
- package/lib/api/benchmark/index.d.ts.map +1 -1
- package/lib/api/benchmark/index.js.map +1 -1
- package/lib/api/benchmark/start.d.ts +1 -7
- package/lib/api/benchmark/start.d.ts.map +1 -1
- package/lib/api/benchmark/start.js +56 -49
- package/lib/api/benchmark/start.js.map +1 -1
- package/lib/api/benchmark/types.d.ts +76 -12
- package/lib/api/benchmark/types.d.ts.map +1 -1
- package/lib/api/benchmark/utils/compilationUtils.d.ts +4 -0
- package/lib/api/benchmark/utils/compilationUtils.d.ts.map +1 -0
- package/lib/api/benchmark/utils/compilationUtils.js +157 -0
- package/lib/api/benchmark/utils/compilationUtils.js.map +1 -0
- package/lib/api/benchmark/utils/stats.d.ts +2 -7
- package/lib/api/benchmark/utils/stats.d.ts.map +1 -1
- package/lib/api/benchmark/utils/stats.js +36 -22
- package/lib/api/benchmark/utils/stats.js.map +1 -1
- package/lib/api/build/index.d.ts +1 -0
- package/lib/api/build/index.d.ts.map +1 -1
- package/lib/api/build/index.js.map +1 -1
- package/lib/api/start/application.experimental.d.ts.map +1 -1
- package/lib/api/start/application.experimental.js +4 -3
- package/lib/api/start/application.experimental.js.map +1 -1
- package/lib/api/start/index.d.ts +1 -0
- package/lib/api/start/index.d.ts.map +1 -1
- package/lib/api/start/index.js.map +1 -1
- package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts +3 -3
- package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts.map +1 -1
- package/lib/builder/webpack/analyzePlugins/rsdoctor.js +1 -11
- package/lib/builder/webpack/analyzePlugins/rsdoctor.js.map +1 -1
- package/lib/builder/webpack/providers/shared.d.ts.map +1 -1
- package/lib/builder/webpack/providers/shared.js +10 -7
- package/lib/builder/webpack/providers/shared.js.map +1 -1
- package/lib/commands/benchmark/benchmark.d.ts +3 -1
- package/lib/commands/benchmark/benchmark.d.ts.map +1 -1
- package/lib/commands/benchmark/benchmark.js +130 -31
- package/lib/commands/benchmark/benchmark.js.map +1 -1
- package/lib/commands/benchmark/command.d.ts +9 -0
- package/lib/commands/benchmark/command.d.ts.map +1 -1
- package/lib/commands/benchmark/command.js +18 -0
- package/lib/commands/benchmark/command.js.map +1 -1
- package/lib/config/configManager.d.ts +1 -0
- package/lib/config/configManager.d.ts.map +1 -1
- package/lib/config/configManager.js +1 -0
- package/lib/config/configManager.js.map +1 -1
- package/lib/library/webpack/common/main.d.ts.map +1 -1
- package/lib/library/webpack/common/main.js +7 -0
- package/lib/library/webpack/common/main.js.map +1 -1
- package/lib/library/webpack/utils/threadLoader.d.ts.map +1 -1
- package/lib/library/webpack/utils/threadLoader.js +2 -1
- package/lib/library/webpack/utils/threadLoader.js.map +1 -1
- package/lib/typings/build/Builder.d.ts +3 -2
- package/lib/typings/build/Builder.d.ts.map +1 -1
- package/package.json +9 -10
- package/src/api/benchmark/__integration__/start.test.ts +10 -12
- package/src/api/benchmark/build.ts +75 -30
- package/src/api/benchmark/const.ts +1 -0
- package/src/api/benchmark/index.ts +4 -6
- package/src/api/benchmark/start.ts +69 -65
- package/src/api/benchmark/types.ts +82 -14
- package/src/api/benchmark/utils/compilationUtils.ts +213 -0
- package/src/api/benchmark/utils/stats.ts +45 -28
- package/src/api/build/index.ts +1 -0
- package/src/api/start/application.experimental.ts +4 -3
- package/src/api/start/index.ts +1 -0
- package/src/builder/webpack/analyzePlugins/rsdoctor.ts +2 -14
- package/src/builder/webpack/providers/shared.ts +7 -4
- package/src/commands/benchmark/benchmark.ts +168 -33
- package/src/commands/benchmark/command.ts +19 -0
- package/src/config/configManager.ts +2 -0
- package/src/library/webpack/common/main.ts +8 -0
- package/src/library/webpack/utils/threadLoader.ts +1 -0
- package/src/typings/build/Builder.ts +4 -2
- package/lib/builder/webpack/utils/calculateBuildTime.d.ts +0 -3
- package/lib/builder/webpack/utils/calculateBuildTime.d.ts.map +0 -1
- package/lib/builder/webpack/utils/calculateBuildTime.js +0 -18
- package/lib/builder/webpack/utils/calculateBuildTime.js.map +0 -1
- package/lib/builder/webpack/utils/maxMemoryRss.d.ts +0 -2
- package/lib/builder/webpack/utils/maxMemoryRss.d.ts.map +0 -1
- package/lib/builder/webpack/utils/maxMemoryRss.js +0 -15
- package/lib/builder/webpack/utils/maxMemoryRss.js.map +0 -1
- package/src/api/benchmark/utils/stats.spec.ts +0 -36
- package/src/builder/webpack/utils/calculateBuildTime.ts +0 -17
- package/src/builder/webpack/utils/maxMemoryRss.ts +0 -12
|
@@ -1,24 +1,19 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
1
2
|
import type { Container } from '@tinkoff/dippy';
|
|
2
3
|
import type { Params as OriginalStartParams, Result as OriginalStartResult } from '../start/index';
|
|
3
4
|
import { COMMAND_PARAMETERS_TOKEN, COMMAND_RUNNER_TOKEN } from '../../di/tokens';
|
|
4
5
|
import type { Params, Result } from './index';
|
|
5
|
-
import type { Samples,
|
|
6
|
+
import type { Samples, CompilationStats } from './types';
|
|
6
7
|
import { clearCacheDirectory } from './utils/clearCache';
|
|
7
8
|
import { getResultStats } from './utils/stats';
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
import { getClientCompilationTimings, getServerCompilationTimings } from './utils/compilationUtils';
|
|
10
|
+
import { DEFAULT_TIMES } from './const';
|
|
10
11
|
|
|
11
12
|
export interface StartParams extends Params {
|
|
12
13
|
command: 'start';
|
|
13
14
|
commandOptions: OriginalStartParams;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export interface StartResult extends Result {
|
|
17
|
-
noCache?: RunStats;
|
|
18
|
-
cache?: RunStats;
|
|
19
|
-
rebuild?: RunStats;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
const runStartCommand = async (
|
|
23
18
|
di: Container,
|
|
24
19
|
{
|
|
@@ -29,17 +24,29 @@ const runStartCommand = async (
|
|
|
29
24
|
shouldClearCache: boolean;
|
|
30
25
|
}
|
|
31
26
|
): Promise<Samples> => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
27
|
+
const clientBuildTimeSamples: number[] = [];
|
|
28
|
+
const serverBuildTimeSamples: number[] = [];
|
|
29
|
+
const clientMaxMemoryRssSamples: number[] = [];
|
|
30
|
+
const serverMaxMemoryRssSamples: number[] = [];
|
|
31
|
+
|
|
32
|
+
const serverCompilationTimings: CompilationStats[] = [];
|
|
33
|
+
const clientCompilationTimings: CompilationStats[] = [];
|
|
34
|
+
|
|
35
|
+
const maxMemoryRssSamples: number[] = [];
|
|
35
36
|
|
|
36
37
|
const { commandOptions } = di.get(COMMAND_PARAMETERS_TOKEN) as StartParams;
|
|
38
|
+
commandOptions.benchmark = true;
|
|
39
|
+
|
|
40
|
+
const buildType = commandOptions.buildType ?? 'all';
|
|
41
|
+
|
|
42
|
+
const benchmarkStartTime = new Date().toISOString();
|
|
37
43
|
|
|
38
44
|
for (let i = 0; i < times; i++) {
|
|
39
45
|
if (shouldClearCache) {
|
|
40
46
|
await clearCacheDirectory(di);
|
|
41
47
|
}
|
|
42
48
|
|
|
49
|
+
const attemptStartTime = Date.now();
|
|
43
50
|
const { close, getBuildStats } = await (di
|
|
44
51
|
.get(COMMAND_RUNNER_TOKEN)
|
|
45
52
|
.run('start', commandOptions) as OriginalStartResult);
|
|
@@ -47,70 +54,67 @@ const runStartCommand = async (
|
|
|
47
54
|
|
|
48
55
|
await close();
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
clientSamples,
|
|
57
|
-
serverSamples,
|
|
58
|
-
maxMemoryRssSamples,
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const runRebuild = async (di: Container, { times }: { times: number }): Promise<Samples> => {
|
|
63
|
-
const clientSamples: number[] = Array(times);
|
|
64
|
-
const serverSamples: number[] = Array(times);
|
|
65
|
-
const maxMemoryRssSamples: number[] = Array(times);
|
|
66
|
-
const { commandOptions } = di.get(COMMAND_PARAMETERS_TOKEN) as StartParams;
|
|
57
|
+
// at first attempt do not save metrics if cache is used
|
|
58
|
+
if (!shouldClearCache && i === 0) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
if (buildType === 'all' || buildType === 'server') {
|
|
63
|
+
serverCompilationTimings.push(
|
|
64
|
+
await getServerCompilationTimings(benchmarkStartTime, attemptStartTime, i)
|
|
65
|
+
);
|
|
66
|
+
serverBuildTimeSamples.push(stats.server.buildTime);
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
if (stats.server.maxMemoryRss) {
|
|
69
|
+
serverMaxMemoryRssSamples.push(stats.server.maxMemoryRss);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
if (buildType === 'all' || buildType === 'client') {
|
|
74
|
+
clientCompilationTimings.push(
|
|
75
|
+
await getClientCompilationTimings(benchmarkStartTime, attemptStartTime, i)
|
|
76
|
+
);
|
|
77
|
+
clientBuildTimeSamples.push(stats.client.buildTime);
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
if (stats.client.maxMemoryRss) {
|
|
80
|
+
clientMaxMemoryRssSamples.push(stats.client.maxMemoryRss);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
if (stats.maxMemoryRss) {
|
|
85
|
+
maxMemoryRssSamples.push(stats.maxMemoryRss);
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
await close();
|
|
88
|
-
|
|
89
89
|
return {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
serverCompilationTimings,
|
|
91
|
+
clientCompilationTimings,
|
|
92
|
+
clientMaxMemoryRssSamples,
|
|
93
|
+
serverMaxMemoryRssSamples,
|
|
94
|
+
clientBuildTimeSamples,
|
|
95
|
+
serverBuildTimeSamples,
|
|
92
96
|
maxMemoryRssSamples,
|
|
93
97
|
};
|
|
94
98
|
};
|
|
95
99
|
|
|
96
|
-
export const benchmarkStart = async (di: Container): Promise<
|
|
97
|
-
const { times =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
export const benchmarkStart = async (di: Container): Promise<Result> => {
|
|
101
|
+
const { times = DEFAULT_TIMES, commandOptions } = di.get(COMMAND_PARAMETERS_TOKEN) as Params;
|
|
102
|
+
const noCache = !commandOptions.fileCache;
|
|
103
|
+
|
|
104
|
+
let result;
|
|
105
|
+
|
|
106
|
+
if (noCache) {
|
|
107
|
+
result = await runStartCommand(di, {
|
|
108
|
+
times,
|
|
109
|
+
shouldClearCache: true,
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
result = await runStartCommand(di, {
|
|
113
|
+
// additional first attempt for cache warmup
|
|
114
|
+
times: times + 1,
|
|
115
|
+
shouldClearCache: false,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
110
118
|
|
|
111
|
-
return
|
|
112
|
-
cache: getResultStats(cache),
|
|
113
|
-
noCache: getResultStats(noCache),
|
|
114
|
-
rebuild: getResultStats(rebuild),
|
|
115
|
-
};
|
|
119
|
+
return getResultStats(result);
|
|
116
120
|
};
|
|
@@ -1,19 +1,87 @@
|
|
|
1
|
-
export interface Stats {
|
|
2
|
-
samples: number[];
|
|
3
|
-
mean: number;
|
|
4
|
-
std: number;
|
|
5
|
-
|
|
6
|
-
variance: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
1
|
export type Samples = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
serverCompilationTimings: CompilationStats[];
|
|
3
|
+
serverBuildTimeSamples: number[];
|
|
4
|
+
clientCompilationTimings: CompilationStats[];
|
|
5
|
+
clientBuildTimeSamples: number[];
|
|
6
|
+
maxMemoryRssSamples: number[];
|
|
7
|
+
clientMaxMemoryRssSamples: number[];
|
|
8
|
+
serverMaxMemoryRssSamples: number[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type CompilationStats = {
|
|
12
|
+
totalBuildCosts: Record<string, number>;
|
|
13
|
+
loaderBuildCosts: Record<string, number>;
|
|
14
|
+
pluginBuildCosts: Record<string, number>;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export type RunStats = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
serverCompilationStats: CompilationStats | undefined;
|
|
19
|
+
clientCompilationStats: CompilationStats | undefined;
|
|
20
|
+
clientBuildTime: number | undefined;
|
|
21
|
+
serverBuildTime: number | undefined;
|
|
22
|
+
clientMaxMemoryRss: number | undefined;
|
|
23
|
+
serverMaxMemoryRss: number | undefined;
|
|
24
|
+
maxMemoryRss: number | undefined;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type PlainObject<T = any> = {
|
|
28
|
+
[key: string]: T;
|
|
19
29
|
};
|
|
30
|
+
|
|
31
|
+
interface ProcessData {
|
|
32
|
+
/**
|
|
33
|
+
* process id
|
|
34
|
+
*/
|
|
35
|
+
pid: number;
|
|
36
|
+
/**
|
|
37
|
+
* parent process id
|
|
38
|
+
*/
|
|
39
|
+
ppid: number | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface LoaderTransformData extends ProcessData {
|
|
43
|
+
/** loader name */
|
|
44
|
+
loader: string;
|
|
45
|
+
/**
|
|
46
|
+
* loader index
|
|
47
|
+
*/
|
|
48
|
+
loaderIndex: number;
|
|
49
|
+
/** loader path */
|
|
50
|
+
path: string;
|
|
51
|
+
input: string | null | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* - isPitch: true: the result of loader.pitch()
|
|
54
|
+
* - isPitch: false: the code result of loader()
|
|
55
|
+
*/
|
|
56
|
+
result: string | null | undefined;
|
|
57
|
+
/** Timestamp when called */
|
|
58
|
+
startAt: number;
|
|
59
|
+
endAt: number;
|
|
60
|
+
/** loader configuration */
|
|
61
|
+
options: PlainObject;
|
|
62
|
+
/** pitching loader */
|
|
63
|
+
isPitch: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* is sync
|
|
66
|
+
*/
|
|
67
|
+
sync: boolean;
|
|
68
|
+
/** Error during conversion */
|
|
69
|
+
// errors: DevToolErrorInstance[];
|
|
70
|
+
/** module layer */
|
|
71
|
+
layer?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type MinimalLoaderData = Pick<LoaderTransformData, 'startAt' | 'endAt' | 'pid' | 'loader'>;
|
|
75
|
+
|
|
76
|
+
export interface PluginData {
|
|
77
|
+
/** hook tap name */
|
|
78
|
+
tapName: string;
|
|
79
|
+
/** hook call time-consuming */
|
|
80
|
+
costs: number;
|
|
81
|
+
startAt: number;
|
|
82
|
+
endAt: number;
|
|
83
|
+
/** hook function type */
|
|
84
|
+
type: 'sync' | 'async' | 'promise';
|
|
85
|
+
/** hook function result */
|
|
86
|
+
result: any;
|
|
87
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { CompilationStats, LoaderTransformData, MinimalLoaderData, PluginData } from '../types';
|
|
4
|
+
|
|
5
|
+
export async function getServerCompilationTimings(
|
|
6
|
+
benchmarkStartTime: string,
|
|
7
|
+
attemptStartTime: number,
|
|
8
|
+
attempt: number
|
|
9
|
+
) {
|
|
10
|
+
return getCompilationTimings(benchmarkStartTime, attemptStartTime, attempt, 'server');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function getClientCompilationTimings(
|
|
14
|
+
benchmarkStartTime: string,
|
|
15
|
+
attemptStartTime: number,
|
|
16
|
+
attempt: number
|
|
17
|
+
) {
|
|
18
|
+
return getCompilationTimings(benchmarkStartTime, attemptStartTime, attempt, 'client');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function getCompilationTimings(
|
|
22
|
+
benchmarkStartTime: string,
|
|
23
|
+
attemptStartTime: number,
|
|
24
|
+
attempt: number,
|
|
25
|
+
type: 'client' | 'server'
|
|
26
|
+
): Promise<CompilationStats> {
|
|
27
|
+
try {
|
|
28
|
+
const rsdoctorData = await getReportData(
|
|
29
|
+
await processReport(benchmarkStartTime, attempt, type)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const { summary, loader, plugin } = rsdoctorData;
|
|
33
|
+
|
|
34
|
+
const totalBuildCosts = calculateTotalCosts(summary, attemptStartTime);
|
|
35
|
+
const loaderBuildCosts = calculateLoadersCosts(loader);
|
|
36
|
+
const pluginBuildCosts = calculatePluginsCosts(plugin);
|
|
37
|
+
|
|
38
|
+
return { totalBuildCosts, loaderBuildCosts, pluginBuildCosts };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
throw new Error(`Failed to get rsdoctor report!\n${err}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function getReportData(reportPath: string) {
|
|
45
|
+
const rsdoctorRawStats = await fs.readFile(reportPath, 'utf-8');
|
|
46
|
+
const rsdoctorStats = JSON.parse(rsdoctorRawStats);
|
|
47
|
+
|
|
48
|
+
return rsdoctorStats.data;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function processReport(
|
|
52
|
+
benchmarkStartTime: string,
|
|
53
|
+
attempt: number,
|
|
54
|
+
type: 'client' | 'server'
|
|
55
|
+
) {
|
|
56
|
+
const reportBasePath = `./.rsdoctor/${benchmarkStartTime}`;
|
|
57
|
+
const reportPath = `${reportBasePath}/${type}-rsdoctor-data-${attempt + 1}.json`;
|
|
58
|
+
|
|
59
|
+
await fs.mkdir(reportBasePath, { recursive: true });
|
|
60
|
+
await fs.rename(`./.rsdoctor/${type}-rsdoctor-data.json`, reportPath);
|
|
61
|
+
|
|
62
|
+
return reportPath;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function mergeIntervals(intervals: [number, number][]) {
|
|
66
|
+
// Sort from small to large
|
|
67
|
+
intervals.sort((a, b) => a[0] - b[0]);
|
|
68
|
+
// The previous interval, the next interval, store the result
|
|
69
|
+
let previous;
|
|
70
|
+
let current;
|
|
71
|
+
const result: [number, number][] = [];
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < intervals.length; i++) {
|
|
74
|
+
current = intervals[i];
|
|
75
|
+
// If the first interval or the current interval does not overlap with the previous interval, add the current interval to the result
|
|
76
|
+
if (!previous || current[0] > previous[1]) {
|
|
77
|
+
// Assign the current interval to the previous interval
|
|
78
|
+
previous = current;
|
|
79
|
+
result.push(current);
|
|
80
|
+
} else {
|
|
81
|
+
// Otherwise, the two intervals overlap
|
|
82
|
+
// Update the end time of the previous interval
|
|
83
|
+
previous[1] = Math.max(previous[1], current[1]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getLoadersCosts(
|
|
91
|
+
filter: (loader: MinimalLoaderData) => boolean,
|
|
92
|
+
loaders: MinimalLoaderData[]
|
|
93
|
+
) {
|
|
94
|
+
const match: { [pid: number | string]: [start: number, end: number][] } = {};
|
|
95
|
+
const others: { [pid: number | string]: [start: number, end: number][] } = {};
|
|
96
|
+
|
|
97
|
+
loaders.forEach((e) => {
|
|
98
|
+
if (filter(e)) {
|
|
99
|
+
if (!match[e.pid]) match[e.pid] = [];
|
|
100
|
+
match[e.pid].push([e.startAt, e.endAt]);
|
|
101
|
+
} else {
|
|
102
|
+
if (!others[e.pid]) others[e.pid] = [];
|
|
103
|
+
others[e.pid].push([e.startAt, e.endAt]);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
let costs = 0;
|
|
108
|
+
|
|
109
|
+
const pids = Object.keys(match);
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < pids.length; i++) {
|
|
112
|
+
const pid = pids[i];
|
|
113
|
+
const _match = mergeIntervals(match[pid]);
|
|
114
|
+
// between in loader.startAt and loader.endAt
|
|
115
|
+
const _others = mergeIntervals(others[pid] || []).filter(([s, e]) =>
|
|
116
|
+
_match.some((el) => s >= el[0] && e <= el[1])
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// eslint-disable-next-line no-param-reassign
|
|
120
|
+
const matchSum = _match.length ? _match.reduce((t, c) => (t += c[1] - c[0]), 0) : 0;
|
|
121
|
+
// eslint-disable-next-line no-param-reassign
|
|
122
|
+
const othersSum = _others.length ? _others.reduce((t, c) => (t += c[1] - c[0]), 0) : 0;
|
|
123
|
+
|
|
124
|
+
costs += matchSum - othersSum;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return costs;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function calculateTotalCosts(
|
|
131
|
+
summary: { costs: { name: string; cost: string }[] },
|
|
132
|
+
attemptStartTime: number
|
|
133
|
+
) {
|
|
134
|
+
const { costs: buildCosts } = summary;
|
|
135
|
+
const buildCostsMap = buildCosts.reduce((acc, cost) => {
|
|
136
|
+
acc[cost.name] = cost;
|
|
137
|
+
return acc;
|
|
138
|
+
}, {});
|
|
139
|
+
|
|
140
|
+
const result: Record<string, number> = {};
|
|
141
|
+
|
|
142
|
+
for (const buildCostName in buildCostsMap) {
|
|
143
|
+
if (buildCostName === 'bootstrap->beforeCompile') {
|
|
144
|
+
// start of bootstrap is start of first build, so use attemptStarTime instead
|
|
145
|
+
result[buildCostName] =
|
|
146
|
+
buildCostsMap['beforeCompile->afterCompile'].startAt - attemptStartTime;
|
|
147
|
+
} else {
|
|
148
|
+
result[buildCostName] = buildCostsMap[buildCostName].costs;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function calculateLoadersCosts(loaders: { loaders: LoaderTransformData[] }[]) {
|
|
156
|
+
const filteredLoaders: MinimalLoaderData[] = [];
|
|
157
|
+
const uniqueLoaders = new Map<
|
|
158
|
+
string,
|
|
159
|
+
{
|
|
160
|
+
files: number;
|
|
161
|
+
path: string;
|
|
162
|
+
}
|
|
163
|
+
>();
|
|
164
|
+
|
|
165
|
+
loaders.forEach((data) => {
|
|
166
|
+
data.loaders.forEach((fl) => {
|
|
167
|
+
const uniqueLoader = uniqueLoaders.get(fl.loader);
|
|
168
|
+
if (uniqueLoader) {
|
|
169
|
+
uniqueLoaders.set(fl.loader, {
|
|
170
|
+
files: uniqueLoader.files + 1,
|
|
171
|
+
path: fl.path,
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
uniqueLoaders.set(fl.loader, { files: 1, path: fl.path });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return filteredLoaders.push({
|
|
178
|
+
loader: fl.loader,
|
|
179
|
+
startAt: fl.startAt,
|
|
180
|
+
endAt: fl.endAt,
|
|
181
|
+
pid: fl.pid,
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const costs: Record<string, number> = {};
|
|
187
|
+
uniqueLoaders.forEach((_, loaderName: string) => {
|
|
188
|
+
costs[loaderName] = getLoadersCosts((item) => item.loader === loaderName, filteredLoaders);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return costs;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function calculatePluginsCosts(plugins: PluginData) {
|
|
195
|
+
const pluginCosts: Record<string, number> = {};
|
|
196
|
+
|
|
197
|
+
for (const hookName in plugins) {
|
|
198
|
+
const hookCalls = plugins[hookName];
|
|
199
|
+
|
|
200
|
+
hookCalls.forEach((plugin) => {
|
|
201
|
+
const pluginName = plugin.tapName;
|
|
202
|
+
const pluginCost = plugin.costs;
|
|
203
|
+
|
|
204
|
+
if (pluginCosts[pluginName]) {
|
|
205
|
+
pluginCosts[pluginName] += pluginCost;
|
|
206
|
+
} else {
|
|
207
|
+
pluginCosts[pluginName] = pluginCost;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return pluginCosts;
|
|
213
|
+
}
|
|
@@ -1,45 +1,62 @@
|
|
|
1
|
-
import type { RunStats,
|
|
1
|
+
import type { RunStats, Samples, CompilationStats } from '../types';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
function getMeanValue(samples: number[]) {
|
|
4
|
+
if (samples.length === 0) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
let sum = 0;
|
|
7
9
|
|
|
8
|
-
for (let i = 0; i <
|
|
10
|
+
for (let i = 0; i < samples.length; i++) {
|
|
9
11
|
sum += samples[i];
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
return sum / samples.length;
|
|
15
|
+
}
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
function getCompilationMeanStats(allStats) {
|
|
18
|
+
const result = {} as CompilationStats;
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
sum += (samples[i] - mean) * (samples[i] - mean);
|
|
18
|
-
}
|
|
20
|
+
const stats: CompilationStats = allStats[0];
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
+
for (const section in stats) {
|
|
23
|
+
for (const metricName in stats[section]) {
|
|
24
|
+
const samples = allStats.reduce((acc, item) => {
|
|
25
|
+
acc.push(item[section][metricName]);
|
|
26
|
+
return acc;
|
|
27
|
+
}, []);
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
if (!result[section]) {
|
|
30
|
+
result[section] = {};
|
|
31
|
+
}
|
|
32
|
+
const meanValue = getMeanValue(samples);
|
|
33
|
+
|
|
34
|
+
// ignore values less than 10ms
|
|
35
|
+
if (meanValue > 10) {
|
|
36
|
+
result[section][metricName] = meanValue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
30
43
|
|
|
31
44
|
export const getResultStats = ({
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
clientBuildTimeSamples,
|
|
46
|
+
clientCompilationTimings,
|
|
47
|
+
clientMaxMemoryRssSamples,
|
|
48
|
+
serverBuildTimeSamples,
|
|
49
|
+
serverCompilationTimings,
|
|
50
|
+
serverMaxMemoryRssSamples,
|
|
34
51
|
maxMemoryRssSamples,
|
|
35
|
-
}: {
|
|
36
|
-
clientSamples: number[];
|
|
37
|
-
serverSamples: number[];
|
|
38
|
-
maxMemoryRssSamples: number[];
|
|
39
|
-
}): RunStats => {
|
|
52
|
+
}: Samples): RunStats => {
|
|
40
53
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
serverBuildTime: getMeanValue(serverBuildTimeSamples),
|
|
55
|
+
serverCompilationStats: getCompilationMeanStats(serverCompilationTimings),
|
|
56
|
+
clientBuildTime: getMeanValue(clientBuildTimeSamples),
|
|
57
|
+
clientCompilationStats: getCompilationMeanStats(clientCompilationTimings),
|
|
58
|
+
maxMemoryRss: getMeanValue(maxMemoryRssSamples),
|
|
59
|
+
clientMaxMemoryRss: getMeanValue(clientMaxMemoryRssSamples),
|
|
60
|
+
serverMaxMemoryRss: getMeanValue(serverMaxMemoryRssSamples),
|
|
44
61
|
};
|
|
45
62
|
};
|
package/src/api/build/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const startApplicationExperimental = async (di: Container): Result => {
|
|
|
24
24
|
const inputParameters: StartParameters = {
|
|
25
25
|
name: configEntry.name,
|
|
26
26
|
mode: 'development',
|
|
27
|
+
benchmark: options.benchmark,
|
|
27
28
|
buildType: options.buildType,
|
|
28
29
|
port: options.port,
|
|
29
30
|
host: options.host,
|
|
@@ -101,7 +102,7 @@ export const startApplicationExperimental = async (di: Container): Result => {
|
|
|
101
102
|
await devServer.invalidate();
|
|
102
103
|
},
|
|
103
104
|
getBuildStats: () => {
|
|
104
|
-
return
|
|
105
|
+
return devServer.getStats();
|
|
105
106
|
},
|
|
106
107
|
builder: {
|
|
107
108
|
name: '@tramvai/plugin-webpack-builder',
|
|
@@ -114,7 +115,7 @@ export const startApplicationExperimental = async (di: Container): Result => {
|
|
|
114
115
|
await devServer.invalidate();
|
|
115
116
|
},
|
|
116
117
|
getBuildStats: () => {
|
|
117
|
-
return
|
|
118
|
+
return devServer.getStats();
|
|
118
119
|
},
|
|
119
120
|
};
|
|
120
121
|
},
|
|
@@ -210,7 +211,7 @@ function mapTramvaiJsonToNewTsConfig({ rootDir }: { rootDir: string }) {
|
|
|
210
211
|
}
|
|
211
212
|
if (applicationProject.polyfill) {
|
|
212
213
|
try {
|
|
213
|
-
if (require.resolve(
|
|
214
|
+
if (require.resolve(applicationProject.polyfill).includes('node_modules')) {
|
|
214
215
|
mappedProject.polyfill = require.resolve(applicationProject.polyfill);
|
|
215
216
|
} else {
|
|
216
217
|
mappedProject.polyfill = path.resolve(applicationProject.polyfill);
|
package/src/api/start/index.ts
CHANGED
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import type Config from 'webpack-chain';
|
|
2
1
|
import { RsdoctorWebpackPlugin } from '@rsdoctor/webpack-plugin';
|
|
3
2
|
import { AnalyzePlugin } from '../types';
|
|
4
3
|
|
|
4
|
+
type options = ConstructorParameters<typeof RsdoctorWebpackPlugin<[]>>;
|
|
5
5
|
export class RsdoctorAnalyzePlugin extends AnalyzePlugin {
|
|
6
6
|
requireDeps = [];
|
|
7
7
|
|
|
8
|
-
options:
|
|
9
|
-
{
|
|
10
|
-
features: ['loader', 'plugins', 'resolver'],
|
|
11
|
-
},
|
|
12
|
-
];
|
|
8
|
+
options: options = [];
|
|
13
9
|
|
|
14
10
|
plugin = RsdoctorWebpackPlugin;
|
|
15
11
|
|
|
16
|
-
patchConfig = (config: Config): Config => {
|
|
17
|
-
super.patchConfigInternal(config);
|
|
18
|
-
// https://github.com/web-infra-dev/rsdoctor/issues/717
|
|
19
|
-
config.set('experiments', { backCompat: true });
|
|
20
|
-
|
|
21
|
-
return config;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
12
|
// rsdoctor поднимает dev server
|
|
25
13
|
afterBuild = () => new Promise(() => null);
|
|
26
14
|
}
|