Skip to content

Commit

Permalink
feat(babel): minify typeof window (#27530)
Browse files Browse the repository at this point in the history
# Why

It's common to use `typeof window === 'undefined'` to conditionally
enable/disable code for server/client environments.

`babel-preset-expo` automatically transforms `typeof window ===
'undefined` to `true` when bundling for server environments, and `false`
when bundling for websites. The check is left as-is when bundling for
native client environments to support apps that polyfill `window`.

This transform is run in both development and production, but only
removes conditional requires in production.

You can configure `babel-preset-expo` to skip the transform by passing
`{ preserveTypeofWindow: false }`.

# How

Add `babel-plugin-transform-define` and conditionally enable when
bundling for websites and server code. Skip entirely for native client
bundles as these are ambiguous. We also expose the ability to force the
flag on/off for native client in case users want a more strict runtime
protection against `typeof window`.

Additionally, I merged `process.env.EXPO_OS` and BASE_URL in this
transform to reduce passes. In a follow up PR, I'll merge
`process.env.NODE_ENV` and `__DEV__` too.

# Test Plan

- Added a bunch of babel tests for this functionality.

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
This is required for changes to Expo modules.
-->

- [ ] Documentation is up to date to reflect these changes (eg:
https://docs.expo.dev and README.md).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
Co-authored-by: Aman Mittal <amandeepmittal@live.com>
  • Loading branch information
3 people committed Mar 13, 2024
1 parent c9a3adf commit bc51c52
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 105 deletions.
64 changes: 64 additions & 0 deletions docs/pages/guides/tree-shaking.mdx
Expand Up @@ -185,6 +185,70 @@ The above code snippet is then minified, which removes the unused conditional:
- This system does not apply to server code as environment variables are not inlined in server bundles.
- Library authors should not use `EXPO_PUBLIC_` environment variables as they only run in application code for security reasons.

## Removing server code

> SDK 51 and greater
It's common to use `typeof window === 'undefined'` to conditionally enable or disable code for server and client environments.

`babel-preset-expo` automatically transforms `typeof window === 'undefined` to `true` when bundling for server environments and `false` when bundling for websites. The check remains unchanged when bundling for native client environments to support apps that polyfill `window`.

This transform is run in both development and production but only removes conditional requires in production.

You can configure `babel-preset-expo` to skip the transform by passing `{ preserveTypeofWindow: false }`.

<Step label="1">

```js Input
if (typeof window === 'undefined') {
console.log('Hello on the server!');
}
```

</Step>

<Step label="2">

The input code from the previous step is transformed to the following code snippet after `babel-preset-expo` when bundling for server environments (API routes, server rendering):

```js Post babel-preset-expo (bundling for server)
if (true) {
console.log('Hello on the server!');
}
```

Bundling client code for web browsers will change `typeof window` to `false`:

```js Post babel-preset-expo
if (false) {
console.log('Hello on the server!');
}
```

Bundling client code for native apps will leave `typeof window` in place:

```js Post babel-preset-expo
if (typeof window === 'undefined') {
console.log('Hello on the server!');
}
```

</Step>

<Step label="3">

The above code snippet is then minified, which removes the unused conditional:

```js Post minifier (server)
console.log('Hello on the server!');
```

```js Post minifier (client websites)
// Empty file
```

</Step>

## Remove unused imports and exports

As of Expo SDK 50, unused imports and exports are not removed from the production bundle. We plan to add this feature to all platforms in a future release.
Expand Down
1 change: 1 addition & 0 deletions packages/babel-preset-expo/CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@

### 🎉 New features

- Minify `typeof window` in server and web contexts. ([#27530](https://github.com/expo/expo/pull/27530) by [@EvanBacon](https://github.com/EvanBacon))
- Add support for using `process.env.EXPO_OS` to detect the platform without platform shaking imports. ([#27509](https://github.com/expo/expo/pull/27509) by [@EvanBacon](https://github.com/EvanBacon))
- Add basic `react-server` support. ([#27264](https://github.com/expo/expo/pull/27264) by [@EvanBacon](https://github.com/EvanBacon))

Expand Down
18 changes: 18 additions & 0 deletions packages/babel-preset-expo/README.md
Expand Up @@ -42,6 +42,24 @@ If the `bundler` is not defined, it will default to checking if a `babel-loader`

## Options

### `minifyTypeofWindow`

Set `minifyTypeofWindow: false` to preserve the `typeof window` check in your code, e.g. `if (typeof window === 'undefined')` -> `if (true)` in servers. This is useful when you're using libraries that mock the window object on native or in the server.

```js
[
'babel-preset-expo',
{
// If your native app doesn't polyfill `window` then setting this to `false` can reduce bundle size.
native: {
minifyTypeofWindow: true,
},
},
];
```

Defaults to `false` for server environments and web, `true` for native platforms to support legacy browser polyfills.

### `reanimated`

`boolean`, defaults to `true`. Set `reanimated: false` to disable adding the `react-native-reanimated/plugin` when `react-native-reanimated` is installed.
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-preset-expo/build/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 23 additions & 10 deletions packages/babel-preset-expo/build/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions packages/babel-preset-expo/build/inline-env-vars.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 1 addition & 32 deletions packages/babel-preset-expo/build/inline-env-vars.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/babel-preset-expo/package.json
Expand Up @@ -49,6 +49,7 @@
"@babel/preset-react": "^7.22.15",
"@react-native/babel-preset": "^0.73.18",
"babel-plugin-react-native-web": "~0.19.10",
"babel-plugin-transform-define": "^2.1.4",
"react-refresh": "0.14.0"
},
"devDependencies": {
Expand Down

0 comments on commit bc51c52

Please sign in to comment.