webpack / enhanced-resolve

Offers an async require.resolve function. It's highly configurable.
MIT License
916 stars 186 forks source link

Hooks do not fire events from CSS/SCSS imports #387

Closed bgonzalez-thestable closed 4 months ago

bgonzalez-thestable commented 1 year ago

This seems to happen only when importing stylesheets that have children imports. The imported children stylesheets are properly bundled in, however the individual imports themselves fail to trigger enhanced-resolve hooks like regular JS files do.

Is there something I'm missing? Any help is greatly appreciated.

This is an example of the setup I have in place:

// main.js

import "./styles/main.scss";
// styles/main.scss

@import "./base/index.scss";
@import "./components/index.scss";
// webpack.js

import { ResolverPlugin } from '@path/to/ResolverPlugin';

module.exports = {
  entry: {
    main: 'path/to/main.js'
  },
  //...
  resolve: {
    plugins: [
      //Resolver plugin setup.
      new ResolverPlugin(),
    ],
  },
  //...
  //Assumes the necessary loaders for SCSS/CSS are in place.
}
// @path/to/ResolverPlugin

class ResolverPlugin {
  constructor() {
    //...
  }

  apply(resolver) {
    resolver.ensureHook('parsedResolve');

    resolver.getHook('beforeResolve').tapAsync('ResolverPlugin', (requestContext, resolveContext, callback) => {

      //HERE: imports defined in a styles file, such as the `styles/main.scss` above, do not fire this hook:
      console.log('file to resolve:', requestContext.request);

      return callback();
    });
  }
}
alexander-akait commented 1 year ago

Sorry, not related to enhanced-resolve, you need a loader, parse CSS and run enhanced-resolve for every @import, webpack nothing know about CSS

bgonzalez-thestable commented 1 year ago

@alexander-akait Thanks for the quick reply!

We're using sass-loader and Webpack docs state here that import at-rules are passed to Webpack's resolving engine.

Doesn't that mean that those imports are effectively being sent to enhanced-resolve?

Edit: It looks like sass-loader has a way of updating the importer, as suggested by you here, maybe this is what I need to hook into to get what I need done.

alexander-akait commented 1 year ago

@bgonzalez-thestable hm, yes, we create resolver here https://github.com/webpack-contrib/sass-loader/blob/master/src/utils.js#L635 with options https://github.com/webpack-contrib/sass-loader/blob/master/src/utils.js#L510 and https://github.com/webpack-contrib/sass-loader/blob/master/src/utils.js#L522

alexander-akait commented 1 year ago

theoretically plugins should be inherited too

alexander-akait commented 1 year ago

@bgonzalez-thestable Technically you can do it like this

const myPlugin = new (class ResolverPlugin {
    constructor() {}

    apply(resolver) {
        resolver.ensureHook("parsedResolve");

        resolver
            .getHook("resolve")
            .tapAsync(
                "ResolverPlugin",
                (requestContext, resolveContext, callback) => {
                    //HERE: imports defined in a styles file, such as the `styles/main.scss` above, do not fire this hook:
                    console.log("file to resolve:", requestContext.request);

                    return callback();
                }
            );
    }
})();

module.exports = {
    mode: "production",
    devtool: false,
    target: ["web", "es2020"],
    entry: {
        main: "./src/entry.mjs"
    },
    resolve: {
                // You can globally say for every sass import
        byDependency: {
            sass: {
                plugins: [myPlugin]
            }
        },
    },
    module: {
        rules: [
            {
                test: /\.scss$/i,
                // Apply this plugin only on required loader(s)
                /*resolve: {
                    plugins: [myPlugin]
                },*/
                use: ["style-loader", "css-loader", "sass-loader"]
            }
        ]
    },
};

But there is another problem here, sass try to resolve firstly, and when it can't, it runs importer (i.e. our resolver), so if you have:

@use "./variables.scss" as vars;

and you have _variables.scss or variables.scss, sass will resolved it and do not run our resolver, but if you change it on:

@use "./unknown.scss" as vars;

And you don't have _unknown.scss or unknown.scss, it will run our resolver and your plugin catch this.

I once raised this issue a long time ago, but the sass team did not want to change their logic, so we are powerless here.

alexander-akait commented 4 months ago

Closing due to inactivity. Please test with latest version and feel free to reopen if still regressions. Thanks!