From e879b6672f99ae819cb1e40894e3e5dced1a05f9 Mon Sep 17 00:00:00 2001 From: jorenbroekema Date: Mon, 8 Jan 2024 17:48:24 +0100 Subject: [PATCH] BREAKING: use preprocessors if the passed SD is prerelease.2 or higher --- .changeset/six-kiwis-drive.md | 5 ++ README.md | 2 + package-lock.json | 38 ++++++-------- package.json | 2 +- src/parsers/add-font-styles.ts | 10 +++- src/parsers/expand-composites.ts | 10 +++- src/registerTransforms.ts | 29 ++++++++--- test/integration/cross-file-refs.test.ts | 51 +++++++++++++++++++ .../tokens/cross-file-refs-1.tokens.json | 8 +++ .../tokens/cross-file-refs-2.tokens.json | 6 +++ .../tokens/cross-file-refs-3.tokens.json | 6 +++ 11 files changed, 134 insertions(+), 33 deletions(-) create mode 100644 .changeset/six-kiwis-drive.md create mode 100644 test/integration/cross-file-refs.test.ts create mode 100644 test/integration/tokens/cross-file-refs-1.tokens.json create mode 100644 test/integration/tokens/cross-file-refs-2.tokens.json create mode 100644 test/integration/tokens/cross-file-refs-3.tokens.json diff --git a/.changeset/six-kiwis-drive.md b/.changeset/six-kiwis-drive.md new file mode 100644 index 0000000..fd29db6 --- /dev/null +++ b/.changeset/six-kiwis-drive.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': minor +--- + +Will now use preprocessors instead of parsers when user Style-Dictionary is v4.0.0-prerelease.2 or higher. Fixes an issue with multi-file references not being resolvable when running composite token expansion or add font style utilities. diff --git a/README.md b/README.md index c3ff4d8..77bd084 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,8 @@ registerTransforms(StyleDictionary, { expand: { composition: false, typography: true, + // Note: when using Style-Dictionary v4.0.0-prerelease.2 or higher, filePath no longer gets passed + // as an argument, because preprocessors work on the full dictionary rather than per file (parsers) border: (token, filePath) => token.value.width !== 0 && filePath.startsWith(path.resolve('tokens/core')), shadow: false, diff --git a/package-lock.json b/package-lock.json index 73a5c92..6d1b8c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "expr-eval": "^2.0.2", "is-mergeable-object": "^1.1.1", "postcss-calc-ast-parser": "^0.1.4", - "style-dictionary": "github:amzn/style-dictionary#types" + "style-dictionary": "4.0.0-prerelease.7" }, "devDependencies": { "@changesets/cli": "^2.26.0", @@ -126,14 +126,14 @@ } }, "node_modules/@bundled-es-modules/glob": { - "version": "10.3.5", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/glob/-/glob-10.3.5.tgz", - "integrity": "sha512-Jn0WAgPKHfOm81mmF9RpzjTr/rdsOESoW9MVvaBBkXDhPhmeeqBugzfXlF0y56atlicKERqrXvW3D6jTQHU7Dw==", + "version": "10.3.13", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/glob/-/glob-10.3.13.tgz", + "integrity": "sha512-eK+st/vwMmQy0pVvHLa2nzsS+p6NkNVR34e8qfiuzpzS1he4bMU3ODl0gbyv4r9INq5x41GqvRmFr8PtNw4yRA==", "hasInstallScript": true, "dependencies": { "buffer": "^6.0.3", "events": "^3.3.0", - "glob": "^10.3.3", + "glob": "^10.3.10", "patch-package": "^8.0.0", "path": "^0.12.7", "stream": "^0.0.2", @@ -202,14 +202,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/@bundled-es-modules/path-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/path-browserify/-/path-browserify-1.0.2.tgz", - "integrity": "sha512-wC1f5y1etCEOO/3zUKlro5JCDKqZ3TAvjXUu9lSpaYHiKTyn0Iwlyfum789+V+DIAH32AJBb3JsWPalDvpvi6Q==", - "dependencies": { - "path-browserify": "^1.0.1" - } - }, "node_modules/@changesets/apply-release-plan": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.3.tgz", @@ -7932,11 +7924,6 @@ "util": "^0.10.3" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8008,6 +7995,11 @@ "node": ">=8" } }, + "node_modules/path-unified": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-unified/-/path-unified-0.1.0.tgz", + "integrity": "sha512-/Oaz9ZJforrkmFrwkR/AcvjVsCAwGSJHO0X6O6ISj8YeFbATjIEBXLDcZfnK3MO4uvCBrJTdVIxdOc79PMqSdg==" + }, "node_modules/path/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -9654,21 +9646,21 @@ } }, "node_modules/style-dictionary": { - "version": "4.0.0-prerelease.4", - "resolved": "git+ssh://git@github.com/amzn/style-dictionary.git#38d0e08d738e6d6f1e14c1b0bc80ceee0ec2a3b6", + "version": "4.0.0-prerelease.7", + "resolved": "https://registry.npmjs.org/style-dictionary/-/style-dictionary-4.0.0-prerelease.7.tgz", + "integrity": "sha512-K5qpS1l/NUH1eV8+XCKVtXe5yrllHz0kbQZKnjcz0+5B8i+d4fhS+niuMJfKHjaE3v6WW1zfSKsXwU0Kr8AgZg==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { "@bundled-es-modules/deepmerge": "^4.3.1", - "@bundled-es-modules/glob": "^10.3.5", + "@bundled-es-modules/glob": "^10.3.13", "@bundled-es-modules/memfs": "^4.2.3", - "@bundled-es-modules/path-browserify": "^1.0.2", "chalk": "^5.3.0", "change-case": "^5.3.0", "commander": "^8.3.0", "is-plain-obj": "^4.1.0", "json5": "^2.2.2", "lodash-es": "^4.17.21", + "path-unified": "^0.1.0", "tinycolor2": "^1.6.0" }, "bin": { diff --git a/package.json b/package.json index 8420365..95b5517 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "expr-eval": "^2.0.2", "is-mergeable-object": "^1.1.1", "postcss-calc-ast-parser": "^0.1.4", - "style-dictionary": "github:amzn/style-dictionary#types" + "style-dictionary": "4.0.0-prerelease.7" }, "devDependencies": { "@changesets/cli": "^2.26.0", diff --git a/src/parsers/add-font-styles.ts b/src/parsers/add-font-styles.ts index 9c54846..c02081a 100644 --- a/src/parsers/add-font-styles.ts +++ b/src/parsers/add-font-styles.ts @@ -20,7 +20,15 @@ function recurse( } let fontWeight = value.fontWeight; if (usesReferences(fontWeight)) { - fontWeight = `${resolveReferences(fontWeight, copy) ?? fontWeight}`; + try { + const resolved = resolveReferences(fontWeight, copy); + if (resolved) { + fontWeight = `${resolved}`; + } + } catch (e) { + // we don't want to throw a fatal error, we'll just keep the ref as is + console.error(e); + } } // cast because fontStyle is a prop we will add ourselves const tokenValue = value as TokenTypographyValue & { fontStyle: string }; diff --git a/src/parsers/expand-composites.ts b/src/parsers/expand-composites.ts index e9e1386..3f5598a 100644 --- a/src/parsers/expand-composites.ts +++ b/src/parsers/expand-composites.ts @@ -111,7 +111,15 @@ function recurse( if (expand) { // if token uses a reference, attempt to resolve it if (typeof token.value === 'string' && usesReferences(token.value)) { - token.value = resolveReferences(token.value, copy as DesignTokens) ?? token.value; + try { + const resolved = resolveReferences(token.value, copy as DesignTokens); + if (resolved) { + token.value = resolved; + } + } catch (e) { + // we don't want to throw a fatal error, expansion can still occur, just with the reference kept as is + console.error(e); + } // If every key of the result (object) is a number, the ref value is a multi-value, which means TokenBoxshadowValue[] if ( typeof token.value === 'object' && diff --git a/src/registerTransforms.ts b/src/registerTransforms.ts index 8378386..42cd2fe 100644 --- a/src/registerTransforms.ts +++ b/src/registerTransforms.ts @@ -40,17 +40,32 @@ export async function registerTransforms( sd: typeof StyleDictionary, transformOpts?: TransformOptions, ) { + // >= 4.0.0-prelease.2, once SD reaches full release: 4.0.0, sd-transforms will get + // a breaking release where we always use preprocessors + // and are no longer backwards compatible with sd 3 or lower + const supportsPreprocessors = + sd.VERSION.match(/4\.0\.0(-prerelease\.([2-9]|[0-9]{2}))/g) !== null; // Allow completely disabling the registering of this parser // in case people want to combine the expandComposites() utility with their own parser and prevent conflicts if (transformOpts?.expand !== false) { // expand composition tokens, typography, border, shadow (latter 3 conditionally, as opt-in) - sd.registerParser({ - pattern: /\.json$/, - parse: ({ filePath, contents }) => { - const tokens = JSON.parse(contents); - return parseTokens(tokens, transformOpts, filePath) as DesignTokens; - }, - }); + if (supportsPreprocessors) { + sd.registerPreprocessor({ + name: 'sd-transforms-preprocessors', + preprocessor: dictionary => { + console.log('halllelujah'); + return parseTokens(dictionary, transformOpts) as DesignTokens; + }, + }); + } else { + sd.registerParser({ + pattern: /\.json$/, + parse: ({ filePath, contents }) => { + const tokens = JSON.parse(contents); + return parseTokens(tokens, transformOpts, filePath) as DesignTokens; + }, + }); + } } sd.registerTransform({ diff --git a/test/integration/cross-file-refs.test.ts b/test/integration/cross-file-refs.test.ts new file mode 100644 index 0000000..5486f82 --- /dev/null +++ b/test/integration/cross-file-refs.test.ts @@ -0,0 +1,51 @@ +import type StyleDictionary from 'style-dictionary'; +import { expect } from '@esm-bundle/chai'; +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/cross-file-refs-*.tokens.json'], + platforms: { + css: { + transformGroup: 'tokens-studio', + prefix: 'sd', + buildPath: outputDir, + files: [ + { + destination: outputFileName, + format: 'css/variables', + }, + ], + }, + }, +}; + +let dict: StyleDictionary | undefined; + +describe('cross file references', () => { + beforeEach(async () => { + cleanup(dict); + dict = await init(cfg, { expand: { typography: true } }); + }); + + afterEach(async () => { + await cleanup(dict); + }); + + it('supports cross file references e.g. expanding typography', async () => { + const file = await promises.readFile(outputFilePath, 'utf-8'); + console.log(file); + expect(file).to.include(` + --sdTypoFontWeight: 400; + --sdTypoFontStyle: italic; + --sdWeight: 400 italic; + --sdTypoAliasFontWeight: 400; + --sdTypoAliasFontStyle: italic; +`); + }); +}); diff --git a/test/integration/tokens/cross-file-refs-1.tokens.json b/test/integration/tokens/cross-file-refs-1.tokens.json new file mode 100644 index 0000000..c06c26c --- /dev/null +++ b/test/integration/tokens/cross-file-refs-1.tokens.json @@ -0,0 +1,8 @@ +{ + "typo": { + "value": { + "fontWeight": "{weight}" + }, + "type": "typography" + } +} diff --git a/test/integration/tokens/cross-file-refs-2.tokens.json b/test/integration/tokens/cross-file-refs-2.tokens.json new file mode 100644 index 0000000..066ed3a --- /dev/null +++ b/test/integration/tokens/cross-file-refs-2.tokens.json @@ -0,0 +1,6 @@ +{ + "weight": { + "value": "Regular italic", + "type": "fontWeights" + } +} diff --git a/test/integration/tokens/cross-file-refs-3.tokens.json b/test/integration/tokens/cross-file-refs-3.tokens.json new file mode 100644 index 0000000..df10d69 --- /dev/null +++ b/test/integration/tokens/cross-file-refs-3.tokens.json @@ -0,0 +1,6 @@ +{ + "typo-alias": { + "value": "{typo}", + "type": "typography" + } +}