@admin-layout/gluestack-ui-mobile 8.5.7-alpha.0 → 9.0.2-alpha.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.
@@ -0,0 +1,1454 @@
1
+ import * as fs from 'fs';
2
+ import path from 'path';
3
+ import prettier from 'prettier';
4
+ import { fileURLToPath } from 'url';
5
+ import { createRequire } from 'module';
6
+ import { exec as execCallback } from 'child_process';
7
+ import { getSortedNavigations } from '@common-stack/client-react/lib/route/react-navigation/get-navigation-utils.js';
8
+ import { getReplacedRouteConfig } from './getReplacedRouteConfig.js';
9
+ const require = createRequire(import.meta.url);
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const configFilePath = path.join(__dirname, '../layout.json');
13
+ const appNavigationFileName = 'navigation.tsx';
14
+ const mainRoutesFileName = 'main_routes.json';
15
+ const modulesFileName = 'modules.ts';
16
+ const stacksDirPath = 'stack/index.tsx';
17
+ const drawerFilePath = 'drawer/index.tsx';
18
+ const hostDrawerFilePath = 'host_drawer/index.tsx';
19
+ const bottomFilePath = 'bottom/index.tsx';
20
+ const hostBottomFilePath = 'host_bottom/index.tsx';
21
+
22
+ type IGenerateMobileNavigationsProps = {
23
+ appDirPath: string;
24
+ modules: any;
25
+ initialRouteName?: string;
26
+ unauthenticatedComponentPath?: string;
27
+ customTabBarPath?: string;
28
+ customDrawerPath?: string;
29
+ customHeaderPath?: string;
30
+ };
31
+
32
+ export const readJsonFile = (filePath) => {
33
+ return new Promise((resolve, reject) => {
34
+ fs.readFile(filePath, 'utf8', (err, data) => {
35
+ if (err) {
36
+ return reject(err);
37
+ }
38
+ try {
39
+ const jsonData = JSON.parse(data);
40
+ resolve(jsonData);
41
+ } catch (parseErr) {
42
+ reject(parseErr);
43
+ }
44
+ });
45
+ });
46
+ };
47
+
48
+ export const readFile = (filePath) => {
49
+ return new Promise((resolve, reject) => {
50
+ fs.readFile(filePath, 'utf8', (err, data) => {
51
+ if (err) {
52
+ return reject(err);
53
+ }
54
+ try {
55
+ resolve(data);
56
+ } catch (parseErr) {
57
+ reject(parseErr);
58
+ }
59
+ });
60
+ });
61
+ };
62
+
63
+ export const getLayoutConfig = async () => {
64
+ const layoutConfigFileData = await readJsonFile(configFilePath);
65
+ return layoutConfigFileData;
66
+ };
67
+
68
+ export class GenerateMobileNavigations {
69
+ #layoutSettings: any = process.env.LAYOUT_SETTINGS ? JSON.parse(process.env.LAYOUT_SETTINGS) : null;
70
+ #appDirPath: string;
71
+ #modules: any;
72
+ #initialRouteName: string;
73
+ #unauthenticatedComponentPath: string;
74
+ #customTabBarPath: string;
75
+ #customDrawerPath: string;
76
+ #customHeaderPath: string;
77
+
78
+ constructor({
79
+ appDirPath,
80
+ modules,
81
+ initialRouteName = '',
82
+ unauthenticatedComponentPath = null,
83
+ customTabBarPath = null,
84
+ customDrawerPath = null,
85
+ customHeaderPath = null,
86
+ }: IGenerateMobileNavigationsProps) {
87
+ this.#layoutSettings = process.env.LAYOUT_SETTINGS ? JSON.parse(process.env.LAYOUT_SETTINGS) : null;
88
+ this.#appDirPath = appDirPath;
89
+ this.#modules = modules;
90
+ this.#initialRouteName = initialRouteName;
91
+ this.#unauthenticatedComponentPath = unauthenticatedComponentPath;
92
+ this.#customTabBarPath = customTabBarPath;
93
+ this.#customDrawerPath = customDrawerPath;
94
+ this.#customHeaderPath = customHeaderPath;
95
+ }
96
+
97
+ async #readJsonFile(filePath) {
98
+ return readJsonFile(filePath);
99
+ }
100
+
101
+ async #getLayoutConfig() {
102
+ return getLayoutConfig();
103
+ }
104
+
105
+ async #execPromise(command) {
106
+ return new Promise(function (resolve, reject) {
107
+ execCallback(command, (error, stdout, stderr) => {
108
+ if (error) {
109
+ return resolve(false);
110
+ }
111
+ return resolve(true);
112
+ });
113
+ });
114
+ }
115
+
116
+ async #renameFile(file, new_name) {
117
+ return new Promise((resolve) => {
118
+ fs.access(file, fs.constants.F_OK, (err) => {
119
+ if (err) {
120
+ resolve(false);
121
+ }
122
+
123
+ return fs.rename(file, new_name, (err) => {
124
+ if (err) resolve(false);
125
+ resolve(true);
126
+ });
127
+ });
128
+ });
129
+ }
130
+
131
+ async #writeFile(file, content) {
132
+ return new Promise((resolve) => {
133
+ return fs.writeFile(file, content, 'utf-8', (err) => {
134
+ if (err) resolve(false);
135
+ resolve(true);
136
+ });
137
+ });
138
+ }
139
+
140
+ async #makeDir(dirName) {
141
+ return new Promise((resolve) => {
142
+ return fs.mkdir(dirName, { recursive: true }, (err) => {
143
+ if (err) resolve(false);
144
+ resolve(true);
145
+ });
146
+ });
147
+ }
148
+
149
+ async #getModulesRouteConfig({ modules }) {
150
+ const allFilteredRoutes = [];
151
+ for (const pkg of modules) {
152
+ const pkgPath = require.resolve(pkg);
153
+ const pkgDirPath = path.dirname(pkgPath);
154
+ const pkgFile = path.join(pkgDirPath, 'routes.json');
155
+ if (fs.existsSync(pkgFile)) {
156
+ const fileModuleJSON: any = await readJsonFile(pkgFile);
157
+ if (fileModuleJSON) {
158
+ allFilteredRoutes.push([...fileModuleJSON]);
159
+ }
160
+ // const fileModuleJSON = await import(pkgFile, { assert: { type: 'json' } });
161
+ // if (fileModuleJSON.default) {
162
+ // allFilteredRoutes.push([...fileModuleJSON.default]);
163
+ // }
164
+ }
165
+ }
166
+ return allFilteredRoutes;
167
+ }
168
+
169
+ async #resolveImportPath(routeConfig, importPath) {
170
+ // Remove the '$.' part from the importPath to get the path within the JSON object
171
+ const path = importPath?.replace('$.', '') ?? null;
172
+
173
+ // Split the path into parts for nested property access
174
+ const parts = path?.split('.') ?? [];
175
+
176
+ // Traverse the routeConfig object to get the actual value
177
+ let value = routeConfig;
178
+ for (let part of parts) {
179
+ if (value && Object.prototype.hasOwnProperty.call(value, part)) {
180
+ value = value[part];
181
+ } else {
182
+ // Return null or handle missing path
183
+ return null;
184
+ }
185
+ }
186
+
187
+ return value;
188
+ }
189
+
190
+ async #generateAppRoutesJson({ appDirPath }) {
191
+ const parentDirPath = path.dirname(appDirPath);
192
+ const parentDirName = path.basename(parentDirPath);
193
+ const appDirName = path.basename(appDirPath);
194
+ // const tsFile = 'src/compute.ts';
195
+ // const outputDir = 'src/app';
196
+ const tsFile = `${parentDirName}/compute.ts`;
197
+ const outputDir = `${parentDirName}/${appDirName}`;
198
+ const tscCommand = `tsc ${tsFile} --outDir ${outputDir} --target es6 --module esnext --jsx react --allowSyntheticDefaultImports true --moduleResolution node --esModuleInterop true --forceConsistentCasingInFileNames true --skipLibCheck true`;
199
+ const mainRoutesJsFile = path.join(appDirPath, '/compute.js');
200
+ const mainRoutesMjsFile = path.join(appDirPath, '/compute.mjs');
201
+ const outputFile = path.join(appDirPath, `/${mainRoutesFileName}`);
202
+ const allFilteredRoutes = [];
203
+ try {
204
+ const execResult = await this.#execPromise(tscCommand);
205
+ if (execResult && fs.existsSync(mainRoutesJsFile)) {
206
+ const jsFiledata = fs.readFileSync(mainRoutesJsFile, 'utf8');
207
+ const noCommentsData = jsFiledata
208
+ .replace(/\/\/.*$/gm, '') // Remove single-line comments
209
+ .replace(/\/\*[\s\S]*?\*\//g, ''); // Remove multi-line comments
210
+ const noWhitespaceJsData = noCommentsData.replace(/\s+/g, '');
211
+ if (noWhitespaceJsData?.length == 0) {
212
+ const outPutDirName = path.dirname(outputFile);
213
+ const isDirCreated = await this.#makeDir(outPutDirName);
214
+ if (isDirCreated) {
215
+ const writeFileResponse = await this.#writeFile(outputFile, JSON.stringify(allFilteredRoutes));
216
+ if (writeFileResponse) {
217
+ return true;
218
+ } else return false;
219
+ } else return false;
220
+ } else {
221
+ const newFilePath = mainRoutesJsFile.replace('.js', '.mjs');
222
+ const renameFileResponse = await this.#renameFile(mainRoutesJsFile, newFilePath);
223
+ if (renameFileResponse) {
224
+ if (fs.existsSync(mainRoutesMjsFile)) {
225
+ // Dynamically import the JS file assuming it exports filteredRoutes
226
+ const module = await import(mainRoutesMjsFile); // file is already absolute
227
+ if (module.filteredRoutes) {
228
+ const newRoutes =
229
+ module?.filteredRoutes?.map((filteredRoute) => {
230
+ const routConfig: any = Object.values(filteredRoute)[0];
231
+ const importPath = routConfig.component
232
+ .toString()
233
+ .match(/import\(['"](.*)['"]\)/)[1];
234
+ // routConfig.componentPath = `../.${importPath}`;
235
+ routConfig.componentPath = `.${importPath}.js`;
236
+ return { [routConfig.path]: routConfig };
237
+ }) ?? [];
238
+ allFilteredRoutes.push(...newRoutes);
239
+ const writeFileResponse = await this.#writeFile(
240
+ outputFile,
241
+ JSON.stringify(allFilteredRoutes, null, 2),
242
+ );
243
+
244
+ if (writeFileResponse) {
245
+ fs.unlinkSync(mainRoutesMjsFile);
246
+ return true;
247
+ } else return false;
248
+ }
249
+ } else return false;
250
+ } else return false;
251
+ }
252
+ } else {
253
+ const outPutDirName = path.dirname(outputFile);
254
+ const isDirCreated = await this.#makeDir(outPutDirName);
255
+ if (isDirCreated) {
256
+ const writeFileResponse = await this.#writeFile(outputFile, JSON.stringify(allFilteredRoutes));
257
+ if (writeFileResponse) {
258
+ return true;
259
+ } else return false;
260
+ } else return false;
261
+ }
262
+ // return true;
263
+ } catch (error) {
264
+ console.error(`exec error: ${error.message}`);
265
+ const outPutDirName = path.dirname(outputFile);
266
+ const isDirCreated = await this.#makeDir(outPutDirName);
267
+ if (isDirCreated) {
268
+ const writeFileResponse = await this.#writeFile(outputFile, JSON.stringify(allFilteredRoutes));
269
+ if (writeFileResponse) {
270
+ return true;
271
+ } else return false;
272
+ } else return false;
273
+ }
274
+ }
275
+
276
+ async #generateImportStatements({ modules, initialRouteName }) {
277
+ try {
278
+ const layoutSettings = this.#layoutSettings;
279
+ const layoutConfigFileData = await this.#getLayoutConfig();
280
+ const hostRouteConfig = layoutConfigFileData['host-bottom'];
281
+ const hostRouteKey = Object.keys(hostRouteConfig)[1];
282
+ const hostLayout = hostRouteConfig[hostRouteKey];
283
+
284
+ let importStatements = '';
285
+ let moduleNames = '';
286
+ let moduleRouteConfig = '';
287
+ let moduleNumber = 0;
288
+ importStatements += `import React,{useState} from 'react';\n`;
289
+ importStatements += `import {Feature} from '@common-stack/client-react/lib/connector/connector.native.js';\n`;
290
+ importStatements += `import { useSelector, useDispatch } from 'react-redux';\n`;
291
+ importStatements += `import { CHANGE_SETTINGS_ACTION } from '@admin-layout/client';\n`;
292
+ importStatements += `import {layoutRouteConfig,getReplacedRouteConfig } from '@admin-layout/gluestack-ui-mobile';\n`;
293
+ importStatements += `import mainRouteConfig from './main_routes.json';\n`;
294
+
295
+ modules?.forEach((packageName) => {
296
+ moduleNumber++;
297
+ importStatements += `import module${moduleNumber} from '${packageName}';\n`;
298
+ // moduleNames += `${moduleName}, `;
299
+ moduleNames += `module${moduleNumber}, `;
300
+ moduleRouteConfig += `...[module${moduleNumber}?.routeConfig], `;
301
+ });
302
+
303
+ //const layoutSettings = process.env.LAYOUT_SETTINGS ? JSON.parse(process.env.LAYOUT_SETTINGS) : null;
304
+
305
+ let classStructure = `
306
+ const features = new Feature(
307
+ ${moduleNames.trim()}
308
+ );
309
+
310
+ const mainAppRoutes = mainRouteConfig || [];
311
+
312
+ const appRoutes = [...[mainAppRoutes],${moduleRouteConfig}]?.filter((rc)=>rc.length)??[];
313
+
314
+ const featureRouteConfig = appRoutes?.flat(1)??features?.routeConfig;
315
+ features.routeConfig = featureRouteConfig;
316
+
317
+ export function useGetModules(){
318
+ const dispatch = useDispatch();
319
+ const defaultSettings = useSelector((state:any) => state.settings);
320
+ const initialRouteName = '${initialRouteName}';
321
+ const layoutSettings = ${JSON.stringify({ ...layoutSettings, hostLayout: hostLayout.key })}
322
+ const [appRouteConfig, setAppRouteConfig]:any = useState(null);
323
+
324
+ React.useEffect(() => {
325
+ setDefalutSettings();
326
+ }, []);
327
+
328
+
329
+ const setDefalutSettings = React.useCallback(()=>{
330
+ const config: any = {
331
+ ...defaultSettings,
332
+ ...layoutSettings,
333
+ };
334
+ dispatch({
335
+ type: CHANGE_SETTINGS_ACTION,
336
+ payload: config,
337
+ });
338
+ },[]);
339
+
340
+ React.useEffect(() => {
341
+ if (defaultSettings) {
342
+ const settingObj: any = { ...defaultSettings };
343
+ const layoutType: any = settingObj.layout;
344
+ const {replacedConfiguredRouteConfig} = getReplacedRouteConfig({
345
+ layoutType: layoutType,
346
+ routeConfig: appRoutes,
347
+ layoutConfigData: layoutRouteConfig,
348
+ initialRouteName,
349
+ });
350
+ if(replacedConfiguredRouteConfig){
351
+ const moduleRouteConfigObject = Object.assign({}, ...replacedConfiguredRouteConfig?.flat(1)??[]);
352
+ const replacedRouteConfig = Object.fromEntries(Object.entries(moduleRouteConfigObject));
353
+ const appReplacedRouteConfig = replacedRouteConfig ? Object.keys(replacedRouteConfig)?.map((k)=>({[k]:replacedRouteConfig[k]})) : [];
354
+
355
+ if (appReplacedRouteConfig) {
356
+ const hostRouteConfig = appReplacedRouteConfig?.map((obj)=> Object.fromEntries(Object.entries(obj)?.filter(([key,val])=>key === '/' || key.startsWith('//'+layoutSettings.hostLayout))))?.filter(value => Object.keys(value).length !== 0)??[];
357
+ const layoutRouteConfig = appReplacedRouteConfig?.map((obj)=> Object.fromEntries(Object.entries(obj)?.filter(([key,val])=>key === '/' || !key.startsWith('//'+layoutSettings.hostLayout))))?.filter(value => Object.keys(value).length !== 0)??[];
358
+ const featureRouteConfig = defaultSettings?.layout == 'host-bottom' ? hostRouteConfig:layoutRouteConfig;
359
+ // features.routeConfig = featureRouteConfig;
360
+ setAppRouteConfig(featureRouteConfig);
361
+ }
362
+ }
363
+ }
364
+ }, [defaultSettings]);
365
+
366
+ return {appRouteConfig};
367
+ }
368
+
369
+ export default features;
370
+ `.replace(/,(\s*)$/, ''); // Removes trailing comma
371
+
372
+ // Use Prettier to format the code
373
+ classStructure = prettier.format(classStructure, { parser: 'babel' });
374
+
375
+ const appFeatures = importStatements + '\n' + classStructure;
376
+
377
+ return { appFeatures };
378
+ } catch (err) {
379
+ console.error('Error:', err);
380
+ return false;
381
+ }
382
+ }
383
+
384
+ async #generateModulesTsFile({ appDirPath, modules, initialRouteName }) {
385
+ const moduleTsFile = path.join(appDirPath, `/${modulesFileName}`);
386
+ const imports: any = await this.#generateImportStatements({ modules, initialRouteName });
387
+ const { appFeatures } = imports;
388
+ if (appFeatures) {
389
+ const writeFileResponse = await this.#writeFile(moduleTsFile, appFeatures);
390
+ if (writeFileResponse) return true;
391
+ else return false;
392
+ }
393
+ return false;
394
+ }
395
+
396
+ async #generateStackNavigations({ appDirPath, modules, initialRouteName, unauthenticatedComponentPath }) {
397
+ const mainRoutes = path.join(appDirPath, `/${mainRoutesFileName}`);
398
+ const stackDirPath = path.join(appDirPath, `/${stacksDirPath}`);
399
+ const layoutSettings = this.#layoutSettings;
400
+ const layoutConfigFileData = await this.#getLayoutConfig();
401
+ const layoutType = layoutSettings?.layout || 'bottom';
402
+ const layoutRouteConfig = layoutConfigFileData[layoutType];
403
+ const layoutRouteKey = Object.keys(layoutRouteConfig)[1];
404
+ const appLayout = layoutRouteConfig[layoutRouteKey];
405
+ const modulesRouteConfig = await this.#getModulesRouteConfig({ modules: modules });
406
+ const mainRouteConfig = await this.#readJsonFile(mainRoutes);
407
+ const allRoutes = [...[mainRouteConfig ?? []], ...(modulesRouteConfig ?? [])];
408
+
409
+ const { replacedNavigationRouteConfig: routeConfig } = getReplacedRouteConfig({
410
+ layoutType: layoutType,
411
+ routeConfig: allRoutes,
412
+ layoutConfigData: layoutConfigFileData,
413
+ initialRouteName: initialRouteName,
414
+ });
415
+ const keyToReplace = appLayout.key || 'bottom_tab';
416
+ const stackRouteConfig =
417
+ routeConfig
418
+ ?.map(
419
+ (rArray) =>
420
+ rArray
421
+ ?.map((r) => {
422
+ const route = r[Object.keys(r)[0]];
423
+ const path = route.path;
424
+ const isExcluded =
425
+ path === '/' ||
426
+ path.startsWith(`/${keyToReplace}`) ||
427
+ path.startsWith(`/:orgName/${keyToReplace}`) ||
428
+ path.startsWith('/host_tab') ||
429
+ path.startsWith('/:orgName/host_tab');
430
+ return isExcluded ? false : route;
431
+ })
432
+ ?.filter((route) => route !== false) ?? [], // Filter out false values
433
+ )
434
+ ?.filter((subArray) => subArray.length > 0) ?? [];
435
+
436
+ let moduleNumber = 0;
437
+ let importStatements = '';
438
+ let moduleContent = '';
439
+ let moduleRender = '';
440
+ let stackNavigator = '';
441
+ let customHeaderNames = '';
442
+ let customHeaderPaths = '';
443
+ let customUnauthenticatedComponentPaths = '';
444
+ let customUnauthenticatedComponentNames = '';
445
+ const regex = /\.(tsx|ts|jsx|js)$/i;
446
+
447
+ importStatements += `import * as React from 'react';\n`;
448
+ importStatements += `import {AuthWrapper} from '@admin-layout/gluestack-ui-mobile';\n`;
449
+
450
+ if (unauthenticatedComponentPath)
451
+ importStatements += `import UnauthenticatedComponent from '${unauthenticatedComponentPath}';\n`;
452
+
453
+ for (const pkg of stackRouteConfig) {
454
+ moduleContent += `<Stack.Group>`;
455
+ for (const pkgRouteConfig of pkg) {
456
+ moduleNumber++;
457
+ let customHeaderName = null;
458
+ const customHeaderComponentPath = await this.#resolveImportPath(
459
+ pkgRouteConfig,
460
+ pkgRouteConfig?.customHeader?.component,
461
+ );
462
+ if (
463
+ pkgRouteConfig?.customHeader &&
464
+ Object.keys(pkgRouteConfig?.customHeader)?.length &&
465
+ customHeaderComponentPath
466
+ ) {
467
+ customHeaderPaths += `${customHeaderComponentPath},`;
468
+ customHeaderName =
469
+ `${pkgRouteConfig?.customHeader?.name ?? `CustomHeader${moduleNumber}`}` ||
470
+ `CustomHeader${moduleNumber}`;
471
+ customHeaderNames += `${customHeaderName},`;
472
+ }
473
+
474
+ let customUnauthenticatedComponentName = null;
475
+ const customUnauthenticatedComponentPath = await this.#resolveImportPath(
476
+ pkgRouteConfig,
477
+ pkgRouteConfig?.unauthenticatedComponent,
478
+ );
479
+ if (pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath) {
480
+ customUnauthenticatedComponentPaths += `${customUnauthenticatedComponentPath},`;
481
+ customUnauthenticatedComponentName = `UnauthenticatedComponent${moduleNumber}`;
482
+ customUnauthenticatedComponentNames += `${customUnauthenticatedComponentName},`;
483
+ }
484
+
485
+ importStatements += `import Component${moduleNumber} from '${pkgRouteConfig.componentPath}';\n`;
486
+ const options = JSON.stringify({ ...(pkgRouteConfig?.props?.options || {}) });
487
+ moduleContent += `<Stack.Screen
488
+ key="${pkgRouteConfig.key}"
489
+ name="${pkgRouteConfig.name}"
490
+ initialParams={${JSON.stringify(pkgRouteConfig?.props?.initialParams || {})}}
491
+ options={{...${options},...{${
492
+ pkgRouteConfig?.customHeader &&
493
+ Object.keys(pkgRouteConfig.customHeader)?.length &&
494
+ customHeaderComponentPath
495
+ ? `header: (props: any) => <${customHeaderName} {...props} {...${JSON.stringify(
496
+ pkgRouteConfig?.customHeader?.props ?? '',
497
+ )}} />`
498
+ : ''
499
+ }}}}
500
+ >{(props:any) => <AuthWrapper
501
+ auth={${pkgRouteConfig?.props?.initialParams?.auth ?? false}}
502
+ component={<Component${moduleNumber} {...props} />}
503
+ ${
504
+ pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath
505
+ ? `unauthenticatedComponent={<${customUnauthenticatedComponentName}/>}`
506
+ : unauthenticatedComponentPath
507
+ ? 'unauthenticatedComponent={<UnauthenticatedComponent/>}'
508
+ : ''
509
+ }
510
+ ${
511
+ pkgRouteConfig?.withLifeCycle
512
+ ? `withLifeCycle={${JSON.stringify(pkgRouteConfig?.withLifeCycle)}}`
513
+ : ''
514
+ }
515
+ ${
516
+ pkgRouteConfig?.withInteraction
517
+ ? `withInteraction={${JSON.stringify(pkgRouteConfig?.withInteraction)}}`
518
+ : ''
519
+ }
520
+ ${
521
+ pkgRouteConfig?.withLifeCycleInteraction
522
+ ? `withLifeCycleInteraction={${JSON.stringify(
523
+ pkgRouteConfig?.withLifeCycleInteraction,
524
+ )}}`
525
+ : ''
526
+ }
527
+ />}</Stack.Screen>`;
528
+ }
529
+ if (customHeaderPaths && customHeaderPaths?.length) {
530
+ const uniqueHeaderNames = [...new Set(customHeaderNames.split(','))]?.filter((str) => str?.length);
531
+ const uniqueHeaderPaths = [...new Set(customHeaderPaths.split(','))]?.filter((str) => str?.length);
532
+ uniqueHeaderPaths?.forEach(function (hPath, i) {
533
+ const impHeaderName = uniqueHeaderNames?.[i] ?? `customHeader${i}`;
534
+ importStatements += `import ${impHeaderName} from '${hPath}';\n`;
535
+ });
536
+ }
537
+
538
+ if (customUnauthenticatedComponentPaths && customUnauthenticatedComponentPaths?.length) {
539
+ const uniqueUnauthenticatedComponentNames = [
540
+ ...new Set(customUnauthenticatedComponentNames.split(',')),
541
+ ]?.filter((str) => str?.length);
542
+ const uniqueUnauthenticatedComponentPaths = [
543
+ ...new Set(customUnauthenticatedComponentPaths.split(',')),
544
+ ]?.filter((str) => str?.length);
545
+ uniqueUnauthenticatedComponentPaths?.forEach(function (unCompPath, i) {
546
+ const impUnauthenticatedName =
547
+ uniqueUnauthenticatedComponentNames?.[i] ?? `UnauthenticatedComponent${i}`;
548
+ importStatements += `import ${impUnauthenticatedName} from '${unCompPath}';\n`;
549
+ });
550
+ }
551
+
552
+ moduleContent += `</Stack.Group>`;
553
+ moduleRender = `export default ({Stack,...rest}:any) => { return (<>${moduleContent}</>)}`;
554
+ }
555
+ stackNavigator = importStatements + '\n' + moduleRender;
556
+ if (stackNavigator) {
557
+ let stackNavigation = stackNavigator;
558
+ stackNavigation = prettier.format(stackNavigation, { parser: 'babel' });
559
+ const stackDirName = path.dirname(stackDirPath);
560
+ const isDirCreated = await this.#makeDir(stackDirName);
561
+ if (isDirCreated) {
562
+ const writeFileResponse = await this.#writeFile(stackDirPath, stackNavigation);
563
+ if (writeFileResponse) return true;
564
+ else return false;
565
+ } else return false;
566
+ } else return false;
567
+ }
568
+
569
+ async #generateDrawerNavigationsFile({ drawerConfig, unauthenticatedComponentPath, drawerDirPath }) {
570
+ let moduleNumber = 0;
571
+ let importStatements = '';
572
+ let moduleContent = '';
573
+ let moduleRender = '';
574
+ let moduleNavigation = '';
575
+ let icons = '';
576
+ let customHeaderNames = '';
577
+ let customHeaderPaths = '';
578
+ let customUnauthenticatedComponentPaths = '';
579
+ let customUnauthenticatedComponentNames = '';
580
+ const regex = /\.(tsx|ts|jsx|js)$/i;
581
+
582
+ importStatements += `import * as React from 'react';\n`;
583
+ importStatements += `import {AuthWrapper} from '@admin-layout/gluestack-ui-mobile';\n`;
584
+
585
+ if (unauthenticatedComponentPath)
586
+ importStatements += `import UnauthenticatedComponent from '${unauthenticatedComponentPath}';\n`;
587
+
588
+ for (const pkgRouteConfig of drawerConfig) {
589
+ moduleNumber++;
590
+ if (pkgRouteConfig?.icon && Object.keys(pkgRouteConfig.icon)?.length && pkgRouteConfig?.icon?.name) {
591
+ icons += `${pkgRouteConfig?.icon?.name},`;
592
+ }
593
+ let customHeaderName = null;
594
+ const customHeaderComponentPath = await this.#resolveImportPath(
595
+ pkgRouteConfig,
596
+ pkgRouteConfig?.customHeader?.component,
597
+ );
598
+ if (
599
+ pkgRouteConfig?.customHeader &&
600
+ Object.keys(pkgRouteConfig?.customHeader)?.length &&
601
+ customHeaderComponentPath
602
+ ) {
603
+ customHeaderPaths += `${customHeaderComponentPath},`;
604
+ customHeaderName =
605
+ `${pkgRouteConfig?.customHeader?.name ?? `CustomHeader${moduleNumber}`}` ||
606
+ `CustomHeader${moduleNumber}`;
607
+ customHeaderNames += `${customHeaderName},`;
608
+ }
609
+ let customUnauthenticatedComponentName = null;
610
+ const customUnauthenticatedComponentPath = await this.#resolveImportPath(
611
+ pkgRouteConfig,
612
+ pkgRouteConfig?.unauthenticatedComponent,
613
+ );
614
+ if (pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath) {
615
+ customUnauthenticatedComponentPaths += `${customUnauthenticatedComponentPath},`;
616
+ customUnauthenticatedComponentName = `UnauthenticatedComponent${moduleNumber}`;
617
+ customUnauthenticatedComponentNames += `${customUnauthenticatedComponentName},`;
618
+ }
619
+
620
+ const options = JSON.stringify({ ...(pkgRouteConfig?.props?.options || {}) });
621
+ importStatements += `import Component${moduleNumber} from '${pkgRouteConfig.componentPath}';\n`;
622
+ moduleContent += `<Drawer.Screen
623
+ key="${pkgRouteConfig.key}"
624
+ name="${pkgRouteConfig.name}"
625
+ //component={Component${moduleNumber}}
626
+ initialParams={${JSON.stringify(pkgRouteConfig?.props?.initialParams || {})}}
627
+ options={{...${options},...{${
628
+ pkgRouteConfig?.icon && Object.keys(pkgRouteConfig.icon)?.length && pkgRouteConfig?.icon?.name
629
+ ? `drawerIcon: ({ color, size }: { color: any,size:any }) => <${pkgRouteConfig?.icon?.name} name="${
630
+ pkgRouteConfig?.icon?.props?.name ?? 'home'
631
+ }" size={${pkgRouteConfig?.icon?.props?.size ?? `size`}} color={${
632
+ pkgRouteConfig?.icon?.props?.color ?? 'color'
633
+ }} />`
634
+ : ''
635
+ }
636
+ ${
637
+ pkgRouteConfig?.customHeader &&
638
+ Object.keys(pkgRouteConfig.customHeader)?.length &&
639
+ customHeaderComponentPath
640
+ ? `header: (props: any) => <${customHeaderName} {...props} {...${JSON.stringify(
641
+ pkgRouteConfig?.customHeader?.props ?? '',
642
+ )}} />`
643
+ : ''
644
+ }}}}
645
+ >{(props:any) => <AuthWrapper
646
+ auth={${pkgRouteConfig?.props?.initialParams?.auth ?? false}}
647
+ component={<Component${moduleNumber} {...props} />}
648
+ ${
649
+ pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath
650
+ ? `unauthenticatedComponent={<${customUnauthenticatedComponentName}/>}`
651
+ : unauthenticatedComponentPath
652
+ ? 'unauthenticatedComponent={<UnauthenticatedComponent/>}'
653
+ : ''
654
+ }
655
+ ${
656
+ pkgRouteConfig?.withLifeCycle
657
+ ? `withLifeCycle={${JSON.stringify(pkgRouteConfig?.withLifeCycle)}}`
658
+ : ''
659
+ }
660
+ ${
661
+ pkgRouteConfig?.withInteraction
662
+ ? `withInteraction={${JSON.stringify(pkgRouteConfig?.withInteraction)}}`
663
+ : ''
664
+ }
665
+ ${
666
+ pkgRouteConfig?.withLifeCycleInteraction
667
+ ? `withLifeCycleInteraction={${JSON.stringify(
668
+ pkgRouteConfig?.withLifeCycleInteraction,
669
+ )}}`
670
+ : ''
671
+ }
672
+ />}</Drawer.Screen>`;
673
+ }
674
+ if (icons && icons?.length) {
675
+ const uniqueIcons = [...new Set(icons.split(','))].join(',');
676
+ importStatements += `import { ${uniqueIcons} } from '@expo/vector-icons';\n`;
677
+ }
678
+
679
+ if (customHeaderPaths && customHeaderPaths?.length) {
680
+ const uniqueHeaderNames = [...new Set(customHeaderNames.split(','))]?.filter((str) => str?.length);
681
+ const uniqueHeaderPaths = [...new Set(customHeaderPaths.split(','))]?.filter((str) => str?.length);
682
+ uniqueHeaderPaths?.forEach(function (hPath, i) {
683
+ const impHeaderName = uniqueHeaderNames?.[i] ?? `customHeader${i}`;
684
+ importStatements += `import ${impHeaderName} from '${hPath}';\n`;
685
+ });
686
+ }
687
+
688
+ if (customUnauthenticatedComponentPaths && customUnauthenticatedComponentPaths?.length) {
689
+ const uniqueUnauthenticatedComponentNames = [
690
+ ...new Set(customUnauthenticatedComponentNames.split(',')),
691
+ ]?.filter((str) => str?.length);
692
+ const uniqueUnauthenticatedComponentPaths = [
693
+ ...new Set(customUnauthenticatedComponentPaths.split(',')),
694
+ ]?.filter((str) => str?.length);
695
+ uniqueUnauthenticatedComponentPaths?.forEach(function (unCompPath, i) {
696
+ const impUnauthenticatedName =
697
+ uniqueUnauthenticatedComponentNames?.[i] ?? `UnauthenticatedComponent${i}`;
698
+ importStatements += `import ${impUnauthenticatedName} from '${unCompPath}';\n`;
699
+ });
700
+ }
701
+
702
+ moduleRender = `export default ({Drawer,...rest}:any) => { return (<>${moduleContent}</>)}`;
703
+ moduleNavigation = importStatements + '\n' + moduleRender;
704
+
705
+ const drawerNavigator = moduleNavigation;
706
+ if (drawerNavigator) {
707
+ let drawerNavigation = drawerNavigator;
708
+ drawerNavigation = prettier.format(drawerNavigation, { parser: 'babel' });
709
+ const drawerDirName = path.dirname(drawerDirPath);
710
+ const isDirCreated = await this.#makeDir(drawerDirName);
711
+ if (isDirCreated) {
712
+ const writeFileResponse = await this.#writeFile(drawerDirPath, drawerNavigation);
713
+ if (writeFileResponse) return true;
714
+ else return false;
715
+ } else return false;
716
+ }
717
+ return false;
718
+ }
719
+
720
+ async #generateDrawerNavigations({ appDirPath, modules, initialRouteName, unauthenticatedComponentPath }) {
721
+ const mainRoutes = path.join(appDirPath, `/${mainRoutesFileName}`);
722
+ const drawerDirPath = path.join(appDirPath, `/${drawerFilePath}`);
723
+ const hostDirPath = path.join(appDirPath, `/${hostDrawerFilePath}`);
724
+
725
+ const layoutSettings = this.#layoutSettings;
726
+ const layoutConfigFileData = await this.#getLayoutConfig();
727
+ const layoutType = 'side';
728
+ const layoutRouteConfig = layoutConfigFileData[layoutType];
729
+ const layoutRouteKey = Object.keys(layoutRouteConfig)[1];
730
+ const appLayout = layoutRouteConfig[layoutRouteKey];
731
+ const hostRouteConfig = layoutConfigFileData['host-bottom'];
732
+ const hostRouteKey = Object.keys(hostRouteConfig)[1];
733
+ const hostLayout = hostRouteConfig[hostRouteKey];
734
+ const modulesRouteConfig = await this.#getModulesRouteConfig({ modules: modules });
735
+ const mainRouteConfig = await this.#readJsonFile(mainRoutes);
736
+ const allRoutes = [...[mainRouteConfig ?? []], ...(modulesRouteConfig ?? [])];
737
+ const { replacedNavigationRouteConfig: routeConfig } = getReplacedRouteConfig({
738
+ layoutType: layoutType,
739
+ routeConfig: allRoutes,
740
+ layoutConfigData: layoutConfigFileData,
741
+ initialRouteName: initialRouteName,
742
+ });
743
+ const keyToReplace = appLayout.key || 'bottom_tab';
744
+ const keyToReplaceHost = hostLayout.key || 'host_tab';
745
+ const moduleRouteConfigObject = Object.assign({}, ...(routeConfig?.flat(1) ?? []));
746
+ const configuredRoutes = await getSortedNavigations('/', moduleRouteConfigObject);
747
+ const layoutBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplace);
748
+ const hostBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplaceHost);
749
+
750
+ const drawerConfig =
751
+ layoutBottomTabRouteConfig?.[0]?.children?.sort((a, b) => {
752
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
753
+ if (b?.props?.options?.priority === undefined) return -1;
754
+ return a?.props?.options?.priority - b?.props?.options?.priority;
755
+ }) ?? [];
756
+ const hostDrawerConfig =
757
+ hostBottomTabRouteConfig?.[0]?.children?.sort((a, b) => {
758
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
759
+ if (b?.props?.options?.priority === undefined) return -1;
760
+ return a?.props?.options?.priority - b?.props?.options?.priority;
761
+ }) ?? [];
762
+
763
+ if (layoutSettings?.layout == 'side') {
764
+ if (drawerConfig) {
765
+ const drawerNavigation = await this.#generateDrawerNavigationsFile({
766
+ drawerConfig: drawerConfig,
767
+ unauthenticatedComponentPath,
768
+ drawerDirPath: drawerDirPath,
769
+ });
770
+ if (drawerNavigation)
771
+ await this.#generateDrawerNavigationsFile({
772
+ drawerConfig: hostDrawerConfig,
773
+ unauthenticatedComponentPath,
774
+ drawerDirPath: hostDirPath,
775
+ });
776
+ return true;
777
+ } else {
778
+ if (hostDrawerConfig) {
779
+ const isDrawerGenerated = await this.#generateDrawerNavigationsFile({
780
+ drawerConfig: hostDrawerConfig,
781
+ unauthenticatedComponentPath,
782
+ drawerDirPath: hostDirPath,
783
+ });
784
+ return isDrawerGenerated;
785
+ } else return false;
786
+ }
787
+ } else return false;
788
+ }
789
+
790
+ async #generateBottomTabNavigationsFile({
791
+ bottomTabConfig,
792
+ unauthenticatedComponentPath,
793
+ bottomDirPath,
794
+ mixLayout = null,
795
+ }) {
796
+ let moduleNumber = 0;
797
+ let importStatements = '';
798
+ let moduleContent = '';
799
+ let moduleRender = '';
800
+ let moduleNavigation = '';
801
+ let icons = '';
802
+ let customHeaderNames = '';
803
+ let customHeaderPaths = '';
804
+ let customUnauthenticatedComponentPaths = '';
805
+ let customUnauthenticatedComponentNames = '';
806
+
807
+ const regex = /\.(tsx|ts|jsx|js)$/i;
808
+
809
+ importStatements += `import * as React from 'react';\n`;
810
+ importStatements += `import {AuthWrapper} from '@admin-layout/gluestack-ui-mobile';\n`;
811
+
812
+ if (unauthenticatedComponentPath)
813
+ importStatements += `import UnauthenticatedComponent from '${unauthenticatedComponentPath}';\n`;
814
+
815
+ for (const pkgRouteConfig of bottomTabConfig) {
816
+ moduleNumber++;
817
+ if (pkgRouteConfig?.icon && Object.keys(pkgRouteConfig.icon)?.length && pkgRouteConfig?.icon?.name) {
818
+ icons += `${pkgRouteConfig?.icon?.name},`;
819
+ }
820
+
821
+ let customHeaderName = null;
822
+ const customHeaderComponentPath = await this.#resolveImportPath(
823
+ pkgRouteConfig,
824
+ pkgRouteConfig?.customHeader?.component,
825
+ );
826
+ if (
827
+ pkgRouteConfig?.customHeader &&
828
+ Object.keys(pkgRouteConfig?.customHeader)?.length &&
829
+ customHeaderComponentPath
830
+ ) {
831
+ customHeaderPaths += `${customHeaderComponentPath},`;
832
+ customHeaderName =
833
+ `${pkgRouteConfig?.customHeader?.name ?? `CustomHeader${moduleNumber}`}` ||
834
+ `CustomHeader${moduleNumber}`;
835
+ customHeaderNames += `${customHeaderName},`;
836
+ }
837
+
838
+ let customUnauthenticatedComponentName = null;
839
+ const customUnauthenticatedComponentPath = await this.#resolveImportPath(
840
+ pkgRouteConfig,
841
+ pkgRouteConfig?.unauthenticatedComponent,
842
+ );
843
+ if (pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath) {
844
+ customUnauthenticatedComponentPaths += `${customUnauthenticatedComponentPath},`;
845
+ customUnauthenticatedComponentName = `UnauthenticatedComponent${moduleNumber}`;
846
+ customUnauthenticatedComponentNames += `${customUnauthenticatedComponentName},`;
847
+ }
848
+
849
+ const options = JSON.stringify(
850
+ {
851
+ ...pkgRouteConfig?.props?.options,
852
+ headerShown: mixLayout ? false : pkgRouteConfig?.props?.options?.headerShown,
853
+ } || { headerShown: mixLayout ? false : true },
854
+ );
855
+ importStatements += `import Component${moduleNumber} from '${pkgRouteConfig.componentPath}';\n`;
856
+ moduleContent += `<Tab.Screen
857
+ key="${pkgRouteConfig.key}"
858
+ name="${pkgRouteConfig.name}"
859
+ //component={Component${moduleNumber}}
860
+ initialParams={${JSON.stringify(pkgRouteConfig?.props?.initialParams || {})}}
861
+ options={{...${options},...{${
862
+ pkgRouteConfig?.icon && Object.keys(pkgRouteConfig.icon)?.length && pkgRouteConfig?.icon?.name
863
+ ? `tabBarIcon: ({ color }: { color: any }) => <${pkgRouteConfig?.icon?.name} name="${
864
+ pkgRouteConfig?.icon?.props?.name ?? 'home'
865
+ }" size={${pkgRouteConfig?.icon?.props?.size ?? 24}} color={${
866
+ pkgRouteConfig?.icon?.props?.color ?? 'color'
867
+ }} />`
868
+ : ''
869
+ }
870
+ ${
871
+ pkgRouteConfig?.customHeader &&
872
+ Object.keys(pkgRouteConfig.customHeader)?.length &&
873
+ customHeaderComponentPath
874
+ ? `,header: (props: any) => <${customHeaderName} {...props} {...${JSON.stringify(
875
+ pkgRouteConfig?.customHeader?.props ?? '',
876
+ )}} />`
877
+ : ''
878
+ }
879
+ }}}
880
+ >{(props:any) => <AuthWrapper
881
+ auth={${pkgRouteConfig?.props?.initialParams?.auth ?? false}}
882
+ component={<Component${moduleNumber} {...props} />}
883
+ ${
884
+ pkgRouteConfig?.unauthenticatedComponent && customUnauthenticatedComponentPath
885
+ ? `unauthenticatedComponent={<${customUnauthenticatedComponentName}/>}`
886
+ : unauthenticatedComponentPath
887
+ ? 'unauthenticatedComponent={<UnauthenticatedComponent/>}'
888
+ : ''
889
+ }
890
+ ${
891
+ pkgRouteConfig?.withLifeCycle
892
+ ? `withLifeCycle={${JSON.stringify(pkgRouteConfig?.withLifeCycle)}}`
893
+ : ''
894
+ }
895
+ ${
896
+ pkgRouteConfig?.withInteraction
897
+ ? `withInteraction={${JSON.stringify(pkgRouteConfig?.withInteraction)}}`
898
+ : ''
899
+ }
900
+ ${
901
+ pkgRouteConfig?.withLifeCycleInteraction
902
+ ? `withLifeCycleInteraction={${JSON.stringify(
903
+ pkgRouteConfig?.withLifeCycleInteraction,
904
+ )}}`
905
+ : ''
906
+ }
907
+ />}
908
+ </Tab.Screen>`;
909
+ }
910
+
911
+ if (icons && icons?.length) {
912
+ const uniqueIcons = [...new Set(icons.split(','))].join(',');
913
+ importStatements += `import { ${uniqueIcons} } from '@expo/vector-icons';\n`;
914
+ }
915
+
916
+ if (customHeaderPaths && customHeaderPaths?.length) {
917
+ const uniqueHeaderNames = [...new Set(customHeaderNames.split(','))]?.filter((str) => str?.length);
918
+ const uniqueHeaderPaths = [...new Set(customHeaderPaths.split(','))]?.filter((str) => str?.length);
919
+ uniqueHeaderPaths?.forEach(function (hPath, i) {
920
+ const impHeaderName = uniqueHeaderNames?.[i] ?? `customHeader${i}`;
921
+ importStatements += `import ${impHeaderName} from '${hPath}';\n`;
922
+ });
923
+ }
924
+
925
+ if (customUnauthenticatedComponentPaths && customUnauthenticatedComponentPaths?.length) {
926
+ const uniqueUnauthenticatedComponentNames = [
927
+ ...new Set(customUnauthenticatedComponentNames.split(',')),
928
+ ]?.filter((str) => str?.length);
929
+ const uniqueUnauthenticatedComponentPaths = [
930
+ ...new Set(customUnauthenticatedComponentPaths.split(',')),
931
+ ]?.filter((str) => str?.length);
932
+ uniqueUnauthenticatedComponentPaths?.forEach(function (unCompPath, i) {
933
+ const impUnauthenticatedName =
934
+ uniqueUnauthenticatedComponentNames?.[i] ?? `UnauthenticatedComponent${i}`;
935
+ importStatements += `import ${impUnauthenticatedName} from '${unCompPath}';\n`;
936
+ });
937
+ }
938
+
939
+ moduleRender = `export default ({Tab,...rest}:any) => { return (<>${moduleContent}</>)}`;
940
+ moduleNavigation = importStatements + '\n' + moduleRender;
941
+ const bottomTabNavigator = moduleNavigation;
942
+ if (bottomTabNavigator) {
943
+ let bottomTabNavigation = bottomTabNavigator;
944
+ bottomTabNavigation = prettier.format(bottomTabNavigation, { parser: 'babel' });
945
+ const bottomDirName = path.dirname(bottomDirPath);
946
+ const isDirCreated = await this.#makeDir(bottomDirName);
947
+ if (isDirCreated) {
948
+ const writeFileResponse = await this.#writeFile(bottomDirPath, bottomTabNavigation);
949
+ if (writeFileResponse) return true;
950
+ else return false;
951
+ } else return false;
952
+ }
953
+ return false;
954
+ }
955
+
956
+ async #generateBottomTabNavigations({ appDirPath, modules, initialRouteName, unauthenticatedComponentPath }) {
957
+ const mainRoutes = path.join(appDirPath, `/${mainRoutesFileName}`);
958
+ const bottomDirPath = path.join(appDirPath, `/${bottomFilePath}`);
959
+ const hostBottomDirPath = path.join(appDirPath, `/${hostBottomFilePath}`);
960
+
961
+ const layoutSettings = this.#layoutSettings;
962
+ const layoutConfigFileData = await this.#getLayoutConfig();
963
+ const layoutType = layoutSettings?.layout ?? 'bottom';
964
+ const layoutRouteConfig = layoutConfigFileData[layoutType];
965
+ const layoutRouteKey = Object.keys(layoutRouteConfig)[1];
966
+ const appLayout = layoutRouteConfig[layoutRouteKey];
967
+ const hostRouteConfig = layoutConfigFileData['host-bottom'];
968
+ const hostRouteKey = Object.keys(hostRouteConfig)[1];
969
+ const hostLayout = hostRouteConfig[hostRouteKey];
970
+ const mixLayoutRouteKey = Object.keys(layoutRouteConfig)?.[2] || null;
971
+ const mixLayout = mixLayoutRouteKey ? layoutRouteConfig[mixLayoutRouteKey] : null;
972
+ const modulesRouteConfig = await this.#getModulesRouteConfig({ modules: modules });
973
+ const mainRouteConfig = await this.#readJsonFile(mainRoutes);
974
+ const allRoutes = [...[mainRouteConfig ?? []], ...(modulesRouteConfig ?? [])];
975
+
976
+ const { replacedNavigationRouteConfig: routeConfig } = getReplacedRouteConfig({
977
+ layoutType: layoutType,
978
+ routeConfig: allRoutes,
979
+ layoutConfigData: layoutConfigFileData,
980
+ initialRouteName: initialRouteName,
981
+ });
982
+ const keyToReplace = appLayout.key || 'bottom_tab';
983
+ const keyToReplaceHost = hostLayout.key || 'host_tab';
984
+ const moduleRouteConfigObject = Object.assign({}, ...(routeConfig?.flat(1) ?? []));
985
+ const configuredRoutes = await getSortedNavigations('/', moduleRouteConfigObject);
986
+ const layoutBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplace);
987
+ const hostBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplaceHost);
988
+
989
+ const bottomTabConfig =
990
+ layoutBottomTabRouteConfig?.[0]?.children?.sort((a, b) => {
991
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
992
+ if (b?.props?.options?.priority === undefined) return -1;
993
+ return a?.props?.options?.priority - b?.props?.options?.priority;
994
+ }) ?? [];
995
+ const hostBottomTabConfig =
996
+ hostBottomTabRouteConfig?.[0]?.children?.sort((a, b) => {
997
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
998
+ if (b?.props?.options?.priority === undefined) return -1;
999
+ return a?.props?.options?.priority - b?.props?.options?.priority;
1000
+ }) ?? [];
1001
+ if (layoutType == 'bottom' || layoutType == 'mixSide') {
1002
+ if (bottomTabConfig) {
1003
+ const drawerNavigation = await this.#generateBottomTabNavigationsFile({
1004
+ bottomTabConfig: bottomTabConfig,
1005
+ unauthenticatedComponentPath,
1006
+ bottomDirPath: bottomDirPath,
1007
+ mixLayout,
1008
+ });
1009
+ if (drawerNavigation)
1010
+ await this.#generateBottomTabNavigationsFile({
1011
+ bottomTabConfig: hostBottomTabConfig,
1012
+ unauthenticatedComponentPath,
1013
+ bottomDirPath: hostBottomDirPath,
1014
+ mixLayout,
1015
+ });
1016
+ return true;
1017
+ } else {
1018
+ if (hostBottomTabConfig) {
1019
+ const isHostGenerated = await this.#generateBottomTabNavigationsFile({
1020
+ bottomTabConfig: hostBottomTabConfig,
1021
+ unauthenticatedComponentPath,
1022
+ bottomDirPath: hostBottomDirPath,
1023
+ mixLayout,
1024
+ });
1025
+ return isHostGenerated;
1026
+ } else return false;
1027
+ }
1028
+ } else return false;
1029
+ }
1030
+
1031
+ async #generateBottomTabDrawerNavigations({ appDirPath, modules, initialRouteName, unauthenticatedComponentPath }) {
1032
+ const mainRoutes = path.join(appDirPath, `/${mainRoutesFileName}`);
1033
+ const bottomDirPath = path.join(appDirPath, `/${bottomFilePath}`);
1034
+ const hostBottomDirPath = path.join(appDirPath, `/${hostBottomFilePath}`);
1035
+ const drawerDirPath = path.join(appDirPath, `/${drawerFilePath}`);
1036
+ const hostDirPath = path.join(appDirPath, `/${hostDrawerFilePath}`);
1037
+
1038
+ const layoutSettings = this.#layoutSettings;
1039
+ const layoutConfigFileData = await this.#getLayoutConfig();
1040
+ const layoutType = layoutSettings?.layout ?? 'bottom';
1041
+ const layoutRouteConfig = layoutConfigFileData[layoutType];
1042
+ const layoutRouteKey = Object.keys(layoutRouteConfig)[1];
1043
+ const appLayout = layoutRouteConfig[layoutRouteKey];
1044
+ const hostRouteConfig = layoutConfigFileData['host-bottom'];
1045
+ const hostRouteKey = Object.keys(hostRouteConfig)[1];
1046
+ const hostLayout = hostRouteConfig[hostRouteKey];
1047
+ const mixLayoutRouteKey = Object.keys(layoutRouteConfig)?.[2] || null;
1048
+ const mixLayout = mixLayoutRouteKey ? layoutRouteConfig[mixLayoutRouteKey] : null;
1049
+ const modulesRouteConfig = await this.#getModulesRouteConfig({ modules: modules });
1050
+ const mainRouteConfig = await this.#readJsonFile(mainRoutes);
1051
+ const allRoutes = [...[mainRouteConfig ?? []], ...(modulesRouteConfig ?? [])];
1052
+
1053
+ const { replacedNavigationRouteConfig: routeConfig } = getReplacedRouteConfig({
1054
+ layoutType: layoutType,
1055
+ routeConfig: allRoutes,
1056
+ layoutConfigData: layoutConfigFileData,
1057
+ initialRouteName: initialRouteName,
1058
+ });
1059
+ const keyToReplace = appLayout.key || 'bottom_tab';
1060
+ const keyToReplaceHost = hostLayout.key || 'host_tab';
1061
+ const moduleRouteConfigObject = Object.assign({}, ...(routeConfig?.flat(1) ?? []));
1062
+ const configuredRoutes = await getSortedNavigations('/', moduleRouteConfigObject);
1063
+ const layoutBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplace);
1064
+ const hostBottomTabRouteConfig = configuredRoutes?.[0]?.children?.filter((r) => r?.key == keyToReplaceHost);
1065
+
1066
+ const bottomTabConfig =
1067
+ layoutBottomTabRouteConfig?.[0]?.children
1068
+ ?.filter((r) => !r?.side)
1069
+ ?.sort((a, b) => {
1070
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
1071
+ if (b?.props?.options?.priority === undefined) return -1;
1072
+ return a?.props?.options?.priority - b?.props?.options?.priority;
1073
+ }) ?? [];
1074
+ const hostBottomTabConfig =
1075
+ hostBottomTabRouteConfig?.[0]?.children
1076
+ ?.filter((r) => !r?.side)
1077
+ ?.sort((a, b) => {
1078
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
1079
+ if (b?.props?.options?.priority === undefined) return -1;
1080
+ return a?.props?.options?.priority - b?.props?.options?.priority;
1081
+ }) ?? [];
1082
+
1083
+ const drawerConfig =
1084
+ layoutBottomTabRouteConfig?.[0]?.children
1085
+ ?.filter((r) => r?.side)
1086
+ ?.sort((a, b) => {
1087
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
1088
+ if (b?.props?.options?.priority === undefined) return -1;
1089
+ return a?.props?.options?.priority - b?.props?.options?.priority;
1090
+ }) ?? [];
1091
+ const hostDrawerConfig =
1092
+ hostBottomTabRouteConfig?.[0]?.children
1093
+ ?.filter((r) => r?.side)
1094
+ ?.sort((a, b) => {
1095
+ if (a?.props?.options?.priority === undefined) return 1; // Push items with missing 'id' to the end
1096
+ if (b?.props?.options?.priority === undefined) return -1;
1097
+ return a?.props?.options?.priority - b?.props?.options?.priority;
1098
+ }) ?? [];
1099
+
1100
+ if (bottomTabConfig) {
1101
+ const drawerNavigation = await this.#generateBottomTabNavigationsFile({
1102
+ bottomTabConfig: bottomTabConfig,
1103
+ unauthenticatedComponentPath,
1104
+ bottomDirPath: bottomDirPath,
1105
+ mixLayout,
1106
+ });
1107
+ if (drawerNavigation)
1108
+ await this.#generateBottomTabNavigationsFile({
1109
+ bottomTabConfig: hostBottomTabConfig,
1110
+ unauthenticatedComponentPath,
1111
+ bottomDirPath: hostBottomDirPath,
1112
+ mixLayout,
1113
+ });
1114
+ if (drawerConfig) {
1115
+ const drawerNavigation = await this.#generateDrawerNavigationsFile({
1116
+ drawerConfig: drawerConfig,
1117
+ unauthenticatedComponentPath,
1118
+ drawerDirPath: drawerDirPath,
1119
+ });
1120
+ if (drawerNavigation)
1121
+ await this.#generateDrawerNavigationsFile({
1122
+ drawerConfig: hostDrawerConfig,
1123
+ unauthenticatedComponentPath,
1124
+ drawerDirPath: hostDirPath,
1125
+ });
1126
+ return true;
1127
+ } else return true;
1128
+ } else return false;
1129
+ }
1130
+
1131
+ async #generateAppNavigationFile({ appDirPath, customTabBarPath, customDrawerPath, customHeaderPath }) {
1132
+ const navigationDirPath = path.join(appDirPath, `/${appNavigationFileName}`);
1133
+ const layoutSettings = this.#layoutSettings;
1134
+ const layoutConfigFileData = await this.#getLayoutConfig();
1135
+ const layoutType = layoutSettings?.layout || 'bottom';
1136
+ const layoutRouteConfig = layoutConfigFileData[layoutType];
1137
+ const layoutRouteKey = Object.keys(layoutRouteConfig)[1];
1138
+ const appLayout = layoutRouteConfig[layoutRouteKey];
1139
+
1140
+ const initialRouteName =
1141
+ layoutType === 'mixSide'
1142
+ ? appLayout?.[appLayout?.key]?.props?.initialRouteName ?? 'MainStack.Layout.Home'
1143
+ : appLayout?.props?.initialRouteName || 'MainStack.Home';
1144
+ const isShowTabs = layoutType === 'mixSide' || layoutType === 'bottom' ? true : false;
1145
+ const isShowDefalutHeader = layoutType === 'mixSide' ? true : false;
1146
+ const defaultHeaderProps = {
1147
+ showToggle: layoutSettings?.topLeftToggle || false,
1148
+ right: layoutSettings?.topRightSettingToggle || false,
1149
+ };
1150
+ const screenOptionsTab =
1151
+ layoutType === 'mixSide'
1152
+ ? { ...(appLayout?.[appLayout?.key]?.props?.screenOptions ?? {}) }
1153
+ : appLayout?.props?.screenOptions || { headerShown: true, title: 'Home', headerTitle: 'Home' };
1154
+ const screenOptions = appLayout?.props?.screenOptions || {
1155
+ headerShown: true,
1156
+ title: 'Home',
1157
+ headerTitle: 'Home',
1158
+ };
1159
+ let importStatements = `
1160
+ import * as React from 'react';
1161
+ import { navigationRef } from '@common-stack/client-react';
1162
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';`;
1163
+ let rootComponent = '';
1164
+ let appComponent = '';
1165
+ let appNavigation = '';
1166
+ if (layoutType == 'side') {
1167
+ if (customDrawerPath) importStatements += `import CustomDrawerContent from '${customDrawerPath}';\n`;
1168
+
1169
+ if (customHeaderPath) importStatements += `import CustomHeader from '${customHeaderPath}';\n`;
1170
+
1171
+ importStatements += `import { createDrawerNavigator } from '@react-navigation/drawer';
1172
+ import { getHeaderTitle } from '@react-navigation/elements';
1173
+ import { useSelector } from 'react-redux';
1174
+ import stackNavigations from './stack';
1175
+ import drawerNavigations from './drawer';
1176
+ import hostDrawerNavigations from './host_drawer';
1177
+ const Stack = createNativeStackNavigator();
1178
+ const Drawer = createDrawerNavigator();
1179
+ `;
1180
+ rootComponent += `
1181
+ const RootComponent = (props:any) => {
1182
+ const settings = useSelector((state: any) => state.settings);
1183
+ const defaultHeaderProps = ${JSON.stringify(defaultHeaderProps || {})};
1184
+ return (
1185
+ <Drawer.Navigator
1186
+ initialRouteName={${JSON.stringify(initialRouteName)}}
1187
+ //screenOptions={${JSON.stringify(screenOptions)}}
1188
+ screenOptions={({ route }) => ({ ...${JSON.stringify(screenOptions)} ,...{
1189
+ ${
1190
+ customHeaderPath
1191
+ ? `header: (props:any) => {
1192
+ const title = getHeaderTitle(props.options, props.route.name);
1193
+ return <CustomHeader {...defaultHeaderProps} {...props} title={title} style={props.options.headerStyle} />;
1194
+ }`
1195
+ : ''
1196
+ }
1197
+ }})}
1198
+ ${customDrawerPath ? 'drawerContent={(props:any) => <CustomDrawerContent {...props} />}' : ''}
1199
+ >
1200
+ {settings?.layout == 'host-bottom' ? hostDrawerNavigations({ Drawer }) : drawerNavigations({ Drawer })}
1201
+ </Drawer.Navigator>
1202
+ );
1203
+ }
1204
+ `;
1205
+ }
1206
+ if (layoutType == 'bottom') {
1207
+ if (customTabBarPath) importStatements += `import CustomTabBar from '${customTabBarPath}';\n`;
1208
+
1209
+ if (customHeaderPath) importStatements += `import CustomHeader from '${customHeaderPath}';\n`;
1210
+
1211
+ importStatements += `import {Header,Drawer as DefaultDrawer} from '@admin-layout/gluestack-ui-mobile';
1212
+ import { getHeaderTitle } from '@react-navigation/elements';
1213
+ import { useSelector } from 'react-redux';
1214
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
1215
+ import stackNavigations from './stack';
1216
+ import bottomNavigations from './bottom';
1217
+ import hostBottomNavigations from './host_bottom';
1218
+ const Stack = createNativeStackNavigator();
1219
+ const Tab = createBottomTabNavigator();
1220
+ `;
1221
+ rootComponent += `
1222
+ const RootComponent = (props:any) => {
1223
+ const settings = useSelector((state: any) => state.settings);
1224
+ const initialRouteName = ${JSON.stringify(initialRouteName)};
1225
+ let defaultScreenOptions = ${JSON.stringify(screenOptionsTab)};
1226
+ const defaultHeaderProps = ${JSON.stringify(defaultHeaderProps || {})};
1227
+ const defaultHeader = {${
1228
+ isShowDefalutHeader ? `header:(props:any)=><Header {...defaultHeaderProps} {...props} />` : ''
1229
+ }};
1230
+ return (
1231
+ <Tab.Navigator
1232
+ initialRouteName={initialRouteName}
1233
+ screenOptions={(props:any)=>({...props,...defaultScreenOptions,...{${
1234
+ customHeaderPath
1235
+ ? `header: (props:any) => {
1236
+ const title = getHeaderTitle(props.options, props.route.name);
1237
+ return <CustomHeader {...defaultHeaderProps} {...props} title={title} style={props.options.headerStyle} />;
1238
+ }`
1239
+ : ''
1240
+ }}})}
1241
+ ${
1242
+ customTabBarPath
1243
+ ? 'tabBar={(props:any) => <CustomTabBar key={props?.key??"customTabBarKey"} {...props} />}'
1244
+ : ''
1245
+ }
1246
+ >
1247
+ {settings?.layout == 'host-bottom' ? hostBottomNavigations({ Tab }) : bottomNavigations({Tab})}
1248
+ </Tab.Navigator>
1249
+ );
1250
+ }
1251
+ `;
1252
+ }
1253
+ if (layoutType == 'mixSide') {
1254
+ if (customTabBarPath) importStatements += `import CustomTabBar from '${customTabBarPath}';\n`;
1255
+
1256
+ if (customDrawerPath) importStatements += `import CustomDrawerContent from '${customDrawerPath}';\n`;
1257
+
1258
+ if (customHeaderPath) importStatements += `import CustomHeader from '${customHeaderPath}';\n`;
1259
+
1260
+ importStatements += `import {Header,Drawer as DefaultDrawer} from '@admin-layout/gluestack-ui-mobile';
1261
+ import { useSelector } from 'react-redux';
1262
+ import { MaterialIcons } from "@expo/vector-icons";
1263
+ import { getHeaderTitle } from '@react-navigation/elements';
1264
+ import { createDrawerNavigator } from '@react-navigation/drawer';
1265
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
1266
+ import stackNavigations from './stack';
1267
+ import bottomNavigations from './bottom';
1268
+ import hostBottomNavigations from './host_bottom';
1269
+ import drawerNavigations from './drawer';
1270
+ import hostDrawerNavigations from './host_drawer';
1271
+ const Stack = createNativeStackNavigator();
1272
+ const Drawer = createDrawerNavigator();
1273
+ const Tab = createBottomTabNavigator();
1274
+ `;
1275
+ rootComponent += `
1276
+ const TabNavigator = () => {
1277
+ const settings = useSelector((state: any) => state.settings);
1278
+ const initialRouteName = ${JSON.stringify(initialRouteName)};
1279
+ let defaultScreenOptions = ${JSON.stringify(screenOptionsTab)};
1280
+ const defaultHeaderProps = ${JSON.stringify(defaultHeaderProps || {})};
1281
+ const defaultHeader = {${
1282
+ isShowDefalutHeader ? `header:(props:any)=><Header {...defaultHeaderProps} {...props} />` : ''
1283
+ }};
1284
+
1285
+ return (
1286
+ <Tab.Navigator
1287
+ initialRouteName={initialRouteName}
1288
+ screenOptions={(props:any)=>({...props,...defaultScreenOptions,...{headerShown: false,header:()=>null},})}
1289
+ ${
1290
+ customTabBarPath
1291
+ ? 'tabBar={(props:any) => <CustomTabBar key={props?.key??"customTabBarKey"} {...props} />}'
1292
+ : ''
1293
+ }
1294
+ >
1295
+ {settings?.layout == 'host-bottom' ? hostBottomNavigations({ Tab }) : bottomNavigations({Tab})}
1296
+ </Tab.Navigator>
1297
+ )
1298
+ }
1299
+ const RootComponent = (props:any) => {
1300
+ const initialRouteName = ${JSON.stringify(initialRouteName)};
1301
+ const settings = useSelector((state: any) => state.settings);
1302
+ let defaultScreenOptions = ${JSON.stringify(screenOptionsTab)};
1303
+ const defaultHeaderProps = ${JSON.stringify(defaultHeaderProps || {})};
1304
+ const defaultHeader = {${
1305
+ isShowDefalutHeader ? `header:(props:any)=><Header {...defaultHeaderProps} {...props} />` : ''
1306
+ }};
1307
+ return (
1308
+ <Drawer.Navigator
1309
+ initialRouteName={${JSON.stringify(initialRouteName)}}
1310
+ // screenOptions={${JSON.stringify(screenOptions)}}
1311
+ screenOptions={({ route }) => ({ ...${JSON.stringify(
1312
+ screenOptions,
1313
+ )} ,...{headerTitle:navigationRef?.isReady() && navigationRef?.getCurrentRoute()
1314
+ ? navigationRef?.getCurrentOptions()?.title
1315
+ ? navigationRef?.getCurrentOptions()?.headerTitle
1316
+ : navigationRef?.getCurrentRoute()?.route?.name
1317
+ : "Home",
1318
+ ${
1319
+ customHeaderPath
1320
+ ? `header: (props:any) => {
1321
+ const title = getHeaderTitle(props.options, props.route.name);
1322
+ return <CustomHeader {...defaultHeaderProps} {...props} title={title} isMixedLayout={true} style={props.options.headerStyle} />;
1323
+ }`
1324
+ : ''
1325
+ }
1326
+
1327
+ }})}
1328
+ ${
1329
+ customDrawerPath
1330
+ ? 'drawerContent={((props:any)) => <CustomDrawerContent {...props} showDefaultRoutes={true} />}'
1331
+ : ''
1332
+ }
1333
+ >
1334
+ <Drawer.Screen name="Layout" options={{title:"Home", drawerIcon: ({ color, size }: { color: any, size: any }) => (
1335
+ <MaterialIcons name="home" size={24} color={color} />
1336
+ ),}}
1337
+ component={TabNavigator} />
1338
+ {settings?.layout == 'host-bottom' ? hostDrawerNavigations({ Drawer }) : drawerNavigations({ Drawer })}
1339
+ </Drawer.Navigator>
1340
+ );
1341
+ }
1342
+ `;
1343
+ }
1344
+ appComponent += `
1345
+ const AppNavigations = () => {
1346
+ return (
1347
+ <Stack.Navigator initialRouteName="${initialRouteName}">
1348
+ <Stack.Screen
1349
+ name="MainStack"
1350
+ options={{ headerShown: false }}
1351
+ component={RootComponent}
1352
+ />
1353
+ {stackNavigations({ Stack })}
1354
+ </Stack.Navigator>
1355
+ )
1356
+ }
1357
+
1358
+ export default AppNavigations;
1359
+ `;
1360
+ appNavigation = importStatements + '\n' + rootComponent + '\n' + appComponent;
1361
+ appNavigation = prettier.format(appNavigation, { parser: 'babel' });
1362
+ const writeFileResponse = await this.#writeFile(navigationDirPath, appNavigation);
1363
+ if (writeFileResponse) return true;
1364
+ else return false;
1365
+ }
1366
+
1367
+ async #setLayoutAndGenerateNavigation() {
1368
+ const appDirPath = this.#appDirPath;
1369
+ const modules = this.#modules;
1370
+ const initialRouteName = this.#initialRouteName;
1371
+ const unauthenticatedComponentPath = this.#unauthenticatedComponentPath;
1372
+ const customTabBarPath = this.#customTabBarPath;
1373
+ const customDrawerPath = this.#customDrawerPath;
1374
+ const customHeaderPath = this.#customHeaderPath;
1375
+
1376
+ const layoutSettings = this.#layoutSettings;
1377
+ const layoutConfigFileData = await this.#getLayoutConfig();
1378
+ const layoutType = layoutSettings?.layout || 'bottom';
1379
+ const isAppRoutesGenerated = await this.#generateAppRoutesJson({ appDirPath });
1380
+ if (isAppRoutesGenerated) {
1381
+ const isModuleTsFileGenerated = await this.#generateModulesTsFile({
1382
+ appDirPath,
1383
+ modules,
1384
+ initialRouteName,
1385
+ });
1386
+ if (isModuleTsFileGenerated) {
1387
+ const isStackCreated = await this.#generateStackNavigations({
1388
+ appDirPath,
1389
+ modules,
1390
+ initialRouteName,
1391
+ unauthenticatedComponentPath,
1392
+ });
1393
+ if (isStackCreated) {
1394
+ if (layoutType == 'side') {
1395
+ const isDrawerGenerated = await this.#generateDrawerNavigations({
1396
+ appDirPath,
1397
+ modules,
1398
+ initialRouteName,
1399
+ unauthenticatedComponentPath,
1400
+ });
1401
+ if (isDrawerGenerated) {
1402
+ const appNavigationGenerated = await this.#generateAppNavigationFile({
1403
+ appDirPath,
1404
+ customTabBarPath,
1405
+ customDrawerPath,
1406
+ customHeaderPath,
1407
+ });
1408
+ if (appNavigationGenerated) return appNavigationGenerated;
1409
+ else return true;
1410
+ } else return false;
1411
+ } else if (layoutType == 'bottom' || layoutType == 'host-bottom') {
1412
+ const isBottomTabGenerated = await this.#generateBottomTabNavigations({
1413
+ appDirPath,
1414
+ modules,
1415
+ initialRouteName,
1416
+ unauthenticatedComponentPath,
1417
+ });
1418
+ if (isBottomTabGenerated) {
1419
+ const appNavigationGenerated = await this.#generateAppNavigationFile({
1420
+ appDirPath,
1421
+ customTabBarPath,
1422
+ customDrawerPath,
1423
+ customHeaderPath,
1424
+ });
1425
+ if (appNavigationGenerated) return appNavigationGenerated;
1426
+ else return true;
1427
+ } else return false;
1428
+ } else {
1429
+ const isBottomTabDrawerGenerated = await this.#generateBottomTabDrawerNavigations({
1430
+ appDirPath,
1431
+ modules,
1432
+ initialRouteName,
1433
+ unauthenticatedComponentPath,
1434
+ });
1435
+ if (isBottomTabDrawerGenerated) {
1436
+ const appNavigationGenerated = await this.#generateAppNavigationFile({
1437
+ appDirPath,
1438
+ customTabBarPath,
1439
+ customDrawerPath,
1440
+ customHeaderPath,
1441
+ });
1442
+ if (appNavigationGenerated) return appNavigationGenerated;
1443
+ else return true;
1444
+ } else return false;
1445
+ }
1446
+ } else return false;
1447
+ } else return false;
1448
+ } else return false;
1449
+ }
1450
+
1451
+ async generateAppNavigations() {
1452
+ return this.#setLayoutAndGenerateNavigation();
1453
+ }
1454
+ }