ES6 import of CSS file results in string output instead of object

Too long; Didn't read

I'm facing an issue where the css file I import into a typescript module resolves to a string instead of an object. Can someone help me understand why this is happening?

For Instance

// 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);

What's displayed on the console: https://i.stack.imgur.com/j30hP.png

In-depth Explanation

Currently, I am setting up a Storybook for my Angular12 component library.

To provide different themes, I intend to utilize the

@etchteam/storybook-addon-css-variables-theme
plugin, which mentions the inline loader syntax of Webpack in its documentation.

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

Upon implementing this in my code, my browser console started throwing errors

Error: myTheme.use is not a function

While investigating, I discovered that the imported stylesheet isn't an evaluated javascript object but rather a string containing the source code created by the style-loader.

I also found that this issue extends beyond the style-loader and applies to other loaders like css-loader, raw-loader, etc.

This problem isn't limited to the inline loader syntax and also occurs with loaders defined in a basic webpack configuration.

Environment details:

  • Angular 12
  • Webpack 5

Replication

I have created a GIT repository demonstrating the problem.

The readme file explains how to reproduce the issue.

Answer №1

Hooray! The problem has been resolved!

Investigation

The root issue lies within the webpack configuration generated by the @angular-devkit/build-angular package. I delved into this by debugging a new angular12 application (you can view it here).

By setting a break-point at

/node_modules/@angular-devkit/build-angular/src/utils/webpack-browser-config.js
, function: generateWebpackConfig(...), I was able to examine the final webpackConfig object in the debugger.

The relevant rule appears as follows:

The crucial part is the rule that defines the module type as asset/source, directing webpack not to assess the loader's result.

https://i.stack.imgur.com/6g0ij.png

Resolution Approach 1: inline loader

With insights from alexander-kait and his valuable suggestions on this issue, I discovered an inline-loader syntax that supersedes 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 am unsure about the url pattern here, as it seems to be an undocumented feature, but my assumption is that it resembles

<query-pattern>.webpack[<module-type>]!=!<loaders><query>
.

Yet, due to its undocumented nature, I displayed caution in utilizing it.

Resolution Approach 2: webpackConfig customization

Given the context of storybook, I opted to customize the webpack configuration following the guidance in the storybook documentation.

My solution necessitates establishing 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 directives in place, I can now easily execute 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 serves as pseudo code for illustration purposes only. In practice, I utilize the @etchteam/storybook-addon-css-variables-theme addon...

Answer №3

Apologies, I need to retract my previous statement. @Akxe's comment did not resolve my issue as I had hoped. Even after using the import statement import * as test from '...', the problem still persists.

To showcase the issue, I have created a GIT Repository. The repro and problem are detailed in the included readme.md file.

It appears that Webpack is failing to properly interpret the output of the loader.

This issue extends beyond just the css-loader; similar results happen with raw-loader, sass-loader, style-loader, and others.

My ultimate objective is to dynamically load theme files into a storybook environment. I am attempting to follow the instructions provided by @etchteam/storybook-addon-css-variables-> theme.

Answer №4

Dealing with storybook and this particular extension presented me with a similar challenge, although I am loading .scss files. I customized solution 2 to fit my specific .scss scenario, and it ended up working perfectly. Solution 1 proved to be elusive for me, but like you mentioned, it seemed hacky compared to the cleaner approach of solution 2 in my view. I truly appreciate you sharing this solution, as I had been struggling for hours before finding it.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can you position the input cursor at the end of the default text when navigating through fields with the tab key?

I've implemented tab index in the HTML to navigate from one field to another. In the image below, you can see me tabbing from "Revise" to "Link". https://i.stack.imgur.com/vb6L.png However, when I press tab, the default text in the Link field is fu ...

Validating (Update database with correct email and token)

Authentication (Update database if email and token are accurate) Even when the code is incorrect, it displays "Great Success", but the status does not update in my database. However, if I input the correct code, it shows "Great Success" and updates the s ...

Ensure every individual input field in a Bootstrap form is validated using JavaScript before submitting the form

As a newcomer to Javascript, I am seeking assistance with validating each field in the form below without having to click on the submit button. Your help is greatly appreciated! <form action="" id = "form1" class = "form-group row"> & ...

Converting Typescript objects containing arrays into a single array

Looking for some assistance with this problem :) I am trying to convert the object into an array with the following expected result: result = [ { id: 'test-1', message: 'test#1.1' }, { id: 'test-1', mess ...

ReactJS encountered an error: Module Build failed due to a SyntaxError caused by an unexpected token '<'

When I attempt to compile my ReactJS code, an error is thrown. Although I have experience with ReactJS, this specific error is new to me (shown below). ERROR in ./kapiche/@kapiche_react/teacher/login.jsx Module build failed (from ./node_modules/babel-load ...

Why does my event dispatch only run once upon form submission in Svelte JS?

My code successfully fetches data and puts it in a card when new data is added to the input. However, the issue arises when more than one data entry is made - although the data gets added to the database, it does not reflect in the data list. Can anyone he ...

Obtaining a Slug in the Server-Side Layout of a Next.js Component

I am facing an issue with passing a slug to a server-side function in my Next.js layout component. The problem arises when the slug is returning as undefined, and I am struggling to correctly access and utilize the slug on the server side within the layout ...

How to Set a Button as Inline Text in a React Native Component

Below is the code snippet I am working with utilizing the Text and Button components provided by react-native-paper: <Text>See also </Text> <Button mode="text" compact onPress={this.nav( name )}>Compass</Button> <Text&g ...

Using Node.js to display the outcome of an SQL query

I have been attempting to execute a select query from the database and display the results. However, although I can see the result in the console, it does not appear on the index page as expected. Additionally, there seems to be an issue with the way the r ...

Running JavaScript function from AJAX response containing both HTML and JavaScript code

For my first time using AJAX to prevent page refresh upon form submission, everything works flawlessly. The data is received in HTML form and placed into the designated div. However, I am encountering an issue with one of the JavaScript functions responsib ...

The server is unable to process your request to /graphql

I've encountered an issue while setting up the GraphiQL API. I keep receiving the error message "Cannot POST /graphql" on both the screen and network tab of the console. Despite having similar boilerplate code functioning in another project, I'm ...

Creating a multi-level signup page in Angular 4 using Bootstrap

Currently, I am working with angular 4 and bootstrap 4 to create a multi-level sign-up page. One of the challenges I am encountering is how to properly include a JavaScript file into my angular project. import '../../../assets/js/js/multiform.js&apo ...

What steps should I take to eliminate the white gap between the paragraph and the footer on my webpage?

If you're new to html and css, feel free to check out the url [file:///Users/CatherineDu/百度云同步盘/practice/about.html]. I'm currently working on building a simple website and facing an issue - there are unwanted white spaces between the ...

Update the color of the label on the ng2-charts/charts.js pie chart

I am currently using ng2-charts and have successfully created a beautiful pie-chart with labels and data. However, I am unable to change the default grey color of the labels. Is this normal? How can I override the default label color? It appears that the ...

The XMLHttpRequest response states that the preflight request did not meet the access control check requirements

I am attempting to subscribe to a firebase cloud messaging topic by sending an http post request. var data = null; var xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.addEventListener("readystatechange", function () { if (this.readyState ...

Converting a JavaScript array into JSON using PHP

After analyzing the text, I was trying to figure out how to convert it into json format using php's json_decode function. Unfortunately, when I used json_decode on this text, it returned a syntax error. $arr = json_decode($str, true); {1: {name: &ap ...

Angular dropdown list is malfunctioning when a value is selected

I'm struggling to use the selected value from a dropdown list in my component class. Despite trying various approaches, such as storing the value using ngModel and triggering an event function, I have not been able to make it work. <select [(ngMod ...

Verify if the Observable (HTTP request) has provided a response, and if not, hold for it?

In my Angular app, I have a calendarComponent that fetches entries from a MongoDB through firebase cloud functions using the calendarService. When creating a new entry, I display an addEventComponent dialog which requires data from the database to function ...

Troubleshooting a cross-origin resource sharing problem in Express.js

I've set up Express as the backend for my client application, but I keep encountering an issue when trying to make a request to the GET /urls endpoint. The error message I receive is: Access to fetch at 'http://localhost:5000/urls' from orig ...

I am facing an issue in my Vue Project where the CSS does not recognize URLs enclosed in quotes

When attempting to pass over a quoted URL to background-image or cursor, I'm facing an issue where the file simply doesn't load. I am currently working with Vue and have installed the following libraries: Vuetify, SASS, SASS-Loader, and Node-S ...