0

TL;DR

I'm importing a css file into a typescript module, but the import resolves to a string instead of an object. Can anyone tell me why I don't get an object??

Example

// preview.ts

import test from './src/assets/test.theme.css';

// also tried this:
// import * as test from './src/assets/test.theme.css';

console.log('typeof test: ', typeof test);
console.log(test);

Console output screenshot

Detailed explanation

Actually, I'm trying to set up a Storybook for my Angular12 component library.

In order to provide various themes, I want to use the @etchteam/storybook-addon-css-variables-theme plugin, which in its documentation refers to the inline loader syntax of Webpack.

import myTheme from '!!style-loader?injectType=lazyStyleTag!css-loader!./assets/my-theme.css';

When applying this to my code my browser console started to complain

Error: myTheme.use is not a function

During my research I recognized that the imported stylesheet is not an evaluated javascript object, but instead it is provided as a string containing the sourcecode generated by the style-loader.

I also recognized, that this issue is not specific to the style-loader, but also occurs for all other loaders, e.g. css-loader, raw-loader, etc.

This issue is also not related to inline loader syntax, as it also shows up with loaders being defined in a minimalistic webpack config.

Environment:

  • Angular 12
  • Webpack 5

Reproduction

I have set up a GIT repo reproducing the issue.

The readme file explains the repro and the issue.

  • Why would you do this with Angular? – Akxe Oct 18 '21 at 10:46
  • Actually, I'm trying to setup a Storybook for my Angular component library. I wanted to use the [Storybook addon "cssVariablesTheme"](https://github.com/etchteam/https://github.com/etchteam/storybook-addon-css-variables-theme) which proposes to use the style-loader lazyStyleTag feature to enable and disable themes at runtime. While following the documentation, I hit weird issues. When further drilling into the issue, I found out, that it also happens with the described simple use case. Btw: Storybook dynamically generates a webpack config based on my angular.json settings. – Markus Windhager Oct 18 '21 at 10:51
  • 1
    Maybe it could be a simple import error try `import * as test from './src/assets/test.theme.css'` – Akxe Oct 18 '21 at 10:57
  • Have you tried this `import './style.css'`? – K i Oct 18 '21 at 10:59

4 Answers4

0

I think you have mistake in your Webpack config. You have nested rules property, instead you should have use:

{
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  }

https://webpack.js.org/loaders/css-loader/

Maksym Shcherban
  • 742
  • 4
  • 13
  • The webpack config is just an excerpt. My actual webpack config is generated by Storybook based on my angular.json settings. My actual issue is described [here](https://stackoverflow.com/questions/69570350/import-test-from-css-loader-test-css-returns-string), but no-one bothered even reading it... maybe I was not clear enough with my question... ;) – Markus Windhager Oct 18 '21 at 10:55
0

I'm sorry, but I have to revert my last statement. My issue has NOT been resolved by @Akxe's comment. Now my import statement (import * as test from '...') resolves to an object, but it's still not correct.

I have set up a GIT Repo to reproduce the issue. The readme.md file explains the repro and the issue.

It looks like Webpack is not executing/evaluating the return value of the loader.

Btw. this is not just the case with the css-loader. The result stays the same for raw-loader, sass-loader, style-loader, etc.

My final goal is to lazily load my theme files into a storybook. I try to follow the documentation of the @etchteam/storybook-addon-css-variables-> theme.

0

Finally I got my issue solved!

Analysis

The main issue here is the webpack configuration generated by the @angular-devkit/build-angular package. I was able to analyze it by debugging a fresh angular12 application (you can check it out here).

By setting a break-point at /node_modules/@angular-devkit/build-angular/src/utils/webpack-browser-config.js, function: generateWebpackConfig(...), I could inspect the final webpackConfig object in the debugger.

The relevant rule looks like this:

The important part here is the rule setting the module type to asset/source, instructing webpack not to evaluate the loader's result.

webpackConfig object

Solution concept 1: inline loader

With the help of alexander-kait and his great hints at this issue, I was able to find an inline-loader syntax that overrides webpack's module declaration:

import Test from 'test.css.webpack[javascript/auto]!=!!!style-loader?injectType=lazyStyleTag!css-loader!./test.css';

console.log(typeof Test); // output: object
console.log(Test); // output: Object { use: () => void, unuse: () => void }
Test.use(); // this should usually be called by a theme switcher...

I'm not really sure about the url pattern here, as it seems to be an undocumented feature, but I assume that it's something like <query-pattern>.webpack[<module-type>]!=!<loaders><query>.

However, since this is an undocumented feature, I was rather reluctant to use it.

Solution concept 2: webpackConfig customization

Since I'm in a storybook context, I decided to customize the webpack configuration according to the storybook documentation.

My solution requires to set up a naming convention (e.g. *.theme.css).

// .storybook/main.js

module.exports = {
  webpackFinal: async (config) => {

    // exclude *.theme.css from the *.css ruleset
    config.module.rules.find(rule => '.css'.match(rule.test)).exclude = [ /\.(?:theme\.css)$/i ];

    // add a rule for *.theme.css
    config.module.rules.push({
      test: /\.(?:theme\.css)$/i,
      use: [
        { loader: 'style-loader', options: { injectType: 'lazyStyleTag' } },
        'css-loader',
      ],
    });
  },
};

With these rules in place, I can now simply do the following:

// preview.js

import LightTheme from './light.theme.css';
import DarkTheme from './dark.theme.css';

setupThemeSwitcher(LightTheme, DarkTheme);

Please note that the setupThemeSwitcher function is just pseudocode merely there for the example. In reality I'm using the @etchteam/storybook-addon-css-variables-theme addon...

0

I had a very similar issue with storybook and this extension, except l’m loading .scss files. I simply adapted solution 2 to suit my .scss case and it works like a charm. I couldn’t make solution 1 to work, but as stated, it sounds hacky whereas solution 2 is cleaner in my opinion. Thanks a lot for sharing this solution, I was struggling for hours.

Sam P.
  • 1
  • Please don't add "thank you" as an answer. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation), you will be able to [vote up questions and answers](https://stackoverflow.com/help/privileges/vote-up) that you found helpful. - [From Review](/review/late-answers/30500703) – Andrey Dec 04 '21 at 07:45