Skip to content

É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é :

txt
@vevedh/nuxt-form-builder

La 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é ;
  • FormBuilder et FormViewer sont 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

SujetNuxt LayerModule Nuxt 4
Activationextendsmodules
Configurationfusion de configconfigKey et options typées
Composants publicsscan du layerenregistrement explicite
Auto-importshérités des dossiersdéclarés via Nuxt Kit
Dépendances Nuxthéritées du layermoduleDependencies
Distributionsources du layermodule/runtime compilés
Choix long termecompatibilitédistribution principale

Structure cible

txt
packages/nuxt-form-builder/
├── src/
│   ├── module.ts
│   └── runtime/
│       ├── assets/
│       ├── components/
│       ├── composables/
│       ├── constants/
│       ├── stores/
│       ├── types/
│       └── utils/
├── playground/
├── test/
├── docs/
└── package.json

Une architecture multi-packages peut extraire les moteurs purs dans qform-core et garder un wrapper layer temporaire.

Options cible

ts
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.

ts
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 :

txt
FormBuilder
FormViewer

Les 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 :

txt
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é

txt
@vevedh/qform-builder-layer   # distribution layer conservée
@vevedh/nuxt-form-builder    # nouvelle distribution module

Le module doit être une nouvelle enveloppe autour du même cœur, pas une réécriture fonctionnelle.

QForm Builder — layer Nuxt 4, Quasar et FormKit pour formulaires dynamiques.