vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
67.3k stars 6.05k forks source link

Add import directive for html attributes #7362

Open grievouz opened 2 years ago

grievouz commented 2 years ago

Clear and concise description of the problem

Vite or rather the html entry plugin only resolves some attributes of some tags in index.html. For example <link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" /> will be compiled to <link rel="icon" type="image/svg+xml" href="/assets/favicon.[id].svg" />

I encountered it during the process of optimizing my website for social sharing through the addition of meta tags like <meta property="og:image" content="<%- VITE_APP_HOST_URL %>/assets/og-image.jpg" /> As you can see already, I solved the problem by using env variables and the public folder to serve the images as static files, which I personally strongly detest due to the issues or inconsistencies that can arise from caching.

I looked through the other issues and if I understood them properly this issue is also related to #5098 and #2270

Suggested solution

My suggestion would be to add a directive, or whatever other name will be used if this is implemented, to allow the user to explicitly add an attribute that should be resolved by the html plugin.

Let's take my meta tag from the above example: <meta property="og:image" content="<%- VITE_APP_HOST_URL %>/assets/og-image.jpg" /> could then be changed to <meta property="og:image" content="import:/src/assets/og-image.jpg" /> and would be compiled to <meta property="og:image" content="/assets/og-image.[id].jpg" />

This could also be taken one step further or just for consistency by allowing for suffixes like ?url, ?raw or ?inline.

My simple implementation to test my proposal added about 20 lines of code, but it didn't add support for the suffixes or accounted for something like the srcset attribute.

Alternative

An alternative would be to just add a config option for users to define the additional tags and the corresponding attributes that should be resolved by the plugin. Though without additional filter logic, this could result in additional problems. Let's take my example again, I would want it to resolve my <meta/> tags with the content="" attribute but I would want it to ignore <meta/> tags with the attribute property="og:description" or property="og:title" due to the fact that those contain just some text.

Additional context

No response

Validations

bluwy commented 2 years ago

This sounds like a more generalized version of https://github.com/vitejs/vite/issues/3000#issuecomment-1059930509, which I like! I think Vite can expand more on the default set of tags to convert into imports, before needing to resort to a import: directive.

Currently the set is defined here, and could be easily expanded out I think. html-loader has a great set of defaults that we could reference. I've applied the same for a Svelte preprocessor I made too.

grievouz commented 2 years ago

Your comment is precisely what I was elaborating on in the alternative I provided. Though I believe that in my haste to write the issue, I dismissed it too quickly. I agree with you that it's probably ideal to expand on the default set before attempting to add unique syntax.

nleroy917 commented 1 year ago

Hey guys, I came from #5292

I can't seem to get vite to display my open graph image. I might be overlooking this but here is my code:

import { FC } from 'react';
import { Helmet } from 'react-helmet-async';

interface Props {
  title?: string;
  description?: string;
  image?: string;
}

export const SEO: FC<Props> = ({ title, description, image }) => {
  const pageTitle = title || 'PEPhub';
  const pageDescription =
    description || '...';
  const pageImage = image || '/og-image.png';
  const pageUrl = '...';
  const pageType = 'website';
  return (
    <Helmet>
      <title>{pageTitle}</title>
      <meta name="description" content={pageDescription} />
      <meta property="og:description" content={pageDescription} />
      <meta property="og:title" content={pageTitle} />
      <meta property="og:image" content={pageImage} />
      <meta property="og:type" content={pageType} />
      <meta property="og:url" content={pageUrl} />
    </Helmet>
  );
};

I can inspect the HTML on the browser and see that its putting in og-image.png. moreover, I can grab it at http://my-host/og-image.png. However, it doesn't render as a preview using all my local testing tools. I also cant see it with vite build && vite preview.

Any help is appreciated!

rshekhtm commented 10 months ago

I think I had a similar issue where I was trying to import the favicon from a location outside of the app. I have defined a ~ alias to point to the root of the project, but found that paths starting with ~/ do not resolve in index.html. I solved it as follows in vite.config.js, which may provide a path forward for folks that need to do something similar:

import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig(({ mode }) => {
    function pathResolver(mode) {
        // Resolves ~ in index.html
        return {
            name: "path-resolver",
            transformIndexHtml: {
                order: "pre",
                handler(html) {
                    // Paths must be handled differently depending on mode
                    let resolvedPath = mode == "development" ? "@fs" + path.resolve(__dirname, "..") : "..";
                    return html.replace(new RegExp("~/"), resolvedPath + "/");
                }
            }
        }
    }

    return {
        plugins: [pathResolver(mode)],
        resolve: {
            alias: {
                '~': path.resolve(__dirname, '..'),
            },
        },
        ...
    }
})

Now the following works in index.html both dev and build/preview: <link rel="icon" type="image/svg+xml" href="~/common/images/favicon.svg" />

tiborsaas commented 8 months ago

I've found a hack to the this work with a simple config, it might not be your cup of tea, but it works to have the full path to OG image meta tags.

The idea is to to remove hashes from the generate filenames:

https://www.fabiofranchino.com/log/how-to-remove-hashing-in-vite-built-file-names/

Then I simply add this at the import section of my main.js file

import prev from '../assets/preview.png';

Then I can hardcode the domain in the meta tags.

camsteffen commented 7 months ago

Are there any simple workarounds?

camsteffen commented 7 months ago

Looks like useSeoMeta from unhead is a good solution for Vue/Nuxt users.

jimblue commented 5 months ago

Meta property content type can be a simple string, but something this string is a static assets path. The most common examples are the meta properties for social network : og:image and twitter:image whose need a path to an image.

<meta property="og:image" content="/src/og-image.jpg" />
<meta property="twitter:image" content="/src/twitter-image.jpg" />

But Vite build handle this type of assets for now, even if the images exist locally.

Here is a stackblitz link that reproduce the issue.

If you run npm run build and look at the assets folder, you'll see the build image of body-image.jpg but none of the images used in meta property tag og-image.jpg and twitter-image.jpg.

j1mmie commented 1 month ago

This issue is particularly troublesome because there's no obvious visual side effect. It's only apparent when testing the Open Graph features (for example, sharing on social media).