CSS Modules Kit is a toolkit that makes CSS Modules more convenient. It uses the TypeScript Language Service Plugin and Volar.js to provide rich editor language features for CSS Modules (Go to Definition, Rename, Find All References, etc.).
The provided language features and available settings are described in README.md.
This project is a monorepo and consists of the following packages:
- packages/core: Common package that provides core functionality such as parsing CSS Modules, generating type definitions, and validation
- packages/ts-plugin: Implementation of the TypeScript Language Service Plugin (Volar.js-based)
- Provides language features such as Go to Definition, Rename, Find All References
- packages/codegen: CLI tool to generate CSS Modules type definition files (
.d.ts) - packages/vscode: VS Code extension
- Wrapper to run ts-plugin in VS Code
- packages/stylelint-plugin: Stylelint plugin
- packages/eslint-plugin: ESLint plugin
- crates/zed: Zed editor extension (Rust implementation)
- Wrapper to run ts-plugin in Zed
Package dependencies:
- ts-plugin, codegen, stylelint-plugin, and eslint-plugin all depend on the core package
- Functionality is implemented via APIs provided by the core package (
parseCSSModule,generateDts,checkCSSModule, etc.)
vp run build # Build all packages
vp check # Run format, lint, and type checks
vp check --fix # Auto-fix format and lint issues
vp test # Run all tests except VS Code extension tests
vp test --project unit # Run only unit tests
vp test --project e2e # Run only E2E tests
vp test packages/core/src/parser/css-module-parser.test.ts # Run a specific test file
vp run vscode-test # Run VS Code extension testsThe examples/ directory is used for manual verification of ts-plugin language features in an editor. When adding a new language feature, add a minimal usage example in examples/.
The examples directory also contains generated type definition files produced by codegen. If the generated type definition files change, these files must also be updated. You can update them with the following command:
vp run update-generated-in-examplesAfter updating, commit the example changes separately with chore(examples): ....
- Write no comments that restate what the code already says. Let the code speak.
- Exception: a comment is warranted for hidden constraints, surprising behavior, or workarounds that the code alone cannot communicate.
Defining error classes:
- Use classes that extend
Errorfor errors to be thrown - Place error classes in
packages/*/src/error.ts
// packages/*/src/error.ts
class AuthError extends Error {
constructor(message: string) {
super(message);
this.name = 'AuthError';
}
}Using @throws:
- For functions that throw, describe exceptions with JSDoc
@throws - Propagate
@throwsto calling functions (when not caught by try)
/**
* @throws {AuthError} When the user is not authorized
*/
function myFunction() {
if (!user.isAuthorized()) {
throw new AuthError('User is not authorized');
}
}- Write test names as concrete verb phrases that describe the expected behavior:
inserts X into Y,omits X from Y,prioritizes X over Y. Avoid vague words likecorrect,properly,right,handle, orwork. - Do not use undefined terms in test names. If a concept isn't in the glossary or the code, describe its property directly.
- In
packages/ts-plugin/e2e-test/, use abstract concepts from the project glossary instead of CSS syntax literals:- ❌
from `@import` in CSS→ ✅from all token importer - ❌
from `@value ... from` in CSS→ ✅from named token importer
- ❌
- Keep one representative test case per distinct output structure. If two inputs produce the same abstract output shape, keep one and remove the other.
- Use
dedentonly for multiline template literals. Single-line fixtures should use a plain backtick string or a variable directly — not wrapped indedent.
- Token: A generic term for things exported by CSS Modules, such as class names,
@valuedefinitions, and@keyframesnames. - Token reference: A usage of a token elsewhere in the CSS. Currently produced for
animation-name: <name>andcomposes: <name>. There are two kinds:- Local token reference: References a token available in the current file. The token may be defined in the same file (e.g.
@keyframes) or imported via@import/@value ... from. - External token reference: References tokens exported by another file, like
composes: <name> ... from '<specifier>'. One reference corresponds to onefromclause and holds an entry per referenced token.
- Local token reference: References a token available in the current file. The token may be defined in the same file (e.g.
- Diagnostic: An object representing errors or warnings
- Parse phase: The phase that parses CSS Modules files and extracts token information
- Check phase: The phase that validates CSS Modules files
- Emit phase: The phase that generates
.d.tsfiles - cmkOptions: Various option settings for CSS Modules Kit
- Mapping: An object that maintains the positional relationship between a
.d.tsfile and its corresponding .css file- Mapping enables language features such as Go to Definition, Find All References, Rename
docs/ts-plugin-internals.md: Deep dive into the TS plugin internals- Read this when working on
packages/ts-pluginorpackages/core/src/dts-generator.ts— especially when modifying Volar.js mappings, virtual code generation, or language service features (Go to Definition, Find References, Rename, etc.)
- Read this when working on
packages/core/src/type.ts: Main type definitions used by CSS Modules Kitpackages/core/src/parser/css-module-parser.ts: CSS Modules parsing- Responsible for extracting tokens and generating diagnostics detectable during parsing
packages/core/src/checker.ts: CSS Modules validation- Responsible for generating diagnostics that cannot be detected in the parse phase
packages/core/src/dts-generator.ts: Generates type definition files (.d.ts) and mappings.
- TypeScript
- Vite+ (
vp) — unified toolchain for test, lint, fmt - pnpm, pnpm workspaces
- Changesets
- Write PR descriptions and commit messages in English
- Commit messages follow Conventional Commits
<type>is one of: feat, fix, docs, refactor, test, chore, deps[optional scope]: choose one of core, ts-plugin, codegen, vscode, stylelint-plugin, eslint-plugin, zed- For changes spanning multiple packages, use comma-separated scopes like
feat(core, ts-plugin): ...
- For changes spanning multiple packages, use comma-separated scopes like
- If you make changes that affect users, add a changeset file under
.changeset- The changeset body must be a single summary line only.
- PR descriptions should be readable without knowledge of internal implementation details. Do not include API-level jargon or notes that only make sense after reading the source.
- Assign appropriate labels when creating a PR
Type: Breaking Change: Breaking changesType: Bug: Bug fixesType: Documentation: Documentation changesType: Feature: New featuresType: Refactoring: RefactoringType: Testing: Test additions/modificationsType: Maintenance: Repository maintenanceType: CI: CI/CD changesType: Security: Security-related changesType: Dependencies: Dependency updates