RSU en SvelteKit

Cómo implementar RSU para el desarrollo local en SvelteKit

Uso de MSW en SvelteKit

Este es un tutorial que le mostrará cómo implementar la biblioteca "Mock Service Worker", caleld MSW, en su aplicación SvelteKit. Tenga en cuenta que esta guía no muestra cómo configurar MSW con Jest, sino cómo usar MSW durante el desarrollo en vivo.

La implementación completa está disponible en el repositorio de spikze.club, enlace también en el apéndice al final de la página.

No se preocupe si esta guía puede parecer abrumadora al principio, los cambios son bastante simples. Usar MSW en SvelteKit para simular solicitudes durante el desarrollo local solo requiere un poco más de esfuerzo.

Instalación de dependencias

Primero, instalemos todas las dependencias necesarias.

npm i -D msw ts-node concurrently

Estamos usando "ts-node" para iniciar también los simulacros de servidor. Antes de continuar, llame al siguiente comando en la raíz de su proyecto SvelteKit.

npx msw init ./static

Esto generará un archivo Javascript con el controlador de trabajador de servicio predeterminado de MSW. Estamos usando "estático" para nuestra aplicación SvelteKit, ya que es el equivalente al directorio público. Finalmente, actualicemos el archivo package.json para que MSW sepa dónde buscar el archivo.

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

Como un script conveniente, también agregué el siguiente comando a mi configuración de "scripts", que es necesario si desea iniciar MSW tanto a través del cliente como del servidor (para simulacros del lado del servidor).

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

Preparación de módulos MSW

Antes de comenzar a escribir el código real, debemos definir nuestro nuevo directorio de trabajo para MSW en la configuración de SvelteKit, así como en Typescript-config. Comenzaremos con el archivo TypeScript.

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

A continuación, actualicemos 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")
        }
      }
    }
  }

Agregar los simulacros y los controladores

¡Muy bien, has superado la primera parte de la guía! Ahora agregaremos el código real que se ejecuta cuando se usa MSW. Tenga en cuenta que este es un ejemplo mínimo, por lo tanto, agregué lo necesario para que todo funcione, pero no más.

Como habrás adivinado por nuestros cambios en SvelteKit-config, todo el código se colocará en un nuevo directorio llamado "msw", que se encuentra directamente en "src".

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

Aquí está el código para agregar los próximos módulos. Debería poder simplemente copiarlos y pegarlos, el nombre del archivo + la ruta del directorio está escrito como un comentario en cada bloque en la parte superior.

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

Iniciando RSU

Un desafío al usar MSW durante el desarrollo en vivo es que debemos asegurarnos de que no haya una condición de carrera. Tenemos que definir un orden de ejecución, de lo contrario, el trabajador de servicio de MSW podría activarse después de que se hayan realizado todas las solicitudes.

Para lograr este objetivo, modificamos nuestro archivo de diseño raíz. Como este archivo se monta para cada página, es un buen lugar para bloquear toda ejecución posterior hasta que MSW haya terminado.

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

Agregar página de demostración

Los siguientes fragmentos de código simplemente muestran el contenido de dos páginas de demostración y el punto final utilizado en este 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"]
  }
});

Agregar una variable de entorno

Ya casi hemos terminado. Lo que falta es agregar el indicador "VITE_MSW_ENABLED" a nuestro entorno. Estamos usando ".env.local" como nuestro archivo para sostener la bandera, ya que Vite lo consumirá y no se agregará a git.

VITE_MSW_ENABLED=true

Ejecutando la aplicación

Muy bien, ¡todo debería estar listo para usar ahora! Para habilitar la burla en el cliente, simplemente asegúrese de que la bandera esté configurada. Ejecute el comando común "dev" para simular las solicitudes del lado del cliente.

npm run dev

Para simular también las solicitudes del lado del servidor, simplemente ejecute el nuevo comando que agregamos al principio.

npm run dev:msw-server

Ahora está listo para simular terminales durante el desarrollo local. Como se indica en el ejemplo de código, esta página de demostración no incluye simulacros del lado del servidor, aunque todo está preparado para ello. Esto significa que localmente, la llamada MSW solo se activa si navega desde la página de índice del sitio de demostración a la página de demostración real haciendo clic en el enlace.

Para simular solicitudes de servidor, tendrá puntos finales más complicados que también obtienen datos de bases de datos. Esas solicitudes se pueden simular en el controlador del servidor. Tenga en cuenta que para los simulacros de servidor, solo las URL absolutas son válidas.