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

Relative media path resolution for url(), when using a SCSS mixin, requires a path relative to mixin file #29412

Open
1 task done
AmadejBukorovic opened this issue Jan 20, 2025 · 0 comments

Comments

@AmadejBukorovic
Copy link

AmadejBukorovic commented Jan 20, 2025

Command

build

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

17

Description

I apologize if this might not be considered a bug, but rather a future improvement. I opted for a bug because the behavior has changed since v17, and I couldn't find any mentions of intentional change, which is not to say there might have been.

The issue is specific to the new @angular-devkit/build-angular:application builder, and a change that seems to have occurred with Angular v18, and is still present in v19.

The issue being that the resolution behavior for relatively referenced media files (resource), used with url(), when calling SCSS mixins, seems to have changed. I'm talking about the rebasing, fingerprinting and copying of image files to the output folder, when building an application.

Previously, using v17 and the new @angular-devkit/build-angular:application builder, as well as the webpack based builder in prior versions, the relative path to the media file had to be relative to the SCSS in which it was "declared", even when passing the path to SCSS mixin in another location, should that mixin end up being the one that contains the CSS url() declaration.

E.g., having a simple mixin such as the one below

@mixin icon-size-24($icon-path) {
  width: 24px;
  height: 24px;
  display: inline-block;
  background-image: url($icon-path);
}

Would require that it's called with a relative path based on the calling file, regardless of the mixin location.

Using such a mixin inside application and/or component stylesheet would require the relative path to written based on that file's location, and not the location of the mixin. E.g.:

@use '../../../../shared-styles';

.icon-container-1 {
  // The background url needs to be relative to the entry caller:
  background-image: url('../../../../shared-styles/resources/add_a_photo.svg');
}
.icon-container-2 {
  @include shared-styles.icon-size-24('../../../../shared-styles/resources/add_a_photo.svg');
}

Such resolution behavior is still present when using the old, webpack based @angular-devkit/build-angular:browser, as well as when creating libraries using the @angular-devkit/build-angular:ng-packagr builder. It worked using the new @angular-devkit/build-angular:application builder` in version 17.

But since v18, in case of the @angular-devkit/build-angular:application builder, the required path that is passed to the mixins needs to be relative to the mixin location, presumably because that's where the style using the url() is located. Meaning the above code needs to change to e.g.:

@use '../../../../shared-styles';

.icon-container-1 {
  // The background url needs to be relative to the entry caller:
  background-image: url('../../../shared-styles/resources/add_a_photo.svg');
}

.icon-container-2 {
  // Same file being referenced as above, but because we are calling a mixin that contains the style declaration using url(), 
  // the path needs to be written based on the called mixin location.
  @include shared-styles.icon-size-24('../resources/add_a_photo.svg');
}

I think the new behavior, compared to the previous one, has negative side effects:

  • requires awareness of the location of the file containing the mixin, which can be intentionally obscured by using SCSS barrel files and forwarding as a mean to decrease coupling (the mixin could in fact be part of a library, being consumed by the workspace, and any change to the inner structure of the SCSS files can force the consumers to have to rewrite their file paths)
  • is different from the behavior of ng-packgr, preventing the use of path mapping to the source of libraries when building applications
  • potentially more confusing as it's inconsistent across the builders (e.g. ng-packgr), and because you might end up with multiple references of the same source of icon files, with a very different base, inside the same SCSS file

I realize the above points might not really matter in terms of officially supported / intended behavior. Personally, I think the new inconsistency between @angular-devkit/build-angular:ng-packagr and @angular-devkit/build-angular:application is the most problematic, since you effectively can't consume libraries in your workspace via their source, instead of build output, in applications that you also have in the same workspace. Well, you can, provided you either avoid such mixins completely, or turn off css inlining for libraries.

Minimal Reproduction

You can checkout https://github.com/AmadejBukorovic/ng-18-mixin-resource-resolve. The repository contains an application and library, which the application depends upon, showcasing the differences mentioned above.

Run npm run build to build the library and consuming application successfully. The changes in Angular v18+ have no direct effect on this behavior, when consuming libraries from the workspace via build outputs, other than the obvious differences in how to correctly write relative paths for url().

Run ng serve, which will fail as it attempts to utilize the tsconfig.app-dev.json to bypass the requirement to build the dependant library for development purposes (if you will). This fails due to differences in the behavior of SCSS resource resolution between different builders. Something that is not an issue in v17 and prior.

Exception or Error

X [ERROR] Could not resolve "../../../shared-styles/resources/add_a_photo.svg" [plugin angular-css-resource]

    projects/sample-app/src/styles.scss:9:24:
      9 │ ...image: url("../../../shared-styles/icon||file:../../../shared-st...
        ╵               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Preprocessor stylesheets may not show the exact file location of the error.

Your Environment

Anything else relevant?

No response

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

No branches or pull requests

2 participants