Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Next.js example to check bundle size on web #3650

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/check-bundle-size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Check bundle size on web
on:
pull_request:
paths:
- .github/workflows/check-bundle-size.yml
- 'src/**'
- 'NextExample/**'
- '*'
push:
branches:
- main

env:
MAX_SIZE_KB: 30
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan is that we will increase this value accordingly if needed.


jobs:
check:
runs-on: ubuntu-latest
concurrency:
group: check-bundle-size-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout Git repository
uses: actions/checkout@v3

- name: Install Reanimated node_modules
run: yarn install --frozen-lockfile

- name: Build Reanimated package
run: ./createNPMPackage.sh nightly

- name: Install NextExample node_modules
working-directory: NextExample
run: yarn install --frozen-lockfile

- name: Install Reanimated package
working-directory: NextExample
run: yarn add file:`find .. -name "react-native-reanimated-*.tgz"`

- name: Compile production build
working-directory: NextExample
run: yarn next build | tee output.txt

- name: Check bundle size
working-directory: NextExample
run: if [[ $SIZE_KB -gt $MAX_SIZE_KB ]]; then false || echo "Bundle size is greater than $MAX_SIZE_KB kB." ; fi
40 changes: 40 additions & 0 deletions NextExample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

.expo
.node_modules
.next
5 changes: 5 additions & 0 deletions NextExample/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": false,
"useTabs": false,
"singleQuote": true
}
31 changes: 31 additions & 0 deletions NextExample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# React Native Web example

This example features how to use [react-native-web](https://github.com/necolas/react-native-web) to bring the platform-agnostic Components and APIs of React Native to the web.

> **High-quality user interfaces**: React Native for Web makes it easy to create fast, adaptive web UIs in JavaScript. It provides native-like interactions, support for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL layout, built-in accessibility, and integrates with React Dev Tools.
>
> **Write once, render anywhere**: React Native for Web interoperates with existing React DOM components and is compatible with the majority of the React Native API. You can develop new components for native and web without rewriting existing code. React Native for Web can also render to HTML and critical CSS on the server using Node.js.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-react-native-web)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-react-native-web&project-name=with-react-native-web&repository-name=with-react-native-web)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:

```bash
npx create-next-app --example with-react-native-web with-react-native-web-app
```

```bash
yarn create next-app --example with-react-native-web with-react-native-web-app
```

```bash
pnpm create next-app --example with-react-native-web with-react-native-web-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
4 changes: 4 additions & 0 deletions NextExample/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
tomekzaw marked this conversation as resolved.
Show resolved Hide resolved
"name": "NextExample",
"displayName": "NextExample"
}
9 changes: 9 additions & 0 deletions NextExample/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use the reanimated SWC plug-in instead of Babel? Or perhaps do a run with and a run without it, using an env variable?

For reference: https://github.com/showtime-xyz/showtime-frontend/blob/staging/apps/next/next.config.js#L91-L99

This isn’t a requirement for this PR, but would be useful for testing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, we can test the SWC plugin as well once it's merged (in a separate PR).

presets: ['next/babel', ['babel-preset-expo', { jsxRuntime: 'automatic' }]],
plugins: [
['@babel/plugin-proposal-class-properties', { loose: true }],
['@babel/plugin-proposal-private-methods', { loose: true }],
['@babel/plugin-proposal-private-property-in-object', { loose: true }],
'react-native-reanimated/plugin',
],
}
39 changes: 39 additions & 0 deletions NextExample/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @ts-check

const enabled = process.env.ANALYZE === 'true'

if (enabled) {
console.log('[next-config] Analyzing bundle size...')
}

const withNextBundleAnalyzer = require('@next/bundle-analyzer')({
enabled,
})

const withTM = require('next-transpile-modules')(['react-native-reanimated'])

const compose = require('next-compose-plugins')

const { withExpo } = require('@expo/next-adapter')

module.exports = compose(
[withTM, withNextBundleAnalyzer, [withExpo, { projectRoot: __dirname }]],
/** @type {import('next').NextConfig} */
{
webpack5: true,
eslint: {
ignoreDuringBuilds: true,
},
webpack(config, _options) {
// config.resolve.alias = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could make this point to the locally-built folder.

// ...config.resolve.alias,
// 'react-native-reanimated': require('path').resolve(
// __dirname,
// 'lib/react-native-reanimated/src/index.js'
// ),
// }

return config
},
}
)
26 changes: 26 additions & 0 deletions NextExample/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"analyze": "ANALYZE=true next build",
"cache:clear": "rm -rf .expo .next node_modules/.cache"
},
"dependencies": {
"next": "latest",
"raf": "^3.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native-reanimated": "3.0.0-rc.3",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any option to use link:../ here so we can have fast refresh when we edit package files? cc @nandorojo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an easier method is probably to un-comment the webpack config resolver in next.config.js and point it to the right place. but link might also work. i assume this is using yarn link? i don’t think i’ve tried this before

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this approach support fast-refresh also when changing Reanimated codebase, e.g. src/reanimated2/core.ts?

"react-native-web": "^0.18.9"
},
"devDependencies": {
"@expo/config": "^7.0.1",
"@expo/next-adapter": "^4.0.12",
"@next/bundle-analyzer": "^12.3.1",
"babel-plugin-react-native-web": "^0.18.9",
"next-compose-plugins": "^2.2.1",
"next-transpile-modules": "^9.0.0"
}
}
5 changes: 5 additions & 0 deletions NextExample/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'raf/polyfill'

export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
1 change: 1 addition & 0 deletions NextExample/pages/_document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@expo/next-adapter/document'
41 changes: 41 additions & 0 deletions NextExample/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { StyleSheet, Text, View } from 'react-native'

const styles = StyleSheet.create({
container: {
alignItems: 'center',
flexGrow: 1,
justifyContent: 'center',
},
link: {
color: 'blue',
},
textContainer: {
alignItems: 'center',
marginTop: 16,
},
text: {
alignItems: 'center',
fontSize: 24,
marginBottom: 24,
},
})

export default function App(props) {
return (
<View style={styles.container}>
<Text accessibilityRole="header" style={styles.text}>
React Native for Web & Next.js
</Text>

<Text style={styles.link} accessibilityRole="link" href={`/alternate`}>
A universal link
</Text>

<View style={styles.textContainer}>
<Text accessibilityRole="header" aria-level="2" style={styles.text}>
Subheader
</Text>
</View>
</View>
)
}
43 changes: 43 additions & 0 deletions NextExample/pages/reanimated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Animated, {
useAnimatedStyle,
useSharedValue,
withRepeat,
withTiming,
} from 'react-native-reanimated'
import { StyleSheet, View } from 'react-native'

import { useEffect } from 'react'

export default function App() {
const sv = useSharedValue(0)

useEffect(() => {
sv.value = 0
sv.value = withRepeat(withTiming(1), -1, true)
})

const animatedStyle = useAnimatedStyle(() => {
return {
width: sv.value * 200 + 10,
}
})

return (
<View style={styles.container}>
<Animated.View style={[styles.box, animatedStyle]} />
</View>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
box: {
height: 100,
backgroundColor: 'blue',
},
})
Loading