diff --git a/example/App2.tsx b/example/App2.tsx index d1c9db04..66602d56 100644 --- a/example/App2.tsx +++ b/example/App2.tsx @@ -1,10 +1,11 @@ import { Text as RNText, View } from 'react-native' import React, { PropsWithChildren } from 'react' -import { StyleSheet, UnistylesVariants } from 'react-native-unistyles' +import { st } from './st' import './unistyles' -type TextProps = PropsWithChildren & UnistylesVariants & { - value: number +type TextProps = PropsWithChildren & { + value: number, + size: 'small' | 'large' } const Text: React.FunctionComponent = ({ value, children, size }) => { @@ -37,7 +38,7 @@ export const App = () => { ) } -const styles = StyleSheet.create((theme, rt) => ({ +const styles = st.create((theme, rt) => ({ container: { flex: 1, alignItems: 'center', diff --git a/example/babel.config.js b/example/babel.config.js index 38bc6814..2690a869 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -9,7 +9,8 @@ module.exports = api => { plugins: [ [path.join(__dirname, '../plugin'), { debug: true, - isLocal: true + isLocal: true, + autoProcessImports: ['@lib/theme', './st'], }], [ 'module-resolver', diff --git a/example/st.ts b/example/st.ts new file mode 100644 index 00000000..b70db15d --- /dev/null +++ b/example/st.ts @@ -0,0 +1,5 @@ +import { StyleSheet as st } from 'react-native-unistyles' + +export { + st +} diff --git a/plugin/__tests__/ref.spec.js b/plugin/__tests__/ref.spec.js index bfffa90f..0939f306 100644 --- a/plugin/__tests__/ref.spec.js +++ b/plugin/__tests__/ref.spec.js @@ -50,143 +50,6 @@ pluginTester({ }) ` }, - { - title: 'Adds ref if there is any import from React Native', - code: ` - import { View, Text } from 'react-native' - - export const Example = () => { - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create({ - container: { - backgroundColor: 'red' - } - }) - `, - output: ` - import { Text } from 'react-native-unistyles/components/native/Text' - import { View } from 'react-native-unistyles/components/native/View' - - export const Example = () => { - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create({ - container: { - backgroundColor: 'red' - } - }) - ` - }, - { - title: 'Adds ref only for React Native components', - code: ` - import { View } from 'react-native' - import { Text } from 'custom-lib' - - export const Example = () => { - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create({ - container: { - backgroundColor: 'red' - }, - text: { - color: 'blue' - } - }) - `, - output: ` - import { View } from 'react-native-unistyles/components/native/View' - - import { Text } from 'custom-lib' - - export const Example = () => { - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create({ - container: { - backgroundColor: 'red' - }, - text: { - color: 'blue' - } - }) - ` - }, - { - title: 'Preserves user\'s ref', - code: ` - import React from 'react' - import { View, Text } from 'react-native' - import { StyleSheet } from 'react-native-unistyles' - - export const Example = () => { - let ref = React.useRef() - - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create({ - container: { - backgroundColor: 'red' - } - }) - `, - output: ` - import { Text } from 'react-native-unistyles/components/native/Text' - import { View } from 'react-native-unistyles/components/native/View' - import React from 'react' - - import { StyleSheet } from 'react-native-unistyles' - - export const Example = () => { - let ref = React.useRef() - - return ( - - Hello world - - ) - } - - const styles = StyleSheet.create( - { - container: { - backgroundColor: 'red' - } - }, - 92366683 - ) - ` - }, { title: 'Preserves user\'s ref as function', code: ` diff --git a/plugin/__tests__/userImports.spec.js b/plugin/__tests__/userImports.spec.js new file mode 100644 index 00000000..d682fca7 --- /dev/null +++ b/plugin/__tests__/userImports.spec.js @@ -0,0 +1,106 @@ +import { pluginTester } from 'babel-plugin-tester' +import plugin from '../' + +pluginTester({ + plugin, + pluginOptions: { + debug: false, + autoProcessImports: ['@codemask/styles'] + }, + babelOptions: { + plugins: ['@babel/plugin-syntax-jsx'], + generatorOpts: { + retainLines: true + } + }, + tests: [ + { + title: 'Should respect user imports', + code: ` + import { View, Text } from 'react-native' + import { StyleSheet } from '@codemask/styles' + + export const Example = () => { + return ( + + Hello world + + ) + } + + const styles = StyleSheet.create({ + container: { + flex: 1 + } + }) + `, + output: ` + import { Text } from 'react-native-unistyles/components/native/Text' + import { View } from 'react-native-unistyles/components/native/View' + + import { StyleSheet } from '@codemask/styles' + + export const Example = () => { + return ( + + Hello world + + ) + } + + const styles = StyleSheet.create( + { + container: { + flex: 1 + } + }, + 467105739 + ) + ` + }, + { + title: 'Should respect user imports event if then changed the name', + code: ` + import { View, Text } from 'react-native' + import { s } from '@codemask/styles' + + export const Example = () => { + return ( + + Hello world + + ) + } + + const styles = s.create({ + container: { + flex: 1 + } + }) + `, + output: ` + import { Text } from 'react-native-unistyles/components/native/Text' + import { View } from 'react-native-unistyles/components/native/View' + + import { s } from '@codemask/styles' + + export const Example = () => { + return ( + + Hello world + + ) + } + + const styles = s.create( + { + container: { + flex: 1 + } + }, + 467105739 + ) + ` + }, + ] +}) diff --git a/plugin/index.js b/plugin/index.js index 3b6c43c5..979e9ce4 100644 --- a/plugin/index.js +++ b/plugin/index.js @@ -1,6 +1,6 @@ const { addUnistylesImport, isInsideNodeModules } = require('./import') const { hasStringRef } = require('./ref') -const { isUnistylesStyleSheet, analyzeDependencies, addStyleSheetTag, getUnistyles } = require('./stylesheet') +const { isUnistylesStyleSheet, analyzeDependencies, addStyleSheetTag, getUnistyles, isKindOfStyleSheet } = require('./stylesheet') const { extractVariants } = require('./variants') const reactNativeComponentNames = [ @@ -19,12 +19,16 @@ const reactNativeComponentNames = [ 'RefreshControl', 'TouchableHighlight', 'TouchableOpacity', - 'VirtualizedList' + 'VirtualizedList', + // Modal - there is no exposed native handle + // TouchableWithoutFeedback - can't accept a ref ] -// Modal - there is no exposed native handle -// TouchableWithoutFeedback - can't accept a ref - +// options +// { debug: boolean, isLocal: boolean, autoProcessImports: Array } +// debug - logs found dependencies in every StyleSheet +// isLocal - only applicable for Unistyles monorepo for path resolution, don't use it! +// autoProcessImports - list of imports that should trigger unistyles babel plugin module.exports = function ({ types: t }) { return { name: 'babel-react-native-unistyles', @@ -36,21 +40,27 @@ module.exports = function ({ types: t }) { } state.file.hasAnyUnistyle = false - state.file.hasUnistylesImport = false - state.file.shouldIncludePressable = false state.file.hasVariants = false state.file.styleSheetLocalName = '' state.file.tagNumber = 0 - state.file.isClassComponent = false state.reactNativeImports = {} + state.file.forceProcessing = false }, exit(path, state) { + if (isInsideNodeModules(state)) { + return + } + if (state.file.hasAnyUnistyle || state.file.hasVariants) { addUnistylesImport(t, path, state) } } }, FunctionDeclaration(path, state) { + if (isInsideNodeModules(state)) { + return + } + const componentName = path.node.id ? path.node.id.name : null @@ -60,13 +70,16 @@ module.exports = function ({ types: t }) { } }, ClassDeclaration(path, state) { + if (isInsideNodeModules(state)) { + return + } + const componentName = path.node.id ? path.node.id.name : null if (componentName) { state.file.hasVariants = false - state.file.isClassComponent = true } }, VariableDeclaration(path, state) { @@ -94,8 +107,6 @@ module.exports = function ({ types: t }) { const importSource = path.node.source.value if (importSource.includes('react-native-unistyles')) { - state.file.hasUnistylesImport = true - path.node.specifiers.forEach(specifier => { if (specifier.imported && specifier.imported.name === 'StyleSheet') { state.file.styleSheetLocalName = specifier.local.name @@ -105,18 +116,14 @@ module.exports = function ({ types: t }) { if (importSource.includes('react-native')) { path.node.specifiers.forEach(specifier => { - if (specifier.imported && specifier.imported.name === 'Pressable' && specifier.local.name !== 'NativePressableReactNative') { - state.file.shouldIncludePressable = true - } - if (specifier.imported && reactNativeComponentNames.includes(specifier.imported.name)) { state.reactNativeImports[specifier.local.name] = specifier.imported.name } }) } - if (importSource.includes('react-native-web/dist/exports/Pressable')) { - state.file.shouldIncludePressable = true + if (!state.file.forceProcessing && Array.isArray(state.opts.autoProcessImports)) { + state.file.forceProcessing = state.opts.autoProcessImports.includes(importSource) } }, JSXElement(path, state) { @@ -124,13 +131,15 @@ module.exports = function ({ types: t }) { return } - state.file.hasAnyUnistyle = true - if (hasStringRef(t, path)) { throw new Error("Detected string based ref which is not supported by Unistyles.") } }, BlockStatement(path, state) { + if (isInsideNodeModules(state)) { + return + } + extractVariants(t, path, state) }, CallExpression(path, state) { @@ -138,10 +147,12 @@ module.exports = function ({ types: t }) { return } - if (!isUnistylesStyleSheet(t, path, state)) { + if (!isUnistylesStyleSheet(t, path, state) && !isKindOfStyleSheet(t, path, state)) { return } + state.file.hasAnyUnistyle = true + addStyleSheetTag(t, path, state) const arg = path.node.arguments[0] diff --git a/plugin/stylesheet.js b/plugin/stylesheet.js index fa341f4b..16cb884a 100644 --- a/plugin/stylesheet.js +++ b/plugin/stylesheet.js @@ -42,6 +42,20 @@ function isUnistylesStyleSheet(t, path, state) { ) } +function isKindOfStyleSheet(t, path, state) { + if (!state.file.forceProcessing) { + return false + } + + const callee = path.get('callee') + + return ( + t.isMemberExpression(callee.node) && + callee.node.property.name === 'create' && + t.isIdentifier(callee.node.object) + ) +} + function addStyleSheetTag(t, path, state) { const callee = path.get('callee') const uniqueId = stringToUniqueId(state.filename.replace(state.cwd, '')) + ++state.file.tagNumber @@ -208,5 +222,6 @@ module.exports = { isUnistylesStyleSheet, analyzeDependencies, addStyleSheetTag, - getUnistyles + getUnistyles, + isKindOfStyleSheet }