@arc-js/core
v0.0.30
Published
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
Readme
@arc-js/core
@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.
✨ Fonctionnalités Principales
🗺️ Routage Auto-Généré
- Génération automatique des routes à partir de la structure du système de fichiers
- Support des layouts hiérarchiques avec héritage automatique
- Pages d'erreur spécifiques par sous-répertoire
- Configuration modulaire avec support des modules indépendants
🧭 Hooks de Navigation Avancés
- Navigation type-safe avec validation des paramètres
- Gestion automatique des query strings avec support multi-langue
- Résolution de routes avec paramètres dynamiques
- Navigation avec rechargement pour les mises à jour critiques
⚙️ Configuration Modulaire
- Configuration par module avec fichiers
config.json - Activation/désactivation dynamique des modules
- Chemins personnalisables par module
- Détection automatique des fichiers de pages
🛡️ Sécurité et Fiabilité
- Validation des chemins avec fallback sécurisé
- Gestion des erreurs avec pages d'erreur hiérarchiques
- Logs détaillés en mode développement seulement
- Types TypeScript complets pour une meilleure autocomplétion
📦 Installation
Via npm/yarn/pnpm
npm install @arc-js/core react-router-dom react
# ou
yarn add @arc-js/core react-router-dom react
# ou
pnpm add @arc-js/core react-router-dom reactDépendances requises
- React 18+
- React Router DOM 6+
- TypeScript 5.0+ (recommandé)
- @arc-js/qust (pour la manipulation de query strings)
🚀 Démarrage Rapide
Structure de projet recommandée
src/
├── pages/
│ ├── _layout.tsx # Layout racine
│ ├── _error.tsx # Page d'erreur racine
│ ├── index.tsx # Page d'accueil
│ ├── about/
│ │ ├── _layout.tsx # Layout spécifique à /about
│ │ ├── _error.tsx # Erreur spécifique à /about
│ │ ├── index.tsx # /about
│ │ └── team.tsx # /about/team
│ └── users/
│ ├── _layout.tsx # Layout spécifique à /users
│ ├── [id].tsx # /users/:id (paramètre dynamique)
│ └── index.tsx # /users
├── modules/
│ └── admin/
│ ├── config.json # Configuration du module admin
│ └── pages/
│ ├── _layout.tsx # Layout du module admin
│ ├── dashboard.tsx # /admin/dashboard
│ └── users.tsx # /admin/users
└── main.tsxConfiguration de base
// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import getRoutes from '@arc-js/core';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
const App = () => {
const routes = getRoutes();
const router = createBrowserRouter(routes);
return <RouterProvider router={router} />;
};
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);Fichier config.json d'un module
{
"path": "/admin",
"name": "Administration",
"description": "Module d'administration",
"author": "Votre Équipe",
"isEnabled": true
}📚 Documentation API
Hook useRootingActions
import { useRootingActions } from '@arc-js/core';
const MyComponent = () => {
const {
params, // Paramètres de route (ex: { id: "123" })
queries, // Query parameters (ex: { lang: "fr", page: "1" })
navigate, // Fonction navigate de react-router-dom
resolveRoute, // Résoudre une route avec paramètres
goToRoute, // Naviguer vers une route
goAndReloadRoute, // Naviguer avec rechargement
pathName, // Chemin actuel
urlSearch, // Query string actuelle
getUrlData, // Analyser une URL
checkIfIsCurrentRoute // Vérifier si on est sur une route
} = useRootingActions();
// Exemple d'utilisation
const handleClick = () => {
goToRoute({
path: '/users/:id',
params: { id: '123' },
queries: { lang: 'fr', view: 'details' }
});
};
return (
<div>
<button onClick={handleClick}>
Voir l'utilisateur 123
</button>
</div>
);
};Interfaces principales
// Configuration de navigation
interface ConfigGoToRoute {
path?: string;
params?: any;
queries?: any;
refreshPage?: boolean;
replace?: boolean;
enableLoader?: boolean;
}
// Configuration de résolution de route
interface ConfigResolveRoute {
path?: string;
params?: any;
queries?: any;
}
// Route générée automatiquement
interface RouteDefinition {
truePath: string; // Chemin physique du fichier
pathParent?: string; // Chemin parent
path: string; // Chemin de la route
component: React.ComponentType<any>;
layout?: React.ComponentType<{ children: ReactNode }>;
error?: React.ComponentType<{}>;
}
// Configuration d'un module
interface ConfigDatasDefinition {
path: string; // Chemin de base du module
name: string | undefined;
author: string | undefined;
isEnabled: boolean; // Activation du module
}Fonctions utilitaires
import { nativeResolveRoute } from '@arc-js/core';
// Résoudre une URL sans utiliser le hook
const url = nativeResolveRoute({
path: '/users/:id',
params: { id: '456' },
queries: { lang: 'en', tab: 'profile' }
});
console.log(url); // "/users/456?lang=en&tab=profile"🔧 Utilisation Avancée
Pages dynamiques avec paramètres
// src/pages/users/[id].tsx
import { useRootingActions } from '@arc-js/core';
const UserPage = () => {
const { params, queries } = useRootingActions();
const userId = params.id;
const lang = queries.lang || 'fr';
return (
<div>
<h1>Utilisateur {userId}</h1>
<p>Langue: {lang}</p>
</div>
);
};
export default UserPage;Layouts hiérarchiques
// src/pages/_layout.tsx (Layout racine)
const RootLayout = ({ children }) => (
<div className="app">
<header>Mon Application</header>
<main>{children}</main>
<footer>© 2024</footer>
</div>
);
// src/pages/admin/_layout.tsx (Layout admin)
const AdminLayout = ({ children }) => (
<div className="admin">
<nav>Menu Admin</nav>
<div className="admin-content">{children}</div>
</div>
);Pages d'erreur spécifiques
// src/pages/_error.tsx (Erreur globale)
const GlobalErrorPage = () => (
<div>
<h1>Une erreur est survenue</h1>
<p>Veuillez réessayer plus tard.</p>
</div>
);
// src/pages/admin/_error.tsx (Erreur admin)
const AdminErrorPage = () => (
<div className="admin-error">
<h1>Erreur d'administration</h1>
<p>Contactez le support technique.</p>
</div>
);Configuration de modules
// src/modules/blog/config.json
{
"path": "/blog",
"name": "Blog",
"description": "Module de blog",
"author": "Équipe Rédaction",
"isEnabled": true
}
// src/modules/blog/pages/index.tsx sera accessible à /blog
// src/modules/blog/pages/[slug].tsx sera accessible à /blog/:slug🎯 Exemples Complets
Exemple 1 : Application avec authentification
// src/pages/_layout.tsx
import { useRootingActions } from '@arc-js/core';
import { Link } from 'react-router-dom';
const AppLayout = ({ children }) => {
const { checkIfIsCurrentRoute } = useRootingActions();
const isActive = (path: string) => checkIfIsCurrentRoute(path);
return (
<div>
<nav>
<Link to="/" className={isActive('/') ? 'active' : ''}>
Accueil
</Link>
<Link to="/about" className={isActive('/about') ? 'active' : ''}>
À propos
</Link>
<Link to="/contact" className={isActive('/contact') ? 'active' : ''}>
Contact
</Link>
</nav>
{children}
</div>
);
};
// src/pages/protected/_layout.tsx
import { useEffect } from 'react';
import { useRootingActions } from '@arc-js/core';
const ProtectedLayout = ({ children }) => {
const { goToRoute } = useRootingActions();
useEffect(() => {
const token = localStorage.getItem('auth_token');
if (!token) {
goToRoute({
path: '/login',
queries: { redirect: window.location.pathname }
});
}
}, []);
return token ? children : null;
};Exemple 2 : Dashboard avec multi-langue
// src/pages/dashboard/index.tsx
import { useRootingActions } from '@arc-js/core';
import { useState, useEffect } from 'react';
const DashboardPage = () => {
const { queries, goToRoute, resolveRoute } = useRootingActions();
const [lang, setLang] = useState(queries.lang || 'fr');
const changeLanguage = (newLang: string) => {
goToRoute({
path: '/dashboard',
queries: { ...queries, lang: newLang }
});
};
const getReportUrl = (reportId: string) => {
return resolveRoute({
path: '/dashboard/reports/:id',
params: { id: reportId },
queries: { lang, format: 'pdf' }
});
};
return (
<div>
<div>
Langue:
<button onClick={() => changeLanguage('fr')}>FR</button>
<button onClick={() => changeLanguage('en')}>EN</button>
</div>
<a href={getReportUrl('monthly')}>
Télécharger le rapport mensuel
</a>
</div>
);
};Exemple 3 : Formulaire avec redirection
// src/pages/contact/index.tsx
import { useState } from 'react';
import { useRootingActions } from '@arc-js/core';
const ContactPage = () => {
const { goToRoute, goAndReloadRoute } = useRootingActions();
const [formData, setFormData] = useState({ name: '', email: '' });
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Simulation d'envoi
const success = await submitContactForm(formData);
if (success) {
// Redirection simple
goToRoute({
path: '/contact/success',
queries: { ref: 'contact-form' }
});
} else {
// Redirection avec rechargement pour nettoyer le cache
goAndReloadRoute({
path: '/contact',
queries: { error: 'submission_failed' }
});
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.name}
onChange={e => setFormData({...formData, name: e.target.value})}
placeholder="Nom"
/>
<input
value={formData.email}
onChange={e => setFormData({...formData, email: e.target.value})}
placeholder="Email"
/>
<button type="submit">Envoyer</button>
</form>
);
};🔧 Configuration Avancée
Variables d'environnement
# .env
VITE_APP_NAME="Mon Application"
VITE_API_URL="http://localhost:3000"
# Activation des logs détaillés
NODE_ENV=development # Logs complets
NODE_ENV=production # Logs minimauxExtension de la configuration
// custom-routes.ts
import { getRoutes as getBaseRoutes } from '@arc-js/core';
import { RouteDefinition } from '@arc-js/core/types';
export async function getRoutes() {
const baseRoutes = await getBaseRoutes();
// Ajouter des routes manuelles
const customRoutes: RouteDefinition[] = [
{
truePath: '/src/pages/custom.tsx',
path: '/custom-route',
component: () => <div>Route personnalisée</div>,
layout: undefined,
error: undefined
}
];
return [...baseRoutes, ...customRoutes];
}Personnalisation du routage
// custom-router.tsx
import { useRootingActions } from '@arc-js/core';
import type { ConfigGoToRoute } from '@arc-js/core';
export const useCustomRooting = () => {
const baseActions = useRootingActions();
const goToRouteWithAnalytics = (
config: ConfigGoToRoute,
analyticsEvent?: string
) => {
// Envoyer l'événement analytics
if (analyticsEvent) {
window.gtag?.('event', analyticsEvent, {
path: config.path,
params: config.params
});
}
// Utiliser la navigation standard
return baseActions.goToRoute(config);
};
return {
...baseActions,
goToRouteWithAnalytics
};
};🛡️ Gestion des Erreurs
Structure d'erreur hiérarchique
pages/
├── _error.tsx # Erreur globale
├── admin/
│ ├── _error.tsx # Erreur admin (surcharge globale)
│ └── dashboard/
│ └── _error.tsx # Erreur dashboard (surcharge admin)
└── public/
└── _error.tsx # Erreur section publiquePage d'erreur avancée
// src/pages/_error.tsx
import { useRouteError, Link } from 'react-router-dom';
import { useRootingActions } from '@arc-js/core';
const ErrorPage = () => {
const error = useRouteError();
const { pathName, goToRoute } = useRootingActions();
console.error('Route Error:', error);
const handleRetry = () => {
goToRoute({
path: pathName,
refreshPage: true // Forcer le rechargement
});
};
return (
<div className="error-container">
<h1>Oups ! Une erreur est survenue</h1>
<p>Désolé, une erreur inattendue s'est produite.</p>
<div className="error-actions">
<button onClick={handleRetry}>
Réessayer
</button>
<Link to="/">
Retour à l'accueil
</Link>
</div>
</div>
);
};📋 Table des Conventions
Fichiers spéciaux
| Fichier | Chemin de route | Description |
|---------|----------------|-------------|
| index.tsx | / (racine) ou /dossier/ | Page d'index |
| _layout.tsx | Non accessible | Layout pour le dossier |
| _error.tsx | Non accessible | Page d'erreur pour le dossier |
| _404.tsx | * | Page 404 (non trouvé) |
| [param].tsx | /:param | Paramètre dynamique |
| [...slug].tsx | /* | Catch-all route |
Paramètres de query string
| Paramètre | Type | Description |
|-----------|------|-------------|
| lang | fr | en | Langue de l'application |
| ref | string | Référence pour le tracking |
| modal | string | Ouvrir un modal spécifique |
| page | number | Numéro de page (pagination) |
| sort | string | Tri des résultats |
🔧 Build et Développement
Scripts recommandés
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"type-check": "tsc --noEmit",
"generate-routes": "node scripts/generate-routes.js"
}
}Configuration Vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@arc-js/core': '/node_modules/@arc-js/core'
}
},
build: {
rollupOptions: {
external: ['react', 'react-dom', 'react-router-dom']
}
}
});Configuration TypeScript
{
"compilerOptions": {
"target": "ES2020",
"lib": ["DOM", "DOM.Iterable", "ES2020"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"types": ["vite/client"]
},
"include": ["src"]
}📄 Licence
MIT License - Voir le fichier LICENSE pour plus de détails.
🐛 Signaler un Bug
Envoyez nous un mail à l'adresse [email protected] pour :
- Signaler un bug
- Proposer une amélioration
- Poser une question
@arc-js/core - Le système de routage intelligent pour React et TypeScript.
Développé par l'équipe INICODE
