@tramvai/cli 6.68.6 → 6.77.3

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 (127) 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/index.js +1 -1
  9. package/lib/api/index.js.map +1 -1
  10. package/lib/api/start/application.experimental.d.ts.map +1 -1
  11. package/lib/api/start/application.experimental.js +13 -0
  12. package/lib/api/start/application.experimental.js.map +1 -1
  13. package/lib/api/start/index.d.ts +1 -0
  14. package/lib/api/start/index.d.ts.map +1 -1
  15. package/lib/api/start/index.js.map +1 -1
  16. package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts +2 -2
  17. package/lib/builder/webpack/analyzePlugins/rsdoctor.d.ts.map +1 -1
  18. package/lib/builder/webpack/analyzePlugins/rsdoctor.js +4 -2
  19. package/lib/builder/webpack/analyzePlugins/rsdoctor.js.map +1 -1
  20. package/lib/builder/webpack/index.d.ts.map +1 -1
  21. package/lib/builder/webpack/index.js +14 -12
  22. package/lib/builder/webpack/index.js.map +1 -1
  23. package/lib/builder/webpack/providers/build/client.d.ts.map +1 -1
  24. package/lib/builder/webpack/providers/build/client.js +3 -2
  25. package/lib/builder/webpack/providers/build/client.js.map +1 -1
  26. package/lib/builder/webpack/providers/build/server.d.ts.map +1 -1
  27. package/lib/builder/webpack/providers/build/server.js +3 -2
  28. package/lib/builder/webpack/providers/build/server.js.map +1 -1
  29. package/lib/builder/webpack/providers/shared.d.ts.map +1 -1
  30. package/lib/builder/webpack/providers/shared.js +31 -1
  31. package/lib/builder/webpack/providers/shared.js.map +1 -1
  32. package/lib/builder/webpack/providers/start/shared.d.ts.map +1 -1
  33. package/lib/builder/webpack/providers/start/shared.js +6 -2
  34. package/lib/builder/webpack/providers/start/shared.js.map +1 -1
  35. package/lib/builder/webpack/tokens.d.ts +4 -0
  36. package/lib/builder/webpack/tokens.d.ts.map +1 -1
  37. package/lib/cli/index.d.ts.map +1 -1
  38. package/lib/cli/index.js +12 -12
  39. package/lib/cli/index.js.map +1 -1
  40. package/lib/commands/analyze/command.d.ts +1 -1
  41. package/lib/commands/analyze/command.d.ts.map +1 -1
  42. package/lib/commands/build/command.d.ts.map +1 -1
  43. package/lib/commands/build/command.js +5 -0
  44. package/lib/commands/build/command.js.map +1 -1
  45. package/lib/commands/new/steps/installDependencies.d.ts.map +1 -1
  46. package/lib/commands/new/steps/installDependencies.js +3 -4
  47. package/lib/commands/new/steps/installDependencies.js.map +1 -1
  48. package/lib/commands/start/command.d.ts.map +1 -1
  49. package/lib/commands/start/command.js +5 -0
  50. package/lib/commands/start/command.js.map +1 -1
  51. package/lib/commands/static/application.d.ts.map +1 -1
  52. package/lib/commands/static/application.js +9 -3
  53. package/lib/commands/static/application.js.map +1 -1
  54. package/lib/commands/static/generate.d.ts.map +1 -1
  55. package/lib/commands/static/generate.js +16 -12
  56. package/lib/commands/static/generate.js.map +1 -1
  57. package/lib/config/configManager.d.ts +1 -0
  58. package/lib/config/configManager.d.ts.map +1 -1
  59. package/lib/config/configManager.js +1 -0
  60. package/lib/config/configManager.js.map +1 -1
  61. package/lib/di/tokens/config.d.ts +2 -0
  62. package/lib/di/tokens/config.d.ts.map +1 -1
  63. package/lib/library/webpack/blocks/css.d.ts.map +1 -1
  64. package/lib/library/webpack/blocks/css.js +42 -15
  65. package/lib/library/webpack/blocks/css.js.map +1 -1
  66. package/lib/library/webpack/blocks/js.d.ts.map +1 -1
  67. package/lib/library/webpack/blocks/js.js +52 -11
  68. package/lib/library/webpack/blocks/js.js.map +1 -1
  69. package/lib/library/webpack/common/main.d.ts.map +1 -1
  70. package/lib/library/webpack/common/main.js +2 -1
  71. package/lib/library/webpack/common/main.js.map +1 -1
  72. package/lib/library/webpack/utils/browserslist.d.ts +4 -0
  73. package/lib/library/webpack/utils/browserslist.d.ts.map +1 -0
  74. package/lib/library/webpack/utils/browserslist.js +27 -0
  75. package/lib/library/webpack/utils/browserslist.js.map +1 -0
  76. package/lib/library/webpack/utils/transpiler.d.ts.map +1 -1
  77. package/lib/library/webpack/utils/transpiler.js +3 -16
  78. package/lib/library/webpack/utils/transpiler.js.map +1 -1
  79. package/lib/schema/autogeneratedSchema.json +216 -15
  80. package/lib/typings/configEntry/cli.d.ts +9 -0
  81. package/lib/typings/configEntry/cli.d.ts.map +1 -1
  82. package/lib/utils/dev-app/request.d.ts +1 -0
  83. package/lib/utils/dev-app/request.d.ts.map +1 -1
  84. package/lib/utils/dev-app/request.js +10 -1
  85. package/lib/utils/dev-app/request.js.map +1 -1
  86. package/package.json +39 -37
  87. package/schema.json +216 -15
  88. package/src/api/analyze/index.ts +3 -16
  89. package/src/api/index.ts +1 -1
  90. package/src/api/start/application.experimental.ts +14 -0
  91. package/src/api/start/index.ts +1 -0
  92. package/src/builder/webpack/analyzePlugins/rsdoctor.ts +6 -3
  93. package/src/builder/webpack/index.ts +16 -21
  94. package/src/builder/webpack/providers/build/client.ts +7 -2
  95. package/src/builder/webpack/providers/build/server.ts +7 -2
  96. package/src/builder/webpack/providers/shared.ts +45 -1
  97. package/src/builder/webpack/providers/start/shared.ts +7 -2
  98. package/src/cli/index.ts +3 -2
  99. package/src/commands/analyze/command.ts +1 -1
  100. package/src/commands/build/command.ts +6 -0
  101. package/src/commands/new/steps/installDependencies.ts +3 -4
  102. package/src/commands/new/templates/app/block/shared/header/Header.spec.tsx.hbs +7 -5
  103. package/src/commands/new/templates/shared/package.json.hbs +1 -1
  104. package/src/commands/start/command.ts +6 -0
  105. package/src/commands/static/application.ts +11 -4
  106. package/src/commands/static/generate.ts +19 -12
  107. package/src/config/configManager.ts +2 -0
  108. package/src/library/webpack/blocks/css.ts +52 -16
  109. package/src/library/webpack/blocks/js.ts +58 -12
  110. package/src/library/webpack/common/main.ts +6 -2
  111. package/src/library/webpack/utils/browserslist.ts +29 -0
  112. package/src/library/webpack/utils/transpiler.ts +3 -20
  113. package/src/models/config.spec.ts +4 -0
  114. package/src/schema/autogeneratedSchema.json +216 -15
  115. package/src/schema/tramvai.spec.ts +2 -0
  116. package/src/typings/configEntry/cli.ts +11 -0
  117. package/src/utils/dev-app/request.ts +15 -0
  118. package/lib/api/analyze/providers/shared.d.ts +0 -3
  119. package/lib/api/analyze/providers/shared.d.ts.map +0 -1
  120. package/lib/api/analyze/providers/shared.js +0 -23
  121. package/lib/api/analyze/providers/shared.js.map +0 -1
  122. package/lib/builder/webpack/providers/analyze/shared.d.ts +0 -3
  123. package/lib/builder/webpack/providers/analyze/shared.d.ts.map +0 -1
  124. package/lib/builder/webpack/providers/analyze/shared.js +0 -137
  125. package/lib/builder/webpack/providers/analyze/shared.js.map +0 -1
  126. package/src/api/analyze/providers/shared.ts +0 -26
  127. package/src/builder/webpack/providers/analyze/shared.ts +0 -156
@@ -6,6 +6,7 @@ import {
6
6
  CLIENT_CONFIG_MANAGER_TOKEN,
7
7
  CLOSE_HANDLER_TOKEN,
8
8
  PROCESS_HANDLER_TOKEN,
9
+ WEBPACK_ANALYZE_PLUGIN_TOKEN,
9
10
  WEBPACK_CLIENT_COMPILER_TOKEN,
10
11
  WEBPACK_CLIENT_CONFIG_TOKEN,
11
12
  } from '../../tokens';
@@ -27,12 +28,16 @@ export const buildClientProviders: Provider[] = [
27
28
  }),
28
29
  provide({
29
30
  provide: WEBPACK_CLIENT_COMPILER_TOKEN,
30
- useFactory: ({ webpackConfig, di }) => {
31
- return createCompiler(toWebpackConfig(webpackConfig), di);
31
+ useFactory: ({ webpackConfig, di, analyzePlugin }) => {
32
+ return createCompiler(
33
+ toWebpackConfig(analyzePlugin ? analyzePlugin.patchConfig(webpackConfig) : webpackConfig),
34
+ di
35
+ );
32
36
  },
33
37
  deps: {
34
38
  webpackConfig: WEBPACK_CLIENT_CONFIG_TOKEN,
35
39
  di: DI_TOKEN,
40
+ analyzePlugin: { token: WEBPACK_ANALYZE_PLUGIN_TOKEN, optional: true },
36
41
  },
37
42
  }),
38
43
  provide({
@@ -8,6 +8,7 @@ import {
8
8
  INIT_HANDLER_TOKEN,
9
9
  PROCESS_HANDLER_TOKEN,
10
10
  SERVER_CONFIG_MANAGER_TOKEN,
11
+ WEBPACK_ANALYZE_PLUGIN_TOKEN,
11
12
  WEBPACK_SERVER_COMPILER_TOKEN,
12
13
  WEBPACK_SERVER_CONFIG_TOKEN,
13
14
  } from '../../tokens';
@@ -18,12 +19,16 @@ import { createCompiler } from '../../utils/compiler';
18
19
  export const buildServerProviders: Provider[] = [
19
20
  provide({
20
21
  provide: WEBPACK_SERVER_COMPILER_TOKEN,
21
- useFactory: ({ webpackConfig, di }) => {
22
- return createCompiler(toWebpackConfig(webpackConfig), di);
22
+ useFactory: ({ webpackConfig, di, analyzePlugin }) => {
23
+ return createCompiler(
24
+ toWebpackConfig(analyzePlugin ? analyzePlugin.patchConfig(webpackConfig) : webpackConfig),
25
+ di
26
+ );
23
27
  },
24
28
  deps: {
25
29
  webpackConfig: WEBPACK_SERVER_CONFIG_TOKEN,
26
30
  di: DI_TOKEN,
31
+ analyzePlugin: { token: WEBPACK_ANALYZE_PLUGIN_TOKEN, optional: true },
27
32
  },
28
33
  }),
29
34
  provide({
@@ -11,8 +11,26 @@ import {
11
11
  INIT_HANDLER_TOKEN,
12
12
  WEBPACK_CLIENT_COMPILER_TOKEN,
13
13
  WEBPACK_SERVER_COMPILER_TOKEN,
14
+ WEBPACK_ANALYZE_PLUGIN_NAME_TOKEN,
15
+ WEBPACK_ANALYZE_PLUGIN_TOKEN,
14
16
  } from '../tokens';
15
17
  import { emitWebpackEvents } from '../utils/webpackEvents';
18
+ import { BundleAnalyzePlugin } from '../analyzePlugins/bundle';
19
+ import { StatoscopeAnalyzePlugin } from '../analyzePlugins/statoscope';
20
+ import { WhyBundledAnalyzePlugin } from '../analyzePlugins/whyBundled';
21
+ import { RsdoctorAnalyzePlugin } from '../analyzePlugins/rsdoctor';
22
+ import type { AnalyzePlugin } from '../types';
23
+
24
+ interface Type<T> extends Function {
25
+ new (...args: any[]): T;
26
+ }
27
+
28
+ const pluginMap: Record<string, Type<AnalyzePlugin>> = {
29
+ bundle: BundleAnalyzePlugin,
30
+ whybundled: WhyBundledAnalyzePlugin,
31
+ statoscope: StatoscopeAnalyzePlugin,
32
+ rsdoctor: RsdoctorAnalyzePlugin,
33
+ };
16
34
 
17
35
  export const sharedProviders: Provider[] = [
18
36
  provide({
@@ -46,6 +64,27 @@ export const sharedProviders: Provider[] = [
46
64
  serverCompiler: { token: WEBPACK_SERVER_COMPILER_TOKEN, optional: true },
47
65
  },
48
66
  }),
67
+ provide({
68
+ provide: WEBPACK_ANALYZE_PLUGIN_TOKEN,
69
+ useFactory: ({ pluginName }) => {
70
+ if (!pluginName) {
71
+ return;
72
+ }
73
+
74
+ const PluginClass = pluginMap[pluginName];
75
+
76
+ if (!PluginClass) {
77
+ throw new Error(
78
+ 'Set correct value for --analytics cli option, <bundle|whybundled|statoscope|rsdoctor>\n'
79
+ );
80
+ }
81
+
82
+ return new PluginClass();
83
+ },
84
+ deps: {
85
+ pluginName: WEBPACK_ANALYZE_PLUGIN_NAME_TOKEN,
86
+ },
87
+ }),
49
88
  provide({
50
89
  provide: EVENT_EMITTER_TOKEN,
51
90
  useClass: EventEmitter,
@@ -80,13 +119,18 @@ export const sharedProviders: Provider[] = [
80
119
  provide({
81
120
  provide: CLOSE_HANDLER_TOKEN,
82
121
  multi: true,
83
- useFactory: ({ configManager }) => {
122
+ useFactory: ({ configManager, analyzePlugin }) => {
84
123
  return async () => {
85
124
  await closeWorkerPool(configManager);
125
+
126
+ if (analyzePlugin) {
127
+ return analyzePlugin.afterBuild();
128
+ }
86
129
  };
87
130
  },
88
131
  deps: {
89
132
  configManager: CONFIG_MANAGER_TOKEN,
133
+ analyzePlugin: { token: WEBPACK_ANALYZE_PLUGIN_TOKEN, optional: true },
90
134
  },
91
135
  }),
92
136
  ];
@@ -9,6 +9,7 @@ import {
9
9
  WEBPACK_COMPILER_TOKEN,
10
10
  WEBPACK_SERVER_COMPILER_TOKEN,
11
11
  WEBPACK_SERVER_CONFIG_TOKEN,
12
+ WEBPACK_ANALYZE_PLUGIN_TOKEN,
12
13
  } from '../../tokens';
13
14
  import { createDevServer } from '../../devServer/setup';
14
15
  import { CONFIG_MANAGER_TOKEN, STATIC_SERVER_TOKEN } from '../../../../di/tokens';
@@ -16,8 +17,11 @@ import { CONFIG_MANAGER_TOKEN, STATIC_SERVER_TOKEN } from '../../../../di/tokens
16
17
  export const startSharedProviders: Provider[] = [
17
18
  provide({
18
19
  provide: WEBPACK_COMPILER_TOKEN,
19
- useFactory: ({ clientConfig, serverConfig }) => {
20
- const configs = [clientConfig, serverConfig].filter(Boolean).map(toWebpackConfig);
20
+ useFactory: ({ clientConfig, serverConfig, analyzePlugin }) => {
21
+ const configs = [clientConfig, serverConfig]
22
+ .filter(Boolean)
23
+ .map((config) => (analyzePlugin ? analyzePlugin.patchConfig(config) : config))
24
+ .map(toWebpackConfig);
21
25
  const multiCompiler = webpack(configs);
22
26
  const { inputFileSystem } = multiCompiler.compilers[0];
23
27
 
@@ -51,6 +55,7 @@ export const startSharedProviders: Provider[] = [
51
55
  deps: {
52
56
  clientConfig: { token: WEBPACK_CLIENT_CONFIG_TOKEN, optional: true },
53
57
  serverConfig: { token: WEBPACK_SERVER_CONFIG_TOKEN, optional: true },
58
+ analyzePlugin: { token: WEBPACK_ANALYZE_PLUGIN_TOKEN, optional: true },
54
59
  },
55
60
  }),
56
61
  provide({
package/src/cli/index.ts CHANGED
@@ -14,8 +14,8 @@ import buildCommand from '../commands/build/command';
14
14
  import { StartCommand } from '../commands/start/command';
15
15
  import lintCommand from '../commands/lint/command';
16
16
  import taskCommand from '../commands/task/command';
17
- import analyze from '../commands/analyze/command';
18
17
  import generate from '../commands/generate/command';
18
+ import analyze from '../commands/analyze/command';
19
19
  import newCommand from '../commands/new/command';
20
20
  import updateCommand from '../commands/update/command';
21
21
  import addCommand from '../commands/add/command';
@@ -29,15 +29,16 @@ import type { TaskMap } from '../models/task';
29
29
  import { getRootFile } from '../utils/getRootFile';
30
30
  import { getTramvaiConfig } from '../utils/getTramvaiConfig';
31
31
  import { syncJsonFile } from '../utils/syncJsonFile';
32
+ import AnalyzeCommand from '../commands/analyze/command';
32
33
 
33
34
  async function loadCommands(): Promise<CommandMap> {
34
35
  return [
35
36
  buildCommand,
37
+ AnalyzeCommand,
36
38
  StartProdCommand,
37
39
  StartCommand,
38
40
  lintCommand,
39
41
  taskCommand,
40
- analyze,
41
42
  generate,
42
43
  newCommand,
43
44
  updateCommand,
@@ -2,7 +2,7 @@ import { CLICommand } from '../../models/command';
2
2
 
3
3
  export type Params = {
4
4
  target: string;
5
- plugin?: 'bundle' | 'whybundled' | 'statoscope';
5
+ plugin?: 'bundle' | 'whybundled' | 'statoscope' | 'rsdoctor';
6
6
  showConfig?: boolean;
7
7
  };
8
8
 
@@ -47,6 +47,12 @@ class BuildCommand extends CLICommand<Params> {
47
47
  value: '[forPublish]',
48
48
  description: '<package> Prepare library package.json for publication',
49
49
  },
50
+ {
51
+ name: '--analyze',
52
+ value: '[analyze]',
53
+ description:
54
+ 'Run build with analyze, supported plugins: <bundle|whybundled|statoscope|rsdoctor>',
55
+ },
50
56
  {
51
57
  name: '--fileCache',
52
58
  value: '[fileCache]',
@@ -9,10 +9,9 @@ const COMMON_JEST_DEPENDENCIES = [
9
9
  '@tramvai/test-unit',
10
10
  '@tramvai/test-react',
11
11
  '@tramvai/test-unit-jest',
12
- '@types/jest@^29.0.0',
13
- 'jest@^29.0.0',
14
- 'jest-circus@^29.0.0',
15
- 'jest-environment-jsdom@^29.0.0',
12
+ '@jest/types@^29.6.3',
13
+ 'jest@^29.7.0',
14
+ 'jest-environment-jsdom@^29.7.0',
16
15
  'ts-node',
17
16
  ];
18
17
 
@@ -4,10 +4,12 @@
4
4
  import { testComponent } from '@tramvai/test-react';
5
5
  import { Header } from './Header';
6
6
 
7
- it('should render header', () => {
8
- const { render } = testComponent(<Header />);
7
+ describe('Header', () => {
8
+ it('should render header', () => {
9
+ const { render } = testComponent(<Header />);
9
10
 
10
- expect(render.container.innerHTML).toMatchInlineSnapshot(
11
- `"<header class="Header"><h1>Tramvai <span role="img" aria-label="Salute">🥳</span></h1></header>"`
12
- );
11
+ expect(render.container.innerHTML).toMatchInlineSnapshot(
12
+ `"<header class="Header"><h1>Tramvai <span role="img" aria-label="Salute">🥳</span></h1></header>"`
13
+ );
14
+ });
13
15
  });
@@ -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",
@@ -88,6 +88,12 @@ export class StartCommand extends CLICommand<Params> {
88
88
  value: '[showConfig]',
89
89
  description: 'Show config with which cli was launched',
90
90
  },
91
+ {
92
+ name: '--analyze',
93
+ value: '[analyze]',
94
+ description:
95
+ 'Run build with analyze, supported plugins: <bundle|whybundled|statoscope|rsdoctor>',
96
+ },
91
97
  {
92
98
  name: '--onlyBundles',
93
99
  value: '[onlyBundles]',
@@ -16,7 +16,7 @@ import { app } from '../index';
16
16
  import { startStaticServer } from './staticServer';
17
17
  import { startServer } from './server';
18
18
  import { handleServerOutput } from './utils/handle-server-output';
19
- import { appBundleInfo } from '../../utils/dev-app/request';
19
+ import { appPrerenderRoutes } from '../../utils/dev-app/request';
20
20
  import { PortManager } from '../../models/port-manager';
21
21
 
22
22
  // eslint-disable-next-line max-statements
@@ -94,10 +94,10 @@ export const staticApp = async (
94
94
  ...safeRequire(path.resolve(process.cwd(), 'env'), true),
95
95
  }
96
96
  : {}),
97
+ CACHE_WARMUP_DISABLED: 'true',
97
98
  ...process.env,
98
99
  NODE_ENV: 'production',
99
100
  TRAMVAI_CLI_COMMAND: 'static',
100
- CACHE_WARMUP_DISABLED: 'true',
101
101
  PORT: `${port}`,
102
102
  PORT_SERVER: `${port}`,
103
103
  TRAMVAI_CLI_ASSETS_PREFIX: staticAssetsPrefix,
@@ -105,7 +105,7 @@ export const staticApp = async (
105
105
  };
106
106
 
107
107
  const server = node(path.resolve(root, 'server.js'), [], {
108
- cwd: root,
108
+ cwd: process.cwd(),
109
109
  env: envVariables,
110
110
  });
111
111
 
@@ -124,6 +124,13 @@ export const staticApp = async (
124
124
 
125
125
  handleServerOutput(context.logger, chunk);
126
126
  });
127
+ server.stderr.on('data', (chunk: Buffer) => {
128
+ if (server.killed) {
129
+ return;
130
+ }
131
+
132
+ handleServerOutput(context.logger, chunk);
133
+ });
127
134
 
128
135
  const readinessProbePath = `${clientConfigManager.httpProtocol}://localhost:${
129
136
  envVariables.UTILITY_SERVER_PORT ?? port
@@ -145,7 +152,7 @@ export const staticApp = async (
145
152
  message: `message: server started, fetch application routes`,
146
153
  });
147
154
 
148
- let paths = await appBundleInfo(serverConfigManager);
155
+ let paths = await appPrerenderRoutes(serverConfigManager);
149
156
 
150
157
  if (isString(rootErrorBoundaryPath)) {
151
158
  // implicit connection with packages/modules/server/src/server/error.ts
@@ -1,6 +1,7 @@
1
1
  import { resolve, join } from 'path';
2
2
  import PQueue from 'promise-queue';
3
3
  import { outputFile } from 'fs-extra';
4
+ import { getHeaders } from '@tinkoff/request-plugin-protocol-http';
4
5
  import type { Context } from '../../models/context';
5
6
  import type { ConfigManager } from '../../config/configManager';
6
7
  import type { ApplicationConfigEntry } from '../../typings/configEntry/application';
@@ -8,7 +9,6 @@ import { appRequest } from '../../utils/dev-app/request';
8
9
  import type { Params } from './command';
9
10
 
10
11
  const MAX_CONCURRENT = 10;
11
- const DYNAMIC_PAGE_REGEX = /\/:.+\//g;
12
12
 
13
13
  export const generateStatic = async (
14
14
  context: Context,
@@ -28,7 +28,9 @@ export const generateStatic = async (
28
28
 
29
29
  return result;
30
30
  },
31
- {} as Record<string, string>
31
+ {
32
+ 'x-tramvai-prerender': 'true',
33
+ } as Record<string, string>
32
34
  );
33
35
 
34
36
  const { rootDir, output } = configManager;
@@ -37,15 +39,7 @@ export const generateStatic = async (
37
39
  for (const path of paths) {
38
40
  promises.push(
39
41
  q.add(async () => {
40
- // @todo need something similar to https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation
41
- if (DYNAMIC_PAGE_REGEX.test(path)) {
42
- context.logger.event({
43
- type: 'warning',
44
- event: 'COMMAND:STATIC:DYNAMIC_PAGE_UNSUPPORTED',
45
- message: `path: ${path}, message: export dynamic pages to HTML is not supported`,
46
- });
47
- return;
48
- }
42
+ let response: any;
49
43
 
50
44
  try {
51
45
  context.logger.event({
@@ -54,7 +48,8 @@ export const generateStatic = async (
54
48
  message: `path: ${path}, message: start fetching page`,
55
49
  });
56
50
 
57
- const html = await appRequest(configManager, path, { headers });
51
+ response = appRequest(configManager, path, { headers });
52
+ const html = await response;
58
53
 
59
54
  await outputFile(join(staticPath, path, 'index.html'), html);
60
55
 
@@ -64,6 +59,18 @@ export const generateStatic = async (
64
59
  message: `path: ${path}, message: page created successfully`,
65
60
  });
66
61
  } catch (e) {
62
+ const responseHeaders = response ? getHeaders(response) : {};
63
+
64
+ if (responseHeaders['x-tramvai-prerender-skip'] === 'true') {
65
+ context.logger.event({
66
+ type: 'debug',
67
+ event: 'COMMAND:STATIC:PAGE_SKIP',
68
+ message: `path: ${path}, message: page prerender skipped`,
69
+ });
70
+
71
+ return;
72
+ }
73
+
67
74
  context.logger.event({
68
75
  type: 'error',
69
76
  event: 'COMMAND:STATIC:ERROR',
@@ -41,6 +41,7 @@ export interface Settings<E extends Env> {
41
41
  resolveSymlinks?: boolean;
42
42
  showConfig?: boolean;
43
43
  onlyBundles?: string[];
44
+ analyze?: false | 'bundle' | 'whybundled' | 'statoscope' | 'rsdoctor';
44
45
  disableProdOptimization?: boolean;
45
46
  fileCache?: boolean;
46
47
  }
@@ -135,6 +136,7 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
135
136
  benchmark: false,
136
137
  resolveSymlinks: true,
137
138
  disableProdOptimization: false,
139
+ analyze: false,
138
140
  onlyBundles: [],
139
141
  // according to measures fileCache in webpack doesn't affect
140
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;
@@ -5,25 +5,22 @@ import type { ConfigManager } from '../../../config/configManager';
5
5
  import { getTranspilerConfig, addTranspilerLoader } from '../utils/transpiler';
6
6
  import type { CliConfigEntry } from '../../../typings/configEntry/cli';
7
7
 
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));
8
+ const projectRulesName = 'project';
9
+ const nodeModuleRulesName = 'node_module';
16
10
 
11
+ const applyProjectRules = (rule, configManager) => {
17
12
  rule
18
- .oneOf('project')
13
+ .oneOf(projectRulesName)
19
14
  .exclude.add(/node_modules/)
20
15
  .end()
21
16
  .use('transpiler')
22
- .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)));
17
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)))
18
+ .end();
19
+ };
23
20
 
21
+ const applyNodeModulesRules = (rule, configManager) => {
24
22
  rule
25
- .oneOf('node_module')
26
- .when(transpileOnlyModernLibs, (cfg) => cfg.include.add(modernLibsFilter))
23
+ .oneOf(nodeModuleRulesName)
27
24
  .merge({
28
25
  // true value forces to use file extensions for importing mjs modules
29
26
  // but we want to use mjs if it exists anyway
@@ -32,3 +29,52 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
32
29
  .use('transpiler')
33
30
  .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager, { hot: false })));
34
31
  };
32
+
33
+ // eslint-disable-next-line import/no-default-export
34
+ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config) => {
35
+ const { transpileOnlyModernLibs } = configManager;
36
+ const { include } = configManager.experiments.transpilation;
37
+ const shouldTranspileOnlyModern = transpileOnlyModernLibs || include === 'only-modern';
38
+
39
+ const rule = config.module
40
+ .rule('js')
41
+ .test(/\.[cm]?js[x]?$/)
42
+ .batch(applyThreadLoader(configManager));
43
+
44
+ if (configManager.env === 'production') {
45
+ applyProjectRules(rule, configManager);
46
+ applyNodeModulesRules(rule, configManager);
47
+
48
+ if (shouldTranspileOnlyModern) {
49
+ rule.oneOf(nodeModuleRulesName).include.add(modernLibsFilter);
50
+ }
51
+ } else {
52
+ const shouldSkipTranspiling = include === 'none';
53
+ const shouldSelectiveTranspile = Array.isArray(include);
54
+
55
+ if (shouldSkipTranspiling) {
56
+ rule.exclude
57
+ .add(/node_modules/)
58
+ .end()
59
+ .use('transpiler')
60
+ .batch(addTranspilerLoader(configManager, getTranspilerConfig(configManager)));
61
+ } else {
62
+ applyProjectRules(rule, configManager);
63
+ applyNodeModulesRules(rule, configManager);
64
+
65
+ if (shouldSelectiveTranspile) {
66
+ const includeForTranspiling = (<string[]>include).map(
67
+ (includePath) => new RegExp(includePath)
68
+ );
69
+
70
+ includeForTranspiling.forEach((includePath) => {
71
+ rule.oneOf(nodeModuleRulesName).include.add(includePath);
72
+ });
73
+ } else if (shouldTranspileOnlyModern) {
74
+ if (shouldTranspileOnlyModern) {
75
+ rule.oneOf(nodeModuleRulesName).include.add(modernLibsFilter);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ };
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import { existsSync } from 'fs-extra';
5
5
  import findCacheDir from 'find-cache-dir';
6
6
  import { RsdoctorWebpackMultiplePlugin } from '@rsdoctor/webpack-plugin';
7
- import { getRsdoctorOptions } from '@tramvai/plugin-webpack-builder';
7
+ import { getBenchmarkRsdoctorOptions } from '@tramvai/plugin-webpack-builder';
8
8
  import { ignoreWarnings } from '../utils/warningsFilter';
9
9
  import resolve from '../blocks/resolve';
10
10
  import ignoreLocales from '../blocks/ignoreLocales';
@@ -124,6 +124,10 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
124
124
  'process.env.ENABLE_DEVTOOLS':
125
125
  process.env.ENABLE_DEVTOOLS || configManager.env === 'development',
126
126
 
127
+ 'typeof window': JSON.stringify(
128
+ configManager.buildType === 'server' ? 'undefined' : 'object'
129
+ ),
130
+
127
131
  'process.env.DISABLE_EXTERNAL_SCRIPTS': process.env.DISABLE_EXTERNAL_SCRIPTS || false,
128
132
  },
129
133
  ]);
@@ -131,7 +135,7 @@ export default (configManager: ConfigManager<CliConfigEntry>) => (config: Config
131
135
  if (configManager.benchmark) {
132
136
  config
133
137
  .plugin('rsdoctor-benchmark')
134
- .use(RsdoctorWebpackMultiplePlugin, [getRsdoctorOptions(configManager.buildType)]);
138
+ .use(RsdoctorWebpackMultiplePlugin, [getBenchmarkRsdoctorOptions(configManager.buildType)]);
135
139
  }
136
140
 
137
141
  // TODO: remove after dropping support for node@14
@@ -0,0 +1,29 @@
1
+ import envTargets from '@tinkoff/browserslist-config';
2
+ import browserslist from 'browserslist';
3
+
4
+ import type { Target } from '../../../typings/target';
5
+
6
+ export function getBrowserslistTargets(rootDir: string, actualTarget: Target) {
7
+ const browserslistConfigRaw = browserslist.findConfig(rootDir);
8
+
9
+ // Set defaults if the explicit config for browserslist was not found or the config does not contain the necessary targets
10
+ const browserslistQuery =
11
+ browserslistConfigRaw?.[actualTarget] ?? envTargets[actualTarget] ?? envTargets.defaults;
12
+
13
+ return browserslist(browserslistQuery, {
14
+ mobileToDesktop: true,
15
+ env: actualTarget,
16
+ });
17
+ }
18
+
19
+ export function getActualTarget(target, isServer) {
20
+ let actualTarget = target;
21
+
22
+ if (!target) {
23
+ if (isServer) {
24
+ actualTarget = 'node';
25
+ }
26
+ }
27
+
28
+ return actualTarget;
29
+ }