@gravity-ui/app-builder 0.11.3 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -205,7 +205,7 @@ With this `{rootDir}/src/ui/tsconfig.json`:
205
205
  - `options` (`import('https').ServerOptions`) — allow to provide your own certificate.
206
206
  - `watchOptions` — a set of options used to customize watch mode, [more](https://webpack.js.org/configuration/watch/#watchoptions)
207
207
  - `watchPackages` (`boolean`) - watch all changes in `node_modules`.
208
- - `disableReactRefresh` (`boolean`) — disable `react-refresh` in dev mode.
208
+ - `reactRefresh` (`false | (options: ReactRefreshPluginOptions) => ReactRefreshPluginOptions`) — disable or configure `react-refresh` in dev mode, [more](https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/API.md#options)
209
209
  - `detectCircularDependencies` (`true | CircularDependenciesOptions`) - detect modules with circular dependencies, [more](https://github.com/aackerman/circular-dependency-plugin)
210
210
  - `lazyCompilation` (`true | LazyCompilationConfig`) — enable experimental [lazy compilation](https://webpack.js.org/configuration/experiments/#experimentslazycompilation) feature
211
211
  - `true` — enable feature
@@ -244,8 +244,8 @@ With this `{rootDir}/src/ui/tsconfig.json`:
244
244
 
245
245
  - `monaco` (`object`) — use [monaco-editor-webpack-plugin](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin#monaco-editor-webpack-loader-plugin)
246
246
 
247
- - `fileName` (`string`) — custom filename template for worker scripts
248
- - `languages` (`string[]`) - include only a subset of the languages supported.
247
+ - `fileName` (`string`) — custom filename template for worker scripts.
248
+ - `languages` (`string[]`) - include only a subset of the languages supported. If you don't need support for all languages, set needed languages explicitly, since it may significantly affect build time.
249
249
  - `features` (`string[]`) - include only a subset of the editor features.
250
250
  - `customLanguages` (`IFeatureDefinition[]`) - include custom languages (outside of the ones shipped with the `monaco-editor`).
251
251
 
@@ -1,3 +1,3 @@
1
1
  import WebpackDevServer from 'webpack-dev-server';
2
2
  import type { NormalizedServiceConfig } from '../../common/models';
3
- export declare function watchClientCompilation(config: NormalizedServiceConfig, onCompilationEnd: () => void): Promise<WebpackDevServer>;
3
+ export declare function watchClientCompilation(config: NormalizedServiceConfig, onManifestReady: () => void): Promise<WebpackDevServer>;
@@ -32,13 +32,18 @@ const fs = __importStar(require("node:fs"));
32
32
  const webpack_1 = __importDefault(require("webpack"));
33
33
  const webpack_dev_server_1 = __importDefault(require("webpack-dev-server"));
34
34
  const webpack_manifest_plugin_1 = require("webpack-manifest-plugin");
35
+ const webpack_assets_manifest_1 = __importDefault(require("webpack-assets-manifest"));
36
+ const utils_1 = require("../../common/utils");
35
37
  const paths_1 = __importDefault(require("../../common/paths"));
36
38
  const logger_1 = require("../../common/logger");
37
39
  const config_1 = require("../../common/webpack/config");
38
- async function watchClientCompilation(config, onCompilationEnd) {
40
+ async function watchClientCompilation(config, onManifestReady) {
39
41
  const clientCompilation = await buildWebpackServer(config);
40
- const { afterEmit } = (0, webpack_manifest_plugin_1.getCompilerHooks)(clientCompilation.compiler);
41
- afterEmit.tap('app-builder: afterEmit', onCompilationEnd);
42
+ const compiler = clientCompilation.compiler;
43
+ if ('compilers' in compiler) {
44
+ throw new Error('Unexpected multi compiler');
45
+ }
46
+ subscribeToManifestReadyEvent(compiler, onManifestReady);
42
47
  return clientCompilation;
43
48
  }
44
49
  async function buildWebpackServer(config) {
@@ -124,3 +129,17 @@ async function buildWebpackServer(config) {
124
129
  }
125
130
  return server;
126
131
  }
132
+ function subscribeToManifestReadyEvent(compiler, onManifestReady) {
133
+ 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);
139
+ }
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
+ Promise.all(promises).then(() => onManifestReady());
145
+ }
@@ -28,6 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.getProjectConfig = getProjectConfig;
30
30
  exports.normalizeConfig = normalizeConfig;
31
+ /* eslint-disable complexity */
31
32
  const path = __importStar(require("node:path"));
32
33
  const lodash_1 = __importDefault(require("lodash"));
33
34
  const cosmiconfig_1 = require("cosmiconfig");
@@ -183,6 +184,9 @@ async function normalizeClientConfig(client, mode) {
183
184
  const normalizedConfig = {
184
185
  ...client,
185
186
  forkTsChecker: client.disableForkTsChecker ? false : client.forkTsChecker,
187
+ reactRefresh: client.disableReactRefresh
188
+ ? false
189
+ : client.reactRefresh ?? ((options) => options),
186
190
  newJsxTransform: client.newJsxTransform ?? true,
187
191
  publicPathPrefix: client.publicPathPrefix || '',
188
192
  modules: client.modules && remapPaths(client.modules),
@@ -13,6 +13,7 @@ import type { SentryWebpackPluginOptions } from '@sentry/webpack-plugin';
13
13
  import type { WebpackMode } from '../webpack/config';
14
14
  import type { UploadOptions } from '../s3-upload/upload';
15
15
  import type { TerserOptions } from 'terser-webpack-plugin';
16
+ import type { ReactRefreshPluginOptions } from '@pmmmwh/react-refresh-webpack-plugin/types/lib/types';
16
17
  export interface Entities<T> {
17
18
  data: Record<string, T>;
18
19
  keys: string[];
@@ -129,9 +130,13 @@ export interface ClientConfig {
129
130
  statoscopeConfig?: Partial<StatoscopeOptions>;
130
131
  reactProfiling?: boolean;
131
132
  /**
132
- * Disable react-refresh in dev mode
133
+ * Disable react-refresh in dev mode
134
+ *
135
+ * @deprecated use `reactRefresh: false` instead
133
136
  */
134
137
  disableReactRefresh?: boolean;
138
+ /** Disable or configure react-refresh in dev mode */
139
+ reactRefresh?: false | ((options: ReactRefreshPluginOptions) => ReactRefreshPluginOptions);
135
140
  /**
136
141
  * Detect modules with circular dependencies
137
142
  */
@@ -213,7 +218,7 @@ export interface ServiceConfig {
213
218
  lib?: never;
214
219
  verbose?: boolean;
215
220
  }
216
- export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker'> & {
221
+ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh'> & {
217
222
  publicPathPrefix: string;
218
223
  hiddenSourceMap: boolean;
219
224
  svgr: NonNullable<ClientConfig['svgr']>;
@@ -230,6 +235,7 @@ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'hi
230
235
  babel: (config: Babel.TransformOptions, options: {
231
236
  configType: `${WebpackMode}`;
232
237
  }) => Babel.TransformOptions | Promise<Babel.TransformOptions>;
238
+ reactRefresh: NonNullable<ClientConfig['reactRefresh']>;
233
239
  };
234
240
  export type NormalizedServerConfig = Omit<ServerConfig, 'port' | 'inspect' | 'inspectBrk'> & {
235
241
  port?: number;
@@ -4,3 +4,8 @@ export declare function getCacheDir(): Promise<string>;
4
4
  export declare function getPort({ port }: {
5
5
  port: number;
6
6
  }): Promise<number>;
7
+ export declare function deferredPromise<T>(): {
8
+ promise: Promise<T | undefined>;
9
+ resolve: (value?: T) => void;
10
+ reject: (reason?: unknown) => void;
11
+ };
@@ -7,6 +7,7 @@ exports.createRunFolder = createRunFolder;
7
7
  exports.shouldCompileTarget = shouldCompileTarget;
8
8
  exports.getCacheDir = getCacheDir;
9
9
  exports.getPort = getPort;
10
+ exports.deferredPromise = deferredPromise;
10
11
  const node_fs_1 = __importDefault(require("node:fs"));
11
12
  const node_os_1 = __importDefault(require("node:os"));
12
13
  const paths_1 = __importDefault(require("./paths"));
@@ -27,3 +28,13 @@ async function getPort({ port }) {
27
28
  const { default: getPortDefault, portNumbers } = await import('get-port');
28
29
  return getPortDefault({ port: portNumbers(port, port + 100) });
29
30
  }
31
+ function deferredPromise() {
32
+ let resolve;
33
+ let reject;
34
+ const promise = new Promise((res, rej) => {
35
+ resolve = res;
36
+ reject = rej;
37
+ });
38
+ // @ts-expect-error Promise callback executes synchronously, so resolve and reject are initialized here
39
+ return { promise, resolve, reject };
40
+ }
@@ -223,7 +223,7 @@ function configureOutput({ isEnvDevelopment, ...rest }) {
223
223
  }
224
224
  function createJavaScriptLoader({ isEnvProduction, isEnvDevelopment, configType, config, }) {
225
225
  const plugins = [];
226
- if (isEnvDevelopment && !config.disableReactRefresh) {
226
+ if (isEnvDevelopment && config.reactRefresh !== false) {
227
227
  plugins.push([
228
228
  require.resolve('react-refresh/babel'),
229
229
  config.devServer?.webSocketPath
@@ -517,6 +517,7 @@ function createMomentTimezoneDataPlugin(options = {}) {
517
517
  function configurePlugins(options) {
518
518
  const { isEnvDevelopment, isEnvProduction, config } = options;
519
519
  const excludeFromClean = config.excludeFromClean || [];
520
+ const manifestFile = 'assets-manifest.json';
520
521
  const plugins = [
521
522
  new clean_webpack_plugin_1.CleanWebpackPlugin({
522
523
  verbose: config.verbose,
@@ -530,6 +531,16 @@ function configurePlugins(options) {
530
531
  writeToFileEmit: true,
531
532
  publicPath: '',
532
533
  }),
534
+ new webpack_assets_manifest_1.default(isEnvProduction
535
+ ? {
536
+ entrypoints: true,
537
+ output: manifestFile,
538
+ }
539
+ : {
540
+ entrypoints: true,
541
+ writeToDisk: true,
542
+ output: path.resolve(paths_1.default.appBuild, manifestFile),
543
+ }),
533
544
  createMomentTimezoneDataPlugin(config.momentTz),
534
545
  new webpack.DefinePlugin({
535
546
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
@@ -564,12 +575,12 @@ function configurePlugins(options) {
564
575
  publicPath: path.normalize(config.publicPathPrefix + '/build/'),
565
576
  }));
566
577
  }
567
- if (isEnvDevelopment && !config.disableReactRefresh) {
578
+ if (isEnvDevelopment && config.reactRefresh !== false) {
568
579
  const { webSocketPath = path.normalize(`/${config.publicPathPrefix}/build/sockjs-node`) } = config.devServer || {};
569
- plugins.push(new react_refresh_webpack_plugin_1.default({
580
+ plugins.push(new react_refresh_webpack_plugin_1.default(config.reactRefresh({
570
581
  overlay: { sockPath: webSocketPath },
571
582
  exclude: [/node_modules/, /\.worker\.[jt]sx?$/],
572
- }));
583
+ })));
573
584
  }
574
585
  if (config.detectCircularDependencies) {
575
586
  let circularPluginOptions = {
@@ -628,17 +639,6 @@ function configurePlugins(options) {
628
639
  }));
629
640
  }
630
641
  }
631
- const manifestFile = 'assets-manifest.json';
632
- plugins.push(new webpack_assets_manifest_1.default(isEnvProduction
633
- ? {
634
- entrypoints: true,
635
- output: manifestFile,
636
- }
637
- : {
638
- entrypoints: true,
639
- writeToDisk: true,
640
- output: path.resolve(paths_1.default.appBuild, manifestFile),
641
- }));
642
642
  if (config.cdn) {
643
643
  let credentialsGlobal;
644
644
  if (process.env.FRONTEND_S3_ACCESS_KEY_ID && process.env.FRONTEND_S3_SECRET_ACCESS_KEY) {
@@ -133,8 +133,8 @@ function configurePlugins({ isEnvDevelopment, isEnvProduction, config }) {
133
133
  publicPath: '/',
134
134
  }));
135
135
  }
136
- if (isEnvDevelopment && !config.disableReactRefresh) {
137
- plugins.push(new react_refresh_webpack_plugin_1.default());
136
+ if (isEnvDevelopment && config.reactRefresh !== false) {
137
+ plugins.push(new react_refresh_webpack_plugin_1.default(config.reactRefresh({})));
138
138
  }
139
139
  if (isEnvProduction) {
140
140
  plugins.push(new mini_css_extract_plugin_1.default({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/app-builder",
3
- "version": "0.11.3",
3
+ "version": "0.12.1",
4
4
  "description": "Develop and build your React client-server projects, powered by typescript and webpack",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",