Giter Club home page Giter Club logo

babel-plugin-react-css-modules's Introduction

Babel Plugin: React CSS Modules

Latest NPM Release NPM monthly downloads CircleCI GitHub Repo stars Dr. Pogodin Studio

Babel plugin for advanced CSS modules support in React:

  • It transforms styleName attribute of JSX components into className using compile-time CSS module resolution, allowing for a cleaner use of CSS modules in React.
  • For server-side rendering (SSR) scenarios it can replace named stylesheet imports by classname mapping objects, and remove anonymous stylesheet imports.

Sponsor

Content

Usage Examples

Assuming style.css in the following examples is compiled as CSS Module.

Without this plugin

import S from './styles.css';

export default function Component() {
  return (
    <div className={S.container}>
      <h1 className={S.title}>Example</div>
      <p styleName={S.text}>Sample text paragraph.</p>
      <p className={`${S.text} ${S.special}`}>
        Sample text paragraph with special style.
      </p>
    </div>
  );
}

With this plugin

import './styles.css';

export default function Component() {
  return (
    <div styleName="container">
      <h1 styleName="title">Example</div>
      <p styleName="text">Sample text paragraph.</p>
      <p styleName="text special">
        Sample text paragraph with special style.
      </p>
    </div>
  );
}

With this plugin and multiple stylesheets

Assuming:

  • Styles container, title, and text are defined in styles-01.css.
  • Style special is defined in styles-02.css.
  • The plugin's autoResolveMultipleImports option is enabled (default).
import './styles-01.css';
import './styles-02.css';

export default function Component() {
  return (
    <div styleName="container">
      <h1 styleName="title">Example</div>
      <p styleName="text">Sample text paragraph.</p>
      <p styleName="text special">
        Sample text paragraph with special style.
      </p>
    </div>
  );
}

If both files, styles-01.css and styles-02.css contain styles with the same names, thus making auto resolution impossible, this plugin allows explicit stylesheet prefixes:

import S1 from './styles-01.css';
import S2 from './styles-02.css';

export default function Component() {
  return (
    <div styleName="S1.container">
      <h1 styleName="S1.title">Example</div>
      <p styleName="S1.text">Sample text paragraph.</p>
      <p styleName="S1.text S2.special">
        Sample text paragraph with special style.
      </p>
    </div>
  );
}

With this plugin and runtime resolution

import './styles-01.css';
import './styles-02.css';

export default function Component({ special }) {
  let textStyle = 'text';
  if (special) textStyle += ' special';

  return (
    <div styleName="container">
      <h1 styleName="title">Example</div>
      <p styleName={textStyle}>Sample text paragraph.</p>
      <p styleName={textStyle}>
        Sample text paragraph with special style.
      </p>
    </div>
  );
}

In the case when the exact style value is not known at the compile time, like in this example, the plugin will inject necessary code to correctly resolve the styleName at runtime (which is somewhat less performant, but otherwise works fine).

SSR scenario

Consider such component, which uses a named stylesheet import in order to use it in some other ways, beside simple styling, e.g. to also display the classname mapping:

import S from './style.css';

export default function Component() {
  return (
    <div styleName="container">
      {JSON.stringify(S)}
    </div>
  )
}

While by default this plugin transforms it into (leaving it to the Webpack's css-loader to handle ./style.scss import for the actual CSS bundling, and leaving a correct JS in place of it):

import S from './style.css';

export default function Component() {
  return (
    <div className="12345">
      {JSON.stringify(S)}
    </div>
  )
}

For server-side environment, if you don't compile server-side code with Webpack, you'll need to replace ./style.css with valid JS code. That is exactly what this plugin does with replaceImport option enabled, it outputs:

const S = {
  container: '12345',
  // Other stylesheet keys, if any.
};

export default function Component() {
  return (
    <div className="12345">
      {JSON.stringify(S)}
    </div>
  )
}

CommonJS require() support

The plugin works the same with require('./style.css') CSS imports.

Installation

  • The core CSS Modules functionality should be enabled and configured elsewhere in your React project:

  • Install this plugin as a direct dependency (in edge-cases not allowing for a compile-time styleName resolution, the plugin falls back to the runtime resolution).

    npm install --save @dr.pogodin/babel-plugin-react-css-modules
    
  • Install Webpack at least as a dev dependency:

    npm install --save-dev webpack
    
  • Add the plugin to Babel configuration:

    {
      "plugins": [
        ["@dr.pogodin/react-css-modules", {
          // The default localIdentName in "css-loader" is "[hash:base64]",
          // it is highly-recommended to explicitly specify the same value
          // both here, and in "css-loader" options, including the hash length
          // (the last digit in the template below).
          "generateScopedName": "[hash:base64:6]"
    
          // See below for all valid options.
        }]
      ]
    }
  • The generateScopedName option value MUST match localIdentName option of css-loader to ensure both Webpack and this plugin generate matching class names. The same goes for other options impacting class names (e.g. the default length of hashes generated by Webpack, which is used if you don't specify the hash length explicitly in localIdentName hash placeholders), and also the actuals version of this plugin and css-loader (see css-loader compatibility).

  • Optional. css-loader is known for eventual minor updates in their default class name generation logic that require counterpart upgrades of this plugin to keep it compatible. They denied to expose the default class name generator for re-used by 3rd party libraries, and suggested to rely on getLocalIdent option if unwanted class name changes due to css-loader updates are a problem for a particular project.

    To alleviate this issue, this plugin provides stable default implementation for getLocalIdent function (taken from a selected earlier version of css-loader). Consider to use it:

    Within Webpack Config

    const { getLocalIdent } = require('@dr.pogodin/babel-plugin-react-css-modules/utils');
    
    const cssLoaderOptions = {
      modules: {
        getLocalIdent,
        localIdentName: '[path]___[name]__[local]___[hash:base64:6]'
      }
    };

    Within Babel Config

    const { generateScopedNameFactory } = require('@dr.pogodin/babel-plugin-react-css-modules/utils');
    
    module.exports = {
      plugins: [
        ["@dr.pogodin/react-css-modules", {
          generateScopedName:
            // The classname template MUST match "localIdentName" option value
            // you passed to "css-loader".
            generateScopedNameFactory("[path]___[name]__[local]___[hash:base64:6]"),
        }]
      ]
    };

    In addition to the standard class name template placeholders mentioned in css-loader documentation the version of getLocalIdent() and generateScopedName() provided by this plugin also support [package] placeholder. If used, it looks up from CSS file for the closest package.json file, takes the package name from it, and inserts it into the class name (this is useful for CSS bundling for libraries).

React Native

If you'd like to get this working in React Native, you're going to have to allow custom import extensions, via a rn-cli.config.js file:

module.exports = {
  getAssetExts() {
    return ["scss"];
  }
}

Remember, also, that the bundler caches things like plugins and presets. If you want to change your .babelrc (to add this plugin) then you'll want to add the --reset-cache flag to the end of the package command.

Configuration

Plugin Options

These are valid plugin options. All are optional, but the overall configuration should be compatible with that of css-loader, thus defaults may not work for you.

  • context - string - Must match webpack context configuration. css-loader inherits context values from webpack. Other CSS module implementations might use different context resolution logic. Defaults process.cwd().

  • exclude - string - A RegExp that will exclude otherwise included files e.g., to exclude all styles from node_modules: exclude: 'node_modules'.

  • filetypes - Configurate syntax loaders like sugarss, LESS and SCSS, and extra plugins for them.

  • generateScopedName - function | string - Allows to customize the exact styleName to className conversion algorithm. For details see Generating scoped names. Defaults [path]___[name]__[local]___[hash:base64:5].

  • replaceImport - boolean - Replaces / removes stylesheet imports for server-side rendering purposes. See details below. Defaults false.

  • webpackHotModuleReloading - boolean | "commonjs" - Enables injection of Hot Module Reloading code.

  • handleMissingStyleName - string - Determines what should be done for undefined CSS modules (using a styleName for which there is no CSS module defined). Valid values: "throw", "warn", "ignore". Setting this option to "ignore" is equivalent to setting errorWhenNotFound: false in react-css-modules. Defaults "throw".

  • attributeNames - Custom Attribute Mapping

  • skip - boolean - Whether to apply plugin if no matching attributeNames found in the file. Defaults false.

  • transform - function - If provided, each CSS source loaded by the plugin will be passed through this function, alongside its path, and this plugin's options, and the output of this function will be used in place of the original CSS.

  • autoResolveMultipleImports - boolean - Allows multiple anonymous imports if styleName is only in one of them. Defaults true.

Deprecated Plugin Options

  • removeImport - boolean - Use replaceImport option instead.

Configurate syntax loaders

To add support for different CSS syntaxes (e.g. SCSS), perform the following two steps:

  1. Add the postcss syntax loader as a development dependency:

    npm install postcss-scss --save-dev
  2. Add a filetypes syntax mapping to the Babel plugin configuration. For example for SCSS:

    "filetypes": {
      ".scss": {
        "syntax": "postcss-scss"
      }
    }

    And optionally specify extra plugins:

    "filetypes": {
      ".scss": {
        "syntax": "postcss-scss",
        "plugins": [
          "postcss-nested"
        ]
      }
    }

    NOTE: postcss-nested is added as an extra plugin for demonstration purposes only. It's not needed with postcss-scss because SCSS already supports nesting.

    Postcss plugins can have options specified by wrapping the name and an options object in an array inside your config:

      "plugins": [
        ["postcss-import-sync2", {
          "path": ["src/styles", "shared/styles"]
        }],
        "postcss-nested"
      ]

Hot Module Reloading

If you don't know what is Hot Module Reloading (HMR), refer to the Webpack documentation.

If you use HMR in your development setup (you probably should), depending on your particular configuration you might need to enable webpackHotModuleReloading option of this plugin, or you may need to leave it disabled (default), as other loaders / plugins in your Webpack pipeline for CSS may already inject required HMR code.

In case you decide to enable it in this plugin, webpackHotModuleReloading option may be set equal:

  • true - this plugin will inject HMR accept code for each imported CSS module, using import.meta.webpackHot (ESM) syntax (see for details).
  • commonjs string - this plugin will inject HMR accept code using the legacy module.hot syntax.

The default value is false - this plugin does not inject HMR accept code.

transform

function transform(cssSource, cssSourceFilePath, pluginOptions): string

The transform function, if provided as the transform option of this plugin, will be called for each loaded CSS source with three arguments:

  • cssSource - string - The loaded CSS code.
  • cssSourceFilePath - string - The path of loaded CSS file.
  • pluginOptions - object - The options set for this plugin.

It should return a string, the actual CSS code to use.

Custom Attribute

You can set your own attribute mapping rules using the attributeNames option.

It's an object, where keys are source attribute names and values are destination attribute names.

For example, the <NavLink> component from React Router has an activeClassName attribute to accept an additional class name. You can set "attributeNames": { "activeStyleName": "activeClassName" } to transform it.

The default styleName -> className transformation will not be affected by an attributeNames value without a styleName key. Of course you can use { "styleName": "somethingOther" } to change it, or use { "styleName": null } to disable it.

Server-Side Rendering

If replaceImport flag is set, this plugin will remove or replace original stylesheet imports, which is needed for server-side rendering:

// Anonymous imports are removed from the code:
import 'path/to/style.css';

// Default and named imports are replaced in the following manner:

// Before:
import styles, {
  className,
  otherClassName as alias,
} from 'path/to/style.css';

// After:
const styles = {
  className: 'generatedClassName',
  otherClassName: 'otherGeneratedClassName',
},
className = 'generatedClassName',
alias = 'otherGeneratedClassName';

// Also this kind of import:
import * as style from 'path/to/style.css';

// is replaced by:
const style = {
  className: 'generatedClassName',
  otherClassName: 'otherGeneratedClassName',
};

Under the hood

How does it work?

This plugin does the following:

  1. Builds index of all stylesheet imports per file (imports of files with .css or .scss extension).
  2. Uses postcss to parse the matching CSS files into a lookup of CSS module references.
  3. Iterates through all JSX element declarations.
  4. Parses the styleName attribute value into anonymous and named CSS module references.
  5. Finds the CSS class name matching the CSS module reference:
    • If styleName value is a string literal, generates a string literal value.
    • If styleName value is a jSXExpressionContainer, uses a helper function (getClassName) to construct the className value at the runtime.
  6. Removes the styleName attribute from the element.
  7. Appends the resulting className to the existing className value (creates className attribute if one does not exist).

Project history

This plugin is an up-to-date, well-maintained fork of the original babel-plugin-react-css-modules:

  • It generates class names matching current css-loader versions (see css-loader compatibility for details).
  • All dependencies are upgraded to the latest versions.
  • Follow-up maintenance and improvements are performed as necessary.

The original babel-plugin-react-css-modules plugin is largely abandoned by its author since March 2019. When an year later updates of css-loader and Webpack broke dependant projects, with no reaction from babel-plugin-react-css-modules author on emerging issue reports in GitHub, I (birdofpreyru) created this fork to ensure stability of my own projects relying on it.

I am banned from commenting in the original project repo since I tried a little self-promo, trying to encourage people to switch over to my fork. If you read this, consider to spread the word to encourage more users to move to this fork.

Migration from babel-plugin-react-css-modules

  • Prefix plugin name in your Babel config by @dr.pogodin/ scope, i.e.: @dr.pogodin/babel-plugin-react-css-modules or @dr.pogodin/react-css-moudles instead of babel-plugin-react-css-modules or react-css-modules.

  • Be sure to have webpack installed (it is a must-to-have peer dependency of this plugin starting from v6.2.0).

css-loader compatibility

css-loader versions this plugin versions
7.0.07.1.2 (latest) 6.13.06.13.2 (latest)
6.7.16.11.0 6.7.06.12.0
6.5.06.7.0 6.5.16.6.1
6.4.0 6.4.06.4.1
6.0.06.3.0 6.2.16.3.1
5.2.55.2.7 6.1.1
5.2.4 6.1.0
5.1.35.2.3 6.0.11 / 6.1.0(1)
5.0.05.1.2 6.0.76.0.11
4.2.04.3.0 6.0.36.0.6
3.6.0 original plugin

1) There might be some corner-case differences in class name transformation between these versions of css-loader and this plugin, but most probably they won't break compatibility for most users.

babel-plugin-react-css-modules's People

Contributors

albertlucianto avatar andreascag avatar andris-silis avatar arashmotamedi avatar assertchris avatar benmvp avatar billy- avatar birdofpreyru avatar clessg avatar curtishumphrey avatar d-oliveros avatar dependabot[bot] avatar dsullivan7 avatar gajus avatar gbiryukov avatar gpittarelli avatar ianvs avatar j4chou avatar jcdekoning avatar jjinux avatar kamui avatar kilian avatar loeck avatar mvsmal avatar oscarbarrett avatar pcreations avatar psykar avatar pturchik avatar sthzg avatar trevorsmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

babel-plugin-react-css-modules's Issues

Implement a "SCSS preset" supporting all standard SCSS features out of the box (those for which just postcss-scss is not enough)

Hi,
I have trouble making the plugin work with Webpack 5 + SCSS. I had a working setup with the original gajus/babel-plugin-react-css-modules, Webpack 4, and SCSS. During the migration to Webpack 5 I had to swap the original plugin for yours (thank you for maintaining the fork!). The configuration remained mostly the same.

I have created a minimalistic example here: https://github.com/vlastimil-dolejs/css-modules-scss-bug

The error I get is:

ERROR in ./src/Test.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
CssSyntaxError: C:\code\css-modules-scss-bug\src\Test.scss:6:4: Expected a pseudo-class or pseudo-element.
    at C:\code\css-modules-scss-bug\src\Test.scss:6:3
    at Input.error (C:\code\css-modules-scss-bug\node_modules\postcss\lib\input.js:148:16)
    at Rule.error (C:\code\css-modules-scss-bug\node_modules\postcss\lib\node.js:60:32)
    at Root._error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:177:25)
    at Root.error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\selectors\root.js:43:19)
    at Parser.error (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:740:21)
    at Parser.expected (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1133:19)
    at Parser.pseudo (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:875:19)
    at Parser.parse (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1084:14)
    at Parser.loop (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:1043:12)
    at new Parser (C:\code\css-modules-scss-bug\node_modules\postcss-selector-parser\dist\parser.js:164:10)
 @ ./src/index.js 3:0-26 4:50-54

The error is thrown, when there is some special SASS construct in the .scss file. It looks like the class name lookup is done in .scss file before it is processed by postcss-scss.

Relevant part of babel.config.js

const plugins = [
    ["@dr.pogodin/react-css-modules", {
        "exclude": "node_modules",
        "generateScopedName": "[path][name]__[local]",
        "filetypes": {
            ".scss": {
                "syntax": "postcss-scss"
            }
        }
    }]
];

Relevant part of webpack.config.js

{
    test: /\.scss$/,
    exclude: /node_modules/,
    use: [
        MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                modules: {
                    localIdentName: "[path][name]__[local]",
                },
                importLoaders: 1, // to include sass-loader
                sourceMap: true,
            }
        },
        "sass-loader"
    ]
}

exclude: 'node_modules' Not working

I have installed antd@4 in my project and included the following line in my src/pages/index.tsx file:

import 'antd/dist/antd.css';

After building the project with pnpm build, I noticed that the CSS for antd in the umi.css file in the build directory includes a _hash suffix, even though I have already excluded node_modules in the build configuration. Can you please help me identify where I went wrong?

Here is a minimal example of my setup.
umi-codemao-mobile 2.zip
Steps:

  1. pnpm i
  2. pnpm build

Cannot find module 'babel-plugin-react-css-modules'

When building my project using your fork, I'm getting this error:

Module build failed (from ../node_modules/babel-loader/lib/index.js):
Error: Cannot find module 'babel-plugin-react-css-modules' from '<path-to-project-directory>'

I removed the original babel-plugin-react-css-modules npm module and replaced it with yours. Am I missing something?

Doesn't work with yarn berry PnP mode

I tried migrating to this but I get an error when it tries to resolve the path to a css file:

 @dr.pogodin/babel-plugin-react-css-modules tried to access @draft-js-plugins/alignment, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.

Required package: @draft-js-plugins/alignment (via "@draft-js-plugins/alignment/lib/plugin.css")
Required by: @dr.pogodin/babel-plugin-react-css-modules@virtual:978be09f033f9d1d87a6ae470cf41768305eae1692596bdc022dee76fc3671ae951ebf07cf433ace646b0e1c79a9e1b2be00e15f7afac12075ecdcf897b30d3a#npm:6.9.4 (via /home/dobes/projects/formative/app/.yarn/__virtual__/@dr.pogodin-babel-plugin-react-css-modules-virtual-cbe7ecd98b/0/cache/@dr.pogodin-babel-plugin-react-css-modules-npm-6.9.4-458b93ef69-ad14c19a11.zip/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/)

In Yarn PnP environments you cannot use require directly to access the dependencies of the packages that use your package. You have to do something like:

import { createRequire } from 'node:module';

// Sometime later

function resolveUsingImportLogic(path, projectDir = process.cwd()) {
  const targetRequire = createRequire(
    path.resolve(projectDir, 'package.json'),
  );
  const resolvedPath = targetRequire.resolve(thingYouWantToResolve);
}

TODO: Add atlernative "stable" getLocalIdent into the codebase

As mentioned in #13, it is not that cool that updates of css-loader alter generated classnames hashes now and then. There is good workaround: to provide css-loader with a custom getLocalIdent() function to use, thus not depending on updates in its default implementation within css-loader. It will be handy to provide such alternative stable function as a part of this plugin.

Cannot find module 'webpack/lib/TemplatedPathPlugin'

I want to use babel-plugin-react-css-modules in umi

This exception occurs when npm run dev is executed

Error: [BABEL]: Cannot find module 'webpack/lib/TemplatedPathPlugin'
Require stack:
- /node_modules/.pnpm/[email protected][email protected]/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/getLocalIdent.js
- /node_modules/.pnpm/[email protected][email protected]/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/requireCssModule.js
- /node_modules/.pnpm/[email protected][email protected]/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/index.js

The simplest demo

  1. npx create-umi@latest
  2. add bottom configuration
  3. npm run dev
// add .umirc.ts
  mfsu: false,
  extraBabelPlugins: [
    [
      '@dr.pogodin/react-css-modules',
      {
        generateScopedName: generateScopedNameFactory(
          '[path]___[name]__[local]___[hash:base64:6]',
        ),
        webpackHotModuleReloading: true,
        handleMissingStyleName: 'warn',
        filetypes: {
          '.less': {
            syntax: 'postcss-less',
          },
          '.scss': {
            syntax: 'postcss-scss',
          },
        },
      },
    ] as any,
  ],
  autoCSSModules: false,
  cssLoader: {
    modules: {
      getLocalIdent,
      localIdentName: '[path]___[name]__[local]___[hash:base64:6]',
    },
  },

Thank you for your help

`v6.10.0` is not compatible with projects that are built and used in different locations

It turns out PR #42 is not compatible with projects that are meant to be build in one location, and used in another. This is because it effectively injects into compiled code the absolute path to getClassName at the moment of compilation; which will be invalid if compiled project is moved to a different location.

Thus, I recommend to use the previous release, v6.9.4 for now; until I find time to investigate how to solve the problem solved by PR #42 without causing the problem it introduced.

TypeError: [BABEL]

TypeError: [BABEL]: Cannot read properties of undefined (reading 'hooks') (While processing: /Users/data/react/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/index.js)

Issues migrating from babel-plugin-css-modules-transform with dart-sass

I’m migrating an older project which uses babel-plugin-css-modules-transform, and after some research I discovered you now recommend your babel-plugin-react-css-modules babel plugin as an alternative approach, as the previous packages are all abandoned/outdated.

My project setup involves React + TypeScript with SCSS. The package is published in two parts: an NPM package containing babelified JS files representing React components as CommonJS, plus a manifest file, and a set of static assets (a single minified CSS file, fonts etc.) which are published to a CDN.

The publish pipeline also runs in two parts: first it uses webpack to compile the Sass into the stylesheet published on the CDN, and then it uses babel to compile the respective JS files. As part of this process I have always used css-modules-transform to add the var styles = { ... } object to each component. So, assuming I start out with:

// styles.scss
.someClassName {
  color: red;
}
/* MyComponent.tsx */
import styles from './styles.scss';

export const MyComponent = () => <div className={styles.someClassName}>hello world</div>;

I would expect to get something like this output:

/* application.css */
.kpwz-fc6e99e5512e50ed{color:red}
/* MyComponent.js */
var styles = {
  "someClassName": "kpwz-fc6e99e5512e50ed"
};

const MyComponent = () => {
  return /*#__PURE__*/(0, _jsxRuntime.jsx)('div', {
    className: styles.someClassName,
  });
};

exports.MyComponent = MyComponent;

All of the above has been working fine with my existing setup, and I’m using it successfully on a couple of sites (e.g. here). But a bunch of the internals were a little outdated, so I’ve started the work to gradually upgrade from e.g. webpack 4 to 5, babel 6 to 7, node 14 to 16 (so far).

However, the upgrade from node-sass to dart-sass has blocked all of this: for whatever reason the ident name generated by generateScopedName is different between the stylesheets emitted by webpack and the styles = { ... } block appended to the component by babel-plugin-css-modules-transform. I’ve exhausted all the paths I feel I can explore (for my level of knowledge of babel, at least) to try and understand why this is happening, but as best as I can tell, something about the different way that the ident name is being determined from babel-plugin-css-modules-transform compared to webpack (with css-loader, postcss-loader and sass-loader) is resulting in these different classnames.

Circling back to the start, I discovered your package and it sounds like it should solve the problem I have, but I’m not able to get it to output the var styles = { ... } block in the published component, despite following what the readme suggests. This is the config I have in my .babelrc:

"plugins": [
  "@babel/plugin-proposal-class-properties",
  "add-react-displayname",
  [
    "@dr.pogodin/react-css-modules",
    {
      "generateScopedName": "kpwz-[contenthash:16]",
      "replaceImport": true
    }
  ]
]

For completeness, here is the equivalent section of my webpack.config.js:

{
  test: /\.scss$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
    },
    {
      loader: 'css-loader',
      options: {
        modules: { localIdentName: 'kpwz-[contenthash:16]' },
        sourceMap: false,
      },
    },
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [cssnano()],
        },
      },
    },
    {
      loader: 'sass-loader',
      options: {
        implementation: require('sass'),
      },
    },
  ],
},

What am I missing from my config to get the desired output?

Support of named CSS imports (`css-loader`'s `namedExport`)

css-loader's namedExport option (which is default in some cases since its v7.0.0) requires to import CSS stylesheets (when the mapping is needed in JS code) like this

import * as S from "./style.css";

// or, I guess
import { a, b, c } from "./style.css";

// Instead of just
import S from "./style.css";

Thus, we need to update this Babel plugin to support named CSS imports (which it currently does not).

Error received after updating to 6.5.0

Not sure why, but when switching from 6.4.1 to 6.5.0, my compile fails with:

ERROR in ./src/universal/ui/widgets/form/FormRatingUiw.tsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: localident.replace(...).replace(...).replace(...).replaceAll is not a function
    at /Users/markcrawshaw/Code/metamap/src/universal/ui/widgets/form/FormRatingUiw.pcss:1:1
    at escapeLocalIdent (/Users/markcrawshaw/Code/metamap/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/getLocalIdent.js:197:101)
    at getLocalIdent (/Users/markcrawshaw/Code/metamap/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/getLocalIdent.js:297:10)
    at generateScopedName (/Users/markcrawshaw/Code/metamap/node_modules/@dr.pogodin/babel-plugin-react-css-modules/dist/requireCssModule.js:95:41)
    at exportScopedName (/Users/markcrawshaw/Code/metamap/node_modules/postcss-modules-scope/src/index.js:95:28)
    at localizeNode (/Users/markcrawshaw/Code/metamap/node_modules/postcss-modules-scope/src/index.js:124:22)
    at Array.map (<anonymous>)
    at Selector.map (/Users/markcrawshaw/Code/metamap/node_modules/postcss-selector-parser/dist/selectors/container.js:347:23)
    at localizeNode (/Users/markcrawshaw/Code/metamap/node_modules/postcss-modules-scope/src/index.js:120:31)
    at traverseNode (/Users/markcrawshaw/Code/metamap/node_modules/postcss-modules-scope/src/index.js:152:32)
    at Selector.each (/Users/markcrawshaw/Code/metamap/node_modules/postcss-selector-parser/dist/selectors/container.js:207:16)

Consider to migrate to "postcss-modules" dependency from "@dr.pogodin/postcss-modules-parser"

Thus, it will be great to migrate postcss-modules.

Does not compile with webpack 5.58.2

Hey!

Thanks for the plugin!
Unfortunately, there's an issue if we try to run it with the latest version of the webpack (5.58.2).

The error looks like this:

 [BABEL]: Cannot read property 'hooks' of undefined

Do you mind having a look?
Thanks!

TODO: Update README

The README should be re-worked to make it clear how to install, configure, and use the plugin. Currently the technicalities not that important to the end users are very prominent, and the actually the most important instructions are not that prominent, buried somewhere deep inside the README.

It can`t work with less className "&-xxx"

less file:
.warp{ color: #f00 &-item:{ color: #0f0 } }
It run with error : cant resolve this styleName : "warp-item" babel config
[
"@dr.pogodin/react-css-modules",
{
// 达到类似vue的scoped效果
filetypes: {
".less": {
syntax: "postcss-less",
plugins: ["postcss-nested"],
},
},
generateScopedName: "[local]-[hash:base64:10]",
webpackHotModuleReloading: true,
},
]
`
node version 1417.3
"less": "^4.1.2",
"less-loader": "^10.2.0",
"webpack": "^5.59.1",
"@babel/core": "^7.15.8",
"style-loader": "^3.3.1",
"@dr.pogodin/babel-plugin-react-css-modules": "^6.5.1",

An issue with "filetypes[type].plugins" config option (or better say with some extra PostCSS plugins which may be applied via that option)

To reproduce, try to upgrade the postcss-nested dev dependency to the latest 5.0.3 version, then run tests, and explore how applies extra plugins test case starts to fail. The root cause is this change in postcss-nested, which in my best understanding moves the postcss-nested transformation to PostCSS exit from a stylesheet code node, thus the transformation happens after the internal PostCSS plugins used by babel-plugin-react-css-modules extract classnames.

Technically the fix is clear: to update those internal PostCSS plugins to do their work on node exits, thus after any other PostCSS plugin; however it probably may have some side-effects, and also those are 3rd-party plugins, thus probably they won't be open to do such change.

Thus, for now just logging it here, and not planning to do anything about it.

Failed tests on CRA app

After this change: 8b5b79d I have errors when I run tests on CRA-app

FAIL src/.../index.test.tsx
   Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    if (import.meta.webpackHot) {
               ^^^^

    SyntaxError: Cannot use 'import.meta' outside a module

cc @pturchik

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.