Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve index.html from custom host #341

Closed
andrefelipe opened this issue Jun 3, 2020 · 12 comments
Closed

Serve index.html from custom host #341

andrefelipe opened this issue Jun 3, 2020 · 12 comments
Labels
enhancement New feature or request

Comments

@andrefelipe
Copy link

Is your feature request related to a problem? Please describe.
My final goal is to use Vite for traditional PHP based websites. Where HTML is rendered on PHP side and we'll be looking into a scenario where Vue components are used where necessary, mixed with regular HTML (not a SPA).

Describe the solution you'd like
For as far as I can go, the solution lies on allowing to customize Vite's index.html path, but not only customizing, but actually requesting it from an URL instead of reading from file.

For example, in vite.config have something like:

{
  indexUrl: 'http://mylocalsite.test'
}

and also expect Vite to follow the paths, for example, by visiting http://localhost:3000/about it would request the HTML from http://mylocalsite.test/about — of course, it is of the server responsibility to output the <script type="module" src="/src/main.js"></script> within the body.

Describe alternatives you've considered
Tried working around with the proxy feature, but no success. For example, proxying "/" to my local server, and then proxying "/src" "/vite" and "/@modules" back to http://localhost:3000 which doesn't work.

Additional context
Here is the code of my trial run:
https://github.com/andrefelipe/vite-php-setup

Thank you very much! Would be fantastic to use Vite in different scenarios.

@yyx990803 yyx990803 changed the title URL based index paths Serve index.html from custom host Jun 4, 2020
@yyx990803 yyx990803 added the enhancement New feature or request label Jun 4, 2020
@Grafikart
Copy link

Grafikart commented Jul 11, 2020

I succcessfully setup Vite with a Symfony application but It would work with every backend.

The goal

We want to use vite to serve assets, and the backend for the rest. So we will ignore vite ability to generate an HTML page.
Another solution would be to use vite to proxy the backend but I'm not a huge fan of this since it creates some problems depending on how your backend generates URLs.

Steps to make the backend "vite friendly"

If i'm in development mode I change assets path to point to the vite webserver. Replacing path to assets with the vite url

<script src="http://localhost:3000/app.js" type="module" defer></script>

Vite will complains about CORS so I have to edit the configuration adding @koa/cors middleware (thanks evan for using a known server library ;) )

// @ts-check
const prefresh = require('@prefresh/vite')
const cors = require('@koa/cors')

/**
 * @type { import('vite').UserConfig }
 */
const config = {
  jsx: 'preact', // Change this to adapt your situation
  plugins: [prefresh()],
  root: './assets', // You can change the root path as you wish
  configureServer: function ({ app }) {
    app.use(cors({ origin: '*' }))
  }
}

module.exports = config

But I won't have the hot reload at this point :(.
Since it's enabled with vite injecting something in the index.html we have to edit our template to inject the module ourself. So in my template, when in development mode, I add the HMR part myself (this should be loaded before the other assets)

<script type="module">
  import "http://localhost:3000/vite/client"
  window.process = { env: { NODE_ENV: "development" }}
</script>

PHP Sample

Here is a small sample to help bootstrap your configuration.

/**
* $path contains the path of the assets, for instance /app.js
*/
function script($path) {
    if ($this->isDev()) {
        $prefix = "http://{$_SERVER['SERVER_NAME']}:3000/" // Vite
    } else {
        $prefix = "/assets/"; // Static path
    }
    $script = "<script src=\"{$prefix}{$name}\" type=\"module\" defer></script>";

    if ($this->isDev()) {
        // We need to add HMR ;)
        $script = <<<HTML
            <script type="module">
            import "{$prefix}vite/client"
            window.process = { env: { NODE_ENV: "development" }}
            </script>
            $script
        HTML;
    }

    return $script;
}

@mxmaxime
Copy link

mxmaxime commented Jul 27, 2020

Hi, I'm using the same approach as @Grafikart for Django and this is working like a charm!

Notice that in latest version of vite, the URL of the hmr has changed to /vite/client, you will have the following error if you call the /vite/hmr endpoint:

[vite] client import path has changed from "/vite/hmr" to "/vite/client". please update your code accordingly.

@andrefelipe
Copy link
Author

andrefelipe commented Jul 27, 2020

Sorry for the long delay here.

Thank you very much @Grafikart for the detailed explanation. @mxmaxime Thanks too.

Now, how are you handling the production build? How to find the hashed index.js and style.css? — The best solution would be to Rollup to export the filenames to a JSON file, right?

I guess if Vite supports this natively it would be much cleaner. There are 3 areas we are hacking here which would be great experience if they "just work":
— The loading of Vite's HMR client (if Vite serve index.html from custom host, it could inject the Vite client itself);
— The handling of the main.js script entry file (Vite's index.html still have to exist right now, to find the main.js);
— The output of hashed file names for production;

What do you think?

By the way, I am putting this all together here https://github.com/andrefelipe/vite-php-setup

@Grafikart
Copy link

Grafikart commented Jul 28, 2020

For the production build I use my own rollup configuration (it's not that hard to setup) since I'm not a huge fan of having a big configuration I don't understand and difficult to extend from vite.

I keep vite for the dev, and a custom rollup setup for the build.

Since I use my own rollup file I can put an extra logic that generates a JSON required for my backend framework (the logic could be extracted in a plugin).

  • My vite.config.js generates a JSON for the backend framework containing urls to vite server before starting.
  • My rollup.js generates a similar JSON for the backend framework containing paths with hashes.

It's a bit of boilerplate at the moment (it was the same nightmare with webpack anyway).

@mxmaxime I fixed my code if someone finds it from a search ^^

@andrefelipe
Copy link
Author

Thanks for the feedback @Grafikart I will keep an eye on this and keep trying to come to nice setup. Hopefully Vite's core can help us in the future.

@stephanedemotte
Copy link

@Grafikart Can you share your rollup.js please ?

I need to generate a json with the css & js path too.

I've found rollup-plugin-output-manifest

But this plugins generate only a path for the js

@Frulko
Copy link

Frulko commented Aug 7, 2020

@stephanedemotte It seem @Grafikart not using vite for building but directly rollup with a special config like here :

https://github.com/Grafikart/Grafikart.fr/blob/master/rollup.config.js

and here for the package.json

https://github.com/Grafikart/Grafikart.fr/blob/c120d6b2b2d63fedd6ace51500a875b506c2416a/package.json#L31-L32

It's a workaround but it could be cool if we can use vite in dev and build mode without plenty of ***.config.js|json files.

The rollup-plugin-output-manifest plugin work well but vite is not using rollup for building css so that's why the plugin generate only the path for js

An enhancement could be to add a option in vite for generate a small manifest in outDir with all file generated in same way the index.html is write (line 483). From line 454 to 480 there is a loop which iterate on all output file it could be a clue for add a manifest generation.

for (const chunk of output) {
if (chunk.type === 'chunk') {
// write chunk
const filepath = path.join(resolvedAssetsPath, chunk.fileName)
let code = chunk.code
if (chunk.map) {
code += `\n//# sourceMappingURL=${path.basename(filepath)}.map`
}
await writeFile(filepath, code, WriteType.JS)
if (chunk.map) {
await writeFile(
filepath + '.map',
chunk.map.toString(),
WriteType.SOURCE_MAP
)
}
} else if (emitAssets) {
if (!chunk.source) continue
// write asset
const filepath = path.join(resolvedAssetsPath, chunk.fileName)
await writeFile(
filepath,
chunk.source,
chunk.fileName.endsWith('.css') ? WriteType.CSS : WriteType.ASSET
)
}
}
// write html
if (indexHtml && emitIndex) {
await writeFile(
path.join(outDir, 'index.html'),
indexHtml,
WriteType.HTML
)
}

My use-case is not so far from @Grafikart. I want to use vite inside a WordPress project and I would like to have a manifest or files without hash in names in generated files for a better handling in WordPress loading assets workflow.

By the way thanks @Grafikart for your approach which works very well in dev mode !

@calaoa
Copy link

calaoa commented Sep 18, 2020

fwiw: also mentioning the Vite dev server proxy (works for me)

@stephanedemotte
Copy link

@Frulko Good news @underfin fix the issue with rollup-plugin-output-manifest

We could get js and css in the manifest.json soon,

I use Twig in wordpress, so we can retrieve

    {% set manifest %}
      {% include 'manifest.json' ignore missing %}
    {% endset %}

    {% set json = json_decode(manifest) %}

     <link href=/dist/{{ json['style.css'] }} rel=stylesheet>

    <script src="/dist/{{ json['index.js'] }}"></script>

@andrefelipe
Copy link
Author

Updated my setup code: https://github.com/andrefelipe/vite-php-setup

With the manifest.json handled we have left:

  • The loading of Vite's HMR client (right now I add the code manually, would be great if Vite served index.html from custom host, so it could inject the Vite client by itself)
  • The handling of the index.js entry file (Vite's index.html still have to exist right now, to find the JS file, would be cleaner if Vite's index.html could be deleted and we tell Vite where our JS file is through vite.config.js);

Comments and ideas welcomed!

Vite is too good for development, I look forward to use it everywhere. :)

@edersoares
Copy link
Contributor

@andrefelipe thanks for sharing! This PR #918 is based on your work.

@andrefelipe
Copy link
Author

Awesome! Thanks Vite team!

With all recent features my example setup is very very clean now.

Job is done!
https://github.com/andrefelipe/vite-php-setup

@github-actions github-actions bot locked and limited conversation to collaborators Jul 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants