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

[9.x] Vite asset url helper #43702

Merged
merged 20 commits into from
Aug 17, 2022
Merged

[9.x] Vite asset url helper #43702

merged 20 commits into from
Aug 17, 2022

Conversation

timacdonald
Copy link
Member

@timacdonald timacdonald commented Aug 15, 2022

When using plain Blade as your templating platform, it can be useful to still process images that are not referenced in your JS with Vite - it may even be the case that you don't have any JS and still want to minify your projects' images.

This PR introduces a Vite::asset() helper, which can be used in Blade templates to generate the URLs to your projects build assets.

With the following manifest:

{
  "resources/images/logo.jpeg": {
    "file": "assets/logo.1ddf943b.jpeg",
    "src": "resources/images/logo.jpeg"
  },
  "resources/js/app.js": {
    "file": "assets/app.e5198d1b.js",
    "src": "resources/js/app.js",
    "isEntry": true
  }
}

You can now generate a URL to the profile image in Blade with the following:

<img src="{{ Vite::asset('resources/images/logo.jpeg') }}">

Which would result in the following HTML:

<img src="http://asset-url.com/build/assets/logo.1ddf943b.jpeg">

In build mode, this will generate a URL to the dev server, resulting in the following HTML:

<img src="http://[::1]:5173/resources/images/logo.jpeg">

For more information on including static assets in your applications build, see laravel/docs#8124

@timacdonald timacdonald changed the title [9.x] Vite asset [9.x] Vite asset url helper Aug 15, 2022
@timacdonald timacdonald requested a review from jessarcher August 15, 2022 04:01
@timacdonald timacdonald marked this pull request as ready for review August 15, 2022 04:28
@timacdonald timacdonald marked this pull request as draft August 16, 2022 07:24
@timacdonald timacdonald marked this pull request as ready for review August 16, 2022 23:50
@taylorotwell taylorotwell merged commit ffee0c0 into laravel:9.x Aug 17, 2022
@vlados
Copy link

vlados commented Aug 17, 2022

Currently vitejs is generating:

  "resources/images/logo-1024-01.jpg": {
    "file": "assets/logo-1024-01.a480100e.js",
    "src": "resources/images/logo-1024-01.jpg",
    "isDynamicEntry": true,
    "assets": [
      "assets/logo-1024-01.d91e6c7b.jpg"
    ]
  },

So if we use file attribute in the blade the image src will be the .js file.
Why not use assets[0]?

@timacdonald timacdonald deleted the vite-tags branch August 17, 2022 23:05
@timacdonald
Copy link
Member Author

Hey @vlados, I'm unable to generate a manifest with what you have shown here. Here is what I get with a fresh project install...

Importing images in your app.js:

import './bootstrap';
import '../images/profile.png'

results in:

{
  "resources/images/profile.png": {
    "file": "assets/profile.1f676f34.png",
    "src": "resources/images/profile.png"
  },
  "resources/js/app.js": {
    "file": "assets/app.334e7359.js",
    "src": "resources/js/app.js",
    "isEntry": true
  }
}

Globing your images in the app.js:

import './bootstrap';
import.meta.glob('../images/**')

results in:

{
  "resources/images/profile.png": {
    "file": "assets/profile.1f676f34.png",
    "src": "resources/images/profile.png"
  },
  "resources/js/app.js": {
    "file": "assets/app.334e7359.js",
    "src": "resources/js/app.js",
    "isEntry": true
  }
}

Referencing an image from a Vue component:

<script setup>
// ...
</script>
<template>
    <Head title="Welcome" />

    <img src="../../images/profile.png">
</template>

results in:

{
  "resources/images/profile.png": {
    "file": "assets/profile.1f676f34.png",
    "src": "resources/images/profile.png"
  },
  "resources/js/app.js": {
    "file": "assets/app.d0e20605.js",
    "src": "resources/js/app.js",
    "isEntry": true,
    "dynamicImports": [
      "resources/js/Pages/Welcome.vue"
    ],
    "css": [
      "assets/app.1fc51312.css"
    ]
  },
  "resources/js/Pages/Welcome.vue": {
    "file": "assets/Welcome.6e7da9ff.js",
    "src": "resources/js/Pages/Welcome.vue",
    "isDynamicEntry": true,
    "imports": [
      "resources/js/app.js"
    ],
    "assets": [
      "assets/profile.1f676f34.png"
    ]
  },
  "resources/js/app.css": {
    "file": "assets/app.1fc51312.css",
    "src": "resources/js/app.css"
  }
}

Could you please share the minimal reproduction steps required to generate the manifest you have?

@vlados
Copy link

vlados commented Aug 18, 2022

I found the problem. When using assetsInclude in vite.config.js

export default defineConfig({
    assetsInclude: ["**/*.mp4"],
    plugins: [
        laravel({
            input: ["resources/css/app.css", "resources/js/app.ts", "resources/js/wireui.ts"],
        }),
    ],
});

This is generating all assets as mentioned before.
Removing this line is fixing the problem and the manifest.json is correct

@timacdonald
Copy link
Member Author

timacdonald commented Aug 19, 2022

Awesome!

assetsInclude is not meant to be used to include files in the manifest directly, but instead to tell Vite when parsing JavaScript what it should consider a static asset (which it will then put in the manifest if it comes across it).

For example, imagine you had the following Vue template:

<template>
    <Head title="Welcome" />

    <img src="../../images/profile.png">
</template>

Vite detects the image and makes it a "static" file that it treats differently - it copies it to the build directory and puts something in the manifest. However, imagine for whatever reason we rename profile.png to profile.foo:

<template>
    <Head title="Welcome" />

    <img src="../../images/profile.foo">
</template>

Vite will now explode...

Screen Shot 2022-08-19 at 10 47 30 am

Vite doesn't know about .foo files, so it will try and parse the file and put it in the JavaScript as if it was source code. We don't want this - we want it to be a "static" asset. So now what we need to do is to tell Vite to treat .foo files as a static asset:

export default defineConfig({
    assetsInclude: '**/*.foo',

    /* ... */
});

The build will now be successful, our profile.foo will be in our manifest and build directory, and our image will display in the browser.

@vlados
Copy link

vlados commented Aug 19, 2022

Thank you @timacdonald for the extensive explanation! ❤️

@timacdonald
Copy link
Member Author

No troubles at all

@fflnvb
Copy link

fflnvb commented Aug 23, 2022

UPDATE: Solved it myself. There still was a file inside my public called hot, which seems to be an indicator of HMR is enabled or not. Had to delete is manually and it works now. Any possibility to get it removed once npm run build is being pushed? Sorry for the disturbance.
Hi timacdonald,
I am using the function right now by building the assets for my js and css via npm run build and on my preview and instead of the path the result on my Website is looking like this (uppercase inserted by me):
http://HOST:ENTRYPOINT/resources/js/app.js
I can see that this is supposed to be displayed when HMR or Hotmode is enabled.

What do I have to do in order to get the versioned file path for the asset as described in docs?

Can´t find anything on the web regarding disabling HMR.
Manifest seems to be looking fine:
{ "resources/js/app.js": { "file": "assets/app.aac2dc08.js", "src": "resources/js/app.js", "isEntry": true }, "resources/sass/app.scss": { "file": "assets/app.c090eeea.css", "src": "resources/sass/app.scss", "isEntry": true } }

@timacdonald
Copy link
Member Author

Hey there,

The plugin is already setup to clear the hot file when npm run dev is killed. Something funky must have happened for you.

Also, make you have have added the hot file to your git ignore as per the upgrade guide / new app skeleton - that way you will never push the hot file to your server.

@IronSinew
Copy link

IronSinew commented Aug 27, 2022

@timacdonald I too have the same issue that @vlados ran into on my vue/inertia project. This is an example entry in my manifest.json

  "resources/static/images/public/startup-business.png": {
    "file": "assets/startup-business.07540cc0.js",
    "src": "resources/static/images/public/startup-business.png",
    "isDynamicEntry": true,
    "assets": [
      "assets/startup-business.195cf5f8.png"
    ]
  },

This is my vite.config.js

import vue from "@vitejs/plugin-vue";
import laravel from "laravel-vite-plugin";
import {defineConfig, splitVendorChunkPlugin} from "vite";

export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/public.js"],
            refresh: ["resources/views/**"]
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false
                }
            }
        }),
        splitVendorChunkPlugin()
    ],
    resolve: {
        alias: {
            "asset": "resources/static/images",
            "webfonts": "resources/static/webfonts"
        }
    }
});

Contents of the js file:

const s="/build/assets/startup-business.195cf5f8.png";export{s as default};

Vite::asset() works correctly with npm run dev .. but not so happy with npm run build.

@timacdonald
Copy link
Member Author

Before we get into debugging this @IronSinew I just wanna ask if you are using Inertia and Vue, do you have an actual need for the asset helper?

You should just reference files in your Vue templates:

<template>
    <img src="../../path/to/image.jpg">
<template>

@IronSinew
Copy link

IronSinew commented Aug 30, 2022

Before we get into debugging this @IronSinew I just wanna ask if you are using Inertia and Vue, do you have an actual need for the asset helper?

Thanks for the reply @timacdonald, we wanted to keep our public-facing files in blade and internal application in inertia so we wouldn't have to set up SSR (for SEO purposes, etc). One could argue that that is an actual need for using the asset helper.

@timacdonald
Copy link
Member Author

Makes sense. Could you put together a minimal reproduction project so I can see what is happening and help you with the issue:

laravel new bug-report --github="--public"

@IronSinew
Copy link

@timacdonald I created a reproduction project but couldn't reproduce the issue. The only difference between the projects was updated npm deps. Our project is fairly up-to-date, so figured it wouldn't hurt to update our JS dependencies anyway. After an npm update and composer update, running through all of the tests and running the npm run build .. it seems to have fixed itself. I'm not sure if there is some minimum dependency version for it to work properly, but it seems to build the asset properly with up-to-date packages. Thanks for beginning the debugging process with me, though. 👍

@timacdonald
Copy link
Member Author

No troubles at all and I'm glad you got it sorted.

@prattcmp
Copy link

prattcmp commented Sep 13, 2022

Screen Shot 2022-09-12 at 8 27 45 PM

I'm experiencing the same issue as @IronSinew but my packages are updated. Not sure what could be going wrong.

@chrispage1
Copy link
Contributor

chrispage1 commented Sep 14, 2022

Thanks for implementing this feature into Laravel core. However, it would have been nice to reference my PR which this code appears to have taken a fair bit of inspiration from #43098

It's a shame that fewer lines of code was rejected as 'we need to be very careful regarding the amount of code we include' being the reason cited. Nothing like a bit of open-source community spirit :)

@timacdonald
Copy link
Member Author

timacdonald commented Sep 16, 2022

I apologise I didn't reference your previous PR @chrispage1.

When you first opened your PR we didn't see the need for the feature, but later came to realise it was a useful feature. I had unfortunately forgotten about your previous PR for this feature when I opened this one.

We appreciate your suggestions and contributions and sometimes I need to see something a few times to really see the community need for it.

I'm sorry if I rubbed you the wrong way with this and I hope it doesn't put you off continuing to contribute and help us make Laravel better in the future.

Thanks @chrispage1 ❤️

@chrispage1
Copy link
Contributor

No worries @timacdonald it's one of those things. I look forward to one day hoping to make a contribution that does get merged! 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants