laravel / vite-plugin

Laravel plugin for Vite.
MIT License
804 stars 152 forks source link

Assets are not served when running "sail npm run dev" without an existing build folder after migrating from Mix to Vite on Laravel using Sail #127

Closed stoffpalette closed 2 years ago

stoffpalette commented 2 years ago

I migrated from Mix 6.0.6 to Vite 3.0.9 (laravel-vite-plugin 0.5.4) on Laravel 9.2.5.1.

I followed these instructions: https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite

The assets resources/css/app.css and resources/js/app.js are not served when i start hot-reloading on Sail via sail npm run dev (= vite) , if i did not build them before via sail npm run build (= vite build) and the ./public/build/** files exist.

If the compiled assets in ./public/build/** do NOT exist, then @vite(['resources/css/app.css', 'resources/js/app.js']) in my blade-template does not touch the defined links - they are referenced like that in the web-browser, and therefore won't be served.

If the compiled assets in ./public/build/** DO exist, then @vite(['resources/css/app.css', 'resources/js/app.js']) in my blade-template is converted to the related compiled assets, e.g. build/assets/app.3696ec54.css or build/assets/app.dfc18a0d.js.

Shouldn't hot-reloading started with sail npm run dev (= vite) also create the build-directories and files on related file-changes?

And is it possible to change the build target directory like we can do it with Mix?

My package.json scripts section:

"scripts": {
        "dev": "vite",
        "build": "vite build"
    }

My vite.config.js:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
    server: {
        host: 'server.apps.local'
    },
    plugins: [
        laravel({
            input: [
              'resources/css/app.css',
              'resources/js/app.js',
            ],
            refresh: true
        }),
        vue({
             template: {
                 transformAssetUrls: {
                     base: null,
                     includeAbsolute: false,
                 },
             },
        }),
    ]
});

My postcss.config.js:

module.exports = {
    plugins: {
        tailwindcss: {},
        autoprefixer: {},
    },
}

My docker-compose.yml:

# For more information: https://laravel.com/docs/sail
version: "3"
x-tz: &tz
    TZ: Europe/Berlin
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.1
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.1/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
        environment:
            <<: *tz
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
            - redis
            - meilisearch
            - selenium
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3307}:3307'
        environment:
            <<: *tz
            MYSQL_TCP_PORT: '${DB_PORT}'
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: "%"
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sail-mysql:/var/lib/mysql'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
            retries: 3
            timeout: 5s
    redis:
        image: "redis:alpine"
        ports:
            - "${FORWARD_REDIS_PORT:-6379}:6379"
        environment:
            <<: *tz
        volumes:
            - 'sail-redis:/data'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "redis-cli", "ping"]
            retries: 3
            timeout: 5s
    meilisearch:
        image: 'getmeili/meilisearch:latest'
        ports:
            - '${FORWARD_MEILISEARCH_PORT:-7700}:7700'
        environment:
            <<: *tz
        volumes:
            - 'sail-meilisearch:/data.ms'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "wget", "--no-verbose", "--spider",  "http://localhost:7700/health"]
            retries: 3
            timeout: 5s
    mailhog:
        image: 'mailhog/mailhog:latest'
        environment:
            <<: *tz
        ports:
            - '${FORWARD_MAILHOG_PORT:-1025}:1025'
            - '${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025'
        networks:
            - sail
    selenium:
        image: 'selenium/standalone-chrome'
        environment:
            <<: *tz
        volumes:
            - '/dev/shm:/dev/shm'
        networks:
            - sail
networks:
    sail:
        driver: bridge
volumes:
    sail-mysql:
        driver: local
    sail-redis:
        driver: local
    sail-meilisearch:
        driver: local
jessarcher commented 2 years ago

Hi @stoffpalette,

When running the Vite development server via npm run dev, no files are written to your build directory. The assets are served directly from the Vite development server.

While the Vite development server is running, it creates a file at public/hot with the address of the server.

When the @vite directive detects this file, it loads your assets from the Vite development server, instead of from Laravel's public directory. Only when the hot file is not present does the @vite directive load compiled assets from the public/build directory.

And is it possible to change the build target directory like we can do it with Mix?

Yes, you may use the buildDirectory configuration option for this plugin to change the name of the build directory. You will also need to pass it as the second argument to the @vite directive so that it knows where to find your assets.

I'd recommend sticking with the defaults if possible though.

stoffpalette commented 2 years ago

Hi @jessarcher ,

thanks for your fast reply and the explanation.

Since Vite is not able to serve the files via Sail, something does not seem to run as it should.

The public/hot file is indeed created when i start Vite within the Sail container:

starting vite:

$ sail npm run dev

> dev
> vite

  VITE v3.0.9  ready in 1701 ms

  ➜  Local:   http://server.apps.local:5173/
  ➜  Network: http://172.25.0.7:5173/

  LARAVEL v9.25.1  plugin v0.5.4

  ➜  APP_URL: http://server.apps.local

public/hot-file is created with the following content:

http://server.apps.local:5173

Vite injects the following via the @vite directive in the corresponding blade-template:

<script type="module" src="http://server.apps.local:5173/@vite/client"></script>
<link rel="stylesheet" href="http://server.apps.local:5173/resources/css/app.css" />
<script type="module" src="http://server.apps.local:5173/resources/js/app.js"></script>

But the resources cannot be loaded.

Chrome-Console reports for each file:

Failed to load response data: No data found for resource with given identifier

Firefox reports for each file:

Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at https://some-url-here. (Reason: CORS request did not succeed).

( Possible causes: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSDidNotSucceed / i assume, it is the 4th possible cause mentioned, that would be similar to the Chrome message: "The server did not respond to the actual request [...]")

Do you have any idea, why this happens?

jessarcher commented 2 years ago

What happens if you browse directly to http://server.apps.local:5173/resources/js/app.js ? Do you get a successful response with JavaScript content?

stoffpalette commented 2 years ago

@jessarcher If i do that, i get an ERR_EMPTY_RESPONSE in Chrome. Similar error message in Firefox: Error: Connection interrupted

jessarcher commented 2 years ago

Sounds like there's some sort of networking issue somewhere between your host machine and the Vite service running in the container.

I'm assuming you're able to access the Sail web server via http://server.apps.local?

stoffpalette commented 2 years ago

@jessarcher yes. Accessing the Sail web server via http://server.apps.local is working without problems. Only Vite ressources on the additionally port 5173 are not loading as expected.

OmarBouziane commented 2 years ago

Browse http://server.apps.local:5173/resources/js/app.js and the you need to accept the security warning from there, it means for the port: 5173

stoffpalette commented 2 years ago

@OmarBouziane Does not help. There is no security warnig. I just get an EMPTY RESPONSE Error (Chrome: ERR_EMPTY_RESPONSE)

Aigarsss commented 2 years ago

Yeah, this does not work for me. I am not using sail, but just Docker + Laravel + Vite + Svelte. None of the solutions I could find on this help.

None of these help, even on "laravel-vite-plugin": "^0.6.0",

So am a bit stumped. Works if you run npm run build, then loads everything fine. Does not work with npm run dev. Failed to load resource: net::ERR_EMPTY_RESPONSE

What is interesting, that http://localhost:5173/ just returns a This page isn’t working. Then when I go to localhost, console shows: Failed to load resource: net::ERR_EMPTY_RESPONSE and its trying this link: http://localhost:5173/resources/js/app.js (which if I try to visit directly, also returns This page isn’t working

I have ran out of ideas how to troubleshoot this

bozsudo commented 2 years ago

Yeah, this does not work for me. I am not using sail, but just Docker + Laravel + Vite + Svelte. None of the solutions I could find on this help.

Did you manage to get something working? I'm in a similar situation using Docker+Laravel+Vite, having tried multiple configs:

vite.config.ts

```js import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import react from '@vitejs/plugin-react'; export default defineConfig({ server: { // host: 'localhost', // host: 'site', // port: 3000, // strictPort: true, hmr: { host: 'localhost', // host: '0.0.0.0', // port: 5173, // clientPort: 80, }, }, plugins: [ laravel({ input: 'resources/js/app.tsx', refresh: true, }), react(), ], resolve: { alias: { '@': '/resources/js', }, }, }); ```

docker-compose.yml

```yml version: '3' networks: laravel: services: site: build: context: ./dockerfiles dockerfile: nginx.dockerfile args: - UID=${UID:-1000} - GID=${GID:-1000} container_name: nginx ports: - 80:80 volumes: - ./src:/var/www/html:delegated depends_on: - php - redis - mysql - mailhog networks: - laravel mysql: image: mariadb:10.6 container_name: mysql restart: unless-stopped tty: true ports: - 3306:3306 environment: MYSQL_DATABASE: homestead MYSQL_USER: homestead MYSQL_PASSWORD: secret MYSQL_ROOT_PASSWORD: secret SERVICE_TAGS: dev SERVICE_NAME: mysql networks: - laravel php: build: context: ./dockerfiles dockerfile: php.dockerfile args: - UID=${UID:-1000} - GID=${GID:-1000} container_name: php volumes: - ./src:/var/www/html:delegated networks: - laravel redis: image: redis:alpine container_name: redis restart: unless-stopped ports: - 6379:6379 networks: - laravel composer: build: context: ./dockerfiles dockerfile: composer.dockerfile args: - UID=${UID:-1000} - GID=${GID:-1000} container_name: composer volumes: - ./src:/var/www/html working_dir: /var/www/html depends_on: - php user: laravel entrypoint: ['composer', '--ignore-platform-reqs'] networks: - laravel npm: image: node:16.7 container_name: npm volumes: - ./src:/var/www/html ports: - 3000:3000 - 3001:3001 - 5173:5173 working_dir: /var/www/html entrypoint: ['npm'] networks: - laravel artisan: build: context: ./dockerfiles dockerfile: php.dockerfile args: - UID=${UID:-1000} - GID=${GID:-1000} container_name: artisan volumes: - ./src:/var/www/html:delegated depends_on: - mysql working_dir: /var/www/html entrypoint: ['php', '/var/www/html/artisan'] networks: - laravel mailhog: image: mailhog/mailhog:latest container_name: mailhog ports: - 1025:1025 - 8025:8025 networks: - laravel ```

localhost/:24 GET http://localhost:5173/@vite/client net::ERR_EMPTY_RESPONSE

localhost/:24 GET http://localhost:5173/resources/js/app.tsx net::ERR_EMPTY_RESPONSE

localhost/:19 GET http://localhost:5173/@react-refresh net::ERR_EMPTY_RESPONSE

stoffpalette commented 2 years ago

@cookiies Still no solution here. Forced to work without hot-reloading at the moment. @jessarcher Shouldn't the ticket be reopened as a bug?

Aigarsss commented 2 years ago

@cookiies Still no solution here. Forced to work without hot-reloading at the moment. @jessarcher Shouldn't the ticket be reopened as a bug?

I agree, as for now, the only way to get changes is to run npm run build on every change

jessarcher commented 2 years ago

Are you able to try it on a fresh Laravel project with Sail and something like Breeze, to see whether the default setup works on your machine?

First, make sure you have no running Docker containers that could conflict by checking docker ps.

Then run the following:

curl -s "https://laravel.build/example-app" | bash
cd example-app
./vendor/bin/sail up

Then in another terminal in the same directory:

./vendor/bin/sail composer require laravel/breeze --dev
./vendor/bin/sail artisan breeze:install vue
./vendor/bin/sail npm run dev

If I follow those steps and then browse to http://localhost, I can see in my browser console that it connected successfully to the Vite dev server.

If that works for you, perhaps you can take a look between the example app and your own for what might be different. You could start tweaking the example-app to be more like your real app by introducing one change at a time until you find one that makes it stop working.

If the example-app doesn't work for you, then there may be an issue.

timacdonald commented 2 years ago

I've just run the exact steps verbatim outlined by Jess above and can confirm that things are working as expected on my Mac.

stoffpalette commented 2 years ago

@jessarcher couldn't get that installation work, since of several problems during single installation steps. therefore i stopped that test.

BUT: Additional research when trying to fix some of the errors during installation brought me to an an article that luckily fixed my problem!

What was missing, is the --host parameter, that obviously must be appended to the vite command within the scripts section in the package json, so:

OLD (hmr NOT working):

"scripts": {
        "dev": "vite",
        "build": "vite build"
    }

NEW (hmr working):

"scripts": {
        "dev": "vite --host",
        "build": "vite build"
    },
jessarcher commented 2 years ago

Glad you got it sorted @stoffpalette! :tada:

The --host flag is the equivalent of setting server.host to true in your vite.config.js. This tells Vite to listen on all addresses, not just localhost. It's important when running inside Docker because listening only on the container's localhost address will make Vite inaccessible from outside.

When using the Laravel Vite plugin with Laravel Sail, we automatically set this value for you unless the user config provides something different:

https://github.com/laravel/vite-plugin/blob/64bef2b3a622a1a9d45c8d7ecccfc93fc9e2dfc9/src/index.ts#L136-L140

I'm wondering whether the server.apps.local setting you had was causing the issue. Depending on what address this resolves to, Vite may not have been able to bind to it if the address did not belong to one of the network devices in your container.

stoffpalette commented 2 years ago

@jessarcher

something is strange here.

Why do i have to add --host (without setting the host value here) inside my package.json file although i had already set the server.host value (and still do) within my vite.config.js ?

I just checked it again, it definitly works only, when i just add --host inside package.json.

Here is my current vite.config.js:

import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({
    server: {
        hmr: {
            host: 'server.apps.local'
        },
        host: 'server.apps.local'
    },
    plugins: [
        laravel({
            input: [
                'resources/sass/app.scss',
                'resources/js/app.js'
            ],
            refresh: true
        })
    ]
});

Note: server.apps.local just points to 127.0.0.1 inside the windows hosts file

jessarcher commented 2 years ago

If server.apps.local resolves to 127.0.0.1 then by specifying it for server.host you're telling Vite to only listen on the localhost of the container (which is not the localhost of your host machine) which would make Vite inaccessible outside of the container.

If running Vite inside Sail's main container, then you should be able to remove server.host from your config and remove the --host flag because we automatically configure it for you. If you're using a different Docker setup, then you should either set server.host to true or use the --host option.

stoffpalette commented 2 years ago

@jessarcher i just tested what you described - these combination only work, when i additionally set server.hmr.host to 'server.apps.local'

working example with server.hmr.host and without server.host and --host option:

vite.config.js:

import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({
    server: {
        hmr: {
            host: 'server.apps.local'
        }
    },
    plugins: [
        laravel({
            input: [
                'resources/sass/app.scss',
                'resources/js/app.js'
            ],
            refresh: true
        })
    ]
});

package.json (snippet):

    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },
Geovanek commented 2 years ago

I'm using Sail withi Laravel UI.

Just adding this solving my problem:

server: {
        hmr: {
            host: 'localhost'
        }
    },
benfreke commented 2 years ago

For the sake of completeness, and since this is the only issue I could find that referenced the problem in the repository.

I run Laravel behind a reverse proxy, with every service separated. So there is a separate Node, PHP, Workers, Nginx, etc. For me, I had to make 2 changes to the vite config for this to work, removing the need to add --host when starting vite. Both of those changes are explained above if you read the entire thread btw, this just puts it in the one place for future readers of this thread.

One is setting the server.hmr.host value to be the domain I'm accessing the site on (in my case it is myapp.test), and the other is to set server.host to true to allow Vite to broadcast to all domains that attempt access.

I don't know why exactly I need both, but this is the minimal amount of work needed to get Vite working (assuming you're exposing the ports correctly).

Vite config

server: {
        host: true,
        hmr: {
            host: 'myapp.test'
        }
    },
    plugins: [
        laravel({
            input: 'resources/js/app.jsx',
            refresh: true,
        }),
        react(),
    ],

If you are running a separate node container, make sure to expose the port that Vite is using!

Node service

  node:
    image: node:18-slim
    init: true
    user: node
    environment:
      - NODE_ENV=development
    volumes:
      - "~/.npm:/usr/local/npm:delegated"
      - "./:/home/node/app:delegated"
    working_dir: "/home/node/app"
    ports:
      - "5173:5173"
    command: "npm run dev"
Aigarsss commented 1 year ago

For me, this was fixed by: 1) Running npm run dev outside of the docker container (with vite --host). So I ran the command on my machine, rather than within the docker container. 2) Second thing, I removed the commented out entries from my config. I think the watch option might have been messing it up.

   server: {
        hmr: {
            host: 'localhost'
        }
        // host: true,
        // watch: {
        //     usePolling: true
        // }
    },

Now it works perfectly.

Aigarsss commented 1 year ago

Spoke too soon, it works only with pages, that don't use passed page props (like login page). After login, page load fails, saying, that the data cannot be retrieved.

greaterking commented 1 year ago

(I know this is a closed thread...but none of the above worked for me at all) This eventually worked for me: https://balajidharma.medium.com/how-to-migrating-from-laravel-mix-to-vite-fbb912237844

server: {
        host: '0.0.0.0',
        hmr: {
            host: 'localhost',
        },
        watch: {
            usePolling: true,
        },
    }

you may or may not need

 watch: {
      usePolling: true,
  },
retifex commented 1 year ago

When following the Laravel Bootcamp, I also ran into the issue described above with Vite not serving CSS. I'm using Docker Desktop v4.22.0 with WSL2 on Win 10 Education 22H2 64-bit, if that helps.

@greaterking Thanks a million, that solution fixed it. I think that should be added to the bootcamp page: https://bootcamp.laravel.com/blade/installation#installing-laravel-breeze (and maybe also https://bootcamp.laravel.com/inertia/installation#installing-laravel-breeze)