-
-
Notifications
You must be signed in to change notification settings - Fork 601
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
CSS Module locals/hashmap are being overwritten when more than one 'module' exists in same file #1518
Comments
Yeah, a known problem with CSS-in-JS solutions without runtime, we can't fix it here, We can't control how CSS was imported (in If you look at generated code, you will see (disable const cssModules = {}
;
cssModules["$style"] = _app_vue_vue_type_style_index_0_id_5ef48958_lang_scss_module_true__WEBPACK_IMPORTED_MODULE_2__["default"];
if (false) {}
cssModules["$style"] = _app_vue_vue_type_style_index_1_id_5ef48958_lang_scss_module_true__WEBPACK_IMPORTED_MODULE_3__["default"]
if (false) {} And as you can see the styles are just overwritten, it can be solved using const cssModules = {}
;
cssModules["$style"] = {}
;
Object.assign(cssModules["$style"], _app_vue_vue_type_style_index_0_id_5ef48958_lang_scss_module_true__WEBPACK_IMPORTED_MODULE_2__["default"]);
if (false) {}
Object.assign(cssModules["$style"], _app_vue_vue_type_style_index_1_id_5ef48958_lang_scss_module_true__WEBPACK_IMPORTED_MODULE_3__["default"])
if (false) {} Also want to note again - it can be a limitation on vue side with multiple style tags with the Feel free to feedback |
First, thank you very much @alexander-akait for taking your time reading and understanding my issue. I appreciate it Like I said, I wasn't even sure if multiple modules would be a bug or just not thought in this scenario. That's why I thought in asking in css-loader instead of vue-loader at first, as you would understand better the concept of the CSS Module itself. I guess I got sidetracked because of the After seeing more of how is used outside Vue (React) during weekend, I came to see it could be really vue-loader, since in react is manually With your confirmation showing the un-devtool'd version (nice tip, I'll use it more) and the suggestion of possible fix, which was kinda what I had imagined in the pseudo-code, I understand now that in fact, that it is vue-loader's job to piece each of these style tags which became "separate" css files into a single applicable style, including the module exports hashmaps. So I went into the real source on where styles are built, in special for modules and what is done in them and yeah, if I change
to
it'll parse each css-loader I'll try now push this into vue-loader and see if Evan and team will accept. Thank you very much once again, Alex! |
@alexander-akait I do have a following question if you don't mind, regarding one of the issues I pointed, and this one I'm not sure is vue-loader's fault but how the hash is made for the class name: Circling back to this PoC:
3 modules names, In this scenario, as I showed in the original post, all them are getting the same name. So I checked the code with devtools off, and seems the module is given for the call, e.g. for
The resulting hashed class always generates the same for all 3 different modules (the push shows the correct css, the locals all have same name):
So, the file name and the class name are the same. Doesn't seem it's taking module name I was thinking that I should be the one to tell css-loader that since I am the one that configured I did test having the 3 modules with 3 different classes (each having it's own module name as a class) and worked ok:
Is this "different module name same class generating same hashmap" a css-loader issue? PS.: I was looking into hashFunction for |
We have this https://github.com/webpack-contrib/css-loader/blob/master/src/utils.js#L340, but because vue-loader doesn't use specific syntax for this (i.e. |
as you can see |
Thank you very much again Alex, I couldn't find exactly how vue-loader would pass this to css-loader but I'll make a separated issue then for them :-) edit: I've created the PR with my changes, though they don't fix all issues, specifically 2 in the |
Bug report
I'm using CSS modules with Vue3/Webpack5. All dependencies in their latest version as of this post (sass-loader, postcss-loader, css-loader, vue-loader, vue-style-loader, vue, webpack, etc). I've created a PoC with the issue https://github.com/RaphaelDDL/vue3-css-module
The css-loader options for module is as following:
CSS Modules create a locals object
___CSS_LOADER_EXPORT___.locals
which has the object/map of class->hashed class, this happens for each css/style, as vue-loader parses and provides for later loaders each style as a separated css source (which can be captured withtest: /\.(css|scss)$/
for e.g.)The issue: When using more than one
style
tag with amodule
attr, only the laststyle
tag retains it's module locals. Each style will have it's locals parsed, but only the last one in each .vue file retains the hashmap. (Reasoning for why have two modules later). Since https://github.com/css-modules/css-modules seems completely abandoned, I have no idea if this is really a bug or never thought.Actual Behavior
I've described in the PoC readme, but adding here as well. Taking the simplest test-case PoC Code module-nameless.vue::
When inspecting the loaders output, I can see the hashmaps being created, example:
and
You can see both were created and exist in the files served to browser
The issue is that the end-result is that only the file's last style's
locals
hashmap exists for Vue, therefore the resulting HTML is a empty class asmulti1
is not inlocals
This issue also happens with two styles with named modules, e.g. PoC Code module-multiple.vue:
The behavior is even weirder when you try multiple modules with different named modules, e.g PoC Code module-named-multiple.vue::
In this case, module names will be ignored as being separated name/scopes, all styles will create their CSS and all them will have the same name and hash instead, making the last style's class overwrite all others
Expected Behavior
The expectation was that multiple modules would work as if shallow copy/merging, where:
$style
, idk others)This way, the locals hashmap would work like CSS where same rule/selector that comes later is applied on top of a earlier. Example:
First style generates:
Second style, also being a module, takes into account whatever locals already exists and merge, example:
Basically, a shallow merge (I assume the place would be in utils.js) for locals, e.g. pseudo-code
And same for named module(s), under each name/scope.
How Do We Reproduce?
As mentioned, I created a PoC in https://github.com/RaphaelDDL/vue3-css-module
It contains various views under
src/components/
where I explore all different combinations withmodule
,scoped
, styles instyle
and also in external files (style
withsrc
). The webpack config is the simplest I could: .vue is parsed byvue-loader
, thenscss/css
is parsed by (in this order): sass-loader, postcss-loader, css-loader, vue-style-loader.Now, after all this, the question that would come to mind is "Why have more than one module?":
To support two or more websites within each self-contained component, via an extra attribute in each style tag, called
brand
. In the PoC, I created two brands:cocacola
andpepsi
. During run and build, I pass which brand I want, Example:npm run start:pepsi
will instruct to run inbrand pepsi
, and in the webpack.config regarding the css, I use the CSS query for the other brand(s) and with nullLoader, to delete the other brand's styles.This is fine in all scenarios using styles, scoped, etc, except when modules is involved.
Please paste the results of
npx webpack-cli info
here, and mention other relevant informationConclusion
What I came to learn when debugging is that nullLoader doesn't "delete" the tag, it empties it's contents as it is what arrives as source when querying for .scss/.css. As I've shown before, only the LAST style with a module in each file keeps it's locals hashmap. Which means, if the last module in the file is the one being emptied, the empty style tag will make the entire locals empty (
locals = {}
).With this, I understood that CSS module doesn't support multiple style tags w/ module attribute, doesn't support multiple module attributes with same name, nor multiple different module names, etc.
As a "hacky" way to circumvent this issue (which doesn't cover everything, only when specifically has 2 styles with module, and both are nameless or named the same), I wrote a loader that runs before vue-loader, where I use RegExp to "delete" the other brand's style tag as a whole (commented, https://github.com/RaphaelDDL/vue3-css-module/blob/main/webpack.config.js#L108-L122 ) but of course, this approach is hard to read for those not used to regexp and VERY error-prone (e.g. the regexp atm doesn't cover yet self-closing styles, normally used when used with
src
for external files, nor covers when there are more than 2 brands).With the little documentation CSS Modules has in https://github.com/css-modules/css-modules (seems abandoned, last commit 6 years ago, multiple issues opened without discussion or conclusion), I'm not sure if having multiple modules not working is a real bug or this was never thought as an use-case in the concept of this feature.
I've looked at code regarding module's locals on css-loader utils L1182+, style-loader (index.js L108+, utils L198+) and vue-style-loader (index.js L43), seems they always expect a module or a named module, only?
Thank you for taking your time reading this, I appreciate any hint regarding this.
Best
The text was updated successfully, but these errors were encountered: