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

@vitejs/plugin-vue does not allow absolute asset URLs to be preserved as-is #4836

Closed
7 tasks done
its-dlh opened this issue Sep 3, 2021 · 5 comments
Closed
7 tasks done

Comments

@its-dlh
Copy link

its-dlh commented Sep 3, 2021

Describe the bug

I've been experimenting with using Vite with WordPress, in place of an existing Webpack config. I've mostly gotten it working, but I can't find an elegant way to handle absolute paths when doing things like <img src="/my/absolute/path.png"> in my (Vue 3) SFCs.

For example, if I do <img src="/wp-content/themes/ff-bulma/assets/images/close-button.png" alt="Close">, I want it to request that absolute path as-is, instead of trying to resolve it as a module, because the backend is already serving the image at that path.

When doing a vite build, Rollup sees those paths as module imports and attempts to resolve them, resulting in a failed build. If I set...

{
  build: {
    rollupOptions: {
      external: [
        /^\/wp-content\//
      ]
    }
  }
}

...then at least Rollup stops complaining, but the bundled script itself tries to import the PNG as an ES module and gets blocked by the browser "because of a disallowed MIME type (image/png)". Setting template: { transformAssetUrls: false } in the Vue plugin config has no effect.

The only way I've found to make it work is to do <img :src.attr="'/wp-content/themes/ff-bulma/assets/images/close-button.png'" alt="Close"> in my Vue SFC, but it seems like there should be a way to handle it via config, especially since I had it working when using Webpack.

To put it another way, I've been relying on vue-loader's documented behavior, where "If the URL is an absolute path (e.g. /images/foo.png), it will be preserved as-is." I am looking for a way to get the same behavior with @vitejs/plugin-vue.

Reproduction

Here's a simplified example:
https://stackblitz.com/edit/vue-3-coaches-2blvac?file=my-absolute-path/src/App.vue

The whole dir is being served by http-server, and Vite builds from /my-absolute-path/src/ into /my-absolute-path/dist/. Look in App.vue for examples of <img src>.

Uncomment external: [/^\/my-absolute-path\//] in vite.config.js to eliminate the Rollup error. However, this introduces a MIME error in your browser, due to the browser trying to load the SVG asset as an ES module.

Now, comment out the <img> tag in App.vue that is labeled as not working on Vite, and you can confirm that the other one always works (even with that external line commented out in the config). However, the one that does works is not ideal for DX, and is not necessary when using vue-loader with Webpack.

System Info

System:
    OS: Linux 5.13 Arch Linux
    CPU: (8) x64 Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
    Memory: 1.07 GB / 19.45 GB
    Container: Yes
    Shell: 5.1.8 - /bin/bash
  Binaries:
    Node: 14.17.5 - ~/.nvm/versions/node/v14.17.5/bin/node
    Yarn: 1.22.10 - ~/.nvm/versions/node/v14.17.5/bin/yarn
    npm: 7.20.5 - ~/.nvm/versions/node/v14.17.5/bin/npm
  Browsers:
    Chromium: 92.0.4515.159
    Firefox: 91.0.2
  npmPackages:
    @vitejs/plugin-vue: ^1.6.0 => 1.6.0 
    vite: ^2.5.3 => 2.5.3

Used Package Manager

pnpm

Logs

vite:config bundled config file loaded in 101ms +0ms
[dotenv][DEBUG] did not match key and value when parsing line 1: # Environment Variables
[dotenv][DEBUG] did not match key and value when parsing line 2: 
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'alias',
  vite:config     'vite:modulepreload-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:vue',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'rollup-plugin-dynamic-import-variables',
  vite:config     'asset-import-meta-url',
  vite:config     'vite:import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:terser',
  vite:config     'vite:manifest',
  vite:config     'vite:reporter',
  vite:config     'load-fallback'
  vite:config   ],
  vite:config   base: '/wp-content/themes/ff-bulma/dist/',
  vite:config   resolve: {
  vite:config     dedupe: undefined,
  vite:config     alias: [ [Object], [Object], [Object], [Object], [Object] ]
  vite:config   },
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
  vite:config     polyfillModulePreload: true,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: false,
  vite:config     rollupOptions: { input: [Array] },
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] },
  vite:config     minify: 'terser',
  vite:config     terserOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: true,
  vite:config     manifest: true,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     brotliSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: true,
  vite:config     w: true
  vite:config   },
  vite:config   mode: 'development',
  vite:config   define: { __VUE_OPTIONS_API__: true, __VUE_PROD_DEVTOOLS__: false },
  vite:config   ssr: { external: [ 'vue', '@vue/server-renderer' ] },
  vite:config   configFile: '/home/dlh/development/clients/fccc/ff-fcccrv/vite.config.js',
  vite:config   configFileDependencies: [ 'vite.config.js' ],
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: 'development',
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     build: { w: true, watch: true }
  vite:config   },
  vite:config   root: '/home/dlh/development/clients/fccc/ff-fcccrv',
  vite:config   publicDir: '/home/dlh/development/clients/fccc/ff-fcccrv/public',
  vite:config   cacheDir: '/home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.vite',
  vite:config   command: 'build',
  vite:config   isProduction: false,
  vite:config   server: { fs: { strict: undefined, allow: [Array] } },
  vite:config   env: {
  vite:config     BASE_URL: '/wp-content/themes/ff-bulma/dist/',
  vite:config     MODE: 'development',
  vite:config     DEV: true,
  vite:config     PROD: false
  vite:config   },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     warnOnce: [Function: warnOnce],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen],
  vite:config     hasErrorLogged: [Function: hasErrorLogged]
  vite:config   },
  vite:config   createResolver: [Function: createResolver],
  vite:config   optimizeDeps: { esbuildOptions: { keepNames: undefined } }
  vite:config } +22ms
vite v2.5.3 building for development...

watching for file changes...

build started...
✓ 58 modules transformed.
[vite]: Rollup failed to resolve import "/wp-content/themes/ff-bulma/assets/images/close-button.png" from "src/vue/coaches/filters-section/FiltersSection.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
(node:80356) UnhandledPromiseRejectionWarning: Error: [vite]: Rollup failed to resolve import "/wp-content/themes/ff-bulma/assets/images/close-button.png" from "src/vue/coaches/filters-section/FiltersSection.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
    at onRollupWarning (/home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/vite@2.5.3/node_modules/vite/dist/node/chunks/dep-1be34a63.js:51246:19)
    at Object.onwarn (/home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/vite@2.5.3/node_modules/vite/dist/node/chunks/dep-1be34a63.js:51031:13)
    at /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/rollup@2.56.3/node_modules/rollup/dist/shared/mergeOptions.js:100:25
    at Object.onwarn (/home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/rollup@2.56.3/node_modules/rollup/dist/shared/rollup.js:20515:13)
    at ModuleLoader.handleResolveId (/home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/rollup@2.56.3/node_modules/rollup/dist/shared/rollup.js:19864:26)
    at /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/rollup@2.56.3/node_modules/rollup/dist/shared/rollup.js:19856:26
(Use `node --trace-warnings ...` to show where the warning was created)
(node:80356) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:80356) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
transforming (123) node_modules/.pnpm/@vue+reactivity@3.2.4/node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js  vite:resolve 1ms   bulma/bulma.sass -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/bulma@0.9.3/node_modules/bulma/bulma.sass +0ms
  vite:resolve 2ms   swiper/swiper -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/swiper@6.8.2/node_modules/swiper/swiper.scss +2s
  vite:resolve 1ms   swiper/components/navigation/navigation -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/swiper@6.8.2/node_modules/swiper/components/navigation/navigation.scss +16ms
  vite:resolve 1ms   swiper/components/pagination/pagination -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/swiper@6.8.2/node_modules/swiper/components/pagination/pagination.scss +8ms
  vite:resolve 1ms   swiper/components/lazy/lazy -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/swiper@6.8.2/node_modules/swiper/components/lazy/lazy.scss +12ms
  vite:resolve 0ms   /wp-content/themes/ff-bulma/assets/images/placeholder.jpg -> null +0ms
  vite:resolve 1ms   /wp-content/themes/ff-bulma/assets/images/arrow.svg -> null +0ms
transforming (125) src/app.scss  vite:resolve 1ms   /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/initial-variables -> /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/_initial-variables.scss +461ms
  vite:resolve 1ms   ~/src/theme/initial-variables -> /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/_initial-variables.scss +0ms
  vite:resolve 1ms   bulma/sass/utilities/_all.sass -> /home/dlh/development/clients/fccc/ff-fcccrv/node_modules/.pnpm/bulma@0.9.3/node_modules/bulma/sass/utilities/_all.sass +3ms
  vite:resolve 0ms   /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/derived-variables -> /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/_derived-variables.scss +1s
  vite:resolve 0ms   ~/src/theme/derived-variables -> /home/dlh/development/clients/fccc/ff-fcccrv/src/theme/_derived-variables.scss +0ms
  vite:resolve 0ms   ./../assets/images/check-solid.svg -> null +1s
  vite:resolve 0ms   /wp-content/themes/ff-bulma/assets/images/fcccrv-coach-background.svg -> null +233ms

Validations

@its-dlh
Copy link
Author

its-dlh commented Sep 3, 2021

I should note that I filed the issue in this repo instead of vue-next because it seemed to be an issue with @vitejs/plugin-vue as opposed to something like @vue/compiler-sfc. However, I can re-file the issue in vue-next if needed.

@its-dlh
Copy link
Author

its-dlh commented Sep 7, 2021

The issue is that @vitejs/plugin-vue is forcing transformAssetUrls: { includeAbsolute: true } whenever doing a build.

// build: force all asset urls into import requests so that they go through
// the assets plugin for asset registration
assetUrlOptions = {
includeAbsolute: true
}

This is clearly intentional, but there's no escape hatch to treat those URLs as-is without running them through the asset plugin. The asset plugin itself has handling for absolute URLs, but only if it can resolve them in the filesystem based on the configured public path.

// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const publicFile = checkPublicFile(id, config)
if (publicFile) {
return id
}

On the one hand, I see how this fits the typical use case of Vite, but it does make it less usable when the Vite build is just one part of the tree that is served by the web server.

@innocenzi
Copy link
Contributor

On the one hand, I see how this fits the typical use case of Vite, but it does make it less usable when the Vite build is just one part of the tree that is served by the web server.

This is indeed a potential issue with back-end frameworks integration. I use the same workaround (<img :src="'/path/to/image.png'">) with Laravel Vite whenever I need to reference something in the public directory, which is not processed by Vite.

I would like to provide an escape hatch for this, and ideally something that would not necessarily be in the @vite/plugin-vue config when registering the plugin because plugins that modify the config would not be able to affect this (I think?).

Maybe even having better heuristics for determining when includeAbsolute should be set to true? Like when build.rollupOptions.input is manually filled, which should not affect the use cases where includeAbsolute: true is useful?

@patak-dev
Copy link
Member

@innocenzi I think we could keep this issue close and create a more focused issue on the idea of other plugins modifying plugin vue options. That I think it is good in general, maybe plugin-vue could use the API scheme that we use in plugin-react for babel plugins to enable any other plugin to add Vue options, and then it will merge all.

@innocenzi
Copy link
Contributor

I'm fine with that idea yes, thanks 👍

I'll come back to this issue later once the official integration from the Laravel team is done

@github-actions github-actions bot locked and limited conversation to collaborators May 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants