Skip to content

Commit

Permalink
refactoring of logic to collect local tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
mizdra committed May 19, 2024
1 parent d070e98 commit b98fd04
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 31 deletions.
10 changes: 7 additions & 3 deletions packages/happy-css-modules/src/locator/postcss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,18 @@ describe('generateLocalTokenNames', () => {
).toStrictEqual([]);
});
test('does not track styles imported by @value in other file because it is not a local token', async () => {
createFixtures({});
createFixtures({
'/test/1.css': dedent`
.a {}
`,
});
expect(
await generateLocalTokenNames(
createRoot(`
@value something from "/test/1.css";
@value a from "/test/1.css";
`),
),
).toStrictEqual([]);
).toStrictEqual(['a']);
});
test('does not track styles imported by composes in other file because it is not a local token', async () => {
createFixtures({
Expand Down
39 changes: 11 additions & 28 deletions packages/happy-css-modules/src/locator/postcss.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import postcss, { type Rule, type AtRule, type Root, type Node, type Declaration, type Plugin } from 'postcss';
import postcss, { type Rule, type AtRule, type Root, type Node, type Declaration } from 'postcss';
import modules from 'postcss-modules';
import selectorParser, { type ClassName } from 'postcss-selector-parser';
import valueParser from 'postcss-value-parser';
Expand Down Expand Up @@ -26,40 +26,27 @@ export type Location =
end: undefined;
};

function removeDependenciesPlugin(): Plugin {
return {
postcssPlugin: 'remove-dependencies',
// eslint-disable-next-line @typescript-eslint/naming-convention
AtRule(atRule) {
if (isAtImportNode(atRule) || isAtValueImport(atRule)) {
atRule.remove();
}
},
// eslint-disable-next-line @typescript-eslint/naming-convention
Declaration(declaration) {
if (isComposesDeclaration(declaration)) {
declaration.remove();
}
},
};
}

/**
* Traverses a local token from the AST and returns its name.
* @param ast The AST to traverse.
* @returns The name of the local token.
*/
export async function generateLocalTokenNames(ast: Root): Promise<string[]> {
class EmptyLoader {
async fetch(_file: string, _relativeTo: string, _depTrace: string): Promise<{ [key: string]: string }> {
// Return an empty object because we do not want to load external tokens in `generateLocalTokenNames`.
return Promise.resolve({});
}
}
return new Promise((resolve, reject) => {
postcss
.default()
// postcss-modules collects tokens (i.e., includes external tokens) by following
// the dependencies specified in the @import and composes properties.
// However, we do not want `generateLocalTokenNames` to return external tokens.
// So we remove the @import and composes properties beforehand.
.use(removeDependenciesPlugin())
.use(
modules({
// `@import`, `@value`, and `composes` can read tokens from external files.
// However, we want to collect only local tokens. So we will fake that
// an empty token is exported from the external file.
Loader: EmptyLoader,
getJSON: (_cssFileName, json) => {
resolve(Object.keys(json));
},
Expand Down Expand Up @@ -139,10 +126,6 @@ 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';
}
Expand Down

0 comments on commit b98fd04

Please sign in to comment.