Support @vitejs/plugin-react

Created on 8 October 2023, 9 months ago
Updated 14 June 2024, 12 days ago

Problem/Motivation

Many thanks for this *excellent* module. I had a little trouble setting up a React app with it and eventually it boiled down to this error:

Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.

Steps to reproduce

Create a new module as normal that leverages this vite module.
Use one of the React templates for the vite app with the new module.

Proposed resolution

The documentation states:

Note if you are using React with @vitejs/plugin-react, you'll also need to add this before the [app's] scripts, since the plugin is not able to modify the HTML you are serving:

<script type="module">
  import RefreshRuntime from 'http://localhost:5173/@react-refresh'
  RefreshRuntime.injectIntoGlobalHook(window)
  window.$RefreshReg$ = () => {}
  window.$RefreshSig$ = () => (type) => type
  window.__vite_plugin_react_preamble_installed__ = true
</script>

I suggest we find a way to inject that script (with the correct hostname) into the DOM before loading the relevant library(ies).

I tried it out by printing it on the page with the same controller that I'm adding the library with (with the domain hard-coded for local development) and it worked fine.

Remaining tasks

User interface changes

API changes

Data model changes

Feature request
Status

RTBC

Version

1.0

Component

Code

Created by

🇦🇺Australia darvanen Sydney, Australia

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @darvanen
  • 🇦🇺Australia darvanen Sydney, Australia

    I'm happy to give this a go but have a couple of questions about how to approach it:

    1. What method would you suggest for injecting the script? There's no current opportunity to print to the DOM from within the Vite or AssetLibrary classes as far as I can see.
    2. How would you like configuration to be handled? Perhaps a new key in the vite: section of a library? Perhaps "support-plugin-react"?
  • 🇵🇱Poland wotnak

    It would be nice to implement this in a general way to allow for including potential similar scripts for other frameworks.
    Maybe we could add a concept of devDependencies to the vite section of library definition. It could allow providing a list of other libraries like in the default 'dependencies' section, but they would be attached only when running in the vite development mode.
    Then for this specific use case, in this module we could define a vite/plugin-react-refresh library that includes the script. Then in a theme/module where vite-plugin-react is used, in the library definition this library could be added to the devDependencies:

    react-block:
      vite:
        enabled: true
        devDependencies:
        - vite/plugin-react-refresh
      js:
        ...
    

    The plugin-react-refresh script will also need the current vite dev server url. Since it can be different for every theme/module passing it through drupalSettings won't really work because the script won't know which url to use. Maybe we could pass it to all devDependecies through data-vite-dev-server-url attribute then in scripts it could be fetched by accessing attributes of document.currentScript.

  • 🇺🇸United States mortona2k Seattle

    Does the library have to be loaded by Drupal? I have unocss devtools included in my main.js.

    I am new to working with manifest.json files and browser js modules. Is it possible to declare dependencies in there?

  • 🇦🇺Australia darvanen Sydney, Australia

    #4: @mortona2k I reckon anything in JS will be loaded fine. The problem with @vitejs/plugin-react is it alters index.html and that isn't used when you're embedding a React app in a Drupal page.

    #3: Thanks @wotnak, that approach sounds good to me. I'll see if I can find some time to give it a go on the weekend. What element would you attach data-vite-dev-server to? Would that have to be documented as a responsibility of the end user dev?

  • 🇮🇹Italy tanc Italy

    I'm also wrestling with this problem at the moment. I'd really like a way for the Vite module to be able to load a library when in dev mode so that I can add the needed preamble for @vitejs/plugin-react.

    @darvanen have you had any further thoughts about how to do it?

  • Pipeline finished with Success
    5 months ago
    Total: 139s
    #88135
  • Status changed to Needs review 5 months ago
  • 🇮🇹Italy tanc Italy

    I've got this working and added a MR so you can review. This basically does as @wotnak suggested, it adds a devDependencies option under the vite key. These library entries are then added as dependencies of the vite controlled library. It also adds the dev server url as a data attribute to those dev dependencies so that it can be fetched and used as suggested.

    An example mymodule.libraries.yml file showing a deep file path for the dev server url:

    reactapp.devmode:
      js:
        js/viteDevMode.js: {}
    
    reactapp:
      vite:
        manifest: app/dist/.vite/manifest.json
        baseUrl: '/profiles/myprofile/modules/mymodule/modules/mydeepermodule/app/dist/'
        devServerUrl: 'http://host.docker.internal:5173/profiles/myprofile/modules/mymodule/modules/mydeepermodule/app'
        devDependencies:
          - mymodule/reactapp.devmode
      js:
        src/main.tsx: {}
      dependencies:
        - core/drupal
        - core/drupalSettings
        - locale/translations

    The contents of js/viteDevMode.js:

    const thisScript = document.currentScript;
    const devServerUrl = thisScript.dataset.viteDevServer;
    
    import(`${devServerUrl}/@react-refresh`)
      .then((RefreshRuntime) => {
        const injectIntoGlobalHook = RefreshRuntime.default.injectIntoGlobalHook;
        console.log(injectIntoGlobalHook);
        injectIntoGlobalHook(window);
        window.$RefreshReg$ = () => {};
        window.$RefreashSig$ = () => (type) => type;
        window.__vite_plugin_react_preamble_installed__ = true;
      })
      .catch(() => {
        console.log('Could not load RefreshRuntime from the vite dev server');
      });
  • 🇺🇸United States brianperry

    Was also working on getting HMR working with a React app, and ran into the same issue. The functionality in the MR work well for me. @tanc the only thing I'd suggest adding is some documentation in the readme about the new devDependencies option so that people are aware of it.

  • 🇮🇹Italy tanc Italy

    Thanks @brianperry. Feel free to contribute some docs, hopefully it's fresher in your mind as you've recently done the steps so you may be better placed to write something. Even just an outline and I can help fill in the details.

  • Status changed to Needs work about 1 month ago
  • 🇵🇱Poland wotnak

    Changing to needs work as it is missing documentation of the new devDependencies option.

  • Pipeline finished with Canceled
    13 days ago
    Total: 49s
    #197784
  • Pipeline finished with Canceled
    13 days ago
    Total: 91s
    #197785
  • Status changed to Needs review 13 days ago
  • 🇮🇹Italy tanc Italy

    I've added the requested documentation and marked as needs review.

  • Pipeline finished with Failed
    13 days ago
    Total: 139s
    #197787
  • Pipeline finished with Failed
    13 days ago
    Total: 168s
    #197789
  • Status changed to RTBC 13 days ago
  • 🇺🇸United States brianperry

    Sorry I wasn't able to get to this myself, but the readme updates look good to me @tanc

  • 🇮🇹Italy tanc Italy

    No problem! Thanks for reviewing

  • 🇦🇺Australia darvanen Sydney, Australia

    +1 RTBC

    We installed the patch, followed the readme and were up and running in no time! Thanks for the great work on this

  • 🇦🇺Australia darvanen Sydney, Australia

    Removing out of date tag

Production build 0.69.0 2024