SvelteKit

SvelteKit est pour le framework Svelte un équivalent à :

J'ai commencé à utiliser SvelteKit en avril 2022.

Depuis, j'ai réalisé de nombreux POC ou Playground basées sur Svelte et SvelteKit.
Mais aussi quelques projets :

SvelteKit permet de générer différent type de site :


Journaux liées à cette note :

J'ai découvert la fonctionnalité SvelteKit Shared hooks init #SvelteKit, #JaiDécouvert

J'ai bien fait de partager poc-sveltekit-custom-server dans la section discussion GitHb de Sveltekit car cela m'a permis de découvrir via ce commentaire l'existence de la fonctionnalité native SvelteKit nommée Shared hooks init.

This function runs once, when the server is created or the app starts in the browser, and is a useful place to do asynchronous work such as initializing a database connection.

source

Cette fonctionnalité a été introduite dans la version 2.10.0 de SvelteKit publiée le 10 décembre 2024.

C'est particulièrement frustrant car j'ai cherché cette fonctionnalité à plusieurs reprises entre mi-2022 et mi-2024, sans la trouver. Je me souviens même avoir lu une issue de Rich Harris expliquant que cette fonctionnalité était complexe à implémenter.

Il y a quelques semaines, lors du développement de poc-sveltekit-custom-server, j'ai refait une recherche de fonctionnalité "init", mais en me limitant à la documentation "Node servers". La présence de "Graceful shutdown" m'a paradoxalement induit en erreur : j'en ai déduit que s'il n'y avait pas d'équivalent pour l'initialisation sur cette page, c'est que la fonctionnalité n'existait toujours pas 😔.

Conséquence de tout cela :

Bilan de poc-sveltekit-custom-server #SvelteKit, #svelte, #node, #javascript, #projet, #POC

Contexte et objectifs

Dans le projet gibbon-replay, j'ai besoin d'exécuter une tâche une fois par jour pour supprimer des anciennes sessions.

gibbon-replay utilise une base de données SQLite qui ne dispose pas nativement de fonctionnalité de type Time To Live, comme on peut trouver dans Clickhouse.
SQLite ne propose pas non plus d'équivalent à pg_cron — ce qui est tout à fait normal étant donnée que SQLite est une librairie et non pas un service à part entière.

Le projet gibbon-replay est un monolith (j'aime les monoliths !) et je souhaite conserver ce choix.

Face à ces contraintes, une solution consiste à intégrer une solution comme Cron for Node.js directement dans l'application gibbon-replay.
Je pense que je dois implémenter cela dans un SvelteKit Custom Server, ce qui me permettrait d'exécuter cette tâche de purge à intervalles réguliers tout en conservant l'architecture monolithique.

Il y a quelques jours, j'ai décidé de tester cette idée dans un POC nommé : poc-sveltekit-custom-server.

J'ai aussi décidé d'expérimenter un objectif supplémentaire dans ce POC : lancer la migration du modèle de données dès le lancement du monolith et non plus lors de la première requête HTTP reçue par le service.

Enfin, je souhaitais ne pas dégrader l'expérience développeur (DX), c'est à dire, je souhaitais pouvoir continuer à simplement utiliser :

$ pnpm run dev

ou

$ pnpm run build
$ pnpm run preview

sans différence avec un projet SvelteKit "vanilla".

Résultats du POC et enseignements

Tout d'abord, le POC fonctionne parfaitement 🙂, sans dégrader l'expérience développeur (DX), qui ressemble à ceci :

$ mise install
$ pnpm install
$ pnpm run load-seed-data
Start data model migration…
Data model migration completed
Start load seed data...
seed data loaded

Lancement du projet en mode développement :

$ pnpm run dev
Start data model migration…
Data model migration completed
Server started on http://localhost:5173 in development mode

Lancement du projet "buildé" :

$ pnpm run build
$ pnpm run preview
Start data model migration…
Data model migration completed
Server started on http://localhost:3000 in production mode

Les migrations et les données "seed.sql" se trouvent dans le dossier /sqls/.

Le SvelteKit Custom Server est implémenté dans le fichier src/server.js et il ressemble à ceci :

import express from 'express';
import cron from 'node-cron';
import db, { migrate } from '@lib/server/db.js';

const isDev = process.env.ENV !== 'production';

migrate(); // Lancement de la migration du modèle de donnée dès de lancement du serveur

// Configuration d'une tâche exécuté toutes les heures
cron.schedule(
    '0 * * * *',
    async () => {
        console.log('Start task...');
        console.log(db().query('SELECT * FROM posts'));
        console.log('Task executed');
    }
);

async function createServer() {
    const app = express();

    ...

Personnellement, je trouve cela simple et minimaliste.

Point de difficulté

SvelteKit utilise des "module alias", comme par exemple $lib.
Problème, par défaut, ces "module alias" ne sont pas configurés lors de l'exécution de node src/server.js.

Pour me permettre d'importer dans src/server.js des modules de src/lib/server/* comme :

import db, { migrate } from '@lib/server/db.js';

j'ai utilisé la librairie esm-module-alias.

Ceci complexifie un peu le projet, j'ai dû configurer ceci dans /package.json :

{
    "scripts": {
        "dev": "ENV=development node --loader esm-module-alias/loader --no-warnings src/server.js",
        "preview": "ENV=production node --loader esm-module-alias/loader --no-warnings build/server.js",
        
    ...

	"aliases": {
        "@lib": "src/lib/"
    }
}
  • ajout de --loader esm-module-alias/loader --no-warnings
  • et la section aliases

Et dans /vite.config.js :

export default defineConfig({
    plugins: [sveltekit()],
    resolve: {
        alias: {
          '@lib': path.resolve('./src/lib')
        }
    }
});
  • ajout de alias

Le fichier src/server.js contient du code spécifique en fonction de son contexte d'exécution ("dev" ou "buildé") :

    if (isDev) {
        const { createServer: createViteServer } = await import('vite');

        const vite = await createViteServer({
            server: { middlewareMode: true },
            appType: 'custom'
        });

        app.use(vite.middlewares);
    } else {
        const { handler } = await import('./handler.js');
        app.use(handler);
    }

En mode "dev" il utilise Vite et en "buildé" il utilise le fichier build/handler.js généré par SvelteKit build en mode SSR.

Le fichier src/server.js est copié vers le dossier /build/ lors de l'exécution de pnpm run build.

J'ai testé le bon fonctionnement du POC dans un container Docker.

J'ai intégré au projet un deployment-playground : https://github.com/stephane-klein/poc-sveltekit-custom-server/tree/main/deployment-playground.

La suite...

Je souhaite rédiger cette note en anglais et la publier sur https://github.com/sveltejs/kit/discussions et https://old.reddit.com/r/sveltejs/ afin :

  • d'avoir des retours d'expérience
  • de découvrir des méthodes alternatives
  • et partager la méthode que j'ai utilisée, qui sera peut-être utile à d'autres développeurs Svelte 🙂

Update du 2025-05-29 à 00:07 - Je viens de publier ceci :


2025-05-29 : voir J'ai découvert la fonctionnalité SvelteKit Shared hooks init

Journal du jeudi 10 avril 2025 à 08:44 #WebDev, #SvelteKit, #NextJS, #selfhosting, #JaiDécouvert, #JaimeraisUnJour

#JaiDécouvert ici la fonctionnalité "Incremental Static Regeneration (ISR)" de NextJS.

L'Incremental Static Regeneration (ISR) est un mélange de génération static et de régénération dynamique.

Lors du build du site toutes les pages sont générées de manière statique. Cependant, certaines peuvent être "marquées" : ces pages clairement identifiées seront régénérées à intervalle régulier après le déploiement du site, faisant appel à des API ou une base de données pour garder la donnée à jour.

Lors de la visite d'une page à régénérer, une version "ancienne" de la page s'affiche, mais une demande de régénération est envoyée au serveur. La page est ainsi régénérée et renvoyée instantanément au visiteur, et prête à être affichée au visiteur suivant. Le cycle peut alors recommencer.

source

J'ai aussi lu cette page de documentation de Vercel :

Incremental Static Regeneration (ISR) allows you to create or update content on your site without redeploying. ISR's main benefits for developers include:

  • Better Performance: Static pages can be consistently fast because ISR allows Vercel to cache generated pages in every region on our global Edge Network and persist files into durable storage
  • Reduced Backend Load: ISR helps reduce backend load by using cached content to make fewer requests to your data sources
  • Faster Builds: Pages can be generated when requested by a visitor or through an API instead of during the build, speeding up build times as your application grows

ISR is available to applications built with:

source

J'ai étudié le support ISR de SvelteKit. Il semble que cette fonctionnalité soit supportée uniquement par l'adapter-vercel.
J'ai identifié l'issue suivante : Would revalidating a static page work when self-hosted?.

#JaimeraisUnJour prendre le temps de creuser plus en profondeur ce sujet.

Journal du mercredi 29 janvier 2025 à 11:55 #serveur-http, #Nuxt, #javascript, #JaiDécouvert, #JeMeDemande

#JaiDécouvert Nitro (https://nitro.build/)

Next Generation Server Toolkit.

Create web servers with everything you need and deploy them wherever you prefer.

source

D'après ce que j'ai compris, Nitro est un serveur http en NodeJS qui a été spécialement conçu pour Nuxt.js.

Nitro a été introduit fin 2021 dans la version 3 de Nuxt.

Nitro fait partie de l'écosystème UnJS.

Je découvre l'existence de UnJS Ecosystem. #JeMeDemande si ce projet a un lien avec VoidZero 🤔.

Je viens de vérifier, SvelteKit ne semble pas utiliser Nitro. Nitro semble être utilisé uniquement par Nuxt.js.

Journal du mardi 15 octobre 2024 à 16:02 #JaiLu, #JaiDécouvert

En étudiant l'annonce de Brief.me j'ai un peu étudié Capacitor :

Build cross-platform Native Progressive Web Apps for iOS, Android, and the Web ⚡️ .

Le projet Capacitor a commencé fin 2017 et d'après ce que j'ai lu, ce projet est la "suite" d' Apache Cordova anciennement nommé PhoneGap.

Je me souviens d'avoir utilisé PhoneGap vers 2010.

Je découvre que je peux utiliser Svelte avec Capacitor : Svelte & Capacitor - Build native mobile apps with web technology and Svelte.

Il est même possible d'utiliser SvelteKit en mode SSG (@sveltejs/adapter-static) : https://ionic.io/blog/cross-platform-sveltekit-capacitor-application-yes-its-possible.

J'ai parcouru ce retour d'expérience : How I published a gratitude journaling app for iOS and Android using SvelteKit and Capacitor.

#JaiDécouvert ce template svelte-capacitor.

#JaiDécouvert whatpwacando.today.

#JaiLu SvelteKit + Capacitor Performance Example


J'ai souvent entendu parler d'Ionic par le passé, mais je n'avais jamais pris le temps de m'y pencher sérieusement. Je pensais que Ionic était un équivalent de React Native, mais j'avais tort. En réalité, Ionic est un UI Toolkit.

Pourquoi faire un refactoring de Nuxt.js vers HTMX ? 🤔 #WebDev, #htmx, #django, #Nuxt, #SvelteKit

En étudiant l'annonce Développeur(se) back-end en CDI de Brief.me j'ai été intrigué par :

Migrer notre front-end existant de Nuxt.js vers HTMX.

Je me suis demandé quelle est la motivation de passer d'un framework de type Nuxt.js, NextJS ou SvelteKit à htmx 🤔.

J'utilise SvelteKit depuis deux ans, qui est comparable Nuxt.js, principalement en mode SSR avec Hydration, et je suis totalement satisfait. Ma Developer eXperience est excellente. Je trouve ce framework minimaliste, conforme au principe Keep it simple, stupid! (KISS), et performant.

Après réflexion, j'ai réalisé que mon expérience de développeur (DX) serait moindre si j'héritais d'un backend codé dans un langage autre que Javascript : Python, PHP, Ruby, Golang

Et Brief.me semble être dans ce cas de figure :

Je me suis connecté à mon compte Brief.me et en regardant ce que me retourne Wappalyzer, je constate que le site est effectivement propulsé par Nuxt.js, VueJS.

En regardant ce qu'il se passe dans l'onglet Réseau de mon navigateur, je constate que https://app.brief.me est un site web de type SPA. Le contenu des articles est chargé via l'api https://www.brief.me/api/ qui est propulsée par Django REST framework.

Si je résume, la stack est la suivante : PostgreSQL => Django => Django REST framework <=> Nuxt.js => Rendu HTML via VueJS.

Je suppose que ce qui motive la migration de Nuxt.js vers HTMX est la suppression de couches.
Plus précisément, je suppose que l'équipe tech de Brief.me souhaite supprimer les couches suivantes :

Et utiliser simplement le système de template de Django en SSR pour afficher le contenu des articles et implémenter les quelques éléments dynamiques côté browser avec HTMX.
De mon point de vue, ceci a pour avantage de largement simplifier la stack, de simplifier le déploiement et d'accélérer le chargement des pages.

Ma conclusion : la librairie HTMX semble être un choix très pertinent quand elle est utilisée dans une stack non NodeJS.

Journal du vendredi 04 octobre 2024 à 21:18 #WebDev, #SvelteKit

Je suis triste de constater que SvelteKit ne propose toujours pas de hooks pour lancer une fonction au démarrage du serveur 😔, voir l'issue Expose a way to inject a start script into adapter-node.

Journal du mercredi 02 octobre 2024 à 18:07 #iteration

Nouvelle #iteration du Projet 11 - "Première version d'un moteur web PKM".

J'ai traité les tâches décrites dans ma dernière note.

  • Comme me l'a signalé à plusieurs reprises Alexandre, je dois améliorer le rendu responsive sur smartphone. Jusqu'à présent, je n'ai pas encore consacré de temps à ce sujet.
  • Je dois améliorer le script d'import des données dans Elasticsearch. Pour le moment, ici, je commence par supprimer toutes les données avant d'effectuer l'importation des données.
    Problème : les pages ne sont plus accessibles pendant l'exécution de ce script.

J'ai enfin publié sklein-pkm-engine sur https://notes.sklein.xyz.

En mars 2024, j'écrivais :

Pour le moment, j'utilise Obsidian Quartz pour déployer https://notes.sklein.xyz.

Est-ce que j'en suis satisfait ? Pour le moment, la réponse est non, parce que je ne le maitrise pas assez.

J'ai une grande envie d'implémenter une version personnelle basée sur SvelteKit et Apache Age, mais j'essaie de ne pas tomber dans ce Yak!.

Début mai 2024, je suis tombé dans ce Yak!, j'y ai consacré 93 heures en tout, soit l'équivalent d'environ 15 jours de travail étalés sur 8 semaines.

J'ai enfin supprimé Obsidian Quartz

J'ai changé plusieurs fois de direction :

Je viens d'essayer de réaliser un screencast de présentation de la version actuelle de sklein-pkm-engine, mais le résultat de mon discours était vraiment trop déstructuré pour être publié. J'essaierai de publier un screencast prochainement.

Je viens de tenter de réaliser un screencast pour présenter la version actuelle de sklein-pkm-engine, mais mon discours était trop désorganisé pour être publié. Je souhaite enregistrer une nouvelle version prochainement.

Prochains objectifs concernant le projet sklein-pkm-engine :

Journal du mercredi 11 septembre 2024 à 11:14 #projet, #javascript, #playground, #JeMeDemande

Dans la branche gibbon-replay-js du projet Idée d'un outil de session recoding web minimaliste basé sur rrweb, j'ai essayé sans succès d'extraire du code dans un package Javascript.

Pour le moment l'import suivant ne fonctionne pas :

import gibbonReplayJs from 'gibbon-replay-js';

Quand je lance pnpm run build, j'ai l'erreur suivante :

$ pnpm run build
...
x Build failed in 336ms
error during build:
src/routes/(record)/+layout.svelte (2:11): "default" is not exported by "packages/gibbon-replay-js/dist/index.js", imported by "src/routes/(record)/+layout.svelte".
file: /home/stephane/git/github.com/stephane-klein/gibbon-replay-poc/src/routes/(record)/+layout.svelte:2:11

1: <script>
2:     import gibbonReplayJs from 'gibbon-replay-js';

Et quand je lance pnpm run dev, j'ai l'erreur suivante :

$ pnpm run dev

...

11:21:21 [vite] Error when evaluating SSR module /packages/gibbon-replay-js/dist/index.js:
|- ReferenceError: exports is not defined
    at eval (/home/stephane/git/github.com/stephane-klein/gibbon-replay-poc/packages/gibbon-replay-js/dist/index.js:5:23)
    at instantiateModule (file:///home/stephane/git/github.com/stephane-klein/gibbon-replay-poc/node_modules/.pnpm/vite@5.4.3/node_modules/vite/dist/node/chunks/dep-BaOMuo4I.js:52904:11)

11:21:21 [vite] Error when evaluating SSR module /src/routes/(record)/+layout.svelte:
|- ReferenceError: exports is not defined
    at eval (/home/stephane/git/github.com/stephane-klein/gibbon-replay-poc/packages/gibbon-replay-js/dist/index.js:5:23)
    at instantiateModule (file:///home/stephane/git/github.com/stephane-klein/gibbon-replay-poc/node_modules/.pnpm/vite@5.4.3/node_modules/vite/dist/node/chunks/dep-BaOMuo4I.js:52904:11)

Suite à cette frustration, j'ai envie de créer un projet, sans doute nommé javascript-package-playground dans lequel je souhaite étudier les sujets suivants :

  • mise en place d'une librairie /packages/lib1/ qui contient une librairie javascript, qui peut être importé avec la méthode ECMAScript Modules ;
  • mise en place d'une app NodeJS dans /services/app1_nodejs/ qui utilise lib1 ;
  • mise en place d'une app SvelteKit dans /services/app2_sveltekit/ qui utilise lib1 dans un fichier coté server et dans une page web coté browser ;
  • mise en place d'une librairie /packages/lib2 qui utilise lib1

Je souhaite décliner ces 2 libs et 2 apps sous plusieurs déclinaisons d'implémentation :

Et le tout encore dans deux déclinaisons : Javascript et TypeScript.

Je ne souhaite pas supporter CommonJS qui est sur le déclin, remplacé par ECMAScript Modules.

Dans ce playground, je souhaite aussi me perfectionner dans l'usage de pnpm link et pnpm workspace.

#JeMeDemande si ces connaissances sont totalement maitrisées et évidentes chez mes amis développeurs Javascript 🤔 et s'ils les considèrent comme "basiques".

Journal du dimanche 08 septembre 2024 à 10:18 #POC, #rrweb, #SvelteKit

J'ai envie d'essayer de créer un "mini" service de session recording, basé sur rrweb, SvelteKit et KeyDB ou DragonflyDB.
Je pense que ce projet pourrait être minimaliste 🤔.

-- from

J'ai publié https://github.com/stephane-klein/gibbon-replay (gibbon-replay).

J'ai passé 2h sur ce projet.