MSW στο SvelteKit

Πώς να εφαρμόσετε MSW για τοπική ανάπτυξη στο SvelteKit

Χρήση MSW στο SvelteKit

Αυτό είναι ένα σεμινάριο που θα σας δείξει πώς να εφαρμόσετε τη βιβλιοθήκη "Mock Service Worker", που ονομάζεται MSW, στην εφαρμογή SvelteKit. Λάβετε υπόψη ότι αυτός ο οδηγός δεν δείχνει πώς να ρυθμίσετε το MSW με το Jest, αλλά μάλλον πώς να χρησιμοποιήσετε το MSW κατά τη ζωντανή ανάπτυξη.

Η πλήρης υλοποίηση είναι διαθέσιμη στο αποθετήριο του spikze.club, σύνδεσμος επίσης στο παράρτημα στο τέλος της σελίδας.

Μην ανησυχείτε αν αυτός ο οδηγός μπορεί να φαίνεται συντριπτικός στην αρχή, οι αλλαγές είναι αρκετά απλές. Η χρήση MSW στο SvelteKit για την κοροϊδία των αιτημάτων κατά την τοπική ανάπτυξη απαιτεί απλώς λίγη περισσότερη προσπάθεια.

Εγκατάσταση εξαρτήσεων

Αρχικά, ας εγκαταστήσουμε όλες τις απαραίτητες εξαρτήσεις.

npm i -D msw ts-node concurrently

Χρησιμοποιούμε το "ts-node" για να ξεκινήσουμε επίσης τα mocks του διακομιστή. Πριν προχωρήσουμε, καλέστε την ακόλουθη εντολή στη ρίζα του έργου SvelteKit.

npx msw init ./static

Αυτό θα δημιουργήσει ένα αρχείο Javascript με τον προεπιλεγμένο χειριστή service worker από το MSW. Χρησιμοποιούμε "στατικό" για την εφαρμογή SvelteKit, καθώς είναι το ισοδύναμο με τον δημόσιο κατάλογο. Τέλος, ας ενημερώσουμε το αρχείο package.json, ώστε το MSW να γνωρίζει πού να αναζητήσει το αρχείο.

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

Ως βολικό σενάριο, πρόσθεσα επίσης την ακόλουθη εντολή στη διαμόρφωση "scripts" μου, η οποία είναι απαραίτητη εάν θέλετε να ξεκινήσετε το MSW τόσο μέσω πελάτη όσο και μέσω διακομιστή (για κοροϊδίες από την πλευρά του διακομιστή).

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

Προετοιμασία μονάδων MSW

Πριν αρχίσουμε να γράφουμε τον πραγματικό κώδικα, πρέπει να ορίσουμε τον νέο μας κατάλογο εργασίας για MSW στη διαμόρφωση του SvelteKit καθώς και στο Typescript-config. Θα ξεκινήσουμε με το αρχείο Typescript.

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

Στη συνέχεια, ας ενημερώσουμε το 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")
        }
      }
    }
  }

Προσθέτοντας τις κοροϊδίες και τους χειριστές

Εντάξει, περάσατε από το πρώτο μέρος του οδηγού! Τώρα θα προσθέσουμε τον πραγματικό κώδικα που εκτελείται κατά τη χρήση MSW. Λάβετε υπόψη ότι αυτό είναι ένα ελάχιστο παράδειγμα, επομένως πρόσθεσα ό,τι είναι απαραίτητο για να λειτουργήσει όλο αυτό, αλλά όχι περισσότερο.

Όπως ίσως μαντέψατε από τις αλλαγές μας στο SvelteKit-config, όλος ο κώδικας θα τοποθετηθεί σε έναν νέο κατάλογο που ονομάζεται "msw", ο οποίος βρίσκεται απευθείας στο "src".

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

Εδώ είναι ο κώδικας για τις επόμενες ενότητες που θα προσθέσετε. Θα πρέπει να μπορείτε απλώς να τα κάνετε αντιγραφή-επικόλληση, το όνομα αρχείου + διαδρομή καταλόγου καταγράφεται ως σχόλιο σε κάθε μπλοκ στο επάνω μέρος.

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

Έναρξη MSW

Μια πρόκληση κατά τη χρήση MSW κατά τη διάρκεια της ζωντανής ανάπτυξης είναι ότι πρέπει να βεβαιωθούμε ότι δεν υπάρχει συνθήκη αγώνα. Πρέπει να ορίσουμε μια σειρά εκτέλεσης, διαφορετικά ο εργάτης σέρβις από MSW μπορεί να γίνει ενεργός αφού έχουν ήδη υποβληθεί όλα τα αιτήματα.

Για να επιτύχουμε αυτόν τον στόχο, τροποποιούμε το ριζικό μας layout-αρχείο. Καθώς αυτό το αρχείο είναι προσαρτημένο για κάθε σελίδα, είναι ένα καλό μέρος για να αποκλείσετε κάθε περαιτέρω εκτέλεση μέχρι να ολοκληρωθεί το 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}

Προσθήκη δοκιμαστικής σελίδας

Τα παρακάτω αποσπάσματα κώδικα απλώς δείχνουν τα περιεχόμενα για δύο σελίδες επίδειξης και το ένα τελικό σημείο που χρησιμοποιείται σε αυτό το σεμινάριο.

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

Προσθήκη μεταβλητής περιβάλλοντος

Έχουμε σχεδόν τελειώσει. Αυτό που λείπει είναι να προσθέσουμε τη σημαία "VITE_MSW_ENABLED" στο περιβάλλον μας. Χρησιμοποιούμε το ".env.local" ως αρχείο μας για να κρατήσουμε τη σημαία, καθώς θα καταναλωθεί από το Vite και δεν θα προστεθεί στο git.

VITE_MSW_ENABLED=true

Εκτέλεση της εφαρμογής

Εντάξει, όλα θα πρέπει να είναι έτοιμα για χρήση τώρα! Για να ενεργοποιήσετε την κοροϊδία στον πελάτη, απλώς βεβαιωθείτε ότι έχει οριστεί η σημαία. Εκτελέστε την κοινή εντολή "dev" και στη συνέχεια για να κοροϊδέψετε αιτήματα από την πλευρά του πελάτη.

npm run dev

Για να προσποιηθείτε επίσης αιτήματα από την πλευρά του διακομιστή, απλώς εκτελέστε τη νέα εντολή που προσθέσαμε στην αρχή.

npm run dev:msw-server

Τώρα είστε έτοιμοι να κοροϊδέψετε τα τελικά σημεία κατά την τοπική ανάπτυξη. Όπως σημειώθηκε στο παράδειγμα κώδικα, αυτή η δοκιμαστική σελίδα δεν περιλαμβάνει μακέτες από την πλευρά του διακομιστή, αν και όλα είναι προετοιμασμένα για αυτό. Αυτό σημαίνει ότι τοπικά, η κλήση MSW ενεργοποιείται μόνο εάν πλοηγηθείτε από τη σελίδα ευρετηρίου της τοποθεσίας επίδειξης στην πραγματική δοκιμαστική σελίδα κάνοντας κλικ στον σύνδεσμο.

Για την προσομοίωση αιτημάτων διακομιστή, θα έχετε πιο περίπλοκα τελικά σημεία που λαμβάνουν επίσης δεδομένα από βάσεις δεδομένων. Αυτά τα αιτήματα μπορούν στη συνέχεια να κοροϊδευτούν στο διακομιστή-χειριστή. Σημειώστε ότι για τα διακομιστές-mocks, μόνο απόλυτες διευθύνσεις URL είναι έγκυρες.