@tramvai/cli 7.5.3 → 7.11.0

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 (90) hide show
  1. package/lib/api/build/providers/shared.d.ts.map +1 -1
  2. package/lib/api/build/providers/shared.js +11 -1
  3. package/lib/api/build/providers/shared.js.map +1 -1
  4. package/lib/api/start/application.d.ts.map +1 -1
  5. package/lib/api/start/application.js +13 -4
  6. package/lib/api/start/application.js.map +1 -1
  7. package/lib/api/start/providers/application/shared.d.ts.map +1 -1
  8. package/lib/api/start/providers/application/shared.js +4 -1
  9. package/lib/api/start/providers/application/shared.js.map +1 -1
  10. package/lib/api/start/providers/shared.d.ts.map +1 -1
  11. package/lib/api/start/providers/shared.js +8 -0
  12. package/lib/api/start/providers/shared.js.map +1 -1
  13. package/lib/api/start-prod/providers/application.d.ts.map +1 -1
  14. package/lib/api/start-prod/providers/application.js +4 -1
  15. package/lib/api/start-prod/providers/application.js.map +1 -1
  16. package/lib/api/start-prod/providers/shared.d.ts.map +1 -1
  17. package/lib/api/start-prod/providers/shared.js +8 -0
  18. package/lib/api/start-prod/providers/shared.js.map +1 -1
  19. package/lib/builder/webpack/devServer/pool/process/pool.d.ts.map +1 -1
  20. package/lib/builder/webpack/devServer/pool/process/pool.js +1 -0
  21. package/lib/builder/webpack/devServer/pool/process/pool.js.map +1 -1
  22. package/lib/builder/webpack/devServer/pool/thread/pool.d.ts.map +1 -1
  23. package/lib/builder/webpack/devServer/pool/thread/pool.js +1 -0
  24. package/lib/builder/webpack/devServer/pool/thread/pool.js.map +1 -1
  25. package/lib/builder/webpack/providers/build/client.d.ts.map +1 -1
  26. package/lib/builder/webpack/providers/build/client.js +3 -1
  27. package/lib/builder/webpack/providers/build/client.js.map +1 -1
  28. package/lib/builder/webpack/providers/build/clientShared.d.ts.map +1 -1
  29. package/lib/builder/webpack/providers/build/clientShared.js +7 -2
  30. package/lib/builder/webpack/providers/build/clientShared.js.map +1 -1
  31. package/lib/builder/webpack/providers/build/server.d.ts.map +1 -1
  32. package/lib/builder/webpack/providers/build/server.js +7 -2
  33. package/lib/builder/webpack/providers/build/server.js.map +1 -1
  34. package/lib/builder/webpack/providers/shared.d.ts.map +1 -1
  35. package/lib/builder/webpack/providers/shared.js.map +1 -1
  36. package/lib/builder/webpack/providers/start/shared.d.ts.map +1 -1
  37. package/lib/builder/webpack/providers/start/shared.js +17 -0
  38. package/lib/builder/webpack/providers/start/shared.js.map +1 -1
  39. package/lib/builder/webpack/tokens.d.ts +7 -0
  40. package/lib/builder/webpack/tokens.d.ts.map +1 -1
  41. package/lib/builder/webpack/tokens.js +2 -1
  42. package/lib/builder/webpack/tokens.js.map +1 -1
  43. package/lib/cli/index.d.ts.map +1 -1
  44. package/lib/cli/index.js +1 -0
  45. package/lib/cli/index.js.map +1 -1
  46. package/lib/commands/static/generate.d.ts +9 -1
  47. package/lib/commands/static/generate.d.ts.map +1 -1
  48. package/lib/commands/static/generate.js +67 -19
  49. package/lib/commands/static/generate.js.map +1 -1
  50. package/lib/commands/update/update.d.ts.map +1 -1
  51. package/lib/commands/update/update.js +1 -0
  52. package/lib/commands/update/update.js.map +1 -1
  53. package/lib/config/configManager.d.ts +3 -0
  54. package/lib/config/configManager.d.ts.map +1 -1
  55. package/lib/config/configManager.js +8 -0
  56. package/lib/config/configManager.js.map +1 -1
  57. package/lib/di/tokens/config.d.ts +2 -0
  58. package/lib/di/tokens/config.d.ts.map +1 -1
  59. package/lib/library/webpack/blocks/configToEnv.d.ts.map +1 -1
  60. package/lib/library/webpack/blocks/configToEnv.js +5 -1
  61. package/lib/library/webpack/blocks/configToEnv.js.map +1 -1
  62. package/lib/utils/dev-app/request.d.ts +3 -0
  63. package/lib/utils/dev-app/request.d.ts.map +1 -1
  64. package/lib/utils/dev-app/request.js +11 -1
  65. package/lib/utils/dev-app/request.js.map +1 -1
  66. package/lib/utils/tramvaiVersions.d.ts.map +1 -1
  67. package/lib/utils/tramvaiVersions.js +1 -0
  68. package/lib/utils/tramvaiVersions.js.map +1 -1
  69. package/package.json +9 -8
  70. package/src/api/build/providers/shared.ts +11 -1
  71. package/src/api/start/application.ts +16 -4
  72. package/src/api/start/providers/application/shared.ts +4 -1
  73. package/src/api/start/providers/shared.ts +8 -0
  74. package/src/api/start-prod/providers/application.ts +4 -1
  75. package/src/api/start-prod/providers/shared.ts +8 -0
  76. package/src/builder/webpack/devServer/pool/process/pool.ts +1 -0
  77. package/src/builder/webpack/devServer/pool/thread/pool.ts +1 -0
  78. package/src/builder/webpack/providers/build/client.ts +4 -1
  79. package/src/builder/webpack/providers/build/clientShared.ts +10 -3
  80. package/src/builder/webpack/providers/build/server.ts +9 -3
  81. package/src/builder/webpack/providers/shared.ts +1 -0
  82. package/src/builder/webpack/providers/start/shared.ts +19 -0
  83. package/src/builder/webpack/tokens.ts +2 -0
  84. package/src/cli/index.ts +1 -0
  85. package/src/commands/static/generate.ts +96 -22
  86. package/src/commands/update/update.ts +2 -0
  87. package/src/config/configManager.ts +11 -0
  88. package/src/library/webpack/blocks/configToEnv.ts +4 -1
  89. package/src/utils/dev-app/request.ts +16 -0
  90. package/src/utils/tramvaiVersions.ts +1 -0
@@ -1,8 +1,10 @@
1
1
  import type { Provider } from '@tinkoff/dippy';
2
2
  import { provide } from '@tinkoff/dippy';
3
+ import { nanoid } from 'nanoid';
3
4
  import { STRICT_ERROR_HANDLE } from '../tokens';
4
5
  import { COMMAND_PARAMETERS_TOKEN } from '../../../di/tokens';
5
6
  import type { Params } from '../index';
7
+ import { BUILD_ID_TOKEN } from '../../../builder/webpack/tokens';
6
8
 
7
9
  export const sharedProviders: readonly Provider[] = [
8
10
  provide({
@@ -14,4 +16,10 @@ export const sharedProviders: readonly Provider[] = [
14
16
  parameters: COMMAND_PARAMETERS_TOKEN,
15
17
  },
16
18
  }),
19
+ provide({
20
+ provide: BUILD_ID_TOKEN,
21
+ useFactory: () => {
22
+ return nanoid();
23
+ },
24
+ }),
17
25
  ] as const;
@@ -18,6 +18,7 @@ import type { ConfigManager } from '../../../config/configManager';
18
18
  import { createConfigManager } from '../../../config/configManager';
19
19
  import { selfSignedCertificateProvider } from '../../shared/providers/selfSignedCertificateProvider';
20
20
  import { createServer } from '../../start/utils/createServer';
21
+ import { BUILD_ID_TOKEN } from '../../../builder/webpack/tokens';
21
22
 
22
23
  export const applicationsProviders: readonly Provider[] = [
23
24
  ...selfSignedCertificateProvider,
@@ -35,8 +36,9 @@ export const applicationsProviders: readonly Provider[] = [
35
36
  }),
36
37
  provide({
37
38
  provide: CONFIG_MANAGER_TOKEN,
38
- useFactory: ({ configEntry, parameters, portManager }) =>
39
+ useFactory: ({ configEntry, parameters, portManager, buildId }) =>
39
40
  createConfigManager(configEntry as ApplicationConfigEntry, {
41
+ buildId,
40
42
  ...parameters,
41
43
  appEnv: parameters.env,
42
44
  env: 'production',
@@ -48,6 +50,7 @@ export const applicationsProviders: readonly Provider[] = [
48
50
  configEntry: CONFIG_ENTRY_TOKEN,
49
51
  parameters: COMMAND_PARAMETERS_TOKEN,
50
52
  portManager: PORT_MANAGER_TOKEN,
53
+ buildId: BUILD_ID_TOKEN,
51
54
  },
52
55
  }),
53
56
  provide({
@@ -4,11 +4,13 @@ import fastify from 'fastify';
4
4
  import fastifyCompress from '@fastify/compress';
5
5
  import fastifyStatic from '@fastify/static';
6
6
  import zlib from 'zlib';
7
+ import { nanoid } from 'nanoid';
7
8
  import { CLOSE_HANDLER_TOKEN, INIT_HANDLER_TOKEN, PROCESS_HANDLER_TOKEN } from '../tokens';
8
9
  import { CONFIG_MANAGER_TOKEN, PORT_MANAGER_TOKEN, STATIC_SERVER_TOKEN } from '../../../di/tokens';
9
10
  import { stopServer } from '../../start/utils/stopServer';
10
11
  import { listenServer } from '../../start/utils/listenServer';
11
12
  import { isApplication, isChildApp } from '../../../config/validate';
13
+ import { BUILD_ID_TOKEN } from '../../../builder/webpack/tokens';
12
14
 
13
15
  export const sharedProviders: readonly Provider[] = [
14
16
  provide({
@@ -89,4 +91,10 @@ export const sharedProviders: readonly Provider[] = [
89
91
  portManager: PORT_MANAGER_TOKEN,
90
92
  },
91
93
  }),
94
+ provide({
95
+ provide: BUILD_ID_TOKEN,
96
+ useFactory: () => {
97
+ return nanoid();
98
+ },
99
+ }),
92
100
  ] as const;
@@ -41,6 +41,7 @@ export const ProcessWorkerBridge: WorkerBridgeFactory<Worker> = (di) => {
41
41
  // https://nodejs.org/dist/latest-v15.x/docs/api/all.html#cluster_how_it_works
42
42
  PORT: `${configManager.port}`,
43
43
  PORT_SERVER: `${configManager.port}`,
44
+ HOST: `${configManager.host}`,
44
45
  HOST_STATIC: `${configManager.staticHost}`,
45
46
  PORT_STATIC: `${configManager.staticPort}`,
46
47
  TRAMVAI_CLI_WATCH_INITIAL_BUILD: firstWorker,
@@ -39,6 +39,7 @@ export const ThreadWorkerBridge: WorkerBridgeFactory<Worker> = (di) => {
39
39
  // https://nodejs.org/dist/latest-v15.x/docs/api/all.html#cluster_how_it_works
40
40
  PORT: `${configManager.port}`,
41
41
  PORT_SERVER: `${configManager.port}`,
42
+ HOST: `${configManager.host}`,
42
43
  HOST_STATIC: `${configManager.staticHost}`,
43
44
  PORT_STATIC: `${configManager.staticPort}`,
44
45
  TRAMVAI_CLI_WATCH_INITIAL_BUILD: firstWorker,
@@ -3,6 +3,7 @@ import { DI_TOKEN, provide } from '@tinkoff/dippy';
3
3
  import { COMMAND_PARAMETERS_TOKEN, CONFIG_MANAGER_TOKEN } from '../../../../di/tokens';
4
4
  import { toWebpackConfig } from '../../../../library/webpack/utils/toWebpackConfig';
5
5
  import {
6
+ BUILD_ID_TOKEN,
6
7
  CLIENT_CONFIG_MANAGER_TOKEN,
7
8
  CLOSE_HANDLER_TOKEN,
8
9
  PROCESS_HANDLER_TOKEN,
@@ -17,13 +18,15 @@ import { createCompiler } from '../../utils/compiler';
17
18
  export const buildClientProviders: Provider[] = [
18
19
  provide({
19
20
  provide: CLIENT_CONFIG_MANAGER_TOKEN,
20
- useFactory: ({ configManager }) => {
21
+ useFactory: ({ configManager, buildId }) => {
21
22
  return configManager.withSettings({
22
23
  buildType: 'client',
24
+ buildId,
23
25
  });
24
26
  },
25
27
  deps: {
26
28
  configManager: CONFIG_MANAGER_TOKEN,
29
+ buildId: BUILD_ID_TOKEN,
27
30
  },
28
31
  }),
29
32
  provide({
@@ -1,18 +1,25 @@
1
1
  import type { Provider } from '@tinkoff/dippy';
2
2
  import { optional } from '@tinkoff/dippy';
3
3
  import { provide } from '@tinkoff/dippy';
4
- import rimraf from 'rimraf';
4
+ import rimrafCb from 'rimraf';
5
+ import { promisify } from 'util';
6
+
5
7
  import { CLI_PACKAGE_MANAGER, CLI_ROOT_DIR_TOKEN } from '../../../../di/tokens';
6
8
  import { npmRequire } from '../../../../utils/npmRequire';
7
9
  import { CLIENT_CONFIG_MANAGER_TOKEN, INIT_HANDLER_TOKEN } from '../../tokens';
8
10
 
11
+ const rimraf = promisify(rimrafCb);
12
+
9
13
  export const buildClientSharedProviders: Provider[] = [
10
14
  provide({
11
15
  provide: INIT_HANDLER_TOKEN,
12
16
  multi: true,
13
17
  useFactory: ({ configManager }) => {
14
- return function clearBuildDir() {
15
- return rimraf.sync(`${configManager.buildPath}/**`, {});
18
+ return async function clearBuildDir() {
19
+ await Promise.all([
20
+ rimraf(`${configManager.buildPath}/**`),
21
+ configManager.prerenderPagesPath && rimraf(`${configManager.prerenderPagesPath}/**`),
22
+ ]);
16
23
  };
17
24
  },
18
25
  deps: {
@@ -1,6 +1,7 @@
1
1
  import type { Provider } from '@tinkoff/dippy';
2
2
  import { DI_TOKEN, provide } from '@tinkoff/dippy';
3
- import rimraf from 'rimraf';
3
+ import rimrafCb from 'rimraf';
4
+ import { promisify } from 'util';
4
5
  import { toWebpackConfig } from '../../../../library/webpack/utils/toWebpackConfig';
5
6
  import { CONFIG_MANAGER_TOKEN } from '../../../../di/tokens';
6
7
  import {
@@ -17,6 +18,8 @@ import { closeWebpack } from '../../utils/closeWebpack';
17
18
  import { runWebpack } from '../../utils/runWebpack';
18
19
  import { createCompiler } from '../../utils/compiler';
19
20
 
21
+ const rimraf = promisify(rimrafCb);
22
+
20
23
  export const buildServerProviders: Provider[] = [
21
24
  provide({
22
25
  provide: WEBPACK_SERVER_COMPILER_TOKEN,
@@ -40,8 +43,11 @@ export const buildServerProviders: Provider[] = [
40
43
  provide: INIT_HANDLER_TOKEN,
41
44
  multi: true,
42
45
  useFactory: ({ configManager }) => {
43
- return function clearBuildDir() {
44
- return rimraf.sync(`${configManager.buildPath}/**`, {});
46
+ return async function clearBuildDir() {
47
+ await Promise.all([
48
+ rimraf(`${configManager.buildPath}/**`),
49
+ configManager.prerenderPagesPath && rimraf(`${configManager.prerenderPagesPath}/**`),
50
+ ]);
45
51
  };
46
52
  },
47
53
  deps: {
@@ -13,6 +13,7 @@ import {
13
13
  WEBPACK_SERVER_COMPILER_TOKEN,
14
14
  WEBPACK_ANALYZE_PLUGIN_NAME_TOKEN,
15
15
  WEBPACK_ANALYZE_PLUGIN_TOKEN,
16
+ BUILD_ID_TOKEN,
16
17
  } from '../tokens';
17
18
  import { emitWebpackEvents } from '../utils/webpackEvents';
18
19
  import { BundleAnalyzePlugin } from '../analyzePlugins/bundle';
@@ -1,6 +1,8 @@
1
1
  import type { Provider } from '@tinkoff/dippy';
2
2
  import { DI_TOKEN, provide } from '@tinkoff/dippy';
3
3
  import webpack from 'webpack';
4
+ import rimrafCb from 'rimraf';
5
+ import { promisify } from 'util';
4
6
  import { toWebpackConfig } from '../../../../library/webpack/utils/toWebpackConfig';
5
7
  import {
6
8
  PROCESS_HANDLER_TOKEN,
@@ -10,10 +12,13 @@ import {
10
12
  WEBPACK_SERVER_COMPILER_TOKEN,
11
13
  WEBPACK_SERVER_CONFIG_TOKEN,
12
14
  WEBPACK_ANALYZE_PLUGIN_TOKEN,
15
+ CLOSE_HANDLER_TOKEN,
13
16
  } from '../../tokens';
14
17
  import { createDevServer } from '../../devServer/setup';
15
18
  import { CONFIG_MANAGER_TOKEN, STATIC_SERVER_TOKEN } from '../../../../di/tokens';
16
19
 
20
+ const rimraf = promisify(rimrafCb);
21
+
17
22
  export const startSharedProviders: Provider[] = [
18
23
  provide({
19
24
  provide: WEBPACK_COMPILER_TOKEN,
@@ -112,4 +117,18 @@ export const startSharedProviders: Provider[] = [
112
117
  staticServer: STATIC_SERVER_TOKEN,
113
118
  },
114
119
  }),
120
+ provide({
121
+ provide: CLOSE_HANDLER_TOKEN,
122
+ multi: true,
123
+ useFactory: ({ configManager }) => {
124
+ return async () => {
125
+ if (configManager.prerenderPagesPath) {
126
+ await rimraf(`${configManager.prerenderPagesPath}/**`);
127
+ }
128
+ };
129
+ },
130
+ deps: {
131
+ configManager: CONFIG_MANAGER_TOKEN,
132
+ },
133
+ }),
115
134
  ];
@@ -58,3 +58,5 @@ export const CLOSE_HANDLER_TOKEN = createToken<() => Promise<void> | void>(
58
58
  'builder-webpack closeHandler',
59
59
  { multi: true }
60
60
  );
61
+
62
+ export const BUILD_ID_TOKEN = createToken<string>('builder-webpack build id');
package/src/cli/index.ts CHANGED
@@ -68,6 +68,7 @@ export async function cliInitialized(cliArgs = process.argv) {
68
68
  }>('package.json');
69
69
  const { content: config } = getTramvaiConfig();
70
70
 
71
+ // todo: different build id here, can be a problem?
71
72
  const configManager = new ConfigManager({ config, syncConfigFile: syncJsonFile });
72
73
  const packageManager = resolvePackageManager({ rootDir: process.cwd() });
73
74
 
@@ -1,25 +1,39 @@
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
+ import { getHeader, getHeaders, getStatus } from '@tinkoff/request-plugin-protocol-http';
5
5
  import type { Context } from '../../models/context';
6
6
  import type { ConfigManager } from '../../config/configManager';
7
7
  import type { ApplicationConfigEntry } from '../../typings/configEntry/application';
8
- import { appRequest } from '../../utils/dev-app/request';
8
+ import { appFetch } from '../../utils/dev-app/request';
9
9
  import type { Params } from './command';
10
10
 
11
11
  const MAX_CONCURRENT = 10;
12
12
 
13
+ export interface PrerenderRequest {
14
+ /**
15
+ * Page path, for example, '/product/123/', without query parameters
16
+ */
17
+ pathname: string;
18
+ headers?: Record<string, string>;
19
+ query?: Record<string, string>;
20
+ }
21
+
13
22
  export const generateStatic = async (
14
23
  context: Context,
15
24
  params: Params,
16
25
  configManager: ConfigManager<ApplicationConfigEntry>,
17
- paths: string[]
26
+ paths: Array<string | PrerenderRequest>
18
27
  ) => {
28
+ const meta: {
29
+ staticPages: Record<string, any>[];
30
+ } = {
31
+ staticPages: [],
32
+ };
19
33
  const { header = [], folder = '' } = params;
20
34
  const q = new PQueue(MAX_CONCURRENT);
21
35
  const promises = [];
22
- const headers = header.reduce(
36
+ const baseHeaders = header.reduce(
23
37
  (result, item) => {
24
38
  const [key, value] = item.split(':');
25
39
 
@@ -36,50 +50,110 @@ export const generateStatic = async (
36
50
  const { rootDir, output } = configManager;
37
51
  const staticPath = resolve(rootDir, output.static, folder);
38
52
 
39
- for (const path of paths) {
53
+ for (const pathOrRequest of paths) {
40
54
  promises.push(
55
+ // eslint-disable-next-line max-statements
41
56
  q.add(async () => {
42
- let response: any;
57
+ let response: Response;
58
+
59
+ const request: PrerenderRequest =
60
+ typeof pathOrRequest === 'string' ? { pathname: pathOrRequest } : pathOrRequest;
61
+
62
+ const { pathname, headers: requestHeaders = {}, query = {} } = request;
63
+
64
+ const mergedHeaders = {
65
+ ...baseHeaders,
66
+ ...requestHeaders,
67
+ };
43
68
 
44
69
  try {
45
70
  context.logger.event({
46
71
  type: 'debug',
47
72
  event: 'COMMAND:STATIC:PAGE_FETCH',
48
- message: `path: ${path}, message: start fetching page`,
73
+ message: `path: ${pathname}, message: start fetching page`,
74
+ payload: {
75
+ headers: mergedHeaders,
76
+ query,
77
+ },
49
78
  });
50
79
 
51
- response = appRequest(configManager, path, { headers });
52
- const html = await response;
53
-
54
- await outputFile(join(staticPath, path, 'index.html'), html);
55
-
56
- context.logger.event({
57
- type: 'debug',
58
- event: 'COMMAND:STATIC:PAGE_CREATED',
59
- message: `path: ${path}, message: page created successfully`,
80
+ response = await appFetch(configManager, pathname, {
81
+ query,
82
+ headers: mergedHeaders,
83
+ // we need to save raw redirect response status code and headers
84
+ redirect: 'manual',
60
85
  });
61
- } catch (e) {
62
- const responseHeaders = response ? getHeaders(response) : {};
63
86
 
64
- if (responseHeaders['x-tramvai-prerender-skip'] === 'true') {
87
+ if (response?.headers?.get('x-tramvai-prerender-skip') === 'true') {
65
88
  context.logger.event({
66
89
  type: 'debug',
67
90
  event: 'COMMAND:STATIC:PAGE_SKIP',
68
- message: `path: ${path}, message: page prerender skipped`,
91
+ message: `path: ${pathname}, message: page prerender skipped`,
92
+ payload: {
93
+ headers: mergedHeaders,
94
+ query,
95
+ },
69
96
  });
70
97
 
71
98
  return;
72
99
  }
73
100
 
101
+ const keyHeader = response.headers.get('x-tramvai-static-page-key');
102
+ const routeHeader = response.headers.get('x-tramvai-static-page-route');
103
+ const filename = keyHeader ? `index.${keyHeader}.html` : 'index.html';
104
+ const metaFilename = keyHeader ? `index.${keyHeader}.json` : 'index.json';
105
+ const outputPath = join(staticPath, pathname, filename);
106
+ const metaOutputPath = join(staticPath, pathname, metaFilename);
107
+
108
+ if (typeof routeHeader === 'string') {
109
+ const route = JSON.parse(routeHeader);
110
+
111
+ if (
112
+ !meta.staticPages.some((item) => item.name === route.name || item.path === route.path)
113
+ ) {
114
+ meta.staticPages.push(route);
115
+ }
116
+ }
117
+
118
+ await outputFile(outputPath, await response.text());
119
+ await outputFile(
120
+ metaOutputPath,
121
+ JSON.stringify(
122
+ {
123
+ // @ts-expect-error
124
+ headers: Object.fromEntries(response.headers),
125
+ status: response.status,
126
+ },
127
+ null,
128
+ 2
129
+ )
130
+ );
131
+
132
+ context.logger.event({
133
+ type: 'debug',
134
+ event: 'COMMAND:STATIC:PAGE_CREATED',
135
+ message: `path: ${pathname}${keyHeader ? `, key: ${keyHeader}` : ''}, message: page created successfully at ${outputPath}`,
136
+ payload: {
137
+ headers: mergedHeaders,
138
+ query,
139
+ },
140
+ });
141
+ } catch (e) {
74
142
  context.logger.event({
75
143
  type: 'error',
76
144
  event: 'COMMAND:STATIC:ERROR',
77
- message: `path: ${path}, message: ${e.message}`,
145
+ message: `path: ${pathname}, message: ${e.message}`,
146
+ payload: {
147
+ headers: mergedHeaders,
148
+ query,
149
+ },
78
150
  });
79
151
  }
80
152
  })
81
153
  );
82
154
  }
83
155
 
84
- return Promise.all(promises);
156
+ return Promise.all(promises).then(() => {
157
+ return outputFile(join(staticPath, 'meta.json'), JSON.stringify(meta, null, 2));
158
+ });
85
159
  };
@@ -58,6 +58,8 @@ export default async (
58
58
 
59
59
  await context.packageManager.install({ stdio: 'inherit' });
60
60
 
61
+ // todo если с --tag то для npm надо запустить `npm update --no-save`
62
+
61
63
  if (context.packageManager.name !== 'npm') {
62
64
  // npm dedupe is extremely slow in most cases
63
65
  // so execute it only for yarn
@@ -5,6 +5,7 @@ import prop from '@tinkoff/utils/object/prop';
5
5
  import mapObj from '@tinkoff/utils/object/map';
6
6
  import { resolve } from 'path';
7
7
  import fs from 'fs';
8
+ import { nanoid } from 'nanoid';
8
9
  import type { BuildType } from '../typings/projectType';
9
10
  import type { Env } from '../typings/Env';
10
11
  import type { ConfigEntry, OverridableOption } from '../typings/configEntry/common';
@@ -25,6 +26,7 @@ export interface Settings<E extends Env> {
25
26
  rootDir?: string;
26
27
  version?: string;
27
28
  buildType?: BuildType;
29
+ buildId?: string;
28
30
  debug?: string | boolean;
29
31
  verboseWebpack?: boolean;
30
32
  trace?: boolean;
@@ -91,8 +93,10 @@ export type ConfigManager<
91
93
  E extends Env = Env,
92
94
  > = OmitOverridable<C> &
93
95
  Required<Settings<E>> & {
96
+ buildId: string;
94
97
  target: Target;
95
98
  buildPath: string;
99
+ prerenderPagesPath?: string;
96
100
  withSettings(settings: Settings<E>): ConfigManager<C, E>;
97
101
  dehydrate(): [C, Settings<E>];
98
102
  assetsPrefix?: string;
@@ -106,6 +110,7 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
106
110
  configEntry: C,
107
111
  settings: Settings<E>
108
112
  ): ConfigManager<C, E> => {
113
+ const buildId = settings.buildId ?? nanoid();
109
114
  const env: E = settings.env ?? ('development' as E);
110
115
  const normalizedConfigEntry = omitEnvOptions(env, configEntry);
111
116
 
@@ -122,6 +127,7 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
122
127
  }
123
128
 
124
129
  const config: ConfigManager<C, E> = {
130
+ buildId,
125
131
  ...normalizedConfigEntry,
126
132
  version:
127
133
  (type === 'module' ? moduleVersion(configEntry) : '') ||
@@ -179,6 +185,7 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
179
185
  return [
180
186
  configEntry,
181
187
  {
188
+ buildId,
182
189
  ...settings,
183
190
  // drop options that couldn't be serialized
184
191
  stdout: undefined,
@@ -199,6 +206,10 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
199
206
  rootDir,
200
207
  buildType === 'server' ? config.output.server : config.output.client
201
208
  );
209
+ config.prerenderPagesPath =
210
+ env === 'development'
211
+ ? resolve(rootDir, config.output.static, config.buildId)
212
+ : resolve(rootDir, config.output.static);
202
213
 
203
214
  const pwa = config.experiments?.pwa;
204
215
  if (pwa.webmanifest?.enabled) {
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import type Config from 'webpack-chain';
2
3
  import type { ConfigManager } from '../../../config/configManager';
3
4
  import { isApplication } from '../../../config/validate';
@@ -5,12 +6,13 @@ import type { ConfigEntry } from '../../../typings/configEntry/common';
5
6
  import { shouldUseReactRoot } from '../../../utils/shouldUseReactRoot';
6
7
 
7
8
  export const configToEnv = (configManager: ConfigManager<ConfigEntry>) => (config: Config) => {
8
- const { fileSystemPages, shared, experiments } = isApplication(configManager)
9
+ const { fileSystemPages, shared, experiments, output } = isApplication(configManager)
9
10
  ? configManager
10
11
  : {
11
12
  fileSystemPages: { enabled: false, routesDir: false, pagesDir: false },
12
13
  shared: { criticalChunks: [] },
13
14
  experiments: {},
15
+ output: { static: 'dist/client' },
14
16
  };
15
17
 
16
18
  config.plugin('define').tap((args) => [
@@ -31,6 +33,7 @@ export const configToEnv = (configManager: ConfigManager<ConfigEntry>) => (confi
31
33
  'process.env.__TRAMVAI_REACT_TRANSITIONS': `'${JSON.stringify(
32
34
  experiments.reactTransitions
33
35
  )}'`,
36
+ 'process.env.__TRAMVAI_OUTPUT_STATIC': `${JSON.stringify(configManager.env === 'development' ? path.join(output.static, configManager.buildId) : output.static)}`,
34
37
  },
35
38
  ]);
36
39
  };
@@ -14,6 +14,22 @@ export const appRequest = <T>(configManager: ConfigManager, path: string, option
14
14
  return request<T>({ url: `${serverPath}${path}`, ...options });
15
15
  };
16
16
 
17
+ export const appFetch = (
18
+ configManager: ConfigManager,
19
+ path: string,
20
+ { query = {}, ...options }: RequestInit & { query?: Record<string, string> } = {}
21
+ ) => {
22
+ const { host, port } = configManager;
23
+ const serverPath = `http://${host}:${port}/`;
24
+ const url = new URL(path, serverPath);
25
+
26
+ Object.entries(query).forEach(([key, value]) => {
27
+ url.searchParams.append(key, value);
28
+ });
29
+
30
+ return fetch(url, options);
31
+ };
32
+
17
33
  interface BundleInfoResponse {
18
34
  resultCode: string;
19
35
  payload: string[];
@@ -3,6 +3,7 @@
3
3
  // actual version to update will be calculated from the some of the @tramvai/module
4
4
  export const DEPENDANT_LIBS_MAP = new Map([
5
5
  ['@tinkoff/logger', '@tramvai/module-log'],
6
+ ['@tinkoff/env-validators', '@tramvai/module-client-hints'],
6
7
  ['@tinkoff/dippy', '@tramvai/core'],
7
8
  ['@tinkoff/router', '@tramvai/module-router'],
8
9
  ['@tinkoff/url', '@tramvai/module-common'],