Skip to content

Commit

Permalink
feat: allow deferring transitive transforms from the transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
jorenbroekema committed Dec 21, 2023
1 parent 5520294 commit 9864cba
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
44 changes: 44 additions & 0 deletions __tests__/exportPlatform.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,50 @@ describe('exportPlatform', () => {
expect(dictionary.eight.value).to.equal('foo-bar-bar');
expect(dictionary.nine.value).to.equal('foo-bar-bar-bar');
});

it('should apply transitive transforms with references nested beyond "value" prop, so transforms can consume the resolved value', async () => {
const StyleDictionaryExtended = new StyleDictionary({
tokens: {
a: {
value: 0.5,
},
b: {
value: '#fff',
type: 'color',
$extensions: {
'bar.foo': {
darken: '{a}',
},
},
},
},
platforms: {
test: {
transforms: ['color/darken'],
},
},
});
StyleDictionaryExtended.registerTransform({
type: 'value',
name: 'color/darken',
transitive: true,
matcher: (token) => token.type === 'color',
transformer: (token) => {
const darkenMod = token?.$extensions?.['bar.foo']?.darken;
if (usesReferences(darkenMod)) {
// defer this transform, because our darken value is a ref
return undefined;
}
if (typeof darkenMod === 'number') {
// don't actually darken, just return darken value for this test
return '#000';
}
return token.value;
},
});
const dictionary = await StyleDictionaryExtended.exportPlatform('test');
expect(dictionary.b.value).to.equal('#000');
});
});

it('should not have mutated the original tokens', async () => {
Expand Down
22 changes: 17 additions & 5 deletions lib/transform/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/

import isPlainObject from 'is-plain-obj';
import usesValueReference from '../utils/references/usesReferences.js';
import usesReferences from '../utils/references/usesReferences.js';
import getName from '../utils/references/getName.js';
import transformToken from './token.js';
import tokenSetup from './tokenSetup.js';
Expand Down Expand Up @@ -76,21 +76,33 @@ export default function transformObject(
// it is safe to run this multiple times on the same property.
const setupProperty = tokenSetup(/** @type {Token|TransformedToken} */ (objProp), name, path);

// If property has a reference, defer its transformations until later
if (usesValueReference(setupProperty.value, options)) {
const deferProp = () => {
// If property path isn't in the deferred array, add it now.
if (deferredPropValueTransforms.indexOf(pathName) === -1) {
deferredPropValueTransforms.push(pathName);
}

transformedObj[name] = setupProperty;
path.pop();
};

// If property has a reference, defer its transformations until later
if (usesReferences(setupProperty.value, options)) {
deferProp();
continue;
}

// If we got here, the property hasn't been transformed yet and
// does not use a value reference. Transform the property now and assign it.
transformedObj[name] = transformToken(setupProperty, options);
const transformedToken = transformToken(setupProperty, options);
// If a value transform returns undefined, it means the transform wants it to be deferred
// e.g. due to a ref existing in a sibling prop that the transform relies on.
// Example: { value: "#fff", darken: "{darken-amount}" }
if (transformedToken === undefined) {
deferProp();
continue;
}

transformedObj[name] = transformedToken;

// Remove the property path from the deferred transform list, starting from end of array
for (let i = deferredPropValueTransforms.length - 1; i >= 0; i--) {
Expand Down
8 changes: 6 additions & 2 deletions lib/transform/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import usesReferences from '../utils/references/usesReferences.js';
* @private
* @param {Token} property
* @param {PlatformConfig} options
* @returns {Token} - A new property object with transforms applied.
* @returns {Token|undefined} - A new property object with transforms applied.
*/
export default function transformProperty(property, options) {
const to_ret = structuredClone(property);
Expand All @@ -49,7 +49,11 @@ export default function transformProperty(property, options) {
// Only transform non-referenced values (from original)
// and transitive transforms if the value has been resolved
if (!usesReferences(property.original.value, options) || transform.transitive) {
to_ret.value = transform.transformer(to_ret, options);
const transformedValue = transform.transformer(to_ret, options);
if (transformedValue === undefined) {
return undefined;
}
to_ret.value = transformedValue;
}
}

Expand Down

0 comments on commit 9864cba

Please sign in to comment.