Évolution vers un module Nuxt 4
La distribution actuelle est un Nuxt Layer npm. La prochaine distribution recommandée est un vrai module Nuxt 4 sous un nouveau nom, tout en conservant le layer comme référence de compatibilité.
Nom technique recommandé :
@vevedh/nuxt-form-builderLa disponibilité npm et les conflits de marque doivent être vérifiés avant publication.
Décision de migration
Le projet est prêt à commencer la phase module parce que :
- le contrat public est gelé ;
FormBuilderetFormViewersont identifiés ;- les moteurs purs sont exportés ;
- une application hôte indépendante est construite ;
- les tests Chromium et multi-OS existent ;
- aucune dépendance backend n’est imposée.
Le module ne doit toutefois pas être publié immédiatement comme version stable. La première étape est une migration de packaging sans changement fonctionnel.
Différence entre layer et module
| Sujet | Nuxt Layer | Module Nuxt 4 |
|---|---|---|
| Activation | extends | modules |
| Configuration | fusion de config | configKey et options typées |
| Composants publics | scan du layer | enregistrement explicite |
| Auto-imports | hérités des dossiers | déclarés via Nuxt Kit |
| Dépendances Nuxt | héritées du layer | moduleDependencies |
| Distribution | sources du layer | module/runtime compilés |
| Choix long terme | compatibilité | distribution principale |
Structure cible
packages/nuxt-form-builder/
├── src/
│ ├── module.ts
│ └── runtime/
│ ├── assets/
│ ├── components/
│ ├── composables/
│ ├── constants/
│ ├── stores/
│ ├── types/
│ └── utils/
├── playground/
├── test/
├── docs/
└── package.jsonUne architecture multi-packages peut extraire les moteurs purs dans qform-core et garder un wrapper layer temporaire.
Options cible
export interface NuxtFormBuilderModuleOptions {
prefix?: string
locale?: 'fr' | 'en'
injectCss?: boolean
components?: {
builder?: boolean
viewer?: boolean
}
dependencies?: {
pinia?: boolean
formkit?: boolean
quasar?: boolean
unocss?: boolean
}
}Entrée module moderne
Les dépendances de modules doivent être déclarées avec moduleDependencies. installModule() est désormais une voie dépréciée dans Nuxt Kit.
import {
addComponent,
addImportsDir,
createResolver,
defineNuxtModule,
} from '@nuxt/kit'
export interface NuxtFormBuilderModuleOptions {
prefix?: string
locale?: 'fr' | 'en'
injectCss?: boolean
components?: {
builder?: boolean
viewer?: boolean
}
}
export default defineNuxtModule<NuxtFormBuilderModuleOptions>({
meta: {
name: '@vevedh/nuxt-form-builder',
configKey: 'formBuilder',
compatibility: {
nuxt: '^4.0.0',
},
},
defaults: {
prefix: '',
locale: 'fr',
injectCss: true,
components: {
builder: true,
viewer: true,
},
},
moduleDependencies: {
'@pinia/nuxt': {
version: '>=0.11.0',
},
'@formkit/nuxt': {
version: '>=2.1.0 <3.0.0',
},
'nuxt-quasar-ui': {
version: '>=2.1.0',
},
'@unocss/nuxt': {
version: '>=66.0.0',
},
},
setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
const runtime = resolver.resolve('./runtime')
const prefix = options.prefix || ''
if (options.components?.builder !== false) {
addComponent({
name: `${prefix}FormBuilder`,
filePath: resolver.resolve(runtime, 'components/FormBuilder.vue'),
})
}
if (options.components?.viewer !== false) {
addComponent({
name: `${prefix}FormViewer`,
filePath: resolver.resolve(runtime, 'components/FormViewer.vue'),
})
}
addImportsDir(resolver.resolve(runtime, 'composables'))
if (options.injectCss !== false) {
nuxt.options.css.push(resolver.resolve(runtime, 'assets/scss/index.scss'))
}
},
})Cet exemple illustre la frontière du module ; la configuration FormKit et les composants internes doivent être enregistrés par une stratégie dédiée avant publication.
Refactors obligatoires
1. Configuration FormKit
Le fichier actuel importe les adaptateurs depuis #components. Dans un module, il faut garantir l’ordre d’enregistrement et éviter de dépendre du scan implicite du layer.
Solutions possibles :
- générer une configuration FormKit depuis le module ;
- enregistrer les adaptateurs runtime explicitement ;
- utiliser des imports directs vers le runtime compilé ;
- ajouter un plugin FormKit produit par
addTemplate().
2. Auto-imports
Les dossiers stores, composables et utils sont actuellement auto-importés par le layer. Le module doit :
- utiliser
addImportsDir()pour les APIs réellement publiques ; - conserver des imports relatifs/directs pour les internals ;
- éviter d’exposer les stores internes comme contrat public implicite.
3. Composants
Le layer scanne 85 composants. Le module doit enregistrer globalement uniquement :
FormBuilder
FormViewerLes composants internes doivent être importés par le runtime ou enregistrés sous un espace de noms privé contrôlé.
4. Styles et chemins
Remplacer les normalisations manuelles d’URL par createResolver(import.meta.url) et tester Windows/Linux sur le tarball installé.
5. Sortie npm
Le module doit publier :
dist/module.mjs
dist/runtime/**
dist/types/**Les sous-chemins publics actuels doivent rester disponibles ou être réexportés depuis un package core.
Ce qui est déjà terminé fonctionnellement
La migration module doit préserver, et non redévelopper :
- catalogue et panneaux custom ;
- conditions avancées ;
- validation avancée ;
- templates ;
- Theme Builder ;
- historique ;
- arborescence ;
- migrations ;
- i18n ;
- accessibilité ;
- import/export.
Les fonctions Phase 3 — Zod/NFZ, CRUD, packs métier, export de code, headless et Génération guidée — ne doivent commencer qu’après la parité du module.
Roadmap module
M0 — référence layer
- lockfile reproductible ;
- CI distante verte ;
- captures et baselines réelles ;
- référence de compatibilité publiée.
M1 — module minimal
- module starter officiel ;
src/module.ts;- runtime déplacé ou partagé ;
- Builder et Viewer enregistrés ;
- aucun changement fonctionnel.
M2 — configuration runtime
- FormKit ;
- Quasar ;
- Pinia ;
- UnoCSS ;
- styles ;
- types et sous-chemins.
M3 — options
- préfixe ;
- locale ;
- CSS ;
- Builder/Viewer séparés ;
- dépendances configurables.
M4 — validation de distribution
- tarball installé dans une app vierge ;
- SSR ;
- Windows/Linux ;
- typecheck consommateur ;
- comparaison du contrat layer/module.
M5 — publication
- alpha ;
- intégration métier réelle ;
- RC ;
- stable.
Convention de compatibilité
@vevedh/qform-builder-layer # distribution layer conservée
@vevedh/nuxt-form-builder # nouvelle distribution moduleLe module doit être une nouvelle enveloppe autour du même cœur, pas une réécriture fonctionnelle.