Skip to content

Conversation

@apple-yagi
Copy link
Contributor

Background

At a social gathering after a certain study session held in Fukuoka the other day, the topic of css-modules-kit came up. At that time, I heard that css-modules-kit does not allow kebab-case, so I couldn't use it. I investigated whether it was not allowed, and found that it was, so I submitted a PR.

Description

I have modified it to generate type definitions using string literals, referring to #177. This allows defining classnames that are not JS identifiers.
Since namedExports can only use JS identifiers, this will only support defaultExports.

As an implementation issue, the logic has become complex because we needed to manage mappings doubly (one for definition jumps and one for renaming) (this change caused various bugs).
While I believe this feature addition is good for the further adoption of css-modules-kit, I also want to avoid making the code so complex that it becomes unmaintainable in the long run. I would like to respect mizdra's opinion on this 🙏

Related issues

#176

AI Assistance

This PR was written with assistance from Codex (ChatGPT).

@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

⚠️ No Changeset found

Latest commit: 51719ff

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@apple-yagi apple-yagi force-pushed the feat/support-non-identifier-classnames branch from 96d2d6f to 3e6cbfb Compare January 21, 2026 14:57
@mizdra
Copy link
Owner

mizdra commented Jan 22, 2026

Great work! This is a big change. I'll review it carefully. It may take some time.

@mizdra mizdra added the Type: Add Add new features. label Jan 22, 2026
@mizdra
Copy link
Owner

mizdra commented Jan 22, 2026

FYI: CSS Modules Kit has flaky tests (#301). Even if your implementation is correct, CI may fail.

@apple-yagi
Copy link
Contributor Author

Looking back at the code, I noticed there was unnecessary code and deleted it 🙏

@mizdra
Copy link
Owner

mizdra commented Jan 24, 2026

@apple-yagi I've made some changes to the main branch—could you please merge or rebase the main branch?

@mizdra
Copy link
Owner

mizdra commented Jan 24, 2026

While I believe this feature addition is good for the further adoption of css-modules-kit, I also want to avoid making the code so complex that it becomes unmaintainable in the long run. I would like to respect mizdra's opinion on this 🙏

It certainly looks like a bit of complex code. I'll think deeply about whether there's a way to replace it with simpler code.

@apple-yagi apple-yagi force-pushed the feat/support-non-identifier-classnames branch 3 times, most recently from 81f5cfb to 76ca2ba Compare January 25, 2026 12:56
@apple-yagi apple-yagi force-pushed the feat/support-non-identifier-classnames branch from 76ca2ba to 1340313 Compare January 25, 2026 12:59
@apple-yagi
Copy link
Contributor Author

@mizdra I believe all functions are now working correctly after fixing the bug in rename.
I don't think I can improve the quality of this code further, so I apologize, but I'll leave the rest to you.
Good luck! 🙇

return result;
}

export function getDefinitionAndBoundSpan(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I still don't understand what this function is trying to do. Is it doing something that contributes to supporting non-identifier classnames?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When type definitions are made into string literals, tsserver cannot return definitions (it might be a bug in some tool, but I don't know). Therefore, I am manually mapping the definitions(I had Codex write the code).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, shouldn't we investigate that bug first? If it's a dependency bug and fixable, fixing it is the best approach.

If the dependency bug is difficult to fix or would take a long time to resolve, I think implementing a workaround to bypass it is worthwhile. Otherwise, I'd prefer to avoid implementing a complex workaround.

I'll investigate this bug. At this stage, I suspect it's a bug in tsserver or Volar.js.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree.
While organizing the discussion, I thought of another implementation method, so I'll create a new Pull Request and commit step by step. When defining types with string literals, I feel that single quotes are also recognized as part of the type. That might be a specification of tsserver (I briefly read the tsserver implementation but couldn't quite understand it).
With that in mind, I'll try implementing it using tsserver.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored dts-generator.ts to avoid breaking the code (1340313...12f5b4b). Additionally, I fixed the regression (c4dbe5b). As a result, it seems adding new mappings is no longer necessary.

By the way, you explained the following in #309 (comment):

As an implementation issue, the logic has become complex because we needed to manage mappings doubly

What does "managing mappings doubly" mean? Does it mean one more element is added to the mapping array? Looking at the changes in dts-generator.ts, it doesn't appear that any additional elements are being added to the mapping array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mizdra Sorry, Initially, when I submitted the PR, I thought that both quoted and unquoted mappings were necessary for string literals like styles['a-1'], because tsserver returns spans including quotes

text += ` '`;
mapping.sourceOffsets.push(token.loc.start.offset);
const quoteStart = base + 2;
const keyStart = base + 3;
mapping.generatedOffsets.push(quoteStart);
mapping.lengths.push(token.name.length);
// Include quotes and `:` so string-literal ranges map correctly.
mapping.generatedLengths!.push(token.name.length + 3);
// Map the inner content so rename ranges without quotes still resolve.
mapping.sourceOffsets.push(token.loc.start.offset);
mapping.generatedOffsets.push(keyStart);
mapping.lengths.push(token.name.length);
mapping.generatedLengths!.push(token.name.length);
text += `${token.name}': '' as readonly string,\n`;
.
However, I realized that go-to-definition works even without double mapping because getDefinitionAndBoundSpan normalizes def.name and replaces it with the CSS location (https://github.com/apple-yagi/css-modules-kit/blob/c4dbe5b554281a3501ec0cb6544cbc62875e6ba6/packages/ts-plugin/src/language-service/feature/definition-and-bound-span.ts#L110-L132). Therefore, I removed it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, performing double mapping allows tsserver's getDefinitionAndBoundSpan to work partially correctly. For example, jumps for styles.a_1; or styles['a-1']; work correctly (this was my original idea). However, bugs occur with duplicate class names like styles.a_2, @import, @value ... from '...';, and symbol renaming, making the implementation increasingly complex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Add Add new features.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants