@thanhphi/create-360-miniapp 0.0.0-stage → 0.2.0

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -2
  3. package/bin/create-360-miniapp.cjs +54 -0
  4. package/lib/create.cjs +56 -0
  5. package/package.json +9 -4
  6. package/template/.eslintrc.cjs +1 -0
  7. package/template/.prettierrc.cjs +1 -0
  8. package/template/.watchmanconfig +1 -0
  9. package/template/App.tsx +2 -0
  10. package/template/README.md +234 -0
  11. package/template/__tests__/auth.test.ts +287 -0
  12. package/template/aliases.cjs +16 -0
  13. package/template/app.config.js +23 -0
  14. package/template/app.json +17 -0
  15. package/template/assets/.gitkeep +0 -0
  16. package/template/babel.config.js +7 -0
  17. package/template/declare/.gitkeep +0 -0
  18. package/template/declare/assets.d.ts +11 -0
  19. package/template/env/beta.json +16 -0
  20. package/template/env/prod.json +16 -0
  21. package/template/env/staging.json +16 -0
  22. package/template/gitignore +19 -0
  23. package/template/index.js +2 -0
  24. package/template/index.repack.js +14 -0
  25. package/template/jest.config.cjs +29 -0
  26. package/template/metro.config.js +6 -0
  27. package/template/package.json +88 -0
  28. package/template/pipelines/beta.yml +91 -0
  29. package/template/pipelines/production.yml +91 -0
  30. package/template/pipelines/staging.yml +91 -0
  31. package/template/pipelines/templates/build-and-zip.yml +89 -0
  32. package/template/pipelines/templates/upload-and-register.yml +89 -0
  33. package/template/react-native.config.js +3 -0
  34. package/template/rspack.config.cjs +53 -0
  35. package/template/scripts/clean-mini-app-output.js +9 -0
  36. package/template/scripts/sync-env.js +29 -0
  37. package/template/src/App.tsx +50 -0
  38. package/template/src/StandaloneApp.tsx +43 -0
  39. package/template/src/assets/icons/.gitkeep +1 -0
  40. package/template/src/assets/images/.gitkeep +1 -0
  41. package/template/src/assets/svg/.gitkeep +1 -0
  42. package/template/src/bootstrap.tsx +7 -0
  43. package/template/src/common/mini-app/provider.tsx +44 -0
  44. package/template/src/common/mini-app/types.ts +13 -0
  45. package/template/src/config/.gitkeep +0 -0
  46. package/template/src/constants/.gitkeep +0 -0
  47. package/template/src/hooks/.gitkeep +0 -0
  48. package/template/src/library/components/.gitkeep +0 -0
  49. package/template/src/library/networking/client.ts +53 -0
  50. package/template/src/library/networking/errors.ts +25 -0
  51. package/template/src/library/utils/responsive.ts +7 -0
  52. package/template/src/library/utils/storage/.gitkeep +0 -0
  53. package/template/src/modules/__MODULE__/assets/.gitkeep +0 -0
  54. package/template/src/modules/__MODULE__/components/ProfileCard.tsx +37 -0
  55. package/template/src/modules/__MODULE__/constants/.gitkeep +0 -0
  56. package/template/src/modules/__MODULE__/navigation/ModuleNavigator.tsx +18 -0
  57. package/template/src/modules/__MODULE__/redux/home-slice.ts +13 -0
  58. package/template/src/modules/__MODULE__/screens/home/hooks/use-home.ts +30 -0
  59. package/template/src/modules/__MODULE__/screens/home/index.tsx +97 -0
  60. package/template/src/modules/__MODULE__/services/profile.service.ts +32 -0
  61. package/template/src/modules/__MODULE__/types/.gitkeep +0 -0
  62. package/template/src/modules/auth/components/.gitkeep +0 -0
  63. package/template/src/modules/auth/constants/.gitkeep +0 -0
  64. package/template/src/modules/auth/hooks/use-auth.ts +55 -0
  65. package/template/src/modules/auth/navigation/AuthenticationNavigator.tsx +16 -0
  66. package/template/src/modules/auth/redux/auth-slice.ts +36 -0
  67. package/template/src/modules/auth/redux/selectors.ts +6 -0
  68. package/template/src/modules/auth/screens/LoginScreen.tsx +75 -0
  69. package/template/src/modules/auth/services/oidc.ts +64 -0
  70. package/template/src/modules/auth/services/standalone-session.ts +121 -0
  71. package/template/src/navigation/MainStack.tsx +46 -0
  72. package/template/src/redux/RootReducers.ts +8 -0
  73. package/template/src/redux/hooks.ts +5 -0
  74. package/template/src/redux/store.ts +10 -0
  75. package/template/src/services/application.ts +87 -0
  76. package/template/src/themes/colors/dark.ts +90 -0
  77. package/template/src/themes/colors/light.ts +90 -0
  78. package/template/src/themes/index.ts +38 -0
  79. package/template/src/themes/standalone.ts +6 -0
  80. package/template/src/types/.gitkeep +0 -0
  81. package/template/tsconfig.json +65 -0
@@ -0,0 +1,121 @@
1
+ import {
2
+ AuthError,
3
+ createSessionCoordinator,
4
+ type HostCapabilities,
5
+ type SessionSnapshot,
6
+ } from '@thanhphi/miniapp-sdk';
7
+ import type { AuthActions } from '../../../common/mini-app/types';
8
+
9
+ export interface OidcTokens {
10
+ accessToken: string;
11
+ refreshToken?: string;
12
+ }
13
+
14
+ export interface OidcOperations {
15
+ isConfigured: boolean;
16
+ authorize(): Promise<OidcTokens>;
17
+ refresh(refreshToken: string): Promise<OidcTokens>;
18
+ }
19
+
20
+ /** Standalone owns its session. No credential is written to Redux or disk. */
21
+ export function createStandaloneSession(operations: OidcOperations) {
22
+ let snapshot: SessionSnapshot = { sessionId: null, accessToken: null };
23
+ let refreshToken: string | undefined;
24
+ let generation = 0;
25
+ let disposed = false;
26
+ let loginPending: Promise<void> | undefined;
27
+ const listeners = new Set<(value: SessionSnapshot) => void>();
28
+ const publish = (next: SessionSnapshot) => {
29
+ snapshot = next;
30
+ for (const listener of listeners) listener({ ...snapshot });
31
+ };
32
+ const signOut = () => {
33
+ generation += 1;
34
+ refreshToken = undefined;
35
+ loginPending = undefined;
36
+ publish({ sessionId: null, accessToken: null });
37
+ };
38
+ const coordinator = createSessionCoordinator({
39
+ getSnapshot: () => ({ ...snapshot }),
40
+ async refresh(before) {
41
+ const credential = refreshToken;
42
+ if (!credential) throw new AuthError('expired');
43
+ const currentGeneration = generation;
44
+ const result = await operations.refresh(credential);
45
+ if (
46
+ disposed ||
47
+ currentGeneration !== generation ||
48
+ before.sessionId !== snapshot.sessionId
49
+ ) {
50
+ throw new AuthError('session-changed');
51
+ }
52
+ if (!result.accessToken) throw new AuthError('expired');
53
+ refreshToken = result.refreshToken || credential;
54
+ publish({ ...snapshot, accessToken: result.accessToken });
55
+ return result.accessToken;
56
+ },
57
+ });
58
+ const actions: AuthActions = {
59
+ isConfigured: operations.isConfigured,
60
+ signIn() {
61
+ if (disposed) return Promise.reject(new AuthError('unavailable'));
62
+ if (!operations.isConfigured) {
63
+ return Promise.reject(
64
+ new AuthError(
65
+ 'unavailable',
66
+ 'Hãy cấu hình OIDC trong env trước khi đăng nhập.',
67
+ ),
68
+ );
69
+ }
70
+ if (loginPending) return loginPending;
71
+ const attempt = ++generation;
72
+ const request = Promise.resolve().then(async () => {
73
+ if (disposed || attempt !== generation)
74
+ throw new AuthError('session-changed');
75
+ const result = await operations.authorize();
76
+ if (disposed || attempt !== generation)
77
+ throw new AuthError('session-changed');
78
+ if (!result.accessToken) throw new AuthError('unavailable');
79
+ refreshToken = result.refreshToken;
80
+ publish({
81
+ sessionId: `standalone-${attempt}`,
82
+ accessToken: result.accessToken,
83
+ });
84
+ });
85
+ loginPending = request;
86
+ const release = () => {
87
+ if (loginPending === request) loginPending = undefined;
88
+ };
89
+ request.then(release, release);
90
+ return request;
91
+ },
92
+ // Clears the local app session; provider-wide logout is a separate host policy.
93
+ signOut,
94
+ };
95
+ const capabilities: HostCapabilities = {
96
+ auth: {
97
+ getSnapshot: () => ({ ...snapshot }),
98
+ refreshIfNeeded: coordinator.refreshIfNeeded,
99
+ subscribe(listener) {
100
+ if (disposed) return () => {};
101
+ listeners.add(listener);
102
+ return () => {
103
+ listeners.delete(listener);
104
+ };
105
+ },
106
+ },
107
+ closeMiniApp: signOut,
108
+ };
109
+ return {
110
+ capabilities,
111
+ actions,
112
+ dispose() {
113
+ disposed = true;
114
+ generation += 1;
115
+ refreshToken = undefined;
116
+ snapshot = { sessionId: null, accessToken: null };
117
+ listeners.clear();
118
+ coordinator.dispose();
119
+ },
120
+ };
121
+ }
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import {
3
+ DefaultTheme,
4
+ DarkTheme,
5
+ NavigationContainer,
6
+ NavigationIndependentTree,
7
+ } from '@react-navigation/native';
8
+ import { useAppSelector } from '../redux/hooks';
9
+ import {
10
+ selectIsAuthenticated,
11
+ selectSessionId,
12
+ } from '../modules/auth/redux/selectors';
13
+ import AuthenticationNavigator from '../modules/auth/navigation/AuthenticationNavigator';
14
+ import ModuleNavigator from '../modules/__MODULE__/navigation/ModuleNavigator';
15
+ import { useUnistyles } from 'react-native-unistyles';
16
+
17
+ export function MainStack() {
18
+ const {
19
+ theme: { type, color },
20
+ } = useUnistyles();
21
+ const base = type === 'dark' ? DarkTheme : DefaultTheme;
22
+ const navigationTheme = {
23
+ ...base,
24
+ colors: {
25
+ ...base.colors,
26
+ background: color.base3,
27
+ card: color.background,
28
+ primary: color.primary500,
29
+ text: color.content1,
30
+ border: color.divider,
31
+ notification: color.danger,
32
+ },
33
+ };
34
+ const authenticated = useAppSelector(selectIsAuthenticated);
35
+ const sessionId = useAppSelector(selectSessionId);
36
+ return (
37
+ <NavigationIndependentTree>
38
+ <NavigationContainer
39
+ key={sessionId ?? 'signed-out'}
40
+ theme={navigationTheme}
41
+ >
42
+ {authenticated ? <ModuleNavigator /> : <AuthenticationNavigator />}
43
+ </NavigationContainer>
44
+ </NavigationIndependentTree>
45
+ );
46
+ }
@@ -0,0 +1,8 @@
1
+ import { combineReducers, createAction } from '@reduxjs/toolkit';
2
+ import auth from '../modules/auth/redux/auth-slice';
3
+ import home from '../modules/__MODULE__/redux/home-slice';
4
+
5
+ export const resetApp = createAction('app/reset');
6
+ const combined = combineReducers({ auth, home });
7
+ export const rootReducer: typeof combined = (state, action) =>
8
+ combined(resetApp.match(action) ? undefined : state, action);
@@ -0,0 +1,5 @@
1
+ import { useDispatch, useSelector } from 'react-redux';
2
+ import type { AppDispatch, RootState } from './store';
3
+
4
+ export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
5
+ export const useAppSelector = useSelector.withTypes<RootState>();
@@ -0,0 +1,10 @@
1
+ import { configureStore } from '@reduxjs/toolkit';
2
+ import { rootReducer } from './RootReducers';
3
+
4
+ /** Factory: each mounted mini app has its own state. Tokens are never persisted. */
5
+ export function createAppStore() {
6
+ return configureStore({ reducer: rootReducer });
7
+ }
8
+ export type AppStore = ReturnType<typeof createAppStore>;
9
+ export type RootState = ReturnType<AppStore['getState']>;
10
+ export type AppDispatch = AppStore['dispatch'];
@@ -0,0 +1,87 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+ import {
3
+ AuthError,
4
+ HttpError,
5
+ createMiniAppRuntime,
6
+ type HostCapabilities,
7
+ type SessionSnapshot,
8
+ } from '@thanhphi/miniapp-sdk';
9
+ import type { AuthActions } from '../common/mini-app/types';
10
+ import { createApiClient } from '../library/networking/client';
11
+ import { createAppStore } from '../redux/store';
12
+ import { resetApp } from '../redux/RootReducers';
13
+ import {
14
+ authenticationFailed,
15
+ sessionReceived,
16
+ } from '../modules/auth/redux/auth-slice';
17
+
18
+ export function createApplicationServices(
19
+ capabilities: HostCapabilities,
20
+ authActions?: AuthActions,
21
+ ) {
22
+ const runtime = createMiniAppRuntime(capabilities);
23
+ const store = createAppStore();
24
+ const queryClient = new QueryClient({
25
+ defaultOptions: {
26
+ queries: {
27
+ staleTime: 60_000,
28
+ retry: (count, error) =>
29
+ !(error instanceof AuthError) &&
30
+ !(
31
+ error instanceof HttpError &&
32
+ ['cancelled', 'disposed'].includes(error.kind)
33
+ ) &&
34
+ count < 1,
35
+ },
36
+ },
37
+ });
38
+ let session: SessionSnapshot | undefined;
39
+ const receiveSession = (next: SessionSnapshot) => {
40
+ if (
41
+ session?.sessionId !== next.sessionId ||
42
+ (session?.accessToken && !next.accessToken)
43
+ ) {
44
+ // Cancellation reaches SDK fetch through React Query's AbortSignal.
45
+ void queryClient.cancelQueries();
46
+ queryClient.clear();
47
+ store.dispatch(resetApp());
48
+ }
49
+ session = { ...next };
50
+ store.dispatch(
51
+ sessionReceived({
52
+ sessionId: next.sessionId,
53
+ authenticated: Boolean(next.sessionId && next.accessToken),
54
+ }),
55
+ );
56
+ };
57
+ const unsubscribe = runtime.auth.subscribe(receiveSession);
58
+ receiveSession(runtime.auth.getSnapshot());
59
+ const api = createApiClient(runtime.auth, (error, failedSessionId) => {
60
+ if (runtime.auth.getSnapshot().sessionId !== failedSessionId) return;
61
+ if (error.kind === 'expired') {
62
+ void queryClient.cancelQueries();
63
+ queryClient.clear();
64
+ authActions?.signOut();
65
+ store.dispatch(resetApp());
66
+ store.dispatch(
67
+ authenticationFailed('Phiên đăng nhập đã hết hạn. Hãy đăng nhập lại.'),
68
+ );
69
+ }
70
+ });
71
+ runtime.onDispose(() => {
72
+ unsubscribe();
73
+ api.dispose();
74
+ void queryClient.cancelQueries();
75
+ queryClient.clear();
76
+ store.dispatch(resetApp());
77
+ });
78
+ return {
79
+ runtime,
80
+ store,
81
+ queryClient,
82
+ api,
83
+ authActions,
84
+ dispose: runtime.dispose,
85
+ };
86
+ }
87
+ export type ApplicationServices = ReturnType<typeof createApplicationServices>;
@@ -0,0 +1,90 @@
1
+ import type { UIColors } from '@thanhphi/ui360';
2
+
3
+ export const darkColors = {
4
+ backdrop: 'rgba(0,0,0,0.5)',
5
+ background: '#18181B',
6
+ base2: '#27272A',
7
+ base3: '#27272A',
8
+ divider: '#3F3F46',
9
+ focus: '#006FEE',
10
+
11
+ content1: '#FAFAFA',
12
+ content2: '#ADAEB5',
13
+ content3: '#797A85',
14
+ content4: '#636470',
15
+
16
+ textPrimary: '#FFFFFF',
17
+
18
+ default: '#3F3F46',
19
+ primary: '#B50101',
20
+ secondary: '#F5A524',
21
+ success: '#17C964',
22
+ warning: '#F5A524',
23
+ danger: '#F5222D',
24
+
25
+ gray50: '#18181B',
26
+ gray100: '#27272A',
27
+ gray200: '#3F3F46',
28
+ gray300: '#52525B',
29
+ gray400: '#71717A',
30
+ gray500: '#A1A1AA',
31
+ gray600: '#D4D4D8',
32
+ gray700: '#E4E4E7',
33
+ gray800: '#F4F4F5',
34
+ gray900: '#FAFAFA',
35
+
36
+ primary50: '#240000',
37
+ primary100: '#480000',
38
+ primary200: '#5B0101',
39
+ primary300: '#6D0101',
40
+ primary400: '#910101',
41
+ primary500: '#B50101',
42
+ primary600: '#C43434',
43
+ primary700: '#D36767',
44
+ primary800: '#E19999',
45
+ primary900: '#F0CCCC',
46
+
47
+ secondary50: '#312107',
48
+ secondary100: '#62420E',
49
+ secondary200: '#7B5312',
50
+ secondary300: '#936316',
51
+ secondary400: '#C4841D',
52
+ secondary500: '#F5A524',
53
+ secondary600: '#F7B750',
54
+ secondary700: '#F9C97C',
55
+ secondary800: '#FBDBA7',
56
+ secondary900: '#FDEDD3',
57
+
58
+ success50: '#052814',
59
+ success100: '#095028',
60
+ success200: '#0E793C',
61
+ success300: '#12A150',
62
+ success400: '#17C964',
63
+ success500: '#45D483',
64
+ success600: '#74DFA2',
65
+ success700: '#A2E9C1',
66
+ success800: '#D1F4E0',
67
+ success900: '#E8FAF0',
68
+
69
+ warning50: '#312107',
70
+ warning100: '#62420E',
71
+ warning200: '#936316',
72
+ warning300: '#C4841D',
73
+ warning400: '#F5A524',
74
+ warning500: '#F7B750',
75
+ warning600: '#F9C97C',
76
+ warning700: '#FBDBA7',
77
+ warning800: '#FDEDD3',
78
+ warning900: '#FEFCE8',
79
+
80
+ danger50: '#310709',
81
+ danger100: '#620E12',
82
+ danger200: '#7B1117',
83
+ danger300: '#93141B',
84
+ danger400: '#C41B24',
85
+ danger500: '#F5222D',
86
+ danger600: '#F74E57',
87
+ danger700: '#F97A81',
88
+ danger800: '#FBA7AB',
89
+ danger900: '#FDD3D5',
90
+ } as const satisfies UIColors;
@@ -0,0 +1,90 @@
1
+ import type { UIColors } from '@thanhphi/ui360';
2
+
3
+ export const lightColors = {
4
+ backdrop: 'rgba(0,0,0,0.5)',
5
+ background: '#FFFFFF',
6
+ base2: '#FFF7F7',
7
+ base3: '#F7F7F7',
8
+ divider: '#F4F4F4',
9
+ focus: '#006FEE',
10
+
11
+ content1: '#232323',
12
+ content2: '#666666',
13
+ content3: '#AFB4B8',
14
+ content4: '#DBDFE2',
15
+
16
+ textPrimary: '#FFFFFF',
17
+
18
+ default: '#D4D4D8',
19
+ primary: '#B50101',
20
+ secondary: '#F5A524',
21
+ success: '#17C964',
22
+ warning: '#F5A524',
23
+ danger: '#F5222D',
24
+
25
+ gray50: '#FAFAFA',
26
+ gray100: '#F4F4F5',
27
+ gray200: '#E4E4E7',
28
+ gray300: '#D4D4D8',
29
+ gray400: '#A1A1AA',
30
+ gray500: '#71717A',
31
+ gray600: '#52525B',
32
+ gray700: '#3F3F46',
33
+ gray800: '#27272A',
34
+ gray900: '#18181B',
35
+
36
+ primary50: '#F8E6E6',
37
+ primary100: '#F0CCCC',
38
+ primary200: '#E19999',
39
+ primary300: '#D36767',
40
+ primary400: '#C43434',
41
+ primary500: '#B50101',
42
+ primary600: '#910101',
43
+ primary700: '#6D0101',
44
+ primary800: '#480000',
45
+ primary900: '#240000',
46
+
47
+ secondary50: '#FEF6E9',
48
+ secondary100: '#FDEDD3',
49
+ secondary200: '#FBDBA7',
50
+ secondary300: '#F9C97C',
51
+ secondary400: '#F7B750',
52
+ secondary500: '#F5A524',
53
+ secondary600: '#C4841D',
54
+ secondary700: '#936316',
55
+ secondary800: '#7B5312',
56
+ secondary900: '#62420E',
57
+
58
+ success50: '#E8FAF0',
59
+ success100: '#D1F4E0',
60
+ success200: '#A2E9C1',
61
+ success300: '#74DFA2',
62
+ success400: '#45D483',
63
+ success500: '#17C964',
64
+ success600: '#12A150',
65
+ success700: '#0E793C',
66
+ success800: '#095028',
67
+ success900: '#095028',
68
+
69
+ warning50: '#FEFCE8',
70
+ warning100: '#FDEDD3',
71
+ warning200: '#FBDBA7',
72
+ warning300: '#F9C97C',
73
+ warning400: '#F7B750',
74
+ warning500: '#F5A524',
75
+ warning600: '#C4841D',
76
+ warning700: '#936316',
77
+ warning800: '#62420E',
78
+ warning900: '#312107',
79
+
80
+ danger50: '#FEE9EA',
81
+ danger100: '#FDD3D5',
82
+ danger200: '#FBA7AB',
83
+ danger300: '#F97A81',
84
+ danger400: '#F74E57',
85
+ danger500: '#F5222D',
86
+ danger600: '#C41B24',
87
+ danger700: '#93141B',
88
+ danger800: '#620E12',
89
+ danger900: '#310709',
90
+ } as const satisfies UIColors;
@@ -0,0 +1,38 @@
1
+ import { lightColors } from './colors/light';
2
+ import { darkColors } from './colors/dark';
3
+ import { create360Theme, type UIThemeOptions } from '@thanhphi/ui360';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { ratioW } from '../library/utils/responsive';
6
+
7
+ function createAppTheme(mode: 'light' | 'dark', options: UIThemeOptions = {}) {
8
+ const colors = mode === 'dark' ? darkColors : lightColors;
9
+ return create360Theme({
10
+ // Match the 360 host; default tokens are scaled exactly once during creation.
11
+ scale: ratioW(1),
12
+ ...options,
13
+ mode,
14
+ colors: { ...colors, ...options.colors },
15
+ });
16
+ }
17
+
18
+ // Pure theme values: importing a screen must never reconfigure the host runtime.
19
+ export const appThemes = {
20
+ light: createAppTheme('light'),
21
+ dark: createAppTheme('dark'),
22
+ };
23
+
24
+ /** Only standalone entries configure native themes; hosted App never calls this. */
25
+ export function configureStandaloneTheme(options: UIThemeOptions = {}) {
26
+ StyleSheet.configure({
27
+ themes: {
28
+ light: createAppTheme('light', options),
29
+ dark: createAppTheme('dark', options),
30
+ },
31
+ settings: { initialTheme: options.mode ?? 'light' },
32
+ });
33
+ }
34
+
35
+ type AppThemes = typeof appThemes;
36
+ declare module 'react-native-unistyles' {
37
+ export interface UnistylesThemes extends AppThemes {}
38
+ }
@@ -0,0 +1,6 @@
1
+ import type { UIThemeOptions } from '@thanhphi/ui360';
2
+ import { configureStandaloneTheme } from './index';
3
+
4
+ // Change dev-only palette/mode overrides here. Hosted App never imports this file.
5
+ export const standaloneThemeOptions: UIThemeOptions = { mode: 'light' };
6
+ configureStandaloneTheme(standaloneThemeOptions);
File without changes
@@ -0,0 +1,65 @@
1
+ {
2
+ "extends": "@react-native/typescript-config",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "paths": {
6
+ "@/*": [
7
+ "./src/*"
8
+ ],
9
+ "@auth/*": [
10
+ "./src/modules/auth/*"
11
+ ],
12
+ "@feature/*": [
13
+ "./src/modules/__MODULE__/*"
14
+ ],
15
+ "@assets/*": [
16
+ "./src/assets/*"
17
+ ],
18
+ "@common/*": [
19
+ "./src/common/*"
20
+ ],
21
+ "@components/*": [
22
+ "./src/library/components/*"
23
+ ],
24
+ "@env-config": [
25
+ "./env/env-config"
26
+ ],
27
+ "@modules/*": [
28
+ "./src/modules/*"
29
+ ],
30
+ "@navigation/*": [
31
+ "./src/navigation/*"
32
+ ],
33
+ "@networking/*": [
34
+ "./src/library/networking/*"
35
+ ],
36
+ "@redux/*": [
37
+ "./src/redux/*"
38
+ ],
39
+ "@storage/*": [
40
+ "./src/library/utils/storage/*"
41
+ ],
42
+ "@theme/*": [
43
+ "./src/themes/*"
44
+ ],
45
+ "@utils/*": [
46
+ "./src/library/utils/*"
47
+ ]
48
+ }
49
+ },
50
+ "include": [
51
+ "App.tsx",
52
+ "src/**/*.ts",
53
+ "src/**/*.tsx",
54
+ "env/env-config.ts",
55
+ "declare/**/*.d.ts",
56
+ "__tests__/**/*.ts",
57
+ "__tests__/**/*.tsx"
58
+ ],
59
+ "exclude": [
60
+ "node_modules",
61
+ "ios",
62
+ "android",
63
+ "build"
64
+ ]
65
+ }