Skip to content

url() with variable or relative path in sass/scss is broken #7651

@sapphi-red

Description

@sapphi-red

This issue organizes many issues around relative url() in sass/scss and adds many information.

(Please convert to discussion if it should be a discussion.)

Related: #6618, #3164, #2993, #5337, #4269

Premise

sass does not support rebasing relative url (sass/libsass#532).
For example,

/* src/foo.scss */
@import "./nested/bar.scss";

/* ---- */
/* src/nested/bar.scss */
.bar {
  background: url('./bar.png'); /* intends nested/bar.png */
}

becomes

/* src/foo.css */
.bar {
  background: url('./bar.png'); /* becomes src/bar.png */
}

Also there is no API for custom rebasing (sass/sass#2535).

In sass-loader, this feature is not implemented. It presents resolve-url-loader for solution. This one rebases url after converted to css and uses sourcemaps to obtain the original file path.

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');
}

This is why #6618 only happened when alias is used. (So url won't be rebased if it is imported by relative path.)

Also rebaseUrls is doing the transform over sass files and this makes it unable to rebase url including variables like url($url) (#3164, #2993, #5337).
This can be mitigated by not transforming url which starts with a variable (#4269).
But it won't work if it is used like below.

$url: './relative.png';

.foo {
  background: url($url);
}

Solutions

I suggest several solutions.

  1. Since this is not a sass's official feature, drop this feature(rebasing relative path) from Vite.
    • Supporting absolute paths (including paths from config.root) is easily achieveable by doing rebaseUrls after the content is transformed to css.
  2. Mitigate as much as possible
    • I have no idea how to call rebaseUrls even if it is imported by relative path.
  3. Implement a logic simillar to resolve-url-loader.
    • Maybe if the sourcemaps are enabled, UrlRewritePostcssPlugin can handle this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feat: cssp3-minor-bugAn edge case that only affects very specific usage (priority)

    Type

    No type

    Projects

    Status

    Discussing

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions