@tramvai/cli 5.53.81 → 5.53.111

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.
Files changed (196) hide show
  1. package/README.md +1 -1
  2. package/bin/const.js +5 -0
  3. package/bin/platform.js +12 -6
  4. package/bin/spawn.js +6 -0
  5. package/lib/api/analyze/index.d.ts.map +1 -1
  6. package/lib/api/analyze/index.js +1 -13
  7. package/lib/api/analyze/index.js.map +1 -1
  8. package/lib/api/benchmark/build.d.ts +1 -6
  9. package/lib/api/benchmark/build.d.ts.map +1 -1
  10. package/lib/api/benchmark/build.js +59 -23
  11. package/lib/api/benchmark/build.js.map +1 -1
  12. package/lib/api/benchmark/const.d.ts +2 -0
  13. package/lib/api/benchmark/const.d.ts.map +1 -0
  14. package/lib/api/benchmark/const.js +5 -0
  15. package/lib/api/benchmark/const.js.map +1 -0
  16. package/lib/api/benchmark/index.d.ts +4 -6
  17. package/lib/api/benchmark/index.d.ts.map +1 -1
  18. package/lib/api/benchmark/index.js.map +1 -1
  19. package/lib/api/benchmark/start.d.ts +1 -7
  20. package/lib/api/benchmark/start.d.ts.map +1 -1
  21. package/lib/api/benchmark/start.js +56 -49
  22. package/lib/api/benchmark/start.js.map +1 -1
  23. package/lib/api/benchmark/types.d.ts +76 -12
  24. package/lib/api/benchmark/types.d.ts.map +1 -1
  25. package/lib/api/benchmark/utils/compilationUtils.d.ts +4 -0
  26. package/lib/api/benchmark/utils/compilationUtils.d.ts.map +1 -0
  27. package/lib/api/benchmark/utils/compilationUtils.js +157 -0
  28. package/lib/api/benchmark/utils/compilationUtils.js.map +1 -0
  29. package/lib/api/benchmark/utils/stats.d.ts +2 -7
  30. package/lib/api/benchmark/utils/stats.d.ts.map +1 -1
  31. package/lib/api/benchmark/utils/stats.js +36 -22
  32. package/lib/api/benchmark/utils/stats.js.map +1 -1
  33. package/lib/api/build/index.d.ts +1 -0
  34. package/lib/api/build/index.d.ts.map +1 -1
  35. package/lib/api/build/index.js.map +1 -1
  36. package/lib/api/index.js +1 -1
  37. package/lib/api/index.js.map +1 -1
  38. package/lib/api/start/index.d.ts +1 -0
  39. package/lib/api/start/index.d.ts.map +1 -1
  40. package/lib/api/start/index.js.map +1 -1
  41. package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts +5 -5
  42. package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts.map +1 -1
  43. package/lib/builder/webpack/analyzePlugins/rsdoctor.js +7 -9
  44. package/lib/builder/webpack/analyzePlugins/rsdoctor.js.map +1 -1
  45. package/lib/builder/webpack/index.d.ts.map +1 -1
  46. package/lib/builder/webpack/index.js +14 -12
  47. package/lib/builder/webpack/index.js.map +1 -1
  48. package/lib/builder/webpack/providers/build/client.d.ts.map +1 -1
  49. package/lib/builder/webpack/providers/build/client.js +3 -2
  50. package/lib/builder/webpack/providers/build/client.js.map +1 -1
  51. package/lib/builder/webpack/providers/build/server.d.ts.map +1 -1
  52. package/lib/builder/webpack/providers/build/server.js +3 -2
  53. package/lib/builder/webpack/providers/build/server.js.map +1 -1
  54. package/lib/builder/webpack/providers/shared.d.ts.map +1 -1
  55. package/lib/builder/webpack/providers/shared.js +39 -5
  56. package/lib/builder/webpack/providers/shared.js.map +1 -1
  57. package/lib/builder/webpack/providers/start/shared.d.ts.map +1 -1
  58. package/lib/builder/webpack/providers/start/shared.js +6 -2
  59. package/lib/builder/webpack/providers/start/shared.js.map +1 -1
  60. package/lib/builder/webpack/tokens.d.ts +10 -16
  61. package/lib/builder/webpack/tokens.d.ts.map +1 -1
  62. package/lib/cli/index.d.ts.map +1 -1
  63. package/lib/cli/index.js +12 -12
  64. package/lib/cli/index.js.map +1 -1
  65. package/lib/commands/analyze/command.d.ts +1 -1
  66. package/lib/commands/analyze/command.d.ts.map +1 -1
  67. package/lib/commands/benchmark/benchmark.d.ts +3 -1
  68. package/lib/commands/benchmark/benchmark.d.ts.map +1 -1
  69. package/lib/commands/benchmark/benchmark.js +130 -31
  70. package/lib/commands/benchmark/benchmark.js.map +1 -1
  71. package/lib/commands/benchmark/command.d.ts +9 -0
  72. package/lib/commands/benchmark/command.d.ts.map +1 -1
  73. package/lib/commands/benchmark/command.js +12 -0
  74. package/lib/commands/benchmark/command.js.map +1 -1
  75. package/lib/commands/build/command.d.ts.map +1 -1
  76. package/lib/commands/build/command.js +5 -0
  77. package/lib/commands/build/command.js.map +1 -1
  78. package/lib/commands/start/command.d.ts.map +1 -1
  79. package/lib/commands/start/command.js +5 -0
  80. package/lib/commands/start/command.js.map +1 -1
  81. package/lib/commands/update/checkVersionValidator.d.ts +1 -1
  82. package/lib/commands/update/checkVersionValidator.d.ts.map +1 -1
  83. package/lib/commands/update/checkVersionValidator.js +3 -2
  84. package/lib/commands/update/checkVersionValidator.js.map +1 -1
  85. package/lib/commands/update/command.d.ts +1 -1
  86. package/lib/commands/update/dependantLibs.d.ts +1 -1
  87. package/lib/commands/update/dependantLibs.d.ts.map +1 -1
  88. package/lib/commands/update/dependantLibs.js +3 -2
  89. package/lib/commands/update/dependantLibs.js.map +1 -1
  90. package/lib/commands/update/update.d.ts.map +1 -1
  91. package/lib/commands/update/update.js +6 -3
  92. package/lib/commands/update/update.js.map +1 -1
  93. package/lib/commands/update/updatePackageJson.d.ts +1 -1
  94. package/lib/commands/update/updatePackageJson.d.ts.map +1 -1
  95. package/lib/commands/update/updatePackageJson.js +7 -7
  96. package/lib/commands/update/updatePackageJson.js.map +1 -1
  97. package/lib/config/configManager.d.ts +2 -0
  98. package/lib/config/configManager.d.ts.map +1 -1
  99. package/lib/config/configManager.js +2 -0
  100. package/lib/config/configManager.js.map +1 -1
  101. package/lib/di/tokens/config.d.ts +5 -8
  102. package/lib/di/tokens/config.d.ts.map +1 -1
  103. package/lib/library/webpack/blocks/css.d.ts.map +1 -1
  104. package/lib/library/webpack/blocks/css.js +42 -15
  105. package/lib/library/webpack/blocks/css.js.map +1 -1
  106. package/lib/library/webpack/blocks/js.d.ts.map +1 -1
  107. package/lib/library/webpack/blocks/js.js +57 -11
  108. package/lib/library/webpack/blocks/js.js.map +1 -1
  109. package/lib/library/webpack/common/main.d.ts.map +1 -1
  110. package/lib/library/webpack/common/main.js +7 -0
  111. package/lib/library/webpack/common/main.js.map +1 -1
  112. package/lib/library/webpack/utils/browserslist.d.ts +4 -0
  113. package/lib/library/webpack/utils/browserslist.d.ts.map +1 -0
  114. package/lib/library/webpack/utils/browserslist.js +27 -0
  115. package/lib/library/webpack/utils/browserslist.js.map +1 -0
  116. package/lib/library/webpack/utils/rsdoctor.d.ts +2 -0
  117. package/lib/library/webpack/utils/rsdoctor.d.ts.map +1 -0
  118. package/lib/library/webpack/utils/rsdoctor.js +27 -0
  119. package/lib/library/webpack/utils/rsdoctor.js.map +1 -0
  120. package/lib/library/webpack/utils/threadLoader.d.ts.map +1 -1
  121. package/lib/library/webpack/utils/threadLoader.js +3 -3
  122. package/lib/library/webpack/utils/threadLoader.js.map +1 -1
  123. package/lib/library/webpack/utils/transpiler.d.ts.map +1 -1
  124. package/lib/library/webpack/utils/transpiler.js +3 -16
  125. package/lib/library/webpack/utils/transpiler.js.map +1 -1
  126. package/lib/schema/autogeneratedSchema.json +240 -120
  127. package/lib/typings/build/Builder.d.ts +6 -2
  128. package/lib/typings/build/Builder.d.ts.map +1 -1
  129. package/lib/typings/configEntry/cli.d.ts +9 -0
  130. package/lib/typings/configEntry/cli.d.ts.map +1 -1
  131. package/lib/utils/commands/dependencies/getLatestPackageVersion.d.ts +1 -1
  132. package/lib/utils/commands/dependencies/getLatestPackageVersion.d.ts.map +1 -1
  133. package/lib/utils/commands/dependencies/getLatestPackageVersion.js +4 -2
  134. package/lib/utils/commands/dependencies/getLatestPackageVersion.js.map +1 -1
  135. package/lib/utils/commands/dependencies/packageHasVersion.d.ts +1 -1
  136. package/lib/utils/commands/dependencies/packageHasVersion.d.ts.map +1 -1
  137. package/lib/utils/commands/dependencies/packageHasVersion.js +3 -2
  138. package/lib/utils/commands/dependencies/packageHasVersion.js.map +1 -1
  139. package/package.json +19 -18
  140. package/schema.json +240 -120
  141. package/src/api/analyze/index.ts +3 -16
  142. package/src/api/benchmark/__integration__/start.test.ts +10 -12
  143. package/src/api/benchmark/build.ts +75 -30
  144. package/src/api/benchmark/const.ts +1 -0
  145. package/src/api/benchmark/index.ts +4 -6
  146. package/src/api/benchmark/start.ts +69 -65
  147. package/src/api/benchmark/types.ts +82 -14
  148. package/src/api/benchmark/utils/compilationUtils.ts +213 -0
  149. package/src/api/benchmark/utils/stats.ts +45 -28
  150. package/src/api/build/index.ts +1 -0
  151. package/src/api/index.ts +1 -1
  152. package/src/api/start/index.ts +1 -0
  153. package/src/builder/webpack/analyzePlugins/rsdoctor.ts +11 -14
  154. package/src/builder/webpack/index.ts +16 -21
  155. package/src/builder/webpack/providers/build/client.ts +7 -2
  156. package/src/builder/webpack/providers/build/server.ts +7 -2
  157. package/src/builder/webpack/providers/shared.ts +53 -5
  158. package/src/builder/webpack/providers/start/shared.ts +7 -2
  159. package/src/cli/index.ts +3 -2
  160. package/src/commands/analyze/command.ts +1 -1
  161. package/src/commands/benchmark/benchmark.ts +168 -33
  162. package/src/commands/benchmark/command.ts +12 -0
  163. package/src/commands/build/command.ts +6 -0
  164. package/src/commands/new/templates/shared/package.json.hbs +1 -1
  165. package/src/commands/start/command.ts +6 -0
  166. package/src/commands/update/checkVersionValidator.ts +4 -2
  167. package/src/commands/update/dependantLibs.ts +3 -1
  168. package/src/commands/update/update.ts +6 -3
  169. package/src/commands/update/updatePackageJson.spec.ts +19 -3
  170. package/src/commands/update/updatePackageJson.ts +7 -2
  171. package/src/config/configManager.ts +4 -0
  172. package/src/library/webpack/blocks/css.ts +52 -16
  173. package/src/library/webpack/blocks/js.ts +61 -12
  174. package/src/library/webpack/common/main.ts +8 -0
  175. package/src/library/webpack/utils/browserslist.ts +29 -0
  176. package/src/library/webpack/utils/rsdoctor.ts +26 -0
  177. package/src/library/webpack/utils/threadLoader.ts +1 -0
  178. package/src/library/webpack/utils/transpiler.ts +3 -18
  179. package/src/models/config.spec.ts +4 -0
  180. package/src/schema/autogeneratedSchema.json +240 -120
  181. package/src/schema/tramvai.spec.ts +2 -0
  182. package/src/typings/build/Builder.ts +7 -2
  183. package/src/typings/configEntry/cli.ts +11 -0
  184. package/src/utils/commands/dependencies/getLatestPackageVersion.ts +4 -1
  185. package/src/utils/commands/dependencies/packageHasVersion.ts +7 -2
  186. package/lib/api/analyze/providers/shared.d.ts +0 -3
  187. package/lib/api/analyze/providers/shared.d.ts.map +0 -1
  188. package/lib/api/analyze/providers/shared.js +0 -23
  189. package/lib/api/analyze/providers/shared.js.map +0 -1
  190. package/lib/builder/webpack/providers/analyze/shared.d.ts +0 -3
  191. package/lib/builder/webpack/providers/analyze/shared.d.ts.map +0 -1
  192. package/lib/builder/webpack/providers/analyze/shared.js +0 -141
  193. package/lib/builder/webpack/providers/analyze/shared.js.map +0 -1
  194. package/src/api/analyze/providers/shared.ts +0 -26
  195. package/src/api/benchmark/utils/stats.spec.ts +0 -36
  196. package/src/builder/webpack/providers/analyze/shared.ts +0 -160
@@ -1,59 +1,194 @@
1
- import mdtable from 'mdtable';
2
- import prettyBytes from 'pretty-bytes';
1
+ import { table, TableUserConfig } from 'table';
2
+ import isEmpty from '@tinkoff/utils/is/empty';
3
+
3
4
  import type { Context } from '../../models/context';
4
5
  import type { CommandResult } from '../../models/command';
5
6
  import type { Result } from '../../api/benchmark';
7
+ import type { CompilationStats } from '../../api/benchmark/types';
8
+ import { DEFAULT_TIMES } from '../../api/benchmark/const';
6
9
 
7
10
  import { app } from '../index';
8
11
 
9
- const roundStats = (n: number) => Math.round(100 * n) / 100;
12
+ export function toFixedDigits(num: number, digits = 2): number {
13
+ if (digits === 0) {
14
+ return Math.floor(num);
15
+ }
16
+ return +num.toFixed(digits);
17
+ }
18
+
19
+ const getUnit = (num: number) => (num > 1 ? 'mins' : 'min');
20
+
21
+ export function formatCosts(costs: number): string {
22
+ // more than 1s
23
+ if (costs >= 1000) {
24
+ const sec = costs / 1000;
25
+ // more than 1min
26
+ if (sec >= 60) {
27
+ let mins = sec / 60;
28
+
29
+ mins = toFixedDigits(mins, 0);
30
+ const mUnit = getUnit(mins);
31
+ const restSec = toFixedDigits(sec % 60, 0);
32
+
33
+ if (restSec > 0) {
34
+ return `${mins}${mUnit} ${restSec}s`;
35
+ }
36
+ return `${mins}${mUnit}`;
37
+ }
38
+
39
+ return `${toFixedDigits(sec, 1)}s`;
40
+ }
41
+
42
+ if (costs >= 10) {
43
+ return `${+toFixedDigits(costs, 0)}ms`;
44
+ }
45
+
46
+ if (costs >= 1) {
47
+ return `${+toFixedDigits(costs, 1)}ms`;
48
+ }
49
+
50
+ let r = +toFixedDigits(costs, 2);
51
+
52
+ if (r === 0) {
53
+ r = +toFixedDigits(costs, 3);
54
+ }
55
+
56
+ return `${r}ms`;
57
+ }
58
+
59
+ const formatValues = (measures: Record<string, number>) => {
60
+ const result: [string, string][] = [];
61
+
62
+ for (const measureName in measures) {
63
+ const measureValue = measures[measureName];
64
+ result.push([measureName, formatCosts(measureValue)]);
65
+ }
66
+
67
+ return result;
68
+ };
69
+
70
+ const filterStats = (measures: Record<string, number>) => {
71
+ const entries = Object.entries(measures);
72
+ entries.sort((a, b) => b[1] - a[1]);
10
73
 
11
- const formatTimeStats = (stats: Result[string]['client']) => {
12
- return `${roundStats(stats.mean)}ms ± ${roundStats(stats.variance)}%`;
74
+ return entries.slice(0, 5).reduce((acc, [key, value]) => {
75
+ acc[key] = value;
76
+ return acc;
77
+ }, {});
13
78
  };
14
79
 
15
- const formatMemoryStats = (stats: Result[string]['client']) => {
16
- const value = prettyBytes(stats.mean, {
17
- maximumFractionDigits: 2,
18
- });
80
+ const getInfo = (compilationStats: CompilationStats) => {
81
+ const { totalBuildCosts, loaderBuildCosts, pluginBuildCosts } = compilationStats;
82
+
83
+ const totalBuildInfo = formatValues(totalBuildCosts);
84
+ const loaderInfo = formatValues(filterStats(loaderBuildCosts));
85
+ const pluginInfo = formatValues(filterStats(pluginBuildCosts));
19
86
 
20
- return `${value} ± ${roundStats(stats.variance)}%`;
87
+ return { totalBuildInfo, loaderInfo, pluginInfo };
21
88
  };
22
89
 
23
- const formatStatsTable = (stats: Result) => {
24
- const header = [''] as string[];
25
- const alignment = ['C'] as string[];
26
- const rows = [['time'], ['mem']] as string[][];
90
+ const mergeTotalInfo = (
91
+ server?: [string, string][] | undefined,
92
+ client?: [string, string][] | undefined
93
+ ) => {
94
+ const result = [];
27
95
 
28
- for (const key in stats) {
29
- header.push(key);
30
- alignment.push('C');
96
+ for (let i = 0; i < (server?.length ?? client?.length); i++) {
97
+ result.push([
98
+ server?.[i][0] ?? client?.[i][0] ?? '-',
99
+ server?.[i][1] ?? '0',
100
+ client?.[i][1] ?? '0',
101
+ ]);
102
+ }
103
+
104
+ return result;
105
+ };
31
106
 
32
- const { client, server, maxMemoryRss } = stats[key];
107
+ const mergeAdditionalInfo = (
108
+ server?: [string, string][] | undefined,
109
+ client?: [string, string][] | undefined
110
+ ) => {
111
+ const result = [];
33
112
 
34
- rows[0].push(`${formatTimeStats(client)} / ${formatTimeStats(server)}`);
35
- rows[1].push(formatMemoryStats(maxMemoryRss));
113
+ for (let i = 0; i < (server?.length ?? client?.length); i++) {
114
+ result.push([
115
+ server?.[i][0] ?? '-',
116
+ server?.[i][1] ?? '0',
117
+ client?.[i][0] ?? '-',
118
+ client?.[i][1] ?? 0,
119
+ ]);
36
120
  }
37
121
 
38
- return mdtable(
39
- {
40
- header,
41
- alignment,
42
- rows,
43
- },
44
- {
45
- borders: true,
46
- padding: 1,
47
- }
48
- );
122
+ return result;
123
+ };
124
+
125
+ const formatBuildTime = (totalBuildTime) =>
126
+ totalBuildTime ? `${(totalBuildTime / 1000).toFixed(2)}s` : '-';
127
+
128
+ const formatMemoryRss = (memoryRss) => (memoryRss ? `${(memoryRss / 1000000).toFixed(2)}mb` : '-');
129
+
130
+ const formatStatsTables = (stats: Result, times: number | undefined = DEFAULT_TIMES) => {
131
+ const {
132
+ clientBuildTime,
133
+ clientCompilationStats,
134
+ serverBuildTime,
135
+ serverCompilationStats,
136
+ maxMemoryRss,
137
+ clientMaxMemoryRss,
138
+ serverMaxMemoryRss,
139
+ } = stats;
140
+ const serverInfo = !isEmpty(serverCompilationStats) && getInfo(serverCompilationStats);
141
+ const clientInfo = !isEmpty(clientCompilationStats) && getInfo(clientCompilationStats);
142
+
143
+ const totalBuildData = [
144
+ [`Mean durations (${times} times)`, 'server', 'client'],
145
+ ['Total build time', formatBuildTime(serverBuildTime), formatBuildTime(clientBuildTime)],
146
+ maxMemoryRss
147
+ ? ['Total memory', formatMemoryRss(maxMemoryRss), '']
148
+ : ['Total memory', formatMemoryRss(serverMaxMemoryRss), formatMemoryRss(clientMaxMemoryRss)],
149
+ ...mergeTotalInfo(serverInfo.totalBuildInfo, clientInfo.totalBuildInfo),
150
+ ].filter(Boolean);
151
+
152
+ const totalBuildTableConfig: TableUserConfig = {
153
+ columns: [{ alignment: 'left' }, { alignment: 'center' }, { alignment: 'center' }],
154
+ };
155
+
156
+ if (maxMemoryRss) {
157
+ // @ts-expect-error
158
+ totalBuildTableConfig.spanningCells = [{ col: 1, row: 2, colSpan: 2, alignment: 'center' }];
159
+ }
160
+
161
+ const mergedLoadersInfo = mergeAdditionalInfo(serverInfo.loaderInfo, clientInfo.loaderInfo);
162
+ const mergedPluginsInfo = mergeAdditionalInfo(serverInfo.pluginInfo, clientInfo.pluginInfo);
163
+ const additionalBuildData = [
164
+ ['server', '', 'client', ''],
165
+ ['Loaders', '', '', ''],
166
+ ...mergedLoadersInfo,
167
+ ['Plugins', '', '', ''],
168
+ ...mergedPluginsInfo,
169
+ ];
170
+
171
+ const additionalBuildTableConfig: TableUserConfig = {
172
+ spanningCells: [
173
+ { col: 0, row: 0, colSpan: 2, alignment: 'center' },
174
+ { col: 2, row: 0, colSpan: 2, alignment: 'center' },
175
+ { col: 0, row: 1, colSpan: 4, alignment: 'center' },
176
+ { col: 0, row: mergedLoadersInfo.length + 2, colSpan: 4, alignment: 'center' },
177
+ ],
178
+ };
179
+
180
+ return [
181
+ table(totalBuildData, totalBuildTableConfig),
182
+ table(additionalBuildData, additionalBuildTableConfig),
183
+ ];
49
184
  };
50
185
 
51
- export default async (context: Context, parameters): Promise<CommandResult | any> => {
186
+ export default async (_context: Context, parameters): Promise<CommandResult | any> => {
52
187
  const { command, times, ...commandOptions } = parameters;
53
188
 
54
189
  const stats = await app.run('benchmark', { command, times, commandOptions });
55
190
 
56
- console.log(formatStatsTable(stats));
191
+ formatStatsTables(stats, times).forEach((buildTable) => console.log(buildTable));
57
192
 
58
193
  return Promise.resolve({
59
194
  status: 'ok',
@@ -36,6 +36,18 @@ export class BenchmarkCommand extends CLICommand<Params> {
36
36
  description:
37
37
  'Specify the names of the bundles that need to be collected, other bundles will not be collected and their request will fail with an error',
38
38
  },
39
+ {
40
+ name: '--fileCache',
41
+ value: '[fileCache]',
42
+ transformer: (value: string) => value !== 'false',
43
+ description: 'Enable/disable persistent file cache for used cli builder',
44
+ },
45
+ {
46
+ name: '-t, --buildType',
47
+ value: '[type]',
48
+ description: 'Build type <client|server>',
49
+ defaultValue: 'client',
50
+ },
39
51
  {
40
52
  name: '--times',
41
53
  value: '[times]',
@@ -54,6 +54,12 @@ class BuildCommand extends CLICommand<Params> {
54
54
  value: '[forPublish]',
55
55
  description: '<package> Prepare library package.json for publication',
56
56
  },
57
+ {
58
+ name: '--analyze',
59
+ value: '[analyze]',
60
+ description:
61
+ 'Run build with analyze, supported plugins: <bundle|whybundled|statoscope|rsdoctor>',
62
+ },
57
63
  {
58
64
  name: '--fileCache',
59
65
  value: '[fileCache]',
@@ -9,7 +9,7 @@
9
9
  "start": "tramvai start {{configEntry.name}}",
10
10
  "build": "tramvai build {{configEntry.name}}",
11
11
  "preview": "tramvai start-prod {{configEntry.name}}",
12
- "analyze": "tramvai analyze {{configEntry.name}}",
12
+ "analyze": "tramvai {{configEntry.name}} --analyze=statoscope",
13
13
  "lint": "eslint --ext .ts,.tsx --ignore-path .gitignore ."{{#if isJest}},
14
14
  "test": "jest --passWithNoTests",
15
15
  "test:watch": "jest --watch",
@@ -95,6 +95,12 @@ export class StartCommand extends CLICommand<Params> {
95
95
  value: '[showConfig]',
96
96
  description: 'Show config with which cli was launched',
97
97
  },
98
+ {
99
+ name: '--analyze',
100
+ value: '[analyze]',
101
+ description:
102
+ 'Run build with analyze, supported plugins: <bundle|whybundled|statoscope|rsdoctor>',
103
+ },
98
104
  {
99
105
  name: '--onlyBundles',
100
106
  value: '[onlyBundles]',
@@ -1,7 +1,7 @@
1
1
  import { packageHasVersion } from '../../utils/commands/dependencies/packageHasVersion';
2
2
  import type { Params } from './update';
3
3
 
4
- export const checkVersion = async (_, { to: version = 'latest' }: Params) => {
4
+ export const checkVersion = async (context, { to: version = 'latest' }: Params) => {
5
5
  if (version === 'latest') {
6
6
  return {
7
7
  name: 'checkVersion',
@@ -9,7 +9,9 @@ export const checkVersion = async (_, { to: version = 'latest' }: Params) => {
9
9
  };
10
10
  }
11
11
 
12
- if (await packageHasVersion('@tramvai/core', version)) {
12
+ const registryUrl = await context.packageManager.getRegistryUrl();
13
+
14
+ if (await packageHasVersion('@tramvai/core', version, registryUrl)) {
13
15
  return {
14
16
  name: 'checkVersion',
15
17
  status: 'ok',
@@ -7,13 +7,15 @@ export const getLibPackageVersion = async (
7
7
  name: string,
8
8
  version: string,
9
9
  resolvedVersion: string,
10
+ registryUrl: string,
10
11
  spinner: Ora
11
12
  ): Promise<string | undefined> => {
12
13
  const unifiedModule = DEPENDANT_LIBS_MAP.get(name);
13
14
 
14
15
  if (!unifiedModule) {
15
16
  try {
16
- return await getLatestPackageVersion(name, { version });
17
+ // @ts-expect-error uncomplete type definition, `registryUrl` is supported
18
+ return await getLatestPackageVersion(name, { version, registryUrl });
17
19
  } catch (e) {
18
20
  // clear the spinner to be able to log info that should be preserved in the output
19
21
  // the idea borrowed from [here](https://github.com/sindresorhus/ora/issues/120)
@@ -16,7 +16,10 @@ export default async (
16
16
  context: Context,
17
17
  { to: version = 'latest', tag }: Params
18
18
  ): Promise<CommandResult> => {
19
- const resolvedVersion = tag ? version : await getLatestPackageVersion('@tramvai/cli', version);
19
+ const registry = await context.packageManager.getRegistryUrl();
20
+ const resolvedVersion = tag
21
+ ? version
22
+ : await getLatestPackageVersion('@tramvai/cli', registry, version);
20
23
  const currentVersion = await findTramvaiVersion();
21
24
 
22
25
  if (!currentVersion) {
@@ -40,12 +43,12 @@ export default async (
40
43
  if (context.packageManager.workspaces) {
41
44
  await Promise.all(
42
45
  context.packageManager.workspaces.map((directory) =>
43
- updatePackageJson(version, resolvedVersion, currentVersion, tag, directory)
46
+ updatePackageJson(version, resolvedVersion, currentVersion, registry, tag, directory)
44
47
  )
45
48
  );
46
49
  }
47
50
 
48
- await updatePackageJson(version, resolvedVersion, currentVersion, tag);
51
+ await updatePackageJson(version, resolvedVersion, currentVersion, registry, tag);
49
52
 
50
53
  context.logger.event({
51
54
  type: 'info',
@@ -76,7 +76,12 @@ it('should update tramvai deps to latest versions', async () => {
76
76
  return {};
77
77
  });
78
78
 
79
- await updatePackageJson(LATEST_TRAMVAI_VERSION, LATEST_TRAMVAI_VERSION, CURRENT_TRAMVAI_VERSION);
79
+ await updatePackageJson(
80
+ LATEST_TRAMVAI_VERSION,
81
+ LATEST_TRAMVAI_VERSION,
82
+ CURRENT_TRAMVAI_VERSION,
83
+ 'fake-registry'
84
+ );
80
85
 
81
86
  // eslint-disable-next-line jest/no-interpolation-in-snapshots
82
87
  expect(mockFsWrite.mock.calls[0]).toMatchInlineSnapshot(`
@@ -138,7 +143,12 @@ it('prerelease should be resolved and used for dependant libs', async () => {
138
143
  return {};
139
144
  });
140
145
 
141
- await updatePackageJson('prerelease', LATEST_TRAMVAI_VERSION, CURRENT_TRAMVAI_VERSION);
146
+ await updatePackageJson(
147
+ 'prerelease',
148
+ LATEST_TRAMVAI_VERSION,
149
+ CURRENT_TRAMVAI_VERSION,
150
+ 'fake-registry'
151
+ );
142
152
 
143
153
  // eslint-disable-next-line jest/no-interpolation-in-snapshots
144
154
  expect(mockFsWrite.mock.calls[0]).toMatchInlineSnapshot(`
@@ -201,7 +211,13 @@ it('dist tag should be used for dependant libs', async () => {
201
211
  return {};
202
212
  });
203
213
 
204
- await updatePackageJson('prerelease', 'prerelease', CURRENT_TRAMVAI_VERSION, true);
214
+ await updatePackageJson(
215
+ 'prerelease',
216
+ 'prerelease',
217
+ CURRENT_TRAMVAI_VERSION,
218
+ 'fake-registry',
219
+ true
220
+ );
205
221
 
206
222
  // eslint-disable-next-line jest/no-interpolation-in-snapshots
207
223
  expect(mockFsWrite.mock.calls[0]).toMatchInlineSnapshot(`
@@ -13,6 +13,7 @@ const updateDependencies = (
13
13
  resolvedVersion: string,
14
14
  currentVersion: string,
15
15
  tag: boolean,
16
+ registryUrl: string,
16
17
  spinner: Ora
17
18
  ) => {
18
19
  return pMap<string, void>(
@@ -25,12 +26,12 @@ const updateDependencies = (
25
26
  } else if (isDependantLib(dep)) {
26
27
  const libVersion = tag
27
28
  ? resolvedVersion
28
- : await getLibPackageVersion(dep, version, resolvedVersion, spinner);
29
+ : await getLibPackageVersion(dep, version, resolvedVersion, registryUrl, spinner);
29
30
  nextVersion = libVersion;
30
31
  }
31
32
 
32
33
  if (nextVersion) {
33
- const depHasVersion = await packageHasVersion(dep, nextVersion);
34
+ const depHasVersion = await packageHasVersion(dep, nextVersion, registryUrl);
34
35
 
35
36
  // clear the spinner to be able to log info that should be preserved in the output
36
37
  // the idea borrowed from [here](https://github.com/sindresorhus/ora/issues/120)
@@ -62,6 +63,7 @@ export const updatePackageJson = async (
62
63
  version: string,
63
64
  resolvedVersion: string,
64
65
  currentVersion: string,
66
+ registryUrl: string,
65
67
  tag?: boolean,
66
68
  path = '.'
67
69
  ) => {
@@ -78,6 +80,7 @@ export const updatePackageJson = async (
78
80
  resolvedVersion,
79
81
  currentVersion,
80
82
  tag,
83
+ registryUrl,
81
84
  spinner
82
85
  );
83
86
  await updateDependencies(
@@ -86,6 +89,7 @@ export const updatePackageJson = async (
86
89
  resolvedVersion,
87
90
  currentVersion,
88
91
  tag,
92
+ registryUrl,
89
93
  spinner
90
94
  );
91
95
  await updateDependencies(
@@ -94,6 +98,7 @@ export const updatePackageJson = async (
94
98
  resolvedVersion,
95
99
  currentVersion,
96
100
  tag,
101
+ registryUrl,
97
102
  spinner
98
103
  );
99
104
 
@@ -37,9 +37,11 @@ export interface Settings<E extends Env> {
37
37
  profile?: boolean;
38
38
  noServerRebuild?: boolean;
39
39
  noClientRebuild?: boolean;
40
+ benchmark?: boolean;
40
41
  resolveSymlinks?: boolean;
41
42
  showConfig?: boolean;
42
43
  onlyBundles?: string[];
44
+ analyze?: false | 'bundle' | 'whybundled' | 'statoscope' | 'rsdoctor';
43
45
  disableProdOptimization?: boolean;
44
46
  fileCache?: boolean;
45
47
  }
@@ -131,8 +133,10 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
131
133
  profile: false,
132
134
  noClientRebuild: false,
133
135
  noServerRebuild: false,
136
+ benchmark: false,
134
137
  resolveSymlinks: true,
135
138
  disableProdOptimization: false,
139
+ analyze: false,
136
140
  onlyBundles: [],
137
141
  // according to measures fileCache in webpack doesn't affect
138
142
  // performance much so enable it by default as it always was before
@@ -1,11 +1,12 @@
1
- import applyOrReturn from '@tinkoff/utils/function/applyOrReturn';
2
1
  import path from 'path';
3
2
  import type Config from 'webpack-chain';
4
3
  import ExtractCssChunks from 'mini-css-extract-plugin';
5
4
  import { createGenerator } from '@tinkoff/minicss-class-generator';
5
+
6
6
  import { safeRequire } from '../../../utils/safeRequire';
7
7
  import type { ConfigManager } from '../../../config/configManager';
8
8
  import type { CliConfigEntry } from '../../../typings/configEntry/cli';
9
+ import { getActualTarget, getBrowserslistTargets } from '../utils/browserslist';
9
10
 
10
11
  const cssLocalIdentNameDevDefault = '[name]__[local]_[minicss]';
11
12
  const cssLocalIdentNameProdDefault = '[minicss]';
@@ -21,8 +22,9 @@ interface Options {
21
22
  export const cssWebpackRulesFactory =
22
23
  (configManager: ConfigManager<CliConfigEntry>, options: Options = {}) =>
23
24
  (config: Config) => {
24
- const { env, sourceMap, buildType } = configManager;
25
+ const { env, sourceMap, buildType, experiments, target } = configManager;
25
26
  const {
27
+ rootDir,
26
28
  postcss: {
27
29
  config: postcssConfig,
28
30
  cssLocalIdentName = env === 'production'
@@ -31,6 +33,9 @@ export const cssWebpackRulesFactory =
31
33
  cssModulePattern,
32
34
  },
33
35
  } = configManager;
36
+ const isServer = configManager.buildType === 'server';
37
+ const actualTarget = getActualTarget(target, isServer);
38
+ const browsersListTargets = getBrowserslistTargets(rootDir, actualTarget);
34
39
  const localIdentName = options.localIdentName ?? cssLocalIdentName;
35
40
 
36
41
  const configCssLoader = (cfg) => {
@@ -73,17 +78,33 @@ export const cssWebpackRulesFactory =
73
78
  typeof postcssConfig === 'undefined'
74
79
  ) ?? {};
75
80
 
76
- const postcssOptionsFn = (...args) => ({
77
- ...postcssCfg,
78
- plugins: [
79
- require('postcss-modules-tilda'),
80
- require('postcss-modules-values-replace')({ importsAsModuleRequests: true }),
81
+ // https://github.com/webpack-contrib/postcss-loader/blob/master/src/config.d.ts
82
+ const postcssOptionsFn = (loaderContext: any) => {
83
+ const isFnConfig = typeof postcssCfg === 'function';
84
+ // TODO: async config fn support?
85
+ const defaultConfig = isFnConfig ? postcssCfg(loaderContext) : postcssCfg;
86
+ // eslint-disable-next-line no-nested-ternary
87
+ const defaultPlugins = defaultConfig.plugins ? defaultConfig.plugins : [];
88
+
89
+ return {
90
+ config: false,
91
+ ...defaultConfig,
81
92
  // TODO: придумать как прокинуть настройки browserslist в autoprefixer - сейчас autoprefixer добавляется в самом приложении и из
82
93
  // конфига нет возможности задавать динамический env в зависимости от сборки. Подсунуть в сам autoprefixer после его инициализации тоже
83
94
  // тоже не получится - https://github.com/postcss/autoprefixer/blob/10.3.1/lib/autoprefixer.js#L108
84
- ...(applyOrReturn(args, postcssCfg.plugins) || []),
85
- ],
86
- });
95
+ plugins: Array.isArray(defaultPlugins)
96
+ ? [
97
+ require('postcss-modules-tilda'),
98
+ require('postcss-modules-values-replace')({ importsAsModuleRequests: true }),
99
+ ...defaultPlugins,
100
+ ]
101
+ : {
102
+ 'postcss-modules-tilda': {},
103
+ 'postcss-modules-values-replace': { importsAsModuleRequests: true },
104
+ ...defaultPlugins,
105
+ },
106
+ };
107
+ };
87
108
 
88
109
  // otherwise postcss-loader will use cosmiconfig to resolve postcss configuration file
89
110
  // https://github.com/webpack-contrib/postcss-loader/blob/6f470db420f6febbea729080921050e8fe353226/src/index.js#L38
@@ -93,12 +114,27 @@ export const cssWebpackRulesFactory =
93
114
  .rule('css')
94
115
  .test(/\.css$/)
95
116
  .batch(configCssLoader)
96
- .use('postcss')
97
- .loader('postcss-loader')
98
- .options({
99
- sourceMap,
100
- postcssOptions: postcssOptionsFn,
101
- });
117
+ .when(
118
+ experiments.lightningcss,
119
+ (cfg) => {
120
+ const lightningcss = require('lightningcss');
121
+
122
+ return cfg
123
+ .use('lightningcss')
124
+ .loader('lightningcss-loader')
125
+ .options({
126
+ options: {
127
+ implementation: lightningcss,
128
+ targets: browsersListTargets,
129
+ },
130
+ });
131
+ },
132
+ (cfg) =>
133
+ cfg.use('postcss').loader('postcss-loader').options({
134
+ sourceMap,
135
+ postcssOptions: postcssOptionsFn,
136
+ })
137
+ );
102
138
  };
103
139
 
104
140
  export default cssWebpackRulesFactory;
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import type Config from 'webpack-chain';
2
3
  import { modernLibsFilter } from '@tinkoff/is-modern-lib';
3
4
  import { applyThreadLoader } from '../utils/threadLoader';
@@ -5,25 +6,22 @@ import type { ConfigManager } from '../../../config/configManager';
5
6
  import { getTranspilerConfig, addTranspilerLoader } from '../utils/transpiler';
6
7
  import type { CliConfigEntry } from '../../../typings/configEntry/cli';
7
8
 
8
- // eslint-disable-next-line import/no-default-export
9
- export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
10
- const { transpileOnlyModernLibs } = configManager;
11
-
12
- const rule = config.module
13
- .rule('js')
14
- .test(/\.[cm]?js[x]?$/)
15
- .batch(applyThreadLoader(configManager));
9
+ const projectRulesName = 'project';
10
+ const nodeModuleRulesName = 'node_module';
16
11
 
12
+ const applyProjectRules = (rule, configManager) => {
17
13
  rule
18
- .oneOf('project')
14
+ .oneOf(projectRulesName)
19
15
  .exclude.add(/node_modules/)
20
16
  .end()
21
17
  .use('transpiler')
22
- .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)));
18
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)))
19
+ .end();
20
+ };
23
21
 
22
+ const applyNodeModulesRules = (rule, configManager) => {
24
23
  rule
25
- .oneOf('node_module')
26
- .when(transpileOnlyModernLibs, (cfg) => cfg.include.add(modernLibsFilter))
24
+ .oneOf(nodeModuleRulesName)
27
25
  .merge({
28
26
  // true value forces to use file extensions for importing mjs modules
29
27
  // but we want to use mjs if it exists anyway
@@ -32,3 +30,54 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
32
30
  .use('transpiler')
33
31
  .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager, { hot: false })));
34
32
  };
33
+
34
+ // eslint-disable-next-line import/no-default-export
35
+ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
36
+ const { transpileOnlyModernLibs } = configManager;
37
+ const { include } = configManager.experiments.transpilation;
38
+ const shouldTranspileOnlyModern = transpileOnlyModernLibs || include === 'only-modern';
39
+
40
+ const rule = config.module
41
+ .rule('js')
42
+ .test(/\.[cm]?js[x]?$/)
43
+ .batch(applyThreadLoader(configManager));
44
+
45
+ if (configManager.env === 'production') {
46
+ applyProjectRules(rule, configManager);
47
+ applyNodeModulesRules(rule, configManager);
48
+
49
+ if (shouldTranspileOnlyModern) {
50
+ rule.oneOf(nodeModuleRulesName).include.add(modernLibsFilter);
51
+ }
52
+ } else {
53
+ const shouldSkipTranspiling = include === 'none';
54
+ const shouldSelectiveTranspile = Array.isArray(include);
55
+
56
+ if (shouldSkipTranspiling) {
57
+ rule.exclude
58
+ .add(/node_modules/)
59
+ .end()
60
+ .use('transpiler')
61
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)));
62
+ } else {
63
+ applyProjectRules(rule, configManager);
64
+ applyNodeModulesRules(rule, configManager);
65
+
66
+ if (shouldSelectiveTranspile) {
67
+ const includeForTranspiling = (<string[]>include).map((includePath) =>
68
+ // require.resolve return absolute path to entrypoint of dependency
69
+ // but we need root dirname of dependency inside node_modules
70
+ path.dirname(require.resolve(`${includePath}/package.json`))
71
+ );
72
+
73
+ includeForTranspiling.forEach((includePath) => {
74
+ rule.oneOf(nodeModuleRulesName).include.add(includePath);
75
+ });
76
+ } else if (shouldTranspileOnlyModern) {
77
+ if (shouldTranspileOnlyModern) {
78
+ rule.oneOf(nodeModuleRulesName).include.add(modernLibsFilter);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ };