vite
vite copied to clipboard
url with relative path in sass/scss is broken when main.js and assets are in subdirectory
Describe the bug
Following on from my comment in #7651 - the linked PR (#10741) doesn't solve the issue when main.js and assets are stored in a top level assets directory, e.g.:
├── assets
│ ├── images
│ │ └── vite.svg
│ ├── main.js
│ └── styles
│ ├── pages
│ │ └── home.scss
│ └── style.scss
├── index.html
├── package.json
├── package-lock.json
├── public
│ └── vite.svg
└── README.md
You can see in the linked reproducer that url('../../images/vite.svg')
does not resolve the image correctly, even though that is the correct relative path to the file from assets/styles/pages/home.scss
. When the path is changed to url('../images/vite.svg')
it resolves correctly.
Reproduction
https://github.com/andyexeter/vitejs-vite-2btrkm
Steps to reproduce
Run npm install
followed by npm run dev
System Info
System:
OS: Linux 5.15 Ubuntu 20.04.5 LTS (Focal Fossa)
CPU: (12) x64 11th Gen Intel(R) Core(TM) i5-11600 @ 2.80GHz
Memory: 6.97 GB / 15.47 GB
Container: Yes
Shell: 5.8 - /usr/bin/zsh
Binaries:
Node: 16.18.1 - /usr/bin/node
Yarn: 1.22.17 - /usr/bin/yarn
npm: 8.19.2 - /usr/bin/npm
Browsers:
Chrome: 107.0.5304.110
Firefox: 107.0
Used Package Manager
npm
Logs
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
Thanks for creating this. The reason why this is happening is explained in #7651:
Current Vite's implementation
It is implemented by this
rebaseUrls
function. This function is called insideimporter
option which is passed to sass. But importer will only be called if it is not a relative import because of the resolve order.Loads are resolved by trying, in order:
- Loading a file from disk relative to the file in which the @use or @import appeared.
- Each custom importer.
- Loading a file relative to the current working directory.
- Each load path in includePaths.
- Each load path specified in the SASS_PATH environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
Which means if a file is resolved by relative path,
rebaseUrls
functions won't be called. The example is below./* src/foo.scss */ @import "./nested/bar.scss"; /* @import "/@/nested/bar.scss"; */ /* if a alias is used `rebaseUrls` will be called */ /* ---- */ /* src/nested/bar.scss */ .bar { background: url('./bar.png'); }
But I forgot about this while creating #10741.
I believe I'm seeing the same symptom. I'm just not sure if the cause is the same. Here is my reproduction: https://github.com/roydukkey/moist/tree/vite/issue-11012
I think I ran into the same issue:
created a new vite-vanilla-typescript project, added npm i bootstrap-icons
and have these files:
├── src
│ ├── main.ts
│ └── style.scss
└── index.html
In main.ts I import the bootstrap-icons via import "./style.scss";
In style.scss I import the bootstrap-icons.scss via @use "../node_modules/bootstrap/scss/bootstrap";
And it fails with:
downloadable font: download failed (font-family: "bootstrap-icons" style:normal weight:400 stretch:100 src index:0): status=2147746065 source: http://localhost:5173/MyProject/fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47
But when using @use "../node_modules/bootstrap/scss/bootstrap.css";
it works as expected.
(It also fails with the same error when importing *.scss)
Came here after a long time of finding out what might be wrong while learning web dev with vite/scss from here: https://github.com/twbs/icons/issues/1381
(vite 4.1.0)
I think I ran into the same issue:
created a new vite-vanilla-typescript project, added
npm i bootstrap-icons
and have these files:├── src │ ├── main.ts │ └── style.scss └── index.html
In main.ts I import the bootstrap-icons via
import "./style.scss";
In style.scss I import the bootstrap-icons.scss via@use "../node_modules/bootstrap/scss/bootstrap";
And it fails with:downloadable font: download failed (font-family: "bootstrap-icons" style:normal weight:400 stretch:100 src index:0): status=2147746065 source: http://localhost:5173/MyProject/fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47
But when using
@use "../node_modules/bootstrap/scss/bootstrap.css";
it works as expected.(It also fails with the same error when importing *.scss)
Came here after a long time of finding out what might be wrong while learning web dev with vite/scss from here: twbs/icons#1381
(vite 4.1.0)
I have faced similar problems when using the library from @arcgis.
The relative path in the library css fails to be resolved if .scss
is imported,
but it works when changing back to .css
.
Thanks for the information.
Is there any news about this bug or workaround? I'm facing this issue with the bootstrap-icons
library.
I'm using vite 4.4.11
@oussama-he the workaround for bootstrap-icons
is to unroll the variable interpolation before you load the bootstrap-icons.scss
file. You should determine where the locations will be based on where the packages are installed, the values below work for me.
This works because $bootstrap-icons-font-file
is marked !default
:
// NOTE: this is a workaround for the vite-sass issue of loading the woff files in bootstrap-icons.scss using variables
$bootstrap-icons-font-file: "~bootstrap-icons/font/fonts/bootstrap-icons";
@import "bootstrap-icons/font/bootstrap-icons.scss";
@dherbst Small side question, how did you manage to get the tilde (~
) to work in Vite? I am getting Unable to resolve
@import "~normalize.css/normalize.css"``?
@frederikbosch you add it to the alias section of the vite.config.js see https://vitejs.dev/config/shared-options.html#resolve-alias
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'~bootstrap': fileURLToPath(new URL('./node_modules/bootstrap', import.meta.url)),
'~bootstrap-icons': fileURLToPath(new URL('./node_modules/bootstrap-icons', import.meta.url)),
}
}
})
Getting this as well, and it is the one thing that is absolutely blocking me from easily moving off of Create React App.
[Edit]
Indeed declaring @fonts
and @images
alias paths works.
... I actually don't really have a problem with that approach. But on principle, this still has me a bit uneasy that it might break in the future on third-party CSS being imported.
This is currently blocking us from moving from a custom webpack build on a larger legacy app to vite. We have a lot of scss files for fonts etc. that are imported in a variety of other scss files. Sadly this breaks right now as we get errors like these:
../../../assets/fonts/opensans/opensans-extrabold-webfont.svg referenced in /some/path/to/screen.scss didn't resolve at build time, it will remain unchanged to be resolved at runtime
Is there any feasible workaround for this that doesn't include changing all the relative paths?
facing the same issue here. relative paths in scss files aren't handled correctly.
Also encountering this problem. It does not seem to require that "main.js and assets are in subdirectory", as per title.
Given this directory structure
styles/
main.scss
base/
_fonts.scss
path/
to/
font.ttf
index.js
Simply using url('./path/to/font.ttf')
from styles/base/_fonts.scss
which is then @import
ed from styles/main.scss
gives
./path/to/font.ttf
referenced in<projectPath>/styles/main.scss
didn't resolve at build time, it will remain unchanged to be resolved at runtime
So I find it quite strange that the documentation still states:
In addition, relative url() references inside imported Sass/Less files that are in different directories from the root file are also automatically rebased to ensure correctness.
Trying to use resolve alias but it didn't work for me, found simple hack to force it
Basic importing sass files from @foo/my-ui
package that relative import fonts, to rewrite paths using postcss-url
import url from 'postcss-url'
// and in config
css: {
postcss: {
plugins: [
// https://github.com/vitejs/vite/issues/11012
url({
url: asset => {
if (asset.url.startsWith('./fonts/')) {
return path.resolve(__dirname, '../node_modules/@foo/my-ui/lib', asset.url)
}
return undefined
},
}),
],
},
},
this is pretty naive implementation as in ma case only needed for fonts.
@dherbst I have tried your workaround but I can't get it work.
I still get this error:
These are my config and scss files
const { resolve } = require('path');
const { fileURLToPath } = require('url')
module.exports = {
root: resolve('src/static'),
base: '/static/',
server: {
host: '0.0.0.0',
port: 3000,
open: false,
watch: {
usePolling: true,
disableGlobbing: false,
},
},
resolve: {
extensions: ['.js', '.json'],
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'~bootstrap': fileURLToPath(new URL('./node_modules/bootstrap', import.meta.url)),
'~bootstrap-icons': fileURLToPath(new URL('./node_modules/bootstrap-icons', import.meta.url)),
}
},
build: {
outDir: resolve('src/static/dist'),
assetsDir: '',
manifest: true,
emptyOutDir: true,
target: 'es2015',
rollupOptions: {
input: {
main: resolve('src/static/js/main.js'),
},
output: {
chunkFileNames: undefined,
},
},
},
};
style.scss
@import url('https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;700&family=Baloo+Bhaijaan+2:wght@400;700&display=swap');
@import 'simplebar/dist/simplebar.min.css';
// NOTE: this is a workaround for the vite-sass issue of loading the woff files in bootstrap-icons.scss using variables
$bootstrap-icons-font-file: "~bootstrap-icons/font/fonts/bootstrap-icons";
@import "~bootstrap-icons/font/bootstrap-icons.scss";
main.js
import 'vite/modulepreload-polyfill';
import '../css/style.scss'
import Alpine from 'alpinejs'
import { Toast } from "bootstrap";
import htmx from 'htmx.org';
import _hyperscript from "hyperscript.org";
import 'simplebar';
Please can you tell me what is wrong?
Thank you in advance.
@oussama-he
In the style.scss
file remove the ~
from the @import
line, but keep it in the variable line.
@import "bootstrap-icons/font/bootstrap-icons.scss";
See if that helps. Other than that, it looks like vite is not rewriting part of the url to the woff files, perhaps something is going wrong somewhere in the build steps? I'm not sure.
@oussama-he
Have you tried preserveSymlinks: true
in the resolve
configuration?
Additionally: not sure if fileURLToPath(new URL("./some/url", import.meta.url))
would do the right thing, esp. if you are using CommonJS require
and module.exports
rather than ES modules for your config file. You might consider trying path.resolve(__dirname, "./some/path")
instead.
Looks like vite will not rewrite paths inside files imported in sass, try moving import bootstrap-icons.scss to main.js before style.sass.
Looks like vite will not rewrite paths inside files imported in sass, try moving import bootstrap-icons.scss to main.js before style.sass.
I have a case where I have a deeply imported sheet that pulls in web fonts from a @fonts
alias and it does resolve that correctly.
Nothing worked for me. I always get the same error.
http://localhost:8000/static/@fs/home/oussama/budget-app/node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47 net::ERR_ABORTED 404 (Not Found)
Looks like vite will not rewrite paths inside files imported in sass, try moving import bootstrap-icons.scss to main.js before style.sass.
I have a case where I have a deeply imported sheet that pulls in web fonts from a
@fonts
alias and it does resolve that correctly.
@rjgotten maybe it's connected from where you importing it, like if sass file is from node_modules
In my case it is rewritting paths in both files, the main styles.scss
where all the imports happen, and imported stylessheets like config/_fonts.scss
.
The problem is that it is transforming the relative path incorrectly; It just uses the assetsDir
build option in the path, not including the outDir
parameter.
So, src: url('../fonts/Inter-Regular.woff2') format('woff2');
in my _fonts.scss
:
- Becomes
http://wp-starter.test/assets/Inter-Regular-CKDp9E3C.woff2
- Insted of
http://wp-starter.test/themes/wp-starter/assets/Inter-Regular-CKDp9E3C.woff2
I've created another issue since I'm not sure it is the same one: https://github.com/vitejs/vite/issues/17605