From 8bde22d685dcb5b58d130e882fccf53d752d0e8f Mon Sep 17 00:00:00 2001 From: Joren Broekema Date: Tue, 23 Jan 2024 11:21:26 +0100 Subject: [PATCH] fix: references inside color modifiers (#252) * chore: failing test for color modifier references * fix: references inside color modifiers --- .changeset/thirty-chairs-end.md | 5 ++ package-lock.json | 8 +-- package.json | 2 +- .../transformColorModifiers.ts | 9 +++ src/registerTransforms.ts | 3 +- .../color-modifier-references.test.ts | 56 +++++++++++++++++++ test/integration/cross-file-refs.test.ts | 4 +- test/integration/custom-group.test.ts | 4 +- test/integration/exclude-parent-keys.test.ts | 4 +- test/integration/expand-composition.test.ts | 4 +- .../math-in-complex-values.test.ts | 4 +- .../object-value-references.test.ts | 4 +- test/integration/output-references.test.ts | 4 +- test/integration/sd-transforms.test.ts | 4 +- test/integration/swift-UI-color.test.ts | 4 +- .../color-modifier-references.tokens.json | 37 ++++++++++++ .../transformColorModifiers.spec.ts | 20 +++++++ 17 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 .changeset/thirty-chairs-end.md create mode 100644 test/integration/color-modifier-references.test.ts create mode 100644 test/integration/tokens/color-modifier-references.tokens.json diff --git a/.changeset/thirty-chairs-end.md b/.changeset/thirty-chairs-end.md new file mode 100644 index 0000000..94f0a79 --- /dev/null +++ b/.changeset/thirty-chairs-end.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': patch +--- + +Support references in color modifiers now that style-dictionary transformers can defer themselves. diff --git a/package-lock.json b/package-lock.json index 789e24b..8d2c408 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "expr-eval-fork": "^2.0.2", "is-mergeable-object": "^1.1.1", "postcss-calc-ast-parser": "^0.1.4", - "style-dictionary": "4.0.0-prerelease.8" + "style-dictionary": "4.0.0-prerelease.9" }, "devDependencies": { "@changesets/cli": "^2.26.0", @@ -9704,9 +9704,9 @@ } }, "node_modules/style-dictionary": { - "version": "4.0.0-prerelease.8", - "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-4.0.0-prerelease.8.tgz", - "integrity": "sha512-L+iLynBUAsIrNdT3nEvkuZlZ3ibZ+dT9CyPa6pfAf68hgnLErxFbmScbplt1suFnpiv6lLv2fmi3re2MnjqqqQ==", + "version": "4.0.0-prerelease.9", + "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-4.0.0-prerelease.9.tgz", + "integrity": "sha512-fpQe740k88tf2HqkqTn2bOOUKTUM2OA+z0Xsy/EAbgncFjohrCdJ855w2Gn9LW0K867qB82Or7lpL8xwaaD3ug==", "hasInstallScript": true, "dependencies": { "@bundled-es-modules/deepmerge": "^4.3.1", diff --git a/package.json b/package.json index 4d1ac44..2882114 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "expr-eval-fork": "^2.0.2", "is-mergeable-object": "^1.1.1", "postcss-calc-ast-parser": "^0.1.4", - "style-dictionary": "4.0.0-prerelease.8" + "style-dictionary": "4.0.0-prerelease.9" }, "devDependencies": { "@changesets/cli": "^2.26.0", diff --git a/src/color-modifiers/transformColorModifiers.ts b/src/color-modifiers/transformColorModifiers.ts index 8e3e666..d08739e 100644 --- a/src/color-modifiers/transformColorModifiers.ts +++ b/src/color-modifiers/transformColorModifiers.ts @@ -1,4 +1,5 @@ import type { DesignToken } from 'style-dictionary/types'; +import { usesReferences } from 'style-dictionary/utils'; import { modifyColor } from './modifyColor.js'; import { ColorModifier } from '@tokens-studio/types'; import { ColorModifierOptions } from '../TransformOptions.js'; @@ -10,6 +11,14 @@ export function transformColorModifiers( options?: ColorModifierOptions, ): string | undefined { const modifier = token.$extensions['studio.tokens']?.modify as ColorModifier; + + // If some of the modifier props contain references or the modifier itself is a reference + // we should return undefined to manually defer this transformation until the references are resolved + // see: https://github.com/amzn/style-dictionary/blob/v4/docs/transforms.md#defer-transitive-transformation-manually + if (usesReferences(modifier) || Object.values(modifier).some(prop => usesReferences(prop))) { + return undefined; + } + if (options?.format) { modifier.format = options.format; } diff --git a/src/registerTransforms.ts b/src/registerTransforms.ts index ec5b414..0ffc884 100644 --- a/src/registerTransforms.ts +++ b/src/registerTransforms.ts @@ -78,6 +78,7 @@ export async function registerTransforms( name: 'ts/size/px', type: 'value', matcher: token => + typeof token.type === 'string' && ['sizing', 'spacing', 'borderRadius', 'borderWidth', 'fontSizes', 'dimension'].includes( token.type, ), @@ -166,7 +167,7 @@ export async function registerTransforms( name: 'ts/shadow/css/shorthand', type: 'value', transitive: true, - matcher: token => ['boxShadow'].includes(token.type), + matcher: token => typeof token.type === 'string' && ['boxShadow'].includes(token.type), transformer: token => Array.isArray(token.value) ? token.value.map(single => transformShadowForCSS(single)).join(', ') diff --git a/test/integration/color-modifier-references.test.ts b/test/integration/color-modifier-references.test.ts new file mode 100644 index 0000000..ea2319b --- /dev/null +++ b/test/integration/color-modifier-references.test.ts @@ -0,0 +1,56 @@ +import type StyleDictionary from 'style-dictionary'; +import { expect } from '@esm-bundle/chai'; +import { promises } from 'node:fs'; +import path from 'node: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/color-modifier-references.tokens.json'], + platforms: { + css: { + transformGroup: 'tokens-studio', + prefix: 'sd', + buildPath: outputDir, + files: [ + { + destination: outputFileName, + format: 'css/variables', + }, + ], + }, + }, +}; + +let dict: StyleDictionary | undefined; + +describe('typography references', () => { + beforeEach(async () => { + if (dict) { + cleanup(dict); + } + dict = await init(cfg); + }); + + afterEach(async () => { + if (dict) { + await cleanup(dict); + } + }); + + it('supports references inside color modifiers', async () => { + const file = await promises.readFile(outputFilePath, 'utf-8'); + expect(file).to.include( + `--sdAlpha: 0.3; + --sdColor: #ffffff4d;`, + ); + }); + + it('supports color modifier that is a reference itself, containing another reference', async () => { + const file = await promises.readFile(outputFilePath, 'utf-8'); + expect(file).to.include(`--sdColor2: #0000004d;`); + }); +}); diff --git a/test/integration/cross-file-refs.test.ts b/test/integration/cross-file-refs.test.ts index 303d4a6..bdc11bf 100644 --- a/test/integration/cross-file-refs.test.ts +++ b/test/integration/cross-file-refs.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/custom-group.test.ts b/test/integration/custom-group.test.ts index e22de03..5add132 100644 --- a/test/integration/custom-group.test.ts +++ b/test/integration/custom-group.test.ts @@ -1,8 +1,8 @@ import { expect } from '@esm-bundle/chai'; import StyleDictionary from 'style-dictionary'; import { transforms, registerTransforms } from '../../src/index.js'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/exclude-parent-keys.test.ts b/test/integration/exclude-parent-keys.test.ts index 1b6de6b..25e4118 100644 --- a/test/integration/exclude-parent-keys.test.ts +++ b/test/integration/exclude-parent-keys.test.ts @@ -1,6 +1,6 @@ import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/expand-composition.test.ts b/test/integration/expand-composition.test.ts index 0e750a7..00d8190 100644 --- a/test/integration/expand-composition.test.ts +++ b/test/integration/expand-composition.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/math-in-complex-values.test.ts b/test/integration/math-in-complex-values.test.ts index 0d4dd63..3a943b3 100644 --- a/test/integration/math-in-complex-values.test.ts +++ b/test/integration/math-in-complex-values.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/object-value-references.test.ts b/test/integration/object-value-references.test.ts index 7a459a5..240f0e4 100644 --- a/test/integration/object-value-references.test.ts +++ b/test/integration/object-value-references.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/output-references.test.ts b/test/integration/output-references.test.ts index 36aa0af..77507a4 100644 --- a/test/integration/output-references.test.ts +++ b/test/integration/output-references.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/sd-transforms.test.ts b/test/integration/sd-transforms.test.ts index a834e11..68fa791 100644 --- a/test/integration/sd-transforms.test.ts +++ b/test/integration/sd-transforms.test.ts @@ -1,7 +1,7 @@ import type StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/swift-UI-color.test.ts b/test/integration/swift-UI-color.test.ts index 434a114..cb3c3c5 100644 --- a/test/integration/swift-UI-color.test.ts +++ b/test/integration/swift-UI-color.test.ts @@ -1,8 +1,8 @@ import StyleDictionary from 'style-dictionary'; import { expect } from '@esm-bundle/chai'; import Color from 'tinycolor2'; -import { promises } from 'fs'; -import path from 'path'; +import { promises } from 'node:fs'; +import path from 'node:path'; import { cleanup, init } from './utils.js'; const outputDir = 'test/integration/tokens/'; diff --git a/test/integration/tokens/color-modifier-references.tokens.json b/test/integration/tokens/color-modifier-references.tokens.json new file mode 100644 index 0000000..c1e13c8 --- /dev/null +++ b/test/integration/tokens/color-modifier-references.tokens.json @@ -0,0 +1,37 @@ +{ + "alpha": { + "value": 0.3, + "type": "other" + }, + "color": { + "value": "#FFFFFF", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": { + "type": "alpha", + "value": "{alpha}", + "space": "srgb", + "format": "hex" + } + } + } + }, + "modifier": { + "value": { + "type": "alpha", + "value": "{alpha}", + "space": "srgb", + "format": "hex" + } + }, + "color2": { + "value": "#000000", + "type": "color", + "$extensions": { + "studio.tokens": { + "modify": "{modifier}" + } + } + } +} diff --git a/test/spec/color-modifiers/transformColorModifiers.spec.ts b/test/spec/color-modifiers/transformColorModifiers.spec.ts index da757c1..2779f47 100644 --- a/test/spec/color-modifiers/transformColorModifiers.spec.ts +++ b/test/spec/color-modifiers/transformColorModifiers.spec.ts @@ -310,4 +310,24 @@ describe('transform color modifiers', () => { expect(transformColorModifiers(token)).to.equal('rgb(50% 0% 25% / 0.5)'); }); + + // https://github.com/amzn/style-dictionary/blob/v4/docs/transforms.md#defer-transitive-transformation-manually + it('should return undefined if the transformation needs to be deferred', () => { + const token = { + value: '#C14242', + type: 'color', + $extensions: { + 'studio.tokens': { + modify: { + type: 'darken', + value: '{darkenAmount}', + space: 'srgb', + format: 'srgb', + }, + }, + }, + }; + + expect(transformColorModifiers(token)).to.be.undefined; + }); });