import {Meta, Source} from '@storybook/addon-docs/blocks';
import '../styles/shared.css';

<Meta title="Guide Du Dev/Migration/Depuis Vue2"/>


<div className="header">
  <h1>Migrer de Vue-dot (Vue2) vers Synapse</h1>
</div>

> **Nouveau Starter Kit** — depuis sa dernière version, le Starter Kit Synapse (`sksn_x`) est une application **Vue 3 + Vite** (SPA). Il n'utilise **plus Nuxt**. La structure de projet, le routeur, la configuration, la localisation et les tests décrits ci-dessous suivent cette nouvelle stack.

## 1 - Commencer la migration

Pour réaliser la migration d'un produit VueJS 2 vers VueJS 3, vous pouvez suivre les étapes suivantes :

- Téléchargez la stack de migration [Starter Kit Synapse](https://gitlab.cnqd.cnamts.fr/human/developpement/sksn_x).

- Copier les pages de votre projet VueJS 2 dans le dossier `src/views/` du projet Starter Kit&nbsp;; les composants peuvent être placés dans le dossier `src/components/`.

- Identifier les dépendances à conserver dans la nouvelle version, en choisissant la version Vue 3 de chaque librairie. Ajouter ces dépendances dans le nouveau projet.
Le package `@cnamts/vue-dot` a été remplacé par `@cnamts/synapse`, et `vue-i18n` passe à sa version Vue 3 (voir la section [Migration de la localisation](#7---migration-de-la-localisation)).

### Styles globaux

Il n'y a plus de `nuxt.config.ts`. Les styles globaux sont importés directement dans le point d'entrée `src/main.ts` :

<Source dark language="ts" code={`
// src/main.ts
import 'vuetify/styles'
import '@cnamts/synapse/synapse.css'
import './assets/styles/index.scss' // vos styles globaux
`}
/>

### Variables de configuration

Deux mécanismes remplacent le `runtimeConfig` de Nuxt :

**1. Variables de build (Vite)** — exposées via `import.meta.env`. La version du projet est par exemple injectée depuis le `package.json` dans `vite.config.ts` :

<Source dark language="ts" code={`
// vite.config.ts
import { version } from './package.json'

process.env = Object.assign(process.env, {
  VITE_APP_VERSION: version,
})
`}
/>

<Source dark language="ts" code={`
// usage dans le code
console.log(import.meta.env.VITE_APP_VERSION)
`}
/>

**2. Variables d'exécution (runtime)** — un fichier JSON chargé au démarrage, modifiable **sans rebuild** (déploiement). Il est chargé dans `src/main.ts` puis fourni à l'application via une `InjectionKey`, en remplacement du `$config.public` de Nuxt :

<Source dark language="ts" code={`
// src/var.env.ts
import type { InjectionKey } from 'vue'

export type Config = {
  title: string
  message: string
  // ... vos variables
}

export const CONFIG_SYMBOL = Symbol('config') as InjectionKey<Config>
`}
/>

<Source dark language="ts" code={`
// src/main.ts
import { CONFIG_SYMBOL, type Config } from './var.env'

const res = await fetch(import.meta.env.VITE_JSON_FILE_NAME)
const config = (await res.json()) as Config

const app = createApp(App)
app.provide(CONFIG_SYMBOL, config)
`}
/>

Pour utiliser ces variables dans un composant, on les récupère avec `inject` (Composition API) :

<Source dark language="vue" code={`
<script setup lang="ts">
import { inject } from 'vue'
import { CONFIG_SYMBOL, type Config } from '@/var.env'

const config = inject(CONFIG_SYMBOL) as Config
</script>

<template>
  <div>{{ config.title }}</div>
</template>
`}
/>

## 2 - Migration du routeur

Il n'y a plus de routing automatique basé sur le dossier `pages/` (Nuxt). Le projet utilise désormais **Vue Router** déclaré explicitement dans `src/router/index.ts` :

<Source dark language="ts" code={`
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', name: 'index', component: HomeView },
    { path: '/about', name: 'about', component: () => import('../views/AboutView.vue') },
    // ... vos routes
  ],
})

export default router
`}
/>

Voir le [guide de migration de vue-router](https://router.vuejs.org/guide/migration/) pour plus d'informations.

## 3 - Migration des stores VueX vers Pinia

Le Starter Kit utilise **Pinia** (enregistré dans `src/main.ts` via `app.use(createPinia())`).

Prenons un exemple de store Vuex :

<Source dark code={`
// Ancien store Vuex (store/index.js)
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0,
    user: null,
  },
  mutations: {
    increment(state) {
      state.count++
    },
    setUser(state, user) {
      state.user = user
    }
  },
  actions: {
    async fetchUser({ commit }) {
      const user = await fetch('/api/user').then(res => res.json())
      commit('setUser', user)
    }
  },
  getters: {
    isAuthenticated: state => !!state.user,
  }
})
`}
/>

Nouvelle version avec Pinia :

<Source dark code={`
// stores/counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    user: null as null | { name: string; email: string }
  }),
  actions: {
    increment() {
      this.count++
    },
    async fetchUser() {
      const user = await fetch('/api/user').then(res => res.json())
      this.user = user
    }
  },
  getters: {
    isAuthenticated: (state) => !!state.user,
  }
})
`}
/>

Différences majeures entre Vuex et Pinia :
-	Plus besoin de mutations → On modifie l’état directement !
-	Les getters et actions sont dans le même objet
-	Plus léger et plus performant

Utilisation du store dans les composants Vue :

<Source dark code={`
// Avec Vuex
<script setup>
import { useStore } from 'vuex'
const store = useStore()

store.commit('increment')
console.log(store.state.count)
</script>
`}
/>

<Source dark code={`
// Avec Pinia
<script setup>
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()

counterStore.increment()
console.log(counterStore.count)
</script>
`}
/>

Nb : pour la persistance des données vous pouvez utiliser le plugin [pinia-plugin-persistedstate](https://prazdevs.github.io/pinia-plugin-persistedstate/).

## 4 - Animations des pages

Sans Nuxt, les transitions de page se gèrent directement avec **Vue Router** et le composant `<Transition>`, en enveloppant la vue dans `App.vue` :

<Source dark code={`
<!-- App.vue -->
<template>
  <RouterView v-slot="{ Component }">
    <Transition name="page" mode="out-in">
      <component :is="Component" />
    </Transition>
  </RouterView>
</template>
`}
/>

- Définir le style de la transition dans un fichier de style global.

<Source dark code={`
.page-enter-active,
.page-leave-active {
	transition-duration: .15s;
	transition-property: opacity;
	transition-timing-function: ease;
}

.page-enter-from,
.page-leave-to {
	opacity: 0;
}
`}
/>

## 5 - Migration de la syntaxe des composants

Voir le [guide migration officiel](https://v3-migration.vuejs.org/).

Utilisation du script [vue-class-migrator](https://github.com/getyourguide/vue-class-migrator)

`vue-class-migrator` est un script permettant de migrer automatiquement les composants vueJs écrient avec `vue-class-component` à la syntaxe native VueJs Option API

- Supprimer tous les décorateurs personnalisés, ils ne seront pas reconnus par `vue-class-migrator` et causeront l'échec de la migration du composant.

- Exécuter la commande `npx vue-class-migrator -d`  à la racine de votre projet.

- Consulter les logs dans le terminal pour repérer les éventuels composants causant des erreurs et n'ayant pas pu être migré automatiquement.

- Si vous avez déclaré vos props en tant que mixins dans vos composants en utilisant le helper 'mixin' de `vue-class-componant`, vous devrez les migrer manuellement, les mixins ne sont pas supportés dans la syntaxe native VueJs.

<Source dark code={`
const Props = Vue.extend({
	props: {
			foo: {
				type: Boolean,
				default: false
			}
	}
});

const MixinsDeclaration = mixins(Props);

export default defineComponent({
extends: MixinsDeclaration,
`}
/>

devient :

<Source dark code={`
const Props = defineComponent({
		props: {
			displayInfo: {
				type: Boolean,
				default: false
			}
		}
	});

	export default defineComponent({
		extends: Props,
		...
`}
/>

- Implémenter les fonctionnalités qui utilisaient des décorateurs retiré à l'étape 1 en utilisant l'option API, des mixins ou des composables. La gestion des balises `<meta>` peut par exemple se faire avec [@unhead/vue](https://unhead.unjs.io/) (`useHead`) ou via `vue-router`.

## 6 - Migration des composants Vuetify

Pour faciliter la migration des composants Vuetify (Vue 2 → Vue 3), vous pouvez ajouter le plugin [eslint-plugin-vuetify](https://github.com/vuetifyjs/eslint-plugin-vuetify) à votre configuration ESLint.
Il permet de faire remonter certains problèmes tels que des props qui n'existent plus ou dont le nom a changé&nbsp;; certains changements peuvent être corrigés automatiquement (`--fix`).

Voici un aperçu des changements typiques :

<Source dark code={`
// passage de la syntaxe vue2 a vue3 pour la réactivité :
- value
+ modelValue

- xxx.sync
+ v-model:xxx

// Changement de nom de certaines props :
- outlined
+ variant="outlined"
- accordion
+ variant="accordion"
- text
+ variant="text"
- background-color="xxx"
+ bg-color="xxx"
- top
+ location="top"
- large
+ size="large"
...

// Autres changements concernants les props :
- validate-on-blur
+ :validate-on="blur"

// Changement concernants les events :
- @change
+ @update:model-value

// Changement de nom de certains composants :
- VExpansionPanelHeader
+ VExpansionPanelTitle
- VExpansionPanelContent
+ VExpansionPanelText
- VSimpleCheckbox
+ VCheckboxBtn
...
`}
/>

Voir aussi le [guide de migration officiel Vuetify](https://vuetifyjs.com/en/getting-started/upgrade-guide/).

## 7 - Migration de la localisation

Sans Nuxt, la localisation se fait avec [`vue-i18n`](https://vue-i18n.intlify.dev/) (version Vue 3, Composition API) au lieu de `@nuxtjs/i18n`.

- Installer `vue-i18n`, puis créer une instance et l'enregistrer dans `src/main.ts` :

<Source dark language="ts" code={`
// src/i18n.ts
import { createI18n } from 'vue-i18n'
import fr from '@/translations/fr'

export const i18n = createI18n({
  legacy: false,
  locale: 'fr',
  messages: { fr },
})
`}
/>

<Source dark language="ts" code={`
// src/main.ts
import { i18n } from './i18n'

app.use(i18n)
`}
/>

- Dans les composants, quand un objet est récupéré via la fonction `$t`, il faut utiliser `$tm`.

- Remplacer l'usage du composant `<i18n>` par `<i18n-t>`.

<Source dark code={`
<i18n path="my.path" tag="p">My default text</i18n>
`}
/>

Devient :

<Source dark code={`
<i18n-t keypath="my.path" tag="p">My default text</i18n-t>
`}
/>

Plus de détails sur la page de [migration vue-i18n](https://vue-i18n.intlify.dev/guide/migration/breaking.html).

## 8 - Migration des tests unitaires et de composants

Les tests unitaires et de composants doivent être mis à jour pour être compatibles avec VueJS 3. Le Starter Kit utilise désormais **Vitest** avec [`@vue/test-utils`](https://test-utils.vuejs.org/) pour les tests de composants, et **Cypress** pour les tests e2e.

L'API de Vitest est similaire à celle de Jest&nbsp;: pour faire des mocks il faut désormais utiliser `vi` au lieu de `jest`.

<Source dark language="ts" code={`
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import HomeView from '@/views/HomeView.vue'

describe('HomeView', () => {
  it('monte le composant', () => {
    const wrapper = mount(HomeView)
    expect(wrapper.exists()).toBe(true)
  })
})
`}
/>
