@gravity-ui/app-builder 0.14.0-beta.2 → 0.14.0-beta.4

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.
package/README.md CHANGED
@@ -192,8 +192,11 @@ With this `{rootDir}/src/ui/tsconfig.json`:
192
192
  - `forkTsCheker` (`false | ForkTsCheckerWebpackPluginOptions`) - config for ForkTsCheckerWebpackPlugin [more](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options). If `false`, ForkTsCheckerWebpackPlugin will be disabled.
193
193
  - `cache` (`boolean | FileCacheOptions | MemoryCacheOptions`) — Cache the generated webpack modules and chunks to improve build speed. [more](https://webpack.js.org/configuration/cache/)
194
194
  - `babelCacheDirectory` (`boolean | string`) — Set directory for babel-loader cache (`default: node_modules/.cache/babel-loader``)
195
- - `babel` (`(config: babel.TransformOptions, options: {configType: 'development' | 'production'}) => babel.TransformOptions | Promise<babel.TransformOptions>`) - Allow override the default babel transform options.
196
- - `webpack` (`(config: webpack.Configuration, options: {configType: 'development' | 'production'}) => webpack.Configuration | Promise<webpack.Configuration>`) - Allow override the default configuration.
195
+ - `babel` (`(config: babel.TransformOptions, options: {configType: 'development' | 'production'; isSsr: boolean}) => babel.TransformOptions | Promise<babel.TransformOptions>`) - Allow override the default babel transform options.
196
+ - `webpack` (`(config: webpack.Configuration, options: {configType: 'development' | 'production'; isSsr: boolean}) => webpack.Configuration | Promise<webpack.Configuration>`) - Allow override the default configuration.
197
+ - `ssr` - build SSR bundle. The SSR entries should be inside `src/ui/ssr` directory and match the client entries.
198
+ - `noExternal` (`string | RegExp | (string | RegExp)[] | true`) - prevent listed dependencies from being externalized for SSR. By default, all dependencies are externalized.
199
+ - `moduleType`: (`'commonjs' | 'esm'`) - library type for the SSR bundle, by default `commonjs`.
197
200
 
198
201
  ##### Dev build
199
202
 
@@ -40,9 +40,6 @@ const config_1 = require("../../common/webpack/config");
40
40
  async function watchClientCompilation(config, onManifestReady) {
41
41
  const clientCompilation = await buildWebpackServer(config);
42
42
  const compiler = clientCompilation.compiler;
43
- if ('compilers' in compiler) {
44
- throw new Error('Unexpected multi compiler');
45
- }
46
43
  subscribeToManifestReadyEvent(compiler, onManifestReady);
47
44
  return clientCompilation;
48
45
  }
@@ -50,7 +47,14 @@ async function buildWebpackServer(config) {
50
47
  const logger = new logger_1.Logger('webpack', config.verbose);
51
48
  const { webSocketPath = path.normalize(`/${config.client.publicPathPrefix}/build/sockjs-node`), writeToDisk, ...devServer } = config.client.devServer || {};
52
49
  const normalizedConfig = { ...config.client, devServer: { ...devServer, webSocketPath } };
53
- const webpackConfig = await (0, config_1.webpackConfigFactory)("development" /* WebpackMode.Dev */, normalizedConfig, { logger });
50
+ const webpackConfigs = [
51
+ await (0, config_1.webpackConfigFactory)("development" /* WebpackMode.Dev */, normalizedConfig, { logger }),
52
+ ];
53
+ const isSsr = Boolean(normalizedConfig.ssr);
54
+ if (isSsr) {
55
+ const logger = new logger_1.Logger('webpack(SSR)', config.verbose);
56
+ webpackConfigs.push(await (0, config_1.webpackConfigFactory)("development" /* WebpackMode.Dev */, normalizedConfig, { logger, isSsr }));
57
+ }
54
58
  const publicPath = path.normalize(config.client.publicPathPrefix + '/build/');
55
59
  const staticFolder = path.resolve(paths_1.default.appDist, 'public');
56
60
  const options = {
@@ -58,7 +62,18 @@ async function buildWebpackServer(config) {
58
62
  devMiddleware: {
59
63
  publicPath,
60
64
  stats: 'errors-warnings',
61
- writeToDisk,
65
+ writeToDisk: (target) => {
66
+ if (writeToDisk === true) {
67
+ return true;
68
+ }
69
+ if (isSsr && target.startsWith(paths_1.default.appSsrBuild)) {
70
+ return true;
71
+ }
72
+ if (typeof writeToDisk === 'function') {
73
+ return writeToDisk(target);
74
+ }
75
+ return false;
76
+ },
62
77
  },
63
78
  liveReload: false,
64
79
  hot: true,
@@ -116,7 +131,7 @@ async function buildWebpackServer(config) {
116
131
  });
117
132
  }
118
133
  options.proxy = proxy;
119
- const compiler = (0, webpack_1.default)(webpackConfig);
134
+ const compiler = (0, webpack_1.default)(webpackConfigs);
120
135
  const server = new webpack_dev_server_1.default(options, compiler);
121
136
  try {
122
137
  await server.start();
@@ -129,17 +144,28 @@ async function buildWebpackServer(config) {
129
144
  }
130
145
  return server;
131
146
  }
132
- function subscribeToManifestReadyEvent(compiler, onManifestReady) {
147
+ function subscribeToManifestReadyEvent(webpackCompiler, onManifestReady) {
133
148
  const promises = [];
134
- const assetsManifestPlugin = compiler.options.plugins.find((plugin) => plugin instanceof webpack_assets_manifest_1.default);
135
- if (assetsManifestPlugin) {
136
- const assetsManifestReady = (0, utils_1.deferredPromise)();
137
- promises.push(assetsManifestReady.promise);
138
- assetsManifestPlugin.hooks.done.tap('app-builder', assetsManifestReady.resolve);
149
+ const options = Array.isArray(webpackCompiler.options)
150
+ ? webpackCompiler.options
151
+ : [webpackCompiler.options];
152
+ const compilers = 'compilers' in webpackCompiler ? webpackCompiler.compilers : [webpackCompiler];
153
+ for (let i = 0; i < options.length; i++) {
154
+ const config = options[i];
155
+ const compiler = compilers[i];
156
+ if (!config || !compiler) {
157
+ throw new Error('Something goes wrong!');
158
+ }
159
+ const assetsManifestPlugin = config.plugins.find((plugin) => plugin instanceof webpack_assets_manifest_1.default);
160
+ if (assetsManifestPlugin) {
161
+ const assetsManifestReady = (0, utils_1.deferredPromise)();
162
+ promises.push(assetsManifestReady.promise);
163
+ assetsManifestPlugin.hooks.done.tap('app-builder', assetsManifestReady.resolve);
164
+ }
165
+ const manifestReady = (0, utils_1.deferredPromise)();
166
+ promises.push(manifestReady.promise);
167
+ const { afterEmit } = (0, webpack_manifest_plugin_1.getCompilerHooks)(compiler);
168
+ afterEmit.tap('app-builder', manifestReady.resolve);
139
169
  }
140
- const manifestReady = (0, utils_1.deferredPromise)();
141
- promises.push(manifestReady.promise);
142
- const { afterEmit } = (0, webpack_manifest_plugin_1.getCompilerHooks)(compiler);
143
- afterEmit.tap('app-builder', manifestReady.resolve);
144
170
  Promise.all(promises).then(() => onManifestReady());
145
171
  }
@@ -56,7 +56,7 @@ async function default_1(config) {
56
56
  script: `${serverPath}/index.js`,
57
57
  args: ['--dev', config.server.port ? `--port=${config.server.port}` : ''],
58
58
  env: {
59
- ...(config.server.port ? { APP_PORT: config.server.port } : undefined),
59
+ ...(config.server.port ? { APP_PORT: `${config.server.port}` } : undefined),
60
60
  },
61
61
  nodeArgs: inspect || inspectBrk
62
62
  ? [`--${inspect ? 'inspect' : 'inspect-brk'}=:::${inspect || inspectBrk}`]
@@ -1,9 +1,13 @@
1
1
  export declare function babelPreset(config: {
2
2
  newJsxTransform?: boolean;
3
+ isSsr?: boolean;
3
4
  }): (string | {
4
5
  env: {
5
6
  modules: boolean;
6
7
  bugfixes: boolean;
8
+ targets: {
9
+ node: string;
10
+ } | undefined;
7
11
  };
8
12
  runtime: {
9
13
  version: string;
@@ -5,7 +5,11 @@ function babelPreset(config) {
5
5
  return [
6
6
  require.resolve('./ui-preset'),
7
7
  {
8
- env: { modules: false, bugfixes: true },
8
+ env: {
9
+ modules: false,
10
+ bugfixes: true,
11
+ targets: config.isSsr ? { node: 'current' } : undefined,
12
+ },
9
13
  runtime: { version: '^7.13.10' },
10
14
  typescript: true,
11
15
  react: {
@@ -180,17 +180,23 @@ export interface ClientConfig {
180
180
  */
181
181
  webpack?: (config: Configuration, options: {
182
182
  configType: `${WebpackMode}`;
183
+ isSsr?: boolean;
183
184
  }) => Configuration | Promise<Configuration>;
184
185
  /**
185
186
  * Modify or return a custom Babel config.
186
187
  */
187
188
  babel?: (config: Babel.TransformOptions, options: {
188
189
  configType: `${WebpackMode}`;
190
+ isSsr: boolean;
189
191
  }) => Babel.TransformOptions | Promise<Babel.TransformOptions>;
190
192
  /**
191
193
  * Modify or return a custom [Terser options](https://github.com/terser/terser#minify-options).
192
194
  */
193
195
  terser?: (options: TerserOptions) => TerserOptions;
196
+ ssr?: {
197
+ noExternal?: string | RegExp | (string | RegExp)[] | true;
198
+ moduleType?: 'commonjs' | 'esm';
199
+ };
194
200
  }
195
201
  export interface CdnUploadConfig {
196
202
  bucket: string;
@@ -230,10 +236,12 @@ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'hi
230
236
  verbose?: boolean;
231
237
  webpack: (config: Configuration, options: {
232
238
  configType: `${WebpackMode}`;
239
+ isSsr: boolean;
233
240
  }) => Configuration | Promise<Configuration>;
234
241
  debugWebpack?: boolean;
235
242
  babel: (config: Babel.TransformOptions, options: {
236
243
  configType: `${WebpackMode}`;
244
+ isSsr: boolean;
237
245
  }) => Babel.TransformOptions | Promise<Babel.TransformOptions>;
238
246
  reactRefresh: NonNullable<ClientConfig['reactRefresh']>;
239
247
  };
@@ -7,6 +7,8 @@ declare const _default: {
7
7
  appDist: string;
8
8
  appRun: string;
9
9
  appBuild: string;
10
+ appSsrEntry: string;
11
+ appSsrBuild: string;
10
12
  src: string;
11
13
  libBuild: string;
12
14
  libBuildEsm: string;
@@ -36,6 +36,8 @@ exports.default = {
36
36
  appDist: resolveApp('dist'),
37
37
  appRun: resolveApp('dist/run'),
38
38
  appBuild: resolveApp('dist/public/build'),
39
+ appSsrEntry: resolveApp('src/ui/ssr'),
40
+ appSsrBuild: resolveApp('dist/ssr'),
39
41
  src: resolveApp('src'),
40
42
  libBuild: resolveApp('build'),
41
43
  libBuildEsm: resolveApp('build/esm'),
@@ -10,24 +10,11 @@ function compile(ts, { projectPath, configFileName = 'tsconfig.json', optionsToE
10
10
  logger.message('Start compilation');
11
11
  logger.message(`Typescript v${ts.version}`);
12
12
  logger.verbose(`Searching for the ${configFileName} in ${projectPath}`);
13
- const configPath = (0, utils_1.getProjectConfig)(ts, projectPath, configFileName);
14
- const formatHost = {
15
- getCanonicalFileName: (path) => path,
16
- getCurrentDirectory: ts.sys.getCurrentDirectory,
17
- getNewLine: () => ts.sys.newLine,
18
- };
19
- const parseConfigFileHost = {
20
- getCurrentDirectory: ts.sys.getCurrentDirectory,
21
- useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
22
- readDirectory: ts.sys.readDirectory,
23
- fileExists: ts.sys.fileExists,
24
- readFile: ts.sys.readFile,
25
- onUnRecoverableConfigFileDiagnostic: reportDiagnostic,
26
- };
27
- const parsedConfig = ts.getParsedCommandLineOfConfigFile(configPath, { noEmit: false, noEmitOnError: true, ...optionsToExtend }, parseConfigFileHost);
28
- if (!parsedConfig) {
29
- throw new Error(`Invalid '${configFileName}'`);
30
- }
13
+ const parsedConfig = (0, utils_1.getTsProjectConfig)(ts, projectPath, configFileName, {
14
+ noEmit: false,
15
+ noEmitOnError: true,
16
+ ...optionsToExtend,
17
+ });
31
18
  logger.verbose('Config found and parsed');
32
19
  logger.verbose("We're about to create the program");
33
20
  const compilerHost = ts.createCompilerHost(parsedConfig.options);
@@ -57,6 +44,11 @@ function compile(ts, { projectPath, configFileName = 'tsconfig.json', optionsToE
57
44
  else {
58
45
  logger.success(`Compiled successfully in ${(0, pretty_time_1.elapsedTime)(start)}`);
59
46
  }
47
+ const formatHost = {
48
+ getCanonicalFileName: (path) => path,
49
+ getCurrentDirectory: ts.sys.getCurrentDirectory,
50
+ getNewLine: () => ts.sys.newLine,
51
+ };
60
52
  function reportDiagnostic(diagnostic) {
61
53
  if (logger.isVerbose) {
62
54
  logger.message(ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost));
@@ -1,6 +1,7 @@
1
1
  import type Typescript from 'typescript';
2
2
  import type { Logger } from '../logger';
3
- export declare function getProjectConfig(ts: typeof Typescript, projectPath: string, filename?: string): string;
3
+ export declare function getTsProjectConfigPath(ts: typeof Typescript, projectPath: string, filename?: string): string;
4
+ export declare function getTsProjectConfig(ts: typeof Typescript, projectPath: string, filename?: string, optionsToExtend?: Typescript.CompilerOptions): Typescript.ParsedCommandLine;
4
5
  export declare function displayFilename(originalFunc: (path: string, encoding?: string) => string | undefined, operationName: string, logger: Logger): {
5
6
  (path: string, encoding?: string | undefined): string | undefined;
6
7
  originalFunc: (path: string, encoding?: string) => string | undefined;
@@ -3,19 +3,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getProjectConfig = getProjectConfig;
6
+ exports.getTsProjectConfigPath = getTsProjectConfigPath;
7
+ exports.getTsProjectConfig = getTsProjectConfig;
7
8
  exports.displayFilename = displayFilename;
8
9
  exports.onHostEvent = onHostEvent;
9
10
  exports.resolveTypescript = resolveTypescript;
10
11
  const node_path_1 = __importDefault(require("node:path"));
11
12
  const paths_1 = __importDefault(require("../paths"));
12
- function getProjectConfig(ts, projectPath, filename = 'tsconfig.json') {
13
+ function getTsProjectConfigPath(ts, projectPath, filename = 'tsconfig.json') {
13
14
  const configPath = ts.findConfigFile(projectPath, ts.sys.fileExists, filename);
14
15
  if (!configPath) {
15
16
  throw new Error(`Could not find a valid '${filename}'.`);
16
17
  }
17
18
  return configPath;
18
19
  }
20
+ function getTsProjectConfig(ts, projectPath, filename = 'tsconfig.json', optionsToExtend) {
21
+ const configPath = getTsProjectConfigPath(ts, projectPath, filename);
22
+ const parseConfigFileHost = {
23
+ getCurrentDirectory: ts.sys.getCurrentDirectory,
24
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
25
+ readDirectory: ts.sys.readDirectory,
26
+ fileExists: ts.sys.fileExists,
27
+ readFile: ts.sys.readFile,
28
+ // this is required in types but not used
29
+ onUnRecoverableConfigFileDiagnostic: () => { },
30
+ };
31
+ const parsedConfig = ts.getParsedCommandLineOfConfigFile(configPath, optionsToExtend, parseConfigFileHost);
32
+ if (!parsedConfig) {
33
+ throw new Error(`Invalid config file '${configPath}'`);
34
+ }
35
+ return parsedConfig;
36
+ }
19
37
  function displayFilename(originalFunc, operationName, logger) {
20
38
  let displayEnabled = false;
21
39
  let count = 0;
@@ -7,7 +7,7 @@ const diagnostic_1 = require("./diagnostic");
7
7
  function watch(ts, projectPath, { logger, onAfterFilesEmitted, enableSourceMap, }) {
8
8
  logger.message('Start compilation in watch mode');
9
9
  logger.message(`Typescript v${ts.version}`);
10
- const configPath = (0, utils_1.getProjectConfig)(ts, projectPath);
10
+ const configPath = (0, utils_1.getTsProjectConfigPath)(ts, projectPath);
11
11
  const createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram;
12
12
  const formatHost = {
13
13
  getCanonicalFileName: (path) => path,
@@ -10,10 +10,15 @@ const config_1 = require("./config");
10
10
  const utils_1 = require("./utils");
11
11
  async function webpackCompile(config) {
12
12
  const logger = new logger_1.Logger('webpack', config.verbose);
13
- const webpackConfig = await (0, config_1.webpackConfigFactory)("production" /* WebpackMode.Prod */, config, { logger });
13
+ const webpackConfigs = [await (0, config_1.webpackConfigFactory)("production" /* WebpackMode.Prod */, config, { logger })];
14
+ const isSsr = Boolean(config.ssr);
15
+ if (isSsr) {
16
+ const logger = new logger_1.Logger('webpack(SSR)', config.verbose);
17
+ webpackConfigs.push(await (0, config_1.webpackConfigFactory)("production" /* WebpackMode.Prod */, config, { logger, isSsr }));
18
+ }
14
19
  logger.verbose('Config created');
15
20
  return new Promise((resolve) => {
16
- const compiler = (0, webpack_1.default)(webpackConfig, (0, utils_1.webpackCompilerHandlerFactory)(logger, async () => {
21
+ const compiler = (0, webpack_1.default)(webpackConfigs, (0, utils_1.webpackCompilerHandlerFactory)(logger, async () => {
17
22
  resolve();
18
23
  }));
19
24
  process.on('SIGINT', async () => {
@@ -7,16 +7,20 @@ export interface HelperOptions {
7
7
  isEnvDevelopment: boolean;
8
8
  isEnvProduction: boolean;
9
9
  configType: `${WebpackMode}`;
10
+ buildDirectory: string;
11
+ entriesDirectory: string;
12
+ isSsr: boolean;
10
13
  }
11
14
  export declare const enum WebpackMode {
12
15
  Prod = "production",
13
16
  Dev = "development"
14
17
  }
15
- export declare function webpackConfigFactory(webpackMode: WebpackMode, config: NormalizedClientConfig, { logger }?: {
18
+ export declare function webpackConfigFactory(webpackMode: WebpackMode, config: NormalizedClientConfig, { logger, isSsr }?: {
16
19
  logger?: Logger;
20
+ isSsr?: boolean;
17
21
  }): Promise<webpack.Configuration>;
18
22
  export declare function configureModuleRules(helperOptions: HelperOptions, additionalRules?: NonNullable<webpack.RuleSetRule['oneOf']>): webpack.RuleSetRule[];
19
23
  export declare function configureResolve({ isEnvProduction, config }: HelperOptions): webpack.ResolveOptions;
20
24
  type Optimization = NonNullable<webpack.Configuration['optimization']>;
21
- export declare function configureOptimization({ config }: HelperOptions): Optimization;
25
+ export declare function configureOptimization({ config, isSsr }: HelperOptions): Optimization;
22
26
  export {};
@@ -44,6 +44,7 @@ const react_refresh_webpack_plugin_1 = __importDefault(require("@pmmmwh/react-re
44
44
  const moment_timezone_data_webpack_plugin_1 = __importDefault(require("moment-timezone-data-webpack-plugin"));
45
45
  const webpack_plugin_1 = __importDefault(require("@statoscope/webpack-plugin"));
46
46
  const circular_dependency_plugin_1 = __importDefault(require("circular-dependency-plugin"));
47
+ const webpack_node_externals_1 = __importDefault(require("webpack-node-externals"));
47
48
  const paths_1 = __importDefault(require("../paths"));
48
49
  const babel_1 = require("../babel");
49
50
  const progress_plugin_1 = require("./progress-plugin");
@@ -53,7 +54,7 @@ const log_config_1 = require("../logger/log-config");
53
54
  const utils_2 = require("../typescript/utils");
54
55
  const imagesSizeLimit = 2048;
55
56
  const fontSizeLimit = 8192;
56
- async function webpackConfigFactory(webpackMode, config, { logger } = {}) {
57
+ async function webpackConfigFactory(webpackMode, config, { logger, isSsr = false } = {}) {
57
58
  const isEnvDevelopment = webpackMode === "development" /* WebpackMode.Dev */;
58
59
  const isEnvProduction = webpackMode === "production" /* WebpackMode.Prod */;
59
60
  const helperOptions = {
@@ -62,11 +63,25 @@ async function webpackConfigFactory(webpackMode, config, { logger } = {}) {
62
63
  isEnvDevelopment,
63
64
  isEnvProduction,
64
65
  configType: webpackMode,
66
+ buildDirectory: isSsr ? paths_1.default.appSsrBuild : paths_1.default.appBuild,
67
+ entriesDirectory: isSsr ? paths_1.default.appSsrEntry : paths_1.default.appEntry,
68
+ isSsr,
65
69
  };
70
+ let externals = config.externals;
71
+ if (isSsr) {
72
+ externals =
73
+ config.ssr?.noExternal === true
74
+ ? undefined
75
+ : (0, webpack_node_externals_1.default)({
76
+ allowlist: config.ssr?.noExternal,
77
+ importType: config.ssr?.moduleType === 'esm' ? 'module' : 'commonjs',
78
+ });
79
+ }
66
80
  let webpackConfig = {
67
81
  mode: webpackMode,
68
82
  context: paths_1.default.app,
69
83
  bail: isEnvProduction,
84
+ target: isSsr ? 'node' : undefined,
70
85
  devtool: configureDevTool(helperOptions),
71
86
  entry: configureEntry(helperOptions),
72
87
  output: configureOutput(helperOptions),
@@ -76,7 +91,7 @@ async function webpackConfigFactory(webpackMode, config, { logger } = {}) {
76
91
  },
77
92
  plugins: configurePlugins(helperOptions),
78
93
  optimization: configureOptimization(helperOptions),
79
- externals: config.externals,
94
+ externals,
80
95
  node: config.node,
81
96
  watchOptions: configureWatchOptions(helperOptions),
82
97
  ignoreWarnings: [/Failed to parse source map/],
@@ -92,7 +107,7 @@ async function webpackConfigFactory(webpackMode, config, { logger } = {}) {
92
107
  },
93
108
  cache: config.cache,
94
109
  };
95
- webpackConfig = await config.webpack(webpackConfig, { configType: webpackMode });
110
+ webpackConfig = await config.webpack(webpackConfig, { configType: webpackMode, isSsr });
96
111
  if (config.debugWebpack) {
97
112
  (0, log_config_1.logConfig)('Preview webpack config', webpackConfig);
98
113
  }
@@ -135,7 +150,10 @@ function configureWatchOptions({ config }) {
135
150
  delete watchOptions.watchPackages;
136
151
  return watchOptions;
137
152
  }
138
- function configureExperiments({ config, isEnvProduction, }) {
153
+ function configureExperiments({ config, isEnvProduction, isSsr, }) {
154
+ if (isSsr) {
155
+ return config.ssr?.moduleType === 'esm' ? { outputModule: true } : undefined;
156
+ }
139
157
  if (isEnvProduction) {
140
158
  return undefined;
141
159
  }
@@ -174,7 +192,7 @@ function configureResolve({ isEnvProduction, config }) {
174
192
  alias['react-dom$'] = 'react-dom/profiling';
175
193
  alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
176
194
  }
177
- const { aliases, modules = [] } = (0, utils_1.resolveTsconfigPathsToAlias)(path.resolve(paths_1.default.appClient, 'tsconfig.json')) || {};
195
+ const { aliases, modules = [] } = (0, utils_1.resolveTsConfigPathsToAlias)(paths_1.default.appClient);
178
196
  return {
179
197
  alias: {
180
198
  ...aliases,
@@ -190,61 +208,74 @@ function createEntryArray(entry) {
190
208
  return [require.resolve('./public-path'), entry];
191
209
  }
192
210
  function addEntry(entry, file) {
193
- const newEntry = path.resolve(paths_1.default.appEntry, file);
194
211
  return {
195
212
  ...entry,
196
- [path.parse(file).name]: createEntryArray(newEntry),
213
+ [path.parse(file).name]: createEntryArray(file),
197
214
  };
198
215
  }
199
- function configureEntry({ config }) {
200
- let entries = fs.readdirSync(paths_1.default.appEntry).filter((file) => /\.[jt]sx?$/.test(file));
216
+ function configureEntry({ config, entriesDirectory }) {
217
+ let entries = fs.readdirSync(entriesDirectory).filter((file) => /\.[jt]sx?$/.test(file));
201
218
  if (Array.isArray(config.entryFilter) && config.entryFilter.length) {
202
219
  entries = entries.filter((entry) => config.entryFilter?.includes(entry.split('.')[0] ?? ''));
203
220
  }
204
221
  if (!entries.length) {
205
- throw new Error('No entries were found after applying UI_CORE_ENTRY_FILTER');
222
+ throw new Error('No entries were found after applying entry filter');
206
223
  }
207
- return entries.reduce((entry, file) => addEntry(entry, file), {});
224
+ return entries.reduce((entry, file) => addEntry(entry, path.resolve(entriesDirectory, file)), {});
208
225
  }
209
- function getFileNames({ isEnvProduction }) {
226
+ function getFileNames({ isEnvProduction, isSsr, config }) {
227
+ let ext = 'js';
228
+ if (isSsr) {
229
+ ext = config.ssr?.moduleType === 'esm' ? 'mjs' : 'cjs';
230
+ }
210
231
  return {
211
- filename: isEnvProduction ? 'js/[name].[contenthash:8].js' : 'js/[name].js',
232
+ filename: isEnvProduction ? `js/[name].[contenthash:8].${ext}` : `js/[name].${ext}`,
212
233
  chunkFilename: isEnvProduction
213
234
  ? 'js/[name].[contenthash:8].chunk.js'
214
235
  : 'js/[name].chunk.js',
215
236
  };
216
237
  }
217
- function configureOutput({ isEnvDevelopment, ...rest }) {
238
+ function configureOutput(options) {
239
+ let ssrOptions;
240
+ if (options.isSsr) {
241
+ ssrOptions = {
242
+ library: { type: options.config.ssr?.moduleType === 'esm' ? 'module' : 'commonjs2' },
243
+ chunkFormat: false,
244
+ };
245
+ }
218
246
  return {
219
- ...getFileNames({ isEnvDevelopment, ...rest }),
220
- path: paths_1.default.appBuild,
221
- pathinfo: isEnvDevelopment,
247
+ ...getFileNames(options),
248
+ path: options.buildDirectory,
249
+ pathinfo: options.isEnvDevelopment,
250
+ ...ssrOptions,
222
251
  };
223
252
  }
224
- function createJavaScriptLoader({ isEnvProduction, isEnvDevelopment, configType, config, }) {
253
+ function createJavaScriptLoader({ isEnvProduction, isEnvDevelopment, configType, config, isSsr, }) {
225
254
  const plugins = [];
226
- if (isEnvDevelopment && config.reactRefresh !== false) {
227
- plugins.push([
228
- require.resolve('react-refresh/babel'),
229
- config.devServer?.webSocketPath
230
- ? {
231
- overlay: {
232
- sockPath: config.devServer.webSocketPath,
233
- },
234
- }
235
- : undefined,
236
- ]);
237
- }
238
- if (isEnvProduction) {
239
- plugins.push([
240
- require.resolve('babel-plugin-import'),
241
- { libraryName: 'lodash', libraryDirectory: '', camel2DashComponentName: false },
242
- ]);
255
+ if (!isSsr) {
256
+ if (isEnvDevelopment && config.reactRefresh !== false) {
257
+ plugins.push([
258
+ require.resolve('react-refresh/babel'),
259
+ config.devServer?.webSocketPath
260
+ ? {
261
+ overlay: {
262
+ sockPath: config.devServer.webSocketPath,
263
+ },
264
+ }
265
+ : undefined,
266
+ ]);
267
+ }
268
+ if (isEnvProduction) {
269
+ plugins.push([
270
+ require.resolve('babel-plugin-import'),
271
+ { libraryName: 'lodash', libraryDirectory: '', camel2DashComponentName: false },
272
+ ]);
273
+ }
243
274
  }
244
275
  const transformOptions = config.babel({
245
- presets: [(0, babel_1.babelPreset)(config)],
276
+ presets: [(0, babel_1.babelPreset)({ newJsxTransform: config.newJsxTransform, isSsr })],
246
277
  plugins,
247
- }, { configType });
278
+ }, { configType, isSsr });
248
279
  return {
249
280
  loader: require.resolve('babel-loader'),
250
281
  options: {
@@ -339,7 +370,7 @@ function createStylesRule(options) {
339
370
  use: loaders,
340
371
  };
341
372
  }
342
- function getCssLoaders({ isEnvDevelopment, isEnvProduction, config }, additionalRules) {
373
+ function getCssLoaders({ isEnvDevelopment, isEnvProduction, config, isSsr }, additionalRules) {
343
374
  const loaders = [];
344
375
  if (!config.transformCssWithLightningCss) {
345
376
  loaders.push({
@@ -362,27 +393,38 @@ function getCssLoaders({ isEnvDevelopment, isEnvProduction, config }, additional
362
393
  loaders.unshift({
363
394
  loader: require.resolve('css-loader'),
364
395
  options: {
365
- esModule: false,
396
+ url: {
397
+ filter: (url) => {
398
+ // ignore data uri
399
+ return !url.startsWith('data:');
400
+ },
401
+ },
366
402
  sourceMap: !config.disableSourceMapGeneration,
367
403
  importLoaders,
368
404
  modules: {
369
405
  auto: true,
370
406
  localIdentName: '[name]__[local]--[hash:base64:5]',
371
407
  exportLocalsConvention: 'camelCase',
408
+ exportOnlyLocals: isSsr,
372
409
  },
373
410
  },
374
411
  });
375
412
  if (isEnvProduction) {
376
- loaders.unshift(mini_css_extract_plugin_1.default.loader);
413
+ loaders.unshift({ loader: mini_css_extract_plugin_1.default.loader, options: { emit: !isSsr } });
377
414
  }
378
415
  if (isEnvDevelopment) {
379
- loaders.unshift({
380
- loader: require.resolve('style-loader'),
381
- });
416
+ if (isSsr || config.ssr) {
417
+ loaders.unshift({ loader: mini_css_extract_plugin_1.default.loader, options: { emit: !isSsr } });
418
+ }
419
+ else {
420
+ loaders.unshift({
421
+ loader: require.resolve('style-loader'),
422
+ });
423
+ }
382
424
  }
383
425
  return loaders;
384
426
  }
385
- function createIconsRule({ isEnvProduction, config }, jsLoader) {
427
+ function createIconsRule({ isEnvProduction, config, isSsr }, jsLoader) {
386
428
  const iconIncludes = config.icons || [];
387
429
  return {
388
430
  // eslint-disable-next-line security/detect-unsafe-regex
@@ -419,11 +461,12 @@ function createIconsRule({ isEnvProduction, config }, jsLoader) {
419
461
  generator: {
420
462
  filename: 'assets/images/[name].[contenthash:8][ext]',
421
463
  publicPath: isEnvProduction ? '../' : undefined,
464
+ emit: !isSsr,
422
465
  },
423
466
  }),
424
467
  };
425
468
  }
426
- function createAssetsRules({ isEnvProduction, config }) {
469
+ function createAssetsRules({ isEnvProduction, config, isSsr }) {
427
470
  const imagesRule = {
428
471
  test: /\.(ico|bmp|gif|jpe?g|png|svg)$/,
429
472
  include: [paths_1.default.appClient, ...(config.images || [])],
@@ -435,6 +478,7 @@ function createAssetsRules({ isEnvProduction, config }) {
435
478
  },
436
479
  generator: {
437
480
  filename: 'assets/images/[name].[contenthash:8][ext]',
481
+ emit: !isSsr,
438
482
  },
439
483
  };
440
484
  const fontsRule = {
@@ -448,6 +492,7 @@ function createAssetsRules({ isEnvProduction, config }) {
448
492
  },
449
493
  generator: {
450
494
  filename: 'assets/fonts/[name].[contenthash:8][ext]',
495
+ emit: !isSsr,
451
496
  },
452
497
  };
453
498
  const rules = [imagesRule, fontsRule];
@@ -467,6 +512,7 @@ function createAssetsRules({ isEnvProduction, config }) {
467
512
  generator: {
468
513
  filename: 'assets/images/[name].[contenthash:8][ext]',
469
514
  publicPath: '../',
515
+ emit: !isSsr,
470
516
  },
471
517
  }, {
472
518
  test: /\.(ttf|eot|woff2?)$/,
@@ -481,17 +527,19 @@ function createAssetsRules({ isEnvProduction, config }) {
481
527
  generator: {
482
528
  filename: 'assets/fonts/[name].[contenthash:8][ext]',
483
529
  publicPath: '../',
530
+ emit: !isSsr,
484
531
  },
485
532
  });
486
533
  }
487
534
  return rules;
488
535
  }
489
- function createFallbackRules({ isEnvProduction }) {
536
+ function createFallbackRules({ isEnvProduction, isSsr }) {
490
537
  const rules = [
491
538
  {
492
539
  type: 'asset/resource',
493
540
  generator: {
494
541
  filename: 'assets/[name].[contenthash:8][ext]',
542
+ emit: !isSsr,
495
543
  },
496
544
  exclude: [/\.[jt]sx?$/, /\.json$/, /\.[cm]js$/, /\.ejs$/],
497
545
  },
@@ -506,6 +554,7 @@ function createFallbackRules({ isEnvProduction }) {
506
554
  generator: {
507
555
  filename: 'assets/[name].[contenthash:8][ext]',
508
556
  publicPath: '../',
557
+ emit: !isSsr,
509
558
  },
510
559
  });
511
560
  }
@@ -520,7 +569,7 @@ function createMomentTimezoneDataPlugin(options = {}) {
520
569
  return new moment_timezone_data_webpack_plugin_1.default({ ...options, startYear, endYear });
521
570
  }
522
571
  function configurePlugins(options) {
523
- const { isEnvDevelopment, isEnvProduction, config } = options;
572
+ const { isEnvDevelopment, isEnvProduction, config, isSsr } = options;
524
573
  const excludeFromClean = config.excludeFromClean || [];
525
574
  const manifestFile = 'assets-manifest.json';
526
575
  const plugins = [
@@ -544,11 +593,11 @@ function configurePlugins(options) {
544
593
  : {
545
594
  entrypoints: true,
546
595
  writeToDisk: true,
547
- output: path.resolve(paths_1.default.appBuild, manifestFile),
596
+ output: path.resolve(options.buildDirectory, manifestFile),
548
597
  }),
549
- createMomentTimezoneDataPlugin(config.momentTz),
550
598
  new webpack.DefinePlugin({
551
599
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
600
+ 'process.env.IS_SSR': JSON.stringify(isSsr),
552
601
  ...config.definitions,
553
602
  }),
554
603
  ];
@@ -558,51 +607,12 @@ function configurePlugins(options) {
558
607
  if (process.env.WEBPACK_PROFILE === 'true') {
559
608
  plugins.push(new webpack.debug.ProfilingPlugin());
560
609
  }
561
- const contextReplacement = config.contextReplacement || {};
562
- plugins.push(new webpack.ContextReplacementPlugin(/moment[\\/]locale$/,
563
- // eslint-disable-next-line security/detect-non-literal-regexp
564
- new RegExp(`^\\./(${(contextReplacement.locale || ['ru']).join('|')})$`)));
565
- plugins.push(new webpack.ContextReplacementPlugin(/dayjs[\\/]locale$/,
566
- // eslint-disable-next-line security/detect-non-literal-regexp
567
- new RegExp(`^\\./(${(contextReplacement.locale || ['ru']).join('|')})\\.js$`)));
568
- if (contextReplacement['highlight.js']) {
569
- plugins.push(new webpack.ContextReplacementPlugin(/highlight\.js[\\/]lib[\\/]languages$/,
570
- // eslint-disable-next-line security/detect-non-literal-regexp
571
- new RegExp(`^\\./(${contextReplacement['highlight.js'].join('|')})$`)));
572
- }
573
- if (config.monaco) {
574
- const MonacoEditorWebpackPlugin = require('monaco-editor-webpack-plugin');
575
- plugins.push(new MonacoEditorWebpackPlugin({
576
- filename: isEnvProduction ? '[name].[hash:8].worker.js' : undefined,
577
- ...config.monaco,
578
- // currently, workers located on cdn are not working properly, so we are enforcing loading workers from
579
- // service instead
580
- publicPath: path.normalize(config.publicPathPrefix + '/build/'),
581
- }));
582
- }
583
- if (isEnvDevelopment && config.reactRefresh !== false) {
584
- const { webSocketPath = path.normalize(`/${config.publicPathPrefix}/build/sockjs-node`) } = config.devServer || {};
585
- plugins.push(new react_refresh_webpack_plugin_1.default(config.reactRefresh({
586
- overlay: { sockPath: webSocketPath },
587
- exclude: [/node_modules/, /\.worker\.[jt]sx?$/],
588
- })));
589
- }
590
- if (config.detectCircularDependencies) {
591
- let circularPluginOptions = {
592
- exclude: /node_modules/,
593
- allowAsyncCycles: true,
594
- };
595
- if (typeof config.detectCircularDependencies === 'object') {
596
- circularPluginOptions = config.detectCircularDependencies;
597
- }
598
- plugins.push(new circular_dependency_plugin_1.default(circularPluginOptions));
599
- }
600
610
  if (config.forkTsChecker !== false) {
601
611
  plugins.push(new fork_ts_checker_webpack_plugin_1.default({
602
612
  ...config.forkTsChecker,
603
613
  typescript: {
604
614
  typescriptPath: (0, utils_2.resolveTypescript)(),
605
- configFile: path.resolve(paths_1.default.app, 'src/ui/tsconfig.json'),
615
+ configFile: path.resolve(paths_1.default.appClient, 'tsconfig.json'),
606
616
  diagnosticOptions: {
607
617
  syntactic: true,
608
618
  },
@@ -611,19 +621,17 @@ function configurePlugins(options) {
611
621
  },
612
622
  }));
613
623
  }
614
- if (config.polyfill?.process) {
615
- plugins.push(new webpack.ProvidePlugin({ process: 'process/browser.js' }));
624
+ if (config.detectCircularDependencies) {
625
+ let circularPluginOptions = {
626
+ exclude: /node_modules/,
627
+ allowAsyncCycles: true,
628
+ };
629
+ if (typeof config.detectCircularDependencies === 'object') {
630
+ circularPluginOptions = config.detectCircularDependencies;
631
+ }
632
+ plugins.push(new circular_dependency_plugin_1.default(circularPluginOptions));
616
633
  }
617
634
  if (isEnvProduction) {
618
- plugins.push(new mini_css_extract_plugin_1.default({
619
- filename: 'css/[name].[contenthash:8].css',
620
- chunkFilename: 'css/[name].[contenthash:8].chunk.css',
621
- ignoreOrder: true,
622
- }));
623
- if (config.sentryConfig) {
624
- const sentryPlugin = require('@sentry/webpack-plugin').sentryWebpackPlugin;
625
- plugins.push(sentryPlugin({ ...config.sentryConfig }));
626
- }
627
635
  if (config.analyzeBundle === 'true') {
628
636
  plugins.push(new webpack_bundle_analyzer_1.BundleAnalyzerPlugin({
629
637
  openAnalyzer: false,
@@ -634,8 +642,8 @@ function configurePlugins(options) {
634
642
  if (config.analyzeBundle === 'statoscope') {
635
643
  const customStatoscopeConfig = config.statoscopeConfig || {};
636
644
  plugins.push(new webpack_plugin_1.default({
637
- saveReportTo: path.resolve(paths_1.default.appBuild, 'report.html'),
638
- saveStatsTo: path.resolve(paths_1.default.appBuild, 'stats.json'),
645
+ saveReportTo: path.resolve(options.buildDirectory, 'report.html'),
646
+ saveStatsTo: path.resolve(options.buildDirectory, 'stats.json'),
639
647
  open: false,
640
648
  statsOptions: {
641
649
  all: true,
@@ -644,50 +652,104 @@ function configurePlugins(options) {
644
652
  }));
645
653
  }
646
654
  }
647
- if (config.cdn) {
648
- let credentialsGlobal;
649
- if (process.env.FRONTEND_S3_ACCESS_KEY_ID && process.env.FRONTEND_S3_SECRET_ACCESS_KEY) {
650
- credentialsGlobal = {
651
- accessKeyId: process.env.FRONTEND_S3_ACCESS_KEY_ID,
652
- secretAccessKey: process.env.FRONTEND_S3_SECRET_ACCESS_KEY,
653
- };
655
+ if (isEnvProduction || isSsr || config.ssr) {
656
+ plugins.push(new mini_css_extract_plugin_1.default({
657
+ filename: isEnvProduction ? 'css/[name].[contenthash:8].css' : 'css/[name].css',
658
+ chunkFilename: isEnvProduction
659
+ ? 'css/[name].[contenthash:8].chunk.css'
660
+ : 'css/[name].chunk.css',
661
+ ignoreOrder: true,
662
+ }));
663
+ }
664
+ if (!isSsr) {
665
+ if (config.monaco) {
666
+ const MonacoEditorWebpackPlugin = require('monaco-editor-webpack-plugin');
667
+ plugins.push(new MonacoEditorWebpackPlugin({
668
+ filename: isEnvProduction ? '[name].[hash:8].worker.js' : undefined,
669
+ ...config.monaco,
670
+ // currently, workers located on cdn are not working properly, so we are enforcing loading workers from
671
+ // service instead
672
+ publicPath: path.normalize(config.publicPathPrefix + '/build/'),
673
+ }));
674
+ }
675
+ const contextReplacement = config.contextReplacement || {};
676
+ plugins.push(createMomentTimezoneDataPlugin(config.momentTz));
677
+ plugins.push(new webpack.ContextReplacementPlugin(/moment[\\/]locale$/,
678
+ // eslint-disable-next-line security/detect-non-literal-regexp
679
+ new RegExp(`^\\./(${(contextReplacement.locale || ['ru']).join('|')})$`)));
680
+ plugins.push(new webpack.ContextReplacementPlugin(/dayjs[\\/]locale$/,
681
+ // eslint-disable-next-line security/detect-non-literal-regexp
682
+ new RegExp(`^\\./(${(contextReplacement.locale || ['ru']).join('|')})\\.js$`)));
683
+ if (contextReplacement['highlight.js']) {
684
+ plugins.push(new webpack.ContextReplacementPlugin(/highlight\.js[\\/]lib[\\/]languages$/,
685
+ // eslint-disable-next-line security/detect-non-literal-regexp
686
+ new RegExp(`^\\./(${contextReplacement['highlight.js'].join('|')})$`)));
654
687
  }
655
- const cdns = Array.isArray(config.cdn) ? config.cdn : [config.cdn];
656
- for (let index = 0; index < cdns.length; index++) {
657
- const cdn = cdns[index];
658
- if (!cdn) {
659
- continue;
688
+ if (isEnvDevelopment && config.reactRefresh !== false) {
689
+ const { webSocketPath = path.normalize(`/${config.publicPathPrefix}/build/sockjs-node`), } = config.devServer || {};
690
+ plugins.push(new react_refresh_webpack_plugin_1.default(config.reactRefresh({
691
+ overlay: { sockPath: webSocketPath },
692
+ exclude: [/node_modules/, /\.worker\.[jt]sx?$/],
693
+ })));
694
+ }
695
+ if (config.polyfill?.process) {
696
+ plugins.push(new webpack.ProvidePlugin({ process: 'process/browser.js' }));
697
+ }
698
+ if (isEnvProduction) {
699
+ if (config.sentryConfig) {
700
+ const sentryPlugin = require('@sentry/webpack-plugin').sentryWebpackPlugin;
701
+ plugins.push(sentryPlugin({ ...config.sentryConfig }));
660
702
  }
661
- let credentials = credentialsGlobal;
662
- const accessKeyId = process.env[`FRONTEND_S3_ACCESS_KEY_ID_${index}`];
663
- const secretAccessKey = process.env[`FRONTEND_S3_SECRET_ACCESS_KEY_${index}`];
664
- if (accessKeyId && secretAccessKey) {
665
- credentials = {
666
- accessKeyId,
667
- secretAccessKey,
703
+ }
704
+ if (config.cdn) {
705
+ let credentialsGlobal;
706
+ if (process.env.FRONTEND_S3_ACCESS_KEY_ID &&
707
+ process.env.FRONTEND_S3_SECRET_ACCESS_KEY) {
708
+ credentialsGlobal = {
709
+ accessKeyId: process.env.FRONTEND_S3_ACCESS_KEY_ID,
710
+ secretAccessKey: process.env.FRONTEND_S3_SECRET_ACCESS_KEY,
668
711
  };
669
712
  }
670
- plugins.push(new s3_upload_1.S3UploadPlugin({
671
- exclude: config.hiddenSourceMap ? /\.map$/ : undefined,
672
- compress: cdn.compress,
673
- s3ClientOptions: {
674
- region: cdn.region,
675
- endpoint: cdn.endpoint,
676
- credentials,
677
- },
678
- s3UploadOptions: {
679
- bucket: cdn.bucket,
680
- targetPath: cdn.prefix,
681
- cacheControl: cdn.cacheControl,
682
- },
683
- additionalPattern: cdn.additionalPattern,
684
- logger: options.logger,
685
- }));
713
+ const cdns = Array.isArray(config.cdn) ? config.cdn : [config.cdn];
714
+ for (let index = 0; index < cdns.length; index++) {
715
+ const cdn = cdns[index];
716
+ if (!cdn) {
717
+ continue;
718
+ }
719
+ let credentials = credentialsGlobal;
720
+ const accessKeyId = process.env[`FRONTEND_S3_ACCESS_KEY_ID_${index}`];
721
+ const secretAccessKey = process.env[`FRONTEND_S3_SECRET_ACCESS_KEY_${index}`];
722
+ if (accessKeyId && secretAccessKey) {
723
+ credentials = {
724
+ accessKeyId,
725
+ secretAccessKey,
726
+ };
727
+ }
728
+ plugins.push(new s3_upload_1.S3UploadPlugin({
729
+ exclude: config.hiddenSourceMap ? /\.map$/ : undefined,
730
+ compress: cdn.compress,
731
+ s3ClientOptions: {
732
+ region: cdn.region,
733
+ endpoint: cdn.endpoint,
734
+ credentials,
735
+ },
736
+ s3UploadOptions: {
737
+ bucket: cdn.bucket,
738
+ targetPath: cdn.prefix,
739
+ cacheControl: cdn.cacheControl,
740
+ },
741
+ additionalPattern: cdn.additionalPattern,
742
+ logger: options.logger,
743
+ }));
744
+ }
686
745
  }
687
746
  }
688
747
  return plugins;
689
748
  }
690
- function configureOptimization({ config }) {
749
+ function configureOptimization({ config, isSsr }) {
750
+ if (isSsr) {
751
+ return {};
752
+ }
691
753
  const configVendors = config.vendors ?? [];
692
754
  let vendorsList = [
693
755
  'react',
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- __webpack_public_path__ = window.__PUBLIC_PATH__ ?? '/build/';
3
+ // @ts-expect-error
4
+ __webpack_public_path__ = globalThis.__PUBLIC_PATH__ ?? '/build/';
@@ -35,6 +35,7 @@ const css_minimizer_webpack_plugin_1 = __importDefault(require("css-minimizer-we
35
35
  const config_1 = require("./config");
36
36
  const config_2 = require("../config");
37
37
  const models_1 = require("../models");
38
+ const paths_1 = __importDefault(require("../paths"));
38
39
  async function configureServiceWebpackConfig(mode, storybookConfig) {
39
40
  const serviceConfig = await (0, config_2.getProjectConfig)(mode === "production" /* WebpackMode.Prod */ ? 'build' : 'dev', {
40
41
  storybook: true,
@@ -104,6 +105,9 @@ async function configureWebpackConfigForStorybook(mode, userConfig = {}, storybo
104
105
  isEnvProduction,
105
106
  config: config.client,
106
107
  configType: mode,
108
+ buildDirectory: paths_1.default.appBuild,
109
+ entriesDirectory: paths_1.default.appEntry,
110
+ isSsr: false,
107
111
  };
108
112
  return {
109
113
  module: {
@@ -1,7 +1,10 @@
1
1
  import type webpack from 'webpack';
2
2
  import type { Logger } from '../logger';
3
- export declare function webpackCompilerHandlerFactory(logger: Logger, onCompilationEnd?: () => void): (err?: Error | null, stats?: webpack.Stats) => Promise<void>;
4
- export declare function resolveTsconfigPathsToAlias(tsConfigPath: string): {
3
+ export declare function webpackCompilerHandlerFactory(logger: Logger, onCompilationEnd?: () => void): (err?: Error | null, stats?: webpack.MultiStats) => Promise<void>;
4
+ export declare function resolveTsConfigPathsToAlias(projectPath: string, filename?: string): {
5
+ aliases?: undefined;
6
+ modules?: undefined;
7
+ } | {
5
8
  aliases: Record<string, string[]>;
6
9
  modules: string[];
7
- } | undefined;
10
+ };
@@ -24,10 +24,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.webpackCompilerHandlerFactory = webpackCompilerHandlerFactory;
27
- exports.resolveTsconfigPathsToAlias = resolveTsconfigPathsToAlias;
27
+ exports.resolveTsConfigPathsToAlias = resolveTsConfigPathsToAlias;
28
28
  const path = __importStar(require("node:path"));
29
- const fs = __importStar(require("node:fs"));
29
+ const ts = __importStar(require("typescript"));
30
30
  const pretty_time_1 = require("../logger/pretty-time");
31
+ const utils_1 = require("../typescript/utils");
31
32
  function webpackCompilerHandlerFactory(logger, onCompilationEnd) {
32
33
  return async (err, stats) => {
33
34
  if (err) {
@@ -50,25 +51,37 @@ function webpackCompilerHandlerFactory(logger, onCompilationEnd) {
50
51
  if (onCompilationEnd) {
51
52
  await onCompilationEnd();
52
53
  }
53
- if (stats) {
54
- const time = stats.endTime - stats.startTime;
54
+ const [clientStats, ssrStats] = stats?.stats ?? [];
55
+ if (clientStats) {
56
+ const time = clientStats.endTime - clientStats.startTime;
55
57
  logger.success(`Client was successfully compiled in ${(0, pretty_time_1.prettyTime)(BigInt(time) * BigInt(1_000_000))}`);
56
58
  }
57
- else {
59
+ if (ssrStats) {
60
+ const time = ssrStats.endTime - ssrStats.startTime;
61
+ logger.success(`SSR: Client was successfully compiled in ${(0, pretty_time_1.prettyTime)(BigInt(time) * BigInt(1_000_000))}`);
62
+ }
63
+ if (!clientStats && !ssrStats) {
58
64
  logger.success(`Client was successfully compiled`);
59
65
  }
60
66
  };
61
67
  }
62
68
  const endStarRe = /\/?\*$/;
63
- function resolveTsconfigPathsToAlias(tsConfigPath) {
64
- if (!fs.existsSync(tsConfigPath) || !fs.statSync(tsConfigPath).isFile) {
65
- return undefined;
69
+ function resolveTsConfigPathsToAlias(projectPath, filename = 'tsconfig.json') {
70
+ let parsed;
71
+ try {
72
+ parsed = (0, utils_1.getTsProjectConfig)(ts, projectPath, filename);
73
+ }
74
+ catch {
75
+ return {};
76
+ }
77
+ if (parsed.errors.length > 0) {
78
+ return {};
66
79
  }
67
- const { paths = {}, baseUrl } = readJsonConfig(tsConfigPath).compilerOptions || {};
80
+ const { paths = {}, baseUrl } = parsed.options;
68
81
  if (!baseUrl) {
69
- return undefined;
82
+ return {};
70
83
  }
71
- const basePath = path.resolve(path.dirname(tsConfigPath), baseUrl);
84
+ const basePath = path.resolve(path.dirname(projectPath), baseUrl);
72
85
  const aliases = {};
73
86
  const modules = [basePath];
74
87
  for (const [key, value] of Object.entries(paths)) {
@@ -84,14 +97,3 @@ function resolveTsconfigPathsToAlias(tsConfigPath) {
84
97
  }
85
98
  return { aliases, modules };
86
99
  }
87
- function readJsonConfig(pathname) {
88
- try {
89
- const json = fs.readFileSync(pathname, 'utf-8');
90
- return {
91
- ...JSON.parse(json),
92
- };
93
- }
94
- catch {
95
- throw new Error(`Couldn't read config ${pathname}`);
96
- }
97
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/app-builder",
3
- "version": "0.14.0-beta.2",
3
+ "version": "0.14.0-beta.4",
4
4
  "description": "Develop and build your React client-server projects, powered by typescript and webpack",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -129,6 +129,7 @@
129
129
  "webpack-bundle-analyzer": "^4.10.2",
130
130
  "webpack-dev-server": "^5.1.0",
131
131
  "webpack-manifest-plugin": "^5.0.0",
132
+ "webpack-node-externals": "^3.0.0",
132
133
  "worker-loader": "^3.0.8",
133
134
  "yargs": "^17.7.2"
134
135
  },
@@ -152,6 +153,7 @@
152
153
  "@types/webpack-assets-manifest": "^5.1.4",
153
154
  "@types/webpack-bundle-analyzer": "^4.7.0",
154
155
  "@types/webpack-manifest-plugin": "^3.0.8",
156
+ "@types/webpack-node-externals": "^3.0.4",
155
157
  "@types/yargs": "17.0.11",
156
158
  "babel-plugin-tester": "^11.0.4",
157
159
  "eslint": "^8.57.0",