Skip to content

Commit

Permalink
fix: proper isObject checks where applicable (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorenbroekema authored Sep 9, 2022
1 parent 6cddc11 commit 1c0ee01
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-hornets-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@divriots/style-dictionary-to-figma': patch
---

Do proper isObject check (typeof null and Array are also 'object') where needed. Fixes bug with metadata props with type Array getting altered by trimValue to become Objects.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,24 @@ Used by Design Systems in [Backlight](https://backlight.dev) using design tokens
## Features

- Allows marking a category as a custom tokenset so that it will appear as a separate tokenset in Figma. This is useful if you want to combine many base tokens into a "global" set for example.
- **Mandatory action required**: Supports marking a category as a custom tokenset so that it will appear as a separate tokenset in Figma. This is useful if you want to combine many base tokens into a "global" set for example and a mandatory step for Figma Tokens Plugin currently, or your references will not work. You can configure it like so:

```json
{
"color": {
"tokenset": "global",
"primary": {
"base": {
"type": "color",
"value": "#14b8a6"
}
}
}
}
```

`color.primary.base` token will appear under `global` tokenset now in the plugin.

- Trims `.value` from reference values as Figma Tokens plugin does not use this suffix.
- Trims the `name` properties from tokens since Figma Tokens plugin uses this property to name its tokens, however, without a name property it creates its own naming/nesting by the object structure which is way nicer.
- Use the reference values rather than its resolved values. Put `ignoreUseRefValue: true` as a sibling property to the value prop if you want to make an exception and keep it as a resolved value.
Expand Down
8 changes: 5 additions & 3 deletions src/mark-tokenset.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObject } from './utils/isObject.js';

/**
* @typedef {import('./style-dictionary-to-figma.js').Obj} Obj
*/
Expand All @@ -9,14 +11,14 @@
export function markTokenset(obj) {
const _obj = { ...obj };
Object.keys(_obj).forEach(key => {
if (typeof _obj[key] === 'object') {
// typeof check so we know it's an object
if (isObject(_obj[key])) {
// check so we know it's an object
const nestedObj = /** @type {Obj} */ (_obj[key]);
Object.keys(nestedObj).forEach(nestedKey => {
if (nestedKey === 'tokenset') {
// tokenset value may only be string
const tokenset = /** @type {string} */ (nestedObj[nestedKey]);
if (typeof _obj[tokenset] !== 'object') {
if (!isObject(_obj[tokenset])) {
_obj[tokenset] = {};
}
/** @type {Obj} */ (_obj[tokenset])[key] = nestedObj;
Expand Down
4 changes: 3 additions & 1 deletion src/trim-name.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObject } from './utils/isObject.js';

/**
* @typedef {import('./style-dictionary-to-figma.js').Obj} Obj
*/
Expand All @@ -12,7 +14,7 @@ export function trimName(obj) {
Object.keys(newObj).forEach(key => {
if (key === 'name') {
delete newObj[key];
} else if (typeof newObj[key] === 'object') {
} else if (isObject(newObj[key]) || Array.isArray(newObj[key])) {
const newValue = trimName(/** @type {Obj} */ (newObj[key]));
newObj[key] = Array.isArray(newObj[key]) ? Object.values(newValue) : newValue;
}
Expand Down
5 changes: 3 additions & 2 deletions src/trim-value.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isObject } from './utils/isObject.js';
/**
* @typedef {import('./style-dictionary-to-figma.js').Obj} Obj
*/
Expand All @@ -18,11 +19,11 @@ export function trimValue(obj, isValueObj = false) {
if (matches && matches[1]) {
newObj[key] = val.replace('.value', '');
}
} else if (typeof newObj[key] === 'object') {
} else if (isObject(newObj[key]) || Array.isArray(newObj[key])) {
const newValue = trimValue(/** @type {Obj} */ (newObj[key]), true);
newObj[key] = Array.isArray(newObj[key]) ? Object.values(newValue) : newValue;
}
} else if (typeof newObj[key] === 'object') {
} else if (isObject(newObj[key])) {
newObj[key] = trimValue(/** @type {Obj} */ (newObj[key]));
}
});
Expand Down
4 changes: 3 additions & 1 deletion src/use-ref-value.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObject } from './utils/isObject.js';

/**
* @typedef {import('./style-dictionary-to-figma.js').Obj} Obj
* @typedef {string|Object|Array<unknown>|number} Value
Expand Down Expand Up @@ -35,7 +37,7 @@ export function useRefValue(obj) {
if (isRefValue(originalValue)) {
newObj.value = /** @type {Obj} */ (newObj[key]).value;
}
} else if (typeof newObj[key] === 'object') {
} else if (isObject(newObj[key]) || Array.isArray(newObj[key])) {
const newValue = useRefValue(/** @type {Obj} */ (newObj[key]));
newObj[key] = Array.isArray(newObj[key]) ? Object.values(newValue) : newValue;
}
Expand Down
7 changes: 7 additions & 0 deletions src/utils/isObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @param {any} val
* @returns {boolean}
*/
export function isObject(val) {
return typeof val === 'object' && val !== null && !Array.isArray(val);
}
33 changes: 33 additions & 0 deletions test/trim-value.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,37 @@ describe('trim-value', () => {

expect(trimmedObj).to.eql(expectedObj);
});

it('keeps arbitrary metadata intact', () => {
const obj = {
core: {
color: {
primary: {
base: {
type: 'color',
value: '#14b8a6',
path: ['core', 'color', 'primary', 'base'],
},
},
},
},
};

const expectedObj = {
core: {
color: {
primary: {
base: {
type: 'color',
value: '#14b8a6',
path: ['core', 'color', 'primary', 'base'],
},
},
},
},
};

const transformedObj = trimValue(obj);
expect(transformedObj).to.eql(expectedObj);
});
});
11 changes: 11 additions & 0 deletions test/utils/isObject.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { expect } from '@esm-bundle/chai';
import { isObject } from '../../src/utils/isObject.js';

describe('isObject utility', () => {
it('detects if something is an object', () => {
expect(isObject({})).to.be.true;
expect(isObject({ foo: 'bar', qux: { test: '1', testTwo: '2' } })).to.be.true;
expect(isObject(['foo', { test: '1', testTwo: '2' }])).to.be.false;
expect(isObject(null)).to.be.false;
});
});

0 comments on commit 1c0ee01

Please sign in to comment.