@enact/cli 5.0.0-alpha.4 → 5.0.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/.travis.yml CHANGED
@@ -1,12 +1,10 @@
1
+ dist: focal
1
2
  language: node_js
2
3
  node_js:
3
- - "14"
4
+ - node
4
5
  sudo: false
5
- cache:
6
- directories:
7
- - $(npm config get cache)
8
6
  install:
9
- - npm config set prefer-offline true
7
+ - npm config set prefer-offline false
10
8
  - npm install
11
9
 
12
10
  script:
package/CHANGELOG.md CHANGED
@@ -1,4 +1,43 @@
1
- ## 5.0.0-alpha.3 (April 28, 2022)
1
+ ## 5.0.0 (July 19, 2022)
2
+
3
+ * Updated dependencies.
4
+
5
+ ### pack
6
+
7
+ * Fixed build failure when `.env` file exists.
8
+ * Fixed `snapshot_blob.bin` file is not listed on the build results.
9
+
10
+ ## 5.0.0-rc.1 (Jun 23, 2022)
11
+
12
+ ### serve
13
+
14
+ * Fixed `enact serve` fails to open another port when the default port is busy.
15
+
16
+ ### pack
17
+
18
+ * Fixed `core-js` version to `3.22.8` for compatibility.
19
+ * Added `ENACT_PACK_ISOMORPHIC` as a global variable to use `hydrateRoot` instead of `createRoot` from app when isomorphic build.
20
+ * Added `ignoreWarning` config to ignore warnings from SnapshotPlugin.
21
+ * Updated webpack config to support `sass-loader` for opt-in support of SASS/SCSS files.
22
+
23
+ ## 5.0.0-alpha.5 (May 31, 2022)
24
+
25
+ * Updated the `lockfileVersion` of npm-shrinkwrap file to v2.
26
+ * Updated to the latest `eslint-config-enact`, `eslint-plugin-enact`, and `@enact/dev-utils` dependency releases.
27
+
28
+ ### create, template
29
+
30
+ * Updated `@enact/template-sandstone` dependency.
31
+
32
+ ### lint
33
+
34
+ * Updated Enact ESLint config to `4.1.0` including replacing deprecated modules and updated lint rules.
35
+
36
+ ### test
37
+
38
+ * Replaced `@testing-library/react-hooks` to `@testing-library/react` for React 18 support.
39
+
40
+ ## 5.0.0-alpha.4 (April 28, 2022)
2
41
 
3
42
  ### pack
4
43
 
package/README.md CHANGED
@@ -68,6 +68,7 @@ The @enact/cli tool will check the project's **package.json** looking for an opt
68
68
  * `ri` _[object]_ - Resolution independence options to be forwarded to the [LESS plugin](https://github.com/enactjs/less-plugin-resolution-independence). By default, will use any preset for a specified theme or fallback to sandstone.
69
69
  * `screenTypes` _[array|string]_ - Array of 1 or more screentype definitions to be used with prerender HTML initialization. Can alternatively reference a json filepath to read for screentype definitions. By default, will use any preset for a specified theme or fallback to sandstone.
70
70
  * `nodeBuiltins` _[object]_ - Configuration settings for polyfilling NodeJS built-ins. See `node` [webpack option](https://webpack.js.org/configuration/node/).
71
+ * `resolveFallback` _[object]_ - Configuration settings for redirecting module requests when normal resolving fails. See `resolve.fallback` [webpack option](https://webpack.js.org/configuration/resolve/#resolvefallback).
71
72
  * `externalStartup` _[boolean]_ - Flag whether to externalize the startup/update js that is normally inlined within prerendered app HTML output.
72
73
  * `forceCSSModules` _[boolean]_ - Flag whether to force all LESS/CSS to be processed in a modular context (not just the `*.module.css` and `*.module.less` files).
73
74
  * `deep` _[string|array]_ - 1 or more JavaScript conditions that, when met, indicate deeplinking and any prerender should be discarded.
@@ -81,10 +82,10 @@ For example:
81
82
  ...
82
83
  "enact": {
83
84
  "theme": "sandstone",
84
- "nodeBuiltins": {
85
- fs: 'empty',
86
- net: 'empty',
87
- tls: 'empty'
85
+ "resolveFallback": {
86
+ fs: false,
87
+ net: false,
88
+ tls: false
88
89
  }
89
90
  }
90
91
  ...
package/commands/pack.js CHANGED
@@ -19,7 +19,7 @@ const minimist = require('minimist');
19
19
  const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
20
20
  const printBuildError = require('react-dev-utils/printBuildError');
21
21
  const stripAnsi = require('strip-ansi');
22
- const {ProgressPlugin, webpack} = require('webpack');
22
+ const webpack = require('webpack');
23
23
  const {optionParser: app, mixins, configHelper: helper} = require('@enact/dev-utils');
24
24
 
25
25
  function displayHelp() {
@@ -147,7 +147,7 @@ function copyPublicFolder(output) {
147
147
  // Print a detailed summary of build files.
148
148
  function printFileSizes(stats, output) {
149
149
  const assets = stats
150
- .toJson({all: false, assets: true})
150
+ .toJson({all: false, assets: true, cachedAssets: true})
151
151
  .assets.filter(asset => /\.(js|css|bin)$/.test(asset.name))
152
152
  .map(asset => {
153
153
  const size = fs.statSync(path.join(output, asset.name)).size;
@@ -250,7 +250,11 @@ function api(opts = {}) {
250
250
 
251
251
  // Do this as the first thing so that any code reading it knows the right env.
252
252
  const configFactory = require('../config/webpack.config');
253
- const config = configFactory(opts.production ? 'production' : 'development', opts['ilib-additional-path']);
253
+ const config = configFactory(
254
+ opts.production ? 'production' : 'development',
255
+ opts.isomorphic,
256
+ opts['ilib-additional-path']
257
+ );
254
258
 
255
259
  // Set any entry path override
256
260
  if (opts.entry) helper.replaceMain(config, path.resolve(opts.entry));
@@ -258,8 +262,6 @@ function api(opts = {}) {
258
262
  // Set any output path override
259
263
  if (opts.output) config.output.path = path.resolve(opts.output);
260
264
 
261
- if (opts.verbose) opts.ProgressPlugin = ProgressPlugin;
262
-
263
265
  mixins.apply(config, opts);
264
266
 
265
267
  // Remove all content but keep the directory so that
package/commands/serve.js CHANGED
@@ -93,7 +93,7 @@ function hotDevServer(config, fastRefresh) {
93
93
  return config;
94
94
  }
95
95
 
96
- function devServerConfig(host, protocol, publicPath, proxy, allowedHost) {
96
+ function devServerConfig(host, port, protocol, publicPath, proxy, allowedHost) {
97
97
  let https = false;
98
98
  const {SSL_CRT_FILE, SSL_KEY_FILE} = process.env;
99
99
  if (protocol === 'https' && [SSL_CRT_FILE, SSL_KEY_FILE].every(f => f && fs.existsSync(f))) {
@@ -127,38 +127,59 @@ function devServerConfig(host, protocol, publicPath, proxy, allowedHost) {
127
127
  // Enable HTTPS if the HTTPS environment variable is set to 'true'
128
128
  https,
129
129
  host,
130
+ port,
130
131
  // Allow cross-origin HTTP requests
131
132
  headers: {
132
133
  'Access-Control-Allow-Origin': '*',
133
134
  'Access-Control-Allow-Methods': '*',
134
135
  'Access-Control-Allow-Headers': '*'
135
136
  },
136
- static: {
137
- // By default WebpackDevServer serves physical files from current directory
138
- // in addition to all the virtual build products that it serves from memory.
139
- // This is confusing because those files won’t automatically be available in
140
- // production build folder unless we copy them. However, copying the whole
141
- // project directory is dangerous because we may expose sensitive files.
142
- // Instead, we establish a convention that only files in `public` directory
143
- // get served. Our build script will copy `public` into the `build` folder.
144
- // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
145
- // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
146
- // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
147
- // Note that we only recommend to use `public` folder as an escape hatch
148
- // for files like `favicon.ico`, `manifest.json`, and libraries that are
149
- // for some reason broken when imported through webpack. If you just want to
150
- // use an image, put it in `src` and `import` it from JavaScript instead.
151
- directory: path.resolve(app.context, 'public'),
152
- publicPath: publicPath,
153
- // By default files from `contentBase` will not trigger a page reload.
154
- watch: {
155
- // Reportedly, this avoids CPU overload on some systems.
156
- // https://github.com/facebook/create-react-app/issues/293
157
- // src/node_modules is not ignored to support absolute imports
158
- // https://github.com/facebook/create-react-app/issues/1065
159
- ignored: ignoredFiles(path.resolve(app.context, 'src'))
137
+ static: [
138
+ {
139
+ // By default WebpackDevServer serves physical files from current directory
140
+ // in addition to all the virtual build products that it serves from memory.
141
+ // This is confusing because those files won’t automatically be available in
142
+ // production build folder unless we copy them. However, copying the whole
143
+ // project directory is dangerous because we may expose sensitive files.
144
+ // Instead, we establish a convention that only files in `public` directory
145
+ // get served. Our build script will copy `public` into the `build` folder.
146
+ // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
147
+ // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
148
+ // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
149
+ // Note that we only recommend to use `public` folder as an escape hatch
150
+ // for files like `favicon.ico`, `manifest.json`, and libraries that are
151
+ // for some reason broken when imported through webpack. If you just want to
152
+ // use an image, put it in `src` and `import` it from JavaScript instead.
153
+ directory: path.resolve(app.context, 'public'),
154
+ publicPath,
155
+ // By default files from `contentBase` will not trigger a page reload.
156
+ watch: {
157
+ // Reportedly, this avoids CPU overload on some systems.
158
+ // https://github.com/facebook/create-react-app/issues/293
159
+ // src/node_modules is not ignored to support absolute imports
160
+ // https://github.com/facebook/create-react-app/issues/1065
161
+ ignored: [
162
+ ignoredFiles(path.resolve(app.context, 'src')),
163
+ '/node_modules[\\/](?!@enact[\\/](?!.*node_modules))/'
164
+ ]
165
+ }
166
+ },
167
+ {
168
+ directory: path.resolve(app.context, '__mocks__'),
169
+ publicPath,
170
+ // By default files from `contentBase` will not trigger a page reload.
171
+ watch: {
172
+ // Reportedly, this avoids CPU overload on some systems.
173
+ // https://github.com/facebook/create-react-app/issues/293
174
+ // src/node_modules is not ignored to support absolute imports
175
+ // https://github.com/facebook/create-react-app/issues/1065
176
+ ignored: [
177
+ ignoredFiles(path.resolve(app.context, 'src')),
178
+ '/node_modules[\\/](?!@enact[\\/](?!.*node_modules))/'
179
+ ]
180
+ }
160
181
  }
161
- },
182
+ ],
162
183
  client: {
163
184
  webSocketURL: {
164
185
  // Enable custom sockjs pathname for websocket connection to hot reloading server.
@@ -249,8 +270,7 @@ function serve(config, host, port, open) {
249
270
  // Serve webpack assets generated by the compiler over a web sever.
250
271
  const serverConfig = Object.assign(
251
272
  {},
252
- config.devServer,
253
- devServerConfig(host, protocol, publicPath, proxyConfig, urls.lanUrlForConfig)
273
+ devServerConfig(host, resolvedPort, protocol, publicPath, proxyConfig, urls.lanUrlForConfig)
254
274
  );
255
275
  const devServer = new WebpackDevServer(serverConfig, compiler);
256
276
  // Launch WebpackDevServer.
@@ -304,8 +324,8 @@ function api(opts) {
304
324
  const config = hotDevServer(configFactory('development'), fastRefresh);
305
325
 
306
326
  // Tools like Cloud9 rely on this.
307
- const host = process.env.HOST || opts.host || config.devServer.host || '0.0.0.0';
308
- const port = parseInt(process.env.PORT || opts.port || config.devServer.port || 8080);
327
+ const host = process.env.HOST || opts.host || '0.0.0.0';
328
+ const port = parseInt(process.env.PORT || opts.port || 8080);
309
329
 
310
330
  // Start serving
311
331
  if (['node', 'async-node', 'webworker'].includes(app.environment)) {
package/config/dotenv.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const dotenv = require('dotenv');
6
- const expand = require('dotenv-expand');
6
+ const {expand} = require('dotenv-expand');
7
7
 
8
8
  // Loads all required .env files in correct order, for a given mode.
9
9
  // See https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
@@ -76,18 +76,17 @@ module.exports = {
76
76
  testURL: 'http://localhost',
77
77
  transform: {
78
78
  '^.+\\.(js|jsx|ts|tsx)$': require.resolve('./babelTransform'),
79
- '^.+\\.(css|less)$': require.resolve('./cssTransform.js'),
80
- '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|less|json)$)': require.resolve('./fileTransform')
79
+ '^.+\\.(css|less|sass|scss)$': require.resolve('./cssTransform.js'),
80
+ '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|less|sass|scss|json)$)': require.resolve('./fileTransform')
81
81
  },
82
82
  transformIgnorePatterns: [
83
83
  '[/\\\\]node_modules[/\\\\](?!@enact).+\\.(js|jsx|mjs|cjs|ts|tsx)$',
84
- '^.+\\.module\\.(css|less)$'
84
+ '^.+\\.module\\.(css|less|sass|scss)$'
85
85
  ],
86
86
  moduleNameMapper: {
87
- '^.+\\.module\\.(css|less)$': require.resolve('identity-obj-proxy'),
87
+ '^.+\\.module\\.(css|less|sass|scss)$': require.resolve('identity-obj-proxy'),
88
88
  '^@testing-library/jest-dom$': require.resolve('@testing-library/jest-dom'),
89
89
  '^@testing-library/react$': require.resolve('@testing-library/react'),
90
- '^@testing-library/react-hooks$': require.resolve('@testing-library/react-hooks'),
91
90
  '^@testing-library/user-event$': require.resolve('@testing-library/user-event'),
92
91
  '^react$': require.resolve('react'),
93
92
  // Backward compatibility for new iLib location with old Enact
@@ -41,7 +41,7 @@ const createEnvironmentHash = require('./createEnvironmentHash');
41
41
 
42
42
  // This is the production and development configuration.
43
43
  // It is focused on developer experience, fast rebuilds, and a minimal bundle.
44
- module.exports = function (env, ilibAdditionalResourcesPath) {
44
+ module.exports = function (env, isomorphic = false, ilibAdditionalResourcesPath) {
45
45
  process.chdir(app.context);
46
46
 
47
47
  // Load applicable .env files into environment variables.
@@ -100,22 +100,18 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
100
100
  process.env.INLINE_STYLES ? require.resolve('style-loader') : MiniCssExtractPlugin.loader,
101
101
  {
102
102
  loader: require.resolve('css-loader'),
103
- options: Object.assign(
104
- {importLoaders: preProcessor ? 2 : 1, sourceMap: shouldUseSourceMap},
105
- cssLoaderOptions,
106
- {
107
- url: {
108
- filter: url => {
109
- // Don't handle absolute path urls
110
- if (url.startsWith('/')) {
111
- return false;
112
- }
113
-
114
- return true;
103
+ options: Object.assign({sourceMap: shouldUseSourceMap}, cssLoaderOptions, {
104
+ url: {
105
+ filter: url => {
106
+ // Don't handle absolute path urls
107
+ if (url.startsWith('/')) {
108
+ return false;
115
109
  }
110
+
111
+ return true;
116
112
  }
117
113
  }
118
- )
114
+ })
119
115
  },
120
116
  {
121
117
  // Options for PostCSS as we reference these options twice
@@ -128,7 +124,7 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
128
124
  // https://github.com/facebook/create-react-app/issues/2677
129
125
  ident: 'postcss',
130
126
  plugins: [
131
- useTailwind && require('tailwindcss'),
127
+ useTailwind && 'tailwindcss',
132
128
  // Fix and adjust for known flexbox issues
133
129
  // See https://github.com/philipwalton/flexbugs
134
130
  'postcss-flexbugs-fixes',
@@ -176,6 +172,14 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
176
172
  }
177
173
  });
178
174
 
175
+ const getScssStyleLoaders = cssLoaderOptions =>
176
+ getStyleLoaders(cssLoaderOptions, {
177
+ loader: require.resolve('sass-loader'),
178
+ options: {
179
+ sourceMap: shouldUseSourceMap
180
+ }
181
+ });
182
+
179
183
  const getAdditionalModulePaths = paths => {
180
184
  if (!paths) return [];
181
185
  return Array.isArray(paths) ? paths : [paths];
@@ -185,6 +189,8 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
185
189
  mode: isEnvProduction ? 'production' : 'development',
186
190
  // Don't attempt to continue if there are any errors.
187
191
  bail: true,
192
+ // Webpack noise constrained to errors and warnings
193
+ stats: 'errors-warnings',
188
194
  // Use source maps during development builds or when specified by GENERATE_SOURCEMAP
189
195
  devtool: shouldUseSourceMap && (isEnvProduction ? 'source-map' : 'cheap-module-source-map'),
190
196
  // These are the "entry points" to our application.
@@ -241,6 +247,13 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
241
247
  infrastructureLogging: {
242
248
  level: 'none'
243
249
  },
250
+ ignoreWarnings: [
251
+ // We ignore 'Module not found' warnings from SnapshotPlugin
252
+ {
253
+ module: /SnapshotPlugin/,
254
+ message: /Module not found/
255
+ }
256
+ ],
244
257
  resolve: {
245
258
  // These are the reasonable defaults supported by the React/ES6 ecosystem.
246
259
  extensions: ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.json'].filter(
@@ -258,7 +271,9 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
258
271
  // and old apps referencing old iLib location with new Enact
259
272
  alias: fs.existsSync(path.join(app.context, 'node_modules', '@enact', 'i18n', 'ilib'))
260
273
  ? Object.assign({ilib: '@enact/i18n/ilib'}, app.alias)
261
- : Object.assign({'@enact/i18n/ilib': 'ilib'}, app.alias)
274
+ : Object.assign({'@enact/i18n/ilib': 'ilib'}, app.alias),
275
+ // Optional configuration for redirecting module requests.
276
+ fallback: app.resolveFallback
262
277
  },
263
278
  // @remove-on-eject-begin
264
279
  // Resolve loaders (webpack plugins for CSS, images, transpilation) from the
@@ -303,6 +318,7 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
303
318
  {
304
319
  test: /\.module\.css$/,
305
320
  use: getStyleLoaders({
321
+ importLoaders: 1,
306
322
  modules: {
307
323
  getLocalIdent,
308
324
  mode: 'local'
@@ -314,6 +330,7 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
314
330
  // The `forceCSSModules` Enact build option can be set true to universally apply
315
331
  // modular CSS support.
316
332
  use: getStyleLoaders({
333
+ importLoaders: 1,
317
334
  modules: {
318
335
  ...(app.forceCSSModules ? {getLocalIdent} : {}),
319
336
  mode: 'icss'
@@ -328,6 +345,7 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
328
345
  {
329
346
  test: /\.module\.less$/,
330
347
  use: getLessStyleLoaders({
348
+ importLoaders: 2,
331
349
  modules: {
332
350
  getLocalIdent,
333
351
  mode: 'local'
@@ -337,6 +355,7 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
337
355
  {
338
356
  test: /\.less$/,
339
357
  use: getLessStyleLoaders({
358
+ importLoaders: 2,
340
359
  modules: {
341
360
  ...(app.forceCSSModules ? {getLocalIdent} : {}),
342
361
  mode: 'icss'
@@ -344,6 +363,29 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
344
363
  }),
345
364
  sideEffects: true
346
365
  },
366
+ // Opt-in support for CSS Modules, but using SASS
367
+ // using the extension .module.scss or .module.sass
368
+ {
369
+ test: /\.module\.(scss|sass)$/,
370
+ use: getScssStyleLoaders({
371
+ importLoaders: 3,
372
+ modules: {
373
+ getLocalIdent,
374
+ mode: 'local'
375
+ }
376
+ })
377
+ },
378
+ // Opt-in support for SASS (using .scss or .sass extensions)
379
+ {
380
+ test: /\.(scss|sass)$/,
381
+ use: getScssStyleLoaders({
382
+ importLoaders: 3,
383
+ modules: {
384
+ ...(app.forceCSSModules ? {getLocalIdent} : {}),
385
+ mode: 'icss'
386
+ }
387
+ })
388
+ },
347
389
  // "file" loader handles on all files not caught by the above loaders.
348
390
  // When you `import` an asset, you get its output filename and the file
349
391
  // is copied during the build process.
@@ -363,12 +405,6 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
363
405
  }
364
406
  ].filter(Boolean)
365
407
  },
366
- // Specific webpack-dev-server options.
367
- devServer: {
368
- // Broadcast http server on the localhost, port 8080.
369
- host: '0.0.0.0',
370
- port: 8080
371
- },
372
408
  // Target app to build for a specific environment (default 'browserslist')
373
409
  target: app.environment,
374
410
  // Optional configuration for polyfilling NodeJS built-ins.
@@ -448,7 +484,10 @@ module.exports = function (env, ilibAdditionalResourcesPath) {
448
484
  // Otherwise React will be compiled in the very slow development mode.
449
485
  new DefinePlugin({
450
486
  'process.env.NODE_ENV': JSON.stringify(isEnvProduction ? 'production' : 'development'),
451
- 'process.env.PUBLIC_URL': JSON.stringify(publicPath)
487
+ 'process.env.PUBLIC_URL': JSON.stringify(publicPath),
488
+ // Define ENACT_PACK_ISOMORPHIC global variable to determine to use
489
+ // `hydrateRoot` for isomorphic build and `createRoot` for non-isomorphic build by app.
490
+ ENACT_PACK_ISOMORPHIC: isomorphic
452
491
  }),
453
492
  // Inject prefixed environment variables within code, when used
454
493
  new EnvironmentPlugin(Object.keys(process.env).filter(key => /^(REACT_APP|WDS_SOCKET)/.test(key))),
@@ -104,6 +104,99 @@ npm install --save typescript @types/react @types/react-dom @types/jest
104
104
 
105
105
  Optionally, [ESLint](https://eslint.org) can be installed globally or locally and configured within a project to enable linting support within the `enact lint` command.
106
106
 
107
+ ## Sass Support
108
+
109
+ CSS stylesheets could get larger and more complex as you develop. To help and enrich the styling of your apps, there are great CSS preprocessors
110
+ out there. Enact CLI provides [LESS](https://lesscss.org) as the default and [Sass](https://sass-lang.com) support is an optional feature since Enact CLI 5.0.0.
111
+
112
+ To use Sass, install Sass globally:
113
+
114
+ ```bash
115
+ npm install -g sass
116
+ ```
117
+
118
+ Note: If you receive an error when building the app that says `Cannot find module 'sass'`, try to set `NODE_PATH` to point global
119
+ node_modules directory like below.
120
+
121
+ ```bash
122
+ export NODE_PATH=/path/to/your/global/node_modules
123
+ ```
124
+
125
+ Now you can rename `src/App.css` to `src/App.scss` or `src/App.sass` and for using CSS modules, `src/App.module.scss` or `src/App.module.sass`. And update `src/App.js` to import `src/App.scss`. Enact CLI will compile these files properly through webpack for you.
126
+
127
+ More information can be found [here](https://sass-lang.com/guide) to learn about Sass.
128
+
129
+ ## Tailwind CSS Support
130
+
131
+ Tailwind CSS works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file.
132
+ Enact CLI supports to use Tailwind CSS as an optional feature since Enact CLI 5.0.0.
133
+
134
+ To use Tailwindcss, install Tailwindcss globally:
135
+
136
+ ```bash
137
+ npm install -g tailwindcss
138
+ ```
139
+
140
+ Note: If you receive an error when building the app that says `Cannot find module 'tailwindcss'`, try to set `NODE_PATH` to point global node_modules directory.
141
+
142
+ And then run the init command to generate tailwind.config.js in your app:
143
+
144
+ ```bash
145
+ npx tailwindcss init
146
+ ```
147
+
148
+ Add the paths to all of your template files in your tailwind.config.js file.
149
+
150
+ ```js
151
+ module.exports = {
152
+ content: [
153
+ "./src/**/*.{js,jsx,ts,tsx}",
154
+ ],
155
+ theme: {
156
+ extend: {},
157
+ },
158
+ plugins: [],
159
+ }
160
+ ```
161
+
162
+ Create `src/tailwind.css` file and add the @tailwind directives for each of Tailwind’s layers to your file.
163
+
164
+ ```css
165
+ @tailwind base;
166
+ @tailwind components;
167
+ @tailwind utilities;
168
+ ```
169
+
170
+ Import `src/tailwind.css` into your `src/index.js` file.
171
+
172
+ ```js
173
+ import {createRoot} from 'react-dom/client';
174
+ import App from './App';
175
+ import './tailwind.css';
176
+
177
+ const appElement = (<App />);
178
+ ...
179
+ ```
180
+
181
+ Now you can start using Tailwind’s utility classes to style your content.
182
+ Here is the example.
183
+
184
+ ```js
185
+ const MainPanel = kind({
186
+ name: 'MainPanel',
187
+
188
+ render: (props) => (
189
+ <Panel {...props}>
190
+ <p className="text-3xl font-bold underline">
191
+ Edit src/views/MainPanel.js and save to reload.
192
+ </p>
193
+ </Panel>
194
+ )
195
+ });
196
+ ```
197
+
198
+ More information can be found [here](https://tailwindcss.com/docs) to learn about tailwindcss.
199
+
107
200
  ## Isomorphic Support & Prerendering
108
201
  By using the isomorphic code layout option, your project bundle will be outputted in a versatile universal code format allowing potential usage outside the browser. The Enact CLI takes advantage of this mode by additionally generating an HTML output of your project and embedding it directly with the resulting **index.html**. By default, isomorphic mode will attempt to prerender only `en-US`, however with the `--locales` option, a wide variety of locales can be specified and prerendered. More details on isomorphic support and its limitations can be found [here](./isomorphic-support.md).
109
202
 
package/docs/index.md CHANGED
@@ -16,3 +16,4 @@ The following sections describe its installation and usage:
16
16
  * [Ejecting Apps](./ejecting-apps.md)
17
17
  * [Template Management](./template-management.md)
18
18
  * [Developing a Custom Template](./developing-a-template.md)
19
+ * [Measuring Performance](./measuring-performance.md)
@@ -31,6 +31,32 @@ npm pack -- --isomorphic
31
31
  npm pack-p -- --isomorphic
32
32
  ```
33
33
 
34
+ If you are using React18, you need to call `hydrateRoot` instead of `createRoot` to hydrate prerendered HTML.
35
+ Enact CLI provides a global variable `ENACT_PACK_ISOMORPHIC` to selectively call those two APIs in your app's `index.js`.
36
+ If you build with isomorphic option, `ENACT_PACK_ISOMORPHIC` will be `true`, otherwise `false`.
37
+ For more detailed information, please refer to the [React 18 migration guide](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#updates-to-client-rendering-apis).
38
+
39
+ Whithin your **src/index.js** file, add a conditional statement to render or hydrate your app:
40
+ ```js
41
+ /* global ENACT_PACK_ISOMORPHIC */
42
+ import {createRoot, hydrateRoot} from 'react-dom/client';
43
+
44
+ import App from './App';
45
+
46
+ const appElement = (<App />);
47
+
48
+ // In a browser environment, render the app to the document.
49
+ if (typeof window !== 'undefined') {
50
+ if (ENACT_PACK_ISOMORPHIC) {
51
+ hydrateRoot(document.getElementById('root'), appElement);
52
+ } else {
53
+ createRoot(document.getElementById('root')).render(appElement);
54
+ }
55
+ }
56
+
57
+ export default appElement;
58
+ ```
59
+
34
60
  ### When to Build Isomorphically
35
61
  By default, the Enact CLI will not use isomorphic code layout, and it should not be considered part of the regular development workflow. It is advisable to only build in isomorphic format when you want to test isomorphic features or in production mode builds.
36
62
 
@@ -0,0 +1,64 @@
1
+ ---
2
+ title: Measuring Performance
3
+ order: 11
4
+ ---
5
+ By default, an app generated from `enact create` with Enact CLI includes a performance relayer that allows you to measure and analyze
6
+ the performance of your application using different metrics.
7
+
8
+ To measure any of the supported metrics, you only need to pass a function into the `reportWebVitals`
9
+ function in `index.js`:
10
+
11
+ ```js
12
+ reportWebVitals(console.log);
13
+ ```
14
+
15
+ This function is fired when the final values for any of the metrics have finished calculating on the
16
+ page. You can use it to log any of the results to the console or send to a particular endpoint.
17
+
18
+ ## Web Vitals
19
+
20
+ [Web Vitals](https://web.dev/vitals/) are a set of useful metrics that aim to capture the user
21
+ experience of a web page. In Create React App, a third-party library is used to measure these
22
+ metrics ([web-vitals](https://github.com/GoogleChrome/web-vitals)).
23
+
24
+ To understand more about the object returned to the function when a metric value is calculated,
25
+ refer to the [documentation](https://github.com/GoogleChrome/web-vitals/#types). The [Browser
26
+ Support](https://github.com/GoogleChrome/web-vitals/#browser-support) section also explains which browsers are supported.
27
+
28
+ ## Sending results to analytics
29
+
30
+ With the `reportWebVitals` function, you can send any of results to an analytics endpoint to measure and track real user performance on your site. For example:
31
+
32
+ ```js
33
+ function sendToAnalytics(metric) {
34
+ const body = JSON.stringify(metric);
35
+ const url = 'https://example.com/analytics';
36
+
37
+ // Use `navigator.sendBeacon()` if available, falling back to `fetch()`
38
+ if (navigator.sendBeacon) {
39
+ navigator.sendBeacon(url, body);
40
+ } else {
41
+ fetch(url, { body, method: 'POST', keepalive: true });
42
+ }
43
+ }
44
+
45
+ reportWebVitals(sendToAnalytics);
46
+ ```
47
+
48
+ > **Note:** If you use Google Analytics, use the `id` value to make it easier to construct metric distributions manually (to calculate percentiles, etc…).
49
+ >
50
+ > ```js
51
+ > function sendToAnalytics({ id, name, value }) {
52
+ > ga('send', 'event', {
53
+ > eventCategory: 'Web Vitals',
54
+ > eventAction: name,
55
+ > eventValue: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers
56
+ > eventLabel: id, // id unique to current page load
57
+ > nonInteraction: true, // avoids affecting bounce rate
58
+ > });
59
+ > }
60
+ >
61
+ > reportWebVitals(sendToAnalytics);
62
+ > ```
63
+ >
64
+ > Read more about sending results to Google Analytics [here](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics).