MSW in SvelteKit

Hoe om MSW vir plaaslike ontwikkeling in SvelteKit te implementeer

Gebruik MSW in SvelteKit

Hierdie is 'n tutoriaal wat jou sal wys hoe om die "Mock Service Worker"-biblioteek, genaamd MSW, in jou SvelteKit-toepassing te implementeer. Neem asseblief kennis dat hierdie gids nie wys hoe om MSW met Jest op te stel nie, maar eerder hoe om MSW tydens lewendige ontwikkeling te gebruik.

Die volledige implementering is beskikbaar in die bewaarplek van spikze.club, skakel ook in die addendum aan die einde van die bladsy.

Moenie bekommerd wees as hierdie gids aanvanklik oorweldigend kan lyk nie, die veranderinge is redelik eenvoudig. Om MSW in SvelteKit te gebruik om versoeke tydens plaaslike ontwikkeling te bespot, verg net 'n bietjie meer moeite.

Installeer afhanklikhede

Laat ons eers alle nodige afhanklikhede installeer.

npm i -D msw ts-node concurrently

Ons gebruik "ts-node" om ook die bedienerspots te begin. Voordat ons voortgaan, roep asseblief die volgende opdrag in die wortel van jou SvelteKit-projek.

npx msw init ./static

Dit sal 'n Javascript-lêer genereer met die verstek dienswerker hanteerder deur MSW. Ons gebruik "staties" vir ons SvelteKit-toepassing, aangesien dit gelykstaande is aan die publieke gids. Laastens, laat ons die package.json-lêer opdateer sodat MSW weet waar om die lêer te soek.

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

As 'n gerieflike skrif, het ek ook die volgende opdrag by my "scripts"-konfigurasie gevoeg wat nodig is as jy MSW beide via kliënt sowel as bediener wil begin (vir bediener-kant bespottings).

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

Voorbereiding van MSW-modules

Voordat ons werklike kode begin skryf, moet ons ons nuwe werkgids vir MSW definieer in SvelteKit se config sowel as Typescript-config. Ons begin met die Typescript-lêer.

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

Kom ons werk dan die SvelteKit-config op.

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")
        }
      }
    }
  }

Voeg die spotters en hanteerders by

Goed, jy het dit deur die eerste deel van die gids gemaak! Nou sal ons die werklike kode byvoeg wat uitgevoer word wanneer MSW gebruik word. Neem asseblief kennis dat dit 'n minimale voorbeeld is, daarom het ek bygevoeg wat nodig is om dit alles te laat werk, maar nie meer nie.

Soos u dalk geraai het uit ons veranderinge aan die SvelteKit-config, sal alle kode in 'n nuwe gids genaamd "msw" geplaas word, wat direk in "src" woon.

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

Hier is die kode vir die volgende modules om by te voeg. Jy behoort hulle eenvoudig te kan kopieer-plak, die lêernaam + gidspad word as 'n opmerking in elke blokkie aan die bokant neergeskryf.

//
// 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" });
  }
}

Begin MSW

Een uitdaging wanneer ons MSW tydens lewendige ontwikkeling gebruik, is dat ons moet seker maak dat daar geen rastoestand is nie. Ons moet 'n uitvoeringsbevel definieer, anders kan die dienswerker van MSW aktief word nadat alle versoeke reeds gemaak is.

Om hierdie doel te bereik, wysig ons ons worteluitleg-lêer. Aangesien hierdie lêer vir elke bladsy gemonteer is, is dit 'n goeie plek om alle verdere uitvoering te blokkeer totdat MSW klaar is.

<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}

Voeg demo-bladsy by

Die volgende kodebrokkies wys bloot inhoud vir twee demonstrasiebladsye en die een eindpunt wat in hierdie tutoriaal gebruik word.

<!-- 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"]
  }
});

Voeg 'n omgewingsveranderlike by

Ons is amper klaar. Wat ontbreek, is om die "VITE_MSW_ENABLED"-vlag by ons omgewing te voeg. Ons gebruik ".env.local" as ons lêer om die vlag te hou, aangesien dit deur Vite verbruik sal word en nie by git gevoeg word nie.

VITE_MSW_ENABLED=true

Begin die toepassing

Goed, alles behoort nou gereed te wees om te gebruik! Om die bespotting van die kliënt moontlik te maak, maak net seker dat die vlag gestel is. Voer die algemene "dev"-opdrag uit om versoeke aan die kliëntkant te bespot.

npm run dev

Om ook versoeke aan die bedienerkant te bespot, voer eenvoudig die nuwe opdrag uit wat ons aan die begin bygevoeg het.

npm run dev:msw-server

Nou is jy gereed om eindpunte tydens plaaslike ontwikkeling te spot. Soos aangedui in die kodevoorbeeld, bevat hierdie demo-bladsy nie bediener-kant-spots nie, al is alles daarvoor voorberei. Dit beteken dat die MSW-oproep plaaslik slegs geaktiveer word as jy van die indeksbladsy van die demo-werf na die werklike demo-bladsy navigeer deur op die skakel te klik.

Om bedienerversoeke te simuleer, sal jy meer ingewikkelde eindpunte hê wat ook data van databasisse af haal. Daardie versoeke kan dan in die bedienerhanteerder bespot word. Let daarop dat slegs absolute URL's vir die bedienerspotters geldig is.