@tramvai/cli 2.112.0 → 2.117.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.
Files changed (98) hide show
  1. package/lib/api/benchmark/build.js +6 -9
  2. package/lib/api/benchmark/build.js.map +1 -1
  3. package/lib/api/benchmark/index.js +4 -0
  4. package/lib/api/benchmark/index.js.map +1 -1
  5. package/lib/api/benchmark/start.js +10 -10
  6. package/lib/api/benchmark/start.js.map +1 -1
  7. package/lib/api/benchmark/types.d.ts +2 -0
  8. package/lib/api/benchmark/utils/stats.d.ts +6 -1
  9. package/lib/api/benchmark/utils/stats.js +9 -1
  10. package/lib/api/benchmark/utils/stats.js.map +1 -1
  11. package/lib/api/build/index.d.ts +1 -0
  12. package/lib/api/build/index.js.map +1 -1
  13. package/lib/api/start/index.d.ts +1 -0
  14. package/lib/api/start/index.js.map +1 -1
  15. package/lib/builder/webpack/providers/shared.js +12 -3
  16. package/lib/builder/webpack/providers/shared.js.map +1 -1
  17. package/lib/builder/webpack/utils/maxMemoryRss.d.ts +1 -0
  18. package/lib/builder/webpack/utils/maxMemoryRss.js +15 -0
  19. package/lib/builder/webpack/utils/maxMemoryRss.js.map +1 -0
  20. package/lib/builder/webpack/utils/runWebpack.js +6 -0
  21. package/lib/builder/webpack/utils/runWebpack.js.map +1 -1
  22. package/lib/commands/benchmark/benchmark.js +15 -7
  23. package/lib/commands/benchmark/benchmark.js.map +1 -1
  24. package/lib/config/constants.js +1 -1
  25. package/lib/config/constants.js.map +1 -1
  26. package/lib/di/providers/builder.js +10 -0
  27. package/lib/di/providers/builder.js.map +1 -1
  28. package/lib/di/tokens/builder.d.ts +5 -0
  29. package/lib/di/tokens/builder.js +2 -1
  30. package/lib/di/tokens/builder.js.map +1 -1
  31. package/lib/library/babel/plugins/fill-action-name.js +1 -2
  32. package/lib/library/babel/plugins/fill-action-name.js.map +1 -1
  33. package/lib/library/swc/index.d.ts +2 -2
  34. package/lib/library/swc/index.js +2 -4
  35. package/lib/library/swc/index.js.map +1 -1
  36. package/lib/library/webpack/blocks/js.js +25 -47
  37. package/lib/library/webpack/blocks/js.js.map +1 -1
  38. package/lib/library/webpack/blocks/pagesResolve.js.map +1 -1
  39. package/lib/library/webpack/blocks/pwa/client.js +1 -1
  40. package/lib/library/webpack/blocks/pwa/client.js.map +1 -1
  41. package/lib/library/webpack/blocks/serverInline.d.ts +1 -1
  42. package/lib/library/webpack/blocks/serverInline.js +8 -12
  43. package/lib/library/webpack/blocks/serverInline.js.map +1 -1
  44. package/lib/library/webpack/blocks/ts.d.ts +1 -1
  45. package/lib/library/webpack/blocks/ts.js +6 -6
  46. package/lib/library/webpack/blocks/ts.js.map +1 -1
  47. package/lib/library/webpack/loaders/pagesResolve.d.ts +2 -2
  48. package/lib/library/webpack/loaders/pagesResolve.js +9 -3
  49. package/lib/library/webpack/loaders/pagesResolve.js.map +1 -1
  50. package/lib/library/webpack/plugins/ModuleFederationFixRange.d.ts +1 -1
  51. package/lib/library/webpack/plugins/ModuleFederationFixRange.js +70 -68
  52. package/lib/library/webpack/plugins/ModuleFederationFixRange.js.map +1 -1
  53. package/lib/library/webpack/plugins/WebpackBar/utils/log-update.js +7 -1
  54. package/lib/library/webpack/plugins/WebpackBar/utils/log-update.js.map +1 -1
  55. package/lib/library/webpack/utils/files.js +4 -2
  56. package/lib/library/webpack/utils/files.js.map +1 -1
  57. package/lib/library/webpack/utils/transpiler.d.ts +1 -1
  58. package/lib/library/webpack/utils/transpiler.js +1 -1
  59. package/lib/library/webpack/utils/transpiler.js.map +1 -1
  60. package/lib/schema/autogeneratedSchema.json +4 -0
  61. package/lib/typings/build/Builder.d.ts +1 -0
  62. package/lib/typings/configEntry/application.d.ts +4 -0
  63. package/package.json +9 -7
  64. package/schema.json +4 -0
  65. package/src/api/benchmark/build.ts +4 -14
  66. package/src/api/benchmark/index.ts +9 -1
  67. package/src/api/benchmark/start.ts +7 -14
  68. package/src/api/benchmark/types.ts +6 -1
  69. package/src/api/benchmark/utils/stats.ts +17 -1
  70. package/src/api/build/index.ts +1 -0
  71. package/src/api/start/__integration__/start.test.ts +2 -2
  72. package/src/api/start/index.ts +1 -0
  73. package/src/builder/webpack/providers/shared.ts +15 -4
  74. package/src/builder/webpack/utils/maxMemoryRss.ts +12 -0
  75. package/src/builder/webpack/utils/runWebpack.ts +9 -0
  76. package/src/commands/benchmark/benchmark.ts +17 -7
  77. package/src/config/constants.ts +1 -1
  78. package/src/di/providers/builder.ts +10 -0
  79. package/src/di/tokens/builder.ts +2 -0
  80. package/src/library/babel/plugins/fill-action-name.ts +1 -2
  81. package/src/library/swc/__integration__/__snapshots__/swc.build.test.ts.snap +7 -24
  82. package/src/library/swc/__integration__/__snapshots__/swc.start.test.ts.snap +11 -23
  83. package/src/library/swc/index.ts +4 -6
  84. package/src/library/webpack/blocks/js.ts +27 -51
  85. package/src/library/webpack/blocks/pagesResolve.ts +2 -1
  86. package/src/library/webpack/blocks/pwa/client.ts +1 -1
  87. package/src/library/webpack/blocks/serverInline.ts +14 -12
  88. package/src/library/webpack/blocks/ts.ts +6 -7
  89. package/src/library/webpack/loaders/pagesResolve.ts +12 -4
  90. package/src/library/webpack/plugins/ModuleFederationFixRange.ts +92 -87
  91. package/src/library/webpack/plugins/WebpackBar/utils/log-update.ts +7 -1
  92. package/src/library/webpack/utils/files.ts +9 -6
  93. package/src/library/webpack/utils/transpiler.ts +16 -18
  94. package/src/schema/autogeneratedSchema.json +4 -0
  95. package/src/typings/build/Builder.ts +1 -0
  96. package/src/typings/configEntry/application.ts +4 -0
  97. /package/src/api/start/__integration__/__fixtures__/app/routes/{about.tsx → about/index.tsx} +0 -0
  98. /package/src/api/start/__integration__/__fixtures__/app/routes/{home.tsx → home/index.tsx} +0 -0
@@ -5,7 +5,7 @@ import browserslist from 'browserslist';
5
5
  import envTargets from '@tinkoff/browserslist-config';
6
6
  import { sync as resolve } from 'resolve';
7
7
  import findCacheDir from 'find-cache-dir';
8
- import type { Config } from '@swc/core';
8
+ import type { Options as SwcOptions } from '@swc/core';
9
9
  import type { TranspilerConfig } from '../webpack/utils/transpiler';
10
10
 
11
11
  const TRAMVAI_SWC_TARGET_PATH = '@tramvai/swc-integration/target/wasm32-wasi';
@@ -13,7 +13,7 @@ const TRAMVAI_SWC_TARGET_PATH = '@tramvai/swc-integration/target/wasm32-wasi';
13
13
  const NOT_SUPPORTED_FIELDS = ['alias', 'generateDataQaTag', 'enableFillActionNamePlugin'];
14
14
  let warningWasShown = false;
15
15
 
16
- export const getSwcOptions = (config: TranspilerConfig): Config => {
16
+ export const getSwcOptions = (config: TranspilerConfig): SwcOptions => {
17
17
  const {
18
18
  env = 'development',
19
19
  target,
@@ -109,9 +109,9 @@ Having swc config may conflict with @tramvai/cli configuration`
109
109
  module: {
110
110
  type: modules || 'es6',
111
111
  },
112
+ isModule: 'unknown',
112
113
  jsc: {
113
- // TODO: should trim output size, but doesn't work well with some libs
114
- // externalHelpers: true,
114
+ externalHelpers: true,
115
115
  parser: {
116
116
  syntax: typescript ? 'typescript' : 'ecmascript',
117
117
  decorators: true,
@@ -129,8 +129,6 @@ Having swc config may conflict with @tramvai/cli configuration`
129
129
  globals: {
130
130
  // let the webpack replace NODE_ENV as replacement with swc may mess up with tests
131
131
  envs: [],
132
- // @ts-ignore
133
- // TODO: there is not typings for typeofs, but the field is mentioned in docs
134
132
  typeofs: removeTypeofWindow
135
133
  ? {
136
134
  window: isServer ? 'undefined' : 'object',
@@ -2,7 +2,6 @@ import type Config from 'webpack-chain';
2
2
  import { modernLibsFilter } from '@tinkoff/is-modern-lib';
3
3
  import { createWorkerPoolTranspiler } from '../utils/workersPool';
4
4
  import type { ConfigManager } from '../../../config/configManager';
5
- import type { TranspilerConfig } from '../utils/transpiler';
6
5
  import { getTranspilerConfig, addTranspilerLoader } from '../utils/transpiler';
7
6
  import type { CliConfigEntry } from '../../../typings/configEntry/cli';
8
7
 
@@ -10,56 +9,33 @@ import type { CliConfigEntry } from '../../../typings/configEntry/cli';
10
9
  export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
11
10
  const { transpileOnlyModernLibs } = configManager;
12
11
 
13
- const jsRule = (transpilerConfig: TranspilerConfig) => (rule: Config.Rule) => {
14
- const cfg = rule
15
- .test(/\.[cm]?js[x]?$/)
16
- .oneOf('default')
17
- // TODO разобраться почему на винде все плохо с thread-loader
18
- .when(process.platform !== 'win32' && !configManager.debug, (cfg) =>
19
- cfg
20
- .use('thread')
21
- .loader('thread-loader')
22
- .options(createWorkerPoolTranspiler(configManager))
23
- .end()
24
- )
25
- .use('transpiler');
12
+ const rule = config.module
13
+ .rule('js')
14
+ .test(/\.[cm]?js[x]?$/)
15
+ // TODO: разобраться почему на винде все плохо с thread-loader
16
+ .when(process.platform !== 'win32' && !configManager.debug, (cfg) =>
17
+ cfg
18
+ .use('thread')
19
+ .loader('thread-loader')
20
+ .options(createWorkerPoolTranspiler(configManager))
21
+ .end()
22
+ );
26
23
 
27
- return addTranspilerLoader(configManager, cfg, transpilerConfig);
28
- };
24
+ rule
25
+ .oneOf('project')
26
+ .exclude.add(/node_modules/)
27
+ .end()
28
+ .use('transpiler')
29
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)));
29
30
 
30
- if (transpileOnlyModernLibs) {
31
- config.module
32
- .rule('js:project')
33
- .exclude.add(/node_modules/)
34
- .end()
35
- .batch(jsRule(getTranspilerConfig(configManager)));
36
-
37
- config.module
38
- .rule('js:node_modules')
39
- .include.add(modernLibsFilter)
40
- .end()
41
- .batch(jsRule(getTranspilerConfig(configManager, { hot: false })))
42
- .merge({
43
- // TODO: некоторые пакеты неправильно описывают импорты для es модулей
44
- // https://github.com/babel/babel/issues/12058
45
- resolve: { fullySpecified: false },
46
- });
47
- } else {
48
- config.module
49
- .rule('js:project')
50
- .exclude.add(/node_modules/)
51
- .end()
52
- .batch(jsRule(getTranspilerConfig(configManager)));
53
-
54
- config.module
55
- .rule('js:node_modules')
56
- .include.add(/node_modules/)
57
- .end()
58
- .batch(jsRule(getTranspilerConfig(configManager, { hot: false })))
59
- .merge({
60
- // TODO: некоторые пакеты неправильно описывают импорты для es модулей
61
- // https://github.com/babel/babel/issues/12058
62
- resolve: { fullySpecified: false },
63
- });
64
- }
31
+ rule
32
+ .oneOf('node_module')
33
+ .when(transpileOnlyModernLibs, (cfg) => cfg.include.add(modernLibsFilter))
34
+ .merge({
35
+ // true value forces to use file extensions for importing mjs modules
36
+ // but we want to use mjs if it exists anyway
37
+ resolve: { fullySpecified: false },
38
+ })
39
+ .use('transpiler')
40
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager, { hot: false })));
65
41
  };
@@ -1,5 +1,6 @@
1
1
  import path from 'path';
2
2
  import type Config from 'webpack-chain';
3
+ import type { PagesResolveOptions } from '../loaders/pagesResolve';
3
4
  import type { ConfigManager } from '../../../config/configManager';
4
5
  import type { ApplicationConfigEntry } from '../../../typings/configEntry/application';
5
6
 
@@ -20,7 +21,7 @@ export const pagesResolve =
20
21
  rootDir: configManager.rootDir,
21
22
  root: configManager.root,
22
23
  extensions: config.resolve.extensions.values(),
23
- })
24
+ } as PagesResolveOptions)
24
25
  .end()
25
26
  // babel-loader is required to process this file
26
27
  .enforce('pre');
@@ -26,7 +26,7 @@ export const pwaBlock =
26
26
  config.batch(pwaSharedBlock(configManager));
27
27
 
28
28
  if (
29
- !safeRequireResolve('@tramvai/module-progressive-web-app') &&
29
+ !safeRequireResolve('@tramvai/module-progressive-web-app', true) &&
30
30
  (pwa.workbox?.enabled || pwa.webmanifest?.enabled)
31
31
  ) {
32
32
  throw Error('PWA functional requires @tramvai/module-progressive-web-app installed');
@@ -1,25 +1,27 @@
1
- import Config from 'webpack-chain';
1
+ import type Config from 'webpack-chain';
2
2
  import type { ConfigManager } from '../../../config/configManager';
3
3
  import type { CliConfigEntry } from '../../../typings/configEntry/cli';
4
- import js from './js';
5
- import ts from './ts';
4
+ import { addTranspilerLoader, getTranspilerConfig } from '../utils/transpiler';
6
5
 
7
6
  export const serverInline = (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
8
7
  // создаём клиентский конфиг и отключаем modern режим
9
8
  const clientConfigManager = configManager.withSettings({ buildType: 'client', modern: false });
10
9
 
11
- const clientConfig = new Config().batch(js(clientConfigManager)).batch(ts(clientConfigManager));
12
-
13
- const addInlineHandler = (type: string, extension: string) => {
10
+ const addInlineHandler = (type: string) => {
14
11
  config.module
15
12
  .rule(type)
16
13
  .oneOf('inline')
17
- .before('default')
18
- .test(new RegExp(`\\.inline(\\.es)?\\.${extension}$`))
19
- .uses.merge(clientConfig.module.rule(type).oneOfs.get('default').uses.entries());
14
+ .before('project')
15
+ .test(new RegExp(`\\.inline(\\.es)?\\.${type}$`))
16
+ .use('transpiler')
17
+ .batch(
18
+ addTranspilerLoader(
19
+ clientConfigManager,
20
+ getTranspilerConfig(clientConfigManager, { typescript: type === 'ts' })
21
+ )
22
+ );
20
23
  };
21
24
 
22
- addInlineHandler('js:project', 'js');
23
- addInlineHandler('js:node_modules', 'js');
24
- addInlineHandler('ts:project', 'ts');
25
+ addInlineHandler('js');
26
+ addInlineHandler('ts');
25
27
  };
@@ -7,13 +7,12 @@ import type { CliConfigEntry } from '../../../typings/configEntry/cli';
7
7
  export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
8
8
  const transpilerConfig = getTranspilerConfig(configManager, { typescript: true });
9
9
 
10
- const cfg = config.module
11
- .rule('ts:project')
10
+ config.module
11
+ .rule('ts')
12
12
  .test(/\.ts[x]?$/)
13
13
  .exclude.add(/node_modules/)
14
14
  .end()
15
- .oneOf('default')
16
- // TODO разобраться почему на винде все плохо с thread-loader
15
+ // TODO: разобраться почему на винде все плохо с thread-loader
17
16
  .when(process.platform !== 'win32', (cfg) =>
18
17
  cfg
19
18
  .use('thread')
@@ -21,7 +20,7 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
21
20
  .options(createWorkerPoolTranspiler(configManager))
22
21
  .end()
23
22
  )
24
- .use('transpiler');
25
-
26
- return addTranspilerLoader(configManager, cfg, transpilerConfig);
23
+ .oneOf('project')
24
+ .use('transpiler')
25
+ .batch(addTranspilerLoader(configManager, transpilerConfig));
27
26
  };
@@ -8,7 +8,7 @@ const LAYOUT_FILENAME = '_layout.tsx';
8
8
  const ERROR_BOUNDARY_FILENAME = '_error.tsx';
9
9
  const WILDCARD_TOKEN = '[...';
10
10
 
11
- interface Options {
11
+ export interface PagesResolveOptions {
12
12
  rootDir: string;
13
13
  root: string;
14
14
  extensions: string[];
@@ -22,8 +22,9 @@ const removeExtension = (filename: string): string => {
22
22
  };
23
23
 
24
24
  // eslint-disable-next-line func-style
25
- const pagesResolve: LoaderDefinitionFunction<Options> = function () {
25
+ const pagesResolve: LoaderDefinitionFunction<PagesResolveOptions> = function () {
26
26
  const { fileSystemPages, rootDir, root, extensions } = this.getOptions();
27
+ const extensionsRegexp = new RegExp(`\\.(${extensions.map((ext) => ext.slice(1)).join('|')})$`);
27
28
  const fsLayouts: string[] = [];
28
29
  const fsErrorBoundaries: string[] = [];
29
30
  const fsWildcards: string[] = [];
@@ -34,9 +35,11 @@ const pagesResolve: LoaderDefinitionFunction<Options> = function () {
34
35
  const filesToPages = ({
35
36
  pagesRootDirectory,
36
37
  isRoutes = false,
38
+ test,
37
39
  }: {
38
40
  pagesRootDirectory: string;
39
41
  isRoutes?: boolean;
42
+ test: RegExp;
40
43
  }) => {
41
44
  const pagesDir = path.resolve(rootDir, root, pagesRootDirectory);
42
45
 
@@ -51,9 +54,10 @@ const pagesResolve: LoaderDefinitionFunction<Options> = function () {
51
54
 
52
55
  for (const file of pagesFiles) {
53
56
  const extname = path.extname(file);
54
- const name = file.replace(new RegExp(`\\${extname}$`), '').replace(/\\/g, '/');
57
+ const normalizedFile = file.replace(/\\/g, '/');
55
58
 
56
- if (extensions.indexOf(extname) !== -1) {
59
+ if (test.test(normalizedFile)) {
60
+ const name = normalizedFile.replace(new RegExp(`\\${extname}$`), '');
57
61
  const pageComponentName = `@/${pagesRootDirectory}/${name}`;
58
62
  const pageComponentPath = path.resolve(pagesDir, name).replace(/\\/g, '\\\\');
59
63
  const chunkname = pageComponentName.replace(/\//g, '_');
@@ -112,11 +116,15 @@ const pagesResolve: LoaderDefinitionFunction<Options> = function () {
112
116
  ? filesToPages({
113
117
  pagesRootDirectory: fileSystemPages.routesDir,
114
118
  isRoutes: true,
119
+ test: new RegExp(`index${extensionsRegexp.source}`),
115
120
  })
116
121
  : [];
117
122
  const fsPages = fileSystemPages.pagesDir
118
123
  ? filesToPages({
119
124
  pagesRootDirectory: fileSystemPages.pagesDir,
125
+ test: fileSystemPages.componentsPattern
126
+ ? new RegExp(fileSystemPages.componentsPattern)
127
+ : extensionsRegexp,
120
128
  })
121
129
  : [];
122
130
 
@@ -1,10 +1,8 @@
1
1
  import type webpack from 'webpack';
2
2
  import type { Compiler, NormalModule } from 'webpack';
3
- import { sync as resolve } from 'resolve';
4
- import type packageJson from 'package-json';
5
- import chalk from 'chalk';
3
+ import { WebpackError } from 'webpack';
6
4
  // eslint-disable-next-line no-restricted-imports
7
- import { parseRange } from 'webpack/lib/util/semver';
5
+ import { parseRange, satisfy } from 'webpack/lib/util/semver';
8
6
  import { isDependantLib, isUnifiedVersion } from '../../../utils/tramvaiVersions';
9
7
 
10
8
  const PLUGIN_NAME = 'ModuleFederationValidateDuplicates';
@@ -22,19 +20,6 @@ interface SharedModule extends NormalModule {
22
20
  options?: SharedModuleOptions;
23
21
  }
24
22
 
25
- const resolvePackageVersion = (name: string, basedir: string) => {
26
- try {
27
- const packageJsonPath = resolve(`${name}/package.json`, {
28
- basedir,
29
- });
30
- const packageJson: packageJson.FullMetadataOptions = require(packageJsonPath);
31
-
32
- return packageJson.version;
33
- } catch (error: any) {
34
- console.warn(`ModuleFederation: could not infer version for package "${name}"`);
35
- }
36
- };
37
-
38
23
  export interface ModuleFederationFixRangeOptions {
39
24
  flexibleTramvaiVersions: boolean;
40
25
  }
@@ -42,14 +27,14 @@ export interface ModuleFederationFixRangeOptions {
42
27
  export class ModuleFederationFixRange implements webpack.WebpackPluginInstance {
43
28
  private flexibleTramvaiVersions: boolean;
44
29
  // { name: { importResolved: { number }} }
45
- private sharedModules: Map<string, Map<string, Set<number>>> = new Map();
30
+ private sharedModules: Map<string, Map<string, Set<SharedModule>>> = new Map();
46
31
 
47
32
  constructor({ flexibleTramvaiVersions }: ModuleFederationFixRangeOptions) {
48
33
  this.flexibleTramvaiVersions = flexibleTramvaiVersions ?? false;
49
34
  }
50
35
 
51
36
  apply(compiler: Compiler) {
52
- compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (_, { normalModuleFactory }) => {
37
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation, { normalModuleFactory }) => {
53
38
  normalModuleFactory.hooks.factorize.intercept({
54
39
  register: (tap) => {
55
40
  if (tap.name === 'ConsumeSharedPlugin') {
@@ -59,7 +44,24 @@ export class ModuleFederationFixRange implements webpack.WebpackPluginInstance {
59
44
  const module: SharedModule | undefined = await originalFn(...args);
60
45
 
61
46
  if (module?.options) {
62
- this.fixVersionRange(module);
47
+ const { shareKey: name, importResolved } = module.options;
48
+
49
+ let shared = this.sharedModules.get(name);
50
+
51
+ if (!shared) {
52
+ shared = new Map();
53
+ this.sharedModules.set(name, shared);
54
+ }
55
+
56
+ let modules = shared.get(importResolved);
57
+
58
+ if (!modules) {
59
+ modules = new Set();
60
+ shared.set(importResolved, modules);
61
+ }
62
+
63
+ // save major version of the semver array
64
+ modules.add(module);
63
65
  }
64
66
 
65
67
  return module;
@@ -69,71 +71,91 @@ export class ModuleFederationFixRange implements webpack.WebpackPluginInstance {
69
71
  return tap;
70
72
  },
71
73
  });
72
- });
73
-
74
- compiler.hooks.done.tap(PLUGIN_NAME, () => {
75
- const duplicates: Array<{ name: string; paths: string[] }> = [];
76
- const criticalDuplicates: Array<{ name: string; path: string }> = [];
77
74
 
78
- for (const [name, shared] of this.sharedModules) {
79
- if (shared.size > 1) {
80
- duplicates.push({
81
- name,
82
- paths: [...shared.keys()],
83
- });
84
- }
85
-
86
- for (const [path, versions] of shared) {
87
- if (versions.size > 1) {
88
- criticalDuplicates.push({ name, path });
75
+ // eslint-disable-next-line max-statements
76
+ compilation.hooks.optimizeDependencies.tap(PLUGIN_NAME, () => {
77
+ for (const [name, sharedModulesByName] of this.sharedModules.entries()) {
78
+ const hasDuplicates = sharedModulesByName.size > 1;
79
+
80
+ for (const [importResolved, sharedModulesByPath] of sharedModulesByName.entries()) {
81
+ if (hasDuplicates) {
82
+ const error = new WebpackError(
83
+ `This file is a duplicate for a module "${name}" that resolved to different path`
84
+ );
85
+ error.file = importResolved;
86
+ compilation.warnings.push(error);
87
+ }
88
+
89
+ let validModule: SharedModule;
90
+ let validVersion: string;
91
+ const invalidModules = new Set<SharedModule>();
92
+
93
+ for (const sharedModule of sharedModulesByPath) {
94
+ const connections = compilation.moduleGraph.getOutgoingConnections(sharedModule);
95
+
96
+ for (const { module } of connections) {
97
+ const resolvedVersion = module.resourceResolveData?.descriptionFileData?.version;
98
+ this.fixVersionRange(sharedModule, resolvedVersion);
99
+
100
+ const requiredVersion = sharedModule?.options?.requiredVersion;
101
+
102
+ if (requiredVersion && resolvedVersion) {
103
+ if (satisfy(requiredVersion, resolvedVersion)) {
104
+ validModule = sharedModule;
105
+ validVersion = resolvedVersion;
106
+ } else {
107
+ invalidModules.add(sharedModule);
108
+ }
109
+ }
110
+ // there should by only one outgoing module for ConsumeSharedModule
111
+ break;
112
+ }
113
+ }
114
+
115
+ if (invalidModules.size > 0 && validModule) {
116
+ for (const sharedModule of invalidModules) {
117
+ const error = new WebpackError(
118
+ `Shared module has been actually resolved to ${validVersion} instead of the expected`
119
+ );
120
+ error.module = sharedModule;
121
+ compilation.warnings.push(error);
122
+
123
+ // replace invalid module with valid version (invalid anyway are resolved to wrong version)
124
+ // to prevent any issues with shared dependencies
125
+ compilation.moduleGraph.moveModuleConnections(
126
+ sharedModule,
127
+ validModule,
128
+ (connection) => {
129
+ // ignore any outgoing connections as we want to ignore that module entirely and all its dependencies
130
+ return connection.originModule !== sharedModule;
131
+ }
132
+ );
133
+ }
134
+ }
89
135
  }
90
136
  }
91
- }
92
-
93
- // reset sharedModules info after compilation has ended
94
- this.sharedModules = new Map();
95
-
96
- if (duplicates.length) {
97
- console.warn(`⚠️ ModuleFederation: Found duplicates for next shared modules:
98
- ${duplicates
99
- .map(({ name, paths }) => {
100
- return `\t${chalk.yellowBright(name)}: ${paths.join(', ')}`;
101
- })
102
- .join('\n')}
103
- `);
104
- }
105
137
 
106
- if (criticalDuplicates.length) {
107
- console.error(
108
- `❗ ModuleFederation: Found duplicates for shared modules with different major versions that are resolved to the same path:
109
- ${criticalDuplicates
110
- .map(({ name, path }) => {
111
- return `\t${chalk.red(name)}: ${path}`;
112
- })
113
- .join('\n')}`
114
- );
115
-
116
- throw new Error(
117
- 'ModuleFederation: Different major versions have resolved to the same path for shared modules, please review errors above'
118
- );
119
- }
138
+ // reset sharedModules info after validation
139
+ this.sharedModules = new Map();
140
+ });
120
141
  });
121
142
  }
122
143
 
123
- fixVersionRange(module: SharedModule) {
144
+ fixVersionRange(sharedModule: SharedModule, resolvedVersion?: string) {
124
145
  const {
125
146
  options,
126
- options: { shareKey: name, singleton, importResolved },
147
+ options: { shareKey: name, singleton },
127
148
  context,
128
- } = module;
149
+ } = sharedModule;
129
150
  let { requiredVersion } = options;
130
151
 
152
+ // if version was not resolved automatically then get the version
153
+ // from actual module
131
154
  // ignore singletons as the actual version won't change anything
132
155
  // and usually it is just a react and co libraries
133
156
  if (!requiredVersion && context && !singleton) {
134
- const version = resolvePackageVersion(name, context);
135
- if (version) {
136
- requiredVersion = parseRange(version);
157
+ if (resolvedVersion) {
158
+ requiredVersion = parseRange(resolvedVersion);
137
159
  }
138
160
  }
139
161
 
@@ -153,22 +175,5 @@ ${criticalDuplicates
153
175
 
154
176
  // change version in webpack module
155
177
  options.requiredVersion = requiredVersion;
156
-
157
- let shared = this.sharedModules.get(name);
158
-
159
- if (!shared) {
160
- shared = new Map();
161
- this.sharedModules.set(name, shared);
162
- }
163
-
164
- let versions = shared.get(importResolved);
165
-
166
- if (!versions) {
167
- versions = new Set();
168
- shared.set(importResolved, versions);
169
- }
170
-
171
- // save major version of the semver array
172
- versions.add(requiredVersion[1]);
173
178
  }
174
179
  }
@@ -69,6 +69,7 @@ export default class LogUpdate {
69
69
  }
70
70
 
71
71
  this._onData(data);
72
+ return stream.write[originalWrite].call(stream, data, ...args);
72
73
  };
73
74
 
74
75
  // Backup original write fn
@@ -93,7 +94,12 @@ export default class LogUpdate {
93
94
  }
94
95
 
95
96
  _onData(data) {
96
- this.extraLines += data;
97
+ const str = String(data);
98
+ const lines = str.split('\n').length - 1;
99
+ if (lines > 0) {
100
+ this.prevLineCount += lines;
101
+ this.extraLines += data;
102
+ }
97
103
  }
98
104
 
99
105
  render(lines) {
@@ -14,12 +14,15 @@ export const addSvgrLoader = (
14
14
  .rule('svgr')
15
15
  .test(/\.svg$/)
16
16
  // @todo: `issuer: /\.tsx?$/` нужен или нет?
17
- .set('resourceQuery', /react/);
18
-
19
- addTranspilerLoader(configManager, svgrConfig.use('svgr-transpiler'), {
20
- ...transpilerConfig,
21
- typescript: true,
22
- });
17
+ .set('resourceQuery', /react/)
18
+ .use('svgr-transpiler')
19
+ .batch(
20
+ addTranspilerLoader(configManager, {
21
+ ...transpilerConfig,
22
+ typescript: true,
23
+ })
24
+ )
25
+ .end();
23
26
 
24
27
  svgrConfig.use('svgr').loader('@svgr/webpack').options({ babel: false, svgo: svgoOptions }).end();
25
28
  };
@@ -25,29 +25,27 @@ export type TranspilerConfig = {
25
25
  rootDir: string;
26
26
  };
27
27
 
28
- export const addTranspilerLoader = (
29
- configManager: ConfigManager<CliConfigEntry>,
30
- rule: Config.Use,
31
- transpilerConfig: TranspilerConfig
32
- ) => {
33
- const { loader } = configManager.experiments.transpilation;
28
+ export const addTranspilerLoader =
29
+ (configManager: ConfigManager<CliConfigEntry>, transpilerConfig: TranspilerConfig) =>
30
+ (rule: Config.Use) => {
31
+ const { loader } = configManager.experiments.transpilation;
34
32
 
35
- if (loader === 'swc') {
36
- try {
37
- resolve('@tramvai/swc-integration/package.json', { basedir: configManager.rootDir });
38
- } catch (error) {
39
- throw new Error(`You are using swc loader for the transpilation, but required module is not installed.
33
+ if (loader === 'swc') {
34
+ try {
35
+ resolve('@tramvai/swc-integration/package.json', { basedir: configManager.rootDir });
36
+ } catch (error) {
37
+ throw new Error(`You are using swc loader for the transpilation, but required module is not installed.
40
38
  Please run "npx tramvai add --dev @tramvai/swc-integration" to fix the problem
41
39
  `);
42
- }
40
+ }
43
41
 
44
- return rule.loader('swc-loader').options(getSwcOptions(transpilerConfig)).end();
45
- }
42
+ return rule.loader('swc-loader').options(getSwcOptions(transpilerConfig)).end();
43
+ }
46
44
 
47
- if (loader === 'babel') {
48
- return rule.loader('babel-loader').options(babelConfig(transpilerConfig)).end();
49
- }
50
- };
45
+ if (loader === 'babel') {
46
+ return rule.loader('babel-loader').options(babelConfig(transpilerConfig)).end();
47
+ }
48
+ };
51
49
 
52
50
  export const getTranspilerConfig = (
53
51
  configManager: ConfigManager<CliConfigEntry>,
@@ -96,6 +96,10 @@
96
96
  "type": "string"
97
97
  }
98
98
  ]
99
+ },
100
+ "componentsPattern": {
101
+ "title": "Test Regexp to add only files with specific name to list of FS Components",
102
+ "type": "string"
99
103
  }
100
104
  },
101
105
  "additionalProperties": false
@@ -41,6 +41,7 @@ export type GetBuildStats = () => {
41
41
  clientBuildTime?: number;
42
42
  clientModernBuildTime?: number;
43
43
  serverBuildTime?: number;
44
+ maxMemoryRss?: number;
44
45
  };
45
46
 
46
47
  export type BuildType = 'client' | 'server' | 'clientModern';
@@ -158,6 +158,10 @@ export interface ApplicationConfigEntry extends CliConfigEntry {
158
158
  * @default "pages"
159
159
  */
160
160
  pagesDir: string | false;
161
+ /**
162
+ * @title Test Regexp to add only files with specific name to list of FS Components
163
+ */
164
+ componentsPattern: string;
161
165
  };
162
166
  /**
163
167
  * @title Configure the options on webpack splitChunks