Giter VIP home page Giter VIP logo

css-modules-typescript-loader's Introduction

Build Status npm semantic-release Commitizen friendly

css-modules-typescript-loader

Webpack loader to create TypeScript declarations for CSS Modules.

Emits TypeScript declaration files matching your CSS Modules in the same location as your source files, e.g. src/Component.css will generate src/Component.css.d.ts.

Why?

There are currently a lot of solutions to this problem. However, this package differs in the following ways:

  • Encourages generated TypeScript declarations to be checked into source control, which allows webpack and tsc commands to be run in parallel in CI.

  • Ensures committed TypeScript declarations are in sync with the code that generated them via the verify mode.

Usage

Place css-modules-typescript-loader directly after css-loader in your webpack config.

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'css-modules-typescript-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }
};

Verify Mode

Since the TypeScript declarations are generated by webpack, they may potentially be out of date by the time you run tsc. To ensure your types are up to date, you can run the loader in verify mode, which is particularly useful in CI.

For example:

{
  loader: 'css-modules-typescript-loader',
  options: {
    mode: process.env.CI ? 'verify' : 'emit'
  }
}

Instead of emitting new TypeScript declarations, this will throw an error if a generated declaration doesn't match the committed one. This allows tsc and webpack to run in parallel in CI, if desired.

This workflow is similar to using the Prettier --list-different option.

With Thanks

This package borrows heavily from typings-for-css-modules-loader.

License

MIT.

css-modules-typescript-loader's People

Contributors

alexanderogilvie avatar bhollis avatar bushee avatar danieljuhl avatar greglockwood avatar kepek avatar kraenhansen avatar markdalgleish avatar mattcompiles avatar michaeltaranto avatar mydatahack avatar stephtr 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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

css-modules-typescript-loader's Issues

Support named exports

Some optimisation techniques require named exports, so styles would be imported as import * as styles from './styles.css';.

By adding a flag/option to during webpack, we can change the output.

Use with CRA without ejecting?

Hello,
This project looks useful, thanks for making this.

I am required to use CRA on a project & considering ways to add css-modules-typescript-loader. CRA evidently supports CSS Modules and TypeScript separately, but not together. At least, not today.

It's been a little while since I faced this problem of CRA deliberately not supporting custom webpack configs by design. In the absence of any knowledge on 2019 practices, my inclination would be to fork react-scripts and add this project as a dep.
While I can do this, I'm wondering if there might be a cleaner way to handle this?

Cannot access directly the style from style object

After upgrading to 4.0.0 I have some issue with access to style object.

  • Previous version: 3.0.1
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  'container': string;
  'large': string;
  'medium': string;
  'small': string;
}
declare const cssExports: CssExports;
export = cssExports;

usage
console.log(style.container);

  • Version: 4.0.0
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  'container': string;
  'large': string;
  'medium': string;
  'small': string;
}
export const cssExports: CssExports;
export default cssExports;

usage
console.log(style.default.container);

Now I have to add one more key .default to access to the style object

Compatibility with modular-css

Since 2.0.2 the loader is not compatible with https://github.com/tivac/modular-css anymore. Maybe we could make that feature optional (default to true, so it wouldn't be a breaking change)?

I wonder if the compatibility was intentional or rather accidental and how big the overhead might be if we want to do it right. I'm thinking mostly about testing. Maybe a better solution is forking this project (ex. as modular-css-typescript-loader)? What do you think?

Question: use generated TypeScript declaration in my React component.

Hi, I'm a Typescript newbie and I have a question not strictly related to this project: I used css-modules-typescript-loader to generate TypeScript declarations:

I have got this structure:

src/Components/MyComponent
├── MyComponent.module.css        // Css modules styles
├── MyComponent.module.css.d.ts   // Autogenerated style type declarations
└── MyComponent.tsx               // The React component

Inside MyComponent.tsx I have a function myFunc passing as a parameter styles object, in this way:

import React from 'react'
import styles from './MyComponent.module.css'

const myFunc = (styles, otherParam) => {...}

const MyComponent: React.FunctionComponent<ButtonProps> = (props) => {...}

Is it possible to import Interface from MyComponent.module.css.d.ts adding types like:

import React from 'react'
import styles from './MyComponent.module.css'

const myFunc = (styles: CssModuleInterface, otherParam: string): string => {...}

const MyComponent: React.FunctionComponent<ButtonProps> = (props) => {...}

How can I do it?

Generate app.d.css.ts instead of app.css.d.ts

TypeScript now supports an allowArbitraryExtensions setting, which allows it to properly find definitions for app.css in app.d.css.ts. From their docs:

Note that historically, a similar effect has often been achievable by adding a declaration file named app.css.d.ts instead of app.d.css.ts - however, this just worked through Node’s require resolution rules for CommonJS. Strictly speaking, the former is interpreted as a declaration file for a JavaScript file named app.css.js. Because relative files imports need to include extensions in Node’s ESM support, TypeScript would error on our example in an ESM file under --moduleResolution node16 or nodenext.

Doesn't export fields with css-loader ^4.0.0

We are using css-modules-typescript-loader to generate our types with CSS modules, css-loader and sass.

I'm seeing an issue in the generated typescript files from css-modules-typescript-loader. When we update to css-loader version 4, the exports have no fields in CssExports.

This works fine with css-loader version 3.6.0 but issue appears when updating to css-loader version 4.

Don't create typings files for non-named imports

I'm using Semantic UI React along with their Semantic UI CSS styles.

App.tsx:

import 'semantic-ui-css/semantic.min.css';
import './App.css'
import MyComponent from './components';

export const App = () => <MyComponent />

export default App;

In the code above, both CSS imports cause a <filename>.css.d.ts file to be created. But this is problematic for the semantic.min.css file because it's not my file. It's one provided by a NPM library. And I'm using a mono-repo with Yarn and PnPJS, so there is no node_modules folder, but a cache of ZIP files with a require.resolve hook/interceptor to provide module resolution for node--i.e. the "node_modules" filesystem is "read-only". And so this loader fails:

D:/src/git/my-project/.yarn/cache/semantic-ui-css-npm-2.4.1-488d289b03-51eb2d50e7.zip/node_modules/semantic-ui-css/semantic.min.css (D:/src/git/my-project/.yarn/$$virtual/css-loader-virtual-73afbc880bb/0/cache/css-loader-npm-4.3.0-33464197c9-1f441ac567.zip/node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!D:/src/git/my-project/.yarn/cache/css-modules-typescript-loader-npm-4.0.1-393a7cad32-5af2583d4a.zip/node_modules/css-modules-typescript-loader!D:/src/git/my-project/.yarn/cache/postcss-loader-npm-3.0.0-f4ab99b685-50b2d8892d.zip/node_modules/postcss-loader/src?postcss!D:/src/git/my-project/.yarn/cache/semantic-ui-css-npm-2.4.1-488d289b03-51e2d50e7.zip/node_modules/semantic-ui-css/semantic.min.css)
Error: EROFS: read-only filesystem, open '/node_modules/semantic-ui-css-semantic.min.css.d.ts'

Definition file generated different in development compared to production mode

I am using this library to build my webpack application and I am even using mode: verify in my CI.
this is my css module written in SASS:

.MDButton {
    &-Container {
         dispaly: flex
    }

    &-Edit {
        > span > i:nth-child(2) {
            display: none;
        }
    }

    &-Command {
        border-color: transparent;
        background: transparent;
    }
}

when I build the application in webpack development mode the output definition file is:

// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  'MDButton': string;
  'MDButton-Command': string;
  'MDButton-Container': string;
  'MDButton-Edit': string;
  'mdButton': string;
  'mdButtonCommand': string;
  'mdButtonContainer': string;
  'mdButtonEdit': string;
}
export const cssExports: CssExports;
export default cssExports;

when I build the application in webpack production mode the output is:

// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  'MDButton-Command': string;
  'MDButton-Container': string;
  'MDButton-Edit': string;
  'mdButtonCommand': string;
  'mdButtonContainer': string;
  'mdButtonEdit': string;
}
export const cssExports: CssExports;
export default cssExports;

not only that when I ran my CI process in verify mode (and in webpack production mod) I get the following error:


Module build failed (from ./node_modules/css-modules-typescript-loader/index.js):
Error: Generated type declaration file is outdated. Run webpack and commit the updated type declaration for '/Users/aguata/dev/Azure-IoT-Defender-Core-UI/src/components/button/MDButton.module.scss.d.ts'

   // This file is automatically generated.
   // Please do not change this file!
   interface CssExports {
 -   'MDButton': string;
 -   'MDButton-Command': string;
 -   'MDButton-Container': string;
 -   'MDButton-Edit': string;
 -   'mdButton': string;
 -   'mdButtonCommand': string;
 -   'mdButtonContainer': string;
 -   'mdButtonEdit': string;
 - }
 - export const cssExports: CssExports;
 +   'MDButton-Command': string;
 +   'MDButton-Container': string;
 +   'MDButton-Edit': string;
 +   'mdButtonCommand': string;
 +   'mdButtonContainer': string;
 +   'mdButtonEdit': string;
 + }
 + export const cssExports: CssExports;
 + export default cssExports;

    at getTypeMismatchError (/Users/aguata/dev/Azure-IoT-Defender-Core-UI/node_modules/css-modules-typescript-loader/index.js:19:10)
    at /Users/aguata/dev/Azure-IoT-Defender-Core-UI/node_modules/css-modules-typescript-loader/index.js:117:11
    at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:73:3)
 @ ./src/components/button/MDButton.module.scss 2:12-238 9:17-24 13:15-22
 @ ./src/components/button/MDButton.tsx
 @ ./src/components/button/index.ts
 @ ./src/index.ts
 @ multi ./src

what am I missing?

Using this alongside Cypress can cause an infinite loop due to Cypresses file watcher

The problem
Cypress is a testing tool that allows you to interactively develop your application. I've hit an issue where because this module always writes type definition files, regardless of whether it changed or not, it will trigger Cypresses file watcher and get caught in an infinite loop.

The solution
There are one of two solutions that could be put into place.

  1. This module reads/checks for an existing file and only writes the file if it's actually changed. In my opinion, this module should be reading the file first anyway to ensure that it is indeed a generated file before trying to write over it.

  2. Cypress adds the ability to ignore certain files in the watcher (unless there's a config I've missed somewhere) - Issue raised here: cypress-io/cypress#3098

I personally prefer option 1 because I feel that it'll help keep this module "just working" alongside others.
Happy to do a PR as well if this solution sounds reasonable.

P.S. Thank you so much for open-sourcing this. In a world full of half-baked open-source, you guys seem to deliver tools that solve the problem adequately and improve developer happiness, so thank you!

Suggestion: use `type` instead of `interface`

interfaces are open to extension, whereas types are not. In turn, this means that interfaces do not have index signatures (unlike types), which causes problems when you try to pass an interface into a function which expects an index signature. This is due to a design limitation: microsoft/TypeScript#15300.

For this reason I would recommend using types instead, or adding an explicit index signature to the interface, so that the CSS module type can be passed into a function expecting an index signature.

type ObjectWithStrings = { [key: string]: string }
declare const requireObjectStrings: <T extends ObjectWithStrings>(object: T) => void;

interface MyInterface {
    foo: string;
}

declare const myInterface: MyInterface;

// Error: Index signature is missing in type 'MyInterface'.
requireObjectStrings(myInterface)

type MyType = {
    foo: string;
}

declare const myType: MyType;

// No error, good :-)
requireObjectStrings(myType)

npm file inclusion

As per default, npm publish is currently including everything. This includes the tests folder, the editorconfig file, etc.

I believe it is safe to add a whitelist to the package.json- something like:

  "files": ["README.md"]

LICENSE, index.js, and package.json are always included.

Error: No exported locals found

I have a index.scss file with such CSS rules:

@font-face {
   font-family: Lato;
   src: url('font-lato/lato-bold-webfont.woff2') format('woff2')
   font-weight: 700;
}

body {
  font-family: Lato;
}

And I got this error Error: No exported locals found for C:\Users\kw\pm\src\css\index.scss at Object.module.exports (C:\Users\kw\pm\node_modules\css-modules-typescript-loader\index.js:73:19)

It works fine if the rules are just class selector .selector { color: red }.

style-loader docs is missing

As I can see behaviour of the loader is tightly coupled with style-loader webpack's plugin.

For example:

index.css

.selector { /* */ }

index.css.d.ts

// This file is automatically generated.
// Please do not change this file!
interface CssExports {
  'selector': string
}
export const cssExports: CssExports;
export default cssExports;

index.js

import styles from './index.css'

console.log(styles['selector'])

Such access is only available with style-loader enabled. But there is no reference nor in documentation itself nor in example.

Mode 'verify' and tests do not run on Windows

Under Windows (just imagine you WOULD or WOULD HAVE TO use it ;) ) the tests fail.
As well 'verify' mode does not work properly, at least if you work with the git config flag core.autocrlf=true. The problem is, that by checking out a branch or changes to CSS module type declaration files, the line endings are set from LF (what is in the repo) to CRLF. The result is, that the comparison in 'verify' between the file on disk and the generated module definition finds diffs and fails by that.

CSS Selectors using hyphens aren't compile-time checked due to square bracket access

Problem
I discovered when using this that if you write CSS selectors like this .my-selector, with a hyphen, when you import the CSS and access it with square bracket syntax, you lose compile-time checking on whether the selector / property exists or not.

.contact-details {
  color: #000;
}
.ContactDetails {
  color: #000;
}
// No compile-time error!
styles["contact-dAetails"]

// You get a compile-time error!
styles.ContactDAetails

Proposed solution(s)
a) Figure out if TypeScript has the ability to enforce checking when accessing something with square brackets.
b) If "a" is not possible, at least mention in documentation that this is a problem!

Notable TSConfig setup

  • noImplicitAny: false

Cannot find module './Component.module.scss' and unused components

If I have a component, which is not in a resulting webpack bundle, then css-modules-typescript-loader does not produce corresponding .scss.d.ts which is correct, since the .scss is not running through the webpack pipeline.

However, ts-loader anyway reports error for this file (Cannot find module './Component.module.scss') and breaks the webpack build. Is it possible to somehow exclude unused components from build?

Disallow or gracefully handle CSS selectors with hyphens (ie. ".main--red")

The problem
If you use hypens in your CSS selectors, it will generate a *.d.ts file that has compilation errors.
For example, the following SCSS file:

.main {
  border: 1px solid black;

  &--red {
    border-color: red;
  }
}

becomes:

// This file is automatically generated.
// Please do not change this file!
export const main: string;
export const main--red: string; // ERROR ON THIS LINE

The solution
At the very least, I think this tool should probably throw some kind of error. I'm not keen on the idea of making it transform into an identifier that doesn't map 1-1 with the original CSS selector name.

EDIT: I'd be interested to know what conventions you guys follow if it isn't BEM, ie. slight permutations on an element, such as it having an alternate color or skin.

Empty css.d.ts file

I have a bare bones "create-react-app", and I've added this module. I can see that it's creating the app.css.d.ts file, but it looks like this:

// This file is automatically generated.
// Please do not change this file!
interface CssExports {

}
export const cssExports: CssExports;
export default cssExports;

Format output using Prettier

This plugin is really useful to my project. However, I would like to format the output of the plugin using Prettier. I have tried several approaches, but I simply cannot get it to work both reliably and quickly.

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.