Skip to content

Commit

Permalink
Merge pull request #104 from tokens-studio/exclude-parent-keys
Browse files Browse the repository at this point in the history
feat: optionally exclude parent keys from token file
  • Loading branch information
jorenbroekema authored Apr 19, 2023
2 parents 124cc8c + a25a1ff commit 2fb1938
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-queens-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tokens-studio/sd-transforms': patch
---

Add excludeParentKeys option to the transform options, in order to exclude parent keys from your token files. This is useful if you use a single-file export from Tokens Studio Figma Plugin.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ to work with Design Tokens that are exported from [Tokens Studio](https://tokens
Generic:

- Expands composition tokens into multiple, optionally also does so for typography, border and shadow tokens -> parser
- Optionally excludes parent keys from your tokens file, e.g. when using single-file export from Tokens Studio Figma plugin -> parser
- Maps token descriptions to comments -> `ts/descriptionToComment`
- Check and evaluate Math expressions (transitive) -> `ts/resolveMath`
- Transform dimensions tokens to have `px` as a unit when missing (transitive) -> `ts/size/px`
Expand Down Expand Up @@ -105,18 +106,21 @@ registerTransforms({
token.value.width !== 0 && filePath.startsWith(path.resolve('tokens/core')),
shadow: false,
},
excludeParentKeys: true,
});
```

Options:

| name | type | required | default | description |
| ------------------ | ------------------------ | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| excludeParentKeys | boolean || `false` | Whether or not to exclude parent keys from your token files |
| expand | boolean \| ExpandOptions || See props below | `false` to not register the parser at all. By default, expands composition tokens. Optionally, border, shadow and typography as well. |
| expand.composition | boolean \| ExpandFilter || `true` | Whether or not to expand compositions. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.typography | boolean \| ExpandFilter || `false` | Whether or not to expand typography. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.shadow | boolean \| ExpandFilter || `false` | Whether or not to expand shadows. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.border | boolean \| ExpandFilter || `false` | Whether or not to expand borders. Also allows a filter callback function to conditionally expand per token/filePath |
| |

> Note: you can also import and use the `expandComposites` function to run the expansion on your token object manually.
> Handy if you have your own parsers set up (e.g. for JS files), and you want the expansions to work there too.
Expand Down Expand Up @@ -179,5 +183,5 @@ sd.cleanAllPlatforms();
sd.buildAllPlatforms();
```

> Note: make sure to choose either the full transformGroup, **OR** its separate transforms, so you can adjust or add your own.
> Note: make sure to choose either the full transformGroup, **OR** its separate transforms so you can adjust or add your own.
> [Combining a transformGroup with a transforms array can give unexpected results](https://github.com/amzn/style-dictionary/issues/813).
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/TransformOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export interface ExpandOptions {

export interface TransformOptions {
expand?: ExpandOptions | false;
excludeParentKeys?: boolean;
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { registerTransforms } from './registerTransforms.js';

export { expandComposites } from './parsers/expand-composites.js';
export { excludeParentKeys } from './parsers/exclude-parent-keys.js';

export { mapDescriptionToComment } from './mapDescriptionToComment.js';
export { checkAndEvaluateMath } from './checkAndEvaluateMath.js';
Expand Down
18 changes: 18 additions & 0 deletions src/parsers/exclude-parent-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DeepKeyTokenMap } from '@tokens-studio/types';
import { TransformOptions } from '../TransformOptions.js';

export function excludeParentKeys(
dictionary: DeepKeyTokenMap<false>,
transformOpts?: TransformOptions,
): DeepKeyTokenMap<false> {
if (!transformOpts?.excludeParentKeys) {
return dictionary;
}
const copy = {} as DeepKeyTokenMap<false>;
Object.values(dictionary).forEach(set => {
Object.entries(set).forEach(([key, tokenGroup]) => {
copy[key] = tokenGroup;
});
});
return copy;
}
4 changes: 3 additions & 1 deletion src/registerTransforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { mapDescriptionToComment } from './mapDescriptionToComment.js';
import { transformColorModifiers } from './color-modifiers/transformColorModifiers.js';
import { TransformOptions } from './TransformOptions.js';
import { expandComposites } from './parsers/expand-composites.js';
import { excludeParentKeys } from './parsers/exclude-parent-keys.js';
import { transformOpacity } from './transformOpacity.js';

const isBrowser = typeof window === 'object';
Expand Down Expand Up @@ -41,7 +42,8 @@ export async function registerTransforms(sd: Core, transformOpts?: TransformOpti
pattern: /\.json$/,
parse: ({ filePath, contents }) => {
const obj = JSON.parse(contents);
const expanded = expandComposites(obj, filePath, transformOpts);
const excluded = excludeParentKeys(obj, transformOpts);
const expanded = expandComposites(excluded, filePath, transformOpts);
return expanded;
},
});
Expand Down
66 changes: 66 additions & 0 deletions test/integration/exclude-parent-keys.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { expect } from '@esm-bundle/chai';
import StyleDictionary from 'style-dictionary';
import { promises } from 'fs';
import path from 'path';
import { cleanup, init } from './utils.js';

const outputDir = 'test/integration/tokens/';
const outputFileName = 'vars.css';
const outputFilePath = path.resolve(outputDir, outputFileName);

const cfg = {
source: ['test/integration/tokens/exclude-parent-keys.tokens.json'],
platforms: {
css: {
transformGroup: 'tokens-studio',
prefix: 'sd',
buildPath: outputDir,
files: [
{
destination: outputFileName,
format: 'css/variables',
},
],
},
},
};
let transformOpts = {};
let dict: StyleDictionary.Core | undefined;

function before() {
if (dict) {
cleanup(dict);
}
dict = init(cfg, transformOpts);
}

function after() {
if (dict) {
cleanup(dict);
}
}

describe('exclude parent keys', () => {
afterEach(() => {
after();
});

it('does not expand parent keys by default and throws on broken references', async () => {
expect(before).to.throw('Problems were found when trying to resolve property references');
});

it('optionally excludes parent keys', async () => {
transformOpts = {
excludeParentKeys: true,
};
before();

const file = await promises.readFile(outputFilePath, 'utf-8');
expect(file).to.include(
`
--sdCoreColor: #FFFFFF;
--sdSemanticColor: #FFFFFF;
--sdButtonColor: #FFFFFF;`,
);
});
});
24 changes: 24 additions & 0 deletions test/integration/tokens/exclude-parent-keys.tokens.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"foo": {
"core": {
"color": {
"value": "#FFFFFF",
"type": "color"
}
},
"semantic": {
"color": {
"value": "{core.color}",
"type": "color"
}
}
},
"bar": {
"button": {
"color": {
"value": "{semantic.color}",
"type": "color"
}
}
}
}
58 changes: 58 additions & 0 deletions test/spec/parsers/excludeParentKeys.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { expect } from '@esm-bundle/chai';
import { DeepKeyTokenMap } from '@tokens-studio/types';
import { excludeParentKeys } from '../../../src/parsers/exclude-parent-keys.js';

const tokenObj = {
foo: {
core: {
color: {
value: '#FFFFFF',
type: 'color',
},
},
semantic: {
color: {
value: '{core.color}',
type: 'color',
},
},
},
bar: {
button: {
color: {
value: '{semantic.color}',
type: 'color',
},
},
},
} as DeepKeyTokenMap<false>;

describe('exclude parent keys', () => {
it('should not exclude parent keys by default', () => {
expect(excludeParentKeys(tokenObj)).to.eql(tokenObj);
expect(excludeParentKeys(tokenObj, { excludeParentKeys: false })).to.eql(tokenObj);
});

it('should exclude parent keys if the option is passed', () => {
expect(excludeParentKeys(tokenObj, { excludeParentKeys: true })).to.eql({
core: {
color: {
value: '#FFFFFF',
type: 'color',
},
},
semantic: {
color: {
value: '{core.color}',
type: 'color',
},
},
button: {
color: {
value: '{semantic.color}',
type: 'color',
},
},
});
});
});

0 comments on commit 2fb1938

Please sign in to comment.