mizdra / happy-css-modules

Typed, definition jumpable CSS Modules. Moreover, easy!
MIT License
213 stars 5 forks source link

Webpack resolve alias not working #200

Closed samhh closed 1 year ago

samhh commented 1 year ago

Hi there! I'm struggling to get --webpackResolveAlias working. I've created a small repro repo here: https://git.sr.ht/~samhh/hcm-repro

What I've observed:

  1. No Webpack alias configuration I've tried works. I've left in the repo what seems the most obvious/simple.
  2. There doesn't appear to be any way from the consumer's perspective to debug why an alias isn't working. With logLevel=debug it'd be convenient to know at least if the alias had hit a match.
  3. Files are generated even if there are errors for imports.
  4. Once files are generated you'll then stop getting errors. This appears to be merely a symptom of the above issue as a result of caching.

For what it's worth the real repo currently successfully uses typed-css-modules... but we'd really like declaration maps :wink:

Cheers!

mizdra commented 1 year ago

@samhh Thanks for the report!

First of all, I think this error message is harmless and can be ignored (more on that later). The error message is occurring during the processing of foo.css, but the generation of the type definition in foo.css is successful.

The reason why the bar.css type definition is not generated is not because an error occurred during the processing of bar.css, but because bar.css was not processed in the first place. Rewrite the command as follows

-hcm --logLevel=debug --webpackResolveAlias='{ \"bar\": \"bar.css\" }' foo.css
+hcm --logLevel=debug --webpackResolveAlias='{ \"bar\": \"bar.css\" }' '*.css'

I think this will solve your problem. Please try it!


In the first place, @value something from "bar"; should not be resolved. With or without this statement, the only selector exported from foo.css is .foo. .bar is not exported from foo.css, because @value <imported-value-name> from '<specifier>'; is an import statement for @value. It does not have the ability to import the selector and expand it to the import destination.

I had intended happy-css-modules to also implement @value <imported-value-name> from '<specifier>'; so that it would not resolve. However, it was being resolved by mistake, causing the aforementioned error. I can generate the type definitions without any problem, just an error message.

However, it is noisy and I will fix this error message problem at a later date.

mizdra commented 1 year ago

This bug is fixed by v2.1.1.

samhh commented 1 year ago

Hey, thank you! Some more observations:

Supplying a bad PostCSS config path offers no feedback, so there's no way to know if it's failing to apply because of a bad path or otherwise. I've pushed this to the above repo.

TCM was happy to type @value foo: val; as if foo were a class name as Webpack's css-loader gives us val instead of a class name. How could one get that behaviour back in HCM?

samhh commented 1 year ago

A little more information on @value.

TCM uses postcss-modules-loader-core, which contrary to its README includes postcss-modules-values.

I've tried replicating that same config - and I know it's loading with a console.log in the PostCSS config - to no avail.

samhh commented 1 year ago

Some progress. The following enables HCM to at least parse @value non-import statements.

patch ```patch From 63cb9b3d1189c03d5998cb10194ec4d77cb90585 Mon Sep 17 00:00:00 2001 From: "Sam A. Horvath-Hunt" Date: Wed, 10 May 2023 20:02:57 +0100 Subject: [PATCH] Partially support CSS Modules @value This doesn't implement location mapping. And it's kinda hacky. --- packages/happy-css-modules/src/locator/index.ts | 13 ++++++++++++- .../happy-css-modules/src/locator/postcss.test.ts | 2 ++ packages/happy-css-modules/src/locator/postcss.ts | 12 ++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/happy-css-modules/src/locator/index.ts b/packages/happy-css-modules/src/locator/index.ts index 3dd0d44..b4709cb 100644 --- a/packages/happy-css-modules/src/locator/index.ts +++ b/packages/happy-css-modules/src/locator/index.ts @@ -164,7 +164,7 @@ export class Locator { const tokens: Token[] = []; - const { atImports, classSelectors, composesDeclarations } = collectNodes(ast); + const { atImports, atValues, classSelectors, composesDeclarations } = collectNodes(ast); // Load imported sheets recursively. for (const atImport of atImports) { @@ -178,6 +178,17 @@ export class Locator { tokens.push(...externalTokens); } + for (const atValue of atValues) { + const name = atValue.params.slice(0, atValue.params.indexOf(':')); + if (!localTokenNames.includes(name)) continue; + + tokens.push({ + name, + // TODO: `getOriginalLocation` expects a `ClassName`. + originalLocations: [], + }); + } + // Traverse the source file to find a class selector that matches the local token. for (const { rule, classSelector } of classSelectors) { // Consider a class selector to be the origin of a token if it matches a token fetched by postcss-modules. diff --git a/packages/happy-css-modules/src/locator/postcss.test.ts b/packages/happy-css-modules/src/locator/postcss.test.ts index c97df15..959985d 100644 --- a/packages/happy-css-modules/src/locator/postcss.test.ts +++ b/packages/happy-css-modules/src/locator/postcss.test.ts @@ -43,9 +43,11 @@ describe('generateLocalTokenNames', () => { .composes { composes: composes_target; } + @value atvalue: foo; `), ), ).toStrictEqual([ + 'atvalue', 'basic', 'cascading', 'pseudo_class_1', diff --git a/packages/happy-css-modules/src/locator/postcss.ts b/packages/happy-css-modules/src/locator/postcss.ts index 11f8ef7..a47c39d 100644 --- a/packages/happy-css-modules/src/locator/postcss.ts +++ b/packages/happy-css-modules/src/locator/postcss.ts @@ -31,7 +31,7 @@ function removeDependenciesPlugin(): Plugin { postcssPlugin: 'remove-dependencies', // eslint-disable-next-line @typescript-eslint/naming-convention AtRule(atRule) { - if (isAtImportNode(atRule) || isAtValueNode(atRule)) { + if (isAtImportNode(atRule) || isAtValueImport(atRule)) { atRule.remove(); } }, @@ -139,6 +139,10 @@ function isAtValueNode(node: Node): node is AtRule { return isAtRuleNode(node) && node.name === 'value'; } +function isAtValueImport(node: Node): node is AtRule { + return isAtValueNode(node) && !node.params.includes(':'); +} + function isRuleNode(node: Node): node is Rule { return node.type === 'rule'; } @@ -153,6 +157,7 @@ function isComposesDeclaration(node: Node): node is Declaration { type CollectNodesResult = { atImports: AtRule[]; + atValues: AtRule[]; classSelectors: { rule: Rule; classSelector: ClassName }[]; composesDeclarations: Declaration[]; }; @@ -163,11 +168,14 @@ type CollectNodesResult = { */ export function collectNodes(ast: Root): CollectNodesResult { const atImports: AtRule[] = []; + const atValues: AtRule[] = []; const classSelectors: { rule: Rule; classSelector: ClassName }[] = []; const composesDeclarations: Declaration[] = []; ast.walk((node) => { if (isAtImportNode(node)) { atImports.push(node); + } else if (isAtValueNode(node)) { + atValues.push(node); } else if (isRuleNode(node)) { // In `rule.selector` comes the following string: // 1. ".foo" @@ -187,7 +195,7 @@ export function collectNodes(ast: Root): CollectNodesResult { composesDeclarations.push(node); } }); - return { atImports, classSelectors, composesDeclarations }; + return { atImports, atValues, classSelectors, composesDeclarations }; } /** -- 2.40.0 ```

Alternatively one of the PostCSS plugins TCM implicitly uses causes it to be transformed into an :export block.