@medway-ui/native 1.0.4 → 1.1.2

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.
Files changed (154) hide show
  1. package/README.md +283 -77
  2. package/lib/commonjs/components/Alert.js +6 -3
  3. package/lib/commonjs/components/Alert.js.map +1 -1
  4. package/lib/commonjs/components/AlertDialog.js +38 -15
  5. package/lib/commonjs/components/AlertDialog.js.map +1 -1
  6. package/lib/commonjs/components/Avatar.js +101 -0
  7. package/lib/commonjs/components/Avatar.js.map +1 -0
  8. package/lib/commonjs/components/CardNews.js +319 -0
  9. package/lib/commonjs/components/CardNews.js.map +1 -0
  10. package/lib/commonjs/components/Checkbox.js +36 -13
  11. package/lib/commonjs/components/Checkbox.js.map +1 -1
  12. package/lib/commonjs/components/Dialog.js +15 -10
  13. package/lib/commonjs/components/Dialog.js.map +1 -1
  14. package/lib/commonjs/components/EmptyState.js +42 -0
  15. package/lib/commonjs/components/EmptyState.js.map +1 -0
  16. package/lib/commonjs/components/FallbackImage.js +55 -0
  17. package/lib/commonjs/components/FallbackImage.js.map +1 -0
  18. package/lib/commonjs/components/Input.js +55 -16
  19. package/lib/commonjs/components/Input.js.map +1 -1
  20. package/lib/commonjs/components/Label.js +43 -14
  21. package/lib/commonjs/components/Label.js.map +1 -1
  22. package/lib/commonjs/components/Loading.js +7 -3
  23. package/lib/commonjs/components/Loading.js.map +1 -1
  24. package/lib/commonjs/components/PageHeader.js +39 -0
  25. package/lib/commonjs/components/PageHeader.js.map +1 -0
  26. package/lib/commonjs/components/Select.js +32 -13
  27. package/lib/commonjs/components/Select.js.map +1 -1
  28. package/lib/commonjs/components/Sheet.js +14 -9
  29. package/lib/commonjs/components/Sheet.js.map +1 -1
  30. package/lib/commonjs/components/Skeleton.js +34 -0
  31. package/lib/commonjs/components/Skeleton.js.map +1 -0
  32. package/lib/commonjs/components/Switch.js +43 -14
  33. package/lib/commonjs/components/Switch.js.map +1 -1
  34. package/lib/commonjs/components/ThemedIcon.js +29 -0
  35. package/lib/commonjs/components/ThemedIcon.js.map +1 -0
  36. package/lib/commonjs/components/Toast.js +24 -12
  37. package/lib/commonjs/components/Toast.js.map +1 -1
  38. package/lib/commonjs/components/Tooltip.js +5 -3
  39. package/lib/commonjs/components/Tooltip.js.map +1 -1
  40. package/lib/commonjs/hooks/useThemeColor.js +71 -0
  41. package/lib/commonjs/hooks/useThemeColor.js.map +1 -0
  42. package/lib/commonjs/index.js +152 -0
  43. package/lib/commonjs/index.js.map +1 -1
  44. package/lib/module/components/Alert.js +6 -3
  45. package/lib/module/components/Alert.js.map +1 -1
  46. package/lib/module/components/AlertDialog.js +38 -15
  47. package/lib/module/components/AlertDialog.js.map +1 -1
  48. package/lib/module/components/Avatar.js +94 -0
  49. package/lib/module/components/Avatar.js.map +1 -0
  50. package/lib/module/components/CardNews.js +303 -0
  51. package/lib/module/components/CardNews.js.map +1 -0
  52. package/lib/module/components/Checkbox.js +37 -14
  53. package/lib/module/components/Checkbox.js.map +1 -1
  54. package/lib/module/components/Dialog.js +15 -10
  55. package/lib/module/components/Dialog.js.map +1 -1
  56. package/lib/module/components/EmptyState.js +39 -0
  57. package/lib/module/components/EmptyState.js.map +1 -0
  58. package/lib/module/components/FallbackImage.js +51 -0
  59. package/lib/module/components/FallbackImage.js.map +1 -0
  60. package/lib/module/components/Input.js +55 -16
  61. package/lib/module/components/Input.js.map +1 -1
  62. package/lib/module/components/Label.js +44 -15
  63. package/lib/module/components/Label.js.map +1 -1
  64. package/lib/module/components/Loading.js +7 -3
  65. package/lib/module/components/Loading.js.map +1 -1
  66. package/lib/module/components/PageHeader.js +36 -0
  67. package/lib/module/components/PageHeader.js.map +1 -0
  68. package/lib/module/components/Select.js +32 -13
  69. package/lib/module/components/Select.js.map +1 -1
  70. package/lib/module/components/Sheet.js +14 -9
  71. package/lib/module/components/Sheet.js.map +1 -1
  72. package/lib/module/components/Skeleton.js +30 -0
  73. package/lib/module/components/Skeleton.js.map +1 -0
  74. package/lib/module/components/Switch.js +44 -15
  75. package/lib/module/components/Switch.js.map +1 -1
  76. package/lib/module/components/ThemedIcon.js +26 -0
  77. package/lib/module/components/ThemedIcon.js.map +1 -0
  78. package/lib/module/components/Toast.js +24 -12
  79. package/lib/module/components/Toast.js.map +1 -1
  80. package/lib/module/components/Tooltip.js +6 -4
  81. package/lib/module/components/Tooltip.js.map +1 -1
  82. package/lib/module/hooks/useThemeColor.js +68 -0
  83. package/lib/module/hooks/useThemeColor.js.map +1 -0
  84. package/lib/module/index.js +15 -0
  85. package/lib/module/index.js.map +1 -1
  86. package/lib/typescript/components/Alert.d.ts.map +1 -1
  87. package/lib/typescript/components/AlertDialog.d.ts +2 -2
  88. package/lib/typescript/components/AlertDialog.d.ts.map +1 -1
  89. package/lib/typescript/components/Avatar.d.ts +28 -0
  90. package/lib/typescript/components/Avatar.d.ts.map +1 -0
  91. package/lib/typescript/components/CardNews.d.ts +54 -0
  92. package/lib/typescript/components/CardNews.d.ts.map +1 -0
  93. package/lib/typescript/components/Checkbox.d.ts +5 -5
  94. package/lib/typescript/components/Checkbox.d.ts.map +1 -1
  95. package/lib/typescript/components/Dialog.d.ts.map +1 -1
  96. package/lib/typescript/components/EmptyState.d.ts +13 -0
  97. package/lib/typescript/components/EmptyState.d.ts.map +1 -0
  98. package/lib/typescript/components/FallbackImage.d.ts +9 -0
  99. package/lib/typescript/components/FallbackImage.d.ts.map +1 -0
  100. package/lib/typescript/components/Input.d.ts +4 -5
  101. package/lib/typescript/components/Input.d.ts.map +1 -1
  102. package/lib/typescript/components/Label.d.ts.map +1 -1
  103. package/lib/typescript/components/Loading.d.ts +5 -1
  104. package/lib/typescript/components/Loading.d.ts.map +1 -1
  105. package/lib/typescript/components/PageHeader.d.ts +12 -0
  106. package/lib/typescript/components/PageHeader.d.ts.map +1 -0
  107. package/lib/typescript/components/Select.d.ts +3 -1
  108. package/lib/typescript/components/Select.d.ts.map +1 -1
  109. package/lib/typescript/components/Sheet.d.ts.map +1 -1
  110. package/lib/typescript/components/Skeleton.d.ts +8 -0
  111. package/lib/typescript/components/Skeleton.d.ts.map +1 -0
  112. package/lib/typescript/components/Switch.d.ts +2 -2
  113. package/lib/typescript/components/Switch.d.ts.map +1 -1
  114. package/lib/typescript/components/ThemedIcon.d.ts +24 -0
  115. package/lib/typescript/components/ThemedIcon.d.ts.map +1 -0
  116. package/lib/typescript/components/Toast.d.ts +1 -1
  117. package/lib/typescript/components/Toast.d.ts.map +1 -1
  118. package/lib/typescript/components/Tooltip.d.ts.map +1 -1
  119. package/lib/typescript/hooks/useThemeColor.d.ts +11 -0
  120. package/lib/typescript/hooks/useThemeColor.d.ts.map +1 -0
  121. package/lib/typescript/index.d.ts +16 -0
  122. package/lib/typescript/index.d.ts.map +1 -1
  123. package/package.json +28 -4
  124. package/setup/MedwayUiProvider.tsx +64 -0
  125. package/setup/babel-brownfield.js +120 -0
  126. package/setup/createMedwayUiNavigator.tsx +94 -0
  127. package/setup/global.css +5 -0
  128. package/setup/index.ts +12 -0
  129. package/setup/index.tsx +42 -0
  130. package/setup/lazyScreen.tsx +14 -0
  131. package/setup/metro.js +266 -0
  132. package/setup/tailwind.js +84 -0
  133. package/setup/tokens.native.css +873 -0
  134. package/src/components/Alert.tsx +3 -1
  135. package/src/components/AlertDialog.tsx +63 -19
  136. package/src/components/Avatar.tsx +130 -0
  137. package/src/components/CardNews.tsx +412 -0
  138. package/src/components/Checkbox.tsx +51 -23
  139. package/src/components/Dialog.tsx +13 -7
  140. package/src/components/EmptyState.tsx +57 -0
  141. package/src/components/FallbackImage.tsx +58 -0
  142. package/src/components/Input.tsx +65 -19
  143. package/src/components/Label.tsx +49 -14
  144. package/src/components/Loading.tsx +13 -2
  145. package/src/components/PageHeader.tsx +50 -0
  146. package/src/components/Select.tsx +22 -10
  147. package/src/components/Sheet.tsx +30 -24
  148. package/src/components/Skeleton.tsx +45 -0
  149. package/src/components/Switch.tsx +64 -32
  150. package/src/components/ThemedIcon.tsx +46 -0
  151. package/src/components/Toast.tsx +26 -13
  152. package/src/components/Tooltip.tsx +1 -0
  153. package/src/hooks/useThemeColor.ts +79 -0
  154. package/src/index.ts +60 -0
@@ -0,0 +1,120 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const DEFAULT_APP_PAGES_DIR = 'medway-ui';
5
+
6
+ function createNativeWindScopeTest(options = {}) {
7
+ const appPagesDir = options.appPagesDir ?? DEFAULT_APP_PAGES_DIR;
8
+ const appPagesSegment = `${path.sep}pages${path.sep}${appPagesDir}${path.sep}`;
9
+
10
+ return function isNativeWindScope(filename) {
11
+ return (
12
+ filename.includes(`${path.sep}@medway-ui${path.sep}native${path.sep}`) ||
13
+ filename.includes(`${path.sep}medway-ui${path.sep}native${path.sep}`) ||
14
+ filename.includes(appPagesSegment)
15
+ );
16
+ };
17
+ }
18
+
19
+ function createMedwayUiSourceTest() {
20
+ return function isMedwayUiSourceFile(currentFile) {
21
+ return (
22
+ currentFile.includes(`${path.sep}@medway-ui${path.sep}native${path.sep}`) ||
23
+ currentFile.includes(`${path.sep}medway-ui${path.sep}native${path.sep}`)
24
+ );
25
+ };
26
+ }
27
+
28
+ function resolveMedwayUiNativeSrc(projectRoot) {
29
+ const npmNativeSrc = path.join(projectRoot, 'node_modules/@medway-ui/native/src');
30
+
31
+ if (fs.existsSync(npmNativeSrc) && !isNativePackageSymlinkedToMonorepo(projectRoot)) {
32
+ return npmNativeSrc;
33
+ }
34
+
35
+ const monorepoNativeSrc = path.resolve(projectRoot, '../medway-ui/native/src');
36
+ if (fs.existsSync(monorepoNativeSrc)) {
37
+ return monorepoNativeSrc;
38
+ }
39
+
40
+ return npmNativeSrc;
41
+ }
42
+
43
+ function isNativePackageSymlinkedToMonorepo(projectRoot) {
44
+ const nativePath = path.join(projectRoot, 'node_modules/@medway-ui/native');
45
+
46
+ if (!fs.existsSync(nativePath)) {
47
+ return false;
48
+ }
49
+
50
+ try {
51
+ const nativeRealPath = fs.realpathSync(nativePath);
52
+ const monorepoNativePath = fs.realpathSync(
53
+ path.resolve(projectRoot, '../medway-ui/native'),
54
+ );
55
+
56
+ return nativeRealPath === monorepoNativePath;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ function createMedwayUiAliasResolver(projectRoot) {
63
+ const medwayUiNativeSrc = resolveMedwayUiNativeSrc(projectRoot);
64
+ const isMedwayUiSourceFile = createMedwayUiSourceTest();
65
+
66
+ return function resolveMedwayUiAlias(sourcePath, currentFile) {
67
+ if (!isMedwayUiSourceFile(currentFile) || !sourcePath.startsWith('@/')) {
68
+ return undefined;
69
+ }
70
+
71
+ const relativePath = sourcePath.slice(2);
72
+ const extensions = ['', '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts'];
73
+
74
+ for (const ext of extensions) {
75
+ const candidate = path.join(medwayUiNativeSrc, `${relativePath}${ext}`);
76
+ if (fs.existsSync(candidate)) {
77
+ return candidate;
78
+ }
79
+ }
80
+
81
+ return undefined;
82
+ };
83
+ }
84
+
85
+ function loadCssInteropBabelPlugin(projectRoot) {
86
+ return require(
87
+ require.resolve('react-native-css-interop/dist/babel-plugin', {
88
+ paths: [projectRoot],
89
+ }),
90
+ ).default;
91
+ }
92
+
93
+ /**
94
+ * Override Babel para NativeWind apenas em arquivos medway-ui + pasta de telas migradas.
95
+ * Apps legados: presets expo normais + este override (sem nativewind global).
96
+ *
97
+ * @param {object} [options]
98
+ * @param {string} [options.appPagesDir='medway-ui'] - subpasta de src/pages para telas novas
99
+ * @param {unknown[]} [options.plugins=[]] - plugins do app repetidos no override (reanimated por ultimo)
100
+ */
101
+ function createBrownfieldOverride(options = {}) {
102
+ const { plugins = [], projectRoot = process.cwd() } = options;
103
+ const test = createNativeWindScopeTest(options);
104
+ const cssInteropPlugin = loadCssInteropBabelPlugin(projectRoot);
105
+
106
+ return {
107
+ test,
108
+ presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }]],
109
+ plugins: [cssInteropPlugin, ...plugins],
110
+ };
111
+ }
112
+
113
+ module.exports = {
114
+ createBrownfieldOverride,
115
+ createNativeWindScopeTest,
116
+ createMedwayUiAliasResolver,
117
+ createMedwayUiSourceTest,
118
+ resolveMedwayUiNativeSrc,
119
+ DEFAULT_APP_PAGES_DIR,
120
+ };
@@ -0,0 +1,94 @@
1
+ import React, { Suspense, lazy, type ComponentType } from 'react';
2
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
3
+
4
+ type MedwayUiScreenContext = {
5
+ keys: () => string[];
6
+ (id: string): { default: ComponentType<object> };
7
+ };
8
+
9
+ type CreateMedwayUiNavigatorOptions = {
10
+ /** Primeira tela ao abrir o stack sem `screen` na rota */
11
+ initialRouteName?: string;
12
+ };
13
+
14
+ let cssBootstrapped = false;
15
+
16
+ async function ensureMedwayUiCss() {
17
+ if (!cssBootstrapped) {
18
+ await import('./MedwayUiProvider');
19
+ cssBootstrapped = true;
20
+ }
21
+ }
22
+
23
+ function parseScreenName(moduleKey: string): string | null {
24
+ const match = moduleKey.match(/^\.\/([^/]+)\/index\.tsx$/);
25
+ return match?.[1] ?? null;
26
+ }
27
+
28
+ function createLazyScreen(
29
+ context: MedwayUiScreenContext,
30
+ moduleKey: string,
31
+ ) {
32
+ const LazyScreen = lazy(async () => {
33
+ await ensureMedwayUiCss();
34
+ return { default: context(moduleKey).default };
35
+ });
36
+
37
+ return function MedwayUiLazyScreen(props: object) {
38
+ return (
39
+ <Suspense fallback={null}>
40
+ <LazyScreen {...props} />
41
+ </Suspense>
42
+ );
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Cria um Stack Navigator com todas as telas em subpastas (NomeDaTela/index.tsx).
48
+ *
49
+ * No app — uma vez, com require.context estático para o Metro.
50
+ * Novas telas: crie pasta em pages/medway-ui sem editar registro manual.
51
+ */
52
+ function createMedwayUiNavigator(
53
+ context: MedwayUiScreenContext,
54
+ options: CreateMedwayUiNavigatorOptions = {},
55
+ ) {
56
+ const Stack = createNativeStackNavigator();
57
+
58
+ const screens = context
59
+ .keys()
60
+ .map((key) => ({ key, name: parseScreenName(key) }))
61
+ .filter((entry): entry is { key: string; name: string } => entry.name != null);
62
+
63
+ if (screens.length === 0) {
64
+ throw new Error(
65
+ '[@medway-ui/native] Nenhuma tela em pages/medway-ui. Crie ex.: Showcase/index.tsx',
66
+ );
67
+ }
68
+
69
+ const initialRouteName = options.initialRouteName ?? screens[0].name;
70
+
71
+ function MedwayUiNavigator() {
72
+ return (
73
+ <Stack.Navigator
74
+ initialRouteName={initialRouteName}
75
+ screenOptions={{ headerShown: false, animation: 'slide_from_right' }}
76
+ >
77
+ {screens.map(({ key, name }) => (
78
+ <Stack.Screen
79
+ key={name}
80
+ name={name}
81
+ component={createLazyScreen(context, key)}
82
+ />
83
+ ))}
84
+ </Stack.Navigator>
85
+ );
86
+ }
87
+
88
+ const screenNames = screens.map(({ name }) => name);
89
+
90
+ return { MedwayUiNavigator, screenNames, initialRouteName };
91
+ }
92
+
93
+ export { createMedwayUiNavigator };
94
+ export type { CreateMedwayUiNavigatorOptions, MedwayUiScreenContext };
@@ -0,0 +1,5 @@
1
+ @import './tokens.native.css';
2
+
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
package/setup/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export { MedwayUiProvider } from './MedwayUiProvider';
2
+ export type {
3
+ MedwayUiProviderProps,
4
+ MedwayUiTheme,
5
+ MedwayUiColorScheme,
6
+ } from './MedwayUiProvider';
7
+ export { lazyMedwayUiScreen } from './lazyScreen';
8
+ export { createMedwayUiNavigator } from './createMedwayUiNavigator';
9
+ export type {
10
+ CreateMedwayUiNavigatorOptions,
11
+ MedwayUiScreenContext,
12
+ } from './createMedwayUiNavigator';
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
3
+
4
+ type RequireContext = {
5
+ keys(): string[];
6
+ (id: string): { default: React.ComponentType<any> };
7
+ };
8
+
9
+ type MedwayUiNavigatorResult = {
10
+ MedwayUiNavigator: React.ComponentType;
11
+ screenNames: string[];
12
+ };
13
+
14
+ function extractScreenName(key: string): string {
15
+ return key.replace(/^\.\//, '').replace(/\/index\.tsx$/, '');
16
+ }
17
+
18
+ function createMedwayUiNavigator(context: RequireContext): MedwayUiNavigatorResult {
19
+ const Stack = createNativeStackNavigator();
20
+
21
+ const screens = context.keys().map((key) => ({
22
+ name: extractScreenName(key),
23
+ component: context(key).default,
24
+ }));
25
+
26
+ const screenNames = screens.map((s) => s.name);
27
+
28
+ function MedwayUiNavigator() {
29
+ return (
30
+ <Stack.Navigator screenOptions={{ headerShown: false }}>
31
+ {screens.map(({ name, component }) => (
32
+ <Stack.Screen key={name} name={name} component={component} />
33
+ ))}
34
+ </Stack.Navigator>
35
+ );
36
+ }
37
+
38
+ return { MedwayUiNavigator, screenNames };
39
+ }
40
+
41
+ export { createMedwayUiNavigator };
42
+ export type { MedwayUiNavigatorResult };
@@ -0,0 +1,14 @@
1
+ import React, { type ComponentType } from 'react';
2
+
3
+ type ScreenModule = { default: ComponentType<object> };
4
+
5
+ /**
6
+ * Lazy-load de telas medway-ui com bootstrap de CSS automático.
7
+ */
8
+ function lazyMedwayUiScreen(loader: () => Promise<ScreenModule>) {
9
+ return React.lazy(() =>
10
+ import('./MedwayUiProvider').then(() => loader()),
11
+ );
12
+ }
13
+
14
+ export { lazyMedwayUiScreen };
package/setup/metro.js ADDED
@@ -0,0 +1,266 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function loadWithNativeWind(projectRoot) {
5
+ return require(require.resolve('nativewind/metro', { paths: [projectRoot] }))
6
+ .withNativeWind;
7
+ }
8
+
9
+ function tryResolveMedwayModule(basePath) {
10
+ const extensions = ['.tsx', '.ts', '.jsx', '.js'];
11
+
12
+ for (const ext of extensions) {
13
+ const candidate = `${basePath}${ext}`;
14
+ if (fs.existsSync(candidate)) return candidate;
15
+ }
16
+
17
+ for (const ext of extensions) {
18
+ const candidate = path.join(basePath, `index${ext}`);
19
+ if (fs.existsSync(candidate)) return candidate;
20
+ }
21
+
22
+ return null;
23
+ }
24
+
25
+ function resolveMedwayAlias(moduleName, medwayUiNativeSrc, medwayUiSharedSrc) {
26
+ if (!moduleName.startsWith('@/')) return null;
27
+
28
+ const relativePath = moduleName.slice(2);
29
+
30
+ return (
31
+ tryResolveMedwayModule(path.join(medwayUiNativeSrc, relativePath)) ??
32
+ (medwayUiSharedSrc
33
+ ? tryResolveMedwayModule(path.join(medwayUiSharedSrc, relativePath))
34
+ : null)
35
+ );
36
+ }
37
+
38
+ function resolvePackageEntry(packageDir, entry) {
39
+ const withoutExt = entry.replace(/\.(tsx|ts|jsx|js)$/, '');
40
+ const candidates = [
41
+ path.resolve(packageDir, entry),
42
+ path.resolve(packageDir, withoutExt),
43
+ ...['.tsx', '.ts', '.jsx', '.js'].map((ext) =>
44
+ path.resolve(packageDir, `${withoutExt}${ext}`),
45
+ ),
46
+ ];
47
+
48
+ for (const candidate of candidates) {
49
+ if (fs.existsSync(candidate)) return candidate;
50
+ }
51
+
52
+ return null;
53
+ }
54
+
55
+ function getAppPackageJsonPath(moduleName, projectRoot) {
56
+ const directPath = path.join(projectRoot, 'node_modules', moduleName, 'package.json');
57
+
58
+ if (fs.existsSync(directPath)) {
59
+ return directPath;
60
+ }
61
+
62
+ try {
63
+ return require.resolve(`${moduleName}/package.json`, { paths: [projectRoot] });
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ function resolveBareModule(moduleName, searchRoots) {
70
+ for (const root of searchRoots) {
71
+ try {
72
+ const packageJsonPath = getAppPackageJsonPath(moduleName, root);
73
+
74
+ if (packageJsonPath) {
75
+ const pkg = require(packageJsonPath);
76
+ const packageDir = path.dirname(packageJsonPath);
77
+ const reactNativeEntry = pkg['react-native'];
78
+
79
+ if (typeof reactNativeEntry === 'string' && !reactNativeEntry.endsWith('cli.js')) {
80
+ const resolved = resolvePackageEntry(packageDir, reactNativeEntry);
81
+
82
+ if (resolved) {
83
+ return resolved;
84
+ }
85
+ }
86
+ }
87
+
88
+ return require.resolve(moduleName, { paths: [root] });
89
+ } catch {
90
+ // try next root
91
+ }
92
+ }
93
+
94
+ return null;
95
+ }
96
+
97
+ /**
98
+ * Detecta monorepo file: (`../medway-ui`) vs pacotes npm (`node_modules/@medway-ui`).
99
+ * Prioriza npm quando o pacote não é symlink para o clone local.
100
+ */
101
+ function isNativePackageSymlinkedToMonorepo(projectRoot) {
102
+ const nativePath = path.join(projectRoot, 'node_modules/@medway-ui/native');
103
+
104
+ if (!fs.existsSync(nativePath)) {
105
+ return false;
106
+ }
107
+
108
+ try {
109
+ const nativeRealPath = fs.realpathSync(nativePath);
110
+ const monorepoNativePath = fs.realpathSync(
111
+ path.resolve(projectRoot, '../medway-ui/native'),
112
+ );
113
+
114
+ return nativeRealPath === monorepoNativePath;
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ function detectLinkedMedwayUiRoot(projectRoot) {
121
+ const npmScopedRoot = 'node_modules/@medway-ui';
122
+ const npmNativeSrc = path.resolve(projectRoot, npmScopedRoot, 'native/src');
123
+
124
+ if (fs.existsSync(npmNativeSrc) && !isNativePackageSymlinkedToMonorepo(projectRoot)) {
125
+ return npmScopedRoot;
126
+ }
127
+
128
+ const monorepoRoot = '../medway-ui';
129
+ const monorepoNativeSrc = path.resolve(projectRoot, monorepoRoot, 'native/src');
130
+
131
+ if (fs.existsSync(monorepoNativeSrc)) {
132
+ return monorepoRoot;
133
+ }
134
+
135
+ if (fs.existsSync(npmNativeSrc)) {
136
+ return npmScopedRoot;
137
+ }
138
+
139
+ return monorepoRoot;
140
+ }
141
+
142
+ function resolveMedwayUiPaths(projectRoot, linkedMedwayUiRoot, nativePackageRoot) {
143
+ const medwayUiRoot = path.resolve(projectRoot, linkedMedwayUiRoot);
144
+ const linkedNativeSrc = path.resolve(medwayUiRoot, 'native/src');
145
+ const npmNativeSrc = path.join(nativePackageRoot, 'src');
146
+ const isMonorepoLink = fs.existsSync(linkedNativeSrc);
147
+
148
+ return {
149
+ medwayUiRoot: isMonorepoLink ? medwayUiRoot : nativePackageRoot,
150
+ medwayUiNativeRoot: isMonorepoLink
151
+ ? path.resolve(medwayUiRoot, 'native')
152
+ : nativePackageRoot,
153
+ medwayUiNativeSrc: isMonorepoLink ? linkedNativeSrc : npmNativeSrc,
154
+ medwayUiSharedSrc: isMonorepoLink ? path.resolve(medwayUiRoot, 'src') : null,
155
+ isMonorepoLink,
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Metro + NativeWind para @medway-ui/native (monorepo file: ou npm).
161
+ *
162
+ * @param {import('expo/metro-config').MetroConfig} config
163
+ * @param {object} [options]
164
+ * @param {string} options.projectRoot
165
+ * @param {string} [options.linkedMedwayUiRoot] - auto-detecta monorepo ou npm quando omitido
166
+ * @param {boolean} [options.resolveNativeSource=true] - aponta @medway-ui/native para src/
167
+ */
168
+ function withMedwayUi(config, options = {}) {
169
+ const projectRoot = options.projectRoot ?? process.cwd();
170
+ const linkedMedwayUiRoot =
171
+ options.linkedMedwayUiRoot ?? detectLinkedMedwayUiRoot(projectRoot);
172
+ const resolveNativeSource = options.resolveNativeSource ?? true;
173
+
174
+ const nativePackageRoot = path.resolve(__dirname, '..');
175
+ const globalCssInput = path.join(nativePackageRoot, 'setup/global.css');
176
+
177
+ const {
178
+ medwayUiRoot,
179
+ medwayUiNativeRoot,
180
+ medwayUiNativeSrc,
181
+ medwayUiSharedSrc,
182
+ isMonorepoLink,
183
+ } = resolveMedwayUiPaths(projectRoot, linkedMedwayUiRoot, nativePackageRoot);
184
+
185
+ if (isMonorepoLink) {
186
+ const escapedMedwayUiRoot = medwayUiRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
187
+
188
+ config.watchFolders = [...(config.watchFolders ?? []), medwayUiRoot];
189
+
190
+ const existingBlockList = config.resolver.blockList;
191
+ const blockListArray = Array.isArray(existingBlockList)
192
+ ? existingBlockList
193
+ : existingBlockList
194
+ ? [existingBlockList]
195
+ : [];
196
+
197
+ config.resolver.blockList = [
198
+ ...blockListArray,
199
+ new RegExp(`${escapedMedwayUiRoot}[\\\\/]native[\\\\/]node_modules[\\\\/].*`),
200
+ new RegExp(`${escapedMedwayUiRoot}[\\\\/]node_modules[\\\\/].*`),
201
+ ];
202
+ }
203
+
204
+ function isMedwayUiOrigin(originModulePath) {
205
+ return (
206
+ originModulePath.includes(`${path.sep}medway-ui${path.sep}`) ||
207
+ originModulePath.includes(`${path.sep}@medway-ui${path.sep}`)
208
+ );
209
+ }
210
+
211
+ function isBareImport(moduleName) {
212
+ return !moduleName.startsWith('.') && !moduleName.startsWith('/');
213
+ }
214
+
215
+ const defaultResolveRequest = config.resolver.resolveRequest;
216
+
217
+ config.resolver.resolveRequest = (context, moduleName, platform) => {
218
+ if (resolveNativeSource && moduleName === '@medway-ui/native') {
219
+ return {
220
+ filePath: path.resolve(medwayUiNativeSrc, 'index.ts'),
221
+ type: 'sourceFile',
222
+ };
223
+ }
224
+
225
+ if (isMedwayUiOrigin(context.originModulePath)) {
226
+ if (moduleName.startsWith('@/')) {
227
+ const resolved = resolveMedwayAlias(
228
+ moduleName,
229
+ medwayUiNativeSrc,
230
+ medwayUiSharedSrc,
231
+ );
232
+
233
+ if (resolved) {
234
+ return { filePath: resolved, type: 'sourceFile' };
235
+ }
236
+ }
237
+
238
+ if (isBareImport(moduleName)) {
239
+ const resolved = resolveBareModule(moduleName, [
240
+ projectRoot,
241
+ nativePackageRoot,
242
+ medwayUiNativeRoot,
243
+ ]);
244
+
245
+ if (resolved) {
246
+ return { filePath: resolved, type: 'sourceFile' };
247
+ }
248
+ }
249
+ }
250
+
251
+ if (defaultResolveRequest) {
252
+ return defaultResolveRequest(context, moduleName, platform);
253
+ }
254
+
255
+ return context.resolveRequest(context, moduleName, platform);
256
+ };
257
+
258
+ const withNativeWind = loadWithNativeWind(projectRoot);
259
+
260
+ return withNativeWind(config, {
261
+ input: globalCssInput,
262
+ ...options.nativeWind,
263
+ });
264
+ }
265
+
266
+ module.exports = { withMedwayUi, detectLinkedMedwayUiRoot, resolveMedwayUiPaths };
@@ -0,0 +1,84 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function loadDesignTokens(projectRoot) {
5
+ const tokensPath = require.resolve('@medway-ui/core/tokens', {
6
+ paths: [projectRoot],
7
+ });
8
+
9
+ try {
10
+ return require(tokensPath);
11
+ } catch (error) {
12
+ // Node 22+ não permite require de .ts dentro de node_modules.
13
+ if (error?.code !== 'ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING') {
14
+ throw error;
15
+ }
16
+
17
+ const jiti = require('jiti')(path.join(projectRoot, 'tailwind.config.js'));
18
+ return jiti(tokensPath);
19
+ }
20
+ }
21
+
22
+ function resolveNativePackageRoot() {
23
+ return path.resolve(__dirname, '..');
24
+ }
25
+
26
+ /**
27
+ * Tailwind config para apps com @medway-ui/native.
28
+ *
29
+ * @param {object} [options]
30
+ * @param {string} options.projectRoot - raiz do app consumidor
31
+ * @param {string[]} [options.content] - globs extras (ex.: telas migradas)
32
+ * @param {string} [options.linkedMedwayUiRoot] - monorepo file: (ex.: '../medway-ui')
33
+ */
34
+ function createMedwayUiTailwindConfig(options = {}) {
35
+ const { projectRoot = process.cwd(), content = [], linkedMedwayUiRoot } = options;
36
+
37
+ const {
38
+ colors,
39
+ fontFamily,
40
+ fontSize,
41
+ fontWeight,
42
+ lineHeight,
43
+ borderRadius,
44
+ boxShadow,
45
+ } = loadDesignTokens(projectRoot);
46
+
47
+ const nativeRoot = resolveNativePackageRoot();
48
+
49
+ const libContent = [
50
+ path.join(nativeRoot, 'src/**/*.{js,jsx,ts,tsx}'),
51
+ path.join(projectRoot, 'node_modules/@medway-ui/native/src/**/*.{ts,tsx}'),
52
+ ];
53
+
54
+ if (linkedMedwayUiRoot) {
55
+ const linkedNativeSrc = path.resolve(
56
+ projectRoot,
57
+ linkedMedwayUiRoot,
58
+ 'native/src',
59
+ );
60
+
61
+ if (fs.existsSync(linkedNativeSrc)) {
62
+ libContent.push(path.join(linkedNativeSrc, '**/*.{ts,tsx}'));
63
+ }
64
+ }
65
+
66
+ return {
67
+ content: ['./App.{js,jsx,ts,tsx}', ...content, ...libContent],
68
+ darkMode: 'class',
69
+ presets: [require('nativewind/preset')],
70
+ theme: {
71
+ extend: {
72
+ colors,
73
+ fontFamily,
74
+ fontSize,
75
+ fontWeight,
76
+ lineHeight,
77
+ borderRadius,
78
+ boxShadow,
79
+ },
80
+ },
81
+ };
82
+ }
83
+
84
+ module.exports = { createMedwayUiTailwindConfig };