@arc-js/core 0.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 +664 -0
- package/index.d.ts +10 -0
- package/index.jsx +33 -0
- package/index.min.jsx +1 -0
- package/index.min.jsx.map +1 -0
- package/minimal-config.d.ts +10 -0
- package/minimal-config.js +67 -0
- package/minimal-config.min.js +2 -0
- package/package.json +25 -0
- package/rooting.hooks.d.ts +87 -0
- package/rooting.hooks.jsx +205 -0
- package/rooting.hooks.min.jsx +1 -0
- package/rooting.hooks.min.jsx.map +1 -0
- package/routes-utils.d.ts +7 -0
- package/routes-utils.js +148 -0
- package/routes-utils.min.js +2 -0
- package/tsconfig.json +27 -0
- package/types.d.ts +31 -0
- package/types.js +1 -0
- package/types.min.js +2 -0
package/README.md
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
# @arc-js/core
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
**@arc-js/core** est un module de routage intelligent et auto-configuré pour les applications React avec TypeScript/Javascript. Il fournit un système de routage basé sur la structure de fichiers, des hooks de navigation avancés et une configuration minimale pour les applications modulaires.
|
|
9
|
+
|
|
10
|
+
## ✨ Fonctionnalités Principales
|
|
11
|
+
|
|
12
|
+
### 🗺️ Routage Auto-Généré
|
|
13
|
+
- **Génération automatique des routes** à partir de la structure du système de fichiers
|
|
14
|
+
- **Support des layouts hiérarchiques** avec héritage automatique
|
|
15
|
+
- **Pages d'erreur spécifiques** par sous-répertoire
|
|
16
|
+
- **Configuration modulaire** avec support des modules indépendants
|
|
17
|
+
|
|
18
|
+
### 🧭 Hooks de Navigation Avancés
|
|
19
|
+
- **Navigation type-safe** avec validation des paramètres
|
|
20
|
+
- **Gestion automatique des query strings** avec support multi-langue
|
|
21
|
+
- **Résolution de routes** avec paramètres dynamiques
|
|
22
|
+
- **Navigation avec rechargement** pour les mises à jour critiques
|
|
23
|
+
|
|
24
|
+
### ⚙️ Configuration Modulaire
|
|
25
|
+
- **Configuration par module** avec fichiers `config.json`
|
|
26
|
+
- **Activation/désactivation** dynamique des modules
|
|
27
|
+
- **Chemins personnalisables** par module
|
|
28
|
+
- **Détection automatique** des fichiers de pages
|
|
29
|
+
|
|
30
|
+
### 🛡️ Sécurité et Fiabilité
|
|
31
|
+
- **Validation des chemins** avec fallback sécurisé
|
|
32
|
+
- **Gestion des erreurs** avec pages d'erreur hiérarchiques
|
|
33
|
+
- **Logs détaillés** en mode développement seulement
|
|
34
|
+
- **Types TypeScript complets** pour une meilleure autocomplétion
|
|
35
|
+
|
|
36
|
+
## 📦 Installation
|
|
37
|
+
|
|
38
|
+
### Via npm/yarn/pnpm
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @arc-js/core react-router-dom react
|
|
42
|
+
# ou
|
|
43
|
+
yarn add @arc-js/core react-router-dom react
|
|
44
|
+
# ou
|
|
45
|
+
pnpm add @arc-js/core react-router-dom react
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Dépendances requises
|
|
49
|
+
- React 18+
|
|
50
|
+
- React Router DOM 6+
|
|
51
|
+
- TypeScript 5.0+ (recommandé)
|
|
52
|
+
- @arc-js/qust (pour la manipulation de query strings)
|
|
53
|
+
|
|
54
|
+
## 🚀 Démarrage Rapide
|
|
55
|
+
|
|
56
|
+
### Structure de projet recommandée
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
src/
|
|
60
|
+
├── pages/
|
|
61
|
+
│ ├── _layout.tsx # Layout racine
|
|
62
|
+
│ ├── _error.tsx # Page d'erreur racine
|
|
63
|
+
│ ├── index.tsx # Page d'accueil
|
|
64
|
+
│ ├── about/
|
|
65
|
+
│ │ ├── _layout.tsx # Layout spécifique à /about
|
|
66
|
+
│ │ ├── _error.tsx # Erreur spécifique à /about
|
|
67
|
+
│ │ ├── index.tsx # /about
|
|
68
|
+
│ │ └── team.tsx # /about/team
|
|
69
|
+
│ └── users/
|
|
70
|
+
│ ├── _layout.tsx # Layout spécifique à /users
|
|
71
|
+
│ ├── [id].tsx # /users/:id (paramètre dynamique)
|
|
72
|
+
│ └── index.tsx # /users
|
|
73
|
+
├── modules/
|
|
74
|
+
│ └── admin/
|
|
75
|
+
│ ├── config.json # Configuration du module admin
|
|
76
|
+
│ └── pages/
|
|
77
|
+
│ ├── _layout.tsx # Layout du module admin
|
|
78
|
+
│ ├── dashboard.tsx # /admin/dashboard
|
|
79
|
+
│ └── users.tsx # /admin/users
|
|
80
|
+
└── main.tsx
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Configuration de base
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// main.tsx
|
|
87
|
+
import React from 'react';
|
|
88
|
+
import ReactDOM from 'react-dom/client';
|
|
89
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
90
|
+
import getRoutes from '@arc-js/core';
|
|
91
|
+
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
|
|
92
|
+
|
|
93
|
+
const App = () => {
|
|
94
|
+
const routes = getRoutes();
|
|
95
|
+
const router = createBrowserRouter(routes);
|
|
96
|
+
|
|
97
|
+
return <RouterProvider router={router} />;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
101
|
+
<React.StrictMode>
|
|
102
|
+
<App />
|
|
103
|
+
</React.StrictMode>
|
|
104
|
+
);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Fichier config.json d'un module
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"path": "/admin",
|
|
112
|
+
"name": "Administration",
|
|
113
|
+
"description": "Module d'administration",
|
|
114
|
+
"author": "Votre Équipe",
|
|
115
|
+
"isEnabled": true
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 📚 Documentation API
|
|
120
|
+
|
|
121
|
+
### Hook useRootingActions
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { useRootingActions } from '@arc-js/core';
|
|
125
|
+
|
|
126
|
+
const MyComponent = () => {
|
|
127
|
+
const {
|
|
128
|
+
params, // Paramètres de route (ex: { id: "123" })
|
|
129
|
+
queries, // Query parameters (ex: { lang: "fr", page: "1" })
|
|
130
|
+
navigate, // Fonction navigate de react-router-dom
|
|
131
|
+
resolveRoute, // Résoudre une route avec paramètres
|
|
132
|
+
goToRoute, // Naviguer vers une route
|
|
133
|
+
goAndReloadRoute, // Naviguer avec rechargement
|
|
134
|
+
pathName, // Chemin actuel
|
|
135
|
+
urlSearch, // Query string actuelle
|
|
136
|
+
getUrlData, // Analyser une URL
|
|
137
|
+
checkIfIsCurrentRoute // Vérifier si on est sur une route
|
|
138
|
+
} = useRootingActions();
|
|
139
|
+
|
|
140
|
+
// Exemple d'utilisation
|
|
141
|
+
const handleClick = () => {
|
|
142
|
+
goToRoute({
|
|
143
|
+
path: '/users/:id',
|
|
144
|
+
params: { id: '123' },
|
|
145
|
+
queries: { lang: 'fr', view: 'details' }
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<div>
|
|
151
|
+
<button onClick={handleClick}>
|
|
152
|
+
Voir l'utilisateur 123
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Interfaces principales
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Configuration de navigation
|
|
163
|
+
interface ConfigGoToRoute {
|
|
164
|
+
path?: string;
|
|
165
|
+
params?: any;
|
|
166
|
+
queries?: any;
|
|
167
|
+
refreshPage?: boolean;
|
|
168
|
+
replace?: boolean;
|
|
169
|
+
enableLoader?: boolean;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Configuration de résolution de route
|
|
173
|
+
interface ConfigResolveRoute {
|
|
174
|
+
path?: string;
|
|
175
|
+
params?: any;
|
|
176
|
+
queries?: any;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Route générée automatiquement
|
|
180
|
+
interface RouteDefinition {
|
|
181
|
+
truePath: string; // Chemin physique du fichier
|
|
182
|
+
pathParent?: string; // Chemin parent
|
|
183
|
+
path: string; // Chemin de la route
|
|
184
|
+
component: React.ComponentType<any>;
|
|
185
|
+
layout?: React.ComponentType<{ children: ReactNode }>;
|
|
186
|
+
error?: React.ComponentType<{}>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Configuration d'un module
|
|
190
|
+
interface ConfigDatasDefinition {
|
|
191
|
+
path: string; // Chemin de base du module
|
|
192
|
+
name: string | undefined;
|
|
193
|
+
author: string | undefined;
|
|
194
|
+
isEnabled: boolean; // Activation du module
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Fonctions utilitaires
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { nativeResolveRoute } from '@arc-js/core';
|
|
202
|
+
|
|
203
|
+
// Résoudre une URL sans utiliser le hook
|
|
204
|
+
const url = nativeResolveRoute({
|
|
205
|
+
path: '/users/:id',
|
|
206
|
+
params: { id: '456' },
|
|
207
|
+
queries: { lang: 'en', tab: 'profile' }
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
console.log(url); // "/users/456?lang=en&tab=profile"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## 🔧 Utilisation Avancée
|
|
214
|
+
|
|
215
|
+
### Pages dynamiques avec paramètres
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// src/pages/users/[id].tsx
|
|
219
|
+
import { useRootingActions } from '@arc-js/core';
|
|
220
|
+
|
|
221
|
+
const UserPage = () => {
|
|
222
|
+
const { params, queries } = useRootingActions();
|
|
223
|
+
const userId = params.id;
|
|
224
|
+
const lang = queries.lang || 'fr';
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div>
|
|
228
|
+
<h1>Utilisateur {userId}</h1>
|
|
229
|
+
<p>Langue: {lang}</p>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export default UserPage;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Layouts hiérarchiques
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// src/pages/_layout.tsx (Layout racine)
|
|
241
|
+
const RootLayout = ({ children }) => (
|
|
242
|
+
<div className="app">
|
|
243
|
+
<header>Mon Application</header>
|
|
244
|
+
<main>{children}</main>
|
|
245
|
+
<footer>© 2024</footer>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// src/pages/admin/_layout.tsx (Layout admin)
|
|
250
|
+
const AdminLayout = ({ children }) => (
|
|
251
|
+
<div className="admin">
|
|
252
|
+
<nav>Menu Admin</nav>
|
|
253
|
+
<div className="admin-content">{children}</div>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Pages d'erreur spécifiques
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// src/pages/_error.tsx (Erreur globale)
|
|
262
|
+
const GlobalErrorPage = () => (
|
|
263
|
+
<div>
|
|
264
|
+
<h1>Une erreur est survenue</h1>
|
|
265
|
+
<p>Veuillez réessayer plus tard.</p>
|
|
266
|
+
</div>
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// src/pages/admin/_error.tsx (Erreur admin)
|
|
270
|
+
const AdminErrorPage = () => (
|
|
271
|
+
<div className="admin-error">
|
|
272
|
+
<h1>Erreur d'administration</h1>
|
|
273
|
+
<p>Contactez le support technique.</p>
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Configuration de modules
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// src/modules/blog/config.json
|
|
282
|
+
{
|
|
283
|
+
"path": "/blog",
|
|
284
|
+
"name": "Blog",
|
|
285
|
+
"description": "Module de blog",
|
|
286
|
+
"author": "Équipe Rédaction",
|
|
287
|
+
"isEnabled": true
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/modules/blog/pages/index.tsx sera accessible à /blog
|
|
291
|
+
// src/modules/blog/pages/[slug].tsx sera accessible à /blog/:slug
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## 🎯 Exemples Complets
|
|
295
|
+
|
|
296
|
+
### Exemple 1 : Application avec authentification
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// src/pages/_layout.tsx
|
|
300
|
+
import { useRootingActions } from '@arc-js/core';
|
|
301
|
+
import { Link } from 'react-router-dom';
|
|
302
|
+
|
|
303
|
+
const AppLayout = ({ children }) => {
|
|
304
|
+
const { checkIfIsCurrentRoute } = useRootingActions();
|
|
305
|
+
const isActive = (path: string) => checkIfIsCurrentRoute(path);
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div>
|
|
309
|
+
<nav>
|
|
310
|
+
<Link to="/" className={isActive('/') ? 'active' : ''}>
|
|
311
|
+
Accueil
|
|
312
|
+
</Link>
|
|
313
|
+
<Link to="/about" className={isActive('/about') ? 'active' : ''}>
|
|
314
|
+
À propos
|
|
315
|
+
</Link>
|
|
316
|
+
<Link to="/contact" className={isActive('/contact') ? 'active' : ''}>
|
|
317
|
+
Contact
|
|
318
|
+
</Link>
|
|
319
|
+
</nav>
|
|
320
|
+
{children}
|
|
321
|
+
</div>
|
|
322
|
+
);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/pages/protected/_layout.tsx
|
|
326
|
+
import { useEffect } from 'react';
|
|
327
|
+
import { useRootingActions } from '@arc-js/core';
|
|
328
|
+
|
|
329
|
+
const ProtectedLayout = ({ children }) => {
|
|
330
|
+
const { goToRoute } = useRootingActions();
|
|
331
|
+
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
const token = localStorage.getItem('auth_token');
|
|
334
|
+
if (!token) {
|
|
335
|
+
goToRoute({
|
|
336
|
+
path: '/login',
|
|
337
|
+
queries: { redirect: window.location.pathname }
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}, []);
|
|
341
|
+
|
|
342
|
+
return token ? children : null;
|
|
343
|
+
};
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Exemple 2 : Dashboard avec multi-langue
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// src/pages/dashboard/index.tsx
|
|
350
|
+
import { useRootingActions } from '@arc-js/core';
|
|
351
|
+
import { useState, useEffect } from 'react';
|
|
352
|
+
|
|
353
|
+
const DashboardPage = () => {
|
|
354
|
+
const { queries, goToRoute, resolveRoute } = useRootingActions();
|
|
355
|
+
const [lang, setLang] = useState(queries.lang || 'fr');
|
|
356
|
+
|
|
357
|
+
const changeLanguage = (newLang: string) => {
|
|
358
|
+
goToRoute({
|
|
359
|
+
path: '/dashboard',
|
|
360
|
+
queries: { ...queries, lang: newLang }
|
|
361
|
+
});
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const getReportUrl = (reportId: string) => {
|
|
365
|
+
return resolveRoute({
|
|
366
|
+
path: '/dashboard/reports/:id',
|
|
367
|
+
params: { id: reportId },
|
|
368
|
+
queries: { lang, format: 'pdf' }
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
return (
|
|
373
|
+
<div>
|
|
374
|
+
<div>
|
|
375
|
+
Langue:
|
|
376
|
+
<button onClick={() => changeLanguage('fr')}>FR</button>
|
|
377
|
+
<button onClick={() => changeLanguage('en')}>EN</button>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<a href={getReportUrl('monthly')}>
|
|
381
|
+
Télécharger le rapport mensuel
|
|
382
|
+
</a>
|
|
383
|
+
</div>
|
|
384
|
+
);
|
|
385
|
+
};
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Exemple 3 : Formulaire avec redirection
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// src/pages/contact/index.tsx
|
|
392
|
+
import { useState } from 'react';
|
|
393
|
+
import { useRootingActions } from '@arc-js/core';
|
|
394
|
+
|
|
395
|
+
const ContactPage = () => {
|
|
396
|
+
const { goToRoute, goAndReloadRoute } = useRootingActions();
|
|
397
|
+
const [formData, setFormData] = useState({ name: '', email: '' });
|
|
398
|
+
|
|
399
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
400
|
+
e.preventDefault();
|
|
401
|
+
|
|
402
|
+
// Simulation d'envoi
|
|
403
|
+
const success = await submitContactForm(formData);
|
|
404
|
+
|
|
405
|
+
if (success) {
|
|
406
|
+
// Redirection simple
|
|
407
|
+
goToRoute({
|
|
408
|
+
path: '/contact/success',
|
|
409
|
+
queries: { ref: 'contact-form' }
|
|
410
|
+
});
|
|
411
|
+
} else {
|
|
412
|
+
// Redirection avec rechargement pour nettoyer le cache
|
|
413
|
+
goAndReloadRoute({
|
|
414
|
+
path: '/contact',
|
|
415
|
+
queries: { error: 'submission_failed' }
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
return (
|
|
421
|
+
<form onSubmit={handleSubmit}>
|
|
422
|
+
<input
|
|
423
|
+
value={formData.name}
|
|
424
|
+
onChange={e => setFormData({...formData, name: e.target.value})}
|
|
425
|
+
placeholder="Nom"
|
|
426
|
+
/>
|
|
427
|
+
<input
|
|
428
|
+
value={formData.email}
|
|
429
|
+
onChange={e => setFormData({...formData, email: e.target.value})}
|
|
430
|
+
placeholder="Email"
|
|
431
|
+
/>
|
|
432
|
+
<button type="submit">Envoyer</button>
|
|
433
|
+
</form>
|
|
434
|
+
);
|
|
435
|
+
};
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## 🔧 Configuration Avancée
|
|
439
|
+
|
|
440
|
+
### Variables d'environnement
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
# .env
|
|
444
|
+
VITE_APP_NAME="Mon Application"
|
|
445
|
+
VITE_API_URL="http://localhost:3000"
|
|
446
|
+
|
|
447
|
+
# Activation des logs détaillés
|
|
448
|
+
NODE_ENV=development # Logs complets
|
|
449
|
+
NODE_ENV=production # Logs minimaux
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Extension de la configuration
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// custom-routes.ts
|
|
456
|
+
import { getRoutes as getBaseRoutes } from '@arc-js/core';
|
|
457
|
+
import { RouteDefinition } from '@arc-js/core/types';
|
|
458
|
+
|
|
459
|
+
export async function getRoutes() {
|
|
460
|
+
const baseRoutes = await getBaseRoutes();
|
|
461
|
+
|
|
462
|
+
// Ajouter des routes manuelles
|
|
463
|
+
const customRoutes: RouteDefinition[] = [
|
|
464
|
+
{
|
|
465
|
+
truePath: '/src/pages/custom.tsx',
|
|
466
|
+
path: '/custom-route',
|
|
467
|
+
component: () => <div>Route personnalisée</div>,
|
|
468
|
+
layout: undefined,
|
|
469
|
+
error: undefined
|
|
470
|
+
}
|
|
471
|
+
];
|
|
472
|
+
|
|
473
|
+
return [...baseRoutes, ...customRoutes];
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Personnalisation du routage
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// custom-router.tsx
|
|
481
|
+
import { useRootingActions } from '@arc-js/core';
|
|
482
|
+
import type { ConfigGoToRoute } from '@arc-js/core';
|
|
483
|
+
|
|
484
|
+
export const useCustomRooting = () => {
|
|
485
|
+
const baseActions = useRootingActions();
|
|
486
|
+
|
|
487
|
+
const goToRouteWithAnalytics = (
|
|
488
|
+
config: ConfigGoToRoute,
|
|
489
|
+
analyticsEvent?: string
|
|
490
|
+
) => {
|
|
491
|
+
// Envoyer l'événement analytics
|
|
492
|
+
if (analyticsEvent) {
|
|
493
|
+
window.gtag?.('event', analyticsEvent, {
|
|
494
|
+
path: config.path,
|
|
495
|
+
params: config.params
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Utiliser la navigation standard
|
|
500
|
+
return baseActions.goToRoute(config);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
return {
|
|
504
|
+
...baseActions,
|
|
505
|
+
goToRouteWithAnalytics
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## 🛡️ Gestion des Erreurs
|
|
511
|
+
|
|
512
|
+
### Structure d'erreur hiérarchique
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
pages/
|
|
516
|
+
├── _error.tsx # Erreur globale
|
|
517
|
+
├── admin/
|
|
518
|
+
│ ├── _error.tsx # Erreur admin (surcharge globale)
|
|
519
|
+
│ └── dashboard/
|
|
520
|
+
│ └── _error.tsx # Erreur dashboard (surcharge admin)
|
|
521
|
+
└── public/
|
|
522
|
+
└── _error.tsx # Erreur section publique
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Page d'erreur avancée
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
// src/pages/_error.tsx
|
|
529
|
+
import { useRouteError, Link } from 'react-router-dom';
|
|
530
|
+
import { useRootingActions } from '@arc-js/core';
|
|
531
|
+
|
|
532
|
+
const ErrorPage = () => {
|
|
533
|
+
const error = useRouteError();
|
|
534
|
+
const { pathName, goToRoute } = useRootingActions();
|
|
535
|
+
|
|
536
|
+
console.error('Route Error:', error);
|
|
537
|
+
|
|
538
|
+
const handleRetry = () => {
|
|
539
|
+
goToRoute({
|
|
540
|
+
path: pathName,
|
|
541
|
+
refreshPage: true // Forcer le rechargement
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
return (
|
|
546
|
+
<div className="error-container">
|
|
547
|
+
<h1>Oups ! Une erreur est survenue</h1>
|
|
548
|
+
<p>Désolé, une erreur inattendue s'est produite.</p>
|
|
549
|
+
|
|
550
|
+
<div className="error-actions">
|
|
551
|
+
<button onClick={handleRetry}>
|
|
552
|
+
Réessayer
|
|
553
|
+
</button>
|
|
554
|
+
<Link to="/">
|
|
555
|
+
Retour à l'accueil
|
|
556
|
+
</Link>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
);
|
|
560
|
+
};
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## 📋 Table des Conventions
|
|
564
|
+
|
|
565
|
+
### Fichiers spéciaux
|
|
566
|
+
|
|
567
|
+
| Fichier | Chemin de route | Description |
|
|
568
|
+
|---------|----------------|-------------|
|
|
569
|
+
| `index.tsx` | `/` (racine) ou `/dossier/` | Page d'index |
|
|
570
|
+
| `_layout.tsx` | Non accessible | Layout pour le dossier |
|
|
571
|
+
| `_error.tsx` | Non accessible | Page d'erreur pour le dossier |
|
|
572
|
+
| `_404.tsx` | `*` | Page 404 (non trouvé) |
|
|
573
|
+
| `[param].tsx` | `/:param` | Paramètre dynamique |
|
|
574
|
+
| `[...slug].tsx` | `/*` | Catch-all route |
|
|
575
|
+
|
|
576
|
+
### Paramètres de query string
|
|
577
|
+
|
|
578
|
+
| Paramètre | Type | Description |
|
|
579
|
+
|-----------|------|-------------|
|
|
580
|
+
| `lang` | `fr` \| `en` | Langue de l'application |
|
|
581
|
+
| `ref` | string | Référence pour le tracking |
|
|
582
|
+
| `modal` | string | Ouvrir un modal spécifique |
|
|
583
|
+
| `page` | number | Numéro de page (pagination) |
|
|
584
|
+
| `sort` | string | Tri des résultats |
|
|
585
|
+
|
|
586
|
+
## 🔧 Build et Développement
|
|
587
|
+
|
|
588
|
+
### Scripts recommandés
|
|
589
|
+
|
|
590
|
+
```json
|
|
591
|
+
{
|
|
592
|
+
"scripts": {
|
|
593
|
+
"dev": "vite",
|
|
594
|
+
"build": "tsc && vite build",
|
|
595
|
+
"preview": "vite preview",
|
|
596
|
+
"type-check": "tsc --noEmit",
|
|
597
|
+
"generate-routes": "node scripts/generate-routes.js"
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Configuration Vite
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
// vite.config.ts
|
|
606
|
+
import { defineConfig } from 'vite';
|
|
607
|
+
import react from '@vitejs/plugin-react';
|
|
608
|
+
|
|
609
|
+
export default defineConfig({
|
|
610
|
+
plugins: [react()],
|
|
611
|
+
resolve: {
|
|
612
|
+
alias: {
|
|
613
|
+
'@arc-js/core': '/node_modules/@arc-js/core'
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
build: {
|
|
617
|
+
rollupOptions: {
|
|
618
|
+
external: ['react', 'react-dom', 'react-router-dom']
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Configuration TypeScript
|
|
625
|
+
|
|
626
|
+
```json
|
|
627
|
+
{
|
|
628
|
+
"compilerOptions": {
|
|
629
|
+
"target": "ES2020",
|
|
630
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
631
|
+
"module": "ESNext",
|
|
632
|
+
"skipLibCheck": true,
|
|
633
|
+
"moduleResolution": "bundler",
|
|
634
|
+
"allowImportingTsExtensions": true,
|
|
635
|
+
"resolveJsonModule": true,
|
|
636
|
+
"isolatedModules": true,
|
|
637
|
+
"noEmit": true,
|
|
638
|
+
"jsx": "react-jsx",
|
|
639
|
+
"strict": true,
|
|
640
|
+
"noUnusedLocals": true,
|
|
641
|
+
"noUnusedParameters": true,
|
|
642
|
+
"noFallthroughCasesInSwitch": true,
|
|
643
|
+
"types": ["vite/client"]
|
|
644
|
+
},
|
|
645
|
+
"include": ["src"]
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## 📄 Licence
|
|
650
|
+
|
|
651
|
+
MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
|
652
|
+
|
|
653
|
+
## 🐛 Signaler un Bug
|
|
654
|
+
|
|
655
|
+
Envoyez nous un mail à l'adresse `contact.inicode@gmail.com` pour :
|
|
656
|
+
- Signaler un bug
|
|
657
|
+
- Proposer une amélioration
|
|
658
|
+
- Poser une question
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
**@arc-js/core** - Le système de routage intelligent pour React et TypeScript.
|
|
663
|
+
|
|
664
|
+
*Développé par l'équipe INICODE*
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RouteObject } from 'react-router-dom';
|
|
2
|
+
export { useRootingActions } from './rooting.hooks.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Permet d'initialiser toutes les routes de l'application
|
|
6
|
+
* @returns RouteObject[]
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: () => RouteObject[];
|
|
9
|
+
|
|
10
|
+
export { _default as default };
|
package/index.jsx
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { j as jsxRuntimeExports } from './_virtual/jsx-runtime';
|
|
2
|
+
import * as routesUtils_js from './routes-utils.js';
|
|
3
|
+
import { routes } from './routes-utils.js';
|
|
4
|
+
export { useRootingActions } from './rooting.hooks.jsx';
|
|
5
|
+
|
|
6
|
+
var index = () => {
|
|
7
|
+
let routes$1 = routes.map((route) => ({
|
|
8
|
+
path: route.path,
|
|
9
|
+
element: route.layout ? (jsxRuntimeExports.jsx(route.layout, { children: jsxRuntimeExports.jsx(route.component, {}) })) : (jsxRuntimeExports.jsx(route.component, {})),
|
|
10
|
+
...(!!route.error ? {
|
|
11
|
+
errorElement: jsxRuntimeExports.jsx(route.error, {}),
|
|
12
|
+
} : {}),
|
|
13
|
+
}));
|
|
14
|
+
let homeRoute = undefined;
|
|
15
|
+
let notFoundRoute = undefined;
|
|
16
|
+
homeRoute = routes$1.find((a) => a.path === '/');
|
|
17
|
+
notFoundRoute = routes$1.find((a) => a.path === '*');
|
|
18
|
+
routes$1 = [
|
|
19
|
+
...(!!homeRoute ? [homeRoute] : []),
|
|
20
|
+
...routes$1.filter((route) => !(!!route.path &&
|
|
21
|
+
['/', '*'].includes(route.path))),
|
|
22
|
+
...(!!notFoundRoute ? [notFoundRoute] : []),
|
|
23
|
+
].filter((route) => (!!route.path && (route.path.indexOf('/_404') === -1 &&
|
|
24
|
+
route.path.indexOf('/_layout') === -1)));
|
|
25
|
+
if (process.env?.NODE_ENV === 'development') {
|
|
26
|
+
console.log(`[router -> routes.tsx] notFoundRoute:: `, notFoundRoute);
|
|
27
|
+
console.log(`[router -> routes.tsx] autoRoutes:: `, routesUtils_js);
|
|
28
|
+
console.log(`[router -> routes.tsx] routes:: `, routes$1);
|
|
29
|
+
}
|
|
30
|
+
return routes$1;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { index as default };
|
package/index.min.jsx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as jsxRuntimeExports}from"./_virtual/jsx-runtime";import*as routesUtils_js from"./routes-utils.js";import{routes}from"./routes-utils.js";export{useRootingActions}from"./rooting.hooks.jsx";var index=()=>{let t=routes.map(t=>({path:t.path,element:t.layout?jsxRuntimeExports.jsx(t.layout,{children:jsxRuntimeExports.jsx(t.component,{})}):jsxRuntimeExports.jsx(t.component,{}),...t.error?{errorElement:jsxRuntimeExports.jsx(t.error,{})}:{}}));var r=t.find(t=>"/"===t.path),e=t.find(t=>"*"===t.path);return t=[...r?[r]:[],...t.filter(t=>!(t.path&&["/","*"].includes(t.path))),...e?[e]:[]].filter(t=>!!t.path&&-1===t.path.indexOf("/_404")&&-1===t.path.indexOf("/_layout")),process.env?.NODE_ENV,t};export{index as default};
|