@tramvai/cli 5.24.0 → 5.41.1

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 (130) hide show
  1. package/README.md +38 -0
  2. package/bin/platform.js +26 -1
  3. package/bin/spawn.js +36 -8
  4. package/lib/api/build/index.d.ts +1 -0
  5. package/lib/api/build/index.d.ts.map +1 -1
  6. package/lib/api/build/index.js.map +1 -1
  7. package/lib/api/start-prod/providers/application.js +2 -2
  8. package/lib/api/start-prod/providers/application.js.map +1 -1
  9. package/lib/builder/webpack/devServer/pool/process/pool.d.ts.map +1 -1
  10. package/lib/builder/webpack/devServer/pool/process/pool.js +2 -1
  11. package/lib/builder/webpack/devServer/pool/process/pool.js.map +1 -1
  12. package/lib/builder/webpack/devServer/pool/thread/pool.d.ts.map +1 -1
  13. package/lib/builder/webpack/devServer/pool/thread/pool.js +2 -1
  14. package/lib/builder/webpack/devServer/pool/thread/pool.js.map +1 -1
  15. package/lib/builder/webpack/devServer/server.d.ts.map +1 -1
  16. package/lib/builder/webpack/devServer/server.js +31 -2
  17. package/lib/builder/webpack/devServer/server.js.map +1 -1
  18. package/lib/builder/webpack/tokens.d.ts +3 -3
  19. package/lib/cli/CLI.d.ts +1 -1
  20. package/lib/cli/CLI.d.ts.map +1 -1
  21. package/lib/cli/CLI.js +2 -2
  22. package/lib/cli/CLI.js.map +1 -1
  23. package/lib/cli/index.d.ts.map +1 -1
  24. package/lib/cli/index.js +1 -2
  25. package/lib/cli/index.js.map +1 -1
  26. package/lib/commands/add/add.d.ts +3 -2
  27. package/lib/commands/add/add.d.ts.map +1 -1
  28. package/lib/commands/add/add.js +8 -2
  29. package/lib/commands/add/add.js.map +1 -1
  30. package/lib/commands/add/checkPackageValidator.d.ts +1 -1
  31. package/lib/commands/add/checkPackageValidator.d.ts.map +1 -1
  32. package/lib/commands/add/checkPackageValidator.js +7 -2
  33. package/lib/commands/add/checkPackageValidator.js.map +1 -1
  34. package/lib/commands/add/command.d.ts +8 -3
  35. package/lib/commands/add/command.d.ts.map +1 -1
  36. package/lib/commands/add/command.js +6 -1
  37. package/lib/commands/add/command.js.map +1 -1
  38. package/lib/commands/new/new.d.ts.map +1 -1
  39. package/lib/commands/new/new.js +22 -20
  40. package/lib/commands/new/new.js.map +1 -1
  41. package/lib/commands/new/steps/installDependencies.d.ts +4 -3
  42. package/lib/commands/new/steps/installDependencies.d.ts.map +1 -1
  43. package/lib/commands/new/steps/installDependencies.js +58 -20
  44. package/lib/commands/new/steps/installDependencies.js.map +1 -1
  45. package/lib/commands/update/update.d.ts.map +1 -1
  46. package/lib/commands/update/update.js +14 -3
  47. package/lib/commands/update/update.js.map +1 -1
  48. package/lib/commands/update/updatePackageJson.d.ts +1 -1
  49. package/lib/commands/update/updatePackageJson.d.ts.map +1 -1
  50. package/lib/commands/update/updatePackageJson.js +5 -11
  51. package/lib/commands/update/updatePackageJson.js.map +1 -1
  52. package/lib/config/configManager.d.ts +1 -1
  53. package/lib/config/configManager.d.ts.map +1 -1
  54. package/lib/config/constants.d.ts +0 -1
  55. package/lib/config/constants.d.ts.map +1 -1
  56. package/lib/config/constants.js +1 -2
  57. package/lib/config/constants.js.map +1 -1
  58. package/lib/config/utils.d.ts +2 -0
  59. package/lib/config/utils.d.ts.map +1 -0
  60. package/lib/config/utils.js +19 -0
  61. package/lib/config/utils.js.map +1 -0
  62. package/lib/di/tokens/config.d.ts +1 -1
  63. package/lib/library/webpack/application/client/dev.js +1 -1
  64. package/lib/library/webpack/application/client/dev.js.map +1 -1
  65. package/lib/library/webpack/application/client/prod/optimization/splitChunks.d.ts.map +1 -1
  66. package/lib/library/webpack/application/client/prod/optimization/splitChunks.js +51 -1
  67. package/lib/library/webpack/application/client/prod/optimization/splitChunks.js.map +1 -1
  68. package/lib/library/webpack/application/common.d.ts.map +1 -1
  69. package/lib/library/webpack/application/common.js +12 -0
  70. package/lib/library/webpack/application/common.js.map +1 -1
  71. package/lib/library/webpack/application/server/dev.js +1 -1
  72. package/lib/library/webpack/application/server/dev.js.map +1 -1
  73. package/lib/library/webpack/utils/threadLoader.d.ts.map +1 -1
  74. package/lib/library/webpack/utils/threadLoader.js +1 -0
  75. package/lib/library/webpack/utils/threadLoader.js.map +1 -1
  76. package/lib/models/context.d.ts +3 -3
  77. package/lib/models/context.d.ts.map +1 -1
  78. package/lib/models/context.js +1 -2
  79. package/lib/models/context.js.map +1 -1
  80. package/lib/schema/autogeneratedSchema.json +34 -24
  81. package/lib/typings/configEntry/application.d.ts +10 -0
  82. package/lib/typings/configEntry/application.d.ts.map +1 -1
  83. package/lib/typings/configEntry/cli.d.ts +1 -1
  84. package/lib/typings/configEntry/cli.d.ts.map +1 -1
  85. package/lib/utils/commands/dependencies/findTramvaiVersion.js +1 -1
  86. package/lib/utils/commands/dependencies/findTramvaiVersion.js.map +1 -1
  87. package/lib/utils/commands/dependencies/getWorkspaceRootByAppName.d.ts +3 -0
  88. package/lib/utils/commands/dependencies/getWorkspaceRootByAppName.d.ts.map +1 -0
  89. package/lib/utils/commands/dependencies/getWorkspaceRootByAppName.js +8 -0
  90. package/lib/utils/commands/dependencies/getWorkspaceRootByAppName.js.map +1 -0
  91. package/lib/utils/npmRequire.js +1 -1
  92. package/lib/utils/npmRequire.js.map +1 -1
  93. package/package.json +5 -4
  94. package/schema.json +34 -24
  95. package/src/api/build/index.ts +1 -0
  96. package/src/api/start-prod/providers/application.ts +2 -2
  97. package/src/builder/webpack/devServer/pool/process/pool.ts +3 -2
  98. package/src/builder/webpack/devServer/pool/thread/pool.ts +3 -2
  99. package/src/builder/webpack/devServer/server.ts +38 -2
  100. package/src/cli/CLI.ts +1 -3
  101. package/src/cli/index.ts +1 -3
  102. package/src/commands/add/add.ts +17 -3
  103. package/src/commands/add/checkPackageValidator.ts +6 -2
  104. package/src/commands/add/command.ts +6 -1
  105. package/src/commands/new/new.ts +34 -21
  106. package/src/commands/new/steps/installDependencies.ts +75 -27
  107. package/src/commands/new/templates/monorepo-block/package.json.hbs +5 -0
  108. package/src/commands/new/templates/shared/package.json.hbs +2 -1
  109. package/src/commands/update/update.ts +26 -3
  110. package/src/commands/update/updatePackageJson.spec.ts +16 -10
  111. package/src/commands/update/updatePackageJson.ts +10 -15
  112. package/src/config/configManager.ts +1 -1
  113. package/src/config/constants.ts +0 -2
  114. package/src/config/utils.ts +18 -0
  115. package/src/library/swc/__integration__/swc.start.test.ts +3 -0
  116. package/src/library/webpack/application/client/dev.ts +1 -1
  117. package/src/library/webpack/application/client/prod/optimization/splitChunks.ts +66 -2
  118. package/src/library/webpack/application/common.ts +13 -0
  119. package/src/library/webpack/application/server/dev.ts +1 -1
  120. package/src/library/webpack/utils/threadLoader.ts +1 -0
  121. package/src/models/config.spec.ts +4 -0
  122. package/src/models/context.ts +3 -2
  123. package/src/schema/autogeneratedSchema.json +34 -24
  124. package/src/schema/tramvai.spec.ts +2 -0
  125. package/src/typings/configEntry/application.ts +11 -0
  126. package/src/typings/configEntry/cli.ts +6 -1
  127. package/src/typings/webpack-chain/index.d.ts +1 -0
  128. package/src/utils/commands/dependencies/findTramvaiVersion.ts +1 -1
  129. package/src/utils/commands/dependencies/getWorkspaceRootByAppName.ts +5 -0
  130. package/src/utils/npmRequire.ts +1 -1
package/schema.json CHANGED
@@ -138,6 +138,11 @@
138
138
  "granularChunks"
139
139
  ]
140
140
  },
141
+ "frameworkChunk": {
142
+ "title": "Move tramvai packages into a separate chunk",
143
+ "default": false,
144
+ "type": "boolean"
145
+ },
141
146
  "granularChunksSplitNumber": {
142
147
  "title": "Move module to shared chunk if used at least as many times in other chunks",
143
148
  "default": 2,
@@ -1005,6 +1010,11 @@
1005
1010
  },
1006
1011
  "additionalProperties": false
1007
1012
  },
1013
+ "withModulesStats": {
1014
+ "title": "Enable generation of stats modules file for polyfill usage analysis",
1015
+ "default": false,
1016
+ "type": "boolean"
1017
+ },
1008
1018
  "sourceMap": {
1009
1019
  "cli_overridable": "",
1010
1020
  "title": "Enable source maps",
@@ -1249,23 +1259,23 @@
1249
1259
  "dotAll": {
1250
1260
  "type": "boolean"
1251
1261
  },
1252
- "__@match@7341": {
1262
+ "__@match@7843": {
1253
1263
  "type": "object",
1254
1264
  "additionalProperties": false
1255
1265
  },
1256
- "__@replace@7343": {
1266
+ "__@replace@7845": {
1257
1267
  "type": "object",
1258
1268
  "additionalProperties": false
1259
1269
  },
1260
- "__@search@7346": {
1270
+ "__@search@7848": {
1261
1271
  "type": "object",
1262
1272
  "additionalProperties": false
1263
1273
  },
1264
- "__@split@7348": {
1274
+ "__@split@7850": {
1265
1275
  "type": "object",
1266
1276
  "additionalProperties": false
1267
1277
  },
1268
- "__@matchAll@7350": {
1278
+ "__@matchAll@7852": {
1269
1279
  "type": "object",
1270
1280
  "additionalProperties": false
1271
1281
  }
@@ -1297,9 +1307,9 @@
1297
1307
  "eval",
1298
1308
  "eval-cheap-module-source-map",
1299
1309
  "eval-cheap-source-map",
1300
- "eval-source-map"
1301
- ],
1302
- "type": "string"
1310
+ "eval-source-map",
1311
+ false
1312
+ ]
1303
1313
  }
1304
1314
  },
1305
1315
  "additionalProperties": false
@@ -2010,23 +2020,23 @@
2010
2020
  "dotAll": {
2011
2021
  "type": "boolean"
2012
2022
  },
2013
- "__@match@7341": {
2023
+ "__@match@7843": {
2014
2024
  "type": "object",
2015
2025
  "additionalProperties": false
2016
2026
  },
2017
- "__@replace@7343": {
2027
+ "__@replace@7845": {
2018
2028
  "type": "object",
2019
2029
  "additionalProperties": false
2020
2030
  },
2021
- "__@search@7346": {
2031
+ "__@search@7848": {
2022
2032
  "type": "object",
2023
2033
  "additionalProperties": false
2024
2034
  },
2025
- "__@split@7348": {
2035
+ "__@split@7850": {
2026
2036
  "type": "object",
2027
2037
  "additionalProperties": false
2028
2038
  },
2029
- "__@matchAll@7350": {
2039
+ "__@matchAll@7852": {
2030
2040
  "type": "object",
2031
2041
  "additionalProperties": false
2032
2042
  }
@@ -2058,9 +2068,9 @@
2058
2068
  "eval",
2059
2069
  "eval-cheap-module-source-map",
2060
2070
  "eval-cheap-source-map",
2061
- "eval-source-map"
2062
- ],
2063
- "type": "string"
2071
+ "eval-source-map",
2072
+ false
2073
+ ]
2064
2074
  }
2065
2075
  },
2066
2076
  "additionalProperties": false
@@ -2771,23 +2781,23 @@
2771
2781
  "dotAll": {
2772
2782
  "type": "boolean"
2773
2783
  },
2774
- "__@match@7341": {
2784
+ "__@match@7843": {
2775
2785
  "type": "object",
2776
2786
  "additionalProperties": false
2777
2787
  },
2778
- "__@replace@7343": {
2788
+ "__@replace@7845": {
2779
2789
  "type": "object",
2780
2790
  "additionalProperties": false
2781
2791
  },
2782
- "__@search@7346": {
2792
+ "__@search@7848": {
2783
2793
  "type": "object",
2784
2794
  "additionalProperties": false
2785
2795
  },
2786
- "__@split@7348": {
2796
+ "__@split@7850": {
2787
2797
  "type": "object",
2788
2798
  "additionalProperties": false
2789
2799
  },
2790
- "__@matchAll@7350": {
2800
+ "__@matchAll@7852": {
2791
2801
  "type": "object",
2792
2802
  "additionalProperties": false
2793
2803
  }
@@ -2819,9 +2829,9 @@
2819
2829
  "eval",
2820
2830
  "eval-cheap-module-source-map",
2821
2831
  "eval-cheap-source-map",
2822
- "eval-source-map"
2823
- ],
2824
- "type": "string"
2832
+ "eval-source-map",
2833
+ false
2834
+ ]
2825
2835
  }
2826
2836
  },
2827
2837
  "additionalProperties": false
@@ -23,6 +23,7 @@ export type Params = WithConfig<{
23
23
  env?: Record<string, string>;
24
24
  fileCache?: boolean;
25
25
  withBuildStats?: boolean;
26
+ withModulesStats?: boolean;
26
27
 
27
28
  // `package` target parameters
28
29
  watchMode?: boolean;
@@ -12,7 +12,7 @@ import {
12
12
  } from '../../../di/tokens';
13
13
  import type { ApplicationConfigEntry } from '../../../typings/configEntry/application';
14
14
  import { CLOSE_HANDLER_TOKEN, SERVER_PROCESS_TOKEN } from '../tokens';
15
- import { DEBUG_ARGV } from '../../../config/constants';
15
+ import { getDebugArg } from '../../../config/utils';
16
16
  import { safeRequire } from '../../../utils/safeRequire';
17
17
  import type { ConfigManager } from '../../../config/configManager';
18
18
  import { createConfigManager } from '../../../config/configManager';
@@ -62,7 +62,7 @@ export const applicationsProviders: readonly Provider[] = [
62
62
  const { debug, port, assetsPrefix, https } = serverConfigManager;
63
63
  const root = serverConfigManager.buildPath;
64
64
  return fork(path.resolve(root, 'server.js'), [], {
65
- execArgv: debug ? DEBUG_ARGV : [],
65
+ execArgv: getDebugArg(debug),
66
66
  cwd: root,
67
67
  env: {
68
68
  ...safeRequire(path.resolve(process.cwd(), 'env.development'), true),
@@ -2,7 +2,8 @@ import path from 'path';
2
2
  import type { Worker } from 'cluster';
3
3
  import cluster from 'cluster';
4
4
  import type { WorkerBridgeFactory } from '../base/types';
5
- import { DEBUG_ARGV, TRACE_ARGV } from '../../../../../config/constants';
5
+ import { TRACE_ARGV } from '../../../../../config/constants';
6
+ import { getDebugArg } from '../../../../../config/utils';
6
7
  import { SERVER_CONFIG_MANAGER_TOKEN } from '../../../tokens';
7
8
  import {
8
9
  STDOUT_TOKEN,
@@ -24,7 +25,7 @@ export const ProcessWorkerBridge: WorkerBridgeFactory<Worker> = (di) => {
24
25
  // указываем другой файл для работы cluster.fork
25
26
  exec: path.resolve(__dirname, './worker.js'),
26
27
  execArgv: ([] as string[]).concat(
27
- configManager.debug ? DEBUG_ARGV : [],
28
+ getDebugArg(configManager.debug),
28
29
  configManager.trace ? TRACE_ARGV : []
29
30
  ),
30
31
  // устанавливаем в качестве дебага дефолтный порт мастера, т.к. у нас и так один процесс может работать
@@ -1,7 +1,8 @@
1
1
  import path from 'path';
2
2
  import { Worker } from 'worker_threads';
3
3
  import type { WorkerBridgeFactory } from '../base/types';
4
- import { DEBUG_ARGV, TRACE_ARGV } from '../../../../../config/constants';
4
+ import { TRACE_ARGV } from '../../../../../config/constants';
5
+ import { getDebugArg } from '../../../../../config/utils';
5
6
  import { SERVER_CONFIG_MANAGER_TOKEN } from '../../../tokens';
6
7
  import { COMMAND_PARAMETERS_TOKEN, CONFIG_ROOT_DIR_TOKEN } from '../../../../../di/tokens';
7
8
 
@@ -27,7 +28,7 @@ export const ThreadWorkerBridge: WorkerBridgeFactory<Worker> = (di) => {
27
28
 
28
29
  const worker = new Worker(path.resolve(__dirname, './worker.js'), {
29
30
  execArgv: [].concat(
30
- configManager.debug ? DEBUG_ARGV : [],
31
+ getDebugArg(configManager.debug),
31
32
  configManager.trace ? TRACE_ARGV : []
32
33
  ),
33
34
  env: {
@@ -1,5 +1,7 @@
1
1
  import noop from '@tinkoff/utils/function/noop';
2
2
  import eachObj from '@tinkoff/utils/object/each';
3
+ import chokidar from 'chokidar';
4
+ import chalk from 'chalk';
3
5
  import path from 'path';
4
6
  import type webpack from 'webpack';
5
7
  import type Config from 'webpack-chain';
@@ -72,6 +74,7 @@ export const serverRunner = ({
72
74
  }
73
75
 
74
76
  const fs = serverCompiler.outputFileSystem as any;
77
+
75
78
  // ThreadWorkerPool is experimental
76
79
  // it doesn't work well when running integration tests in tramvai repo
77
80
  // mostly because of the some problems with `babel-plugin-lodash`
@@ -85,6 +88,7 @@ export const serverRunner = ({
85
88
  ? ThreadWorkerBridge
86
89
  : ProcessWorkerBridge
87
90
  );
91
+
88
92
  let worker: Worker | null;
89
93
  let serverInvalidated = true;
90
94
  let workerPort: number | null;
@@ -260,14 +264,14 @@ export const serverRunner = ({
260
264
  }
261
265
  });
262
266
 
263
- compiler.hooks.done.tap(HOOK_NAME, async (stats) => {
267
+ async function runWorker(stats?: any) {
264
268
  if (serverInvalidated) {
265
269
  workerPort = null;
266
270
  workerPortPromise = null;
267
271
 
268
272
  serverInvalidated = false;
269
273
 
270
- if (stats.hasErrors()) {
274
+ if (stats?.hasErrors()) {
271
275
  // всплыли ошибки при сборке - просто игнорим калбек чтобы не падать ниже и дать возможность выполнить пересборку
272
276
  return;
273
277
  }
@@ -313,6 +317,38 @@ export const serverRunner = ({
313
317
 
314
318
  await send(worker, 'script', { filename: realFilename, script });
315
319
  }
320
+ }
321
+
322
+ // отключаем watch за при выключенном rebuild
323
+ if (!configManager.noServerRebuild) {
324
+ const watchedFileName = 'env.development.js';
325
+ try {
326
+ const envPath = path.resolve(process.cwd(), watchedFileName);
327
+ const watchHandler = (_changedFilePath) => {
328
+ console.log(chalk.yellow(`${envPath} changed, restart server...`));
329
+ serverInvalidated = true;
330
+ runWorker();
331
+ };
332
+
333
+ const watcher = chokidar.watch(envPath);
334
+ watcher.on('change', watchHandler);
335
+
336
+ di.register({
337
+ provide: CLOSE_HANDLER_TOKEN,
338
+ multi: true,
339
+ useValue: () => {
340
+ watcher.close();
341
+ },
342
+ });
343
+ } catch (err) {
344
+ console.error(
345
+ `Something went wrong while watching for changes in ${watchedFileName}: ${err}`
346
+ );
347
+ }
348
+ }
349
+
350
+ compiler.hooks.done.tap(HOOK_NAME, async (stats) => {
351
+ runWorker(stats);
316
352
  });
317
353
  };
318
354
  };
package/src/cli/CLI.ts CHANGED
@@ -49,8 +49,7 @@ export class CLI {
49
49
  config: ConfigManager,
50
50
  analytics: Analytics,
51
51
  cliRootDir: string,
52
- cliPackageManager: PackageManager,
53
- packageManager: PackageManager
52
+ cliPackageManager: PackageManager
54
53
  ) {
55
54
  this.context = new Context(
56
55
  config,
@@ -58,7 +57,6 @@ export class CLI {
58
57
  analytics,
59
58
  cliRootDir,
60
59
  cliPackageManager,
61
- packageManager,
62
60
  this.runTask,
63
61
  this.runCommand,
64
62
  this.getTasks,
package/src/cli/index.ts CHANGED
@@ -77,7 +77,6 @@ export async function cliInitialized(cliArgs = process.argv) {
77
77
  });
78
78
 
79
79
  const configManager = new ConfigManager({ config, syncConfigFile: syncJsonFile });
80
- const packageManager = resolvePackageManager({ rootDir: process.cwd() });
81
80
 
82
81
  const cliRootDir = path.resolve(__dirname, '../', '../');
83
82
  const cliPackageManager = new NpmPackageManager({
@@ -91,8 +90,7 @@ export async function cliInitialized(cliArgs = process.argv) {
91
90
  configManager,
92
91
  analytic,
93
92
  cliRootDir,
94
- cliPackageManager,
95
- packageManager
93
+ cliPackageManager
96
94
  );
97
95
 
98
96
  return await cliInstance.run(cliArgs);
@@ -3,13 +3,18 @@ import type { CommandResult } from '../../models/command';
3
3
  import { migrate } from '../../utils/commands/dependencies/migrate';
4
4
  import { findTramvaiVersion } from '../../utils/commands/dependencies/findTramvaiVersion';
5
5
  import { checkVersions } from '../../utils/commands/dependencies/checkVersions';
6
+ import { getWorkspaceRootByAppName } from '../../utils/commands/dependencies/getWorkspaceRootByAppName';
6
7
 
7
8
  export type Params = {
8
- packageName: string;
9
+ packageNames: string | string[];
9
10
  dev?: boolean;
11
+ app?: string;
10
12
  };
11
13
 
12
- export default async (context: Context, { packageName, dev }: Params): Promise<CommandResult> => {
14
+ export default async (
15
+ context: Context,
16
+ { packageNames, dev, app }: Params
17
+ ): Promise<CommandResult> => {
13
18
  const version = await findTramvaiVersion();
14
19
 
15
20
  if (!version) {
@@ -18,10 +23,19 @@ export default async (context: Context, { packageName, dev }: Params): Promise<C
18
23
  );
19
24
  }
20
25
 
26
+ const workspace = app ? getWorkspaceRootByAppName(context, app) : undefined;
27
+
28
+ if (workspace && !context.packageManager.isWorkspaceExists(workspace)) {
29
+ throw new Error(
30
+ `Workspace ${workspace} for app ${app} is not exists. Check your package.json and tramvai.json`
31
+ );
32
+ }
33
+
21
34
  await context.packageManager.install({
22
- name: packageName,
35
+ packageNames,
23
36
  version,
24
37
  devDependency: dev,
38
+ workspace,
25
39
  stdio: 'inherit',
26
40
  });
27
41
 
@@ -1,8 +1,12 @@
1
1
  import latestVersion from 'latest-version';
2
2
  import type { Params } from './add';
3
3
 
4
- export const checkPackage = async (_, { packageName }: Params) => {
5
- await latestVersion(packageName);
4
+ export const checkPackage = async (_, { packageNames }: Params) => {
5
+ if (Array.isArray(packageNames)) {
6
+ await Promise.all(packageNames.map((packageName) => latestVersion(packageName)));
7
+ } else {
8
+ await latestVersion(packageNames);
9
+ }
6
10
 
7
11
  return {
8
12
  name: 'checkPackage',
@@ -7,13 +7,18 @@ class AddCommand extends CLICommand {
7
7
 
8
8
  description = 'Tramvai package install command';
9
9
 
10
- command = 'add <packageName>';
10
+ command = 'add <packageNames...>';
11
11
 
12
12
  options = [
13
13
  {
14
14
  name: '-D, --dev',
15
15
  description: 'Save package to devDependencies',
16
16
  },
17
+ {
18
+ name: '-a, --app',
19
+ value: '[app]',
20
+ description: 'Install package in app-specific workspace',
21
+ },
17
22
  ];
18
23
 
19
24
  alias = '';
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
3
  import inquirer from 'inquirer';
4
+ import { packageManagerFactory } from '@tinkoff/package-manager-wrapper';
4
5
  import type { Context } from '../../models/context';
5
6
  import type { CommandResult } from '../../models/command';
6
7
  import type { Params } from './typings';
@@ -64,41 +65,53 @@ export default async function createNew(context: Context, params: Params): Promi
64
65
  const templateDir = getPathToTemplate(type, template);
65
66
  const sharedDir = getPathToShared();
66
67
  const blockDir = getPathToBlock(type);
67
- const isNpm = packageManager === 'npm';
68
- const isYarn = packageManager === 'yarn';
69
- const isJest = testingFramework === 'jest';
70
-
68
+ const isMonorepo = template === 'monorepo';
69
+ const baseDir = type === 'app' ? 'apps' : 'child-apps';
71
70
  const blockDirectoryName = {
72
- monorepo: path.join(type === 'app' ? 'apps' : 'child-apps', name),
71
+ monorepo: path.join(baseDir, name),
73
72
  multirepo: 'src',
74
73
  }[template];
75
74
 
76
- await renderTemplate(templateDir, directoryName, { configEntry, isJest, isNpm, isYarn });
77
- await renderTemplate(sharedDir, directoryName, { configEntry, isJest, isNpm, isYarn });
78
- await renderTemplate(blockDir, path.join(directoryName, blockDirectoryName), {
75
+ const templateData = {
79
76
  configEntry,
80
- isJest,
81
- isNpm,
82
- isYarn,
83
- });
77
+ isJest: testingFramework === 'jest',
78
+ isNpm: packageManager === 'npm',
79
+ isYarn: packageManager === 'yarn',
80
+ workspaceBaseDir: isMonorepo ? baseDir : undefined,
81
+ };
82
+
83
+ await renderTemplate(templateDir, directoryName, templateData);
84
+ await renderTemplate(sharedDir, directoryName, templateData);
85
+ await renderTemplate(blockDir, path.join(directoryName, blockDirectoryName), templateData);
84
86
  if (template === 'monorepo') {
85
87
  const monorepoBlockDir = getPathToMonorepoBlock();
86
88
 
87
- await renderTemplate(monorepoBlockDir, path.join(directoryName, blockDirectoryName), {
88
- configEntry,
89
- isJest,
90
- });
89
+ await renderTemplate(
90
+ monorepoBlockDir,
91
+ path.join(directoryName, blockDirectoryName),
92
+ templateData
93
+ );
91
94
  }
92
95
 
93
96
  if (testingFramework !== 'none') {
94
- await renderTemplate(getPathToTestingFramework(type, testingFramework), directoryName, {
95
- configEntry,
96
- isJest,
97
- });
97
+ await renderTemplate(
98
+ getPathToTestingFramework(type, testingFramework),
99
+ directoryName,
100
+ templateData
101
+ );
98
102
  }
99
103
 
100
104
  await initializationGit(directoryName);
101
- await installDependencies({ localDir: directoryName, type, packageManager, testingFramework });
105
+ await installDependencies({
106
+ localDir: directoryName,
107
+ type,
108
+ packageManager: packageManagerFactory(
109
+ { rootDir: path.resolve(process.cwd(), directoryName) },
110
+ packageManager
111
+ ),
112
+ testingFramework,
113
+ workspace: isMonorepo ? blockDirectoryName : undefined,
114
+ });
102
115
 
103
116
  console.log(
104
117
  `\n\n Project ${name} has been successfully created. To run the project, enter in the terminal`,
@@ -1,7 +1,5 @@
1
- import type { Options } from 'execa';
2
- import execa from 'execa';
3
1
  import chalk from 'chalk';
4
- import type { PackageManagers } from '../questions/packageManager';
2
+ import type { InstallOptions, PackageManager } from '@tinkoff/package-manager-wrapper';
5
3
  import type { TestingFrameworks } from '../questions/testingFramework';
6
4
  import type { Type } from '../questions/type';
7
5
 
@@ -57,7 +55,6 @@ const devDependencies = [
57
55
  '@tinkoff/eslint-config',
58
56
  '@tinkoff/eslint-config-react',
59
57
  '@tinkoff/eslint-plugin-tramvai',
60
- '@tramvai/cli',
61
58
  '@types/react',
62
59
  'postcss-custom-media',
63
60
  'postcss-custom-properties',
@@ -69,56 +66,107 @@ const devDependencies = [
69
66
  'typescript',
70
67
  ];
71
68
 
72
- const packagesInstallCommands: Record<PackageManagers, { deps: string[]; devDeps: string[] }> = {
73
- npm: {
74
- deps: ['install', '--save', '--package-lock', '--legacy-peer-deps'],
75
- devDeps: ['install', '--save-dev', '--legacy-peer-deps'],
76
- },
77
- yarn: {
78
- deps: ['add'],
79
- devDeps: ['add', '--dev'],
80
- },
81
- pnpm: {
82
- deps: ['add'],
83
- devDeps: ['add', '--save-dev'],
84
- },
69
+ const rootDependencies = {
70
+ devDependencies: ['@tramvai/cli'],
71
+ dependencies: [],
85
72
  };
86
73
 
74
+ function getBaseDeps(type: Type, isDev: boolean) {
75
+ const depsMap = DEPS[type];
76
+ const jestDeps = depsMap.jestDevDependencies;
77
+ const baseDeps = isDev ? devDependencies : depsMap.dependencies;
78
+ const rootDeps = rootDependencies[isDev ? 'devDependencies' : 'dependencies'];
79
+
80
+ return { rootDeps, baseDeps, jestDeps };
81
+ }
82
+
83
+ function getDeps(type: Type, options?: { isDev?: boolean; isRoot?: boolean }) {
84
+ const { rootDeps, baseDeps } = getBaseDeps(type, options?.isDev);
85
+
86
+ return [...rootDeps, ...baseDeps];
87
+ }
88
+
89
+ function getDepsWorkspace(type: Type, options?: { isDev?: boolean; isRoot?: boolean }) {
90
+ const { rootDeps, baseDeps } = getBaseDeps(type, options?.isDev);
91
+
92
+ if (options?.isRoot) {
93
+ return rootDeps;
94
+ }
95
+
96
+ return baseDeps;
97
+ }
98
+
99
+ function jestDeps(type: Type) {
100
+ return getBaseDeps(type, true).jestDeps;
101
+ }
102
+
87
103
  export async function installDependencies({
88
104
  localDir,
89
105
  type,
90
106
  packageManager,
91
107
  testingFramework,
108
+ workspace,
92
109
  }: {
93
110
  localDir: string;
94
111
  type: Type;
95
- packageManager: PackageManagers;
112
+ packageManager: PackageManager;
96
113
  testingFramework: TestingFrameworks;
114
+ workspace?: string;
97
115
  }) {
98
- const installCommands = packagesInstallCommands[packageManager];
99
- const options: Options = {
116
+ const deps = workspace ? getDepsWorkspace : getDeps;
117
+
118
+ const options: InstallOptions = {
100
119
  cwd: localDir,
101
120
  env: {
102
121
  SKIP_TRAMVAI_MIGRATIONS: 'true',
103
122
  },
104
123
  stdio: 'inherit',
124
+ workspace,
105
125
  };
106
126
 
127
+ // Install cli and core packages into the root of repository if using workspaces for update command to work correctly
128
+ if (workspace !== undefined) {
129
+ console.log(`${chalk.blue('[DEPENDENCIES]')} Installing root dependencies`);
130
+
131
+ await packageManager.install({
132
+ packageNames: deps(type, { isDev: true, isRoot: true }),
133
+ devDependency: true,
134
+ ...options,
135
+ workspace: undefined,
136
+ });
137
+
138
+ console.log(`${chalk.blue('[DEPENDENCIES]')} Installing root dev dependencies`);
139
+
140
+ await packageManager.install({
141
+ packageNames: deps(type, { isDev: false, isRoot: true }),
142
+ devDependency: false,
143
+ ...options,
144
+ workspace: undefined,
145
+ });
146
+ }
147
+
107
148
  console.log(`${chalk.blue('[DEPENDENCIES]')} Installing app dependencies`);
108
149
 
109
- await execa(packageManager, [...installCommands.deps, ...DEPS[type].dependencies], options);
150
+ await packageManager.install({
151
+ packageNames: deps(type),
152
+ ...options,
153
+ });
110
154
 
111
155
  console.log(`${chalk.blue('[DEPENDENCIES]')} Installing dev dependencies`);
112
156
 
113
- await execa(packageManager, [...installCommands.devDeps, ...devDependencies], options);
157
+ await packageManager.install({
158
+ packageNames: deps(type, { isDev: true }),
159
+ devDependency: true,
160
+ ...options,
161
+ });
114
162
 
115
163
  if (testingFramework === 'jest') {
116
164
  console.log(`${chalk.blue('[DEPENDENCIES]')} Installing jest dependencies`);
117
165
 
118
- await execa(
119
- packageManager,
120
- [...installCommands.devDeps, ...DEPS[type].jestDevDependencies],
121
- options
122
- );
166
+ await packageManager.install({
167
+ packageNames: jestDeps(type),
168
+ devDependency: true,
169
+ ...options,
170
+ });
123
171
  }
124
172
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "{{configEntry.name}}",
3
+ "version": "0.0.0-stub",
4
+ "private": true
5
+ }
@@ -22,5 +22,6 @@
22
22
  "*.{js,jsx,ts,tsx}": [
23
23
  "eslint --fix"
24
24
  ]
25
- }
25
+ }{{#if workspaceBaseDir}},
26
+ "workspaces": ["{{workspaceBaseDir}}/*"]{{/if}}
26
27
  }