@gravity-ui/app-builder 0.3.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.1](https://github.com/gravity-ui/app-builder/compare/v0.4.0...v0.4.1) (2023-05-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **worker-loader:** correctly get main worker asset ([#22](https://github.com/gravity-ui/app-builder/issues/22)) ([31984c9](https://github.com/gravity-ui/app-builder/commit/31984c9c057a915233db3d9aed470ce6d4040db4))
9
+
10
+ ## [0.4.0](https://github.com/gravity-ui/app-builder/compare/v0.3.0...v0.4.0) (2023-05-19)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * **Client:** change default value for setting `client.symlinks`, used default (true) from webpack.
16
+
17
+ ### Features
18
+
19
+ * **Client:** add setting to watch changes in node_modules ([#21](https://github.com/gravity-ui/app-builder/issues/21)) ([3a5dea6](https://github.com/gravity-ui/app-builder/commit/3a5dea6284f3a768650b388115a83136ba1b7a07))
20
+ * **WebWorker:** support webpack 5 web workers syntax ([#19](https://github.com/gravity-ui/app-builder/issues/19)) ([bf784bb](https://github.com/gravity-ui/app-builder/commit/bf784bb6480c60497bc79c93e70d61aaead80909))
21
+
3
22
  ## [0.3.0](https://github.com/gravity-ui/app-builder/compare/v0.2.2...v0.3.0) (2023-05-08)
4
23
 
5
24
 
package/README.md CHANGED
@@ -198,6 +198,7 @@ With this `{rootDir}/src/ui/tsconfig.json`:
198
198
  - `type` (`'https'`) — allow to serve over HTTPS.
199
199
  - `options` (`import('https').ServerOptions`) — allow to provide your own certificate.
200
200
  - `watchOptions` — a set of options used to customize watch mode, [more](https://webpack.js.org/configuration/watch/#watchoptions)
201
+ - `watchPackages` (`boolean`) - watch all changes in `node_modules`.
201
202
  - `disableReactRefresh` (`boolean`) — disable `react-refresh` in dev mode.
202
203
  - `detectCircularDependencies` (`true | CircularDependenciesOptions`) - detect modules with circular dependencies, [more](https://github.com/aackerman/circular-dependency-plugin)
203
204
 
@@ -232,3 +233,71 @@ With this `{rootDir}/src/ui/tsconfig.json`:
232
233
  - `languages` (`string[]`) - include only a subset of the languages supported.
233
234
  - `features` (`string[]`) - include only a subset of the editor features.
234
235
  - `customLanguages` (`IFeatureDefinition[]`) - include custom languages (outside of the ones shipped with the `monaco-editor`).
236
+
237
+ ##### WebWorker support
238
+
239
+ Web workers allow you to run JavaScript code in a separate thread from the main UI thread.
240
+ This can improve the performance and responsiveness of your web application by offloading
241
+ intensive tasks to the background.
242
+
243
+ To create a web worker, you need to write a script file that defines the logic of the worker. For example,
244
+ this file (my.worker.ts) implements a simple function that adds two numbers and sends the result back to the main thread:
245
+
246
+ ```ts
247
+ // my.worker.ts
248
+ self.onmessage = async (ev) => {
249
+ const {a = 0, b = 0} = ev.data || {};
250
+ const result = a + b;
251
+ self.postMessage({
252
+ result,
253
+ });
254
+ };
255
+ ```
256
+
257
+ `app-builder` provides built-in support for web workers for files with the `.worker.[jt]s` suffix. You can choose
258
+ between two variants of getting web workers by setting the `newWebWorkerSyntax` option:
259
+
260
+ - `newWebWorkerSyntax: false` (default) - use the `worker-loader` to import web workers.
261
+ Content of worker file will be included in main bundle as blob. This variant does not
262
+ support dynamic imports inside worker. For example:
263
+
264
+ ```ts
265
+ // main.ts
266
+ import MyWorker from './my.worker.ts';
267
+
268
+ const worker = new MyWorker();
269
+ ```
270
+
271
+ In this variant, you need to add some type declarations for the worker files::
272
+
273
+ ```ts
274
+ // worker.d.ts
275
+ declare module '*.worker.ts' {
276
+ class WebpackWorker extends Worker {}
277
+
278
+ export default WebpackWorker;
279
+ }
280
+ ```
281
+
282
+ - `newWebWorkerSyntax: true` - use the webpack 5 web workers [syntax](https://webpack.js.org/guides/web-workers/#syntax)
283
+ to import web workers. This variant allows to use dynamic imports inside worker and load worker bundle from CDN. For example:
284
+
285
+ ```ts
286
+ import {Worker} from '@gravity-ui/app-builder/worker';
287
+
288
+ const MyWorker = new Worker(new URL('./my.worker'), import.meta.url);
289
+ ```
290
+
291
+ To use the web worker in your main script, you need to communicate with it using the postMessage and onmessage methods. For example:
292
+
293
+ ```ts
294
+ // main.ts
295
+
296
+ const worker = '...'; // Worker creation, first or second variant
297
+
298
+ worker.onmessage = ({data: {result}}) => {
299
+ console.log(result);
300
+ };
301
+
302
+ worker.postMessage({a: 1, b: 2});
303
+ ```
@@ -137,9 +137,21 @@ export interface ClientConfig {
137
137
  polyfill?: {
138
138
  process?: boolean;
139
139
  };
140
+ /**
141
+ * Add additional options to DefinePlugin
142
+ */
140
143
  definitions?: DefinePlugin['definitions'];
141
- watchOptions?: Configuration['watchOptions'];
144
+ watchOptions?: Configuration['watchOptions'] & {
145
+ /**
146
+ * watch changes in node_modules
147
+ */
148
+ watchPackages?: boolean;
149
+ };
142
150
  cdn?: CdnUploadConfig | CdnUploadConfig[];
151
+ /**
152
+ * use webpack 5 Web Workers [syntax](https://webpack.js.org/guides/web-workers/#syntax)
153
+ */
154
+ newWebWorkerSyntax?: boolean;
143
155
  }
144
156
  interface CdnUploadConfig {
145
157
  bucket: string;
@@ -79,7 +79,7 @@ function webpackConfigFactory(webpackMode, config, { logger } = {}) {
79
79
  optimization: configureOptimization(helperOptions),
80
80
  externals: config.externals,
81
81
  node: config.node,
82
- watchOptions: config.watchOptions,
82
+ watchOptions: configureWatchOptions(helperOptions),
83
83
  ignoreWarnings: [/Failed to parse source map/],
84
84
  infrastructureLogging: config.verbose
85
85
  ? {
@@ -99,6 +99,9 @@ function webpackConfigFactory(webpackMode, config, { logger } = {}) {
99
99
  : undefined,
100
100
  }
101
101
  : undefined,
102
+ snapshot: {
103
+ managedPaths: config.watchOptions?.watchPackages ? [] : undefined,
104
+ },
102
105
  };
103
106
  }
104
107
  exports.webpackConfigFactory = webpackConfigFactory;
@@ -156,6 +159,17 @@ function configureDevTool({ isEnvProduction, config }) {
156
159
  }
157
160
  return config.disableSourceMapGeneration ? false : format;
158
161
  }
162
+ function configureWatchOptions({ config }) {
163
+ const watchOptions = {
164
+ ...config.watchOptions,
165
+ followSymlinks: config.watchOptions?.followSymlinks ??
166
+ (!config.symlinks && config.watchOptions?.watchPackages)
167
+ ? true
168
+ : undefined,
169
+ };
170
+ delete watchOptions.watchPackages;
171
+ return watchOptions;
172
+ }
159
173
  function configureResolve({ isEnvProduction, config, tsLinkedPackages, }) {
160
174
  let alias = config.alias || {};
161
175
  alias = Object.entries(alias).reduce((result, [key, value]) => {
@@ -179,7 +193,7 @@ function configureResolve({ isEnvProduction, config, tsLinkedPackages, }) {
179
193
  },
180
194
  modules: ['node_modules', ...modules, ...(config.modules || [])],
181
195
  extensions: ['.mjs', '.cjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
182
- symlinks: config.symlinks || false,
196
+ symlinks: config.symlinks,
183
197
  fallback: config.fallback,
184
198
  };
185
199
  }
@@ -280,14 +294,18 @@ function createWorkerRule(options) {
280
294
  test: /\.worker\.[jt]sx?$/,
281
295
  exclude: /node_modules/,
282
296
  use: [
283
- {
284
- loader: require.resolve('worker-loader'),
285
- // currently workers located on cdn are not working properly, so we are enforcing loading workers from
286
- // service instead
287
- options: {
288
- inline: 'no-fallback',
297
+ options.config.newWebWorkerSyntax
298
+ ? {
299
+ loader: require.resolve('./worker/worker-loader'),
300
+ }
301
+ : {
302
+ loader: require.resolve('worker-loader'),
303
+ // currently workers located on cdn are not working properly, so we are enforcing loading workers from
304
+ // service instead
305
+ options: {
306
+ inline: 'no-fallback',
307
+ },
289
308
  },
290
- },
291
309
  createJavaScriptLoader(options),
292
310
  ],
293
311
  };
@@ -533,7 +551,10 @@ function configurePlugins(options) {
533
551
  }
534
552
  if (isEnvDevelopment && !config.disableReactRefresh) {
535
553
  const { webSocketPath = path_1.default.normalize(`/${config.publicPathPrefix}/build/sockjs-node`) } = config.devServer || {};
536
- plugins.push(new react_refresh_webpack_plugin_1.default({ overlay: { sockPath: webSocketPath } }));
554
+ plugins.push(new react_refresh_webpack_plugin_1.default({
555
+ overlay: { sockPath: webSocketPath },
556
+ exclude: /\.worker\.[jt]sx?$/,
557
+ }));
537
558
  }
538
559
  if (config.detectCircularDependencies) {
539
560
  let circularPluginOptions = {
@@ -1,2 +1,2 @@
1
1
  "use strict";
2
- __webpack_public_path__ = window.__PUBLIC_PATH__;
2
+ __webpack_public_path__ = window.__PUBLIC_PATH__ ?? '/build/';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // eslint-disable-next-line camelcase, no-implicit-globals
3
+ __webpack_public_path__ = self.__PUBLIC_PATH__;
@@ -0,0 +1,4 @@
1
+ declare class WebWorker extends Worker {
2
+ constructor(url: string | URL, options?: WorkerOptions);
3
+ }
4
+ export { WebWorker as Worker };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Worker = void 0;
4
+ class WebWorker extends Worker {
5
+ constructor(url, options) {
6
+ // eslint-disable-next-line camelcase
7
+ const publicPath = __webpack_public_path__;
8
+ const workerPublicPath = publicPath.match(/^https?:\/\//)
9
+ ? publicPath
10
+ : new URL(publicPath, window.location.href).toString();
11
+ const objectURL = URL.createObjectURL(new Blob([
12
+ [
13
+ `self.__PUBLIC_PATH__ = ${JSON.stringify(workerPublicPath)}`,
14
+ `importScripts(${JSON.stringify(url.toString())});`,
15
+ ].join('\n'),
16
+ ], {
17
+ type: 'application/javascript',
18
+ }));
19
+ super(objectURL, options);
20
+ URL.revokeObjectURL(objectURL);
21
+ }
22
+ }
23
+ exports.Worker = WebWorker;
@@ -0,0 +1,2 @@
1
+ import webpack from 'webpack';
2
+ export declare const pitch: webpack.PitchLoaderDefinitionFunction;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.pitch = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const webpack_1 = __importDefault(require("webpack"));
9
+ const NodeTargetPlugin_1 = __importDefault(require("webpack/lib/node/NodeTargetPlugin"));
10
+ const WebWorkerTemplatePlugin_1 = __importDefault(require("webpack/lib/webworker/WebWorkerTemplatePlugin"));
11
+ const FetchCompileWasmPlugin_1 = __importDefault(require("webpack/lib/web/FetchCompileWasmPlugin"));
12
+ const FetchCompileAsyncWasmPlugin_1 = __importDefault(require("webpack/lib/web/FetchCompileAsyncWasmPlugin"));
13
+ const paths_1 = __importDefault(require("../../paths"));
14
+ const publicPath = node_path_1.default.resolve(__dirname, 'public-path.worker.js');
15
+ const pitch = function (request) {
16
+ this.cacheable(false);
17
+ if (!this._compiler || !this._compilation) {
18
+ throw new Error('Something went wrong');
19
+ }
20
+ const compilerOptions = this._compiler.options;
21
+ const logger = this.getLogger('APP_BUILDER_WORKER_LOADER');
22
+ if (compilerOptions.output.globalObject === 'window') {
23
+ logger.warn('Warning (app-builder-worker-loader): output.globalObject is set to "window". It should be set to "self" or "this" to support HMR in Workers.');
24
+ }
25
+ const isEnvProduction = compilerOptions.mode === 'production';
26
+ const filename = 'worker.js';
27
+ const chunkFilename = isEnvProduction
28
+ ? 'js/[name].[contenthash:8].worker.js'
29
+ : 'js/[name].worker.js';
30
+ const workerOptions = {
31
+ filename,
32
+ chunkFilename,
33
+ publicPath: compilerOptions.output.publicPath,
34
+ globalObject: 'self',
35
+ devtoolNamespace: node_path_1.default.resolve('/', node_path_1.default.relative(paths_1.default.app, this.resource)),
36
+ };
37
+ const workerCompiler = this._compilation.createChildCompiler(`worker ${request}`, workerOptions);
38
+ new WebWorkerTemplatePlugin_1.default().apply(workerCompiler);
39
+ if (this.target !== 'webworker' && this.target !== 'web') {
40
+ new NodeTargetPlugin_1.default().apply(workerCompiler);
41
+ }
42
+ new FetchCompileWasmPlugin_1.default({
43
+ mangleImports: this._compiler.options.optimization.mangleWasmImports,
44
+ }).apply(workerCompiler);
45
+ new FetchCompileAsyncWasmPlugin_1.default().apply(workerCompiler);
46
+ const bundleName = node_path_1.default.parse(this.resourcePath).name;
47
+ new webpack_1.default.EntryPlugin(this.context, `!!${publicPath}`, bundleName).apply(workerCompiler);
48
+ new webpack_1.default.EntryPlugin(this.context, `!!${request}`, bundleName).apply(workerCompiler);
49
+ configureSourceMap(workerCompiler);
50
+ const cb = this.async();
51
+ workerCompiler.compile((err, compilation) => {
52
+ if (!compilation) {
53
+ return undefined;
54
+ }
55
+ workerCompiler.parentCompilation?.children.push(compilation);
56
+ if (err) {
57
+ return cb(err);
58
+ }
59
+ if (compilation.errors && compilation.errors.length) {
60
+ const errorDetails = compilation.errors
61
+ .map((error) => {
62
+ if (error instanceof Error) {
63
+ return error.stack;
64
+ }
65
+ return error;
66
+ })
67
+ .join('\n');
68
+ return cb(new Error('Child compilation failed:\n' + errorDetails));
69
+ }
70
+ const contents = compilation.assets[filename]?.source();
71
+ const mapFile = `${filename}.map`;
72
+ let map = compilation.assets[mapFile]?.source();
73
+ if (map) {
74
+ const sourceMap = JSON.parse(map.toString());
75
+ if (Array.isArray(sourceMap.sources)) {
76
+ sourceMap.sources = sourceMap.sources.map((pathname) => pathname.replace(/webpack:\/\/[^/]+\//, 'webpack://'));
77
+ }
78
+ map = JSON.stringify(sourceMap);
79
+ }
80
+ for (const [assetName, asset] of Object.entries(compilation.assets)) {
81
+ if ([filename, mapFile].includes(assetName)) {
82
+ continue;
83
+ }
84
+ workerCompiler.parentCompilation?.emitAsset(assetName, asset, compilation.assetsInfo.get(assetName));
85
+ }
86
+ return cb(null, contents, map?.toString());
87
+ });
88
+ };
89
+ exports.pitch = pitch;
90
+ function configureSourceMap(compiler) {
91
+ const devtool = compiler.options.devtool;
92
+ if (devtool) {
93
+ if (devtool.includes('source-map')) {
94
+ // remove parent SourceMapDevToolPlugin from compilation
95
+ for (const hook of Object.values(compiler.hooks)) {
96
+ for (let i = hook.taps.length - 1; i >= 0; i--) {
97
+ const tap = hook.taps[i];
98
+ if (tap?.name === 'SourceMapDevToolPlugin') {
99
+ hook.taps.splice(i, 1);
100
+ }
101
+ }
102
+ }
103
+ const hidden = devtool.includes('hidden');
104
+ const inline = devtool.includes('inline');
105
+ const cheap = devtool.includes('cheap');
106
+ const moduleMaps = devtool.includes('module');
107
+ new webpack_1.default.SourceMapDevToolPlugin({
108
+ filename: inline ? null : compiler.options.output.sourceMapFilename,
109
+ moduleFilenameTemplate: compiler.options.output.devtoolModuleFilenameTemplate,
110
+ fallbackModuleFilenameTemplate: compiler.options.output.devtoolFallbackModuleFilenameTemplate,
111
+ append: hidden ? false : undefined,
112
+ module: moduleMaps ? true : !cheap,
113
+ columns: !cheap,
114
+ noSources: false,
115
+ namespace: (compiler.parentCompilation?.outputOptions.devtoolNamespace ?? '') +
116
+ compiler.options.output.devtoolNamespace,
117
+ }).apply(compiler);
118
+ }
119
+ }
120
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { configureWebpackConfigForStorybook, configureServiceWebpackConfig, } from './common/webpack/storybook';
2
+ export * from './common/s3-upload';
2
3
  export { createTransformPathsToLocalModules } from './common/typescript/transformers';
3
4
  export { defineConfig } from './common/models';
4
5
  export type { ProjectConfig, ServiceConfig, LibraryConfig, ProjectFileConfig } from './common/models';
package/dist/index.js CHANGED
@@ -1,9 +1,24 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.defineConfig = exports.createTransformPathsToLocalModules = exports.configureServiceWebpackConfig = exports.configureWebpackConfigForStorybook = void 0;
4
18
  var storybook_1 = require("./common/webpack/storybook");
5
19
  Object.defineProperty(exports, "configureWebpackConfigForStorybook", { enumerable: true, get: function () { return storybook_1.configureWebpackConfigForStorybook; } });
6
20
  Object.defineProperty(exports, "configureServiceWebpackConfig", { enumerable: true, get: function () { return storybook_1.configureServiceWebpackConfig; } });
21
+ __exportStar(require("./common/s3-upload"), exports);
7
22
  var transformers_1 = require("./common/typescript/transformers");
8
23
  Object.defineProperty(exports, "createTransformPathsToLocalModules", { enumerable: true, get: function () { return transformers_1.createTransformPathsToLocalModules; } });
9
24
  var models_1 = require("./common/models");
package/package.json CHANGED
@@ -1,10 +1,30 @@
1
1
  {
2
2
  "name": "@gravity-ui/app-builder",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Develop and build your React client-server projects, powered by typescript and webpack",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "exports": {
9
+ "./worker": {
10
+ "types": "./dist/common/webpack/worker/web-worker.d.ts",
11
+ "default": "./dist/common/webpack/worker/web-worker.js"
12
+ },
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "typesVersions": {
19
+ "*": {
20
+ "index.d.ts": [
21
+ "./dist/index.d.ts"
22
+ ],
23
+ "worker": [
24
+ "./dist/common/webpack/worker/web-worker.d.ts"
25
+ ]
26
+ }
27
+ },
8
28
  "bin": {
9
29
  "app-builder": "dist/cli.js"
10
30
  },