diff --git a/.changeset/little-mice-pump.md b/.changeset/little-mice-pump.md new file mode 100644 index 000000000..e2d78ef53 --- /dev/null +++ b/.changeset/little-mice-pump.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': patch +--- + +Pass SD options to fileheaders and filters, to make it easier to use and adjust according to config or options like usesDTCG. diff --git a/.changeset/ten-numbers-lay.md b/.changeset/ten-numbers-lay.md new file mode 100644 index 000000000..c09817320 --- /dev/null +++ b/.changeset/ten-numbers-lay.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': patch +--- + +Add unfilteredAllTokens property in dictionary object for formats, which is an unfiltered version of allTokens property, or a flattened version of the unfilteredTokens property. diff --git a/.eslintrc.json b/.eslintrc.json index c897b1813..a5cb810b1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -15,7 +15,6 @@ "rules": { "no-unused-vars": 1, "no-console": ["error", { "allow": ["warn", "error"] }], - "mocha/no-skipped-tests": "warn", "mocha/no-exclusive-tests": "error", "no-var": "error" }, diff --git a/__integration__/__snapshots__/customFormats.test.snap.js b/__integration__/__snapshots__/customFormats.test.snap.js index c85748c65..060c21614 100644 --- a/__integration__/__snapshots__/customFormats.test.snap.js +++ b/__integration__/__snapshots__/customFormats.test.snap.js @@ -269,7 +269,93 @@ snapshots["integration custom formats inline custom with new args should match s } } } - } + }, + "unfilteredAllTokens": [ + { + "value": "0.5rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 0.5, + "type": "dimension" + }, + "name": "SizePaddingSmall", + "attributes": { + "category": "size", + "type": "padding", + "item": "small" + }, + "path": [ + "size", + "padding", + "small" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingMedium", + "attributes": { + "category": "size", + "type": "padding", + "item": "medium" + }, + "path": [ + "size", + "padding", + "medium" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingLarge", + "attributes": { + "category": "size", + "type": "padding", + "item": "large" + }, + "path": [ + "size", + "padding", + "large" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingXl", + "attributes": { + "category": "size", + "type": "padding", + "item": "xl" + }, + "path": [ + "size", + "padding", + "xl" + ] + } + ] }, "allTokens": [ { @@ -497,9 +583,373 @@ snapshots["integration custom formats inline custom with new args should match s "destination": "inlineCustomFormatWithNewArgs.json" }, "options": { + "hooks": { + "parsers": {}, + "preprocessors": {}, + "transformGroups": { + "web": [ + "attribute/cti", + "name/kebab", + "size/px", + "color/css" + ], + "js": [ + "attribute/cti", + "name/pascal", + "size/rem", + "color/hex" + ], + "scss": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/css", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "css": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/css", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "less": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/hex", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "html": [ + "attribute/cti", + "attribute/color", + "name/human" + ], + "android": [ + "attribute/cti", + "name/snake", + "color/hex8android", + "size/remToSp", + "size/remToDp" + ], + "compose": [ + "attribute/cti", + "name/camel", + "color/composeColor", + "size/compose/em", + "size/compose/remToSp", + "size/compose/remToDp" + ], + "ios": [ + "attribute/cti", + "name/pascal", + "color/UIColor", + "content/objC/literal", + "asset/objC/literal", + "size/remToPt" + ], + "ios-swift": [ + "attribute/cti", + "name/camel", + "color/UIColorSwift", + "content/swift/literal", + "asset/swift/literal", + "size/swift/remToCGFloat" + ], + "ios-swift-separate": [ + "attribute/cti", + "name/camel", + "color/UIColorSwift", + "content/swift/literal", + "asset/swift/literal", + "size/swift/remToCGFloat" + ], + "assets": [ + "attribute/cti" + ], + "flutter": [ + "attribute/cti", + "name/camel", + "color/hex8flutter", + "size/flutter/remToDouble", + "content/flutter/literal", + "asset/flutter/literal" + ], + "flutter-separate": [ + "attribute/cti", + "name/camel", + "color/hex8flutter", + "size/flutter/remToDouble", + "content/flutter/literal", + "asset/flutter/literal" + ], + "react-native": [ + "name/camel", + "color/css", + "size/object" + ] + }, + "transforms": { + "attribute/cti": { + "type": "attribute" + }, + "attribute/color": { + "type": "attribute" + }, + "name/human": { + "type": "name" + }, + "name/camel": { + "type": "name" + }, + "name/kebab": { + "type": "name" + }, + "name/snake": { + "type": "name" + }, + "name/constant": { + "type": "name" + }, + "name/pascal": { + "type": "name" + }, + "color/rgb": { + "type": "value" + }, + "color/hsl": { + "type": "value" + }, + "color/hsl-4": { + "type": "value" + }, + "color/hex": { + "type": "value" + }, + "color/hex8": { + "type": "value" + }, + "color/hex8android": { + "type": "value" + }, + "color/composeColor": { + "type": "value" + }, + "color/UIColor": { + "type": "value" + }, + "color/UIColorSwift": { + "type": "value" + }, + "color/ColorSwiftUI": { + "type": "value" + }, + "color/css": { + "type": "value" + }, + "color/sketch": { + "type": "value" + }, + "size/sp": { + "type": "value" + }, + "size/dp": { + "type": "value" + }, + "size/object": { + "type": "value" + }, + "size/remToSp": { + "type": "value" + }, + "size/remToDp": { + "type": "value" + }, + "size/px": { + "type": "value" + }, + "size/rem": { + "type": "value" + }, + "size/remToPt": { + "type": "value" + }, + "size/compose/remToSp": { + "type": "value" + }, + "size/compose/remToDp": { + "type": "value" + }, + "size/compose/em": { + "type": "value" + }, + "size/swift/remToCGFloat": { + "type": "value" + }, + "size/remToPx": { + "type": "value" + }, + "size/pxToRem": { + "type": "value" + }, + "html/icon": { + "type": "value" + }, + "content/quote": { + "type": "value" + }, + "content/objC/literal": { + "type": "value" + }, + "content/swift/literal": { + "type": "value" + }, + "time/seconds": { + "type": "value" + }, + "fontFamily/css": { + "type": "value" + }, + "cubicBezier/css": { + "type": "value" + }, + "strokeStyle/css/shorthand": { + "type": "value", + "transitive": true + }, + "border/css/shorthand": { + "type": "value", + "transitive": true + }, + "typography/css/shorthand": { + "type": "value", + "transitive": true + }, + "transition/css/shorthand": { + "type": "value", + "transitive": true + }, + "shadow/css/shorthand": { + "type": "value", + "transitive": true + }, + "asset/url": { + "type": "value" + }, + "asset/base64": { + "type": "value" + }, + "asset/path": { + "type": "value" + }, + "asset/objC/literal": { + "type": "value" + }, + "asset/swift/literal": { + "type": "value" + }, + "color/hex8flutter": { + "type": "value" + }, + "content/flutter/literal": { + "type": "value" + }, + "asset/flutter/literal": { + "type": "value" + }, + "size/flutter/remToDouble": { + "type": "value" + } + }, + "formats": {}, + "fileHeaders": {}, + "filters": {}, + "actions": { + "android/copyImages": {}, + "copy_assets": {} + } + }, + "source": [ + "__integration__/tokens/size/padding.json" + ], + "platforms": { + "inlineCustomFormats": { + "transformGroup": "js", + "buildPath": "__integration__/build/", + "options": { + "otherOption": "platform option" + }, + "files": [ + { + "destination": "inlineCustomFormatWithOldArgs.json", + "format": "inlineCustomFormatWithOldArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + }, + { + "destination": "inlineCustomFormatWithNewArgs.json", + "format": "inlineCustomFormatWithNewArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + } + ] + }, + "customFormats": { + "transformGroup": "js", + "buildPath": "__integration__/build/", + "options": { + "otherOption": "platform option" + }, + "files": [ + { + "destination": "registerCustomFormatWithNewArgs.json", + "format": "registerCustomFormatWithNewArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + } + ] + } + }, + "log": { + "warnings": "warn", + "verbosity": "default" + }, + "usesDtcg": false, "otherOption": "Test", - "showFileHeader": true, - "usesDtcg": false + "showFileHeader": true } }`; /* end snapshot integration custom formats inline custom with new args should match snapshot */ @@ -772,7 +1222,93 @@ snapshots["integration custom formats register custom format with new args shoul } } } - } + }, + "unfilteredAllTokens": [ + { + "value": "0.5rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 0.5, + "type": "dimension" + }, + "name": "SizePaddingSmall", + "attributes": { + "category": "size", + "type": "padding", + "item": "small" + }, + "path": [ + "size", + "padding", + "small" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingMedium", + "attributes": { + "category": "size", + "type": "padding", + "item": "medium" + }, + "path": [ + "size", + "padding", + "medium" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingLarge", + "attributes": { + "category": "size", + "type": "padding", + "item": "large" + }, + "path": [ + "size", + "padding", + "large" + ] + }, + { + "value": "1rem", + "type": "dimension", + "filePath": "__integration__/tokens/size/padding.json", + "isSource": true, + "original": { + "value": 1, + "type": "dimension" + }, + "name": "SizePaddingXl", + "attributes": { + "category": "size", + "type": "padding", + "item": "xl" + }, + "path": [ + "size", + "padding", + "xl" + ] + } + ] }, "allTokens": [ { @@ -993,9 +1529,373 @@ snapshots["integration custom formats register custom format with new args shoul "destination": "registerCustomFormatWithNewArgs.json" }, "options": { + "hooks": { + "parsers": {}, + "preprocessors": {}, + "transformGroups": { + "web": [ + "attribute/cti", + "name/kebab", + "size/px", + "color/css" + ], + "js": [ + "attribute/cti", + "name/pascal", + "size/rem", + "color/hex" + ], + "scss": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/css", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "css": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/css", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "less": [ + "attribute/cti", + "name/kebab", + "time/seconds", + "html/icon", + "size/rem", + "color/hex", + "asset/url", + "fontFamily/css", + "cubicBezier/css", + "strokeStyle/css/shorthand", + "border/css/shorthand", + "typography/css/shorthand", + "transition/css/shorthand", + "shadow/css/shorthand" + ], + "html": [ + "attribute/cti", + "attribute/color", + "name/human" + ], + "android": [ + "attribute/cti", + "name/snake", + "color/hex8android", + "size/remToSp", + "size/remToDp" + ], + "compose": [ + "attribute/cti", + "name/camel", + "color/composeColor", + "size/compose/em", + "size/compose/remToSp", + "size/compose/remToDp" + ], + "ios": [ + "attribute/cti", + "name/pascal", + "color/UIColor", + "content/objC/literal", + "asset/objC/literal", + "size/remToPt" + ], + "ios-swift": [ + "attribute/cti", + "name/camel", + "color/UIColorSwift", + "content/swift/literal", + "asset/swift/literal", + "size/swift/remToCGFloat" + ], + "ios-swift-separate": [ + "attribute/cti", + "name/camel", + "color/UIColorSwift", + "content/swift/literal", + "asset/swift/literal", + "size/swift/remToCGFloat" + ], + "assets": [ + "attribute/cti" + ], + "flutter": [ + "attribute/cti", + "name/camel", + "color/hex8flutter", + "size/flutter/remToDouble", + "content/flutter/literal", + "asset/flutter/literal" + ], + "flutter-separate": [ + "attribute/cti", + "name/camel", + "color/hex8flutter", + "size/flutter/remToDouble", + "content/flutter/literal", + "asset/flutter/literal" + ], + "react-native": [ + "name/camel", + "color/css", + "size/object" + ] + }, + "transforms": { + "attribute/cti": { + "type": "attribute" + }, + "attribute/color": { + "type": "attribute" + }, + "name/human": { + "type": "name" + }, + "name/camel": { + "type": "name" + }, + "name/kebab": { + "type": "name" + }, + "name/snake": { + "type": "name" + }, + "name/constant": { + "type": "name" + }, + "name/pascal": { + "type": "name" + }, + "color/rgb": { + "type": "value" + }, + "color/hsl": { + "type": "value" + }, + "color/hsl-4": { + "type": "value" + }, + "color/hex": { + "type": "value" + }, + "color/hex8": { + "type": "value" + }, + "color/hex8android": { + "type": "value" + }, + "color/composeColor": { + "type": "value" + }, + "color/UIColor": { + "type": "value" + }, + "color/UIColorSwift": { + "type": "value" + }, + "color/ColorSwiftUI": { + "type": "value" + }, + "color/css": { + "type": "value" + }, + "color/sketch": { + "type": "value" + }, + "size/sp": { + "type": "value" + }, + "size/dp": { + "type": "value" + }, + "size/object": { + "type": "value" + }, + "size/remToSp": { + "type": "value" + }, + "size/remToDp": { + "type": "value" + }, + "size/px": { + "type": "value" + }, + "size/rem": { + "type": "value" + }, + "size/remToPt": { + "type": "value" + }, + "size/compose/remToSp": { + "type": "value" + }, + "size/compose/remToDp": { + "type": "value" + }, + "size/compose/em": { + "type": "value" + }, + "size/swift/remToCGFloat": { + "type": "value" + }, + "size/remToPx": { + "type": "value" + }, + "size/pxToRem": { + "type": "value" + }, + "html/icon": { + "type": "value" + }, + "content/quote": { + "type": "value" + }, + "content/objC/literal": { + "type": "value" + }, + "content/swift/literal": { + "type": "value" + }, + "time/seconds": { + "type": "value" + }, + "fontFamily/css": { + "type": "value" + }, + "cubicBezier/css": { + "type": "value" + }, + "strokeStyle/css/shorthand": { + "type": "value", + "transitive": true + }, + "border/css/shorthand": { + "type": "value", + "transitive": true + }, + "typography/css/shorthand": { + "type": "value", + "transitive": true + }, + "transition/css/shorthand": { + "type": "value", + "transitive": true + }, + "shadow/css/shorthand": { + "type": "value", + "transitive": true + }, + "asset/url": { + "type": "value" + }, + "asset/base64": { + "type": "value" + }, + "asset/path": { + "type": "value" + }, + "asset/objC/literal": { + "type": "value" + }, + "asset/swift/literal": { + "type": "value" + }, + "color/hex8flutter": { + "type": "value" + }, + "content/flutter/literal": { + "type": "value" + }, + "asset/flutter/literal": { + "type": "value" + }, + "size/flutter/remToDouble": { + "type": "value" + } + }, + "formats": {}, + "fileHeaders": {}, + "filters": {}, + "actions": { + "android/copyImages": {}, + "copy_assets": {} + } + }, + "source": [ + "__integration__/tokens/size/padding.json" + ], + "platforms": { + "inlineCustomFormats": { + "transformGroup": "js", + "buildPath": "__integration__/build/", + "options": { + "otherOption": "platform option" + }, + "files": [ + { + "destination": "inlineCustomFormatWithOldArgs.json", + "format": "inlineCustomFormatWithOldArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + }, + { + "destination": "inlineCustomFormatWithNewArgs.json", + "format": "inlineCustomFormatWithNewArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + } + ] + }, + "customFormats": { + "transformGroup": "js", + "buildPath": "__integration__/build/", + "options": { + "otherOption": "platform option" + }, + "files": [ + { + "destination": "registerCustomFormatWithNewArgs.json", + "format": "registerCustomFormatWithNewArgs", + "options": { + "showFileHeader": true, + "otherOption": "Test" + } + } + ] + } + }, + "log": { + "warnings": "warn", + "verbosity": "default" + }, + "usesDtcg": false, "otherOption": "Test", - "showFileHeader": true, - "usesDtcg": false + "showFileHeader": true } }`; /* end snapshot integration custom formats register custom format with new args should match snapshot */ diff --git a/docs/src/content/docs/reference/Hooks/Formats/index.md b/docs/src/content/docs/reference/Hooks/Formats/index.md index 3c4b07fa6..1d80f4fe5 100644 --- a/docs/src/content/docs/reference/Hooks/Formats/index.md +++ b/docs/src/content/docs/reference/Hooks/Formats/index.md @@ -269,16 +269,17 @@ You can create custom formats using the [`registerFormat`](/reference/api#regist The format function that is called when Style Dictionary builds files. -| Param | Type | Description | -| ---------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | -| `args` | `Object` | A single argument to support named parameters and destructuring. | -| `args.dictionary` | `Dictionary` | Transformed Dictionary object containing allTokens, tokens and unfilteredTokens. | -| `args.dictionary.allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | -| `args.dictionary.tokens` | `TransformedTokens` | All tokens, still in unflattened object format. | -| `args.dictionary.unfilteredTokens` | `TransformedTokens` | All tokens, still in unflattened object format, including tokens that were filtered out by filters. | -| `args.platform` | `Platform` | [Platform config](/reference/config#platform) | -| `args.file` | `File` | [File config](/reference/config#file) | -| `args.options` | `Object` | Merged object with SD [Config](/reference/config#properties) & [FormatOptions](#format-configuration) | +| Param | Type | Description | +| ------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | +| `args` | `Object` | A single argument to support named parameters and destructuring. | +| `args.dictionary` | `Dictionary` | Transformed Dictionary object containing allTokens, tokens and unfilteredTokens. | +| `args.dictionary.allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | +| `args.dictionary.tokens` | `TransformedTokens` | All tokens, still in unflattened object format. | +| `args.dictionary.unfilteredAllTokens` | `TransformedToken[]` | Flattened array of all tokens, including tokens that were filtered out by filters. | +| `args.dictionary.unfilteredTokens` | `TransformedTokens` | All tokens, still in unflattened object format, including tokens that were filtered out by filters. | +| `args.platform` | `Platform` | [Platform config](/reference/config#platform) | +| `args.file` | `File` | [File config](/reference/config#file) | +| `args.options` | `Object` | Merged object with SD [Config](/reference/config#properties) & [FormatOptions](#format-configuration) | Example: diff --git a/docs/src/content/docs/reference/Hooks/Transforms/index.md b/docs/src/content/docs/reference/Hooks/Transforms/index.md index 96906fea6..8fa28aed8 100644 --- a/docs/src/content/docs/reference/Hooks/Transforms/index.md +++ b/docs/src/content/docs/reference/Hooks/Transforms/index.md @@ -60,7 +60,7 @@ StyleDictionary.registerTransform({ type: `value`, transitive: true, name: `myTransitiveTransform`, - filter: (token) => {}, + filter: (token, options) => {}, transform: (token) => { // token.value will be resolved and transformed at this point }, diff --git a/docs/src/content/docs/reference/Hooks/filters.md b/docs/src/content/docs/reference/Hooks/filters.md index 11c20421a..c985a6d2b 100644 --- a/docs/src/content/docs/reference/Hooks/filters.md +++ b/docs/src/content/docs/reference/Hooks/filters.md @@ -16,13 +16,13 @@ Common use cases for filtering are: A filter is an object with two props: - `name`: the name of the filter -- `filter`: a callback function that receives the `token` as argument and returns a boolean, `true` to include the token, `false` to exclude/filter it out. Can also be an async function. +- `filter`: a callback function that receives the `token` as argument and returns a boolean, `true` to include the token, `false` to exclude/filter it out. Can also be an async function. Also has a second argument with the Style Dictionary options, which also contains the `tokens` object, `usesDTCG` option, etc. ```javascript title="my-filter.js" const myFilter = { name: 'my-filter', // async is optional - filter: async (token) => { + filter: async (token, options) => { return !token.filePath.endsWith('core.json'); }, }; @@ -117,7 +117,7 @@ export default { export default { hooks: { filters: { - 'no-colors': (token) => { + 'no-colors': (token, options) => { return token.type !== 'color'; }, }, diff --git a/examples/advanced/create-react-native-app/src/App.js b/examples/advanced/create-react-native-app/src/App.js index 78a53bccc..a95e034df 100644 --- a/examples/advanced/create-react-native-app/src/App.js +++ b/examples/advanced/create-react-native-app/src/App.js @@ -8,6 +8,7 @@ export default function App() { Testing! Testing! All done! + {/* eslint-disable-next-line react/style-prop-object */} ); diff --git a/lib/StyleDictionary.js b/lib/StyleDictionary.js index 9909ea414..b8511044a 100644 --- a/lib/StyleDictionary.js +++ b/lib/StyleDictionary.js @@ -140,6 +140,8 @@ export default class StyleDictionary extends Register { * @type {TransformedTokens} */ this.unfilteredTokens = {}; + /** @type {TransformedToken[]} */ + this.unfilteredAllTokens = []; this.hasInitialized = new Promise((resolve) => { this.hasInitializedResolve = resolve; diff --git a/lib/buildFile.js b/lib/buildFile.js index 6d766c057..711fa2ae6 100644 --- a/lib/buildFile.js +++ b/lib/buildFile.js @@ -15,6 +15,7 @@ import { dirname } from 'path-unified'; import chalk from 'chalk'; import { fs } from 'style-dictionary/fs'; import filterTokens from './filterTokens.js'; +import flattenTokens from './utils/flattenTokens.js'; import GroupMessages, { verbosityInfo } from './utils/groupMessages.js'; import createFormatArgs from './utils/createFormatArgs.js'; @@ -73,6 +74,7 @@ export default async function buildFile(file, platform = {}, dictionary, options allTokens: filteredTokens.allTokens, // keep the unfiltered tokens object for reference resolution unfilteredTokens: dictionary.tokens, + unfilteredAllTokens: flattenTokens(dictionary.tokens, options.usesDtcg), }); // if tokens object is empty, return without creating a file diff --git a/lib/common/formatHelpers/fileHeader.js b/lib/common/formatHelpers/fileHeader.js index a63da210b..52d8d0d57 100644 --- a/lib/common/formatHelpers/fileHeader.js +++ b/lib/common/formatHelpers/fileHeader.js @@ -15,6 +15,7 @@ * @typedef {import('../../../types/File.d.ts').File} File * @typedef {import('../../../types/File.d.ts').FileHeader} FileHeader * @typedef {import('../../../types/File.d.ts').FormattingOptions} Formatting + * @typedef {import('../../../types/Config.d.ts').Config} Config */ const lineSeparator = `\n`; @@ -34,10 +35,11 @@ const defaultFormatting = { * default file header. * @memberof module:formatHelpers * @name fileHeader - * @param {Object} options - * @param {File} [options.file] - The file object that is passed to the format. - * @param {'short' | 'xml' | 'long'} [options.commentStyle] - The only options are 'short', 'xml' and 'long', which will use the // or \ or \/\* style comments respectively. Default fallback is 'long'. - * @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, and footer. + * @param {Object} opts + * @param {File} [opts.file] - The file object that is passed to the format. + * @param {'short' | 'xml' | 'long'} [opts.commentStyle] - The only options are 'short', 'xml' and 'long', which will use the // or \ or \/\* style comments respectively. Default fallback is 'long'. + * @param {Formatting} [opts.formatting] - Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, and footer. + * @param {Config} [opts.options] * @returns {Promise} * @example * ```js @@ -51,7 +53,7 @@ const defaultFormatting = { * }); * ``` */ -export default async function fileHeader({ file, commentStyle, formatting = {} }) { +export default async function fileHeader({ file, commentStyle, formatting = {}, options = {} }) { // showFileHeader is true by default let showFileHeader = true; if (typeof file?.options?.showFileHeader !== 'undefined') { @@ -91,7 +93,7 @@ export default async function fileHeader({ file, commentStyle, formatting = {} } footer = `${lineSeparator}-->`; } - const headerContent = await fn(defaultHeader); + const headerContent = await fn(defaultHeader, options); return `${header}${headerContent .map(/** @param {string} line */ (line) => `${prefix}${line}`) diff --git a/lib/common/formats.js b/lib/common/formats.js index 0734654b1..4e496fb73 100644 --- a/lib/common/formats.js +++ b/lib/common/formats.js @@ -93,6 +93,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -131,6 +132,7 @@ const formats = { file, commentStyle: 'long', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return scssMapFlat({ allTokens, options, header }); }, @@ -164,6 +166,7 @@ const formats = { file, commentStyle: 'long', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( '\n' + @@ -200,6 +203,7 @@ const formats = { file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -232,6 +236,7 @@ const formats = { file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return header + iconsWithPrefix('$', dictionary.allTokens, options, platform); }, @@ -253,6 +258,7 @@ const formats = { file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -278,6 +284,7 @@ const formats = { file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return header + iconsWithPrefix('@', dictionary.allTokens, options, platform); }, @@ -300,6 +307,7 @@ const formats = { file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -331,6 +339,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return header + 'module.exports = ' + JSON.stringify(dictionary.tokens, null, 2) + ';\n'; }, @@ -352,6 +361,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -393,6 +403,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -440,6 +451,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -500,6 +512,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -556,6 +569,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return ( header + @@ -667,6 +681,7 @@ const formats = { const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); const output = header + @@ -711,6 +726,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidResources({ dictionary, file, header, options }); }, @@ -745,6 +761,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidColors({ dictionary, options, header }); }, @@ -779,6 +796,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidDimens({ dictionary, options, header }); }, @@ -813,6 +831,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidFontDimens({ dictionary, options, header }); }, @@ -849,6 +868,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidIntegers({ dictionary, options, header }); }, @@ -884,6 +904,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return androidStrings({ dictionary, options, header }); }, @@ -938,6 +959,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return composeObject({ allTokens: sortedTokens, options, formatProperty, header }); }, @@ -964,6 +986,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return macrosTemplate({ dictionary, options, file, header }); }, @@ -981,6 +1004,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'xml', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return plistTemplate({ dictionary, options, header }); }, @@ -998,6 +1022,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosSingletonM({ dictionary, options, file, header }); }, @@ -1015,6 +1040,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosSingletonH({ file, options, header }); }, @@ -1032,6 +1058,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosStaticH({ dictionary, file, options, header }); }, @@ -1049,6 +1076,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosStaticM({ dictionary, options, file, header }); }, @@ -1066,6 +1094,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosColorsH({ dictionary, file, options, header }); }, @@ -1083,6 +1112,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosColorsM({ dictionary, options, file, header }); }, @@ -1100,6 +1130,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosStringsH({ dictionary, file, options, header }); }, @@ -1117,6 +1148,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosStringsM({ dictionary, options, file, header }); }, @@ -1164,6 +1196,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosSwiftAny({ allTokens: sortedTokens, file, options, formatProperty, header }); }, @@ -1210,6 +1243,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosSwiftAny({ allTokens: sortedTokens, file, options, formatProperty, header }); }, @@ -1267,6 +1301,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return iosSwiftAny({ allTokens: sortedTokens, file, options, formatProperty, header }); }, @@ -1489,6 +1524,7 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul file, commentStyle: 'short', formatting: getFormattingCloneWithoutPrefix(formatting), + options, }); return flutterClassDart({ allTokens: sortedTokens, file, options, formatProperty, header }); }, diff --git a/lib/filterTokens.js b/lib/filterTokens.js index 73fc608aa..fea6a0f63 100644 --- a/lib/filterTokens.js +++ b/lib/filterTokens.js @@ -23,9 +23,10 @@ import isPlainObject from 'is-plain-obj'; /** * @param {Token[]} arr * @param {Filter['filter']} predicate + * @param {Config} options */ -async function asyncFilter(arr, predicate) { - return Promise.all(arr.map(predicate)).then((results) => +async function asyncFilter(arr, predicate, options) { + return Promise.all(arr.map((token) => predicate(token, options))).then((results) => arr.filter((_, index) => results[index]), ); } @@ -55,7 +56,7 @@ async function filterTokenObject(tokens, filter, options) { // the filter function and either include it in the final `acc` object or // exclude it (by returning the `acc` object without it added). } else if (typeof tokenValue !== 'undefined') { - const filtered = await asyncFilter(/** @type {Token[]} */ ([token]), filter); + const filtered = await asyncFilter(/** @type {Token[]} */ ([token]), filter, options); return filtered.length === 0 ? acc : { ...acc, [key]: token }; // If we got here we have an object that is not a property. We'll assume // it's an object containing multiple tokens and recursively filter it @@ -91,7 +92,7 @@ export default async function filterTokens(dictionary, filter, options = {}) { if (typeof filter !== 'function') { throw new Error('filter is not a function'); } else { - const allTokens = await asyncFilter(dictionary.allTokens ?? [], filter); + const allTokens = await asyncFilter(dictionary.allTokens ?? [], filter, options); const tokens = await filterTokenObject(dictionary.tokens, filter, options); return { diff --git a/lib/transform/token.js b/lib/transform/token.js index fedab0442..09d71ffeb 100644 --- a/lib/transform/token.js +++ b/lib/transform/token.js @@ -40,7 +40,7 @@ export default async function transformToken(token, config, options, vol) { for (let i = 0; i < transforms.length; i++) { const transform = transforms[i]; - if (!transform.filter || transform.filter(to_ret)) { + if (!transform.filter || transform.filter(to_ret, options)) { if (transform.type === 'name') { to_ret.name = await /** @type {Omit} */ (transform).transform( to_ret, diff --git a/lib/utils/createFormatArgs.js b/lib/utils/createFormatArgs.js index e68b6dff0..c837d2845 100644 --- a/lib/utils/createFormatArgs.js +++ b/lib/utils/createFormatArgs.js @@ -42,6 +42,7 @@ export default function createFormatArgs({ dictionary, platform, options, file } platform, file, options: { + ...options, ...(file.options || {}), usesDtcg: options?.usesDtcg ?? false, }, diff --git a/types/DesignToken.d.ts b/types/DesignToken.d.ts index 4a5b8c323..163b893be 100644 --- a/types/DesignToken.d.ts +++ b/types/DesignToken.d.ts @@ -65,4 +65,5 @@ export interface Dictionary { tokens: TransformedTokens; allTokens: TransformedToken[]; unfilteredTokens?: TransformedTokens; + unfilteredAllTokens?: TransformedToken[]; } diff --git a/types/File.d.ts b/types/File.d.ts index d974cdbc5..b87c49d32 100644 --- a/types/File.d.ts +++ b/types/File.d.ts @@ -1,6 +1,6 @@ import type { TransformedToken } from './DesignToken.d.ts'; import type { FormatFn } from './Format.d.ts'; -import type { LocalOptions } from './Config.d.ts'; +import type { LocalOptions, Config } from './Config.d.ts'; import type { Filter } from './Filter.d.ts'; export interface FormattingOptions { @@ -16,7 +16,10 @@ export interface FormattingOptions { fileHeaderTimestamp?: boolean; } -export type FileHeader = (defaultMessage: string[]) => Promise | string[]; +export type FileHeader = ( + defaultMessage: string[], + options?: Config, +) => Promise | string[]; export interface File { destination: string; diff --git a/types/Filter.d.ts b/types/Filter.d.ts index 9d5cac918..4a6b6ef56 100644 --- a/types/Filter.d.ts +++ b/types/Filter.d.ts @@ -11,8 +11,9 @@ * and limitations under the License. */ import type { TransformedToken } from './DesignToken.d.ts'; +import type { Config } from './Config.d.ts'; export interface Filter { name: string; - filter: (token: TransformedToken) => boolean | Promise; + filter: (token: TransformedToken, options: Config) => boolean | Promise; }