Skip to content

Commit

Permalink
refactor: ♻️ Conform cli arguments for output (#2884)
Browse files Browse the repository at this point in the history
- `tokens create|build` will now by default write tokens
- Removed: 
  - `-o, --out` on `tokens build`
  - `-w, --write` on `tokens create`
- New:
- `-o, --out-dir` for defining which folder `tokens create|build` will
be outputted to.
- `--dry` option for ommiting writing/building tokens to test which
token configuration would be generated


Notes:
- Used postcss and typescript cli as inspiration for cli commands
  • Loading branch information
mimarz authored Dec 11, 2024
1 parent a3225bb commit 2f94b73
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 98 deletions.
5 changes: 5 additions & 0 deletions .changeset/gorgeous-geese-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet": minor
---

Added option dry run using `--dry` on `tokens create` & `tokens build`
5 changes: 5 additions & 0 deletions .changeset/lazy-wolves-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet": minor
---

`tokens create` now writes by default
5 changes: 5 additions & 0 deletions .changeset/wicked-pots-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet": minor
---

Changed `-w, --write` to `-o, --out-dir` on `tokens create` & `tokens build` for defining output dir
3 changes: 1 addition & 2 deletions apps/theme/components/TokenModal/TokenModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ export const TokenModal = () => {
--${colorCliOptions.main} ${setCliColors(colors.main)} \\
--${colorCliOptions.neutral} "${colors.neutral[0]?.colors.light[8].hex}" \\
--${colorCliOptions.support} ${setCliColors(colors.support)} \\
--theme "${themeName}" \\
--write`;
--theme "${themeName}"`;

type InfoBoxType = {
title: string;
Expand Down
39 changes: 25 additions & 14 deletions packages/cli/bin/designsystemet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,45 @@ program.name('designsystemet').description('CLI for working with Designsystemet'

function makeTokenCommands() {
const tokenCmd = createCommand('tokens');
const DEFAULT_TOKENSDIR = './design-tokens';
const DEFAULT_TOKENS_DIR = './design-tokens';
const DEFAULT_BUILD_DIR = './design-tokens-build';

tokenCmd
.command('build')
.description('Build Designsystemet tokens')
.option('-t, --tokens <string>', `Path to ${chalk.blue('design-tokens')}`, DEFAULT_TOKENSDIR)
.option('-o, --out <string>', `Output directory for built ${chalk.blue('design-tokens')}`, './build')
.option('-t, --tokens <string>', `Path to ${chalk.blue('design-tokens')}`, DEFAULT_TOKENS_DIR)
.option('-o, --out-dir <string>', `Output directory for built ${chalk.blue('design-tokens')}`, DEFAULT_BUILD_DIR)
.option('--dry [boolean]', `Dry run for built ${chalk.blue('design-tokens')}`, false)
.option('-p, --preview', 'Generate preview token.ts files', false)
.option('--verbose', 'Enable verbose output', false)
.action((opts) => {
const tokens = typeof opts.tokens === 'string' ? opts.tokens : DEFAULT_TOKENSDIR;
const out = typeof opts.out === 'string' ? opts.out : './dist/tokens';
const preview = opts.preview;
const verbose = opts.verbose;
const { preview, verbose } = opts;
const tokens = typeof opts.tokens === 'string' ? opts.tokens : DEFAULT_TOKENS_DIR;
const outDir = typeof opts.outDir === 'string' ? opts.outDir : './dist/tokens';
const dry = Boolean(opts.dry);

console.log(`Building tokens in ${chalk.green(tokens)}`);
return buildTokens({ tokens, out, preview, verbose });

if (dry) {
console.log(`Performing dry run, no files will be written`);
}

return buildTokens({ tokens, outDir, preview, verbose, dry });
});
tokenCmd
.command('create')
.description('Create Designsystemet tokens')
.requiredOption(`-m, --${colorCliOptions.main} <name:hex...>`, `Main colors`, parseColorValues)
.requiredOption(`-s, --${colorCliOptions.support} <name:hex...>`, `Support colors`, parseColorValues)
.requiredOption(`-n, --${colorCliOptions.neutral} <hex>`, `Neutral hex color`, convertToHex)
.option('-w, --write [string]', `Output directory for created ${chalk.blue('design-tokens')}`, DEFAULT_TOKENSDIR)
.option('-o, --out-dir <string>', `Output directory for created ${chalk.blue('design-tokens')}`, DEFAULT_TOKENS_DIR)
.option('--dry [boolean]', `Dry run for created ${chalk.blue('design-tokens')}`, false)
.option('-f, --font-family <string>', `Font family`, 'Inter')
.option('--theme <string>', `Theme name`, 'theme')
.action(async (opts) => {
const { theme, fontFamily } = opts;
const { theme, fontFamily, outDir } = opts;
const dry = Boolean(opts.dry);
console.log(`Creating tokens with options ${chalk.green(JSON.stringify(opts, null, 2))}`);
const write = typeof opts.write === 'boolean' ? DEFAULT_TOKENSDIR : opts.write;

const props = {
themeName: theme,
Expand All @@ -56,11 +65,13 @@ function makeTokenCommands() {
},
};

if (dry) {
console.log(`Performing dry run, no files will be written`);
}

const tokens = createTokens(props);

if (write) {
await writeTokens({ writeDir: write, tokens, themeName: theme, colors: props.colors });
}
await writeTokens({ outDir, tokens, themeName: theme, colors: props.colors, dry });

return Promise.resolve();
});
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@
"build:tokens:debug": "yarn clean:theme && tsx --inspect-brk ./bin/designsystemet.ts tokens build -p -t ../../design-tokens -o ../../packages/theme/brand",
"build": "tsup && yarn build:types",
"build:types": "tsc --emitDeclarationOnly --declaration",
"test:tokens-create": "yarn designsystemet tokens create -m dominant:#007682 secondary:#ff0000 -n #003333 -s support1:#12404f support2:#0054a6 support3:#942977 -w ./test-tokens-create",
"test:tokens-create": "yarn designsystemet tokens create -m dominant:#007682 secondary:#ff0000 -n #003333 -s support1:#12404f support2:#0054a6 support3:#942977 -o ./test-tokens-create",
"test:tokens-build": "yarn designsystemet tokens build -t ./test-tokens-create -o ./test-tokens-build",
"test:tokens-create-and-build": "rimraf test-tokens-create && rimraf test-tokens-build && yarn test:tokens-create && yarn test:tokens-build",
"test": "yarn test:tokens-create-and-build",
"clean": "rimraf dist",
"clean:theme": "yarn workspace @digdir/designsystemet-theme clean",
"update:template": "tsx ./src/tokens/template.ts",
"internal:tokens-create-digdir": "yarn designsystemet tokens create --theme theme -m accent:#0062BA -n #1E2B3C -s brand1:#F45F63 brand2:#E5AA20 brand3:#1E98F5 -w ./internal/design-tokens",
"internal:tokens-create-altinn": "yarn designsystemet tokens create --theme theme2 -m accent:#0162BA -n #1E2B3C -s brand1:#0162BA brand2:#3F3161 brand3:#E02F4A -w ./internal/design-tokens",
"internal:tokens-create-uutilsynet": "yarn designsystemet tokens create --theme theme3 -m accent:#0162BA -n #1E2B3C -s brand1:#5B60D1 brand2:#FEA769 brand3:#5DA290 -w ./internal/design-tokens",
"internal:tokens-create-portal": "yarn designsystemet tokens create --theme theme4 -m accent:#4D107D -n #1E2B3C -s brand1:#A259DC brand2:#DF73E4 brand3:#E86ABF -w ./internal/design-tokens",
"internal:tokens-create-digdir": "yarn designsystemet tokens create --theme theme -m accent:#0062BA -n #1E2B3C -s brand1:#F45F63 brand2:#E5AA20 brand3:#1E98F5 -o ./internal/design-tokens",
"internal:tokens-create-altinn": "yarn designsystemet tokens create --theme theme2 -m accent:#0162BA -n #1E2B3C -s brand1:#0162BA brand2:#3F3161 brand3:#E02F4A -o ./internal/design-tokens",
"internal:tokens-create-uutilsynet": "yarn designsystemet tokens create --theme theme3 -m accent:#0162BA -n #1E2B3C -s brand1:#5B60D1 brand2:#FEA769 brand3:#5DA290 -o ./internal/design-tokens",
"internal:tokens-create-portal": "yarn designsystemet tokens create --theme theme4 -m accent:#4D107D -n #1E2B3C -s brand1:#A259DC brand2:#DF73E4 brand3:#E86ABF -o ./internal/design-tokens",
"internal:tokens-create-all": "yarn internal:tokens-create-digdir && yarn internal:tokens-create-altinn && yarn internal:tokens-create-uutilsynet && yarn internal:tokens-create-portal"
},
"dependencies": {
Expand Down
37 changes: 23 additions & 14 deletions packages/cli/src/tokens/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@ import { configs, getConfigsForThemeDimensions } from './build/configs.js';
import { type BuildConfig, type ThemePermutation, colorCategories } from './build/types.js';
import { makeEntryFile } from './build/utils/entryfile.js';
import { type ProcessedThemeObject, processThemeObject } from './build/utils/getMultidimensionalThemes.js';
import { copyFile, writeFile } from './utils.js';

export const DEFAULT_COLOR = 'accent';

type Options = {
/** Design tokens path */
tokens: string;
/** Output directory for built tokens */
out: string;
outDir: string;
/** Generate preview tokens */
preview: boolean;
/** Enable verbose output */
verbose: boolean;
/** Set the default "accent" color, if not overridden with data-color */
accentColor?: string;
/** Dry run */
dry?: boolean;
};

export let buildOptions: Options | undefined;
Expand All @@ -50,26 +53,26 @@ const buildConfigs = {
name: 'CSS entry files',
getConfig: configs.semanticVariables,
dimensions: ['semantic'],
build: async (sdConfigs, { outPath }) => {
build: async (sdConfigs, { outPath, dry }) => {
await Promise.all(
sdConfigs.map(async ({ permutation: { theme } }) => {
console.log(`👷 ${theme}.css`);

const builtinColorsFilename = 'builtin-colors.css';
const builtinColors = path.resolve(import.meta.dirname, 'build', builtinColorsFilename);
await fs.copyFile(builtinColors, path.resolve(outPath, theme, builtinColorsFilename));
await copyFile(builtinColors, path.resolve(outPath, theme, builtinColorsFilename), dry);

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

export async function buildTokens(options: Options): Promise<void> {
buildOptions = options;
const tokensDir = options.tokens;
const outPath = path.resolve(options.out);
export async function buildTokens(buildOptions: Options): Promise<void> {
const { dry } = buildOptions;
const tokensDir = buildOptions.tokens;
const targetDir = path.resolve(buildOptions.outDir);

/*
* Build the themes
Expand Down Expand Up @@ -97,7 +100,7 @@ export async function buildTokens(options: Options): Promise<void> {
(val: BuildConfig) => ({
buildConfig: val,
sdConfigs: getConfigsForThemeDimensions(val.getConfig, relevant$themes, val.dimensions, {
outPath,
outPath: targetDir,
tokensDir,
...val.options,
}),
Expand All @@ -114,15 +117,20 @@ export async function buildTokens(options: Options): Promise<void> {
console.log(`\n🍱 Building ${chalk.green(buildConfig.name ?? key)}`);

if (buildConfig.build) {
await buildConfig.build(sdConfigs, { outPath, tokensDir, ...buildConfig.options });
await buildConfig.build(sdConfigs, { outPath: targetDir, tokensDir, ...buildConfig.options, dry });
}

await Promise.all(
sdConfigs.map(async ({ config, permutation }) => {
const modes: Array<keyof ThemePermutation> = ['theme', ...buildConfig.dimensions];
const modeMessage = modes.map((x) => permutation[x]).join(' - ');
console.log(modeMessage);

return (await sd.extend(config)).buildAllPlatforms();
if (!dry) {
return (await sd.extend(config)).buildAllPlatforms();
}

return Promise.resolve();
}),
);
}
Expand All @@ -138,10 +146,10 @@ export async function buildTokens(options: Options): Promise<void> {
throw err;
}

await writeColorTypeDeclaration($themes, outPath);
await writeColorTypeDeclaration($themes, targetDir, dry);
}

async function writeColorTypeDeclaration($themes: ProcessedThemeObject[], outPath: string) {
async function writeColorTypeDeclaration($themes: ProcessedThemeObject[], outPath: string, dry?: boolean) {
const colorsFileName = 'colors.d.ts';
console.log(`\n🍱 Building ${chalk.green('type declarations')}`);
console.log(colorsFileName);
Expand All @@ -159,5 +167,6 @@ ${mainAndSupportColors.map((color) => ` ${color}: never;`).join('\n')}
}
}
`.trimStart();
await fs.writeFile(path.resolve(outPath, colorsFileName), typeDeclaration, 'utf-8');

await writeFile(path.resolve(outPath, colorsFileName), typeDeclaration, dry);
}
40 changes: 0 additions & 40 deletions packages/cli/src/tokens/build/actions.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/cli/src/tokens/build/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Config as StyleDictionaryConfig, TransformedToken } from 'style-di
import { outputReferencesFilter } from 'style-dictionary/utils';

import { DEFAULT_COLOR, buildOptions } from '../build.js';
import { isColorCategoryToken, pathStartsWithOneOf, typeEquals } from '../utils.js';
import { formats } from './formats/css.js';
import { jsTokens } from './formats/js-tokens.js';
import { nameKebab, resolveMath, sizeRem, typographyName } from './transformers.js';
Expand All @@ -17,7 +18,6 @@ import type {
ThemePermutation,
} from './types.js';
import { type ProcessedThemeObject, getMultidimensionalThemes } from './utils/getMultidimensionalThemes.js';
import { isColorCategoryToken, pathStartsWithOneOf, typeEquals } from './utils/utils.js';

void register(StyleDictionary, { withSDBuiltins: false });
/** Use official W3C design token format
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/tokens/build/formats/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { TransformedToken } from 'style-dictionary';
import type { Format } from 'style-dictionary/types';
import { createPropertyFormatter, fileHeader, usesReferences } from 'style-dictionary/utils';

import { getValue, isColorCategoryToken, isGlobalColorToken, isSemanticToken } from '../../utils.js';
import { type IsCalculatedToken, colorCategories } from '../types.js';
import { getValue, isColorCategoryToken, isGlobalColorToken, isSemanticToken } from '../utils/utils.js';

const prefersColorScheme = (colorScheme: string, content: string) => `
@media (prefers-color-scheme: ${colorScheme}) {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/tokens/build/formats/js-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as R from 'ramda';
import type { Format, TransformedToken } from 'style-dictionary/types';
import { createPropertyFormatter, fileHeader } from 'style-dictionary/utils';

import { getType, isColorCategoryToken } from '../utils/utils.js';
import { getType, isColorCategoryToken } from '../../utils.js';

const groupByType = R.groupBy((token: TransformedToken) => getType(token));

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/tokens/build/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { checkAndEvaluateMath } from '@tokens-studio/sd-transforms';
import * as R from 'ramda';
import type { Transform } from 'style-dictionary/types';

import { getValue, pathStartsWithOneOf, typeEquals } from '../utils.js';
import { noCase } from './utils/noCase.js';
import { getValue, pathStartsWithOneOf, typeEquals } from './utils/utils.js';

const isPx = R.test(/\b\d+px\b/g);

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/tokens/build/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type IsCalculatedToken = (token: TransformedToken, options?: StyleDiction
export type GetSdConfigOptions = {
outPath: string;
tokensDir: string;
dry?: boolean;
};

export type BuildConfig = {
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/tokens/build/utils/entryfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import chalk from 'chalk';
import glob from 'fast-glob';
import fs from 'fs-extra';
import * as R from 'ramda';
import { writeFile } from '../../utils';

/**
* Defines a sort order for the sections of the entry CSS file.
Expand Down Expand Up @@ -68,18 +69,19 @@ type EntryFile = (options: {
outPath: string;
buildPath: string;
theme: string;
dry?: boolean;
}) => Promise<undefined>;

/**
* Creates a CSS entry file that imports base CSS files for a theme
*/
export const makeEntryFile: EntryFile = async ({ outPath, buildPath, theme }) => {
export const makeEntryFile: EntryFile = async ({ outPath, buildPath, theme, dry }) => {
const writePath = `${outPath}/${theme}.css`;

const files = await glob(`**/*`, { cwd: buildPath });
const sortAlphabetically = R.sort<string>(R.ascend((x) => x));
const sortedFileNames = R.pipe(sortAlphabetically, sortByDefinedOrder)(files);
const content = header + concat(sortedFileNames.map((file) => `${buildPath}/${file}`));

await fs.writeFile(writePath, content);
await writeFile(writePath, content, dry);
};
Loading

0 comments on commit 2f94b73

Please sign in to comment.