Skip to content

Commit

Permalink
feat: add NativeWind plugin (#841)
Browse files Browse the repository at this point in the history
* [WIP] feat: add NativeWind plugin

* chore: setup

* fix: use nativewind import for now

* feat: working on webpack

* chore: webpack setup alignment

* fix: hmr with swc/rspack

* chore: cleanup and comments

* chore: cleanup tsconfig

* chore: cleanup in tester app

* fix: double stringify

* chore: update Podfile.lock

* feat: add examples for NativeWind functionality

* feat: add a demo using NativeWind with RN Reusables

* feat: add several more NativeWind features to tester app

* fix: don't set ScriptManager storage in dev

* feat: set importSource to nativewind

* chore: remove hardcoded nativewind in core

* chore: add missing clsx

* chore: make webpack tester app work with nativewind

* chore: update webpack config in tester app

* chore: cleanup

* chore: update podfile locks

* chore: update package.json

* chore: update lockfile

* chore: remove redundant README.md in tester-app

* chore: update README.md

* chore: update babel config in tester app

* chore: add changeset

---------

Co-authored-by: Jakub Romanczyk <[email protected]>
  • Loading branch information
borisyankov and jbroma authored Jan 19, 2025
1 parent 32ae1e0 commit d9d64ef
Show file tree
Hide file tree
Showing 34 changed files with 1,510 additions and 60 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-geese-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": minor
---

Add support for NativeWind through a dedicated optional plugin called `@callstack/repack-plugin-nativewind`
9 changes: 9 additions & 0 deletions apps/tester-app/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
comments: true,
plugins: [
[
'@babel/plugin-transform-react-jsx',
{
runtime: 'automatic',
importSource: 'nativewind',
},
],
],
};
26 changes: 26 additions & 0 deletions apps/tester-app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

.custom-style-test {
color: mediumpurple;
}

.calc-element {
width: calc(var(--my-variable) - (20px + 2rem));
}

:root {
--H: 200;
--S: 50%;
--L: 40%;
--color-values: purple;
}

.color-element {
background-color: hsl(
calc(var(--H) + 20),
calc(var(--S) - 10%),
calc(var(--L) + 30%)
);
}
6 changes: 4 additions & 2 deletions apps/tester-app/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Script, ScriptManager } from '@callstack/repack/client';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './src/App';

ScriptManager.shared.setStorage(AsyncStorage);
if (!__DEV__) {
ScriptManager.shared.setStorage(AsyncStorage);
}

ScriptManager.shared.addResolver((scriptId, _caller) => {
if (__DEV__) {
return {
Expand Down
70 changes: 70 additions & 0 deletions apps/tester-app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,72 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-safe-area-context (4.14.0):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-safe-area-context/common (= 4.14.0)
- react-native-safe-area-context/fabric (= 4.14.0)
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-safe-area-context/common (4.14.0):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-safe-area-context/fabric (4.14.0):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-safe-area-context/common
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- React-nativeconfig (0.76.3)
- React-NativeModulesApple (0.76.3):
- glog
Expand Down Expand Up @@ -1756,6 +1822,7 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
Expand Down Expand Up @@ -1871,6 +1938,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
Expand Down Expand Up @@ -1978,6 +2047,7 @@ SPEC CHECKSUMS:
React-logger: 26155dc23db5c9038794db915f80bd2044512c2e
React-Mapbuffer: ad1ba0205205a16dbff11b8ade6d1b3959451658
React-microtasksnativemodule: e771eb9eb6ace5884ee40a293a0e14a9d7a4343c
react-native-safe-area-context: 2500e4fe998caad50ad3bc51ec23ef951308569e
React-nativeconfig: aeed6e2a8ac02b2df54476afcc7c663416c12bf7
React-NativeModulesApple: c5b7813da94136f50ef084fa1ac077332dcfc658
React-perflogger: 6afb7eebf7d9521cc70481688ccddf212970e9d3
Expand Down
3 changes: 3 additions & 0 deletions apps/tester-app/nativewind-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference types="nativewind/types" />

// NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind.
15 changes: 14 additions & 1 deletion apps/tester-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,23 @@
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.23.1",
"@rn-primitives/slot": "^1.1.0",
"@rn-primitives/types": "^1.1.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-native": "0.76.3",
"react-native-css-interop": "0.1.22",
"react-native-reanimated": "^3.16.3",
"react-native-svg": "15.8.0"
"react-native-safe-area-context": "^4.14.0",
"react-native-svg": "15.8.0",
"tailwind-merge": "^2.6.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@callstack/repack": "workspace:*",
"@callstack/repack-plugin-nativewind": "workspace:*",
"@callstack/repack-plugin-reanimated": "workspace:*",
"@react-native-community/cli": "15.0.1",
"@react-native-community/cli-platform-android": "15.0.1",
Expand All @@ -38,11 +47,15 @@
"@swc/helpers": "^0.5.13",
"@types/jest": "^29.5.12",
"@types/react": "^18.2.51",
"autoprefixer": "^10.4.20",
"execa": "^6.1.0",
"get-port": "^6.1.2",
"globby": "^13.1.2",
"http-server": "^14.1.1",
"postcss": "^8.4.49",
"postcss-loader": "^8.1.1",
"react-native-test-app": "^4.0.7",
"tailwindcss": "^3.4.17",
"terser-webpack-plugin": "^5.3.10",
"typescript": "^5.7.2",
"vitest": "^2.0.5",
Expand Down
4 changes: 3 additions & 1 deletion apps/tester-app/rspack.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { createRequire } from 'node:module';
import path from 'node:path';
import * as Repack from '@callstack/repack';
import { NativeWindPlugin } from '@callstack/repack-plugin-nativewind';
import { ReanimatedPlugin } from '@callstack/repack-plugin-reanimated';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';

Expand Down Expand Up @@ -76,6 +77,7 @@ export default (env) => {
transform: {
react: {
runtime: 'automatic',
importSource: 'nativewind',
},
},
},
Expand Down Expand Up @@ -157,7 +159,6 @@ export default (env) => {
},
],
},

plugins: [
/**
* Configure other required and additional plugins to make the bundle
Expand Down Expand Up @@ -198,6 +199,7 @@ export default (env) => {
// }),
process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
new ReanimatedPlugin(),
new NativeWindPlugin(),
].filter(Boolean),
};
};
12 changes: 9 additions & 3 deletions apps/tester-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Appearance } from 'react-native';

import { AppContainer } from './ui/AppContainer';
import { Section } from './ui/Section';
import { SectionContainer } from './ui/SectionContainer';
// nativewind styles
import '../global.css';

import { AssetsTestContainer } from './assetsTest/AssetsTestContainer';
import { AsyncContainer } from './asyncChunks/AsyncContainer';
import { MiniAppsContainer } from './miniapp/MiniAppsContainer';
import { NativeWindView } from './nativewind/NativeWindView';
import { ReanimatedBox } from './reanimated/ReanimatedBox';
import { RemoteContainer } from './remoteChunks/RemoteContainer';
import { AppContainer } from './ui/AppContainer';
import { Section } from './ui/Section';
import { SectionContainer } from './ui/SectionContainer';

Appearance.setColorScheme('light');

Expand All @@ -31,6 +34,9 @@ const App = () => {
<Section title="Reanimated test">
<ReanimatedBox />
</Section>
<Section title="NativeWind test">
<NativeWindView />
</Section>
</SectionContainer>
</AppContainer>
);
Expand Down
16 changes: 16 additions & 0 deletions apps/tester-app/src/nativewind/Basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { StyleSheet, Text, View } from 'react-native';

export default function Basic() {
return (
<View className="bg-blue-100 p-5 rounded-full" style={styles.container}>
<Text className="custom-style-test">Colored through CSS!</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
});
29 changes: 29 additions & 0 deletions apps/tester-app/src/nativewind/ComponentWithVariants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Text } from 'react-native';

const variantStyles = {
default: 'rounded',
primary: 'bg-blue-500 text-white',
secondary: 'bg-white-500 text-black',
};

type Props = {
variant: keyof typeof variantStyles;
className?: string;
} & React.ComponentProps<typeof Text>;

export default function ComponentWithVariants({
variant,
className,
...props
}: Props) {
return (
<Text
className={`
${variantStyles.default}
${variantStyles[variant]}
${className}
`}
{...props}
/>
);
}
10 changes: 10 additions & 0 deletions apps/tester-app/src/nativewind/CustomComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Text } from 'react-native';

export default function CustomComponent({ className }: { className: string }) {
const defaultStyles = 'p-2 text-black dark:text-white';
return (
<Text className={`${defaultStyles} ${className}`}>
I am a custom component
</Text>
);
}
13 changes: 13 additions & 0 deletions apps/tester-app/src/nativewind/DarkMode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { colorScheme } from 'nativewind';
import { Button } from 'react-native';

export default function DarkMode() {
return (
<Button
onPress={() => {
colorScheme.set('dark');
}}
title="Toggle dark mode"
/>
);
}
15 changes: 15 additions & 0 deletions apps/tester-app/src/nativewind/FuncsDirs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { vars } from 'nativewind';
import { Text, View } from 'react-native';

export default function FuncsDirs() {
return (
<>
<View style={vars({ '--my-custom-color': 'green' })}>
<Text className="text-custom">Custom color</Text>
</View>
<View className="p-2 calc-element color-element">
<Text>Calculated size</Text>
</View>
</>
);
}
22 changes: 22 additions & 0 deletions apps/tester-app/src/nativewind/NativeWindView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { View } from 'react-native';
import Basic from './Basic.tsx';
import ComponentWithVariants from './ComponentWithVariants.tsx';
import CustomComponent from './CustomComponent.tsx';
import DarkMode from './DarkMode.tsx';
import FuncsDirs from './FuncsDirs.tsx';
import Responsive from './Responsive.tsx';
import Reusables from './Reusables.tsx';

export function NativeWindView() {
return (
<View className="gap-4 p-2">
<Basic />
<Responsive />
<CustomComponent className="bg-green-600" />
<ComponentWithVariants variant="primary" className="bg-yellow-500" />
<Reusables />
<DarkMode />
<FuncsDirs />
</View>
);
}
9 changes: 9 additions & 0 deletions apps/tester-app/src/nativewind/Responsive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Text, View } from 'react-native';

export default function Responsive() {
return (
<View className="p-2 bg-orange-500 sm:bg-green-500 md:bg-red-500">
<Text className="text-white">Responsive style based on width!</Text>
</View>
);
}
10 changes: 10 additions & 0 deletions apps/tester-app/src/nativewind/Reusables.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Button } from './ui/Button';
import { Text } from './ui/Text';

export default function Reusables() {
return (
<Button variant="primary" className="bg-purple-700">
<Text className="text-white">I am a button from RN Reusables</Text>
</Button>
);
}
Loading

0 comments on commit d9d64ef

Please sign in to comment.