@arcpkg/initiator 0.0.1-beta.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +524 -0
- package/config.ts +153 -0
- package/index.ts +23 -0
- package/intl.ts +366 -0
- package/js/config.js +110 -0
- package/js/index.js +17 -0
- package/js/intl.js +287 -0
- package/js/native-routing.js +696 -0
- package/js/provider.js +303 -0
- package/js/routing.js +533 -0
- package/native-routing.ts +871 -0
- package/package.json +21 -0
- package/provider.ts +403 -0
- package/routing.ts +691 -0
- package/tsconfig.json +27 -0
package/js/routing.js
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { Pajo } from '@arcpkg/pajo';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
export class RouteGenerator {
|
|
8
|
+
config;
|
|
9
|
+
routeFiles = [];
|
|
10
|
+
modules = new Set();
|
|
11
|
+
modulePathOverrides = new Map();
|
|
12
|
+
constructor(config = {}, dirname = __dirname) {
|
|
13
|
+
this.config = {
|
|
14
|
+
srcDir: config.srcDir || Pajo.join(dirname, 'src') || '',
|
|
15
|
+
modulesDir: config.modulesDir || Pajo.join(dirname, 'src\\modules') || '',
|
|
16
|
+
pagesDir: config.pagesDir || Pajo.join(dirname, 'src\\pages') || '',
|
|
17
|
+
outputFile: config.outputFile || Pajo.join(dirname, 'src\\auto-routes.tsx') || '',
|
|
18
|
+
layoutFileName: config.layoutFileName || '_layout',
|
|
19
|
+
errorFileName: config.errorFileName || '_error',
|
|
20
|
+
notFoundFileName: config.notFoundFileName || '_404'
|
|
21
|
+
};
|
|
22
|
+
console.log(`--> RouteGenerator config:: `, this.config);
|
|
23
|
+
}
|
|
24
|
+
async generate() {
|
|
25
|
+
console.log('🔍 Scanning for route files...');
|
|
26
|
+
await this.loadModulePathOverrides();
|
|
27
|
+
await this.findRouteFiles();
|
|
28
|
+
await this.generateAutoRoutesFile();
|
|
29
|
+
console.log(`✅ Generated ${this.config.outputFile} with ${this.routeFiles.length} routes`);
|
|
30
|
+
console.log(`📦 Found modules with routes: ${Array.from(this.modules).join(', ')}`);
|
|
31
|
+
console.log(`🔄 Module path overrides: ${Array.from(this.modulePathOverrides.entries()).map(([k, v]) => `${k} -> ${v}`).join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
async loadModulePathOverrides() {
|
|
34
|
+
try {
|
|
35
|
+
if (!fs.existsSync(this.config.modulesDir)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const moduleDirs = fs.readdirSync(this.config.modulesDir, { withFileTypes: true })
|
|
39
|
+
.filter(dirent => dirent.isDirectory())
|
|
40
|
+
.map(dirent => dirent.name);
|
|
41
|
+
for (const moduleName of moduleDirs) {
|
|
42
|
+
const configPath = path.join(this.config.modulesDir, moduleName, 'config.json');
|
|
43
|
+
if (fs.existsSync(configPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
46
|
+
const config = JSON.parse(configContent);
|
|
47
|
+
if (config.path && typeof config.path === 'string') {
|
|
48
|
+
let modulePath = config.path.trim();
|
|
49
|
+
if (modulePath.startsWith('/')) {
|
|
50
|
+
modulePath = modulePath.substring(1);
|
|
51
|
+
}
|
|
52
|
+
if (modulePath.endsWith('/')) {
|
|
53
|
+
modulePath = modulePath.slice(0, -1);
|
|
54
|
+
}
|
|
55
|
+
if (modulePath) {
|
|
56
|
+
this.modulePathOverrides.set(moduleName, modulePath);
|
|
57
|
+
console.log(`🔄 Module ${moduleName} path override: ${modulePath}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`Error reading config.json for module ${moduleName}:`, error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error('Error loading module path overrides:', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async findRouteFiles() {
|
|
72
|
+
this.routeFiles = [];
|
|
73
|
+
await this.scanDirectoryForRoutes(this.config.pagesDir);
|
|
74
|
+
if (fs.existsSync(this.config.modulesDir)) {
|
|
75
|
+
const moduleDirs = fs.readdirSync(this.config.modulesDir, { withFileTypes: true })
|
|
76
|
+
.filter(dirent => dirent.isDirectory())
|
|
77
|
+
.map(dirent => dirent.name);
|
|
78
|
+
for (const moduleName of moduleDirs) {
|
|
79
|
+
const modulePagesDir = path.join(this.config.modulesDir, moduleName, 'pages');
|
|
80
|
+
if (fs.existsSync(modulePagesDir)) {
|
|
81
|
+
this.modules.add(moduleName);
|
|
82
|
+
await this.scanDirectoryForRoutes(modulePagesDir, moduleName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async scanDirectoryForRoutes(dir, moduleName) {
|
|
88
|
+
try {
|
|
89
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
90
|
+
for (const item of items) {
|
|
91
|
+
const fullPath = path.join(dir, item.name);
|
|
92
|
+
if (item.name === 'node_modules' ||
|
|
93
|
+
item.name === 'dist' ||
|
|
94
|
+
item.name === 'build' ||
|
|
95
|
+
item.name.startsWith('.')) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (item.isDirectory()) {
|
|
99
|
+
await this.scanDirectoryForRoutes(fullPath, moduleName);
|
|
100
|
+
}
|
|
101
|
+
else if (item.isFile()) {
|
|
102
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
103
|
+
if (['.tsx', '.jsx'].includes(ext)) {
|
|
104
|
+
const fileName = path.basename(item.name, ext);
|
|
105
|
+
if (fileName === this.config.layoutFileName ||
|
|
106
|
+
fileName === this.config.errorFileName ||
|
|
107
|
+
fileName === this.config.notFoundFileName) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
await this.processRouteFile(fullPath, moduleName);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error(`Error scanning directory ${dir}:`, error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async processRouteFile(filePath, moduleName) {
|
|
120
|
+
try {
|
|
121
|
+
let routePath = this.convertFilePathToRoutePath(filePath, moduleName);
|
|
122
|
+
const componentName = this.extractComponentName(filePath, moduleName);
|
|
123
|
+
const layoutComponent = this.findLayoutComponent(filePath, moduleName);
|
|
124
|
+
const errorComponent = this.findErrorComponent(filePath, moduleName);
|
|
125
|
+
const notFoundComponent = this.findNotFoundComponent(filePath, moduleName);
|
|
126
|
+
const routeFile = {
|
|
127
|
+
filePath,
|
|
128
|
+
relativePath: path.relative(this.config.srcDir, filePath),
|
|
129
|
+
routePath,
|
|
130
|
+
componentName,
|
|
131
|
+
layoutComponent,
|
|
132
|
+
errorComponent,
|
|
133
|
+
notFoundComponent,
|
|
134
|
+
moduleName
|
|
135
|
+
};
|
|
136
|
+
this.routeFiles.push(routeFile);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error(`Error processing route file ${filePath}:`, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
convertFilePathToRoutePath(filePath, moduleName) {
|
|
143
|
+
let routePath = filePath;
|
|
144
|
+
routePath = routePath.replace(this.config.srcDir, '');
|
|
145
|
+
if (moduleName) {
|
|
146
|
+
const modulePagesPrefix = `modules${path.sep}${moduleName}${path.sep}pages`;
|
|
147
|
+
if (routePath.includes(modulePagesPrefix)) {
|
|
148
|
+
routePath = routePath.substring(routePath.indexOf(modulePagesPrefix) + modulePagesPrefix.length);
|
|
149
|
+
let modulePrefix = `/${moduleName}`;
|
|
150
|
+
if (this.modulePathOverrides.has(moduleName)) {
|
|
151
|
+
const overridePath = this.modulePathOverrides.get(moduleName);
|
|
152
|
+
if (overridePath) {
|
|
153
|
+
modulePrefix = `/${overridePath}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
routePath = modulePrefix + routePath;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const pagesPrefix = 'pages';
|
|
161
|
+
if (routePath.includes(pagesPrefix)) {
|
|
162
|
+
routePath = routePath.substring(routePath.indexOf(pagesPrefix) + pagesPrefix.length);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
routePath = routePath.replace(/\.(tsx|jsx)$/, '');
|
|
166
|
+
routePath = routePath.replace(/\\/g, '/');
|
|
167
|
+
if (routePath.endsWith('/index')) {
|
|
168
|
+
routePath = routePath.replace(/\/index$/, '');
|
|
169
|
+
}
|
|
170
|
+
routePath = routePath.replace(/\[([^\]]+)\]/g, '{$1}');
|
|
171
|
+
if (!routePath.startsWith('/')) {
|
|
172
|
+
routePath = '/' + routePath;
|
|
173
|
+
}
|
|
174
|
+
if (routePath === '/' || routePath === '//') {
|
|
175
|
+
return '/';
|
|
176
|
+
}
|
|
177
|
+
return routePath;
|
|
178
|
+
}
|
|
179
|
+
extractComponentName(filePath, moduleName) {
|
|
180
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
181
|
+
const dirName = path.dirname(filePath);
|
|
182
|
+
const parts = dirName.split(path.sep);
|
|
183
|
+
let startIndex = 0;
|
|
184
|
+
if (moduleName) {
|
|
185
|
+
const modulePagesPath = path.sep + 'modules' + path.sep + moduleName + path.sep + 'pages';
|
|
186
|
+
startIndex = parts.findIndex((part, index, arr) => {
|
|
187
|
+
const pathSoFar = arr.slice(0, index + 1).join(path.sep);
|
|
188
|
+
return pathSoFar.endsWith(modulePagesPath);
|
|
189
|
+
}) + 1;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const pagesPath = path.sep + 'pages';
|
|
193
|
+
startIndex = parts.findIndex((part, index, arr) => {
|
|
194
|
+
const pathSoFar = arr.slice(0, index + 1).join(path.sep);
|
|
195
|
+
return pathSoFar.endsWith(pagesPath);
|
|
196
|
+
}) + 1;
|
|
197
|
+
}
|
|
198
|
+
if (startIndex < 0)
|
|
199
|
+
startIndex = 0;
|
|
200
|
+
const relevantParts = parts.slice(startIndex);
|
|
201
|
+
let componentName = moduleName ? this.toPascalCase(moduleName) : '';
|
|
202
|
+
relevantParts.forEach(part => {
|
|
203
|
+
if (part && part !== 'pages') {
|
|
204
|
+
componentName += this.toPascalCase(part);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
let fileNamePart = fileName;
|
|
208
|
+
if (fileName.startsWith('{') && fileName.endsWith('}')) {
|
|
209
|
+
const paramName = fileName.substring(1, fileName.length - 1);
|
|
210
|
+
fileNamePart = this.toPascalCase(paramName) + 'Params';
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
fileNamePart = this.toPascalCase(fileName);
|
|
214
|
+
}
|
|
215
|
+
if (!fileNamePart.endsWith('Page')) {
|
|
216
|
+
fileNamePart += 'Page';
|
|
217
|
+
}
|
|
218
|
+
const reservedNames = ['Layout', 'ErrorBoundary', 'NotFound'];
|
|
219
|
+
if (reservedNames.includes(fileNamePart)) {
|
|
220
|
+
fileNamePart += 'Component';
|
|
221
|
+
}
|
|
222
|
+
return componentName + fileNamePart;
|
|
223
|
+
}
|
|
224
|
+
toPascalCase(str) {
|
|
225
|
+
if (!str)
|
|
226
|
+
return '';
|
|
227
|
+
return str
|
|
228
|
+
.split(/[-_]/)
|
|
229
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
230
|
+
.join('');
|
|
231
|
+
}
|
|
232
|
+
findLayoutComponent(filePath, moduleName) {
|
|
233
|
+
const dir = path.dirname(filePath);
|
|
234
|
+
let currentDir = dir;
|
|
235
|
+
while (currentDir !== this.config.srcDir && currentDir !== path.dirname(this.config.srcDir)) {
|
|
236
|
+
const layoutPath = path.join(currentDir, `${this.config.layoutFileName}.tsx`);
|
|
237
|
+
if (fs.existsSync(layoutPath)) {
|
|
238
|
+
return this.generateComponentName(layoutPath, moduleName, 'Layout');
|
|
239
|
+
}
|
|
240
|
+
if (moduleName) {
|
|
241
|
+
const modulePath = path.join(this.config.modulesDir, moduleName);
|
|
242
|
+
if (currentDir === modulePath) {
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
currentDir = path.dirname(currentDir);
|
|
247
|
+
}
|
|
248
|
+
const globalLayoutPath = path.join(this.config.pagesDir, `${this.config.layoutFileName}.tsx`);
|
|
249
|
+
if (fs.existsSync(globalLayoutPath)) {
|
|
250
|
+
return 'Layout';
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
findErrorComponent(filePath, moduleName) {
|
|
255
|
+
const dir = path.dirname(filePath);
|
|
256
|
+
let currentDir = dir;
|
|
257
|
+
while (currentDir !== this.config.srcDir && currentDir !== path.dirname(this.config.srcDir)) {
|
|
258
|
+
const errorPath = path.join(currentDir, `${this.config.errorFileName}.tsx`);
|
|
259
|
+
if (fs.existsSync(errorPath)) {
|
|
260
|
+
return this.generateComponentName(errorPath, moduleName, 'ErrorBoundary');
|
|
261
|
+
}
|
|
262
|
+
if (moduleName) {
|
|
263
|
+
const modulePath = path.join(this.config.modulesDir, moduleName);
|
|
264
|
+
if (currentDir === modulePath) {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
currentDir = path.dirname(currentDir);
|
|
269
|
+
}
|
|
270
|
+
const globalErrorPath = path.join(this.config.pagesDir, `${this.config.errorFileName}.tsx`);
|
|
271
|
+
if (fs.existsSync(globalErrorPath)) {
|
|
272
|
+
return 'ErrorBoundary';
|
|
273
|
+
}
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
findNotFoundComponent(filePath, moduleName) {
|
|
277
|
+
if (!moduleName)
|
|
278
|
+
return undefined;
|
|
279
|
+
const modulePagesDir = path.join(this.config.modulesDir, moduleName, 'pages');
|
|
280
|
+
const notFoundPath = path.join(modulePagesDir, `${this.config.notFoundFileName}.tsx`);
|
|
281
|
+
if (fs.existsSync(notFoundPath)) {
|
|
282
|
+
return this.generateComponentName(notFoundPath, moduleName, 'NotFound');
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
generateComponentName(filePath, moduleName, suffix) {
|
|
287
|
+
if (!moduleName) {
|
|
288
|
+
return suffix;
|
|
289
|
+
}
|
|
290
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
291
|
+
const dirName = path.dirname(filePath);
|
|
292
|
+
const modulePagesPath = path.join(this.config.modulesDir, moduleName, 'pages');
|
|
293
|
+
const relativePath = path.relative(modulePagesPath, dirName);
|
|
294
|
+
let componentName = this.toPascalCase(moduleName);
|
|
295
|
+
if (relativePath && relativePath !== '.') {
|
|
296
|
+
const dirParts = relativePath.split(path.sep).filter(part => part && part !== '.');
|
|
297
|
+
dirParts.forEach(part => {
|
|
298
|
+
componentName += this.toPascalCase(part);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (fileName !== this.config.layoutFileName &&
|
|
302
|
+
fileName !== this.config.errorFileName &&
|
|
303
|
+
fileName !== this.config.notFoundFileName) {
|
|
304
|
+
componentName += this.toPascalCase(fileName);
|
|
305
|
+
}
|
|
306
|
+
return componentName + suffix;
|
|
307
|
+
}
|
|
308
|
+
async generateAutoRoutesFile() {
|
|
309
|
+
const outputDir = path.dirname(this.config.outputFile);
|
|
310
|
+
if (!fs.existsSync(outputDir)) {
|
|
311
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
312
|
+
}
|
|
313
|
+
const content = this.generateFileContent();
|
|
314
|
+
fs.writeFileSync(this.config.outputFile, content, 'utf-8');
|
|
315
|
+
}
|
|
316
|
+
getRelativeImportPath(targetPath) {
|
|
317
|
+
const outputDir = path.dirname(this.config.outputFile);
|
|
318
|
+
const relative = path.relative(outputDir, targetPath);
|
|
319
|
+
let importPath = relative.replace(/\\/g, '/');
|
|
320
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
|
|
321
|
+
importPath = './' + importPath;
|
|
322
|
+
}
|
|
323
|
+
importPath = importPath.replace(/\.(tsx|jsx)$/, '');
|
|
324
|
+
return importPath;
|
|
325
|
+
}
|
|
326
|
+
generateFileContent() {
|
|
327
|
+
const components = new Set();
|
|
328
|
+
const imports = [
|
|
329
|
+
`import { lazy } from 'react';`,
|
|
330
|
+
`import type { RouteObject } from 'react-router-dom';`,
|
|
331
|
+
``
|
|
332
|
+
];
|
|
333
|
+
const globalLayoutPath = path.join(this.config.pagesDir, `${this.config.layoutFileName}.tsx`);
|
|
334
|
+
const globalErrorPath = path.join(this.config.pagesDir, `${this.config.errorFileName}.tsx`);
|
|
335
|
+
const global404Path = path.join(this.config.pagesDir, `${this.config.notFoundFileName}.tsx`);
|
|
336
|
+
if (fs.existsSync(globalLayoutPath)) {
|
|
337
|
+
const importPath = this.getRelativeImportPath(globalLayoutPath);
|
|
338
|
+
imports.push(`const Layout = lazy(() => import('${importPath}'));`);
|
|
339
|
+
components.add('Layout');
|
|
340
|
+
}
|
|
341
|
+
if (fs.existsSync(globalErrorPath)) {
|
|
342
|
+
const importPath = this.getRelativeImportPath(globalErrorPath);
|
|
343
|
+
imports.push(`const ErrorBoundary = lazy(() => import('${importPath}'));`);
|
|
344
|
+
components.add('ErrorBoundary');
|
|
345
|
+
}
|
|
346
|
+
if (fs.existsSync(global404Path)) {
|
|
347
|
+
const importPath = this.getRelativeImportPath(global404Path);
|
|
348
|
+
imports.push(`const NotFound = lazy(() => import('${importPath}'));`);
|
|
349
|
+
components.add('NotFound');
|
|
350
|
+
}
|
|
351
|
+
imports.push(``);
|
|
352
|
+
const specialComponents = new Map();
|
|
353
|
+
const findSpecialFiles = (baseDir, isModule = false, moduleName) => {
|
|
354
|
+
if (!fs.existsSync(baseDir))
|
|
355
|
+
return;
|
|
356
|
+
const walkDir = (dir) => {
|
|
357
|
+
try {
|
|
358
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
359
|
+
for (const item of items) {
|
|
360
|
+
const fullPath = path.join(dir, item.name);
|
|
361
|
+
if (item.isDirectory()) {
|
|
362
|
+
walkDir(fullPath);
|
|
363
|
+
}
|
|
364
|
+
else if (item.isFile()) {
|
|
365
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
366
|
+
if (['.tsx', '.jsx'].includes(ext)) {
|
|
367
|
+
const fileName = path.basename(item.name, ext);
|
|
368
|
+
if (fileName === this.config.layoutFileName ||
|
|
369
|
+
fileName === this.config.errorFileName ||
|
|
370
|
+
fileName === this.config.notFoundFileName) {
|
|
371
|
+
const componentName = this.generateComponentName(fullPath, moduleName, fileName === this.config.layoutFileName ? 'Layout' :
|
|
372
|
+
fileName === this.config.errorFileName ? 'ErrorBoundary' : 'NotFound');
|
|
373
|
+
if (!components.has(componentName)) {
|
|
374
|
+
specialComponents.set(fullPath, componentName);
|
|
375
|
+
components.add(componentName);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
console.error(`Error walking directory ${dir}:`, error);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
walkDir(baseDir);
|
|
387
|
+
};
|
|
388
|
+
findSpecialFiles(this.config.pagesDir, false);
|
|
389
|
+
this.modules.forEach(moduleName => {
|
|
390
|
+
const modulePagesDir = path.join(this.config.modulesDir, moduleName, 'pages');
|
|
391
|
+
if (fs.existsSync(modulePagesDir)) {
|
|
392
|
+
findSpecialFiles(modulePagesDir, true, moduleName);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
specialComponents.forEach((componentName, filePath) => {
|
|
396
|
+
const importPath = this.getRelativeImportPath(filePath);
|
|
397
|
+
imports.push(`const ${componentName} = lazy(() => import('${importPath}'));`);
|
|
398
|
+
});
|
|
399
|
+
if (specialComponents.size > 0) {
|
|
400
|
+
imports.push(``);
|
|
401
|
+
}
|
|
402
|
+
this.routeFiles.forEach(route => {
|
|
403
|
+
const importPath = this.getRelativeImportPath(route.filePath);
|
|
404
|
+
imports.push(`const ${route.componentName} = lazy(() => import('${importPath}'));`);
|
|
405
|
+
components.add(route.componentName);
|
|
406
|
+
});
|
|
407
|
+
imports.push(``);
|
|
408
|
+
const routes = ['export const routes: RouteObject[] = ['];
|
|
409
|
+
const sortedRoutes = this.routeFiles.sort((a, b) => {
|
|
410
|
+
if (a.routePath === '/')
|
|
411
|
+
return -1;
|
|
412
|
+
if (b.routePath === '/')
|
|
413
|
+
return 1;
|
|
414
|
+
if (!a.moduleName && b.moduleName)
|
|
415
|
+
return -1;
|
|
416
|
+
if (a.moduleName && !b.moduleName)
|
|
417
|
+
return 1;
|
|
418
|
+
if (a.moduleName === b.moduleName) {
|
|
419
|
+
return a.routePath.localeCompare(b.routePath);
|
|
420
|
+
}
|
|
421
|
+
return 0;
|
|
422
|
+
});
|
|
423
|
+
let lastModuleName = undefined;
|
|
424
|
+
sortedRoutes.forEach((route, index) => {
|
|
425
|
+
if (route.moduleName !== lastModuleName) {
|
|
426
|
+
if (lastModuleName !== undefined) {
|
|
427
|
+
routes.push(``);
|
|
428
|
+
}
|
|
429
|
+
if (route.moduleName) {
|
|
430
|
+
let displayName = route.moduleName;
|
|
431
|
+
if (this.modulePathOverrides.has(route.moduleName)) {
|
|
432
|
+
const overridePath = this.modulePathOverrides.get(route.moduleName);
|
|
433
|
+
displayName = `${route.moduleName} (path: ${overridePath})`;
|
|
434
|
+
}
|
|
435
|
+
routes.push(` // ${displayName} module routes`);
|
|
436
|
+
}
|
|
437
|
+
lastModuleName = route.moduleName;
|
|
438
|
+
}
|
|
439
|
+
const routeLines = [' {'];
|
|
440
|
+
routeLines.push(` path: '${route.routePath.replace(/\{([^}]+)\}/g, ':$1')}',`);
|
|
441
|
+
let layoutComponent = route.layoutComponent;
|
|
442
|
+
if (!layoutComponent && route.moduleName) {
|
|
443
|
+
const moduleLayoutPath = path.join(this.config.modulesDir, route.moduleName, 'pages', `${this.config.layoutFileName}.tsx`);
|
|
444
|
+
if (fs.existsSync(moduleLayoutPath)) {
|
|
445
|
+
layoutComponent = this.generateComponentName(moduleLayoutPath, route.moduleName, 'Layout');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (layoutComponent) {
|
|
449
|
+
routeLines.push(` element: <${layoutComponent}><${route.componentName} /></${layoutComponent}>,`);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
routeLines.push(` element: <${route.componentName} />,`);
|
|
453
|
+
}
|
|
454
|
+
let errorComponent = route.errorComponent;
|
|
455
|
+
if (!errorComponent && route.moduleName) {
|
|
456
|
+
const moduleErrorPath = path.join(this.config.modulesDir, route.moduleName, 'pages', `${this.config.errorFileName}.tsx`);
|
|
457
|
+
if (fs.existsSync(moduleErrorPath)) {
|
|
458
|
+
errorComponent = this.generateComponentName(moduleErrorPath, route.moduleName, 'ErrorBoundary');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (errorComponent) {
|
|
462
|
+
routeLines.push(` errorElement: <${errorComponent} />`);
|
|
463
|
+
}
|
|
464
|
+
if (routeLines[routeLines.length - 1].endsWith(',')) {
|
|
465
|
+
routeLines[routeLines.length - 1] = routeLines[routeLines.length - 1].slice(0, -1);
|
|
466
|
+
}
|
|
467
|
+
routeLines.push(' }');
|
|
468
|
+
if (index < sortedRoutes.length - 1) {
|
|
469
|
+
routeLines[routeLines.length - 1] += ',';
|
|
470
|
+
}
|
|
471
|
+
routes.push(routeLines.join('\n'));
|
|
472
|
+
});
|
|
473
|
+
const modulesWith404 = new Set();
|
|
474
|
+
specialComponents.forEach((componentName, filePath) => {
|
|
475
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
476
|
+
if (fileName === this.config.notFoundFileName) {
|
|
477
|
+
const pathParts = filePath.split(path.sep);
|
|
478
|
+
const modulesIndex = pathParts.indexOf('modules');
|
|
479
|
+
if (modulesIndex !== -1 && modulesIndex + 1 < pathParts.length) {
|
|
480
|
+
const moduleName = pathParts[modulesIndex + 1];
|
|
481
|
+
if (!modulesWith404.has(moduleName)) {
|
|
482
|
+
modulesWith404.add(moduleName);
|
|
483
|
+
if (routes[routes.length - 1].endsWith(',')) {
|
|
484
|
+
routes[routes.length - 1] = routes[routes.length - 1].slice(0, -1);
|
|
485
|
+
}
|
|
486
|
+
let moduleRoutePath = `/${moduleName}`;
|
|
487
|
+
if (this.modulePathOverrides.has(moduleName)) {
|
|
488
|
+
const overridePath = this.modulePathOverrides.get(moduleName);
|
|
489
|
+
if (overridePath) {
|
|
490
|
+
moduleRoutePath = `/${overridePath}`;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
routes.push(`,`);
|
|
494
|
+
routes.push(` {`);
|
|
495
|
+
routes.push(` path: '${moduleRoutePath}/*',`.replace(/\{([^}]+)\}/g, ':$1'));
|
|
496
|
+
routes.push(` element: <${componentName} />`);
|
|
497
|
+
routes.push(` }`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
if (components.has('NotFound')) {
|
|
503
|
+
if (routes[routes.length - 1].endsWith(',')) {
|
|
504
|
+
routes[routes.length - 1] = routes[routes.length - 1].slice(0, -1);
|
|
505
|
+
}
|
|
506
|
+
routes.push(`,`);
|
|
507
|
+
routes.push(` {`);
|
|
508
|
+
routes.push(` path: '*',`.replace(/\{([^}]+)\}/g, ':$1'));
|
|
509
|
+
routes.push(` element: <NotFound />`);
|
|
510
|
+
routes.push(` }`);
|
|
511
|
+
}
|
|
512
|
+
routes.push('];');
|
|
513
|
+
routes.push('');
|
|
514
|
+
routes.push('export default routes;');
|
|
515
|
+
return [
|
|
516
|
+
...imports,
|
|
517
|
+
'',
|
|
518
|
+
...routes
|
|
519
|
+
].join('\n');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
async function RouteInitiator(dirname = __dirname) {
|
|
523
|
+
const config = {};
|
|
524
|
+
const generator = new RouteGenerator(config, dirname);
|
|
525
|
+
try {
|
|
526
|
+
await generator.generate();
|
|
527
|
+
console.log('🎉 Route generation completed successfully!');
|
|
528
|
+
}
|
|
529
|
+
catch (err) {
|
|
530
|
+
console.error('❌ Failed to generate routes: ', err);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
export default RouteInitiator;
|