vite icon indicating copy to clipboard operation
vite copied to clipboard

url with relative path in sass/scss is broken when main.js and assets are in subdirectory

Open andyexeter opened this issue 1 year ago • 12 comments

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

andyexeter avatar Nov 21 '22 10:11 andyexeter

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 inside importer 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.

Interface LegacyStringOptions importer

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.

sapphi-red avatar Nov 22 '22 13:11 sapphi-red

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

roydukkey avatar Dec 20 '22 20:12 roydukkey

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)

nonsensation avatar Feb 13 '23 23:02 nonsensation

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.

tlyau62 avatar Dec 06 '23 04:12 tlyau62

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 avatar Jan 03 '24 09:01 oussama-he

@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-fileis 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 avatar Jan 04 '24 10:01 dherbst

@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 avatar Jan 10 '24 12:01 frederikbosch

@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)),
    }
  }
})

dherbst avatar Jan 10 '24 13:01 dherbst

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.

rjgotten avatar Feb 02 '24 16:02 rjgotten

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?

eXaminator avatar Feb 28 '24 11:02 eXaminator

facing the same issue here. relative paths in scss files aren't handled correctly.

nckirik avatar Mar 10 '24 19:03 nckirik

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 @imported 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.

unekinn avatar Apr 29 '24 14:04 unekinn