@gravity-ui/app-builder 0.21.1 → 0.23.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.
package/README.md CHANGED
@@ -177,6 +177,9 @@ With this `{rootDir}/src/ui/tsconfig.json`:
177
177
  - `icons` (`string[]`) — Additional paths for svg icons. By default, all svgs with paths including `icons/` will be processed.
178
178
  Example: `icons: [node_modules/@fortawesome/fontawesome-pro/svgs]`
179
179
  - `publicPathPrefix` (`string`) — publicPath prefix, will be added to `/build/`
180
+ - `publicPath` (`string`) — publicPath for bundler, this option has higher priority than publicPathPrefix
181
+ - `outputPath` (`string`) — Build directory for output, default: `dist/public/build` and `dist/ssr` - for SSR
182
+ - `assetsManifestFile` (`string`) — File name for assets manifest, default: `assets-manifest.json`
180
183
  - `symlinks` (`boolean`) — Follow symbolic links while looking for a file. [more](https://webpack.js.org/configuration/resolve/#resolvesymlinks)
181
184
  - `externals` — specify dependencies that shouldn't be resolved by webpack, but should become dependencies of the resulting bundle. [more](https://webpack.js.org/configuration/externals/)
182
185
  - `node` — include polyfills or mocks for various node stuff. [more](https://webpack.js.org/configuration/node/)
@@ -187,6 +190,7 @@ With this `{rootDir}/src/ui/tsconfig.json`:
187
190
  - `definitions` — add additional options to DefinePlugin. [more](https://webpack.js.org/plugins/define-plugin/#usage)
188
191
  - `newJsxTransform` (`boolean=true`) — use new JSX Transform.
189
192
  - `svgr` (`SvgrConfig`) — svgr plugin options. [more](https://react-svgr.com/docs/options/)
193
+ - `entry` (`string | string[] | Record<string, string | string[]>`) — entry for bundler, overrides entry which is generated from entries directory
190
194
  - `entryFilter` (`string[]`) — filter used entrypoints.
191
195
  - `excludeFromClean` (`string[]`) — do not clean provided paths before build.
192
196
  - `forkTsCheker` (`false | ForkTsCheckerWebpackPluginOptions`) - config for ForkTsCheckerWebpackPlugin [more](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options). If `false`, ForkTsCheckerWebpackPlugin will be disabled.
@@ -49,7 +49,8 @@ async function watchClientCompilation(config, onManifestReady) {
49
49
  async function buildDevServer(config) {
50
50
  const bundler = config.client.bundler;
51
51
  const logger = new logger_1.Logger('client', config.verbose);
52
- const { webSocketPath = path.normalize(`/${config.client.publicPathPrefix}/build/sockjs-node`), writeToDisk, ...devServer } = config.client.devServer || {};
52
+ const { publicPath } = config.client;
53
+ const { webSocketPath = path.normalize(`/${publicPath}/sockjs-node`), writeToDisk, ...devServer } = config.client.devServer || {};
53
54
  const normalizedConfig = { ...config.client, devServer: { ...devServer, webSocketPath } };
54
55
  const isSsr = Boolean(normalizedConfig.ssr);
55
56
  let webpackConfigs = [];
@@ -90,7 +91,6 @@ async function buildDevServer(config) {
90
91
  }));
91
92
  }
92
93
  }
93
- const publicPath = path.normalize(config.client.publicPathPrefix + '/build/');
94
94
  const staticFolder = path.resolve(paths_1.default.appDist, 'public');
95
95
  const options = {
96
96
  static: staticFolder,
@@ -170,6 +170,7 @@ async function buildDevServer(config) {
170
170
  if (bundler === 'rspack') {
171
171
  // Rspack multicompiler dont work with lazy compilation.
172
172
  // Pass a single config to avoid multicompiler when SSR disabled.
173
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
173
174
  const compiler = (0, core_1.rspack)(isSsr ? rspackConfigs : rspackConfigs[0]);
174
175
  server = new dev_server_1.RspackDevServer(options, compiler);
175
176
  }
@@ -197,11 +198,11 @@ function subscribeToManifestReadyEvent(compiler, onManifestReady) {
197
198
  const compilers = 'compilers' in compiler ? compiler.compilers : [compiler];
198
199
  for (let i = 0; i < options.length; i++) {
199
200
  const config = options[i];
200
- const compiler = compilers[i];
201
- if (!config || !compiler) {
201
+ const currentCompiler = compilers[i];
202
+ if (!config || !currentCompiler) {
202
203
  throw new Error('Something goes wrong!');
203
204
  }
204
- if (!isRspackCompiler(compiler)) {
205
+ if (!isRspackCompiler(currentCompiler)) {
205
206
  const assetsManifestPlugin = config.plugins.find((plugin) => plugin instanceof webpack_assets_manifest_1.default);
206
207
  if (assetsManifestPlugin) {
207
208
  const assetsManifestReady = (0, utils_1.deferredPromise)();
@@ -211,12 +212,12 @@ function subscribeToManifestReadyEvent(compiler, onManifestReady) {
211
212
  }
212
213
  const manifestReady = (0, utils_1.deferredPromise)();
213
214
  promises.push(manifestReady.promise);
214
- if (isRspackCompiler(compiler)) {
215
- const { afterEmit } = (0, rspack_manifest_plugin_1.getCompilerHooks)(compiler);
215
+ if (isRspackCompiler(currentCompiler)) {
216
+ const { afterEmit } = (0, rspack_manifest_plugin_1.getCompilerHooks)(currentCompiler);
216
217
  afterEmit.tap('app-builder', manifestReady.resolve);
217
218
  }
218
219
  else {
219
- const { afterEmit } = (0, webpack_manifest_plugin_1.getCompilerHooks)(compiler);
220
+ const { afterEmit } = (0, webpack_manifest_plugin_1.getCompilerHooks)(currentCompiler);
220
221
  afterEmit.tap('app-builder', manifestReady.resolve);
221
222
  }
222
223
  }
@@ -189,7 +189,8 @@ async function normalizeClientConfig(client, mode) {
189
189
  ? false
190
190
  : (client.reactRefresh ?? ((options) => options)),
191
191
  newJsxTransform: client.newJsxTransform ?? true,
192
- publicPathPrefix: client.publicPathPrefix || '',
192
+ publicPath: client.publicPath || path.normalize(`${client.publicPathPrefix || ''}/build/`),
193
+ assetsManifestFile: client.assetsManifestFile || 'assets-manifest.json',
193
194
  modules: client.modules && remapPaths(client.modules),
194
195
  includes: client.includes && remapPaths(client.includes),
195
196
  images: client.images && remapPaths(client.images),
@@ -79,9 +79,24 @@ export interface ClientConfig {
79
79
  devServer?: DevServerConfig;
80
80
  contextReplacement?: ContextReplacement;
81
81
  /**
82
- * publicPath prefix, will be added to '/build/'
82
+ * publicPath prefix, will be added to '/build/'
83
83
  */
84
84
  publicPathPrefix?: string;
85
+ /**
86
+ * publicPath for bundler
87
+ * This option has higher priority than publicPathPrefix
88
+ */
89
+ publicPath?: string;
90
+ /**
91
+ * Build directory for output
92
+ * Default: 'dist/public/build' and 'dist/ssr' - for SSR
93
+ */
94
+ outputPath?: string;
95
+ /**
96
+ * File name for assets manifest
97
+ * Default: 'assets-manifest.json'
98
+ */
99
+ assetsManifestFile?: string;
85
100
  /**
86
101
  * Add monaco-editor support
87
102
  */
@@ -92,7 +107,7 @@ export interface ClientConfig {
92
107
  customLanguages?: IFeatureDefinition[];
93
108
  };
94
109
  /**
95
- * if false - source maps will be generated for prod builds,
110
+ * if false - source maps will be generated for prod builds
96
111
  */
97
112
  hiddenSourceMap?: boolean;
98
113
  /**
@@ -131,6 +146,11 @@ export interface ClientConfig {
131
146
  * svgr plugin options.
132
147
  */
133
148
  svgr?: SvgrConfig;
149
+ /**
150
+ * entry for bundler
151
+ * Overrides entry which is generated from entries directory
152
+ */
153
+ entry?: string | string[] | Record<string, string | string[]>;
134
154
  entryFilter?: string[];
135
155
  excludeFromClean?: string[];
136
156
  analyzeBundle?: 'true' | 'statoscope' | 'rsdoctor';
@@ -252,10 +272,11 @@ export interface ServiceConfig {
252
272
  lib?: never;
253
273
  verbose?: boolean;
254
274
  }
255
- export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh'> & {
275
+ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'publicPath' | 'assetsManifestFile' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh'> & {
256
276
  bundler: Bundler;
257
277
  javaScriptLoader: JavaScriptLoader;
258
- publicPathPrefix: string;
278
+ publicPath: string;
279
+ assetsManifestFile: string;
259
280
  hiddenSourceMap: boolean;
260
281
  svgr: NonNullable<ClientConfig['svgr']>;
261
282
  lazyCompilation?: LazyCompilationConfig;
@@ -9,6 +9,8 @@ export interface HelperOptions {
9
9
  isEnvProduction: boolean;
10
10
  configType: `${WebpackMode}`;
11
11
  buildDirectory: string;
12
+ assetsManifestFile: string;
13
+ entry?: string | string[] | Record<string, string | string[]>;
12
14
  entriesDirectory: string;
13
15
  isSsr: boolean;
14
16
  }
@@ -60,7 +60,6 @@ const node_externals_1 = require("./node-externals");
60
60
  const statoscope_1 = require("./statoscope");
61
61
  const imagesSizeLimit = 2048;
62
62
  const fontSizeLimit = 8192;
63
- const assetsManifestFile = 'assets-manifest.json';
64
63
  function getHelperOptions({ webpackMode, config, logger, isSsr = false, }) {
65
64
  const isEnvDevelopment = webpackMode === "development" /* WebpackMode.Dev */;
66
65
  const isEnvProduction = webpackMode === "production" /* WebpackMode.Prod */;
@@ -70,7 +69,9 @@ function getHelperOptions({ webpackMode, config, logger, isSsr = false, }) {
70
69
  isEnvDevelopment,
71
70
  isEnvProduction,
72
71
  configType: webpackMode,
73
- buildDirectory: isSsr ? paths_1.default.appSsrBuild : paths_1.default.appBuild,
72
+ buildDirectory: config.outputPath || (isSsr ? paths_1.default.appSsrBuild : paths_1.default.appBuild),
73
+ assetsManifestFile: config.assetsManifestFile,
74
+ entry: config.entry,
74
75
  entriesDirectory: isSsr ? paths_1.default.appSsrEntry : paths_1.default.appEntry,
75
76
  isSsr,
76
77
  };
@@ -315,7 +316,10 @@ function configureResolve({ isEnvProduction, config }) {
315
316
  };
316
317
  }
317
318
  function createEntryArray(entry) {
318
- return [require.resolve('./public-path'), entry];
319
+ if (typeof entry === 'string') {
320
+ return [require.resolve('./public-path'), entry];
321
+ }
322
+ return [require.resolve('./public-path'), ...entry];
319
323
  }
320
324
  function addEntry(entry, file) {
321
325
  return {
@@ -323,15 +327,24 @@ function addEntry(entry, file) {
323
327
  [path.parse(file).name]: createEntryArray(file),
324
328
  };
325
329
  }
326
- function configureEntry({ config, entriesDirectory }) {
330
+ function configureEntry({ config, entry, entriesDirectory }) {
331
+ if (typeof entry === 'string' || Array.isArray(entry)) {
332
+ return createEntryArray(entry);
333
+ }
334
+ if (typeof entry === 'object') {
335
+ return Object.entries(entry).reduce((acc, [key, value]) => ({
336
+ ...acc,
337
+ [key]: createEntryArray(value),
338
+ }), {});
339
+ }
327
340
  let entries = fs.readdirSync(entriesDirectory).filter((file) => /\.[jt]sx?$/.test(file));
328
341
  if (Array.isArray(config.entryFilter) && config.entryFilter.length) {
329
- entries = entries.filter((entry) => config.entryFilter?.includes(entry.split('.')[0] ?? ''));
342
+ entries = entries.filter((file) => config.entryFilter?.includes(file.split('.')[0] ?? ''));
330
343
  }
331
344
  if (!entries.length) {
332
345
  throw new Error('No entries were found after applying entry filter');
333
346
  }
334
- return entries.reduce((entry, file) => addEntry(entry, path.resolve(entriesDirectory, file)), {});
347
+ return entries.reduce((acc, file) => addEntry(acc, path.resolve(entriesDirectory, file)), {});
335
348
  }
336
349
  function getFileNames({ isEnvProduction, isSsr, config }) {
337
350
  let ext = 'js';
@@ -609,6 +622,7 @@ function getCssLoaders({ isEnvDevelopment, isEnvProduction, config, isSsr }, add
609
622
  else {
610
623
  loaders.unshift({
611
624
  loader: require.resolve('style-loader'),
625
+ options: { insert: require.resolve('./insert-style-tag.js') },
612
626
  });
613
627
  }
614
628
  }
@@ -864,7 +878,7 @@ function configureCommonPlugins(options, bundlerPlugins) {
864
878
  ...config.monaco,
865
879
  // currently, workers located on cdn are not working properly, so we are enforcing loading workers from
866
880
  // service instead
867
- publicPath: path.normalize(config.publicPathPrefix + '/build/'),
881
+ publicPath: config.publicPath,
868
882
  }));
869
883
  }
870
884
  plugins.push(createMomentTimezoneDataPlugin(config.momentTz));
@@ -928,17 +942,17 @@ function configureWebpackPlugins(options) {
928
942
  new webpack_assets_manifest_1.default(isEnvProduction
929
943
  ? {
930
944
  entrypoints: true,
931
- output: assetsManifestFile,
945
+ output: options.assetsManifestFile,
932
946
  }
933
947
  : {
934
948
  entrypoints: true,
935
949
  writeToDisk: true,
936
- output: path.resolve(options.buildDirectory, assetsManifestFile),
950
+ output: path.resolve(options.buildDirectory, options.assetsManifestFile),
937
951
  }),
938
952
  ...(process.env.WEBPACK_PROFILE === 'true' ? [new webpack.debug.ProfilingPlugin()] : []),
939
953
  ];
940
954
  if (!isSsr && isEnvDevelopment && config.reactRefresh !== false) {
941
- const { webSocketPath = path.normalize(`/${config.publicPathPrefix}/build/sockjs-node`) } = config.devServer || {};
955
+ const { webSocketPath = path.normalize(`/${config.publicPath}/sockjs-node`) } = config.devServer || {};
942
956
  const reactRefreshConfig = config.reactRefresh({
943
957
  overlay: { sockPath: webSocketPath },
944
958
  exclude: [/node_modules/, /\.worker\.[jt]sx?$/],
@@ -963,8 +977,8 @@ function configureRspackPlugins(options) {
963
977
  ...configureCommonPlugins(options, plugins),
964
978
  new rspack_manifest_plugin_1.RspackManifestPlugin({
965
979
  fileName: isEnvProduction
966
- ? assetsManifestFile
967
- : path.resolve(options.buildDirectory, assetsManifestFile),
980
+ ? options.assetsManifestFile
981
+ : path.resolve(options.buildDirectory, options.assetsManifestFile),
968
982
  writeToFileEmit: true,
969
983
  useLegacyEmit: true,
970
984
  publicPath: '',
@@ -972,7 +986,7 @@ function configureRspackPlugins(options) {
972
986
  }),
973
987
  ];
974
988
  if (!isSsr && isEnvDevelopment && config.reactRefresh !== false) {
975
- const { webSocketPath = path.normalize(`/${config.publicPathPrefix}/build/sockjs-node`) } = config.devServer || {};
989
+ const { webSocketPath = path.normalize(`/${config.publicPath}/sockjs-node`) } = config.devServer || {};
976
990
  const { overlay, ...reactRefreshConfig } = config.reactRefresh({
977
991
  overlay: { sockPath: webSocketPath },
978
992
  exclude: [/node_modules/, /\.worker\.[jt]sx?$/],
@@ -0,0 +1,9 @@
1
+ export = insertStyleTag;
2
+ /**
3
+ * This function inserts a style tag into the document head if it doesn't already exist.
4
+ * It is needed to do not allow duplicate style tags.
5
+ *
6
+ * @param {HTMLStyleElement} $targetStyle - The style element to be inserted
7
+ * @returns {void}
8
+ */
9
+ declare function insertStyleTag($targetStyle: HTMLStyleElement): void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /* eslint-env browser */
3
+ /**
4
+ * This function inserts a style tag into the document head if it doesn't already exist.
5
+ * It is needed to do not allow duplicate style tags.
6
+ *
7
+ * @param {HTMLStyleElement} $targetStyle - The style element to be inserted
8
+ * @returns {void}
9
+ */
10
+ function insertStyleTag($targetStyle) {
11
+ // setTimeout is used to queue the insertion of the style tag
12
+ // to the end of the current event loop, allowing other
13
+ setTimeout(() => {
14
+ const $head = document.head;
15
+ let targetStyleExists = false;
16
+ $head.querySelectorAll('style').forEach(($style) => {
17
+ if ($style.innerHTML === $targetStyle.innerHTML) {
18
+ targetStyleExists = true;
19
+ }
20
+ });
21
+ if (!targetStyleExists) {
22
+ $head.appendChild($targetStyle);
23
+ }
24
+ });
25
+ }
26
+ module.exports = insertStyleTag;
@@ -112,7 +112,9 @@ async function configureWebpackConfigForStorybook(mode, userConfig = {}, storybo
112
112
  isEnvProduction,
113
113
  config: config.client,
114
114
  configType: mode,
115
- buildDirectory: paths_1.default.appBuild,
115
+ buildDirectory: config.client.outputPath || paths_1.default.appBuild,
116
+ assetsManifestFile: config.client.assetsManifestFile,
117
+ entry: config.client.entry,
116
118
  entriesDirectory: paths_1.default.appEntry,
117
119
  isSsr: false,
118
120
  };
@@ -3,9 +3,9 @@ export declare function createCli(argv: string[]): {
3
3
  [x: string]: unknown;
4
4
  verbose: boolean | undefined;
5
5
  cdn: string | undefined;
6
+ c: unknown;
6
7
  env: string[] | undefined;
7
8
  target: "client" | "server" | undefined;
8
- c: unknown;
9
9
  inspect: number | undefined;
10
10
  inspectBrk: number | undefined;
11
11
  "inspect-brk": number | undefined;
@@ -31,9 +31,9 @@ export declare function createCli(argv: string[]): {
31
31
  [x: string]: unknown;
32
32
  verbose: boolean | undefined;
33
33
  cdn: string | undefined;
34
+ c: unknown;
34
35
  env: string[] | undefined;
35
36
  target: "client" | "server" | undefined;
36
- c: unknown;
37
37
  inspect: number | undefined;
38
38
  inspectBrk: number | undefined;
39
39
  "inspect-brk": number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/app-builder",
3
- "version": "0.21.1",
3
+ "version": "0.23.0",
4
4
  "description": "Develop and build your React client-server projects, powered by typescript and webpack",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -72,7 +72,7 @@
72
72
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
73
73
  "@rsdoctor/rspack-plugin": "^0.4.13",
74
74
  "@rsdoctor/webpack-plugin": "^0.4.13",
75
- "@rspack/core": "1.2.7",
75
+ "@rspack/core": "1.2.8",
76
76
  "@rspack/dev-server": "^1.0.10",
77
77
  "@rspack/plugin-react-refresh": "^1.0.1",
78
78
  "@statoscope/webpack-plugin": "^5.28.2",