diff --git a/components/native/NativeText/package.json b/components/native/NativeText/package.json new file mode 100644 index 00000000..fa3128a3 --- /dev/null +++ b/components/native/NativeText/package.json @@ -0,0 +1,5 @@ +{ + "main": "../../../lib/commonjs/components/native/NativeText.js", + "module": "../../../lib/module/components/native/NativeText.js", + "react-native": "../../../src/components/native/NativeText.native.tsx" +} diff --git a/components/native/NativeView/package.json b/components/native/NativeView/package.json new file mode 100644 index 00000000..9b02cd91 --- /dev/null +++ b/components/native/NativeView/package.json @@ -0,0 +1,5 @@ +{ + "main": "../../../lib/commonjs/components/native/NativeView.js", + "module": "../../../lib/module/components/native/NativeView.js", + "react-native": "../../../src/components/native/NativeView.native.tsx" +} diff --git a/expo-example/app/(tabs)/index.tsx b/expo-example/app/(tabs)/index.tsx index 56609d92..bd9f8e54 100644 --- a/expo-example/app/(tabs)/index.tsx +++ b/expo-example/app/(tabs)/index.tsx @@ -1,6 +1,6 @@ -import { Link } from 'expo-router' import React from 'react' -import { Pressable, Text, View } from 'react-native' +import { Link } from 'expo-router' +import { Pressable, View, Text } from 'react-native' import { StyleSheet } from 'react-native-unistyles' export default function HomeScreen() { @@ -11,12 +11,12 @@ export default function HomeScreen() { return ( - + Hello world - + Explore @@ -33,6 +33,11 @@ const styles = StyleSheet.create(theme => ({ alignItems: 'center', backgroundColor: theme.colors.backgroundColor }, + typography: { + fontSize: 20, + fontWeight: 'bold', + color: theme.colors.typography + }, test: { width: '100%', variants: { diff --git a/package.json b/package.json index 5de00f24..7cf0279e 100644 --- a/package.json +++ b/package.json @@ -11,15 +11,15 @@ "precommit": "concurrently 'yarn tsc' 'yarn lint' 'yarn test'", "release": "release-it" }, - "main": "lib/commonjs/index", - "module": "lib/module/index", + "main": "lib/commonjs/index.js", + "module": "lib/module/index.js", "types": "lib/typescript/src/index.d.ts", "source": "src/index", "exports": { ".": { "types": "./lib/typescript/src/index.d.ts", - "module": "./lib/module/index", - "default": "./lib/commonjs/index", + "module": "./lib/module/index.js", + "default": "./lib/commonjs/index.js", "react-native": "./src/index" }, "./components/native/*": { @@ -34,8 +34,8 @@ "./package.json": "./package.json", "./server": { "types": "./lib/typescript/src/server/index.d.ts", - "module": "./lib/module/server/index", - "default": "./lib/commonjs/server/index", + "module": "./lib/module/server/index.js", + "default": "./lib/commonjs/server/index.js", "react-native": "./src/server" } }, diff --git a/plugin/consts.js b/plugin/consts.js index dd911f8a..9a9ff674 100644 --- a/plugin/consts.js +++ b/plugin/consts.js @@ -30,13 +30,33 @@ const REPLACE_WITH_UNISTYLES_PATHS = [ // this is more powerful API as it allows to convert unmatched imports to Unistyles // { path: string, imports: Array<{ name: string, isDefault: boolean, path: string, mapTo: string }> } -// name <- target import name +// path => node_modules path +// imports: +// name? <- target import name if isDefault is false // isDefault <- is the import default? // path <- path to the target import // mapTo <- name of the Unistyles component const REPLACE_WITH_UNISTYLES_EXOTIC_PATHS = [] +// this list will additionally detect React Native direct imports +const NATIVE_COMPONENTS_PATHS = { + imports: [ + { + name: 'NativeText', + isDefault: false, + path: 'react-native/Libraries/Text/TextNativeComponent', + mapTo: 'NativeText' + }, + { + isDefault: true, + path: 'react-native/Libraries/Components/View/ViewNativeComponent', + mapTo: 'NativeView' + } + ] +} + module.exports = { + NATIVE_COMPONENTS_PATHS, REACT_NATIVE_COMPONENT_NAMES, REPLACE_WITH_UNISTYLES_PATHS, REPLACE_WITH_UNISTYLES_EXOTIC_PATHS diff --git a/plugin/exotic.js b/plugin/exotic.js index 50c6ca42..98b5b21f 100644 --- a/plugin/exotic.js +++ b/plugin/exotic.js @@ -8,28 +8,40 @@ function handleExoticImport(t, path, state, exoticImport) { specifiers.forEach(specifier => { for (const rule of exoticImport.imports) { - const hasMatchingImportType = !rule.isDefault || t.isImportDefaultSpecifier(specifier) - const hasMatchingImportName = rule.name === specifier.local.name + const hasMatchingImportType = (!rule.isDefault && t.isImportSpecifier(specifier)) || (rule.isDefault && t.isImportDefaultSpecifier(specifier)) + const hasMatchingImportName = rule.isDefault || (!rule.isDefault && rule.name === specifier.local.name) const hasMatchingPath = rule.path === source.value if (!hasMatchingImportType || !hasMatchingImportName || !hasMatchingPath) { continue } - const newImport = t.importDeclaration( - [t.importSpecifier(t.identifier(rule.mapTo), t.identifier(rule.mapTo))], - t.stringLiteral(state.opts.isLocal - ? state.file.opts.filename.split('react-native-unistyles').at(0).concat(`react-native-unistyles/components/native/${rule.mapTo}`) - : `react-native-unistyles/components/native/${rule.mapTo}` + if (t.isImportDefaultSpecifier(specifier)) { + const newImport = t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(specifier.local.name))], + t.stringLiteral(state.opts.isLocal + ? state.file.opts.filename.split('react-native-unistyles').at(0).concat(`react-native-unistyles/components/native/${rule.mapTo}`) + : `react-native-unistyles/components/native/${rule.mapTo}` + ) ) - ) - // remove old import - if (t.isImportDefaultSpecifier(specifier)) { path.replaceWith(newImport) } else { + const newImport = t.importDeclaration( + [t.importSpecifier(t.identifier(rule.mapTo), t.identifier(rule.mapTo))], + t.stringLiteral(state.opts.isLocal + ? state.file.opts.filename.split('react-native-unistyles').at(0).concat(`react-native-unistyles/components/native/${rule.mapTo}`) + : `react-native-unistyles/components/native/${rule.mapTo}` + ) + ) + path.node.specifiers = specifiers.filter(s => s !== specifier) - path.unshift(newImport) + + if (path.node.specifiers.length === 0) { + path.replaceWith(newImport) + } else { + path.insertBefore(newImport) + } } return diff --git a/plugin/index.js b/plugin/index.js index 6367e596..ca6c5554 100644 --- a/plugin/index.js +++ b/plugin/index.js @@ -2,7 +2,7 @@ const { addUnistylesImport, isInsideNodeModules } = require('./import') const { hasStringRef } = require('./ref') const { isUnistylesStyleSheet, analyzeDependencies, addStyleSheetTag, getUnistyles, isKindOfStyleSheet } = require('./stylesheet') const { extractVariants } = require('./variants') -const { REACT_NATIVE_COMPONENT_NAMES, REPLACE_WITH_UNISTYLES_PATHS, REPLACE_WITH_UNISTYLES_EXOTIC_PATHS } = require('./consts') +const { REACT_NATIVE_COMPONENT_NAMES, REPLACE_WITH_UNISTYLES_PATHS, REPLACE_WITH_UNISTYLES_EXOTIC_PATHS, NATIVE_COMPONENTS_PATHS } = require('./consts') const { handleExoticImport } = require('./exotic') module.exports = function ({ types: t }) { @@ -109,6 +109,10 @@ module.exports = function ({ types: t }) { }) } + if (importSource.includes('react-native/Libraries')) { + handleExoticImport(t, path, state, NATIVE_COMPONENTS_PATHS) + } + if (!state.file.forceProcessing && Array.isArray(state.opts.autoProcessImports)) { state.file.forceProcessing = state.opts.autoProcessImports.includes(importSource) } diff --git a/src/components/native/NativeText.native.tsx b/src/components/native/NativeText.native.tsx new file mode 100644 index 00000000..78bd655b --- /dev/null +++ b/src/components/native/NativeText.native.tsx @@ -0,0 +1,12 @@ +import type { TextProps } from 'react-native' +import { type ComponentType, createElement, forwardRef } from 'react' +import { createUnistylesElement } from '../../core' + +// credits to @hirbod +const LeanText = forwardRef((props, ref) => { + return createElement('RCTText', { ...props, ref }) +}) as ComponentType + +LeanText.displayName = 'RCTText' + +export const NativeText = createUnistylesElement(LeanText) diff --git a/src/components/native/NativeText.tsx b/src/components/native/NativeText.tsx new file mode 100644 index 00000000..c79f9eed --- /dev/null +++ b/src/components/native/NativeText.tsx @@ -0,0 +1 @@ +export { Text } from './Text' diff --git a/src/components/native/NativeView.native.tsx b/src/components/native/NativeView.native.tsx new file mode 100644 index 00000000..a64c518c --- /dev/null +++ b/src/components/native/NativeView.native.tsx @@ -0,0 +1,13 @@ +import type { ViewProps } from 'react-native' +import { type ComponentType, createElement, forwardRef } from 'react' +import { createUnistylesElement } from '../../core' + +// credits to @hirbod +const LeanView = forwardRef((props, ref) => { + return createElement('RCTView', { ...props, ref }) +}) as ComponentType + +LeanView.displayName = 'RCTView' + +// this will match default export from react-native +export default createUnistylesElement(LeanView) diff --git a/src/components/native/NativeView.tsx b/src/components/native/NativeView.tsx new file mode 100644 index 00000000..38f26a81 --- /dev/null +++ b/src/components/native/NativeView.tsx @@ -0,0 +1,3 @@ +import { View } from './View' + +export default View