diff --git a/.changeset/shaggy-pigs-flash.md b/.changeset/shaggy-pigs-flash.md new file mode 100644 index 000000000..b5dfac598 --- /dev/null +++ b/.changeset/shaggy-pigs-flash.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': minor +--- + +Mark javascript/esm as nested, use Prettier on all JavaScript/TypeScript formats, use 3.x.x peerDependency so the user's installation is used when possible. diff --git a/.changeset/violet-goats-smile.md b/.changeset/violet-goats-smile.md new file mode 100644 index 000000000..a6e7a5112 --- /dev/null +++ b/.changeset/violet-goats-smile.md @@ -0,0 +1,5 @@ +--- +'style-dictionary': minor +--- + +Apply stripMeta from "json" format to the new "javascript/esm" as well. diff --git a/.changeset/warm-teachers-teach.md b/.changeset/warm-teachers-teach.md new file mode 100644 index 000000000..4f0ce655d --- /dev/null +++ b/.changeset/warm-teachers-teach.md @@ -0,0 +1,7 @@ +--- +'style-dictionary': minor +--- + +Add new utility in `style-dictionary/utils` -> `stripMeta` for stripping metadata from tokens. +This utility is used now as an opt-in for the built-in `'json'` format by using `options.stripMeta`, which if set to `true` will strip Style Dictionary meta props. +You can specify `keep`/`strip` (allow/blocklist) for granular control about which properties to keep or strip. diff --git a/__integration__/__snapshots__/customFileHeader.test.snap.js b/__integration__/__snapshots__/customFileHeader.test.snap.js index 72ed4100c..ef51b0eb8 100644 --- a/__integration__/__snapshots__/customFileHeader.test.snap.js +++ b/__integration__/__snapshots__/customFileHeader.test.snap.js @@ -43,46 +43,40 @@ snapshots["integration valid custom file headers platform options no file option */ module.exports = { - "color": { - "red": { - "value": "#ff0000", - "original": { - "value": "#ff0000" + color: { + red: { + value: "#ff0000", + original: { + value: "#ff0000", }, - "name": "ColorRed", - "attributes": { - "category": "color", - "type": "red" + name: "ColorRed", + attributes: { + category: "color", + type: "red", }, - "path": [ - "color", - "red" - ] - } - } + path: ["color", "red"], + }, + }, }; `; /* end snapshot integration valid custom file headers platform options no file options should match snapshot */ snapshots["integration valid custom file headers platform options showFileHeader should match snapshot"] = `module.exports = { - "color": { - "red": { - "value": "#ff0000", - "original": { - "value": "#ff0000" + color: { + red: { + value: "#ff0000", + original: { + value: "#ff0000", }, - "name": "ColorRed", - "attributes": { - "category": "color", - "type": "red" + name: "ColorRed", + attributes: { + category: "color", + type: "red", }, - "path": [ - "color", - "red" - ] - } - } + path: ["color", "red"], + }, + }, }; `; /* end snapshot integration valid custom file headers platform options showFileHeader should match snapshot */ @@ -93,23 +87,20 @@ snapshots["integration valid custom file headers platform options file header ov */ module.exports = { - "color": { - "red": { - "value": "#ff0000", - "original": { - "value": "#ff0000" + color: { + red: { + value: "#ff0000", + original: { + value: "#ff0000", }, - "name": "ColorRed", - "attributes": { - "category": "color", - "type": "red" + name: "ColorRed", + attributes: { + category: "color", + type: "red", }, - "path": [ - "color", - "red" - ] - } - } + path: ["color", "red"], + }, + }, }; `; /* end snapshot integration valid custom file headers platform options file header override should match snapshot */ diff --git a/__tests__/StyleDictionary.test.js b/__tests__/StyleDictionary.test.js index de7cd3111..220177b4b 100644 --- a/__tests__/StyleDictionary.test.js +++ b/__tests__/StyleDictionary.test.js @@ -18,6 +18,7 @@ import { fileToJSON, clearOutput, fileExists, clearSDMeta } from './__helpers.js import { resolve } from '../lib/resolve.js'; import GroupMessages from '../lib/utils/groupMessages.js'; import flattenTokens from '../lib/utils/flattenTokens.js'; +import { stripMeta } from '../lib/utils/stripMeta.js'; import formats from '../lib/common/formats.js'; import { restore, stubMethod } from 'hanbi'; @@ -30,18 +31,6 @@ function traverseObj(obj, fn) { } } -function stripMeta(obj) { - Object.keys(obj).forEach((key) => { - if (['attributes', 'name', 'original', 'path'].includes(key)) { - delete obj[key]; - } - if (typeof obj[key] === 'object') { - stripMeta(obj[key]); - } - }); - return obj; -} - const test_props = { size: { padding: { @@ -570,7 +559,7 @@ Refer to: https://styledictionary.com/reference/logging/ await sd.hasInitialized; const cssTokens = await sd.exportPlatform('css'); const jsTokens = await sd.exportPlatform('js'); - expect(stripMeta(cssTokens)).to.eql({ + expect(stripMeta(cssTokens, { keep: ['type', 'value'] })).to.eql({ border: { color: { type: 'color', @@ -586,7 +575,7 @@ Refer to: https://styledictionary.com/reference/logging/ }, }, }); - expect(stripMeta(jsTokens)).to.eql(input); + expect(stripMeta(jsTokens, { keep: ['type', 'value'] })).to.eql(input); }); it('should allow combining global expand with per platform expand', async () => { @@ -628,7 +617,7 @@ Refer to: https://styledictionary.com/reference/logging/ const cssTokens = await sd.exportPlatform('css'); const jsTokens = await sd.exportPlatform('js'); - expect(stripMeta(cssTokens)).to.eql({ + expect(stripMeta(cssTokens, { keep: ['type', 'value'] })).to.eql({ border: { color: { type: 'color', @@ -645,7 +634,7 @@ Refer to: https://styledictionary.com/reference/logging/ }, borderTwo: input.borderTwo, }); - expect(stripMeta(jsTokens)).to.eql({ + expect(stripMeta(jsTokens, { keep: ['type', 'value'] })).to.eql({ border: { color: { type: 'color', diff --git a/__tests__/formats/__snapshots__/all.test.snap.js b/__tests__/formats/__snapshots__/all.test.snap.js index 61dacabd3..2678f4d58 100644 --- a/__tests__/formats/__snapshots__/all.test.snap.js +++ b/__tests__/formats/__snapshots__/all.test.snap.js @@ -84,21 +84,18 @@ snapshots["formats all should match javascript/module snapshot"] = */ module.exports = { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/module snapshot */ @@ -109,7 +106,7 @@ snapshots["formats all should match javascript/module-flat snapshot"] = */ module.exports = { - "color_red": "#FF0000" + color_red: "#FF0000", }; `; /* end snapshot formats all should match javascript/module-flat snapshot */ @@ -120,21 +117,18 @@ snapshots["formats all should match javascript/object snapshot"] = */ var _styleDictionary = { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/object snapshot */ @@ -144,7 +138,7 @@ snapshots["formats all should match javascript/umd snapshot"] = * Do not edit directly, this file was auto-generated. */ -(function(root, factory) { +(function (root, factory) { if (typeof module === "object" && module.exports) { module.exports = factory(); } else if (typeof exports === "object") { @@ -154,25 +148,22 @@ snapshots["formats all should match javascript/umd snapshot"] = } else { root["_styleDictionary"] = factory(); } -}(this, function() { +})(this, function () { return { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", + }, + name: "color_red", + comment: "comment", + path: ["color", "red"], }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } -}; -})) + }, + }; +}); `; /* end snapshot formats all should match javascript/umd snapshot */ @@ -191,7 +182,7 @@ snapshots["formats all should match typescript/es6-declarations snapshot"] = */ /** comment */ -export const color_red : string; +export const color_red: string; `; /* end snapshot formats all should match typescript/es6-declarations snapshot */ @@ -213,10 +204,10 @@ declare interface DesignToken { } declare const tokens: { - "color": { - "red": DesignToken - } -} + color: { + red: DesignToken; + }; +}; `; /* end snapshot formats all should match typescript/module-declarations snapshot */ @@ -878,21 +869,18 @@ snapshots["formats all should match javascript/module snapshot with fileHeaderTi */ module.exports = { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/module snapshot with fileHeaderTimestamp set */ @@ -904,7 +892,7 @@ snapshots["formats all should match javascript/module-flat snapshot with fileHea */ module.exports = { - "color_red": "#FF0000" + color_red: "#FF0000", }; `; /* end snapshot formats all should match javascript/module-flat snapshot with fileHeaderTimestamp set */ @@ -916,21 +904,18 @@ snapshots["formats all should match javascript/object snapshot with fileHeaderTi */ var _styleDictionary = { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/object snapshot with fileHeaderTimestamp set */ @@ -941,7 +926,7 @@ snapshots["formats all should match javascript/umd snapshot with fileHeaderTimes * Generated on Sat, 01 Jan 2000 00:00:00 GMT */ -(function(root, factory) { +(function (root, factory) { if (typeof module === "object" && module.exports) { module.exports = factory(); } else if (typeof exports === "object") { @@ -951,25 +936,22 @@ snapshots["formats all should match javascript/umd snapshot with fileHeaderTimes } else { root["_styleDictionary"] = factory(); } -}(this, function() { +})(this, function () { return { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", + }, + name: "color_red", + comment: "comment", + path: ["color", "red"], }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } -}; -})) + }, + }; +}); `; /* end snapshot formats all should match javascript/umd snapshot with fileHeaderTimestamp set */ @@ -990,7 +972,7 @@ snapshots["formats all should match typescript/es6-declarations snapshot with fi */ /** comment */ -export const color_red : string; +export const color_red: string; `; /* end snapshot formats all should match typescript/es6-declarations snapshot with fileHeaderTimestamp set */ @@ -1013,10 +995,10 @@ declare interface DesignToken { } declare const tokens: { - "color": { - "red": DesignToken - } -} + color: { + red: DesignToken; + }; +}; `; /* end snapshot formats all should match typescript/module-declarations snapshot with fileHeaderTimestamp set */ @@ -1519,21 +1501,18 @@ snapshots["formats all should match javascript/esm snapshot"] = */ export default { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/esm snapshot */ @@ -1545,21 +1524,18 @@ snapshots["formats all should match javascript/esm snapshot with fileHeaderTimes */ export default { - "color": { - "red": { - "value": "#FF0000", - "type": "color", - "original": { - "value": "#FF0000" + color: { + red: { + value: "#FF0000", + type: "color", + original: { + value: "#FF0000", }, - "name": "color_red", - "comment": "comment", - "path": [ - "color", - "red" - ] - } - } + name: "color_red", + comment: "comment", + path: ["color", "red"], + }, + }, }; `; /* end snapshot formats all should match javascript/esm snapshot with fileHeaderTimestamp set */ diff --git a/__tests__/formats/__snapshots__/es6Module.test.snap.js b/__tests__/formats/__snapshots__/es6Module.test.snap.js index f74df61d3..258e48e9f 100644 --- a/__tests__/formats/__snapshots__/es6Module.test.snap.js +++ b/__tests__/formats/__snapshots__/es6Module.test.snap.js @@ -7,12 +7,133 @@ snapshots["formats javascript/esm should be a valid JS file and match snapshot"] */ export default { - "color": { - "red": { - "value": "#FF0000" - } - } + colors: { + red: { + 500: { + value: "#ff0000", + type: "color", + path: ["colors", "red", "500"], + filePath: "tokens.json", + attributes: { + foo: "bar", + }, + name: "colors-red-500", + }, + }, + }, + dimensions: { + xs: { + value: "15px", + type: "dimension", + path: ["dimension", "xs"], + filePath: "tokens.json", + attributes: { + foo: "bar", + }, + name: "dimension-xs", + }, + }, }; `; /* end snapshot formats javascript/esm should be a valid JS file and match snapshot */ +snapshots["formats javascript/esm should optionally allow stripping StyleDictionary metadata"] = +`/** + * Do not edit directly, this file was auto-generated. + */ + +export default { + colors: { + red: { + 500: { + value: "#ff0000", + type: "color", + }, + }, + }, + dimensions: { + xs: { + value: "15px", + type: "dimension", + }, + }, +}; +`; +/* end snapshot formats javascript/esm should optionally allow stripping StyleDictionary metadata */ + +snapshots["formats javascript/esm should optionally allow stripping everything but an allowlist of props"] = +`/** + * Do not edit directly, this file was auto-generated. + */ + +export default { + colors: { + red: { + 500: { + value: "#ff0000", + type: "color", + }, + }, + }, + dimensions: { + xs: { + value: "15px", + type: "dimension", + }, + }, +}; +`; +/* end snapshot formats javascript/esm should optionally allow stripping everything but an allowlist of props */ + +snapshots["formats javascript/esm should optionally allow stripping custom list of metadata props"] = +`/** + * Do not edit directly, this file was auto-generated. + */ + +export default { + colors: { + red: { + 500: { + value: "#ff0000", + type: "color", + path: ["colors", "red", "500"], + name: "colors-red-500", + }, + }, + }, + dimensions: { + xs: { + value: "15px", + type: "dimension", + path: ["dimension", "xs"], + name: "dimension-xs", + }, + }, +}; +`; +/* end snapshot formats javascript/esm should optionally allow stripping custom list of metadata props */ + +snapshots["formats javascript/esm should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens"] = +`/** + * Do not edit directly, this file was auto-generated. + */ + +export default { + colors: { + red: { + 500: { + $value: "#ff0000", + $type: "color", + }, + }, + }, + dimensions: { + xs: { + $value: "15px", + $type: "dimension", + }, + }, +}; +`; +/* end snapshot formats javascript/esm should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens */ + diff --git a/__tests__/formats/__snapshots__/es6ModuleMinify.test.snap.js b/__tests__/formats/__snapshots__/es6ModuleMinify.test.snap.js index af0bd1d0c..b6e03a2aa 100644 --- a/__tests__/formats/__snapshots__/es6ModuleMinify.test.snap.js +++ b/__tests__/formats/__snapshots__/es6ModuleMinify.test.snap.js @@ -7,9 +7,9 @@ snapshots["formats javascript/esm should be a valid JS file and match snapshot"] */ export default { - "color": { - "red": "#FF0000" - } + color: { + red: "#FF0000", + }, }; `; /* end snapshot formats javascript/esm should be a valid JS file and match snapshot */ diff --git a/__tests__/formats/__snapshots__/javascriptModule.test.snap.js b/__tests__/formats/__snapshots__/javascriptModule.test.snap.js index e68258d9a..67e8a9142 100644 --- a/__tests__/formats/__snapshots__/javascriptModule.test.snap.js +++ b/__tests__/formats/__snapshots__/javascriptModule.test.snap.js @@ -7,11 +7,11 @@ snapshots["formats javascript/module should be a valid JS file and match snapsho */ module.exports = { - "color": { - "red": { - "value": "#FF0000" - } - } + color: { + red: { + value: "#FF0000", + }, + }, }; `; /* end snapshot formats javascript/module should be a valid JS file and match snapshot */ diff --git a/__tests__/formats/__snapshots__/javascriptModuleFlat.test.snap.js b/__tests__/formats/__snapshots__/javascriptModuleFlat.test.snap.js index aef449948..8f546def9 100644 --- a/__tests__/formats/__snapshots__/javascriptModuleFlat.test.snap.js +++ b/__tests__/formats/__snapshots__/javascriptModuleFlat.test.snap.js @@ -7,7 +7,7 @@ snapshots["formats javascript/module-flat should be a valid JS file and match sn */ module.exports = { - "ColorRed": "#EF5350" + ColorRed: "#EF5350", }; `; /* end snapshot formats javascript/module-flat should be a valid JS file and match snapshot */ diff --git a/__tests__/formats/__snapshots__/javascriptObject.test.snap.js b/__tests__/formats/__snapshots__/javascriptObject.test.snap.js index d5a44faf7..453c3b204 100644 --- a/__tests__/formats/__snapshots__/javascriptObject.test.snap.js +++ b/__tests__/formats/__snapshots__/javascriptObject.test.snap.js @@ -7,11 +7,11 @@ snapshots["formats javascript/object should be valid JS syntax and match snapsho */ var foo = { - "color": { - "red": { - "value": "#FF0000" - } - } + color: { + red: { + value: "#FF0000", + }, + }, }; `; /* end snapshot formats javascript/object should be valid JS syntax and match snapshot */ diff --git a/__tests__/formats/__snapshots__/javascriptUmd.test.snap.js b/__tests__/formats/__snapshots__/javascriptUmd.test.snap.js index acc2dd985..143a5ae95 100644 --- a/__tests__/formats/__snapshots__/javascriptUmd.test.snap.js +++ b/__tests__/formats/__snapshots__/javascriptUmd.test.snap.js @@ -6,7 +6,7 @@ snapshots["formats javascript/umd should be a valid JS file and match snapshot"] * Do not edit directly, this file was auto-generated. */ -(function(root, factory) { +(function (root, factory) { if (typeof module === "object" && module.exports) { module.exports = factory(); } else if (typeof exports === "object") { @@ -16,15 +16,15 @@ snapshots["formats javascript/umd should be a valid JS file and match snapshot"] } else { root["_styleDictionary"] = factory(); } -}(this, function() { +})(this, function () { return { - "color": { - "red": { - "value": "#FF0000" - } - } -}; -})) + color: { + red: { + value: "#FF0000", + }, + }, + }; +}); `; /* end snapshot formats javascript/umd should be a valid JS file and match snapshot */ diff --git a/__tests__/formats/__snapshots__/json.test.snap.js b/__tests__/formats/__snapshots__/json.test.snap.js index 64e11b458..5e323fe0e 100644 --- a/__tests__/formats/__snapshots__/json.test.snap.js +++ b/__tests__/formats/__snapshots__/json.test.snap.js @@ -3,12 +3,131 @@ export const snapshots = {}; snapshots["formats json should be a valid JSON file and match snapshot"] = `{ - "color": { + "colors": { "red": { - "value": "#FF0000" + "500": { + "value": "#ff0000", + "type": "color", + "path": [ + "colors", + "red", + "500" + ], + "filePath": "tokens.json", + "attributes": { + "foo": "bar" + }, + "name": "colors-red-500" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension", + "path": [ + "dimension", + "xs" + ], + "filePath": "tokens.json", + "attributes": { + "foo": "bar" + }, + "name": "dimension-xs" } } } `; /* end snapshot formats json should be a valid JSON file and match snapshot */ +snapshots["formats json should optionally allow stripping StyleDictionary metadata"] = +`{ + "colors": { + "red": { + "500": { + "value": "#ff0000", + "type": "color" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension" + } + } +} +`; +/* end snapshot formats json should optionally allow stripping StyleDictionary metadata */ + +snapshots["formats json should optionally allow stripping everything but an allowlist of props"] = +`{ + "colors": { + "red": { + "500": { + "value": "#ff0000", + "type": "color" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension" + } + } +} +`; +/* end snapshot formats json should optionally allow stripping everything but an allowlist of props */ + +snapshots["formats json should optionally allow stripping custom list of metadata props"] = +`{ + "colors": { + "red": { + "500": { + "value": "#ff0000", + "type": "color", + "path": [ + "colors", + "red", + "500" + ], + "name": "colors-red-500" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension", + "path": [ + "dimension", + "xs" + ], + "name": "dimension-xs" + } + } +} +`; +/* end snapshot formats json should optionally allow stripping custom list of metadata props */ + +snapshots["formats json should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens"] = +`{ + "colors": { + "red": { + "500": { + "$value": "#ff0000", + "$type": "color" + } + } + }, + "dimensions": { + "xs": { + "$value": "15px", + "$type": "dimension" + } + } +} +`; +/* end snapshot formats json should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens */ + diff --git a/__tests__/formats/__snapshots__/typeScriptEs6Declarations.test.snap.js b/__tests__/formats/__snapshots__/typeScriptEs6Declarations.test.snap.js index 9a2481da5..e2547f5de 100644 --- a/__tests__/formats/__snapshots__/typeScriptEs6Declarations.test.snap.js +++ b/__tests__/formats/__snapshots__/typeScriptEs6Declarations.test.snap.js @@ -7,8 +7,8 @@ snapshots["formats typescript/es6-declarations with outputStringLiterals should */ /** Used for errors */ -export const colorRed : "#FF0000"; -export const fontFamily : "\\"Source Sans Pro\\", Arial, sans-serif"; +export const colorRed: "#FF0000"; +export const fontFamily: '"Source Sans Pro", Arial, sans-serif'; `; /* end snapshot formats typescript/es6-declarations with outputStringLiterals should match snapshot */ diff --git a/__tests__/formats/es6Module.test.js b/__tests__/formats/es6Module.test.js index 05cfae406..b6738174a 100644 --- a/__tests__/formats/es6Module.test.js +++ b/__tests__/formats/es6Module.test.js @@ -24,8 +24,60 @@ const file = { }; const tokens = { - color: { - red: { value: '#FF0000' }, + colors: { + red: { + 500: { + value: '#ff0000', + type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + value: '15px', + type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, +}; + +const DTCGTokens = { + colors: { + red: { + 500: { + $value: '#ff0000', + $type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + $value: '15px', + $type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, }, }; @@ -46,5 +98,78 @@ describe('formats', () => { ), ).to.matchSnapshot(); }); + + it('should optionally allow stripping StyleDictionary metadata', async () => { + await expect( + await format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: true, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping everything but an allowlist of props', async () => { + await expect( + await format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: { + keep: ['value', 'type'], + }, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping custom list of metadata props', async () => { + await expect( + await format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: { + strip: ['attributes', 'filePath'], + }, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens', async () => { + await expect( + await format( + createFormatArgs({ + dictionary: { tokens: DTCGTokens, allTokens: flattenTokens(DTCGTokens) }, + file, + platform: {}, + options: { + usesDtcg: true, + stripMeta: true, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); }); }); diff --git a/__tests__/formats/json.test.js b/__tests__/formats/json.test.js index 852c689e0..87bfd94a3 100644 --- a/__tests__/formats/json.test.js +++ b/__tests__/formats/json.test.js @@ -21,8 +21,60 @@ const file = { }; const tokens = { - color: { - red: { value: '#FF0000' }, + colors: { + red: { + 500: { + value: '#ff0000', + type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + value: '15px', + type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, +}; + +const DTCGTokens = { + colors: { + red: { + 500: { + $value: '#ff0000', + $type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + $value: '15px', + $type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, }, }; @@ -43,5 +95,78 @@ describe('formats', () => { ), ).to.matchSnapshot(); }); + + it('should optionally allow stripping StyleDictionary metadata', async () => { + await expect( + format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: true, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping everything but an allowlist of props', async () => { + await expect( + format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: { + keep: ['value', 'type'], + }, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping custom list of metadata props', async () => { + await expect( + format( + createFormatArgs({ + dictionary: { tokens, allTokens: flattenTokens(tokens) }, + file, + platform: {}, + options: { + stripMeta: { + strip: ['attributes', 'filePath'], + }, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); + + it('should optionally allow stripping StyleDictionary metadata for DTCG formatted tokens', async () => { + await expect( + format( + createFormatArgs({ + dictionary: { tokens: DTCGTokens, allTokens: flattenTokens(DTCGTokens) }, + file, + platform: {}, + options: { + usesDtcg: true, + stripMeta: true, + }, + }), + {}, + file, + ), + ).to.matchSnapshot(); + }); }); }); diff --git a/__tests__/formats/typeScriptEs6Declarations.test.js b/__tests__/formats/typeScriptEs6Declarations.test.js index 00df69698..9d990b0c0 100644 --- a/__tests__/formats/typeScriptEs6Declarations.test.js +++ b/__tests__/formats/typeScriptEs6Declarations.test.js @@ -54,7 +54,7 @@ describe('formats', () => { // assert that any lines have a string type definition lines.forEach((l) => { - expect(l.match(/^export.* : string;$/g).length).to.equal(1); + expect(l.match(/^export.*: string;$/g).length).to.equal(1); }); }); diff --git a/__tests__/formats/typeScriptModuleDeclarations.test.js b/__tests__/formats/typeScriptModuleDeclarations.test.js index 33ddb856a..3072a63ee 100644 --- a/__tests__/formats/typeScriptModuleDeclarations.test.js +++ b/__tests__/formats/typeScriptModuleDeclarations.test.js @@ -47,7 +47,7 @@ describe('formats', () => { // assert that any lines have a DesignToken type definition lines.forEach((l) => { - expect(l.match(/^.*: DesignToken$/g).length).to.equal(1); + expect(l.match(/^.*: DesignToken;$/g).length).to.equal(1); }); }); }); diff --git a/__tests__/utils/__snapshots__/stripMeta.test.snap.js b/__tests__/utils/__snapshots__/stripMeta.test.snap.js new file mode 100644 index 000000000..3dbf589fa --- /dev/null +++ b/__tests__/utils/__snapshots__/stripMeta.test.snap.js @@ -0,0 +1,79 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["utils stripMeta should strip meta data properties that are supplied"] = +`{ + "colors": { + "red": { + "500": { + "value": "#ff0000", + "type": "color" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension" + } + } +}`; +/* end snapshot utils stripMeta should strip meta data properties that are supplied */ + +snapshots["utils stripMeta supports passing an allowlist instead"] = +`{ + "colors": { + "red": { + "500": { + "value": "#ff0000", + "type": "color" + } + } + }, + "dimensions": { + "xs": { + "value": "15px", + "type": "dimension" + } + } +}`; +/* end snapshot utils stripMeta supports passing an allowlist instead */ + +snapshots["utils stripMeta should only strip these properties on the token level, not group level"] = +`{ + "name": { + "red": { + "500": { + "value": "#ff0000", + "type": "color" + } + } + }, + "dimensions": { + "attributes": { + "value": "15px", + "type": "dimension" + } + } +}`; +/* end snapshot utils stripMeta should only strip these properties on the token level, not group level */ + +snapshots["utils stripMeta should work for DTCG formatted tokens"] = +`{ + "colors": { + "red": { + "500": { + "$value": "#ff0000", + "$type": "color" + } + } + }, + "dimensions": { + "xs": { + "$value": "15px", + "$type": "dimension" + } + } +}`; +/* end snapshot utils stripMeta should work for DTCG formatted tokens */ + diff --git a/__tests__/utils/stripMeta.test.js b/__tests__/utils/stripMeta.test.js new file mode 100644 index 000000000..1cdb49850 --- /dev/null +++ b/__tests__/utils/stripMeta.test.js @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import { stripMeta } from '../../lib/utils/stripMeta.js'; + +const tokens = { + colors: { + red: { + 500: { + value: '#ff0000', + type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + value: '15px', + type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, +}; + +describe('utils', () => { + describe('stripMeta', () => { + it('should strip meta data properties that are supplied', async () => { + await expect( + JSON.stringify( + stripMeta(tokens, { strip: ['path', 'filePath', 'attributes', 'name'] }), + null, + 2, + ), + ).to.matchSnapshot(); + }); + + it('supports passing an allowlist instead', async () => { + await expect( + JSON.stringify(stripMeta(tokens, { keep: ['value', 'type'] }), null, 2), + ).to.matchSnapshot(); + }); + + it('should only strip these properties on the token level, not group level', async () => { + const groupTokens = { + name: { + red: { + 500: { + value: '#ff0000', + type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + attributes: { + value: '15px', + type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, + }; + + await expect( + JSON.stringify( + stripMeta(groupTokens, { strip: ['path', 'filePath', 'attributes', 'name'] }), + null, + 2, + ), + ).to.matchSnapshot(); + }); + + it('should work for DTCG formatted tokens', async () => { + const DTCGTokens = { + colors: { + red: { + 500: { + $value: '#ff0000', + $type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + $value: '15px', + $type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, + }; + + await expect( + JSON.stringify( + stripMeta(DTCGTokens, { + strip: ['path', 'filePath', 'attributes', 'name'], + usesDtcg: true, + }), + null, + 2, + ), + ).to.matchSnapshot(); + }); + + it('should not mutate the input', async () => { + stripMeta(tokens, { strip: ['path', 'filePath', 'attributes', 'name'] }); + expect(tokens).to.eql({ + colors: { + red: { + 500: { + value: '#ff0000', + type: 'color', + path: ['colors', 'red', '500'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'colors-red-500', + }, + }, + }, + dimensions: { + xs: { + value: '15px', + type: 'dimension', + path: ['dimension', 'xs'], + filePath: 'tokens.json', + attributes: { + foo: 'bar', + }, + name: 'dimension-xs', + }, + }, + }); + }); + }); +}); diff --git a/docs/src/content/docs/reference/Hooks/Formats/predefined.md b/docs/src/content/docs/reference/Hooks/Formats/predefined.md index 9b9d5bf33..213f0873f 100644 --- a/docs/src/content/docs/reference/Hooks/Formats/predefined.md +++ b/docs/src/content/docs/reference/Hooks/Formats/predefined.md @@ -308,10 +308,13 @@ Creates an ES6 module object of the style dictionary. } ``` -| Param | Type | Description | -| ---------------- | --------- | --------------------------------------------------------- | -| `options` | `Object` | | -| `options.minify` | `boolean` | Whether or not to minify the output. Defaults to `false`. | +| Param | Type | Description | +| ------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `options` | `Object` | | +| `options.minify` | `boolean` | Whether or not to minify the output. Defaults to `false`. | +| `options.stripMeta` | `Object \| boolean` | Control whether meta data is stripped from the output tokens. `false` by default. If set to `true`, will strip known Style Dictionary meta props: `['attributes', 'filePath', 'name', 'path', 'comment']`. Note that using minify means that meta is stripped as a side effect of this already, so using both is unnecessary. | +| `options.stripMeta.keep` | `string[]` | Array of property keys to keep in the output. | +| `options.stripMeta.strip` | `string[]` | Array of property keys to strip from the output. | Example: @@ -348,6 +351,19 @@ export default { }; ``` +Example with `stripMeta` flag: + +```js title="vars.js" +export default { + colors: { + black: { + $value: '#000000', + $type: 'color', + }, + }, +}; +``` + --- ### typescript/es6-declarations @@ -845,20 +861,50 @@ Creates CSS file with @font-face declarations Creates a JSON file of the style dictionary. -Example: +| Param | Type | Description | +| ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `options` | `Object` | | +| `options.stripMeta` | `Object \| boolean` | Control whether meta data is stripped from the output tokens. `false` by default. If set to `true`, will strip known Style Dictionary meta props: `['attributes', 'filePath', 'name', 'path', 'comment']` | +| `options.stripMeta.keep` | `string[]` | Array of property keys to keep in the output. | +| `options.stripMeta.strip` | `string[]` | Array of property keys to strip from the output. | -```json title="vars.json" +Live Demo: + +~ sd-playground + +```json tokens { "color": { "base": { "red": { - "value": "#ff0000" + "value": "#ff0000", + "type": "color" } } } } ``` +```js config +export default { + platforms: { + json: { + files: [ + { + destination: 'output.json', + format: 'json', + options: { + stripMeta: { + keep: ['value'], + }, + }, + }, + ], + }, + }, +}; +``` + --- ### json/asset diff --git a/docs/src/content/docs/reference/Utils/tokens.md b/docs/src/content/docs/reference/Utils/tokens.md index 867ac194e..a56966115 100644 --- a/docs/src/content/docs/reference/Utils/tokens.md +++ b/docs/src/content/docs/reference/Utils/tokens.md @@ -52,3 +52,68 @@ const flat = flattenTokens(sd); :::note You can pass a second argument `usesDtcg`, if set to true, the flattenTokens utility will assume DTCG syntax (`$value` props). ::: + +## stripMeta + +Allows you to strip meta data from design tokens, useful if you want to output clean nested formats. + +You can define which meta properties to strip or which properties to keep (allowlist / blocklist), in the second `options` parameter. + +This utility is also used in the [`'json'` format](/reference/hooks/formats/predefined#json). + +```javascript title="build-tokens.js" +import StyleDictionary from 'style-dictionary'; +import { stripMeta } from 'style-dictionary/utils'; + +const sd = new StyleDictionary({ + tokens: { + colors: { + black: { + value: '#000', + type: 'color', + name: 'colors-black', + attributes: { foo: 'bar' }, + path: ['colors', 'black'], + }, + }, + spacing: { + 2: { + value: '2px', + type: 'dimension', + name: 'spacing-2', + attributes: { foo: 'bar' }, + path: ['spacing', '2'], + }, + }, + border: { + value: 'solid {spacing.2} {colors.black}', + name: 'border', + attributes: { foo: 'bar' }, + path: ['border'], + }, + }, +}); + +const stripped = stripMeta(sd, { keep: ['value'] }); +/** + * { + * colors: { + * black: { + * value: '#000', + * }, + * }, + * spacing: { + * 2: { + * value: '2px', + * }, + * }, + * border: { + * value: 'solid {spacing.2} {colors.black}', + * }, + * } + */ +``` + +:::note +You can pass `usesDtcg` property in the `options` object parameter, if set to true, the stripMeta utility will assume DTCG syntax (`$value` props). +::: diff --git a/lib/common/formats.js b/lib/common/formats.js index 173d06139..80b94c67d 100644 --- a/lib/common/formats.js +++ b/lib/common/formats.js @@ -10,6 +10,11 @@ * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions * and limitations under the License. */ +import * as prettier from 'prettier/standalone'; +import prettierPluginBabel from 'prettier/plugins/babel'; +import prettierPluginEstree from 'prettier/plugins/estree'; +import prettierPluginTypescript from 'prettier/plugins/typescript'; + import { fileHeader, formattedVariables, @@ -23,6 +28,8 @@ import { setComposeObjectProperties, } from './formatHelpers/index.js'; +import { stripMeta as stripMetaUtil } from '../utils/stripMeta.js'; + import androidColors from './templates/android/colors.template.js'; import androidDimens from './templates/android/dimens.template.js'; import androidFontDimens from './templates/android/fontDimens.template.js'; @@ -53,12 +60,52 @@ import plistTemplate from './templates/ios/plist.template.js'; * @typedef {import('../../types/Format.d.ts').OutputReferences} OutputReferences * @typedef {import('../../types/DesignToken.d.ts').TransformedToken} Token * @typedef {import('../../types/DesignToken.d.ts').TransformedTokens} Tokens + * @typedef {import('../../types/Config.d.ts').Config} Config + * @typedef {import('../../types/Config.d.ts').LocalOptions} LocalOptions + * @typedef {import('../utils/stripMeta.js').StripMetaOptions} StripMetaOptions */ /** * @namespace Formats */ +/** + * Strip meta properties from tokens object + * + * @param {Tokens} tokens + * @param {Config & LocalOptions & { stripMeta: boolean | StripMetaOptions}} options + */ +function stripMetaProps(tokens, options) { + const sdMetaProps = ['attributes', 'filePath', 'name', 'path', 'comment']; + const { stripMeta, usesDtcg } = options; + let opts = /** @type {StripMetaOptions} */ ({ usesDtcg }); + + if (stripMeta) { + if (stripMeta === true) { + opts.strip = sdMetaProps; + } else { + opts = { + usesDtcg: usesDtcg ?? false, + ...stripMeta, + }; + } + tokens = stripMetaUtil(tokens, opts); + } + return tokens; +} + +/** + * Prettier format JS contents + * @param {string} content + * @param {boolean} [ts] whether or not to use typescript + */ +async function formatJS(content, ts = false) { + return prettier.format(content, { + parser: ts ? `typescript` : `babel`, + plugins: [prettierPluginBabel, prettierPluginEstree, prettierPluginTypescript], + }); +} + /** * Remove prefix because the prefix option for createPropertyFormatter * is not the same as the prefix inside header comment @@ -343,7 +390,9 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return header + 'module.exports = ' + JSON.stringify(dictionary.tokens, null, 2) + ';\n'; + const content = + header + 'module.exports = ' + JSON.stringify(dictionary.tokens, null, 2) + ';\n'; + return formatJS(content); }, /** @@ -365,7 +414,7 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return ( + const content = header + 'module.exports = ' + '{\n' + @@ -377,8 +426,8 @@ const formats = { }) .join(',\n') + '\n}' + - ';\n' - ); + ';\n'; + return formatJS(content); }, /** @@ -407,14 +456,14 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return ( + const content = header + 'var ' + (file.options?.name || '_styleDictionary') + ' = ' + JSON.stringify(dictionary.tokens, null, 2) + - ';\n' - ); + ';\n'; + return formatJS(content); }, /** @@ -455,7 +504,7 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return ( + const content = header + '(function(root, factory) {\n' + ' if (typeof module === "object" && module.exports) {\n' + @@ -475,8 +524,8 @@ const formats = { ' return ' + JSON.stringify(dictionary.tokens, null, 2) + ';\n' + - '}))\n' - ); + '}))\n'; + return formatJS(content); }, /** @@ -516,7 +565,7 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return ( + const content = header + dictionary.allTokens .map(function (token) { @@ -530,8 +579,8 @@ const formats = { return to_ret; }) .join('\n') + - '\n' - ); + '\n'; + return formatJS(content); }, /** @@ -552,6 +601,12 @@ const formats = { */ 'javascript/esm': async function ({ dictionary, file, options }) { const { formatting, minify = false } = options; + let { tokens } = dictionary; + tokens = stripMetaProps( + tokens, + /** @type {LocalOptions & Config & { stripMeta: boolean | StripMetaOptions}} */ (options), + ); + const header = await fileHeader({ file, formatting: getFormattingCloneWithoutPrefix(formatting), @@ -559,12 +614,13 @@ const formats = { }); const dictionaryString = JSON.stringify( - minify ? minifyDictionary(dictionary.tokens, options.usesDtcg) : dictionary.tokens, + minify ? minifyDictionary(tokens, options.usesDtcg) : tokens, null, 2, ); - return `${header}export default ${dictionaryString};\n`; + const content = `${header}export default ${dictionaryString};\n`; + return formatJS(content); }, // TypeScript declarations @@ -606,7 +662,7 @@ const formats = { formatting: getFormattingCloneWithoutPrefix(formatting), options, }); - return ( + const content = header + dictionary.allTokens .map(function (token) { @@ -621,8 +677,8 @@ const formats = { return to_ret_token; }) .join('\n') + - '\n' - ); + '\n'; + return formatJS(content, true); }, /** @@ -728,7 +784,8 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul // JSON stringify will quote strings, because this is a type we need // it unquoted. - return output.replace(/"DesignToken"/g, 'DesignToken') + '\n'; + const content = output.replace(/"DesignToken"/g, 'DesignToken') + '\n'; + return formatJS(content, true); }, // Android templates @@ -1374,8 +1431,13 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul * } * ``` */ - json: function ({ dictionary }) { - return JSON.stringify(dictionary.tokens, null, 2) + '\n'; + json: function ({ dictionary, options }) { + let { tokens } = dictionary; + tokens = stripMetaProps( + tokens, + /** @type {LocalOptions & Config & { stripMeta: boolean | StripMetaOptions}} */ (options), + ); + return JSON.stringify(tokens, null, 2) + '\n'; }, /** @@ -1402,7 +1464,6 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul /** * Creates a JSON nested file of the style dictionary. - * * @memberof Formats * @kind member * @example @@ -1571,5 +1632,6 @@ declare const ${moduleName}: ${JSON.stringify(treeWalker(dictionary.tokens), nul formats['json/nested'].nested = true; formats['javascript/module'].nested = true; formats['javascript/object'].nested = true; +formats['javascript/esm'].nested = true; export default formats; diff --git a/lib/utils/index.js b/lib/utils/index.js index da9f0cb86..eec211937 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -19,6 +19,7 @@ import { outputReferencesTransformed } from './references/outputReferencesTransf import flattenTokens from './flattenTokens.js'; import { typeDtcgDelegate } from './typeDtcgDelegate.js'; import { convertToDTCG, convertJSONToDTCG, convertZIPToDTCG } from './convertToDTCG.js'; +import { stripMeta } from './stripMeta.js'; // Public style-dictionary/utils API export { @@ -32,5 +33,6 @@ export { convertToDTCG, convertJSONToDTCG, convertZIPToDTCG, + stripMeta, }; export * from '../common/formatHelpers/index.js'; diff --git a/lib/utils/stripMeta.js b/lib/utils/stripMeta.js new file mode 100644 index 000000000..0ab8b6516 --- /dev/null +++ b/lib/utils/stripMeta.js @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with + * the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/** + * @typedef {import('../../types/DesignToken.d.ts').TransformedTokens} TransformedTokens + * @typedef {{strip?: string[]; keep?: string[]; usesDtcg?: boolean;}} StripMetaOptions + */ + +/** + * @param {TransformedTokens} obj + * @param {StripMetaOptions} options + */ +export function _stripMeta(obj, options) { + const { strip, keep, usesDtcg } = options; + Object.keys(obj).forEach((key) => { + if (Object.hasOwn(obj, usesDtcg ? '$value' : 'value')) { + if (strip && strip.includes(key)) { + delete obj[key]; + } + + if (keep && !keep.includes(key)) { + delete obj[key]; + } + } + + if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { + _stripMeta(obj[key], options); + } + }); + + return obj; +} + +/** + * @param {TransformedTokens} obj + * @param {StripMetaOptions} options + */ +export function stripMeta(obj, options) { + const clone = structuredClone(obj); + return _stripMeta(clone, options); +} diff --git a/package-lock.json b/package-lock.json index c1f9a092a..6e9bdf0b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,6 @@ "mocha": "^10.2.0", "monaco-editor": "^0.47.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", "sass": "^1.69.5", "sharp": "^0.32.5", "starlight-links-validator": "^0.12.1", @@ -76,6 +75,9 @@ }, "engines": { "node": ">=18.0.0" + }, + "peerDependencies": { + "prettier": "3.x" } }, "node_modules/@75lb/deep-merge": { @@ -19429,10 +19431,11 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", - "dev": true, + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/package.json b/package.json index cedf8dacc..45dab8436 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,6 @@ "mocha": "^10.2.0", "monaco-editor": "^0.47.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", "sass": "^1.69.5", "sharp": "^0.32.5", "starlight-links-validator": "^0.12.1", @@ -162,5 +161,8 @@ "unist-util-visit": "^5.0.0", "uuid": "^9.0.1", "yaml": "^2.3.4" + }, + "peerDependencies": { + "prettier": "3.x" } }