glromeo / esbuild-sass-plugin

esbuild plugin for sass
MIT License
153 stars 41 forks source link

[Bug] Incorrect @import paths passed to importMapper #136

Closed alexandernst closed 1 year ago

alexandernst commented 1 year ago

Lets say I have my esbuild script in the root folder of my project an a src folder that contains my code.

-  esbuild.mjs
-  src
|--- index.jsx
|--- index.scss
|--- vars.scss
|--- secondary.scss

Lets say that I have an entrypoint index.jsx that looks like this:

src/index.jsx

import "./index.scss";

Lets also say that I have the following SCSS files:

index.scss

@import "@src/vars.scss";
@import "@src/secondary.scss";

vars.scss

$foo: #111;

secondary.scss

@import "@src/vars.scss";

Finally, lets say that I have the following importMapper function:

importMapper: (filepath) => {  
    const transformed = filepath.replace(/^@src\//, path.resolve(__dirname, "src") + "/")
    console.log(`Received ${filepath} --> converting to ${transformed}`);
    return transformed;
},

I'd expect to be able to build the project, but this is not the case.

Looking at the output of the build script I see the following:

Received @src/vars.scss --> converting to /Users/alexandernst/Projects/esbuild-proj1/src/vars.scss
Received @src/secondary.scss --> converting to /Users/alexandernst/Projects/esbuild-proj1/src/secondary.scss
Received /Users/alexandernst/Projects/esbuild-proj1/src/@src/vars.scss --> converting to /Users/alexandernst/Projects/esbuild-proj1/src/@src/vars.scss

If I understand correctly, this is what happens:

  1. The index.scss is being processed and its @import statements are getting resolved one by one.
  2. The first @import statement is the path @src/vars.scss, which is passed to the importMapper function and gets converted to /Users/alexandernst/Projects/esbuild-proj1/src/vars.scss (good)
  3. The second @import statement is the path @src/secondary.scss, which is passed to the importMapper function and gets converted to /Users/alexandernst/Projects/esbuild-proj1/src/secondary.scss (good)
  4. The secondary.scss is being processed and its @import statements are getting resolved one by one.
  5. The first @import statement is the path @src/vars.scss. The importMapper function should receive @src/vars.scss, but instead is receiving /Users/alexandernst/Projects/esbuild-proj1/@src/vars.scss (bad), which then leads the path to be resolved to /Users/alexandernst/Projects/esbuild-proj1/src/@src/vars.scss (bad).

I could easily fix this with filepath.replace(/^(.*?)@src\//, path.resolve(__dirname, "src") + "/"), but I think that the current behavior is incorrect and the importMapper function should always receive the unresolved @import path.

glromeo commented 1 year ago

sorry for taking so long...the issue you have is due to the fact that sass treats files that start with @src/ as if they are relative (ie. as if they are ./@src/). This is utterly annoying but it is what it is...the problem is in sass not the plugin. As a consequence of that sass tends sometimes to resolve the path before passing it on to the canonicalize method of importer that the plugin implements, sometimes not...and for me to be able to fix that randomness is unlikely.

There's fortunately a workaround which also improves IMHO the quality and readability of your solution. Instead of using @src use something that makes the import look like a url (e.g. local:src) that way sass won't try and do clever things and all will work as you want.

This should help: https://github.com/glromeo/esbuild-sass-plugin/tree/main/test/issues/136