RSU in SvelteKit

Come implementare RSU per lo sviluppo locale in SvelteKit

Utilizzo di RSU in SvelteKit

Questo è un tutorial che ti mostrerà come implementare la libreria "Mock Service Worker", chiamata MSW, nella tua applicazione SvelteKit. Tieni presente che questa guida non mostra come configurare MSW con Jest, ma piuttosto come utilizzare MSW durante lo sviluppo live.

L'implementazione completa è disponibile nel repository di spikze.club, link anche nell'addendum a fine pagina.

Non preoccuparti se questa guida potrebbe sembrare travolgente all'inizio, le modifiche sono abbastanza semplici. L'uso di MSW in SvelteKit per simulare le richieste durante lo sviluppo locale richiede solo un piccolo sforzo in più.

Installazione delle dipendenze

Innanzitutto, installiamo tutte le dipendenze necessarie.

npm i -D msw ts-node concurrently

Stiamo usando "ts-node" anche per avviare i server-mock. Prima di procedere, chiama il seguente comando nella radice del tuo progetto SvelteKit.

npx msw init ./static

Questo genererà un file Javascript con il gestore di lavoro del servizio predefinito da MSW. Stiamo usando "statico" per la nostra app SvelteKit, poiché è l'equivalente della directory pubblica. Infine, aggiorniamo il file package.json in modo che MSW sappia dove cercare il file.

{
  "scripts": ...
  ...
  "msw": {
    "workerDirectory": "static"
  }
}

Come script conveniente, ho anche aggiunto il seguente comando alla mia configurazione "script" che è necessaria se si desidera avviare MSW sia tramite client che server (per simulazioni lato server).

{
  "scripts": {
    "dev": "svelte-kit dev",
    "dev:msw-server": "concurrently \"cd msw && ts-node server.ts\" \"npm run dev\"",
    ...
  },
}

Preparazione dei moduli RSU

Prima di iniziare a scrivere il codice vero e proprio, dobbiamo definire la nostra nuova directory di lavoro per MSW nella configurazione di SvelteKit e in Typescript-config. Inizieremo con il file Typescript.

{
  "compilerOptions": {
    ...,
    "paths": {
      "$lib": ["src/lib"],
      "$lib/*": ["src/lib/*"],
      "$msw": ["src/msw"],
      "$msw/*": ["src/msw/*"]
    }
  }
}

Quindi, aggiorniamo SvelteKit-config.

import adapter from "@sveltejs/adapter-auto";
import preprocess from "svelte-preprocess";
import path from "path";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Consult https://github.com/sveltejs/svelte-preprocess
  // for more information about preprocessors
  preprocess: preprocess(),
  kit: {
    adapter: adapter(),
    vite: {
      ...,
      resolve: {
        alias: {
          $msw: path.resolve("./src/msw"),
          $lib: path.resolve("./src/lib")
        }
      }
    }
  }

Aggiunta delle derisioni e dei gestori

Bene, hai superato la prima parte della guida! Ora aggiungeremo il codice effettivo che viene eseguito quando si utilizza MSW. Tieni presente che questo è un esempio minimo, quindi ho aggiunto ciò che è necessario per far funzionare tutto, ma non di più.

Come avrai intuito dalle nostre modifiche allo SvelteKit-config, tutto il codice verrà inserito in una nuova directory chiamata "msw", che risiede direttamente in "src".

|- app/
|-- src/
|--- pages/
|--- msw/
|---- fixtures/
...

Ecco il codice per i prossimi moduli da aggiungere. Dovresti essere in grado di copiarli e incollarli semplicemente, il nome del file + il percorso della directory viene annotato come commento in ogni blocco in alto.

//
// app/src/msw/handlers.server.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

export const handlers = [
  // Here, you can mock absolute URL requests,
  // e.g. to a database. For the current implementation,
  // no data is mocked in this place.
  //
  // Note: This is also the place to mock absolute
  // SSR-imports. Everything in 'handlers.workers.ts'
  // is mocked client-side.
];
//
// app/src/msw/handlers.worker.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

// Mock relative URLs that map to your
// routes' data endpoints. This mock only
// happens for client-side requests.
//
// Note that if you use shadow endpoints, this still works
// as the endpoint gets created by SvelteKit.
export const handlers = [
  rest.get("/msw/demo/__data.json", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json({ values }));
  })
];
//
// app/src/msw/server.ts
//

import { setupServer } from "msw/node";
import { handlers } from "./handlers.server";

export const server = setupServer(...handlers);
//
// app/src/msw/worker.ts 
//

import { setupWorker } from "msw";
import { handlers } from "./handlers.worker";

export const worker = setupWorker(...handlers);
//
// app/src/msw/fixtures/msw-demo.ts
//

export const values = ["foo", "bar"];
//
// app/src/msw/index.ts 
//

import { browser, dev } from "$app/env";

/**
 * Lazy-inject the MSW handler
 * so that no errors happen during
 * build/runtime due to invalid
 * imports from server/client.
 */
export async function inject() {
  if (dev && browser) {
    const { worker } = await import("../msw/worker");
    // For live development, I disabled all warnings
    // for requests that are not mocked. Change how
    // you think it best fits your project.
    return worker.start({ onUnhandledRequest: "bypass" }).catch(console.warn);
  }
  if (dev && !browser) {
    const { server } = await import("../msw/server");
    // Same as in worker-mock above.
    return server.listen({ onUnhandledRequest: "bypass" });
  }
}

Inizio RSU

Una sfida quando si utilizza MSW durante lo sviluppo live è che dobbiamo assicurarci che non ci siano condizioni di razza. Dobbiamo definire un ordine di esecuzione, altrimenti il service worker di MSW potrebbe diventare attivo dopo che tutte le richieste sono già state fatte.

Per raggiungere questo obiettivo, modifichiamo il nostro file di layout principale. Poiché questo file viene montato per ogni pagina, è un buon posto per bloccare tutte le ulteriori esecuzioni fino al termine di MSW.

<script>
  import "../app.css";
  import { dev } from "$app/env";
  
  // Loaded from .env.local, guide covers this
  // step in a moment.
  const isMswEnabled = dev && import.meta.env.VITE_MSW_ENABLED === "true";
  // Flag to defer rendering of components
  // until certain criteria are met on dev,
  // e.g. MSW init.
  let isReady = !isMswEnabled;
  
  if (isMswEnabled) {
    import("$msw")
      .then((res) => res.inject())
      .then(() => (isReady = true));
  }
</script>

{#if isReady}
  <slot />
{/if}

Aggiunta di una pagina demo

I frammenti di codice seguenti mostrano semplicemente il contenuto di due pagine demo e l'endpoint utilizzato in questo tutorial.

<!-- app/src/routes/msw/index.svelte -->

<script>
  import DisplayProse from "$lib/display/views/DisplayProse.svelte";
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
</script>

<PageLayout>
  <SectionLayout withContentTopSpacing withHeaderSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Landing Page</ProminentDisplayTitle>
    <DisplayProse>
      <p>
        This compoonent has no purpose other than being part of an MSW demo implementation. See <a
          href="https://flaming.codes"
          alt="Link to flaming.codes with blog posts">flaming.codes</a
        > for more details.
      </p>
      <p>
        This page doesn't fetch any data for shows how client-side fetches are mocked with MSW in
        SvelteKit.
      </p>
      <p>Simply click the link below to access the page with data.</p>
      <p>
        <a href="/msw/demo" alt="Link to demo page with data">msw/demo</a>
      </p>
    </DisplayProse>
  </SectionLayout>
</PageLayout>
<!-- app/src/routes/msw/demo.svelte -->

<script lang="ts">
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
  export let values: string[];
</script>

<PageLayout>
  <SectionLayout withHeaderSpacing withContentTopSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Demo</ProminentDisplayTitle>
    <p>
      This compoonent has no purpose other than being part of an MSW demo implementation. See <a
        href="https://flaming.codes"
        alt="Link to flaming.codes with blog posts">flaming.codes</a
      > for more details.
    </p>
    <p>
      Values: {values}
    </p>
  </SectionLayout>
</PageLayout>
//
// app/src/routes/msw/demo.ts
//

import type { RequestHandler } from "@sveltejs/kit";

// Just for demo purposes.
export const get: RequestHandler = async () => ({
  status: 200,
  body: {
    values: ["production", "data", "not", "msw"]
  }
});

Aggiunta di una variabile di ambiente

Abbiamo quasi finito. Quello che manca è aggiungere il flag "VITE_MSW_ENABLED" al nostro ambiente. Stiamo usando ".env.local" come file per mantenere il flag, poiché verrà consumato da Vite e non aggiunto a git.

VITE_MSW_ENABLED=true

Esecuzione dell'applicazione

Bene, ora dovrebbe essere tutto pronto per l'uso! Per abilitare la presa in giro sul client, assicurati semplicemente che il flag sia impostato. Esegui il comando comune "dev" quindi per deridere le richieste lato client.

npm run dev

Per prendere in giro anche le richieste lato server, esegui semplicemente il nuovo comando che abbiamo aggiunto all'inizio.

npm run dev:msw-server

Ora sei pronto per deridere gli endpoint durante lo sviluppo locale. Come notato nell'esempio di codice, questa pagina demo non include mock lato server, anche se tutto è pronto per questo. Ciò significa che localmente, la chiamata MSW viene attivata solo se si naviga dalla pagina indice del sito demo alla pagina demo effettiva facendo clic sul collegamento.

Per simulare le richieste del server, avrai endpoint più complicati che recuperano anche i dati dai database. Tali richieste possono quindi essere derise nel gestore del server. Nota che per i server-mock sono validi solo gli URL assoluti.