Skip to content

Commit

Permalink
use our own permutateThemes
Browse files Browse the repository at this point in the history
  • Loading branch information
mimarz committed Jun 12, 2024
1 parent eeeb4d0 commit ba79a9f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 2 deletions.
5 changes: 3 additions & 2 deletions packages/cli/src/tokens/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Config, TransformedToken } from 'style-dictionary/types';
import * as R from 'ramda';
import type { ThemeObject } from '@tokens-studio/types';

import { permutateThemes as permutateThemes_ } from './permutateThemes';
import { nameKebab, typographyShorthand, sizeRem } from './transformers.js';
import { jsTokens } from './formats/js-tokens.js';
import { cssVariables } from './formats/css-variables.js';
Expand Down Expand Up @@ -56,9 +57,9 @@ const outputColorReferences = (token: TransformedToken) => {
};

export const permutateThemes = ($themes: ThemeObject[]) =>
tokenStudio.permutateThemes($themes, {
permutateThemes_($themes, {
separator,
}) as Record<string, string[]>;
});

type GetConfig = (options: {
mode?: string;
Expand Down
75 changes: 75 additions & 0 deletions packages/cli/src/tokens/permutateThemes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { ThemeObject } from '@tokens-studio/types';
import { TokenSetStatus } from '@tokens-studio/types';

declare interface Options {
separator?: string;
}

function mapThemesToSetsObject(themes: ThemeObject[]) {
return Object.fromEntries(themes.map((theme) => [theme.name, filterTokenSets(theme.selectedTokenSets)]));
}

export function permutateThemes(themes: ThemeObject[], { separator = '-' } = {} as Options): Record<string, string[]> {
if (themes.some((theme) => theme.group)) {
// Sort themes by groups
const groups: Record<string, ThemeObject[]> = {};
themes.forEach((theme) => {
if (theme.group) {
groups[theme.group] = [...(groups[theme.group] ?? []), theme];
} else {
throw new Error(
`Theme ${theme.name} does not have a group property, which is required for multi-dimensional theming.`,
);
}
});

if (Object.keys(groups).length <= 1) {
return mapThemesToSetsObject(themes);
}

// Create theme permutations
const permutations = cartesian(Object.values(groups)) as Array<ThemeObject[]>;

return Object.fromEntries(
permutations.map((perm) => {
// 1) concat the names of the theme groups to create the permutation theme name
// 2) merge the selectedTokenSets together from the different theme group parts
const reduced = perm.reduce(
(acc, curr) => [
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${acc[0]}${acc[0] ? separator : ''}${curr.name}`,
[...acc[1], ...filterTokenSets(curr.selectedTokenSets)],
],
['', [] as string[]],
);

// Dedupe the tokensets, return as entries [name, sets]
return [reduced[0], [...new Set(reduced[1])]];
}),
) as Record<string, string[]>;
} else {
return mapThemesToSetsObject(themes);
}
}

function filterTokenSets(tokensets: Record<string, TokenSetStatus>) {
return (
Object.entries(tokensets)
.filter(([, val]) => val !== TokenSetStatus.DISABLED)
// ensure source type sets are always ordered before enabled type sets
.sort((a, b) => {
if (a[1] === TokenSetStatus.SOURCE && b[1] === TokenSetStatus.ENABLED) {
return -1;
} else if (a[1] === TokenSetStatus.ENABLED && b[1] === TokenSetStatus.SOURCE) {
return 1;
}
return 0;
})
.map((entry) => entry[0])
);
}

// cartesian permutations: [[1,2], [3,4]] -> [[1,3], [1,4], [2,3], [2,4]]
function cartesian(a: Array<unknown[]>) {
return a.reduce((a, b) => a.flatMap((d) => b.map((e) => [d, e].flat())));
}

0 comments on commit ba79a9f

Please sign in to comment.