@mgcrea/react-native-tailwind 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1887,6 +1887,135 @@ const styles = StyleSheet.create({
1887
1887
  - Choose a name that won't conflict with existing variables in your files
1888
1888
  - The same identifier is used across all files in your project
1889
1889
 
1890
+ ### Custom Color Scheme Hook
1891
+
1892
+ By default, the plugin uses React Native's built-in `useColorScheme()` hook for `dark:` and `light:` modifiers. You can configure it to use a custom color scheme hook from theme providers like React Navigation, Expo, or your own implementation.
1893
+
1894
+ **Configuration:**
1895
+
1896
+ ```javascript
1897
+ // babel.config.js
1898
+ module.exports = {
1899
+ plugins: [
1900
+ [
1901
+ "@mgcrea/react-native-tailwind/babel",
1902
+ {
1903
+ colorScheme: {
1904
+ importFrom: "@/hooks/useColorScheme", // Module to import from
1905
+ importName: "useColorScheme", // Hook name to import
1906
+ },
1907
+ },
1908
+ ],
1909
+ ],
1910
+ };
1911
+ ```
1912
+
1913
+ **Use Cases:**
1914
+
1915
+ #### 1. Custom Theme Provider
1916
+
1917
+ Override system color scheme with user preferences from a store:
1918
+
1919
+ ```typescript
1920
+ // src/hooks/useColorScheme.ts
1921
+ import { useColorScheme as useSystemColorScheme } from "react-native";
1922
+ import { profileStore } from "@/stores/profileStore";
1923
+ import { type ColorSchemeName } from "react-native";
1924
+
1925
+ export const useColorScheme = (): ColorSchemeName => {
1926
+ const systemColorScheme = useSystemColorScheme();
1927
+ const userTheme = profileStore.theme; // 'dark' | 'light' | 'auto'
1928
+
1929
+ // Return user preference, or fall back to system if set to 'auto'
1930
+ return userTheme === 'auto' ? systemColorScheme : userTheme;
1931
+ };
1932
+ ```
1933
+
1934
+ ```javascript
1935
+ // babel.config.js
1936
+ {
1937
+ colorScheme: {
1938
+ importFrom: "@/hooks/useColorScheme",
1939
+ importName: "useColorScheme"
1940
+ }
1941
+ }
1942
+ ```
1943
+
1944
+ #### 2. React Navigation Theme
1945
+
1946
+ Integrate with React Navigation's theme system:
1947
+
1948
+ ```typescript
1949
+ // Wrap React Navigation's useTheme to return ColorSchemeName
1950
+ import { useTheme as useNavTheme } from "@react-navigation/native";
1951
+ import { type ColorSchemeName } from "react-native";
1952
+
1953
+ export const useColorScheme = (): ColorSchemeName => {
1954
+ const { dark } = useNavTheme();
1955
+ return dark ? "dark" : "light";
1956
+ };
1957
+ ```
1958
+
1959
+ #### 3. Expo Router Theme
1960
+
1961
+ Use Expo Router's theme hook:
1962
+
1963
+ ```javascript
1964
+ // babel.config.js
1965
+ {
1966
+ colorScheme: {
1967
+ importFrom: "expo-router",
1968
+ importName: "useColorScheme"
1969
+ }
1970
+ }
1971
+ ```
1972
+
1973
+ #### 4. Testing
1974
+
1975
+ Mock color scheme for tests:
1976
+
1977
+ ```typescript
1978
+ // test/mocks/useColorScheme.ts
1979
+ export const useColorScheme = () => "light"; // Or "dark" for dark mode tests
1980
+ ```
1981
+
1982
+ ```javascript
1983
+ // babel.config.js (test environment)
1984
+ {
1985
+ colorScheme: {
1986
+ importFrom: "@/test/mocks/useColorScheme",
1987
+ importName: "useColorScheme"
1988
+ }
1989
+ }
1990
+ ```
1991
+
1992
+ #### How it works
1993
+
1994
+ When you use `dark:` or `light:` modifiers:
1995
+
1996
+ ```tsx
1997
+ <View className="bg-white dark:bg-gray-900" />
1998
+ ```
1999
+
2000
+ The plugin will:
2001
+
2002
+ 1. Import your custom hook: `import { useColorScheme } from "@/hooks/useColorScheme"`
2003
+ 2. Inject it in components: `const _twColorScheme = useColorScheme();`
2004
+ 3. Generate conditionals: `_twColorScheme === "dark" && styles._dark_bg_gray_900`
2005
+
2006
+ #### Default behavior (no configuration)
2007
+
2008
+ Without custom configuration, the plugin uses React Native's built-in hook:
2009
+
2010
+ - Import: `import { useColorScheme } from "react-native"`
2011
+ - This works out of the box for basic system color scheme detection
2012
+
2013
+ #### Requirements
2014
+
2015
+ - Your custom hook must return `ColorSchemeName` (type from React Native: `"light" | "dark" | null | undefined`)
2016
+ - The hook must be compatible with React's rules of hooks (can only be called in function components)
2017
+ - Import merging works automatically if you already import from the same source
2018
+
1890
2019
  ### Arbitrary Values
1891
2020
 
1892
2021
  Use arbitrary values for custom sizes, spacing, and borders not in the preset scales:
@@ -2518,33 +2518,33 @@ function addPlatformImport(path2, t) {
2518
2518
  path2.unshiftContainer("body", importDeclaration);
2519
2519
  }
2520
2520
  }
2521
- function addColorSchemeImport(path2, t) {
2521
+ function addColorSchemeImport(path2, importSource, hookName, t) {
2522
2522
  const body = path2.node.body;
2523
- let reactNativeImport = null;
2523
+ let existingValueImport = null;
2524
2524
  for (const statement of body) {
2525
- if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
2526
- reactNativeImport = statement;
2527
- break;
2525
+ if (t.isImportDeclaration(statement) && statement.source.value === importSource) {
2526
+ if (statement.importKind !== "type") {
2527
+ existingValueImport = statement;
2528
+ break;
2529
+ }
2528
2530
  }
2529
2531
  }
2530
- if (reactNativeImport) {
2531
- const hasUseColorScheme = reactNativeImport.specifiers.some(
2532
- (spec) => t.isImportSpecifier(spec) && spec.imported.type === "Identifier" && spec.imported.name === "useColorScheme"
2532
+ if (existingValueImport) {
2533
+ const hasHook = existingValueImport.specifiers.some(
2534
+ (spec) => t.isImportSpecifier(spec) && spec.imported.type === "Identifier" && spec.imported.name === hookName
2533
2535
  );
2534
- if (!hasUseColorScheme) {
2535
- reactNativeImport.specifiers.push(
2536
- t.importSpecifier(t.identifier("useColorScheme"), t.identifier("useColorScheme"))
2537
- );
2536
+ if (!hasHook) {
2537
+ existingValueImport.specifiers.push(t.importSpecifier(t.identifier(hookName), t.identifier(hookName)));
2538
2538
  }
2539
2539
  } else {
2540
2540
  const importDeclaration = t.importDeclaration(
2541
- [t.importSpecifier(t.identifier("useColorScheme"), t.identifier("useColorScheme"))],
2542
- t.stringLiteral("react-native")
2541
+ [t.importSpecifier(t.identifier(hookName), t.identifier(hookName))],
2542
+ t.stringLiteral(importSource)
2543
2543
  );
2544
2544
  path2.unshiftContainer("body", importDeclaration);
2545
2545
  }
2546
2546
  }
2547
- function injectColorSchemeHook(functionPath, colorSchemeVariableName, t) {
2547
+ function injectColorSchemeHook(functionPath, colorSchemeVariableName, hookName, localIdentifier, t) {
2548
2548
  let body = functionPath.node.body;
2549
2549
  if (!t.isBlockStatement(body)) {
2550
2550
  if (t.isArrowFunctionExpression(functionPath.node) && t.isExpression(body)) {
@@ -2566,10 +2566,11 @@ function injectColorSchemeHook(functionPath, colorSchemeVariableName, t) {
2566
2566
  if (hasHook) {
2567
2567
  return false;
2568
2568
  }
2569
+ const identifierToCall = localIdentifier ?? hookName;
2569
2570
  const hookCall = t.variableDeclaration("const", [
2570
2571
  t.variableDeclarator(
2571
2572
  t.identifier(colorSchemeVariableName),
2572
- t.callExpression(t.identifier("useColorScheme"), [])
2573
+ t.callExpression(t.identifier(identifierToCall), [])
2573
2574
  )
2574
2575
  ]);
2575
2576
  body.body.unshift(hookCall);
@@ -2852,6 +2853,8 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2852
2853
  darkSuffix: options?.schemeModifier?.darkSuffix ?? "-dark",
2853
2854
  lightSuffix: options?.schemeModifier?.lightSuffix ?? "-light"
2854
2855
  };
2856
+ const colorSchemeImportSource = options?.colorScheme?.importFrom ?? "react-native";
2857
+ const colorSchemeHookName = options?.colorScheme?.importName ?? "useColorScheme";
2855
2858
  return {
2856
2859
  name: "react-native-tailwind",
2857
2860
  visitor: {
@@ -2865,6 +2868,8 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2865
2868
  state.hasColorSchemeImport = false;
2866
2869
  state.needsColorSchemeImport = false;
2867
2870
  state.colorSchemeVariableName = "_twColorScheme";
2871
+ state.colorSchemeImportSource = colorSchemeImportSource;
2872
+ state.colorSchemeHookName = colorSchemeHookName;
2868
2873
  state.supportedAttributes = exactMatches;
2869
2874
  state.attributePatterns = patterns;
2870
2875
  state.stylesIdentifier = stylesIdentifier;
@@ -2888,11 +2893,17 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2888
2893
  addPlatformImport(path2, t);
2889
2894
  }
2890
2895
  if (state.needsColorSchemeImport && !state.hasColorSchemeImport) {
2891
- addColorSchemeImport(path2, t);
2896
+ addColorSchemeImport(path2, state.colorSchemeImportSource, state.colorSchemeHookName, t);
2892
2897
  }
2893
2898
  if (state.needsColorSchemeImport) {
2894
2899
  for (const functionPath of state.functionComponentsNeedingColorScheme) {
2895
- injectColorSchemeHook(functionPath, state.colorSchemeVariableName, t);
2900
+ injectColorSchemeHook(
2901
+ functionPath,
2902
+ state.colorSchemeVariableName,
2903
+ state.colorSchemeHookName,
2904
+ state.colorSchemeLocalIdentifier,
2905
+ t
2906
+ );
2896
2907
  }
2897
2908
  }
2898
2909
  injectStylesAtTop(path2, state.styleRegistry, state.stylesIdentifier, t);
@@ -2915,23 +2926,26 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2915
2926
  }
2916
2927
  return false;
2917
2928
  });
2918
- const hasUseColorScheme = specifiers.some((spec) => {
2919
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
2920
- return spec.imported.name === "useColorScheme";
2921
- }
2922
- return false;
2923
- });
2924
2929
  if (hasStyleSheet) {
2925
2930
  state.hasStyleSheetImport = true;
2926
2931
  }
2927
2932
  if (hasPlatform) {
2928
2933
  state.hasPlatformImport = true;
2929
2934
  }
2930
- if (hasUseColorScheme) {
2931
- state.hasColorSchemeImport = true;
2932
- }
2933
2935
  state.reactNativeImportPath = path2;
2934
2936
  }
2937
+ if (node.source.value === state.colorSchemeImportSource) {
2938
+ const specifiers = node.specifiers;
2939
+ for (const spec of specifiers) {
2940
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
2941
+ if (spec.imported.name === state.colorSchemeHookName) {
2942
+ state.hasColorSchemeImport = true;
2943
+ state.colorSchemeLocalIdentifier = spec.local.name;
2944
+ break;
2945
+ }
2946
+ }
2947
+ }
2948
+ }
2935
2949
  if (node.source.value === "@mgcrea/react-native-tailwind") {
2936
2950
  const specifiers = node.specifiers;
2937
2951
  specifiers.forEach((spec) => {
@@ -41,6 +41,40 @@ export type PluginOptions = {
41
41
  darkSuffix?: string;
42
42
  lightSuffix?: string;
43
43
  };
44
+ /**
45
+ * Configuration for color scheme hook import (dark:/light: modifiers)
46
+ *
47
+ * Allows using custom color scheme hooks from theme providers instead of
48
+ * React Native's built-in useColorScheme.
49
+ *
50
+ * @example
51
+ * // Use custom hook from theme provider
52
+ * {
53
+ * importFrom: '@/hooks/useColorScheme',
54
+ * importName: 'useColorScheme'
55
+ * }
56
+ *
57
+ * @example
58
+ * // Use React Navigation theme
59
+ * {
60
+ * importFrom: '@react-navigation/native',
61
+ * importName: 'useTheme' // You'd wrap this to return ColorSchemeName
62
+ * }
63
+ *
64
+ * @default { importFrom: 'react-native', importName: 'useColorScheme' }
65
+ */
66
+ colorScheme?: {
67
+ /**
68
+ * Module to import the color scheme hook from
69
+ * @default 'react-native'
70
+ */
71
+ importFrom?: string;
72
+ /**
73
+ * Name of the hook to import
74
+ * @default 'useColorScheme'
75
+ */
76
+ importName?: string;
77
+ };
44
78
  };
45
79
  type PluginState = PluginPass & {
46
80
  styleRegistry: Map<string, StyleObject>;
@@ -51,6 +85,9 @@ type PluginState = PluginPass & {
51
85
  hasColorSchemeImport: boolean;
52
86
  needsColorSchemeImport: boolean;
53
87
  colorSchemeVariableName: string;
88
+ colorSchemeImportSource: string;
89
+ colorSchemeHookName: string;
90
+ colorSchemeLocalIdentifier?: string;
54
91
  customTheme: CustomTheme;
55
92
  schemeModifierConfig: SchemeModifierConfig;
56
93
  supportedAttributes: Set<string>;
@@ -7,7 +7,9 @@ import babelPlugin, { type PluginOptions } from "./plugin.js";
7
7
  * Helper to transform code with the Babel plugin
8
8
  */
9
9
  function transform(code: string, options?: PluginOptions, includeJsx = false) {
10
- const presets = includeJsx ? ["@babel/preset-react"] : [];
10
+ const presets = includeJsx
11
+ ? ["@babel/preset-react", ["@babel/preset-typescript", { isTSX: true, allExtensions: true }]]
12
+ : [];
11
13
 
12
14
  const result = transformSync(code, {
13
15
  presets,
@@ -1022,6 +1024,278 @@ describe("Babel plugin - color scheme modifier transformation", () => {
1022
1024
  });
1023
1025
  });
1024
1026
 
1027
+ describe("Babel plugin - custom color scheme hook import", () => {
1028
+ it("should use custom import source for color scheme hook", () => {
1029
+ const input = `
1030
+ import React from 'react';
1031
+ import { View } from 'react-native';
1032
+
1033
+ export function Component() {
1034
+ return <View className="dark:bg-gray-900" />;
1035
+ }
1036
+ `;
1037
+
1038
+ const output = transform(
1039
+ input,
1040
+ {
1041
+ colorScheme: {
1042
+ importFrom: "@/hooks/useColorScheme",
1043
+ importName: "useColorScheme",
1044
+ },
1045
+ },
1046
+ true,
1047
+ );
1048
+
1049
+ // Should import from custom source
1050
+ expect(output).toContain('from "@/hooks/useColorScheme"');
1051
+ expect(output).not.toContain('useColorScheme } from "react-native"');
1052
+
1053
+ // Should inject hook call
1054
+ expect(output).toContain("_twColorScheme = useColorScheme()");
1055
+
1056
+ // Should have conditional styling
1057
+ expect(output).toMatch(/_twColorScheme\s*===\s*['"]dark['"]/);
1058
+ });
1059
+
1060
+ it("should use custom hook name", () => {
1061
+ const input = `
1062
+ import React from 'react';
1063
+ import { View } from 'react-native';
1064
+
1065
+ export function Component() {
1066
+ return <View className="dark:bg-gray-900" />;
1067
+ }
1068
+ `;
1069
+
1070
+ const output = transform(
1071
+ input,
1072
+ {
1073
+ colorScheme: {
1074
+ importFrom: "@react-navigation/native",
1075
+ importName: "useTheme",
1076
+ },
1077
+ },
1078
+ true,
1079
+ );
1080
+
1081
+ // Should import useTheme from React Navigation
1082
+ expect(output).toContain('from "@react-navigation/native"');
1083
+ expect(output).toContain("useTheme");
1084
+
1085
+ // Should call useTheme hook
1086
+ expect(output).toContain("_twColorScheme = useTheme()");
1087
+
1088
+ // Should have conditional styling
1089
+ expect(output).toMatch(/_twColorScheme\s*===\s*['"]dark['"]/);
1090
+ });
1091
+
1092
+ it("should merge custom hook with existing import from same source", () => {
1093
+ const input = `
1094
+ import React from 'react';
1095
+ import { View, Text } from 'react-native';
1096
+ import { useNavigation } from '@react-navigation/native';
1097
+
1098
+ export function Component() {
1099
+ const navigation = useNavigation();
1100
+ return (
1101
+ <View className="dark:bg-gray-900">
1102
+ <Text onPress={() => navigation.navigate('Home')}>Go Home</Text>
1103
+ </View>
1104
+ );
1105
+ }
1106
+ `;
1107
+
1108
+ const output = transform(
1109
+ input,
1110
+ {
1111
+ colorScheme: {
1112
+ importFrom: "@react-navigation/native",
1113
+ importName: "useTheme",
1114
+ },
1115
+ },
1116
+ true,
1117
+ );
1118
+
1119
+ // Should merge with existing import (both useNavigation and useTheme in same import)
1120
+ expect(output).toMatch(
1121
+ /import\s+\{\s*useNavigation[^}]*useTheme[^}]*\}\s+from\s+['"]@react-navigation\/native['"]/,
1122
+ );
1123
+ expect(output).toContain("useNavigation()");
1124
+ expect(output).toContain("useTheme()");
1125
+
1126
+ // Should only have one import from that source
1127
+ const importCount = (output.match(/@react-navigation\/native/g) ?? []).length;
1128
+ expect(importCount).toBe(1);
1129
+ });
1130
+
1131
+ it("should not duplicate custom hook if already imported", () => {
1132
+ const input = `
1133
+ import React from 'react';
1134
+ import { View } from 'react-native';
1135
+ import { useColorScheme } from '@/hooks/useColorScheme';
1136
+
1137
+ export function Component() {
1138
+ return <View className="dark:bg-gray-900" />;
1139
+ }
1140
+ `;
1141
+
1142
+ const output = transform(
1143
+ input,
1144
+ {
1145
+ colorScheme: {
1146
+ importFrom: "@/hooks/useColorScheme",
1147
+ importName: "useColorScheme",
1148
+ },
1149
+ },
1150
+ true,
1151
+ );
1152
+
1153
+ // Should not add duplicate import
1154
+ const importMatches = output.match(/import.*useColorScheme.*from ['"]@\/hooks\/useColorScheme['"]/g);
1155
+ expect(importMatches).toHaveLength(1);
1156
+
1157
+ // Should still inject hook call
1158
+ expect(output).toContain("_twColorScheme = useColorScheme()");
1159
+ });
1160
+
1161
+ it("should use react-native by default when no custom config provided", () => {
1162
+ const input = `
1163
+ import React from 'react';
1164
+ import { View } from 'react-native';
1165
+
1166
+ export function Component() {
1167
+ return <View className="dark:bg-gray-900" />;
1168
+ }
1169
+ `;
1170
+
1171
+ const output = transform(input, undefined, true);
1172
+
1173
+ // Should use default react-native import (can be single or double quotes)
1174
+ expect(output).toMatch(/useColorScheme\s*}\s*from\s+['"]react-native['"]/);
1175
+ expect(output).not.toContain("@/hooks");
1176
+ expect(output).not.toContain("@react-navigation");
1177
+
1178
+ // Should inject hook call with default name
1179
+ expect(output).toContain("_twColorScheme = useColorScheme()");
1180
+ });
1181
+
1182
+ it("should create separate import when only type-only import exists", () => {
1183
+ const input = `
1184
+ import React from 'react';
1185
+ import { View } from 'react-native';
1186
+ import type { NavigationProp } from '@react-navigation/native';
1187
+
1188
+ export function Component() {
1189
+ return <View className="dark:bg-gray-900" />;
1190
+ }
1191
+ `;
1192
+
1193
+ const output = transform(
1194
+ input,
1195
+ {
1196
+ colorScheme: {
1197
+ importFrom: "@react-navigation/native",
1198
+ importName: "useTheme",
1199
+ },
1200
+ },
1201
+ true,
1202
+ );
1203
+
1204
+ // TypeScript preset strips type-only imports, but the important thing is:
1205
+ // 1. useTheme hook is imported (not skipped thinking it was already imported)
1206
+ // 2. Hook is correctly called in the component
1207
+ expect(output).toMatch(/import\s+\{\s*useTheme\s*\}\s+from\s+['"]@react-navigation\/native['"]/);
1208
+ expect(output).toContain("_twColorScheme = useTheme()");
1209
+ });
1210
+
1211
+ it("should use aliased identifier when hook is already imported with alias", () => {
1212
+ const input = `
1213
+ import React from 'react';
1214
+ import { View, Text } from 'react-native';
1215
+ import { useTheme as navTheme } from '@react-navigation/native';
1216
+
1217
+ export function Component() {
1218
+ const theme = navTheme();
1219
+ return (
1220
+ <View className="dark:bg-gray-900">
1221
+ <Text>{theme.dark ? 'Dark' : 'Light'}</Text>
1222
+ </View>
1223
+ );
1224
+ }
1225
+ `;
1226
+
1227
+ const output = transform(
1228
+ input,
1229
+ {
1230
+ colorScheme: {
1231
+ importFrom: "@react-navigation/native",
1232
+ importName: "useTheme",
1233
+ },
1234
+ },
1235
+ true,
1236
+ );
1237
+
1238
+ // Should not add duplicate import
1239
+ const importMatches = output.match(
1240
+ /import\s+\{[^}]*useTheme[^}]*\}\s+from\s+['"]@react-navigation\/native['"]/g,
1241
+ );
1242
+ expect(importMatches).toHaveLength(1);
1243
+
1244
+ // Should still have the aliased import
1245
+ expect(output).toMatch(/useTheme\s+as\s+navTheme/);
1246
+
1247
+ // Should call the aliased name (navTheme), not the export name (useTheme)
1248
+ // Both the user's code and our injected hook should use navTheme
1249
+ expect(output).toContain("_twColorScheme = navTheme()");
1250
+ expect(output).not.toContain("_twColorScheme = useTheme()");
1251
+ });
1252
+
1253
+ it("should handle both type-only and aliased imports together", () => {
1254
+ const input = `
1255
+ import React from 'react';
1256
+ import { View, Text } from 'react-native';
1257
+ import type { Theme } from '@react-navigation/native';
1258
+ import { useTheme as getNavTheme } from '@react-navigation/native';
1259
+
1260
+ export function Component() {
1261
+ const theme = getNavTheme();
1262
+ return (
1263
+ <View className="dark:bg-gray-900">
1264
+ <Text>{theme.dark ? 'Dark Mode' : 'Light Mode'}</Text>
1265
+ </View>
1266
+ );
1267
+ }
1268
+ `;
1269
+
1270
+ const output = transform(
1271
+ input,
1272
+ {
1273
+ colorScheme: {
1274
+ importFrom: "@react-navigation/native",
1275
+ importName: "useTheme",
1276
+ },
1277
+ },
1278
+ true,
1279
+ );
1280
+
1281
+ // TypeScript preset strips type-only imports
1282
+ // The important thing is: should not add duplicate import, and should use aliased name
1283
+ expect(output).toMatch(
1284
+ /import\s+\{[^}]*useTheme\s+as\s+getNavTheme[^}]*\}\s+from\s+['"]@react-navigation\/native['"]/,
1285
+ );
1286
+
1287
+ // Should not add duplicate import - useTheme should only appear in the aliased import
1288
+ const useThemeImports = output.match(
1289
+ /import\s+\{[^}]*useTheme[^}]*\}\s+from\s+['"]@react-navigation\/native['"]/g,
1290
+ );
1291
+ expect(useThemeImports).toHaveLength(1);
1292
+
1293
+ // Should call the aliased name for both user code and our injected hook
1294
+ expect(output).toContain("_twColorScheme = getNavTheme()");
1295
+ expect(output).not.toContain("_twColorScheme = useTheme()");
1296
+ });
1297
+ });
1298
+
1025
1299
  describe("Babel plugin - import injection", () => {
1026
1300
  it("should not add StyleSheet import to files without className usage", () => {
1027
1301
  const input = `