Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cli): declarative build configuration #2633

Open
wants to merge 4 commits into
base: next
Choose a base branch
from

Conversation

unekinn
Copy link
Contributor

@unekinn unekinn commented Oct 17, 2024

This started as a refactor allowing this

const semanticThemes = R.filter((val) => val.mode === 'light' && val.typography === 'primary', themes)

to become this

const semanticThemes = getThemesFor('semantic');

The main reason for doing this was that it requires no changes to the filters when adding new modes, while the code as it is before this commit would require every R.filter(...) statement to be updated to ignore irrelevant modes -- or suffer exponential increase in build time.

Declarative config

However, after some issues with moving the definition of getThemesFor out of function buildTokens(...) I started refactoring the build script more generally. This ended up with a declarative config defined in build.ts, which looks like this

/*
 * Declarative configuration of the build output
 */
const buildConfigs = {
  typography: { sdConfig: 'typographyVariables', dimensions: ['typography'] },
  'color-mode': { sdConfig: 'colorModeVariables', dimensions: ['mode'] },
  semantic: { sdConfig: 'semanticVariables', dimensions: ['semantic'] },
  storefront: {
    name: 'Storefront preview tokens',
    sdConfig: 'typescriptTokens',
    dimensions: ['mode'],
    options: { outPath: path.resolve('../../apps/storefront/tokens') },
  },
  entryFiles: {
    name: 'CSS entry files',
    sdConfig: 'semanticVariables',
    dimensions: ['semantic'],
    build: async (sdConfigs, { outPath }) => {
      await Promise.all(
        sdConfigs.map(async ({ theme }) => {
          console.log(`👷 ${theme}.css`);

          return makeEntryFile({ theme, outPath, buildPath: path.resolve(`${outPath}/${theme}`) });
        }),
      );
    },
  },
} satisfies Record<string, BuildConfig>;

Note the BuildConfig type definition, which is

export type BuildConfig = {
  /** Optional name of the build config - only used in the console output */
  name?: string;
  /** Style Dictionary configuration id */
  sdConfig: StyleDictionaryConfigs;
  /** Which theme dimensions to include. `theme` (e.g. digdir/altinn) is always included. */
  dimensions: ThemeDimension[];
  /** Custom StyleDictionary options. If not supplied, the default is used */
  options?: Partial<GetSdConfigOptions>;
  /** Custom build function. If not supplied, the default is used. */
  build?: (sdConfigs: SDConfigForThemePermutation[], options: GetSdConfigOptions) => Promise<void>;
};

The resulting css files from the build command remain unchanged.

Copy link

changeset-bot bot commented Oct 17, 2024

⚠️ No Changeset found

Latest commit: 2792ebd

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

Copy link
Contributor

github-actions bot commented Oct 17, 2024

Preview deployments for this pull request:

Storybook - 23. Oct 2024 - 12:28

Copy link
Contributor

github-actions bot commented Oct 17, 2024

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 57.87% 3636 / 6282
🔵 Statements 57.87% 3636 / 6282
🔵 Functions 83.91% 167 / 199
🔵 Branches 75.51% 515 / 682
File CoverageNo changed files found.
Generated in workflow #518 for commit 2792ebd by the Vitest Coverage Report Action

@unekinn
Copy link
Contributor Author

unekinn commented Oct 17, 2024

Example run to demonstrate the verbose logging:
image
(...more output below...)

@unekinn unekinn force-pushed the refactor/simpler-theme-config-filtering branch from b4a096e to d4fb0de Compare October 18, 2024 11:41
@mimarz mimarz force-pushed the add-new-border-radius-tokens branch from 46a902c to b79da12 Compare October 22, 2024 11:04
Base automatically changed from add-new-border-radius-tokens to next October 22, 2024 12:38
@unekinn unekinn force-pushed the refactor/simpler-theme-config-filtering branch from 5f2c88e to 49e0e58 Compare October 22, 2024 14:38
@unekinn unekinn marked this pull request as ready for review October 22, 2024 14:44
@unekinn unekinn changed the title refactor(cli): add and use getThemesFor(dimensions) in build command refactor(cli): declarative build configuration Oct 22, 2024
}
const result: ProcessedThemeObject = { ...theme, [processed]: true };
if (result.group) {
result.group = camelCase(result.group);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suggest we use the same casing for name and group on our themes so its more predictable to use in code. Ideally lowerCase, but maybe noCase since we might have group names with spaces?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What does noCase actually do? I like camelCase since it is quite predictable:

import camelCase from 'camelcase';

camelCase('foo-bar');
//=> 'fooBar'

camelCase('foo_bar');
//=> 'fooBar'

camelCase('Foo-Bar');
//=> 'fooBar'

camelCase('--foo.bar');
//=> 'fooBar'

camelCase('foo bar');
//=> 'fooBar'

camelCase('Foo bar');
//=> 'fooBar'

camelCase(['__foo__', '--bar']);
//=> 'fooBar'

Copy link
Collaborator

Choose a reason for hiding this comment

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

noCase is just kebab-case with some additional parsing for replacing spaces. Maybe we should rename it then?

Its something thats been there since before I started and we still had some design-tokens we needed it for.

I think whats most important is we use the same case conversion for our fields for further use. Since we've already started using kebab-case for folders in design-tokens and names I feel its maybe the best fit?

const semanticThemes = R.filter((val) => val.mode === 'light' && val.typography === 'primary', themes);
// We only use the 'default' theme for the 'size' group
const relevant$themes = $themes.filter((theme) =>
R.not(theme.group === 'size' && theme.name.toLowerCase() !== 'default'),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Fix for potential pitfall filtering themeobjects from $themes

Suggested change
R.not(theme.group === 'size' && theme.name.toLowerCase() !== 'default'),
R.not(theme.group.toLowerCase() === 'size' && theme.name.toLowerCase() !== 'default'),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is unneccessary because the names are normalised in processTheme. Actually I should have removed the other toLowerCase() as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

But these names are not normalised in this filter since its dones before processTheme. Its done straight on the raw ThemeObjects from $themes.json

Looking at it now this filter does not work for our own design-tokens as group is uppercase Size.

Removing it does not alter the build output either so suggest just removing as it seems redundant now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is true! I guess I didn't complete my thought process, I was planning to move processTheme earlier in the "pipeline".

It works right now because the permutation with size: "default" comes before size: "compact", due to the order they are defined in $themes.json. Since none of the build configurations depend on the size dimension, the first one is always used.

This automates the config filtering which so far has been done manually, e.g. this
```ts
const semanticThemes = R.filter((val) => val.mode === 'light' && val.typography === 'primary', themes)
```

is now this
```
const semanticThemes = getThemesFor('semantic');
```

The main reason for doing this is that it requires no changes to the filters when adding new modes, while the code as it is before this commit would require every `R.filter(...)` statement to be updated to ignore irrelevant modes -- or suffer exponential increase in build time.

The resulting css files from the build command remain unchanged.
@unekinn unekinn force-pushed the refactor/simpler-theme-config-filtering branch from 49e0e58 to 2792ebd Compare October 23, 2024 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants