First in the front of the public number from the advanced to the hospital, welcome attention.

Hi, I’m SSH. A few days ago, I was surfing the web when Francois Valdy announced that he had made Browser-Vite and that vite was running successfully in the browser. This got me wondering, how do you run a Vite that relies heavily on Node in the browser? Next, join me to discover the secrets.

The principle in a nutshell

  • Service Worker: HTTP server used to replace Vite.

  • Web Worker: Run browser-vite to process the main thread.

  • The file system is replaced by an in-memory emulated file system.

  • Convert special extensions (.ts,.tsx,.scss…) The import.

Challenges encountered

There is no real file system

Vite does a lot of work with the file system. Read project files, listen for file changes, handle globs, and more… This is difficult to implement in the in-memory file system of the browser’s emulated implementation, so Browser-Vite removes listeners, globs, and configuration files to reduce complexity.

Project files are stored in an in-memory file system, so Broswer-Vite and Vite plugins can handle them properly.

There is no “node_modules”

Vite relies on the presence of node_modules to resolve dependencies. Dependencing pre-bundling will be optimized at startup.

Also to reduce complexity, Broswer-Vite was careful to remove node_modules parsing and dependency prepackaging from Vite.

So browser-Vite users need to create a Vite plugin to resolve raw module imports.

Regular expression “Trailing assertion”

Some of the code in Vite uses postline assertions. It works fine in Node.js, but Safari doesn’t support it.

So the author rewrites these regulars.

Hot update (HMR)

Vite uses WebSockets to synchronize code changes between server (Node) and client (browser).

In browser-Vite, the server is ServiceWorker + Vite worker, and the client is iframe. So the author switched WebSockets to post messages for iframes.

How to use

As of this writing, the tool isn’t ready out of the box, and you’ll need to read a lot of processing details inside Vite if you want to use it.

If you’re interested, keep an eye on Browser-Vite’s README for the latest usage.

The installation

Install browser-Vite NPM package.

$ npm install --save browser-vite
Copy the code

or

$ npm install --save vite@npm:browser-vite
Copy the code

To rewrite import from “vite” to “browser-vite”

Iframe-browser-vite window

An iframe is required to display the internal pages provided by Browser-Vite.

Service Worker – Web server in browser

The Service Worker catches a specific URL request from the iframe.

An example of using WorkBox:

workbox.routing.registerRoute(
  /^https? :\/\/HOST/BASE_URL\/(\/.*)$/,
  async ({
    request,
    params,
    url,
  }: import('workbox-routing/types/RouteHandler').RouteHandlerCallbackContext): Promise<Response> => {
    constreq = request? .url || url.toString();const [pathname] = params as string[];
    // send the request to vite worker
    const response = await postToViteWorker(pathname)
    returnresponse; });Copy the code

In most cases, messages to the “Vite Worker” are sent using postMessage and broadcast-channel.

Vite Worker – Handles requests

The Vite Worker is a Web Worker that handles requests captured by the Service Worker.

Example of creating a Vite server:

import {
  transformWithEsbuild,
  ModuleGraph,
  transformRequest,
  createPluginContainer,
  createDevHtmlTransformFn,
  resolveConfig,
  generateCodeFrame,
  ssrTransform,
  ssrLoadModule,
  ViteDevServer,
  PluginOption
} from 'vite';

export async function createServer = async () = >{
  const config = await resolveConfig(
    {
      plugins: [
        // virtual plugin to provide vite client/env special entries (see below)
        viteClientPlugin,
        // virtual plugin to resolve NPM dependencies, e.g. using unpkg, skypack or another provider (browser-vite only handles project files)
        nodeResolvePlugin,
        // add vite plugins you need here (e.g. vue, react, astro ...)
      ]
      base: BASE_URL, // as hooked in service worker
      // not really used, but needs to be defined to enable dep optimizations
      cacheDir: 'browser'.root: VFS_ROOT,
      // any other configuration (e.g. resolve alias)
    },
    'serve'
  );
  const plugins = config.plugins;
  const pluginContainer = await createPluginContainer(config);
  const moduleGraph = new ModuleGraph((url) = > pluginContainer.resolveId(url));

  const watcher: any = {
    on(what: string, cb: any) {
      return watcher;
    },
    add(){}};const server: ViteDevServer = {
    config,
    pluginContainer,
    moduleGraph,
    transformWithEsbuild,
    transformRequest(url, options) {
      return transformRequest(url, server, options);
    },
    ssrTransform,
    printUrls() {},
    _globImporters: {},
    ws: {
      send(data) {
        // send HMR data to vite client in iframe however you want (post/broadcast-channel ...)
      },
      async close() {},
      on() {},
      off() {},
    },
    watcher,
    async ssrLoadModule(url) {
      return ssrLoadModule(url, server, loadModule);
    },
    ssrFixStacktrace() {},
    async close() {},
    async restart() {},
    _optimizeDepsMetadata: null._isRunningOptimizer: false._ssrExternals: []._restartPromise: null._forceOptimizeOnRestart: false._pendingRequests: new Map()}; server.transformIndexHtml = createDevHtmlTransformFn(server);// apply server configuration hooks from plugins
  const postHooks: ((() = > void) | void=) [] [];for (const plugin of plugins) {
    if (plugin.configureServer) {
      postHooks.push(awaitplugin.configureServer(server)); }}// run post config hooks
  // This is applied before the html middleware so that user middleware can
  // serve custom content instead of index.html.
  postHooks.forEach((fn) = > fn && fn());

  await pluginContainer.buildStart({});
  await runOptimize(server);
  
  return server;
}
Copy the code

Pseudo-code for handling requests through Browser-Vite:

import {
  transformRequest,
  isCSSRequest,
  isDirectCSSRequest,
  injectQuery,
  removeImportQuery,
  unwrapId,
  handleFileAddUnlink,
  handleHMRUpdate,
} from 'vite/dist/browser'; .async (req) => {
  let { url, accept } = req
  consthtml = accept? .includes('text/html');
  // strip ? import
  url = removeImportQuery(url);
  // Strip valid id prefix. This is prepended to resolved Ids that are
  // not valid browser import specifiers by the importAnalysis plugin.
  url = unwrapId(url);
  // for CSS, we need to differentiate between normal CSS requests and
  // imports
  if(isCSSRequest(url) && accept? .includes('text/css')) {
    url = injectQuery(url, 'direct');
  }
  let path: string | undefined = url;
  try {
    let code;
    path = url.slice(1);
    if (html) {
      code = await server.transformIndexHtml(` /${path}`, fs.readFileSync(path,'utf8'));
    } else {
      const ret = awaittransformRequest(url, server, { html }); code = ret? .code; }// Return code reponse
  } catch (err: any) {
    // Return error response}}Copy the code

See the Vite internal middleware source code for more details.

How does Stackblitz WebContainers compare

“WebContainers” : Run Node.js in the browser

Stackblitz WebContainers can also run Vite in a browser. You can go elegantly to Viet. new to have a working environment.

The author states that he is not an expert on WebContainers, but in a nutshell, Browser-Vite simulates FS and HTTPS servers at the Vite level, and WebContainers simulates FS and much more at the Node.js level, Vite runs on it with a few extra modifications.

It stores node_modules in the browser’s WebContainer. But it doesn’t run NPM or YARN directly, probably because it takes up too much space. They link these commands to Turbo ———— their package manager.

WebContainers can also run other frameworks such as Remix, SvelteKit, or Astro.

This is amazing ✨ this is exciting 🤯 the author has huge respect for the team at WebContainer, Stackblitz team is awesome!

One drawback of WebContainers is that it currently only works on Chrome, but may soon work on Firefox. Browser-vite currently works with Chrome, Firefox and Safari.

In short, WebContainers runs Vite at a lower level of abstraction. Browser-vite runs at a higher level of abstraction, very close to Vite itself.

For example, browser-Vite is a bit like UltraHLE (Nintendo N64 emulator) 🕹️😊 for retro gamers

Gametechwiki.com: High/low level emulator

The author’s next plan

Browser-vite is the core of the authors’ proposed solution. Intends to gradually promote to their full range of products:

  • Backlight.dev
  • Components.studio
  • WebComponents.dev
  • Replic.dev (new app to be released soon)

Going forward, the authors will continue to invest in Browser-Vite and report upstream. Last month they also announced a sponsorship to Evan You and Patak to support Vite in support of this amazing project.

Want to know more?

  • Making library: browser – vite
  • Join Discord. There’s a channel called # browser-Vite. 🤗

The resources

  • Divriots.com/blog/vite-i…
  • Github.com/divriots/br…
  • Blog.stackblitz.com/posts/intro…

First in the front of the public number from the advanced to the hospital, more interesting front-end articles, welcome to pay attention to.