1 |
|
2 | <h1 align="center">
|
3 | 👋 Welcome to <br/><code>@expo/webpack-config</code>
|
4 | </h1>
|
5 |
|
6 |
|
7 |
|
8 | <p align="center">
|
9 | <b>Webpack config that's optimized for running universal React and react-native-web projects</b>
|
10 | <br/>
|
11 | <br/>
|
12 | <a aria-label="Circle CI" href="https://circleci.com/gh/expo/expo-cli/tree/master">
|
13 | <img alt="Circle CI" src="https://flat.badgen.net/circleci/github/expo/expo-cli?label=Circle%20CI&labelColor=555555&icon=circleci">
|
14 | </a>
|
15 | </p>
|
16 |
|
17 | ---
|
18 |
|
19 | ## [Documentation][docs]
|
20 |
|
21 | To learn more about how to use this Webpack config, check out the docs here: [Customizing the Webpack config][docs]
|
22 |
|
23 | ### Contributing to the docs
|
24 |
|
25 | - [Documentation for the master branch][docs-latest]
|
26 | - [Documentation for the latest stable release][docs]
|
27 |
|
28 | ## API
|
29 |
|
30 | Running `expo customize:web` will generate this default config in your project.
|
31 |
|
32 | ```js
|
33 | const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
34 |
|
35 | module.exports = async function(env, argv) {
|
36 | const config = await createExpoWebpackConfigAsync(env, argv);
|
37 | // Customize the config before returning it.
|
38 | return config;
|
39 | };
|
40 | ```
|
41 |
|
42 | ## Types
|
43 |
|
44 | ### `Environment`
|
45 |
|
46 | The main options used to configure how `@expo/webpack-config` works.
|
47 |
|
48 | | name | type | default | description |
|
49 | | --------------------------- | --------------------------------------- | ----------- | -------------------------------------------------------------------- |
|
50 | | `projectRoot` | `string` | required | Root of the Expo project. |
|
51 | | `https` | `boolean` | `false` | Should the dev server use https protocol. |
|
52 | | `offline` | `boolean` | `false` | Passing `true` will enable offline support and add a service worker. |
|
53 | | `mode` | `Mode` | required | The Webpack mode to bundle the project in. |
|
54 | | `platform` | [`ExpoPlatform`](#ExpoPlatform) | required | The target platform to bundle for. |
|
55 | | `pwa` | `boolean` | `true` | Generate the PWA image assets in production mode. |
|
56 | | `babel` | [`ExpoBabelOptions`](#ExpoBabelOptions) | `undefined` | Control how the default Babel loader is configured. |
|
57 | | `removeUnusedImportExports` | `boolean` | `false` | Enables advanced tree-shaking with deep scope analysis. |
|
58 |
|
59 | ### `Environment` internal
|
60 |
|
61 | | name | type | default | description |
|
62 | | ----------- | ------------ | ----------- | ------------------------------------------------------------------ |
|
63 | | `config` | `ExpoConfig` | `undefined` | The Expo project config, this should be read using `@expo/config`. |
|
64 | | `locations` | `FilePaths` | `undefined` | Paths used to locate where things are. |
|
65 |
|
66 | ### `ExpoPlatform`
|
67 |
|
68 | | type | description |
|
69 | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
70 | | `'ios' | 'android' | 'web' | 'electron'` | The target platform to bundle for. Native platforms are experimental and require a special native runtime. |
|
71 |
|
72 | ### `ExpoBabelOptions`
|
73 |
|
74 | Control how the default Babel loader is configured.
|
75 |
|
76 | | name | type | default | description |
|
77 | | -------------------------------------- | ---------- | ----------- | ------------------------------------------------------------------------- |
|
78 | | `dangerouslyAddModulePathsToTranspile` | `string[]` | `undefined` | Add the names of node_modules that should be included transpilation step. |
|
79 |
|
80 | ## Guides
|
81 |
|
82 | ### PWAs
|
83 |
|
84 | - See the docs for [`expo-pwa`](../pwa) to learn more about creating the assets manually.
|
85 | - Disable automatic PWA generation with `expo build:web --no-pwa`.
|
86 | - `expo build:web` will automatically skip any PWA asset that's already linked in the project's local `web/index.html`.
|
87 | - Having sharp CLI installed globally will speed up asset generation, if it's not installed, Jimp will be used instead.
|
88 |
|
89 | #### Chrome PWAs
|
90 |
|
91 | ##### Manifest.json
|
92 |
|
93 | The `manifest.json` will be created using the values in the project's `app.config.js`:
|
94 |
|
95 | Generating the `manifest.json` will be skipped if the following exists in the project's `web/index.html`:
|
96 |
|
97 | <details><summary>Show HTML</summary>
|
98 |
|
99 | ```html
|
100 | <link rel="manifest" href="..." />
|
101 | ```
|
102 |
|
103 | </details>
|
104 |
|
105 | If the `icons` array is defined in your `manifest.json`, then Chrome PWA icon generation will be skipped.
|
106 |
|
107 | ##### Chrome PWA Icons
|
108 |
|
109 | Icons will be generated using the file defined in your `app.config.js` under `android.icon` and it'll fallback to `icon`.
|
110 |
|
111 | <details><summary>Show manifest.json</summary>
|
112 |
|
113 | ```json
|
114 | {
|
115 | "icons": [
|
116 | {
|
117 | "src": "...",
|
118 | "sizes": "144x144",
|
119 | "type": "image/png"
|
120 | },
|
121 | {
|
122 | "src": "...",
|
123 | "sizes": "192x192",
|
124 | "type": "image/png"
|
125 | },
|
126 | {
|
127 | "src": "...",
|
128 | "sizes": "512x512",
|
129 | "type": "image/png"
|
130 | }
|
131 | ]
|
132 | }
|
133 | ```
|
134 |
|
135 | </details>
|
136 |
|
137 | #### Favicons
|
138 |
|
139 | Favicons will be generated using the file defined in your `app.config.js` under `web.favicon` and it'll fallback to `icon`.
|
140 |
|
141 | Asset generation for Favicons will be individually skipped if any of the following fields exist in your `web/index.html`:
|
142 |
|
143 | <details><summary>Show HTML</summary>
|
144 |
|
145 | ```html
|
146 | <link rel="icon" type="image/png" sizes="16x16" href="..." />
|
147 | <link rel="icon" type="image/png" sizes="32x32" href="..." />
|
148 | <link rel="shortcut icon" href="..." />
|
149 | ```
|
150 |
|
151 | </details>
|
152 |
|
153 | #### Safari PWAs
|
154 |
|
155 | Icons will be generated using the file defined in your `app.config.js` under `ios.icon` and it'll fallback to `icon`. The splash screens look at `ios.splash` and fallback to `splash`.
|
156 |
|
157 | Asset generation for Safari PWA icons/splash screens will be individually skipped if any of the following fields exist in your `web/index.html`:
|
158 |
|
159 | ##### Icons
|
160 |
|
161 | <details><summary>Show HTML</summary>
|
162 |
|
163 | ```html
|
164 | <link rel="apple-touch-icon" sizes="180x180" href="..." />
|
165 | ```
|
166 |
|
167 | </details>
|
168 |
|
169 | ##### Splash Screens
|
170 |
|
171 | <details><summary>Show HTML</summary>
|
172 |
|
173 | ```html
|
174 | <link
|
175 | rel="apple-touch-startup-image"
|
176 | media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
177 | href="..."
|
178 | />
|
179 | <link
|
180 | rel="apple-touch-startup-image"
|
181 | media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
182 | href="..."
|
183 | />
|
184 | <link
|
185 | rel="apple-touch-startup-image"
|
186 | media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
187 | href="..."
|
188 | />
|
189 | <link
|
190 | rel="apple-touch-startup-image"
|
191 | media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
192 | href="..."
|
193 | />
|
194 | <link
|
195 | rel="apple-touch-startup-image"
|
196 | media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
197 | href="..."
|
198 | />
|
199 | <link
|
200 | rel="apple-touch-startup-image"
|
201 | media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
202 | href="..."
|
203 | />
|
204 | <link
|
205 | rel="apple-touch-startup-image"
|
206 | media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
207 | href="..."
|
208 | />
|
209 | <link
|
210 | rel="apple-touch-startup-image"
|
211 | media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
212 | href="..."
|
213 | />
|
214 | <link
|
215 | rel="apple-touch-startup-image"
|
216 | media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
217 | href="..."
|
218 | />
|
219 | <link
|
220 | rel="apple-touch-startup-image"
|
221 | media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
222 | href="..."
|
223 | />
|
224 | <link
|
225 | rel="apple-touch-startup-image"
|
226 | media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
227 | href="..."
|
228 | />
|
229 | <link
|
230 | rel="apple-touch-startup-image"
|
231 | media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
232 | href="..."
|
233 | />
|
234 | ```
|
235 |
|
236 | </details>
|
237 |
|
238 | ### Include modules
|
239 |
|
240 | You may find that you want to include universal modules that aren't part of the default modules. You can do this by customizing the Webpack config:
|
241 |
|
242 | ```ts
|
243 | const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
244 |
|
245 | module.exports = async function(env, argv) {
|
246 | const config = await createExpoWebpackConfigAsync(
|
247 | {
|
248 | ...env,
|
249 | babel: {
|
250 | dangerouslyAddModulePathsToTranspile: [
|
251 | // Ensure that all packages starting with @evanbacon are transpiled.
|
252 | '@evanbacon',
|
253 | ],
|
254 | },
|
255 | },
|
256 | argv
|
257 | );
|
258 | return config;
|
259 | };
|
260 | ```
|
261 |
|
262 | **`withUnimodules`**
|
263 |
|
264 | If you're adding support to some other Webpack config like in Storybook or Gatsby you can use the same process to include custom modules:
|
265 |
|
266 | ```ts
|
267 | const { withUnimodules } = require('@expo/webpack-config/addons');
|
268 |
|
269 | module.exports = function() {
|
270 | const someWebpackConfig = {
|
271 | /* Your custom Webpack config */
|
272 | };
|
273 |
|
274 | // Add Expo support...
|
275 | const configWithExpo = withUnimodules(someWebpackConfig, {
|
276 | projectRoot: __dirname,
|
277 | babel: {
|
278 | dangerouslyAddModulePathsToTranspile: [
|
279 | // Ensure that all packages starting with @evanbacon are transpiled.
|
280 | '@evanbacon',
|
281 | ],
|
282 | },
|
283 | });
|
284 |
|
285 | return configWithExpo;
|
286 | };
|
287 | ```
|
288 |
|
289 | This method should be used instead of using the `expo.web.build.babel.include` field of the `app.json`.
|
290 |
|
291 | ### Modify the babel loader
|
292 |
|
293 | If you want to modify the babel loader further, you can retrieve it using the helper method `getExpoBabelLoader` like this:
|
294 |
|
295 | ```ts
|
296 | const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
297 | const { getExpoBabelLoader } = require('@expo/webpack-config/utils');
|
298 |
|
299 | module.exports = async function(env, argv) {
|
300 | const config = await createExpoWebpackConfigAsync(env, argv);
|
301 | const loader = getExpoBabelLoader(config);
|
302 | if (loader) {
|
303 | // Modify the loader...
|
304 | }
|
305 | return config;
|
306 | };
|
307 | ```
|
308 |
|
309 | ### Service workers
|
310 |
|
311 | Service workers are great for emulating native functionality, but they can also lead to a lot of confusion so they are opt-in only (starting in Expo SDK 39 and greater) in this Webpack config. To enable the default workbox plugin pass the options `{ offline: true }` to the creator method.
|
312 |
|
313 | #### How Expo service workers ... work
|
314 |
|
315 | By default Expo web has a two part service worker. The first part `web/expo-service-worker.js` setups up optional features of the Expo SDK like Notifications. The second part `web/service-worker.js` is generated by [Workbox][workbox] and manages the offline support.
|
316 |
|
317 | The entry point for the service workers is a file called: [`register-service-worker.js`](./web-default/register-service-worker.js). This file gets copied to your static folder and registered in Webpack's `entry` property (which is why you don't see it referenced in `index.html`).
|
318 |
|
319 | #### Extending the service worker
|
320 |
|
321 | If you'd like to add extra functionality, you can simply:
|
322 |
|
323 | - Eject the Webpack config: `expo customize:web`
|
324 | - Select `web/expo-service-worker.js`
|
325 | - Modify the `web/expo-service-worker.js` however you'd like!
|
326 |
|
327 | #### Fully disabling the service worker
|
328 |
|
329 | This can have some unfortunate side-effects as application libraries like expo-notifications may expect the SW to exist. Proceed with caution.
|
330 |
|
331 | - Eject the Webpack config: `expo customize:web`
|
332 | - Select `webpack.config.js`
|
333 | - Modify the `webpack.config.js`:
|
334 |
|
335 | ```js
|
336 | const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
337 |
|
338 | module.exports = async function(env, argv) {
|
339 | // Set offline to `false`
|
340 | const config = await createExpoWebpackConfigAsync({ ...env, offline: false }, argv);
|
341 | return config;
|
342 | };
|
343 | ```
|
344 |
|
345 | - This will do the following:
|
346 | - Skip registering [`register-service-worker.js`](./web-default/register-service-worker.js) in the Webpack config `entry`.
|
347 | - Skip including the [Webpack Workbox plugin][workbox] and creating the `web/service-worker.js`.
|
348 | - Skip including the [`web/expo-service-worker.js`](./web-default/expo-service-worker.js)
|
349 |
|
350 | [workbox]: https://developers.google.com/web/tools/workbox
|
351 |
|
352 | ## Environment Variables
|
353 |
|
354 | - `EXPO_WEBPACK_DEFINE_ENVIRONMENT_AS_KEYS`: Should the define plugin explicitly set environment variables like `process.env.FOO` instead of creating an object like `proces.env: { FOO }`. Defaults to `false`. Next.js uses this to prevent overwriting injected environment variables.
|
355 | - `IMAGE_INLINE_SIZE_LIMIT`: By default, images smaller than 10,000 bytes are encoded as a data URI in base64 and inlined in the CSS or JS build artifact. Set this to control the size limit in bytes. Setting it to 0 will disable the inlining of images. This is only used in production.
|
356 | - `EXPO_WEBPACK_FAST_REFRESH`: Enable experimental fast refresh in development mode.
|
357 |
|
358 | ## Exports
|
359 |
|
360 | ### addons
|
361 |
|
362 | For adding features to an existing Webpack config.
|
363 |
|
364 | #### `withUnimodules`
|
365 |
|
366 | ```js
|
367 | import { withUnimodules } from '@expo/webpack-config/addons';
|
368 | ```
|
369 |
|
370 | Wrap your existing webpack config with support for Unimodules (Expo web). ex: **Storybook** `({ config }) => withUnimodules(config)`
|
371 |
|
372 | **params**
|
373 |
|
374 | - `webpackConfig: AnyConfiguration = {}` Optional existing Webpack config to modify.
|
375 | - `env: InputEnvironment = {}` Optional [`Environment`][#environment] options for configuring what features the Webpack config supports.
|
376 | - `argv: Arguments = {}`
|
377 |
|
378 | #### `withWorkbox`
|
379 |
|
380 | Add offline support with Workbox (`workbox-webpack-plugin`).
|
381 |
|
382 | ```js
|
383 | import { withWorkbox } from '@expo/webpack-config/addons';
|
384 | ```
|
385 |
|
386 | #### `withOptimizations`
|
387 |
|
388 | ```js
|
389 | import { withOptimizations } from '@expo/webpack-config/addons';
|
390 | ```
|
391 |
|
392 | #### `withAlias`
|
393 |
|
394 | Apply aliases to a Webpack config.
|
395 |
|
396 | ```js
|
397 | import { withAlias } from '@expo/webpack-config/addons';
|
398 | ```
|
399 |
|
400 | #### `withDevServer`
|
401 |
|
402 | ```js
|
403 | import { withDevServer } from '@expo/webpack-config/addons';
|
404 | ```
|
405 |
|
406 | #### `withNodeMocks`
|
407 |
|
408 | ```js
|
409 | import { withNodeMocks } from '@expo/webpack-config/addons';
|
410 | ```
|
411 |
|
412 | #### `withEntry`
|
413 |
|
414 | ```js
|
415 | import { withEntry } from '@expo/webpack-config/addons';
|
416 | ```
|
417 |
|
418 | #### `withTypeScriptAsync`
|
419 |
|
420 | ```js
|
421 | import { withTypeScriptAsync } from '@expo/webpack-config/addons';
|
422 | ```
|
423 |
|
424 | ### env
|
425 |
|
426 | Getting the config, paths, mode, and various other settings in your environment.
|
427 |
|
428 | #### `getConfig`
|
429 |
|
430 | ```js
|
431 | import { getConfig } from '@expo/webpack-config/env';
|
432 | ```
|
433 |
|
434 | #### `getMode`
|
435 |
|
436 | ```js
|
437 | import { getMode } from '@expo/webpack-config/env';
|
438 | ```
|
439 |
|
440 | #### `validateEnvironment`
|
441 |
|
442 | ```js
|
443 | import { validateEnvironment } from '@expo/webpack-config/env';
|
444 | ```
|
445 |
|
446 | #### `getAliases`
|
447 |
|
448 | ```js
|
449 | import { getAliases } from '@expo/webpack-config/env';
|
450 | ```
|
451 |
|
452 | #### `getPaths`
|
453 |
|
454 | ```js
|
455 | import { getPaths } from '@expo/webpack-config/env';
|
456 | ```
|
457 |
|
458 | #### `getPathsAsync`
|
459 |
|
460 | ```js
|
461 | import { getPathsAsync } from '@expo/webpack-config/env';
|
462 | ```
|
463 |
|
464 | #### `getServedPath`
|
465 |
|
466 | ```js
|
467 | import { getServedPath } from '@expo/webpack-config/env';
|
468 | ```
|
469 |
|
470 | #### `getPublicPaths`
|
471 |
|
472 | ```js
|
473 | import { getPublicPaths } from '@expo/webpack-config/env';
|
474 | ```
|
475 |
|
476 | #### `getProductionPath`
|
477 |
|
478 | ```js
|
479 | import { getProductionPath } from '@expo/webpack-config/env';
|
480 | ```
|
481 |
|
482 | #### `getAbsolute`
|
483 |
|
484 | ```js
|
485 | import { getAbsolute } from '@expo/webpack-config/env';
|
486 | ```
|
487 |
|
488 | #### `getModuleFileExtensions`
|
489 |
|
490 | ```js
|
491 | import { getModuleFileExtensions } from '@expo/webpack-config/env';
|
492 | ```
|
493 |
|
494 | ### loaders
|
495 |
|
496 | The module rules used to load various files.
|
497 |
|
498 | #### `imageLoaderRule`
|
499 |
|
500 | ```js
|
501 | import { imageLoaderRule } from '@expo/webpack-config/loaders';
|
502 | ```
|
503 |
|
504 | This is needed for webpack to import static images in JavaScript files.
|
505 | "url" loader works like "file" loader except that it embeds assets smaller than specified limit in bytes as data URLs to avoid requests.
|
506 | A missing `test` is equivalent to a match.
|
507 |
|
508 | #### `fallbackLoaderRule`
|
509 |
|
510 | ```js
|
511 | import { fallbackLoaderRule } from '@expo/webpack-config/loaders';
|
512 | ```
|
513 |
|
514 | "file" loader makes sure those assets get served by WebpackDevServer.
|
515 | When you `import` an asset, you get its (virtual) filename.
|
516 | In production, they would get copied to the `build` folder.
|
517 | This loader doesn't use a "test" so it will catch all modules
|
518 | that fall through the other loaders.
|
519 |
|
520 | #### `styleLoaderRule`
|
521 |
|
522 | ```js
|
523 | import { styleLoaderRule } from '@expo/webpack-config/loaders';
|
524 | ```
|
525 |
|
526 | Default CSS loader.
|
527 |
|
528 | ### plugins
|
529 |
|
530 | ```js
|
531 | import /* */ '@expo/webpack-config/plugins';
|
532 | ```
|
533 |
|
534 | Custom versions of Webpack Plugins that are optimized for use with native React runtimes.
|
535 |
|
536 | #### `ExpoDefinePlugin`
|
537 |
|
538 | ```js
|
539 | import { ExpoDefinePlugin } from '@expo/webpack-config/plugins';
|
540 | ```
|
541 |
|
542 | Required for `expo-constants` https://docs.expo.io/versions/latest/sdk/constants/.
|
543 | This surfaces the `app.json` (config) as an environment variable which is then parsed by `expo-constants`.
|
544 |
|
545 | #### `ExpoHtmlWebpackPlugin`
|
546 |
|
547 | ```js
|
548 | import { ExpoHtmlWebpackPlugin } from '@expo/webpack-config/plugins';
|
549 | ```
|
550 |
|
551 | Generates an `index.html` file with the <script> injected.
|
552 |
|
553 | #### `ExpoInterpolateHtmlPlugin`
|
554 |
|
555 | ```js
|
556 | import { ExpoInterpolateHtmlPlugin } from '@expo/webpack-config/plugins';
|
557 | ```
|
558 |
|
559 | Add variables to the `index.html`.
|
560 |
|
561 | ### utils
|
562 |
|
563 | Tools for resolving fields, or searching and indexing loaders and plugins.
|
564 |
|
565 | #### `resolveEntryAsync`
|
566 |
|
567 | ```js
|
568 | import { resolveEntryAsync } from '@expo/webpack-config/utils';
|
569 | ```
|
570 |
|
571 | ## What it does not do
|
572 |
|
573 | - **Gzip compression:** This was supported in beta but later removed in favor of hosting providers like [Now](http://now.sh/) and [Netlify](https://www.netlify.com/) automatically compressing files in the server.
|
574 |
|
575 | ## License
|
576 |
|
577 | The Expo source code is made available under the [MIT license](LICENSE). Some of the dependencies are licensed differently, with the BSD license, for example.
|
578 |
|
579 |
|
580 |
|
581 | ---
|
582 |
|
583 | <p>
|
584 | <a aria-label="sponsored by expo" href="http://expo.io">
|
585 | <img src="https://img.shields.io/badge/Sponsored_by-Expo-4630EB.svg?style=for-the-badge&logo=EXPO&labelColor=000&logoColor=fff" target="_blank" />
|
586 | </a>
|
587 | <a aria-label="expo webpack-config is free to use" href="/LICENSE" target="_blank">
|
588 | <img align="right" alt="License: MIT" src="https://img.shields.io/badge/License-MIT-success.svg?style=for-the-badge&color=33CC12" target="_blank" />
|
589 | </a>
|
590 | </p>
|
591 |
|
592 | [docs]: https://docs.expo.io/guides/customizing-webpack/
|
593 | [docs-latest]: https://github.com/expo/expo/blob/master/docs/pages/versions/unversioned/guides/customizing-webpack.md
|