{"id":14990541,"url":"https://github.com/mizdra/happy-css-modules","last_synced_at":"2025-10-09T04:43:53.965Z","repository":{"id":37088393,"uuid":"500850247","full_name":"mizdra/happy-css-modules","owner":"mizdra","description":"Typed, definition jumpable CSS Modules. Moreover, easy!","archived":false,"fork":false,"pushed_at":"2025-04-29T10:51:54.000Z","size":2612,"stargazers_count":277,"open_issues_count":8,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-29T10:52:07.703Z","etag":null,"topics":["css","css-modules"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mizdra.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"mizdra"}},"created_at":"2022-06-07T13:23:44.000Z","updated_at":"2025-09-06T06:24:02.000Z","dependencies_parsed_at":"2023-01-31T19:46:09.657Z","dependency_job_id":"7593a8ff-6911-452c-8c05-db003a9c0a29","html_url":"https://github.com/mizdra/happy-css-modules","commit_stats":{"total_commits":888,"total_committers":29,"mean_commits":"30.620689655172413","dds":"0.28378378378378377","last_synced_commit":"01f855fecf37c6504f5c2e232aca9a3046c6d0d7"},"previous_names":["mizdra/enhanced-typed-css-modules"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/mizdra/happy-css-modules","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizdra%2Fhappy-css-modules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizdra%2Fhappy-css-modules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizdra%2Fhappy-css-modules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizdra%2Fhappy-css-modules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mizdra","download_url":"https://codeload.github.com/mizdra/happy-css-modules/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mizdra%2Fhappy-css-modules/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000709,"owners_count":26082921,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-modules"],"created_at":"2024-09-24T14:20:19.547Z","updated_at":"2025-10-09T04:43:53.958Z","avatar_url":"https://github.com/mizdra.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Cover image\" src=\"./docs/cover.svg\" /\u003e\n\u003c/p\u003e\n\n\u003ch2 align=\"center\"\u003eHappy CSS Modules\u003c/h2\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eTyped, definition jumpable CSS Modules.\u003c/em\u003e\n  \u003cbr /\u003e\n  \u003cem\u003eMoreover, easy!\u003c/em\u003e\n\u003c/p\u003e\n\nhttps://user-images.githubusercontent.com/9639995/189538880-872ad38d-2c9d-4c19-b257-521018963eec.mov\n\n## NOTICE: Migration to css-modules-kit\n\nConsider migrating to [`mizdra/css-modules-kit`](https://github.com/mizdra/css-modules-kit), which is the successor to happy-css-modules. It offers enhanced features such as Renaming and Find All References support.\n\nPlease note that `css-modules-kit` **does not support** Sass and Less. There are no plans to add support in the future. If you depend on these preprocessors, continue using `happy-css-modules`.\n\nSee the [`css-modules-kit` repository](https://github.com/mizdra/css-modules-kit).\n\n## Features\n\n- :white_check_mark: Strict type checking\n  - Generate `.d.ts` of CSS Modules for type checking\n- :mag: Definition jumps\n  - Clicking on a property on `.jsx`/`.tsx` will jump to the source of the definition on `.module.css`.\n  - This is accomplished by generating `.d.ts.map` (a.k.a. [Declaration Map](https://www.typescriptlang.org/tsconfig#declarationMap)).\n- :handshake: High compatibility with the ecosystem\n  - Support for Postcss/Sass/Less\n  - Implement webpack-compatible resolving algorithms\n  - Also supports [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias)\n- :beginner: Easy to use\n  - No configuration file, some simple CLI options\n\n## Installation\n\n```console\n$ npm i -D happy-css-modules\n```\n\n## Usage\n\nIn the simple case, everything goes well with the following!\n\n```console\n$ hcm 'src/**/*.module.{css,scss,less}'\n```\n\nIf you want to customize the behavior, see `--help`.\n\n```console\n$ hcm --help\nGenerate .d.ts and .d.ts.map for CSS modules.\n\nhcm [options] \u003cglob\u003e\n\nOptions:\n  -w, --watch                Watch input directory's css files or pattern                                         [boolean] [default: false]\n      --localsConvention     Style of exported class names.                  [choices: \"camelCase\", \"camelCaseOnly\", \"dashes\", \"dashesOnly\"]\n      --declarationMap       Create sourcemaps for d.ts files                                                      [boolean] [default: true]\n      --sassLoadPaths        The option compatible with sass's `--load-path`.                                                        [array]\n      --lessIncludePaths     The option compatible with less's `--include-path`.                                                     [array]\n      --webpackResolveAlias  The option compatible with webpack's `resolve.alias`.                                                  [string]\n      --postcssConfig        The option compatible with postcss's `--config`.                                                       [string]\n      --arbitraryExtensions  Generate `.d.css.ts` instead of `.css.d.ts`.                                          [boolean] [default: true]\n      --cache                Only generate .d.ts and .d.ts.map for changed files.                                  [boolean] [default: true]\n      --cacheStrategy        Strategy for the cache to use for detecting changed files.[choices: \"content\", \"metadata\"] [default: \"content\"]\n      --logLevel             What level of logs to report.                            [choices: \"debug\", \"info\", \"silent\"] [default: \"info\"]\n  -o, --outDir               Output directory for generated files.                                                                  [string]\n  -h, --help                 Show help                                                                                             [boolean]\n  -v, --version              Show version number                                                                                   [boolean]\n\nExamples:\n  hcm 'src/**/*.module.css'                                       Generate .d.ts and .d.ts.map.\n  hcm 'src/**/*.module.{css,scss,less}'                           Also generate files for sass and less.\n  hcm 'src/**/*.module.css' --watch                               Watch for changes and generate .d.ts and .d.ts.map.\n  hcm 'src/**/*.module.css' --declarationMap=false                Generate .d.ts only.\n  hcm 'src/**/*.module.css' --sassLoadPaths=src/style             Run with sass's `--load-path`.\n  hcm 'src/**/*.module.css' --lessIncludePaths=src/style          Run with less's `--include-path`.\n  hcm 'src/**/*.module.css' --webpackResolveAlias='{\"@\": \"src\"}'  Run with webpack's `resolve.alias`.\n  hcm 'src/**/*.module.css' --cache=false                         Disable cache.\n```\n\n## How docs definition jumps work?\n\nIn addition to `.module.css.d.ts`, happy-css-modules also generates a `.module.css.d.ts.map` file (a.k.a. [Declaration Map](https://www.typescriptlang.org/tsconfig#declarationMap)). This file is a Source Map that contains code mapping information from generated (`.module.css.d.ts`) to source (`.module.css`).\n\nWhen tsserver (TypeScript Language Server for VSCode) tries to jump to the code on `.module.css.d.ts`, it restores the original location from this Source Map and redirects to the code on` .module.css`. happy-css-modules uses this mechanism to realize definition jump.\n\n![Illustration of how definition jump works](docs/how-does-definition-jumps-work/basic/flow.drawio.svg)\n\nThe case of multiple definitions is a bit more complicated. This is because the Source Map specification does not allow for a 1:N mapping of the generated:original locations. Therefore, happy-css-modules define multiple definitions of the same property type and map each property to a different location in `.module.css`.\n\n![Illustration of a case with multiple definitions](docs/how-does-definition-jumps-work/multiple-definitions/flow.drawio.svg)\n\n## How to use `--outDir` option\n\nUse `--outDir` to output `.module.css.d.ts` and `.module.css.d.ts.map` in a separate directory. This is useful for keeping the `src/` directory clean.\n\nHowever, by default tsc and tsserver cannot load it. To enable tsc or tsserver to load them, use the [`rootDirs`](https://www.typescriptlang.org/tsconfig/#rootDirs) option in `tsconfig.json`. An example is given below.\n\n```json\n// package.json\n{\n  \"scripts\": {\n    \"gen\": \"hcm -o generated/hcm 'src/**/*.module.css'\"\n  }\n}\n```\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"rootDirs\": [\"src\", \"generated/hcm/src\"]\n  }\n}\n```\n\n## Node.js API (Experimental)\n\n\u003e **Warning**\n\u003e This feature is experimental and may change significantly. The API is not stable and may have breaking changes even in minor or patch version updates.\n\n`happy-css-modules` provides Node.js API for programmatically generating .d.ts and .d.ts.map.\n\nSee [packages/happy-css-modules/src/index.ts](https://github.com/mizdra/happy-css-modules/blob/main/packages/happy-css-modules/src/index.ts) for available API.\n\n### Example: Custom `hcm` commands\n\nYou can create your own customized `hcm` commands. We also provide a `parseArgv` utility that parses `process.argv` and extracts options.\n\n```javascript\n#!/usr/bin/env ts-node\n// scripts/hcm.ts\n\nimport { run, parseArgv } from 'happy-css-modules';\n\n// Write your code here...\n\nrun({\n  // Inherit default CLI options (e.g. --watch).\n  ...parseArgv(process.argv),\n  // Add custom CLI options.\n  cwd: __dirname,\n}).catch((e) =\u003e {\n  console.error(e);\n  process.exit(1);\n});\n```\n\n### Example: Custom transformer\n\nWith the `transformer` option, you can use AltCSS, which is not supported by `happy-css-modules`.\n\n```typescript\n#!/usr/bin/env ts-node\n\nimport { run, parseArgv, createDefaultTransformer, type Transformer } from 'happy-css-modules';\nimport sass from 'sass';\nimport { promisify } from 'util';\n\nconst defaultTransformer = createDefaultTransformer();\nconst render = promisify(sass.render);\n\n// The custom transformer supporting sass indented syntax\nconst transformer: Transformer = async (source, options) =\u003e {\n  if (from.endsWith('.sass')) {\n    const result = await render({\n      // Use indented syntax.\n      // ref: https://sass-lang.com/documentation/syntax#the-indented-syntax\n      indentedSyntax: true,\n      data: source,\n      file: options.from,\n      outFile: 'DUMMY',\n      // Output sourceMap.\n      sourceMap: true,\n      // Resolve import specifier using resolver.\n      importer: (url, prev, done) =\u003e {\n        options\n          .resolver(url, { request: prev })\n          .then((resolved) =\u003e done({ file: resolved }))\n          .catch((e) =\u003e done(e));\n      },\n    });\n    return { css: result.css, map: result.sourceMap!, dependencies: result.loadedUrls };\n  }\n  // Fallback to default transformer.\n  return await defaultTransformer(source, from);\n};\n\nrun({ ...parseArgv(process.argv), transformer }).catch((e) =\u003e {\n  console.error(e);\n  process.exit(1);\n});\n```\n\n### Example: Custom resolver\n\nWith the `resolver` option, you can customize the resolution algorithm for import specifier (such as `@import \"specifier\"`).\n\n```typescript\n#!/usr/bin/env ts-node\n\nimport { run, parseArgv, createDefaultResolver, type Resolver } from 'happy-css-modules';\nimport { exists } from 'fs/promises';\nimport { resolve, join } from 'path';\n\nconst cwd = process.cwd();\nconst runnerOptions = parseArgv(process.argv);\nconst { sassLoadPaths, lessIncludePaths, webpackResolveAlias } = runnerOptions;\n// Some runner options must be passed to the default resolver.\nconst defaultResolver = createDefaultResolver({ cwd, sassLoadPaths, lessIncludePaths, webpackResolveAlias });\nconst stylesDir = resolve(__dirname, 'src/styles');\n\nconst resolver: Resolver = async (specifier, options) =\u003e {\n  // If the default resolver cannot resolve, fallback to a customized resolve algorithm.\n  const resolvedByDefaultResolver = await defaultResolver(specifier, options);\n  if (resolvedByDefaultResolver === false) {\n    // Search for files in `src/styles` directory.\n    const path = join(stylesDir, specifier);\n    if (await exists(path)) return path;\n  }\n  // Returns `false` if specifier cannot be resolved.\n  return false;\n};\n\nrun({ ...runnerOptions, resolver, cwd }).catch((e) =\u003e {\n  console.error(e);\n  process.exit(1);\n});\n```\n\n### Example: Get locations for selectors exported by CSS Modules\n\n`Locator` can be used to get location for selectors exported by CSS Modules.\n\n```typescript\nimport { Locator } from 'happy-css-modules';\nimport { resolve } from 'path';\nimport assert from 'assert';\n\nconst locator = new Locator({\n  // You can customize the transformer and resolver used by the locator.\n  // transformer: createDefaultTransformer(),\n  // resolver: createDefaultResolver(),\n});\n\n// Process https://github.com/mizdra/happy-css-modules/blob/main/packages/example/02-import/2.css\nconst filePath = resolve('example/02-import/2.css'); // Convert to absolute path\nconst result = await locator.load(filePath);\n\nassert.deepEqual(result, {\n  dependencies: ['/Users/mizdra/src/github.com/mizdra/packages/example/02-import/3.css'],\n  tokens: [\n    {\n      name: 'b',\n      originalLocation: {\n        filePath: '/Users/mizdra/src/github.com/mizdra/packages/example/02-import/3.css',\n        start: { line: 1, column: 1 },\n        end: { line: 1, column: 2 },\n      },\n    },\n    {\n      name: 'a',\n      originalLocation: {\n        filePath: '/Users/mizdra/src/github.com/mizdra/packages/example/02-import/2.css',\n        start: { line: 3, column: 1 },\n        end: { line: 3, column: 2 },\n      },\n    },\n  ],\n});\n```\n\n## About the origins of this project\n\nThis project was born as a PoC for [Quramy/typed-css-modules#177](https://github.com/Quramy/typed-css-modules/issues/177). That is why this project forks [`Quramy/typed-css-modules`](https://github.com/Quramy/typed-css-modules). Due to refactoring, only a small amount of code now comes from `Quramy/typed-css-modules`, but its contributions can still be found in the credits of the license.\n\nThank you [@Quramy](https://github.com/Quramy)!\n\n## Prior art\n\nThere is a lot of excellent prior art.\n\n- ✅ Supported\n- 🔶 Partially supported\n- 🛑 Not supported\n- ❓ Unknown\n\n| Repository                                                                                        | Strict type checking | Definition jumps | Sass | Less | `resolve.alias` |              How implemented              |\n| :------------------------------------------------------------------------------------------------ | :------------------: | :--------------: | :--: | :--: | :-------------: | :---------------------------------------: |\n| [Quramy/typed-css-modules](https://github.com/Quramy/typed-css-modules)                           |          ✅          |        🛑        |  🛑  |  🛑  |       🛑        |                 CLI Tool                  |\n| [skovy/typed-scss-modules](https://github.com/skovy/typed-scss-modules)                           |          ✅          |        🛑        |  ✅  |  🛑  |       🛑        |                 CLI Tool                  |\n| [qiniu/typed-less-modules](https://github.com/qiniu/typed-less-modules)                           |          ✅          |        🛑        |  🛑  |  ✅  |       🛑        |                 CLI Tool                  |\n| [mrmckeb/typescript-plugin-css-modules](https://github.com/mrmckeb/typescript-plugin-css-modules) |   🔶\u003csup\u003e\\*1\u003c/sup\u003e   | 🔶\u003csup\u003e\\*2\u003c/sup\u003e |  ✅  |  ✅  |       🛑        | TypeScript Language Service\u003csup\u003e\\*3\u003c/sup\u003e |\n| [clinyong/vscode-css-modules](https://github.com/clinyong/vscode-css-modules)                     |          🛑          |        ✅        |  ✅  |  ✅  |       🛑        |             VSCode Extension              |\n| [Viijay-Kr/react-ts-css](https://github.com/Viijay-Kr/react-ts-css)                               |   🔶\u003csup\u003e\\*1\u003c/sup\u003e   |        ✅        |  ✅  |  ✅  |       ❓        |             VSCode Extension              |\n| [mizdra/happy-css-modules](https://github.com/mizdra/happy-css-modules)                           |          ✅          |        ✅        |  ✅  |  ✅  |       ✅        |        CLI Tool + Declaration Map         |\n\n- \\*1: Warnings are displayed in the editor, but not at compile time.\n- \\*2: Not supported for `.less` definition jumps.\n- \\*3: The TypeScript language service can display warnings in the editor, but not at compile time. It is also complicated to set up.\n\nAnother known tool for generating `.css.d.ts` is [wix/stylable](https://github.com/wix/stylable) , which does not use CSS Modules.\n","funding_links":["https://github.com/sponsors/mizdra"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmizdra%2Fhappy-css-modules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmizdra%2Fhappy-css-modules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmizdra%2Fhappy-css-modules/lists"}