From d2117a700dd88a5b8b2e4f67deec5eb63faeacb4 Mon Sep 17 00:00:00 2001 From: jorenbroekema Date: Tue, 9 Jul 2024 14:57:29 +0200 Subject: [PATCH 1/2] fix: split fontWeight tokens font style properly into parent group --- .changeset/two-ads-confess.md | 5 +++ package-lock.json | 42 ++++++------------- package.json | 2 +- src/preprocessors/add-font-styles.ts | 22 ++++++---- test/integration/cross-file-refs.test.ts | 12 +++--- test/integration/expand-composition.test.ts | 9 ++-- .../preprocessors/add-font-styles.spec.ts | 19 ++++++--- test/spec/register.spec.ts | 14 +++---- 8 files changed, 64 insertions(+), 61 deletions(-) create mode 100644 .changeset/two-ads-confess.md diff --git a/.changeset/two-ads-confess.md b/.changeset/two-ads-confess.md new file mode 100644 index 0000000..f9521d1 --- /dev/null +++ b/.changeset/two-ads-confess.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': minor +--- + +Properly split fontWeight tokens that contain fontStyle into the parent group. For typography tokens this was correct but for fontWeight tokens, collision could occur between the token and its sibling tokens. diff --git a/package-lock.json b/package-lock.json index f92e899..1b242fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tokens-studio/sd-transforms", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tokens-studio/sd-transforms", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "dependencies": { "@bundled-es-modules/deepmerge": "^4.3.1", @@ -45,7 +45,7 @@ "node": ">=18.0.0" }, "peerDependencies": { - "style-dictionary": "^4.0.0" + "style-dictionary": "^4.0.1" } }, "node_modules/@75lb/deep-merge": { @@ -145,29 +145,20 @@ } }, "node_modules/@bundled-es-modules/memfs": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.8.1.tgz", - "integrity": "sha512-9BodQuihWm3XJGKYuV/vXckK8Tkf9EDiT/au1NJeFUyBMe7EMYRtOqL9eLzrjqJSDJUFoGwQFHvraFHwR8cysQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.9.4.tgz", + "integrity": "sha512-1XyYPUaIHwEOdF19wYVLBtHJRr42Do+3ctht17cZOHwHf67vkmRNPlYDGY2kJps4RgE5+c7nEZmEzxxvb1NZWA==", "peer": true, "dependencies": { "assert": "^2.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "memfs": "^4.8.1", + "memfs": "^4.9.3", "path": "^0.12.7", - "stream": "^0.0.2", + "stream": "^0.0.3", "util": "^0.12.5" } }, - "node_modules/@bundled-es-modules/memfs/node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "peer": true, - "dependencies": { - "emitter-component": "^1.1.1" - } - }, "node_modules/@bundled-es-modules/postcss-calc-ast-parser": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@bundled-es-modules/postcss-calc-ast-parser/-/postcss-calc-ast-parser-0.1.6.tgz", @@ -3983,15 +3974,6 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -9944,15 +9926,15 @@ } }, "node_modules/style-dictionary": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-4.0.0.tgz", - "integrity": "sha512-7UA+gdkNumWwKF8EIWIxvY2kNjNizeozA5P04c9EDZzP/bMAqTgf3GpwDNEFSv9QFAjQ/KXEhfyWZZDUoIKuLA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-4.0.1.tgz", + "integrity": "sha512-aZ2iouI0i0DIXk3QhCkwOeo5rQeuk5Ja0PhHo32/EXCNuay4jK4CZ+hQJW0Er0J74VWniR+qaeoWgjklcULxOQ==", "hasInstallScript": true, "peer": true, "dependencies": { "@bundled-es-modules/deepmerge": "^4.3.1", "@bundled-es-modules/glob": "^10.4.2", - "@bundled-es-modules/memfs": "^4.8.1", + "@bundled-es-modules/memfs": "^4.9.4", "@zip.js/zip.js": "^2.7.44", "chalk": "^5.3.0", "change-case": "^5.3.0", diff --git a/package.json b/package.json index fe9203f..2df3983 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "is-mergeable-object": "^1.1.1" }, "peerDependencies": { - "style-dictionary": "^4.0.0" + "style-dictionary": "^4.0.1" }, "devDependencies": { "@changesets/cli": "^2.27.6", diff --git a/src/preprocessors/add-font-styles.ts b/src/preprocessors/add-font-styles.ts index e6842c3..84156d3 100644 --- a/src/preprocessors/add-font-styles.ts +++ b/src/preprocessors/add-font-styles.ts @@ -66,15 +66,15 @@ function recurse( if (tokenType === 'typography') { const tokenTypographyValue = tokenValue as TokenTypographyValue & { fontStyle: string }; if (tokenTypographyValue.fontWeight === undefined) return; + const fontWeight = resolveFontWeight( `${tokenTypographyValue.fontWeight}`, refCopy, usesDtcg, ); const { weight, style } = splitWeightStyle(fontWeight, alwaysAddFontStyle); - - tokenTypographyValue.fontWeight = weight; if (style) { + tokenTypographyValue.fontWeight = weight; tokenTypographyValue.fontStyle = style; } } else if (tokenType === 'fontWeight') { @@ -82,13 +82,19 @@ function recurse( const fontWeight = resolveFontWeight(`${tokenFontWeightsValue}`, refCopy, usesDtcg); const { weight, style } = splitWeightStyle(fontWeight, alwaysAddFontStyle); - // since tokenFontWeightsValue is a primitive (string), we have to permutate the change directly - token[`${usesDtcg ? '$' : ''}value`] = weight; if (style) { - (slice as DeepKeyTokenMap)[`fontStyle`] = { - ...token, - [`${usesDtcg ? '$' : ''}type`]: 'fontStyle', - [`${usesDtcg ? '$' : ''}value`]: style, + // since tokenFontWeightsValue is a primitive (string), we have to permutate the change directly + (slice[key] as DeepKeyTokenMap) = { + weight: { + ...token, + [`${usesDtcg ? '$' : ''}type`]: 'fontWeight', + [`${usesDtcg ? '$' : ''}value`]: weight, + }, + style: { + ...token, + [`${usesDtcg ? '$' : ''}type`]: 'fontStyle', + [`${usesDtcg ? '$' : ''}value`]: style, + }, }; } } diff --git a/test/integration/cross-file-refs.test.ts b/test/integration/cross-file-refs.test.ts index 3b47834..6c1f88d 100644 --- a/test/integration/cross-file-refs.test.ts +++ b/test/integration/cross-file-refs.test.ts @@ -42,8 +42,8 @@ describe('cross file references', () => { it('supports cross file references e.g. expanding typography', async () => { const file = await promises.readFile(outputFilePath, 'utf-8'); expect(file).to.include(` - --sdTypoFontWeight: 400; - --sdTypoFontStyle: italic; + --sdTypoFontWeightWeight: 400; + --sdTypoFontWeightStyle: italic; --sdPrimaryFont: Inter; --sdFontWeight: 800; --sdLineHeight: 1.5; @@ -67,14 +67,14 @@ describe('cross file references', () => { --sdTestTypographyTextFontSize: 25px; --sdTestTypographyTextLineHeight: 32px; --sdTestTypographyTextFontWeight: 700; - --sdWeight: 400; - --sdTypoAliasFontWeight: 400; - --sdTypoAliasFontStyle: italic; + --sdWeightWeight: 400; + --sdWeightStyle: italic; + --sdTypoAliasFontWeightWeight: 400; + --sdTypoAliasFontWeightStyle: italic; --sdTypo3FontFamily: Inter; --sdTypo3FontWeight: 800; --sdTypo3LineHeight: 1.5; --sdTypo3FontSize: 8px; - --sdFontStyle: italic; `); }); }); diff --git a/test/integration/expand-composition.test.ts b/test/integration/expand-composition.test.ts index 77968bc..e10d5e9 100644 --- a/test/integration/expand-composition.test.ts +++ b/test/integration/expand-composition.test.ts @@ -74,7 +74,8 @@ describe('expand composition tokens', () => { --sdCompositionHeaderFontSizes: 96px; --sdCompositionHeaderFontWeights: 700; --sdTypography: italic 800 26px/1.25 Arial; - --sdFontWeightRef: 800; + --sdFontWeightRefWeight: 800; + --sdFontWeightRefStyle: italic; --sdBorder: 4px solid #FFFF00; --sdShadowSingle: inset 0 4px 10px 0 rgba(0,0,0,0.4); --sdShadowDouble: inset 0 4px 10px 0 rgba(0,0,0,0.4), 0 8px 12px 5px rgba(0,0,0,0.4); @@ -95,7 +96,8 @@ describe('expand composition tokens', () => { --sdCompositionHeaderFontSizes: 96px; --sdCompositionHeaderFontWeights: 700; --sdTypographyFontFamily: Arial; - --sdTypographyFontWeight: 800; + --sdTypographyFontWeightWeight: 800; + --sdTypographyFontWeightStyle: italic; --sdTypographyLineHeight: 1.25; --sdTypographyFontSize: 26px; --sdTypographyLetterSpacing: 0rem; @@ -104,7 +106,8 @@ describe('expand composition tokens', () => { --sdTypographyTextDecoration: none; --sdTypographyTextCase: none; --sdTypographyFontStyle: italic; - --sdFontWeightRef: 800; + --sdFontWeightRefWeight: 800; + --sdFontWeightRefStyle: italic; --sdBorderColor: #ffff00; --sdBorderWidth: 4px; --sdBorderStyle: solid; diff --git a/test/spec/preprocessors/add-font-styles.spec.ts b/test/spec/preprocessors/add-font-styles.spec.ts index 1c11e13..8175f9e 100644 --- a/test/spec/preprocessors/add-font-styles.spec.ts +++ b/test/spec/preprocessors/add-font-styles.spec.ts @@ -106,8 +106,13 @@ describe('add font style', () => { fw: { value: 'SemiBold Italic', type: 'fontWeight' }, }), ).to.eql({ - fw: { value: 'SemiBold', type: 'fontWeight' }, - fontStyle: { value: 'italic', type: 'fontStyle' }, + fw: { + weight: { + value: 'SemiBold', + type: 'fontWeight', + }, + style: { value: 'italic', type: 'fontStyle' }, + }, }); }); @@ -122,8 +127,10 @@ describe('add font style', () => { }), ).to.eql({ fw: { - weight: { $value: 'SemiBold', $type: 'fontWeight' }, - fontStyle: { $value: 'italic', $type: 'fontStyle' }, + weight: { + weight: { $value: 'SemiBold', $type: 'fontWeight' }, + style: { $value: 'italic', $type: 'fontStyle' }, + }, }, }); }); @@ -244,7 +251,7 @@ describe('add font style', () => { }); }); - it('handlestinvalid fontweight structures e.g. mixing token group / token, also handles DTCG format', () => { + it('handles invalid fontweight structures e.g. mixing token group / token, also handles DTCG format', () => { // @ts-expect-error aligned types already here const tokens = { foo: { @@ -277,7 +284,7 @@ describe('add font style', () => { thing: { $type: 'typography', $value: { - fontWeight: '700', + fontWeight: '{foo}', }, }, }); diff --git a/test/spec/register.spec.ts b/test/spec/register.spec.ts index 9a65fac..6e4d1cb 100644 --- a/test/spec/register.spec.ts +++ b/test/spec/register.spec.ts @@ -178,7 +178,7 @@ const tokens = { }, type: 'typography', }, - 'shadow-blur': { + blur: { value: '10', type: 'sizing', }, @@ -187,7 +187,7 @@ const tokens = { { x: '0', y: '4', - blur: '{shadow-blur}', + blur: '{blur}', spread: '0', color: 'rgba(0,0,0,0.4)', type: 'innerShadow', @@ -337,7 +337,7 @@ describe('register', () => { --fontSizesH6: 16px; --fontSizesBody: 16px; --heading6: 700 16px/1 'Arial Black', 'Suisse Int\\'l', sans-serif; - --shadowBlur: 10px; + --blur: 10px; --shadow: inset 0 4px 10px 0 rgba(0,0,0,0.4); --borderWidth: 5px; --border: 5px solid #000000; @@ -352,7 +352,7 @@ describe('register', () => { tokens, preprocessors: ['tokens-studio'], expand: { - include: ['border', 'boxShadow'], + include: ['border', 'shadow'], }, platforms: { compose: { @@ -386,6 +386,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.* object bar { + val blur = 10px val borderColor = #000000 val borderStyle = solid val borderWidth = 5px @@ -423,12 +424,11 @@ Arial Black, Suisse Int'l, sans-serif val lineHeightsHeading = 1.1 val opacity = 0.25 val shadowBlur = 10px - val shadowBlur = 10px val shadowColor = rgba(0,0,0,0.4) + val shadowOffsetX = 0 + val shadowOffsetY = 4px val shadowSpread = 0 val shadowType = innerShadow - val shadowX = 0 - val shadowY = 4 /** You can have multiple values in a single spacing token. Read more on these: https://docs.tokens.studio/available-tokens/spacing-tokens#multi-value-spacing-tokens */ val spacingMultiValue = 8px 64px val spacingSm = 8px From ab13f30c9e0feae2d83730f73c056c2391fd58b6 Mon Sep 17 00:00:00 2001 From: jorenbroekema Date: Thu, 18 Jul 2024 21:46:16 +0200 Subject: [PATCH 2/2] fix: add lineHeight and fontWeight types to expandTypesMap --- .changeset/cool-windows-hang.md | 5 +++ src/index.ts | 4 ++ src/preprocessors/align-types.ts | 4 +- test/integration/cross-file-refs.test.ts | 13 ++++--- test/integration/expand-composition.test.ts | 39 +++++++++---------- .../tokens/expand-composition.tokens.json | 6 +-- 6 files changed, 42 insertions(+), 29 deletions(-) create mode 100644 .changeset/cool-windows-hang.md diff --git a/.changeset/cool-windows-hang.md b/.changeset/cool-windows-hang.md new file mode 100644 index 0000000..d8a3dc3 --- /dev/null +++ b/.changeset/cool-windows-hang.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': minor +--- + +Add lineHeight and fontWeight types to the expandTypesMap. Even though DTCG spec does not yet recognize them, they really are special types and marking them as such enables transforms to target them specifically. diff --git a/src/index.ts b/src/index.ts index 3b2e6f6..1628a4d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,10 @@ export const expandTypesMap = { paragraphIndent: 'dimension', textDecoration: 'other', textCase: 'other', + // even though this type does not yet exist in DTCG, it really should, since lineHeights can be both dimension or number + lineHeight: 'lineHeight', + // same as lineHeight except for fontWeight: recognized fontWeight keys (e.g. "regular") should be recognized as well as numbers + fontWeight: 'fontWeight', }, /** * boxShadow/x/y are not shadow/offsetX/offsetY here because the SD expand on global level diff --git a/src/preprocessors/align-types.ts b/src/preprocessors/align-types.ts index c17157b..eea4a67 100644 --- a/src/preprocessors/align-types.ts +++ b/src/preprocessors/align-types.ts @@ -1,4 +1,5 @@ import { DeepKeyTokenMap, SingleToken, TokenTypes } from '@tokens-studio/types'; +import { typeDtcgDelegate } from 'style-dictionary/utils'; // TODO: composition tokens props also need the same types alignments.. // nested composition tokens are out of scope. @@ -86,7 +87,8 @@ function recurse(slice: DeepKeyTokenMap | SingleToken) { } export function alignTypes(dictionary: DeepKeyTokenMap): DeepKeyTokenMap { - const copy = structuredClone(dictionary); + // pre-emptively type dtcg delegate because otherwise we cannot align types properly here + const copy = typeDtcgDelegate(structuredClone(dictionary)); recurse(copy); return copy; } diff --git a/test/integration/cross-file-refs.test.ts b/test/integration/cross-file-refs.test.ts index 6c1f88d..7221a6d 100644 --- a/test/integration/cross-file-refs.test.ts +++ b/test/integration/cross-file-refs.test.ts @@ -32,7 +32,10 @@ let dict: StyleDictionary | undefined; describe('cross file references', () => { beforeEach(async () => { cleanup(dict); - dict = await init(cfg, { withSDBuiltins: false, expand: { typography: true } }); + dict = await init(cfg, { + withSDBuiltins: false, + expand: { typography: true }, + }); }); afterEach(async () => { @@ -42,8 +45,8 @@ describe('cross file references', () => { it('supports cross file references e.g. expanding typography', async () => { const file = await promises.readFile(outputFilePath, 'utf-8'); expect(file).to.include(` - --sdTypoFontWeightWeight: 400; - --sdTypoFontWeightStyle: italic; + --sdTypoFontWeight: 400; + --sdTypoFontStyle: italic; --sdPrimaryFont: Inter; --sdFontWeight: 800; --sdLineHeight: 1.5; @@ -69,8 +72,8 @@ describe('cross file references', () => { --sdTestTypographyTextFontWeight: 700; --sdWeightWeight: 400; --sdWeightStyle: italic; - --sdTypoAliasFontWeightWeight: 400; - --sdTypoAliasFontWeightStyle: italic; + --sdTypoAliasFontWeight: 400; + --sdTypoAliasFontStyle: italic; --sdTypo3FontFamily: Inter; --sdTypo3FontWeight: 800; --sdTypo3LineHeight: 1.5; diff --git a/test/integration/expand-composition.test.ts b/test/integration/expand-composition.test.ts index e10d5e9..2075c21 100644 --- a/test/integration/expand-composition.test.ts +++ b/test/integration/expand-composition.test.ts @@ -70,9 +70,9 @@ describe('expand composition tokens', () => { --sdCompositionFontSize: 96px; --sdCompositionFontFamily: Roboto; --sdCompositionFontWeight: 700; - --sdCompositionHeaderFontFamilies: Roboto; - --sdCompositionHeaderFontSizes: 96px; - --sdCompositionHeaderFontWeights: 700; + --sdCompositionHeaderFontFamily: Roboto; + --sdCompositionHeaderFontSize: 96px; + --sdCompositionHeaderFontWeight: 700; --sdTypography: italic 800 26px/1.25 Arial; --sdFontWeightRefWeight: 800; --sdFontWeightRefStyle: italic; @@ -92,12 +92,11 @@ describe('expand composition tokens', () => { --sdCompositionFontSize: 96px; --sdCompositionFontFamily: Roboto; --sdCompositionFontWeight: 700; - --sdCompositionHeaderFontFamilies: Roboto; - --sdCompositionHeaderFontSizes: 96px; - --sdCompositionHeaderFontWeights: 700; + --sdCompositionHeaderFontFamily: Roboto; + --sdCompositionHeaderFontSize: 96px; + --sdCompositionHeaderFontWeight: 700; --sdTypographyFontFamily: Arial; - --sdTypographyFontWeightWeight: 800; - --sdTypographyFontWeightStyle: italic; + --sdTypographyFontWeight: 800; --sdTypographyLineHeight: 1.25; --sdTypographyFontSize: 26px; --sdTypographyLetterSpacing: 0rem; @@ -111,23 +110,23 @@ describe('expand composition tokens', () => { --sdBorderColor: #ffff00; --sdBorderWidth: 4px; --sdBorderStyle: solid; - --sdShadowSingleX: 0rem; - --sdShadowSingleY: 4px; --sdShadowSingleBlur: 10px; --sdShadowSingleSpread: 0rem; --sdShadowSingleColor: rgba(0, 0, 0, 0.4); --sdShadowSingleType: innerShadow; - --sdShadowDouble1X: 0rem; - --sdShadowDouble1Y: 4px; + --sdShadowSingleOffsetX: 0rem; + --sdShadowSingleOffsetY: 4px; --sdShadowDouble1Blur: 10px; --sdShadowDouble1Spread: 0rem; --sdShadowDouble1Color: rgba(0, 0, 0, 0.4); --sdShadowDouble1Type: innerShadow; - --sdShadowDouble2X: 0rem; - --sdShadowDouble2Y: 8px; + --sdShadowDouble1OffsetX: 0rem; + --sdShadowDouble1OffsetY: 4px; --sdShadowDouble2Blur: 12px; --sdShadowDouble2Spread: 5px; - --sdShadowDouble2Color: rgba(0, 0, 0, 0.4);`, + --sdShadowDouble2Color: rgba(0, 0, 0, 0.4); + --sdShadowDouble2OffsetX: 0rem; + --sdShadowDouble2OffsetY: 8px;`, ); }); @@ -162,17 +161,17 @@ describe('expand composition tokens', () => { const file = await promises.readFile(outputFilePath, 'utf-8'); expect(file).to.include( ` - --sdDeepRefShadowMulti1X: 0rem; - --sdDeepRefShadowMulti1Y: 4px; --sdDeepRefShadowMulti1Blur: 10px; --sdDeepRefShadowMulti1Spread: 0rem; --sdDeepRefShadowMulti1Color: rgba(0, 0, 0, 0.4); --sdDeepRefShadowMulti1Type: innerShadow; - --sdDeepRefShadowMulti2X: 0rem; - --sdDeepRefShadowMulti2Y: 8px; + --sdDeepRefShadowMulti1OffsetX: 0rem; + --sdDeepRefShadowMulti1OffsetY: 4px; --sdDeepRefShadowMulti2Blur: 12px; --sdDeepRefShadowMulti2Spread: 5px; - --sdDeepRefShadowMulti2Color: rgba(0, 0, 0, 0.4)`, + --sdDeepRefShadowMulti2Color: rgba(0, 0, 0, 0.4); + --sdDeepRefShadowMulti2OffsetX: 0rem; + --sdDeepRefShadowMulti2OffsetY: 8px;`, ); }); }); diff --git a/test/integration/tokens/expand-composition.tokens.json b/test/integration/tokens/expand-composition.tokens.json index fd1d13b..7510544 100644 --- a/test/integration/tokens/expand-composition.tokens.json +++ b/test/integration/tokens/expand-composition.tokens.json @@ -22,9 +22,9 @@ }, "header": { "value": { - "fontFamilies": "{composition.fontFamily}", - "fontSizes": "96", - "fontWeights": "{composition.fontWeight}" + "fontFamily": "{composition.fontFamily}", + "fontSize": "96", + "fontWeight": "{composition.fontWeight}" }, "type": "composition" }