daKmoR/rocket

Error while building a page with many details and code blocks

LarsDenBakker opened this issue · 1 comments

I have a page with a lot of details elements and code snippets, eleventy is throwing an error when building this file. I tried to debug it, and couldn't find a clear cause yet. Below is an example that fails for me in moder-web.

---
title: Writing plugins
eleventyNavigation:
  key: Writing plugins
  parent: Plugins
  order: 6
---

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    serverStart() {
      setTimeout(() => {}, 1000);
    },
  };
}
```

</details>

This is a regular page where this happens:

---
title: Writing plugins
eleventyNavigation:
  key: Writing plugins
  parent: Plugins
  order: 6
---

A plugin is an object that you add to the `plugins` array in your configuration file. You can add an object directly, or create one from a function somewhere:

<details>
  <summary>Read more</summary>

In your `web-dev-server.config.js` or `web-test-runner.config.js`:

```js
import awesomePlugin from 'awesome-plugin';

export default {
  plugins: [
    // use a plugin
    awesomePlugin({ someOption: 'someProperty' }),
    // create an inline plugin
    {
      name: 'my-plugin',
      transform(context) {
        if (context.response.is('html')) {
          return { body: context.body.replace(/<base href=".*">/, '<base href="/foo/">') };
        }
      },
    },
  ],
};
```

</details>
&nbsp;

This is the full type interface for all options:

<details>
  <summary>Read more</summary>

```ts
import { FSWatcher } from 'chokidar';
import Koa, { Context } from 'koa';
import { Server } from 'net';

import { DevServerCoreConfig, Logger, WebSocketsManager } from '@web/dev-server-core';

export type ServeResult =
  | void
  | string
  | { body: string; type?: string; headers?: Record<string, string> };
export type TransformResult =
  | void
  | string
  | { body?: string; headers?: Record<string, string>; transformCache?: boolean };
export type ResolveResult = void | string | { id?: string };
export type ResolveMimeTypeResult = void | string | { type?: string };

export interface ServerArgs {
  config: DevServerCoreConfig;
  app: Koa;
  server: Server;
  fileWatcher: FSWatcher;
  logger: Logger;
  webSockets?: WebSocketsManager;
}

export interface Plugin {
  name: string;
  injectWebSocket?: boolean;
  serverStart?(args: ServerArgs): void | Promise<void>;
  serverStop?(): void | Promise<void>;
  serve?(context: Context): ServeResult | Promise<ServeResult>;
  transform?(context: Context): TransformResult | Promise<TransformResult>;
  transformCacheKey?(context: Context): string | undefined | Promise<string> | Promise<undefined>;
  resolveImport?(args: {
    source: string;
    context: Context;
    code?: string;
    column?: number;
    line?: number;
  }): ResolveResult | Promise<ResolveResult>;
  transformImport?(args: {
    source: string;
    context: Context;
    code?: string;
    column?: number;
    line?: number;
  }): ResolveResult | Promise<ResolveResult>;
  resolveMimeType?(context: Context): ResolveMimeTypeResult | Promise<ResolveMimeTypeResult>;
}
```

</details>
&nbsp;

## Plugins hooks

## Hook: serve

The serve hook can be used to serve virtual files from the server. The first plugin to respond with a body is used. It can return a Promise.

<details>
<summary>Read more</summary>

Serve an auto generated `index.html`:

```js
const indexHTML = generateIndexHTML();

export default {
  plugins: [
    {
      name: 'my-plugin',
      serve(context) {
        if (context.path === '/index.html') {
          return indexHTML;
        }
      },
    },
  ],
};
```

Serve a virtual module:

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      serve(context) {
        if (context.path === '/messages.js') {
          return 'export default "Hello world";';
        }
      },
    },
  ],
};
```

The file extension is used to infer the mime type to respond with. If you are using a non-standard file extension you need to use the `type` property to set it explicitly:

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      serve(context) {
        if (context.path === '/foo.xyz') {
          return { body: 'console.log("foo bar");', type: 'js' };
        }
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: resolveMimeType

Browsers don't use file extensions to know how to interpret files. Instead, they use [media or MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) which is set using the `content-type` header.

The dev server guesses the MIME type based on the file extension. When serving virtual files with non-standard file extensions, you can set the MIME type in the returned result (see the examples above). If you are transforming code from one format to another, you need to use the `resolveMimeType` hook.

<details>
<summary>Read more</summary>

The returned MIME type can be a file extension, this will be used to set the corresponding default MIME type. For example `js` resolves to `application/javascript` and `css` to `text/css`.

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      resolveMimeType(context) {
        // change all MD files to HTML
        if (context.path.endsWith('.md')) {
          return 'html';
        }
      },
    },
    {
      name: 'my-plugin',
      resolveMimeType(context) {
        // change all CSS files to JS, except for a specific file
        if (context.path.endsWith('.css') && context.path !== '/global.css') {
          return 'js';
        }
      },
    },
  ],
};
```

You can use a mime type shorthand, such as `js` or `css`. Koa will resolve this to the full mimetype. It is also possible to set the full mime type directly:

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      resolveMimeType(context) {
        if (context.response.is('md')) {
          return 'text/html';
        }
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: transform

The transform hook is called for each file and can be used to change a file's content before it is served to the browser. Multiple plugins can transform a single file. It can return a Promise.

This hook is useful for small modifications, such as injecting environment variables, or for compiling files to JS before serving them to the browser.

In a web server, the response body is not always a string, but it can be a binary buffer or stream. If the dev server sees that the response is utf-8, it will convert the body to a string for you to make writing transform plugins easier. If you are transforming non-standard file types, you may also need to include a `resolveMimeType` hook. A good example of this is `.ts` files, which in Koa defaults to a streaming video.

<details>
  <summary>Read more</summary>

Rewrite the base path of your application for local development;

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      transform(context) {
        if (context.path === '/index.html') {
          const transformedBody = context.body.replace(/<base href=".*">/, '<base href="/foo/">');
          return transformedBody;
        }
      },
    },
  ],
};
```

Inject a script to set global variables during local development:

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      transform(context) {
        if (context.path === '/index.html') {
          const transformedBody = context.body.replace(
            '</head>',
            '<script>window.process = { env: { NODE_ENV: "development" } }</script></head>',
          );
          return transformedBody;
        }
      },
    },
  ],
};
```

Inject environment variables into a JS module:

```js
import fs from 'fs';

const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));

export default {
  plugins: [
    {
      name: 'my-plugin',
      transform(context) {
        if (context.path === '/src/environment.js') {
          return `export const version = '${packageJson.version}';`;
        }
      },
    },
  ],
};
```

Transform markdown to HTML:

```js
import { markdownToHTML } from 'markdown-to-html-library';

export default {
  plugins: [
    {
      name: 'my-plugin',
      resolveMimeType(context) {
        // this ensures the browser interprets .md files as .html
        if (context.path.endsWith('.md')) {
          return 'html';
        }
      },

      async transform(context) {
        // this will transform all MD files. if you only want to transform certain MD files
        // you can check context.path
        if (context.path.endsWith('.md')) {
          const html = await markdownToHTML(body);

          return html;
        }
      },
    },
  ],
};
```

Polyfill CSS modules in JS:

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      resolveMimeType(context) {
        if (context.path.endsWith('.css')) {
          return 'js';
        }
      },

      async transform(context) {
        if (context.path.endsWith('.css')) {
          const stylesheet = `
            const stylesheet = new CSSStyleSheet();
            stylesheet.replaceSync(${JSON.stringify(body)});
            export default stylesheet;
          `;

          return stylesheet;
        }
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: resolveImport

The `resolveImport` hook is called for each module import. It can be used to resolve module imports before it reaches the browser. When a one plugin returns a resolved, further resolve hooks are not called.

<details>
  <summary>Read more</summary>

The dev server already resolves module imports when the `--node-resolve` flag is turned on. You can do the resolving yourself, or overwrite it for some files.

The hook receives the import string and should return the string to replace it with. This should be a browser-compatible path, not a file path.

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      async resolveImport({ source, context }) {
        const resolvedImport = fancyResolveLibrary(source);
        return resolvedImport;
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: transformImport

The `transformImport` hook is called for each module import. It can be used to transform module imports before they reach the browser. The difference from `resolveImport` is that this hook is always called for all plugins.

<details>
  <summary>Read more</summary>

The hook receives the import string and should return the string to replace it with. This should be a browser-compatible path, not a file path.

```js
export default {
  plugins: [
    {
      name: 'my-plugin',
      async transformImport({ source, context }) {
        return `${source}?foo=bar;
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: serverStart

The `serverStart` hook is called when the server starts. It is the ideal location to boot up other servers you will proxy to. It receives the server config, which you can use if plugins need access to general information such as the `rootDir` or `appIndex`. It also receives the HTTP server, Koa app, and `chokidar` file watcher instance. These can be used for more advanced plugins. This hook can be async, and it awaited before actually booting the server and opening the browser.

<details>
<summary>Read more</summary>

Accessing the serverStart parameters:

```js
function myFancyPlugin() {
  let rootDir;

  return {
    name: 'my-plugin',
    serverStart({ config, app, server, fileWatcher }) {
      // take the rootDir to access it later
      rootDir = config.rootDir;

      // register a koa middleware directly
      app.use((context, next) => {
        console.log(context.path);
        return next();
      });

      // register a file to be watched
      fileWatcher.add('/foo.md');
    },
  };
}

export default {
  plugins: [myFancyPlugin()],
};
```

Boot up another server for proxying in serverStart:

```js
import proxy from 'koa-proxies';

export default {
  plugins: [
    {
      name: 'my-plugin',
      async serverStart({ app }) {
        // set up a proxy for certain requests
        app.use(
          proxy('/api', {
            target: 'http://localhost:9001',
          }),
        );

        // boot up the other server because it is awaited the dev server will also wait for it
        await startOtherServer({ port: 9001 });
      },
    },
  ],
};
```

</details>
&nbsp;

## Hook: serverStop

The `serverStop` hook is called when the server stops. You can use this to do cleanup work, such as closing connections

<details>
<summary>Read more</summary>

```js
function myFancyPlugin() {
  return {
    name: 'my-plugin',
    serverStop() {
      // cleanup
    },
  };
}

export default {
  plugins: [myFancyPlugin()],
};
```

Boot up another server for proxying in serverStart:

</details>
&nbsp;

## Web Sockets

The dev server has a web socket API for communicating with the browser. To use web sockets, your plugin must set the `injectWebSocket` option to `true`. If one plugin has this option set, a web socket script will be injected into the pages server by the dev server.

After setting the option, the server will pass the connected web sockets to the `serverStart` hook.

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart({ webSockets }) {
      // print a console.log in the browser after 1sec
      setTimeout(() => {
        webSockets.sendConsoleLog('my-plugin', 'Hello world!');
      }, 1000);
    },
  };
}

export default {
  plugins: [myPlugin()],
};
```

</details>
&nbsp;

### Built-in messages

The web sockets manager has two built-in messages. `sendConsoleLog` is shown above, and will log any message to the browser console.

Another built-in message is `sendImport`. This will send a module path to be imported by the browser. You can use this to execute code in the browser.

<details>
<summary>Read more</summary>

In this example, `/foo.js`, will be imported in the browser using a dynamic import. The imported file can be a real file on the file system or a virtual file served by your plugin.

The module should have a default export, this is called each time `sendImport` is called.

```js
function myPlugin() {
  let webSockets;
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart(options) {
      ({ webSockets } = options);

      setTimeout(() => {
        // this will import /foo.js in the browser
        webSockets.sendImport('/foo.js');
      }, 1000);
    },

    serve(context) {
      // you can serve a virtual module to be imported
      if (context.path === '/foo.js') {
        return 'export default () => console.log("/foo.js");';
      }
    },
  };
}
```

You can pass parameters to the function in the browser with the third parameter of the `sendImport` function.

```js
function myPlugin() {
  let webSockets;
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart(options) {
      ({ webSockets } = options);

      setTimeout(() => {
        // this will import /foo.js in the browser
        webSockets.sendImport('/foo.js', ['a', 'b', 'c']);
      }, 1000);
    },

    serve(context) {
      // you can serve a virtual module to be imported
      if (context.path === '/foo.js') {
        return 'export default (...args) => console.log(...args);';
      }
    },
  };
}
```

If the code you want to execute is very simple, you can also send the import as a data URL. Data URLs are valid import paths, and very useful for this use case.

```js
function myPlugin() {
  let webSockets;
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart(options) {
      ({ webSockets } = options);

      setTimeout(() => {
        // this will reload the browser
        webSockets.sendImport('data:text/javascript,window.location.reload()');
      }, 1000);
    },
  };
}
```

</details>
&nbsp;

### Custom messages

To send custom messages to opened web sockets you can use the `send` method.

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart({ webSockets }) {
      setTimeout(() => {
        // this will send a message to all opened web sockets
        // the message must be parsable as JSON
        webSockets.send(JSON.stringify({ type: 'x' }));
      }, 1000);
    },
  };
}
```

</details>
&nbsp;

To respond to messages from the browser, you can listen to the `message` event from the web socket manager.

<details>
<summary>Read more</summary>

```js
function myPlugin() {
  return {
    name: 'my-plugin',
    injectWebSocket: true,
    serverStart({ webSockets }) {
      webSockets.on('message', ({ webSocket, data }) => {
        console.log('received message', data);
        webSocket.send('message response');
      });
    },
  };
}
```

</details>
&nbsp;

The message event provides the web socket that fired the event. You can use this to keep track of which sockets send which messages and send messages to specific sockets.

## Koa Context

The plugin hooks receive the raw Koa `Context` object. This contains information about the server's request and response. Check the [Koa documentation](https://koajs.com/) to learn more about this.

To transform specific kinds of files we don't recommend relying on file extensions. Other plugins may be using non-standard file extensions. Instead, you should use the server's MIME type or content-type header. You can check by using the `context.response.is()` function. This is used a lot in the examples above.

Because files can be requested with query parameters and hashes, we recommend using `context.path` for reading the path segment of the URL only. If you do need to access search parameters, we recommend using `context.URL.searchParams.get('my-parameter')`.