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

manifest.json does not include assets #2375

Closed
thasmo opened this issue Mar 4, 2021 · 20 comments
Closed

manifest.json does not include assets #2375

thasmo opened this issue Mar 4, 2021 · 20 comments

Comments

@thasmo
Copy link

thasmo commented Mar 4, 2021

Describe the bug

Statically imported assets (SVG file in my case) will get bundled and created in the outDir, but are missing in the generated manifest.json.

vite.config.js

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

export default defineConfig({
    base: '/static/',
    publicDir: 'assets/static/',
    build: {
        manifest: true,
        outDir: 'public/static/',
        assetsInlineLimit: 0,
        rollupOptions: {
            input: 'assets/main.ts',
        },
    },
    plugins: [legacy()],
});

assets/main.ts

import './icon.svg';

manifest.json

{
  "assets/main-legacy.ts": {
    "file": "assets/main-legacy.dd90b2c6.js",
    "src": "assets/main-legacy.ts",
    "isEntry": true
  },
  "assets/main.ts": {
    "file": "assets/main.9470b65c.js",
    "src": "assets/main.ts",
    "isEntry": true
  }
}

output files

  • main.9470b65c.js
  • main-legacy.dd90b2c6.js
  • icon.a0d73e22.svg

System Info

  • vite version: 2.0.5
  • Operating System: Windows 10
  • Node version: 12.16.1
  • Package manager (npm/yarn/pnpm) and version: npm 7.5.3
@thasmo
Copy link
Author

thasmo commented Mar 4, 2021

It seems this happens when using the legacy plugin. Removing the legacy plugin resolves the issue. In addition, I noticed when setting build.cssCodeSplit: false CSS files won't be generated/created if the legacy plugin is used. On top of that, if the legacy plugin is disabled and build.cssCodeSplit: false is still set, the CSS files will be generated, but are still missing in the manifest.json.

@thasmo
Copy link
Author

thasmo commented Mar 27, 2021

repro repo: https://github.com/thasmo/vite-2375

Anything else I can provide? Thank you!

@liuhanqu
Copy link

I have encounter same question.
use build.cssCodeSplit: false would miss css in the manifest.json

@thasmo
Copy link
Author

thasmo commented Jun 13, 2021

It seems that with the latest vite@2.3.x version, even without the legacy plugin, the issue now pops up. Statically imported images will be copied to the outDir folder, but do not show up in the generated manifest.json file.

I realize that this behavior is probably something which is not needed in modern, JS-based (PWA) applications, but in our case, when using vite in a more classic backend-rendered (PHP) application, not having all imported assets in the manifest.json file, results in not being able to render/access those assets in the first place.

Is there any chance it would be considered for vite to list all imported assets in manifest.json for this reason? Thanks a lot!

@thasmo
Copy link
Author

thasmo commented Jun 13, 2021

@Shinigami92 Would you be okay with removing the plugin: legacy tag as I think the issue now also occurs without it? Thanks!

@brycewray
Copy link

brycewray commented Jul 23, 2021

Am also seeing this behavior (Vite 2.4.3). With a plain vanilla create-vite repo with zero plugins, I imported CSS, JS, font, and image files. All were generated with hashed file names, but only the CSS and JS files were mentioned in manifest.json.

@engram-design
Copy link

engram-design commented Jul 27, 2021

Also seeing this, and like @thasmo we're using Vite in a hybrid approach where once built a PHP back-end takes over. The files are indeed copied to the dist directory (in our case, we want to run them through imagemin), but are missing from the manifest.

vite.config.js

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    manifest: true,
  },
})

src/main.js

import './icon.svg';

manifest.json

{
  "index.html": {
    "file": "assets/index.aa78867d.js",
    "src": "index.html",
    "isEntry": true
  }
}

That's despite the actual dist folder containing the asset.

assets/index.aa78867d.js
assets/icon.03d6d6da.svg
index.html
manifest.json

I've gone all the way back to Vite v2.0.0 and this has never worked it seems?

The only way I have got this to work is if I actively use the asset somehow. For instance, doing:

import icon from './icon.svg';
console.log(icon)

Will add it to the manifest exactly as I expect. Not sure if this is a bug, or expected behaviour?

@khalwat
Copy link
Contributor

khalwat commented Jul 27, 2021

If I had to guess, imports are probably promises that are lazily resolved. So it isn't until an import is actually used (thus the behavior that @engram-design is seeing) that it'd be added to any manifest, just as a resource that can't be resolved (404 for example) isn't added either.

So I'm guessing this probably is a feature, not a bug, in that unused resources (unresolved promises) are not included in the production build.

The behavioral expectation likely comes from experience with task runners, rather than loaders.

@brycewray
Copy link

@engram-design That gets me over the hump in handling this for other types of assets. Much obliged! And thanks, also, to @khalwat for the educated guess, which suggests we shouldn’t wait for a fix if, indeed, it’s intentional rather than a bug.

@engram-design
Copy link

What threw me was the fact that it was processing the file - moving it to dist but not adding it to the manifest. If the file is deemed not required, should it even end up in dist?

I'll say that in my case, I mention the use of imagemin. As @khalwat rightly guessed, I'm coming from Webpack (Laravel Mix) where I would have as part of my production build process, a task that scans the src/img/**/* folder for any and all images, processes them, and copies them to dist/img.

I'm trying to replicate that by using vite-plugin-imagemin which works perfectly, but only for assets used in Vue components, or CSS files. There are cases I want to use these assets in Twig templates, outside of Vite's awareness. Hence, my journey to try and "add" every asset to the manifest.

But I've pivoted back to my original goal, and forked the vite-plugin-imagemin plugin to allow direct copying from src/img to dist/img. In my Twig templates, I can now refer to these specifically via /dist/img/my-image.png upon build. During development these are resolved to use the dev server, http://localhost:3000/img/my-image.png.

It's quite likely me scenario is different, but just thought I'd chime in - just in case anyone has a similar scenario and how to approach this. My approach using Craft CMS for the project involves Twig server-side code, but also a sprinkling of Vue, so it's very much a hybrid approach, similar to a lot of people in the Laravel community.

@brycewray
Copy link

brycewray commented Jul 27, 2021

@engram-design Yes. Now I have to get my CSS’s @font-face to point to the hashed font file. I see there’s postcss-url so I’m now looking into that.

Edit: Looks like that ain’t gonna cut it. 😄 Still looking.

Edit 2: The reason I’m trying to figure out this latest item is because Vite isn’t converting my @font-face src: url() to the necessary URL, although the docs say this is automatic. Here's my current vite.config.js, which — like most of my repo’s Vite-related config — is borrowed heavily from the eleventy-with-vite repo:

import { defineConfig } from "vite";
import legacy from "@vitejs/plugin-legacy";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [legacy()],
  build: {
    outDir: "_site",
    assetsDir: "assets-vite", // default = "assets"
    // Sourcemaps are nice, but not critical for this to work
    sourcemap: true,
    manifest: true,
    rollupOptions: {
      input: "/src/client/main.js",
    },
  },
  css: {
    postcss: {
      plugins: [
        require('postcss-import'),
        require('tailwindcss'),
        require('autoprefixer'),
        require('postcss-nesting'),
        require('postcss-csso'),
      ],
    }
  }
})

Edit 3: Also, my entry file:

import "vite/dynamic-import-polyfill"
import "../assets/js/lazyload.min.js"
import "../assets/js/lazyload-helper.js"
import "../assets/js/mobile-menu-helper.js"
import font from "../assets/fonts/Inter-VF_subset.woff2"
console.log(font) // see https://github.com/vitejs/vite/issues/2375
import "../assets/css/index.css"

Edit 4: Trying a similar import on a bare-bones Vite (not Eleventy/Vite) repo, the @font-face url() does get the proper treatment, so this would appear not to be a Vite limitation.

@ElMassimo
Copy link
Contributor

@Shinigami92 This behavior is intentional, at least at the time of this comment: #1774 (comment)

@Shinigami92
Copy link
Member

@Shinigami92 This behavior is intentional, at least at the time of this comment: #1774 (comment)

Thanks for the info
Should I close this issue then and mark as wont fix?

@ghost
Copy link

ghost commented Aug 11, 2021

If it is intentional to exclude assets from the manifest(s),

  1. Why is there nothing about this in the docs at https://vite-ruby.netlify.app/guide/migration.html#manual-steps or https://vitejs.dev/config/#resolve-dedupe ?
  2. How does one resolve the issue? What is the workaround? const images = import.meta.glob('../images/.') and then console.log(images)? config option?

@ElMassimo
Copy link
Contributor

@tgrushka Vite Ruby does not mention it because it generates its own manifest-assets.json to cover for this missing feature in Vite.js.

@ghost
Copy link

ghost commented Aug 11, 2021

No, I checked both manifest files, and neither includes required assets unless imported AND used in a JS file. Further, the keys are wrong in that case, as the keys are literally "../images/something" which is not consistent with they way it worked with webpacker. All of these JS bundlers are so configuration heavy. They say they work, but not for images, not for SVGs, not for Vue components... Isn't there one that actually works as a drop-in replacement for webpacker and isn't so brittle? (The whole reason for switching away from webpacker/webpack is because it's always breaking things and not intuitive, but then if all the alternatives are just as troublesome to figure out, and the docs are just as bad, what is the incentive to switch?)

@ElMassimo
Copy link
Contributor

This thread is about Vite.js, please open a Discussion in Vite Ruby if you need help migrating from Webpacker.

@rynpsc
Copy link

rynpsc commented Aug 30, 2021

Had a look and Vite is already tracking and outputting any assets regardless of their usage so I think it makes sense to expose them in the manifest so those integrating with a backend can take advantage of Vite for processing assets not directly used by Vite.

In my testing importing static assets via import.meta.glob is enough for Vite to process them even if not referenced.

src
├── entry.scss
├── files
│   └── favicon.svg
├── fonts
│   ├── my-font.woff
│   └── my-font.woff2
├── main.scss
└── main.ts

vite.config.js

export default defineConfig({
  build: {
    rollupOptions: {
      input: 'src/main.ts'
    }
  }
})

src/main.ts

import './main.scss'

let files = import.meta.glob('./files/**/*');

// The "files" variable doesn't need to used for imports to get processed.

src/main.scss

@font-face {
  font-family: "Font";
  font-style: normal;
  font-weight: 400;
  src:
    url("/src/fonts/my-font.woff2") format("woff2"),
    url("/src/fonts/my-font.woff") format("woff");
}

With the above Vite will process and output files matching the glob ./files/**/*. The only missing bit is a way to get the final path. Looking at generateBundle the asset chunks are present but not exposed so I propose we add a _assets entry within the manifest that contains all assets Vite has processed e.g.

{
  "_assets": {
    "src/files/favicon.svg": "assets/favicon.17e50649.svg",
    "src/fonts/my-font.woff2": "assets/my-font.bf47658a.woff2",
    "src/fonts/my-font.woff": "assets/my-font.80d4cac9.woff",
    "main.css": "assets/main.eac10a5f.css"
  },
  "src/main.ts": {
    "file": "assets/main.6097d7e4.js",
    "src": "src/main.ts",
    "isEntry": true,
    "css": [
      "assets/main.eac10a5f.css"
    ],
    "assets": [
      "assets/my-font.bf47658a.woff2",
      "assets/my-font.80d4cac9.woff"
    ]
  }
}

The _assets entry contains assets both used within chunks i.e. fonts and non used assets i.e. those imported via the glob. This is desirable as it then allows you to more easily reference files like fonts for things like generating preload directives and other assets such as favicons or static assets referenced in the frameworks templating language.

I've quickly modified vite/packages/vite/src/node/plugins/manifest.ts to achieve the above but I'm likely missing something.

if (chunk.type === 'asset') {
  if (!manifest['_assets']) {
    manifest['_assets'] = {}
  }
  let name = normalizePath(
    path.relative(config.root, chunk.name)
  )
  manifest['_assets'][name] = chunk.fileName
}

The only thing that isn't quite right is how css is handled as Vite considers it an asset but the full path is stripped by the time the manifest is generated i.e. instead of src/main.scss it appears as main.css.

I'm not sure if we should remove the CSS properties or try to add the full path back in. If we did it keep it and use the source path it would then be possible to use css as an entry.

export default defineConfig({
  build: {
    rollupOptions: {
      input: [
        'src/main.ts',
        'src/entry.scss',
      ]
    }
  }
})

This is currently possible (not sure if intentional) but you'd have to remember to reference your css by the file name (entry.scss) rather than path (src/entry.scss) and I'm assuming that relying on filename only will result in conflicts.

<link rel="stylesheet" href="/assets/{{ manifest['_assets']['entry.css'] }}" />

But by keeping the original path things would work as expected.

{
  "_assets": {
    "src/files/favicon.svg": "assets/favicon.17e50649.svg",
    "src/fonts/my-font.woff2": "assets/my-font.bf47658a.woff2",
    "src/fonts/my-font.woff": "assets/my-font.80d4cac9.woff",
    "src/main.scss": "assets/main.eac10a5f.css",
    "src/entry.scss": "assets/entry.wef646xf.css"
  },
  "src/main.ts": {
    "file": "assets/main.6097d7e4.js",
    "src": "src/main.ts",
    "isEntry": true,
    "css": [
      "assets/main.eac10a5f.css"
    ],
    "assets": [
      "assets/my-font.bf47658a.woff2",
      "assets/my-font.80d4cac9.woff"
    ]
  }
}
<link rel="stylesheet" href="/assets/{{ manifest['_assets']['src/entry.scss'] }}" />

To summarise there are two things to look at:

  • Exposing all assets in the manifest via something like _assets
  • Providing the full source path for css

@thasmo
Copy link
Author

thasmo commented Oct 12, 2021

I feel like a "web bundle manifest" specification/standard would be really helpful. As some already mentioned, I would also assume all files, which end up in the final bundle/dist/build directory, to be present in the manifest.

@bluwy
Copy link
Member

bluwy commented Jun 26, 2022

Fixed in #6649

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

No branches or pull requests

9 participants