@teambit/react 0.0.804 → 0.0.807

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/apps/web/compute-results.ts +3 -1
  2. package/apps/web/react-app-options.ts +6 -1
  3. package/apps/web/react-build-result.ts +1 -0
  4. package/apps/web/react.app-type.ts +1 -0
  5. package/apps/web/react.application.ts +62 -83
  6. package/apps/web/webpack/mutators.ts +69 -0
  7. package/apps/web/webpack/webpack.app.ssr.config.ts +24 -0
  8. package/artifacts/__bit_junit.xml +2 -2
  9. package/artifacts/env-template/public/{22.20acd98351f2158a41da.js → 22.0f657f2dfdd8e90d81ba.js} +0 -0
  10. package/artifacts/env-template/public/{994.bb73ab7fa3ec9ae9dc3b.js → 502.1f3ad81d16d1a387ed6e.js} +19 -11
  11. package/artifacts/env-template/public/assets-manifest.json +14 -14
  12. package/artifacts/env-template/public/{compositions.52254428c46016cd7126.js → compositions.1a2eae0121924a2382b5.js} +1 -1
  13. package/artifacts/env-template/public/compositions.html +1 -1
  14. package/artifacts/env-template/public/{overview.feaf6bb95eb88100792c.js → overview.4c90e935a13e148728cc.js} +1 -1
  15. package/artifacts/env-template/public/overview.html +1 -1
  16. package/artifacts/env-template/public/peers.9bf322cedc753fa8456b.js +1 -0
  17. package/artifacts/env-template/public/{preview-root.3a0d522c80063747b87d.js → preview-root.6f380ef699dc27c5b1d6.js} +1 -1
  18. package/artifacts/env-template/public/static/css/{994.90cfc900.css → 502.f6840c3e.css} +1 -1
  19. package/artifacts/preview/teambit_react_react-preview.js +1 -1
  20. package/{compositions-1659347901202.js → compositions-1659583874630.js} +0 -0
  21. package/dist/apps/web/compute-results.d.ts +2 -1
  22. package/dist/apps/web/compute-results.js +3 -1
  23. package/dist/apps/web/compute-results.js.map +1 -1
  24. package/dist/apps/web/react-app-options.d.ts +5 -1
  25. package/dist/apps/web/react-app-options.js.map +1 -1
  26. package/dist/apps/web/react-build-result.d.ts +1 -0
  27. package/dist/apps/web/react-build-result.js.map +1 -1
  28. package/dist/apps/web/react.app-type.js +1 -1
  29. package/dist/apps/web/react.app-type.js.map +1 -1
  30. package/dist/apps/web/react.application.d.ts +8 -4
  31. package/dist/apps/web/react.application.js +62 -95
  32. package/dist/apps/web/react.application.js.map +1 -1
  33. package/dist/apps/web/webpack/mutators.d.ts +3 -0
  34. package/dist/apps/web/webpack/mutators.js +91 -0
  35. package/dist/apps/web/webpack/mutators.js.map +1 -1
  36. package/dist/apps/web/webpack/webpack.app.ssr.config.d.ts +6 -0
  37. package/dist/apps/web/webpack/webpack.app.ssr.config.js +29 -0
  38. package/dist/apps/web/webpack/webpack.app.ssr.config.js.map +1 -1
  39. package/dist/react.main.runtime.d.ts +3 -2
  40. package/dist/react.main.runtime.js +4 -3
  41. package/dist/react.main.runtime.js.map +1 -1
  42. package/dist/webpack/webpack.config.base.js +15 -1
  43. package/dist/webpack/webpack.config.base.js.map +1 -1
  44. package/{overview-1659347901202.js → overview-1659583874630.js} +0 -0
  45. package/package-tar/teambit-react-0.0.807.tgz +0 -0
  46. package/package.json +32 -30
  47. package/{preview-1659347901202.js → preview-1659583874630.js} +2 -2
  48. package/webpack/webpack.config.base.ts +2 -1
  49. package/artifacts/env-template/public/peers.1d2b76311bfbf98a86fc.js +0 -1
  50. package/package-tar/teambit-react-0.0.804.tgz +0 -0
@@ -3,15 +3,17 @@ import { ReactAppBuildResult } from './react-build-result';
3
3
 
4
4
  type resultsOptions = {
5
5
  publicDir: string;
6
+ ssrPublicDir?: string;
6
7
  };
7
8
  export async function computeResults(
8
9
  results: BundlerResult[],
9
- { publicDir }: resultsOptions
10
+ { publicDir, ssrPublicDir }: resultsOptions
10
11
  ): Promise<ReactAppBuildResult> {
11
12
  const result = results[0];
12
13
 
13
14
  return {
14
15
  publicDir,
16
+ ssrPublicDir,
15
17
  errors: result.errors,
16
18
  warnings: result.warnings,
17
19
  };
@@ -70,7 +70,7 @@ export type ReactAppOptions = {
70
70
  /**
71
71
  * path to entry files of the application.
72
72
  */
73
- entry: string[] | (() => Promise<string[]>);
73
+ entry: string[] | ((path?: string) => Promise<string[]>);
74
74
 
75
75
  /**
76
76
  * path to server-rendered entrypoint of the app
@@ -82,6 +82,11 @@ export type ReactAppOptions = {
82
82
  */
83
83
  bundler?: Bundler;
84
84
 
85
+ /**
86
+ * instance of serverside bundler to use. default is Webpack.
87
+ */
88
+ ssrBundler?: Bundler;
89
+
85
90
  /**
86
91
  * instance of dev server to use. default is Webpack.
87
92
  */
@@ -6,4 +6,5 @@ export type ReactAppBuildResult = {
6
6
  * when using ssr, public dir is undefined.
7
7
  */
8
8
  publicDir?: string;
9
+ ssrPublicDir?: string;
9
10
  } & AppBuildResult;
@@ -16,6 +16,7 @@ export class ReactAppType implements ApplicationType<ReactAppOptions> {
16
16
  this.reactEnv,
17
17
  options.prerender,
18
18
  options.bundler,
19
+ options.ssrBundler,
19
20
  options.devServer,
20
21
  options.webpackTransformers,
21
22
  options.deploy,
@@ -3,8 +3,7 @@ import { Application, AppContext, AppBuildContext, AppResult } from '@teambit/ap
3
3
  import type { Bundler, DevServer, BundlerContext, DevServerContext, BundlerHtmlConfig } from '@teambit/bundler';
4
4
  import { Port } from '@teambit/toolbox.network.get-port';
5
5
  import type { Logger } from '@teambit/logger';
6
- import { remove } from 'lodash';
7
- import TerserPlugin from 'terser-webpack-plugin';
6
+ import compact from 'lodash.compact';
8
7
  import { WebpackConfigTransformer } from '@teambit/webpack';
9
8
  import { BitError } from '@teambit/bit-error';
10
9
  import { ReactEnv } from '../../react.env';
@@ -14,19 +13,20 @@ import { ReactAppPrerenderOptions } from './react-app-options';
14
13
  import { html } from '../../webpack';
15
14
  import { ReactDeployContext } from '.';
16
15
  import { computeResults } from './compute-results';
17
- import { clientConfig, ssrConfig, calcOutputPath } from './webpack/webpack.app.ssr.config';
18
- import { addDevServer, setOutput } from './webpack/mutators';
16
+ import { clientConfig, ssrConfig, calcOutputPath, ssrBuildConfig, buildConfig } from './webpack/webpack.app.ssr.config';
17
+ import { addDevServer, setOutput, replaceTerserPlugin } from './webpack/mutators';
19
18
  import { createExpressSsr, loadSsrApp, parseAssets } from './ssr/ssr-express';
20
19
 
21
20
  export class ReactApp implements Application {
22
21
  constructor(
23
22
  readonly name: string,
24
- readonly entry: string[] | (() => Promise<string[]>),
23
+ readonly entry: string[] | ((path?: string) => Promise<string[]>),
25
24
  readonly ssr: string | (() => Promise<string>) | undefined,
26
25
  readonly portRange: [number, number],
27
26
  private reactEnv: ReactEnv,
28
27
  readonly prerender?: ReactAppPrerenderOptions,
29
28
  readonly bundler?: Bundler,
29
+ readonly ssrBundler?: Bundler,
30
30
  readonly devServer?: DevServer,
31
31
  readonly transformers: WebpackConfigTransformer[] = [],
32
32
  readonly deploy?: (context: ReactDeployContext) => Promise<void>,
@@ -35,6 +35,7 @@ export class ReactApp implements Application {
35
35
  ) {}
36
36
  readonly applicationType = 'react-common-js';
37
37
  readonly dir = 'public';
38
+ readonly ssrDir = 'ssr';
38
39
 
39
40
  async run(context: AppContext): Promise<number> {
40
41
  const [from, to] = this.portRange;
@@ -163,22 +164,71 @@ export class ReactApp implements Application {
163
164
  const bundler = await this.getBundler(context);
164
165
  const bundleResult = await bundler.run();
165
166
 
166
- return computeResults(bundleResult, { publicDir: `${this.getPublicDir(context.artifactsDir)}/${this.dir}` });
167
+ if (this.ssr) {
168
+ const bunlder = await this.getSsrBundler(context);
169
+ await bunlder.run();
170
+ }
171
+
172
+ return computeResults(bundleResult, {
173
+ publicDir: `${this.getPublicDir(context.artifactsDir)}/${this.dir}`,
174
+ ssrPublicDir: this.ssr ? `${this.getPublicDir(context.artifactsDir)}/${this.ssrDir}` : undefined,
175
+ });
167
176
  }
168
177
 
169
178
  private getBundler(context: AppBuildContext) {
170
179
  if (this.bundler) return this.bundler;
171
180
  return this.getDefaultBundler(context);
172
181
  }
182
+
183
+ private getSsrBundler(context: AppBuildContext) {
184
+ if (this.ssrBundler) return this.ssrBundler;
185
+ return this.getDefaultSsrBundler(context);
186
+ }
187
+
173
188
  private async getDefaultBundler(context: AppBuildContext) {
174
189
  const { capsule } = context;
175
- const reactEnv: ReactEnv = context.env as ReactEnv;
176
190
  const publicDir = this.getPublicDir(context.artifactsDir);
177
191
  const outputPath = join(capsule.path, publicDir);
192
+
193
+ const bundlerContext = await this.getBuildContext(context, { outputPath });
194
+ const transformers: WebpackConfigTransformer[] = compact([
195
+ (configMutator) => configMutator.merge(buildConfig({ outputPath: join(outputPath, this.dir) })),
196
+ (config) => {
197
+ if (this.prerender) config.addPlugin(prerenderPlugin(this.prerender));
198
+ return config;
199
+ },
200
+ replaceTerserPlugin({ prerender: !!this.prerender }),
201
+ ...this.transformers,
202
+ ]);
203
+
204
+ const reactEnv = context.env as ReactEnv;
205
+ const bundler: Bundler = await reactEnv.getBundler(bundlerContext, transformers);
206
+ return bundler;
207
+ }
208
+
209
+ private async getDefaultSsrBundler(context: AppBuildContext) {
210
+ const { capsule } = context;
211
+ const publicDir = this.getPublicDir(context.artifactsDir);
212
+ const outputPath = join(capsule.path, publicDir);
213
+
214
+ const bundlerContext = await this.getBuildContext(context, { outputPath });
215
+ const transformers: WebpackConfigTransformer[] = compact([
216
+ (configMutator) => configMutator.merge(ssrBuildConfig({ outputPath: join(outputPath, this.ssrDir) })),
217
+ replaceTerserPlugin({ prerender: !!this.prerender }),
218
+ ...this.transformers,
219
+ ]);
220
+
221
+ const reactEnv = context.env as ReactEnv;
222
+ const bundler: Bundler = await reactEnv.getBundler(bundlerContext, transformers);
223
+ return bundler;
224
+ }
225
+
226
+ private async getBuildContext(context: AppBuildContext, { outputPath }: { outputPath: string }) {
227
+ const { capsule } = context;
228
+ const reactEnv = context.env as ReactEnv;
178
229
  const { distDir } = reactEnv.getCompiler();
179
- const targetEntries = Array.isArray(this.entry) ? this.entry : await this.entry();
230
+ const targetEntries = await this.getEntries(`${capsule.path}/${distDir}`);
180
231
  const entries = targetEntries.map((entry) => require.resolve(`${capsule.path}/${distDir}/${basename(entry)}`));
181
- const staticDir = join(outputPath, this.dir);
182
232
 
183
233
  const bundlerContext: BundlerContext = Object.assign(context, {
184
234
  targets: [
@@ -199,87 +249,16 @@ export class ReactApp implements Application {
199
249
  },
200
250
  });
201
251
 
202
- const defaultTransformer: WebpackConfigTransformer = (configMutator) => {
203
- const config = configMutator.addTopLevel('output', {
204
- path: staticDir,
205
- publicPath: `/`,
206
- filename: '[name].[chunkhash].js',
207
- });
208
- if (this.prerender) config.addPlugin(prerenderPlugin(this.prerender));
209
- if (config.raw.optimization?.minimizer) {
210
- remove(config.raw.optimization?.minimizer, (minimizer) => {
211
- return minimizer.constructor.name === 'TerserPlugin';
212
- });
213
-
214
- config.raw.optimization?.minimizer.push(this.getESBuildConfig());
215
- }
216
-
217
- return config;
218
- };
219
- const transformers = [defaultTransformer, ...this.transformers];
220
- const bundler: Bundler = await reactEnv.getBundler(bundlerContext, transformers);
221
- return bundler;
252
+ return bundlerContext;
222
253
  }
223
- private getESBuildConfig = (isPrerendering = this.prerender) => {
224
- // We don't want to use esbuild in case of prerendering since the headless browser puppeteer doesn't support
225
- // the output of esbuild
226
-
227
- if (isPrerendering) {
228
- return new TerserPlugin({
229
- extractComments: false,
230
- terserOptions: {
231
- parse: {
232
- // We want terser to parse ecma 8 code. However, we don't want it
233
- // to apply any minification steps that turns valid ecma 5 code
234
- // into invalid ecma 5 code. This is why the 'compress' and 'output'
235
- // sections only apply transformations that are ecma 5 safe
236
- // https://github.com/facebook/create-react-app/pull/4234
237
- ecma: 8,
238
- },
239
- compress: {
240
- ecma: 5,
241
- warnings: false,
242
- // Disabled because of an issue with Uglify breaking seemingly valid code:
243
- // https://github.com/facebook/create-react-app/issues/2376
244
- // Pending further investigation:
245
- // https://github.com/mishoo/UglifyJS2/issues/2011
246
- comparisons: false,
247
- // Disabled because of an issue with Terser breaking valid code:
248
- // https://github.com/facebook/create-react-app/issues/5250
249
- // Pending further investigation:
250
- // https://github.com/terser-js/terser/issues/120
251
- inline: 2,
252
- },
253
- mangle: {
254
- safari10: true,
255
- },
256
- output: {
257
- ecma: 5,
258
- comments: false,
259
- // Turned on because emoji and regex is not minified properly using default
260
- // https://github.com/facebook/create-react-app/issues/2488
261
- ascii_only: true,
262
- },
263
- },
264
- });
265
- }
266
- return new TerserPlugin({
267
- minify: TerserPlugin.esbuildMinify,
268
- // `terserOptions` options will be passed to `esbuild`
269
- // Link to options - https://esbuild.github.io/api/#minify
270
- terserOptions: {
271
- minify: true,
272
- },
273
- });
274
- };
275
254
 
276
255
  private getPublicDir(artifactsDir: string) {
277
256
  return join(artifactsDir, this.applicationType, this.name);
278
257
  }
279
258
 
280
- async getEntries(): Promise<string[]> {
259
+ async getEntries(path?: string): Promise<string[]> {
281
260
  if (Array.isArray(this.entry)) return this.entry;
282
- return this.entry();
261
+ return this.entry(path);
283
262
  }
284
263
 
285
264
  async getSsrEntries(): Promise<string[]> {
@@ -1,7 +1,10 @@
1
1
  import { WebpackConfigMutator } from '@teambit/webpack';
2
+ import { remove } from 'lodash';
3
+ import TerserPlugin from 'terser-webpack-plugin';
2
4
 
3
5
  export function addDevServer(configMutator: WebpackConfigMutator) {
4
6
  return configMutator.addTopLevel('devServer', {
7
+ allowedHosts: 'all',
5
8
  historyApiFallback: {
6
9
  index: '/index.html',
7
10
  disableDotRule: true,
@@ -18,3 +21,69 @@ export function setOutput(configMutator: WebpackConfigMutator) {
18
21
 
19
22
  return configMutator;
20
23
  }
24
+
25
+ export function replaceTerserPlugin({ prerender = false }: { prerender: boolean }) {
26
+ return (configMutator: WebpackConfigMutator) => {
27
+ if (!configMutator.raw.optimization?.minimizer) return configMutator;
28
+
29
+ remove(configMutator.raw.optimization?.minimizer, (minimizer) => {
30
+ return minimizer.constructor.name === 'TerserPlugin';
31
+ });
32
+
33
+ const terserer = prerender ? CreateTerserPluginForPrerender() : CreateTerserPlugin();
34
+ configMutator.raw.optimization?.minimizer.push(terserer);
35
+
36
+ return configMutator;
37
+ };
38
+ }
39
+
40
+ function CreateTerserPlugin() {
41
+ return new TerserPlugin({
42
+ minify: TerserPlugin.esbuildMinify,
43
+ // `terserOptions` options will be passed to `esbuild`
44
+ // Link to options - https://esbuild.github.io/api/#minify
45
+ terserOptions: {
46
+ minify: true,
47
+ },
48
+ });
49
+ }
50
+
51
+ function CreateTerserPluginForPrerender() {
52
+ return new TerserPlugin({
53
+ extractComments: false,
54
+ terserOptions: {
55
+ parse: {
56
+ // We want terser to parse ecma 8 code. However, we don't want it
57
+ // to apply any minification steps that turns valid ecma 5 code
58
+ // into invalid ecma 5 code. This is why the 'compress' and 'output'
59
+ // sections only apply transformations that are ecma 5 safe
60
+ // https://github.com/facebook/create-react-app/pull/4234
61
+ ecma: 8,
62
+ },
63
+ compress: {
64
+ ecma: 5,
65
+ warnings: false,
66
+ // Disabled because of an issue with Uglify breaking seemingly valid code:
67
+ // https://github.com/facebook/create-react-app/issues/2376
68
+ // Pending further investigation:
69
+ // https://github.com/mishoo/UglifyJS2/issues/2011
70
+ comparisons: false,
71
+ // Disabled because of an issue with Terser breaking valid code:
72
+ // https://github.com/facebook/create-react-app/issues/5250
73
+ // Pending further investigation:
74
+ // https://github.com/terser-js/terser/issues/120
75
+ inline: 2,
76
+ },
77
+ mangle: {
78
+ safari10: true,
79
+ },
80
+ output: {
81
+ ecma: 5,
82
+ comments: false,
83
+ // Turned on because emoji and regex is not minified properly using default
84
+ // https://github.com/facebook/create-react-app/issues/2488
85
+ ascii_only: true,
86
+ },
87
+ },
88
+ });
89
+ }
@@ -35,3 +35,27 @@ export function ssrConfig(): Configuration {
35
35
  },
36
36
  };
37
37
  }
38
+
39
+ export function buildConfig({ outputPath }: { outputPath: string }): Configuration {
40
+ return {
41
+ output: {
42
+ path: outputPath,
43
+ publicPath: `/`,
44
+ filename: '[name].[chunkhash].js',
45
+ },
46
+ };
47
+ }
48
+
49
+ export function ssrBuildConfig({ outputPath }: { outputPath: string }): Configuration {
50
+ return {
51
+ target: 'node',
52
+ devtool: 'eval-cheap-source-map',
53
+
54
+ output: {
55
+ path: outputPath,
56
+ publicPath: `/`,
57
+ libraryTarget: 'commonjs',
58
+ filename: 'index.js',
59
+ },
60
+ };
61
+ }
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <testsuites>
3
- <testsuite name="teambit.react/react@0.0.804" timestamp="2022-08-01T10:17:25.882Z" tests="1" failures="0" errors="0" skipped="0">
4
- <testcase classname="index-file.spec.js" name="should match snapshot" time="0.256"/>
3
+ <testsuite name="teambit.react/react@0.0.807" timestamp="2022-08-04T03:53:03.273Z" tests="1" failures="0" errors="0" skipped="0">
4
+ <testcase classname="index-file.spec.js" name="should match snapshot" time="0.184"/>
5
5
  </testsuite>
6
6
  </testsuites>