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

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


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

## 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 pages du projet Starter Kit, les composants peuvent être placés dans le dossier components.

- Identifier les dépendances à conserver dans la nouvelle version, il faut choisir la version vue3 de la librairie et la version Nuxt si elle existe. Ajouter ces dépendances dans le nouveau projet.
Le package `@cnamts/vue-dot` a été remplacé par `@cnamts/synapse`, le package `vue-i18n` a été remplacé par `@nuxtjs/i18n`.

- Dans le fichier nuxt.config.ts, ajouter le lien vers votre fichier de style global.

<Source dark code={`
// nuxt.config.ts
export default defineNuxtConfig({
	...
	css: [
		...
		'@/theme/styles.scss' // chemin vers votre fichier de style global
	],
	...
})
`}
/>

- Dans le fichier nuxt.config.ts, ajouter les variables de configurations.

<Source dark code={`
// nuxt.config.ts
export default defineNuxtConfig({
	runtimeConfig: {
		public: {
			// ajouter les variables de configuration public ici
			version: process.env.VUE_APP_VERSION,
		},
		...
	},
	...
})
`}
/>

Pour modifier ces variables sans regénérer le projet, il faut les écraser dans le fichier `.output/public/json/config.env.json`.

Pour utiliser ces variables dans un composant, il faut les importer et les utiliser comme suit :

<Source dark code={`
<script lang="ts">
  export default defineNuxtComponent({
    mounted() {
      console.log(this.$config.public.version)
    }
  })
</script>

<template>
  <div>
    {{ $config.public.version }}
  </div>
</template>
`}
/>

## 2 - Migration du routeur

Dans Nuxt 3, il n’est pas nécessaire de créer un fichier `router/index.ts` comme dans Vue classique. Nuxt 3 utilise un système de routing automatique basé sur les fichiers présents dans le dossier `pages/`.
Cependant, pour ajouter un router personnalisé, voici comment faire :

Ouvrir `router.options.ts` dans le dossier `app/` et ajoutez vos routes :

<Source dark code={`
// app/router.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: () => import('@/pages/index.vue') },
    { path: '/about', component: () => import('@/pages/about.vue') },
    { path: '/contact', component: () => import('@/pages/contact.vue') },
  ],
})

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

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

Si le projet utilise des transitions de page,

- Retirer le composant `<transition>` dans le fichier `app.vue`

- Configurer une transition de page dans le fichier `nuxt.config.ts`

<Source dark code={`
// nuxt.config.ts
export default defineNuxtConfig({
	...
	app: {
		...
		pageTransition: {
			name: 'page',
			mode: 'out-in'
		}
	}
`}
/>

- 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,
.page-leave-active {
	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.

Par example : la gestion des [tag metas](https://nuxt.com/docs/migration/meta#options-api)

<Source dark code={`
@Meta
metaInfo(): MetaInfo {
	return {
		title: 'Page title',
		meta: [
			{
					name: 'description',
					vmid: 'description',
					content: 'This is my page description.'
			}
		]
	};
}
`}
/>

## 6 - Migration des composants Vuetify

Pour faciliter la migration des composants Vuetify, ce starter kit intègre le plugin [eslint-plugin-vuetify](https://github.com/vuetifyjs/eslint-plugin-vuetify).
Il permet de faire remonter certains problèmes tels que des props qui n'existent plus ou dont le nom a changé.
Certains changements peuvent être effectués automatiquement.

<Source dark code={`
pnpm lint:migrate:fix
`}
/>

Voici un aperçu des changements qui seront effectués par le script :

<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
...
`}
/>

Ces changements seront uniquement effectués sur les composants Vuetify.

## 7 - Migration de la localisation

Le plugin `vue-i18n` a été remplacé par [`@nuxtjs/i18n`]( https://v8.i18n.nuxtjs.org/getting-started/basic-usage ).

- Mettre à jour le fichier `nuxt.config.ts` pour configurer le plugin `@nuxtjs/i18n`.

<Source dark code={`
 modules: [
    '@nuxtjs/i18n',
    ...
  ],
  i18n: {
		vueI18n: './nuxt-i18n.ts'
	},
`}
/>

- Créer un fichier `nuxt-i18n.ts` à la racine du projet, importer les traductions et les configurer.

<Source dark code={`
import translationFR from "
import '../styles/shared.css';@/translations/fr";

export default {
	legacy: false,
	locale: 'fr',
	messages : {
		fr: translationFR
	}
}
`}
/>

- Dans les composants, quand un object est importé avec la fonction `$t`, il faut le remplacer par `$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. Ils utilisent désormais vitest et `@nuxt/test-utils` pour les tests de composants.

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

Pour l'utilisation de `@nuxt/test-utils`, voir la [documentation](https://nuxt.com/docs/getting-started/testing).