Skip to content

Commit

Permalink
Feat(variables-scss): Support for themes #DS-1436
Browse files Browse the repository at this point in the history
  • Loading branch information
curdaj committed Sep 10, 2024
1 parent 63c80b3 commit d8c587b
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 88 deletions.
20 changes: 10 additions & 10 deletions exporters/variables-scss/generated/exporter.cjs

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions exporters/variables-scss/src/config/fileConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { TokenType } from '@supernovaio/sdk-exporters';

export type FileData = {
fileName: string;
tokenTypes: TokenType[];
groupNames: string[];
withCssObject?: boolean;
hasParentPrefix?: boolean;
sortByNumValue?: boolean;
};

export const nonThemedFilesData: FileData[] = [
{
fileName: '_borders.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Border'],
withCssObject: false,
sortByNumValue: true,
},
{
fileName: '_other.scss',
tokenTypes: [TokenType.dimension, TokenType.string],
groupNames: ['Grid', 'Container', 'Breakpoint'],
},
{
fileName: '_radii.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Radius'],
hasParentPrefix: false,
sortByNumValue: true,
},
{
fileName: '_spacing.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Spacing'],
hasParentPrefix: false,
sortByNumValue: true,
},
];

export const themedFilesData: FileData[] = [
{
fileName: '_colors.scss',
tokenTypes: [TokenType.color],
groupNames: [''],
},
];
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import fs from 'fs';
import path from 'path';
import { Token, TokenGroup } from '@supernovaio/sdk-exporters';
import { generateFiles } from '../fileGenerator';
import { Supernova, Token, TokenGroup, TokenTheme } from '@supernovaio/sdk-exporters';
import { generateFiles, generateOutputFilesByThemes } from '../fileGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../formatters/__fixtures__/mockedExampleTokens';
import { nonThemedFilesData } from '../../config/fileConfig';

const mockedExpectedResult = fs.readFileSync(
path.join(__dirname, '../../formatters/__fixtures__/exampleFileContent.scss'),
Expand All @@ -13,19 +14,52 @@ const tokenGroups: Array<TokenGroup> = exampleMockedGroups;
const emptyFile = `/* This file was generated by Supernova, don't change manually */\n\n`;

describe('fileGenerator', () => {
it('should generate files', () => {
const tokens = Array.from(exampleMockedTokens.values());
const files = generateFiles(tokens, mappedTokens, tokenGroups);
describe('generateOutputFilesByThemes', () => {
it('should generate output files by themes', async () => {
const tokens = Array.from(exampleMockedTokens.values());
const sdk = {
tokens: {
computeTokensByApplyingThemes: jest.fn().mockResolvedValue(tokens),
},
};
const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
const outputFiles = await generateOutputFilesByThemes(
tokens,
mappedTokens,
tokenGroups,
themes as TokenTheme[],
sdk as unknown as Supernova,
);

expect(files).toStrictEqual([
{
fileName: '_borders.scss',
content: emptyFile,
},
{ fileName: '_other.scss', content: mockedExpectedResult },
{ fileName: '_radii.scss', content: emptyFile },
{ fileName: '_spacing.scss', content: emptyFile },
{ fileName: '_colors.scss', content: emptyFile },
]);
expect(outputFiles).toStrictEqual([
{
path: './globals/',
fileName: '_borders.scss',
content: emptyFile,
},
{ path: './globals/', fileName: '_other.scss', content: mockedExpectedResult },
{ path: './globals/', fileName: '_radii.scss', content: emptyFile },
{ path: './globals/', fileName: '_spacing.scss', content: emptyFile },
{ path: './themes/theme-light/', fileName: '_colors.scss', content: emptyFile },
{ path: './themes/theme-light-inverted/', fileName: '_colors.scss', content: emptyFile },
]);
});
});

describe('generateFiles', () => {
it('should generate files', () => {
const tokens = Array.from(exampleMockedTokens.values());
const files = generateFiles(tokens, mappedTokens, tokenGroups, nonThemedFilesData);

expect(files).toStrictEqual([
{
fileName: '_borders.scss',
content: emptyFile,
},
{ fileName: '_other.scss', content: mockedExpectedResult },
{ fileName: '_radii.scss', content: emptyFile },
{ fileName: '_spacing.scss', content: emptyFile },
]);
});
});
});
82 changes: 40 additions & 42 deletions exporters/variables-scss/src/generators/fileGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,51 @@
import { TokenGroup, Token, TokenType } from '@supernovaio/sdk-exporters';
import { TokenGroup, Token, Supernova, TokenTheme } from '@supernovaio/sdk-exporters';
import { generateFileContent } from './contentGenerator';
import { FileData, nonThemedFilesData, themedFilesData } from '../config/fileConfig';

export type FileData = {
fileName: string;
tokenTypes: TokenType[];
groupNames: string[];
withCssObject?: boolean;
hasParentPrefix?: boolean;
sortByNumValue?: boolean;
};
export const generateOutputFilesByThemes = async (
tokens: Token[],
mappedTokens: Map<string, Token>,
tokenGroups: TokenGroup[],
themes: TokenTheme[],
sdk: Supernova,
): Promise<{ path: string; fileName: string; content: string }[]> => {
const outputFiles: { path: string; fileName: string; content: string }[] = [];

// Generate global files for non-themed tokens
const globalFiles = generateFiles(tokens, mappedTokens, tokenGroups, nonThemedFilesData);
outputFiles.push(
...globalFiles.map((file) => {
return { path: './globals/', fileName: file.fileName, content: file.content };
}),
);

// Compute themed tokens for all themes in parallel
const allThemes = await Promise.all(
themes.map(async (theme) => {
const themedTokens = await sdk.tokens.computeTokensByApplyingThemes(tokens, [theme]);

const filesData: FileData[] = [
{
fileName: '_borders.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Border'],
withCssObject: false,
sortByNumValue: true,
},
{
fileName: '_other.scss',
tokenTypes: [TokenType.dimension, TokenType.string],
groupNames: ['Grid', 'Container', 'Breakpoint'],
},
{
fileName: '_radii.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Radius'],
hasParentPrefix: false,
sortByNumValue: true,
},
{
fileName: '_spacing.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Spacing'],
hasParentPrefix: false,
sortByNumValue: true,
},
{
fileName: '_colors.scss',
tokenTypes: [TokenType.color],
groupNames: [''],
},
];
return { themedTokens, theme };
}),
);

// Generate files for each theme
for (const { themedTokens, theme } of allThemes) {
const themeFiles = generateFiles(themedTokens, mappedTokens, tokenGroups, themedFilesData);
outputFiles.push(
...themeFiles.map((file) => {
return { path: `./themes/${theme.name}/`, fileName: file.fileName, content: file.content };
}),
);
}

return outputFiles;
};

export const generateFiles = (
tokens: Array<Token>,
mappedTokens: Map<string, Token>,
tokenGroups: Array<TokenGroup>,
filesData: FileData[],
) => {
return filesData.map(
({ fileName, tokenTypes, groupNames, withCssObject = true, hasParentPrefix = true, sortByNumValue = false }) => {
Expand Down
33 changes: 12 additions & 21 deletions exporters/variables-scss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
OutputTextFile,
} from '@supernovaio/sdk-exporters';
import { ExporterConfiguration } from '../config';
import { generateFiles } from './generators/fileGenerator';
import { generateOutputFilesByThemes } from './generators/fileGenerator';

// https://github.com/Supernova-Studio/exporters/issues/4
// @ts-ignore-next-line
Expand All @@ -28,20 +28,9 @@ Pulsar.export(async (sdk: Supernova, context: PulsarContext): Promise<Array<AnyO
tokenGroups = tokenGroups.filter((tokenGroup) => tokenGroup.brandId === context.brandId);
}

// Apply theme, if specified by the VSCode extension or pipeline configuration
if (context.themeId) {
const themes = await sdk.tokens.getTokenThemes(remoteVersionIdentifier);
const currentTheme = themes.find((theme) => theme.id === context.themeId);
if (currentTheme) {
tokens = await sdk.tokens.computeTokensByApplyingThemes(tokens, [currentTheme]);
} else {
// Don't allow applying theme which doesn't exist in the system
throw new Error('Unable to apply theme which does not exist in the system.');
}
}

// Convert all color tokens to CSS variables
const mappedTokens = new Map(tokens.map((token) => [token.id, token]));
const themes = await sdk.tokens.getTokenThemes(remoteVersionIdentifier);

const createTextFile = (relativePath: string, fileName: string, content: string): OutputTextFile => {
return FileHelper.createTextFile({
Expand All @@ -51,7 +40,11 @@ Pulsar.export(async (sdk: Supernova, context: PulsarContext): Promise<Array<AnyO
});
};

const files = generateFiles(tokens, mappedTokens, tokenGroups);
let textFiles: Array<OutputTextFile> = [];
const outputFilesData = await generateOutputFilesByThemes(tokens, mappedTokens, tokenGroups, themes, sdk);
textFiles = outputFilesData.map((file) => {
return createTextFile(file.path, file.fileName, file.content);
});

// TODO: Only for debugging purposes, remove for production!
const safeStringify = (obj: object) => {
Expand All @@ -75,15 +68,13 @@ Pulsar.export(async (sdk: Supernova, context: PulsarContext): Promise<Array<AnyO
return str;
};

return [
...files.map((file) => {
return createTextFile('./global/', file.fileName, file.content);
}),
// TODO: Only for debugging purposes - remove for production!
// TODO: Only for debugging purposes - remove for production!
textFiles.push(
createTextFile('./original-data/', '_original-tokens.json', safeStringify(tokens)),
// TODO: Only for debugging purposes - remove for production!
createTextFile('./original-data/', '_original-groups.json', JSON.stringify(tokenGroups, null, 2)),
];
);

return textFiles;
});

export const exportConfiguration = Pulsar.exportConfig<ExporterConfiguration>();

0 comments on commit d8c587b

Please sign in to comment.