diff --git a/.changeset/five-crews-hear.md b/.changeset/five-crews-hear.md new file mode 100644 index 000000000..7ae3db260 --- /dev/null +++ b/.changeset/five-crews-hear.md @@ -0,0 +1,36 @@ +--- +'style-dictionary': major +--- + +BREAKING: File headers, when registered, are put inside the `hooks.fileHeaders` property now, as opposed to `fileHeader`. +Note the change from singular to plural form here. + +Before: + +```js +export default { + fileHeader: { + foo: (defaultMessages = []) => [ + 'Ola, planet!', + ...defaultMessages, + 'Hello, World!' + ], + }, +} +``` + +After: + +```js +export default { + hooks: { + fileHeaders: { + foo: (defaultMessages = []) => [ + 'Ola, planet!', + ...defaultMessages, + 'Hello, World!' + ], + }, + }, +} +``` diff --git a/.changeset/large-peaches-fetch.md b/.changeset/large-peaches-fetch.md new file mode 100644 index 000000000..ca297758e --- /dev/null +++ b/.changeset/large-peaches-fetch.md @@ -0,0 +1,34 @@ +--- +'style-dictionary': major +--- + +BREAKING: Actions, when registered, are put inside the `hooks.actions` property now, as opposed to `action`. +Note the change from singular to plural form here. + +Before: + +```js +export default { + action: { + 'copy-assets': { + do: () => {} + undo: () => {} + } + }, +}; +``` + +After: + +```js +export default { + hooks: { + actions: { + 'copy-assets': { + do: () => {} + undo: () => {} + } + }, + }, +}; +``` diff --git a/.changeset/old-nails-hammer.md b/.changeset/old-nails-hammer.md new file mode 100644 index 000000000..e437f9c80 --- /dev/null +++ b/.changeset/old-nails-hammer.md @@ -0,0 +1,72 @@ +--- +'style-dictionary': major +--- + +Filters, when registered, are put inside the `hooks.filters` property now, as opposed to `filter`. +Note the change from singular to plural form here. + +Before: + +```js +export default { + filter: { + 'colors-only': (token) => token.type === 'color, + }, + platforms: { + css: { + files: [{ + format: 'css/variables', + destination: '_variables.css', + filter: 'colors-only', + }], + }, + }, +}; +``` + +After: + +```js +export default { + hooks: { + filters: { + 'colors-only': (token) => token.type === 'color, + }, + }, + platforms: { + css: { + files: [{ + format: 'css/variables', + destination: '_variables.css', + filter: 'colors-only', + }], + }, + }, +}; +``` + +In addition, when using [`registerFilter`](/reference/api#registerfilter) method, the name of the filter function is now `filter` instead of `matcher`. + +Before: + +```js title="build-tokens.js" del={5} ins={6} +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerFilter({ + name: 'colors-only', + matcher: (token) => token.type === 'color', +}) +``` + +After: + +```js title="build-tokens.js" del={5} ins={6} +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerFilter({ + name: 'colors-only', + filter: (token) => token.type === 'color', +}) +``` + +> These changes also apply for the `filter` function (previously `matcher`) inside `transforms`. diff --git a/.changeset/poor-parents-cover.md b/.changeset/poor-parents-cover.md new file mode 100644 index 000000000..41dc3369e --- /dev/null +++ b/.changeset/poor-parents-cover.md @@ -0,0 +1,28 @@ +--- +'style-dictionary': major +--- + +BREAKING: Transform groups, when registered, are put inside the `hooks.transformGroups` property now, as opposed to `transformGroup`. + +Before: + +```js +export default { + // register it inline or by SD.registerTransformGroup + transformGroup: { + foo: ['foo-transform'] + }, +}; +``` + +After: + +```js +export default { + hooks: { + transformGroups: { + foo: ['foo-transform'] + }, + }, +}; +``` diff --git a/.changeset/seven-candles-smile.md b/.changeset/seven-candles-smile.md new file mode 100644 index 000000000..63bd8416d --- /dev/null +++ b/.changeset/seven-candles-smile.md @@ -0,0 +1,67 @@ +--- +'style-dictionary': major +--- + +BREAKING: Formats, when registered, are put inside the `hooks.formats` property now, as opposed to `format`. +The `formatter` handler function has been renamed to `format` for consistency. + +The importable type interfaces have also been renamed, `Formatter` is now `FormatFn` and `FormatterArguments` is now `FormatFnArguments`. +Note that you can also use `Format['format']` instead of `FormatFn`, or `Parameters` instead of `FormatFnArguments`, so these renames may not matter. + +Before: + +```ts +import StyleDictionary from 'style-dictionary'; +import type { Formatter, FormatterArguments } from 'style-dictionary/types'; + +// register it with register method +StyleDictionary.registerFormat({ + name: 'custom/json', + formatter: ({ dictionary }) => JSON.stringify(dictionary, 2, null), +}) + +export default { + // OR define it inline + format: { + 'custom/json': ({ dictionary }) => JSON.stringify(dictionary, 2, null), + }, + platforms: { + json: { + files: [{ + destination: 'output.json', + format: 'custom/json' + }], + }, + }, +}; +``` + +After: + +```ts +import StyleDictionary from 'style-dictionary'; +import type { FormatFn, FormatFnArguments } from 'style-dictionary/types'; + +// register it with register method +StyleDictionary.registerFormat({ + name: 'custom/json', + format: ({ dictionary }) => JSON.stringify(dictionary, 2, null), +}) + +export default { + // OR define it inline + hooks: { + formats: { + 'custom/json': ({ dictionary }) => JSON.stringify(dictionary, 2, null), + }, + }, + platforms: { + json: { + files: [{ + destination: 'output.json', + format: 'custom/json' + }], + }, + }, +}; +``` \ No newline at end of file diff --git a/.changeset/smooth-jobs-attack.md b/.changeset/smooth-jobs-attack.md index 92986f17b..0c3dcebd9 100644 --- a/.changeset/smooth-jobs-attack.md +++ b/.changeset/smooth-jobs-attack.md @@ -2,7 +2,7 @@ 'style-dictionary': major --- -BREAKING: `className`, `packageName`, `mapName` and `type` options for a bunch of built-in formats have been moved from `file` to go inside the `file.options` object, for API consistency reasons. +BREAKING: `className`, `packageName`, `mapName`, `type`, `name`, `resourceType` and `resourceMap` options for a bunch of built-in formats have been moved from `file` to go inside the `file.options` object, for API consistency reasons. Before: diff --git a/.changeset/spicy-pears-work.md b/.changeset/spicy-pears-work.md new file mode 100644 index 000000000..875987e2d --- /dev/null +++ b/.changeset/spicy-pears-work.md @@ -0,0 +1,50 @@ +--- +'style-dictionary': major +--- + +BREAKING: Transforms, when registered, are put inside the `hooks.transforms` property now, as opposed to `transform`. +The `matcher` property has been renamed to `filter` (to align with the Filter hook change), and the `transformer` handler function has been renamed to `transform` for consistency. + +Before: + +```js +export default { + // register it inline or by SD.registerTransform + transform: { + 'color-transform': { + type: 'value', + matcher: (token) => token.type === 'color', + transformer: (token) => token.value, + }, + }, + platforms: { + css: { + // apply it per platform + transforms: ['color-transform'], + }, + }, +}; +``` + +After + +```js +export default { + // register it inline or by SD.registerTransform + hooks: { + transforms: { + 'color-transform': { + type: 'value', + filter: (token) => token.type === 'color', + transform: (token) => token.value, + }, + }, + }, + platforms: { + css: { + // apply it per platform + transforms: ['color-transform'], + }, + }, +}; +``` diff --git a/.changeset/wet-sloths-taste.md b/.changeset/wet-sloths-taste.md new file mode 100644 index 000000000..765ea5353 --- /dev/null +++ b/.changeset/wet-sloths-taste.md @@ -0,0 +1,41 @@ +--- +'style-dictionary': major +--- + +BREAKING: Parsers, when registered, are put inside the `hooks.parsers` property now, as opposed to `parsers`. +`parsers` property has been repurposed: you will now also need to explicitly apply registered parsers by name in the `parsers` property, they no longer apply by default. +When registering a parser, you must also supply a `name` property just like with all other hooks, and the `parse` function has been renamed to `parser` for consistency. + +Before: + +```js +export default { + // register it inline or by SD.registerPreprocessor + parsers: [ + { + pattern: /\.json5$/, + parse: ({ contents, filePath}) => { + return JSON5.parse(contents); + }, + } + ], +}; +``` + +After: + +```js +export default { + hooks: { + parsers: { + name: 'json5-parser', + pattern: /\.json5$/, + parser: ({ contents, filePath}) => { + return JSON5.parse(contents); + }, + } + }, + // apply it globally by name reference + parsers: ['json5-parser'], +}; +``` diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index c95cf610f..2149c7054 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -38,4 +38,4 @@ jobs: run: npx playwright install --with-deps chromium - name: Browser tests - run: npm run test:browser + run: npm run test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0e1b62c70..4728beb5b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ We use ESLint on the code to ensure a consistent style. Any new code committed m ### Code Rules -1. **Do not mutate token names or values in a format.** Mutations like this should happen in a transformer. +1. **Do not mutate token names or values in a format.** Mutations like this should happen in a transform. 1. **Be as generic as possible.** Do not hard-code any values or configuration in formats. 1. **Fail loudly.** Users should be aware if something is missing or configurations aren't correct. This will help debug any issues instead of failing silently. 1. **Rely on few dependencies.** This framework is meant to be extended and allows for customization. We don't want to bring a slew of dependencies that most people don't need. diff --git a/README.md b/README.md index b9c8ade36..201873000 100644 --- a/README.md +++ b/README.md @@ -298,10 +298,10 @@ const StyleDictionary = require('style-dictionary').extend('config.json'); StyleDictionary.registerTransform({ name: 'time/seconds', type: 'value', - matcher: function (token) { + filter: function (token) { return token.type === 'time'; }, - transformer: function (token) { + transform: function (token) { return (parseInt(token.original.value) / 1000).toString() + 's'; }, }); diff --git a/__integration__/async.test.js b/__integration__/async.test.js index 6ff25387c..a21495931 100644 --- a/__integration__/async.test.js +++ b/__integration__/async.test.js @@ -33,8 +33,9 @@ describe('integration', async function () { const SDExtension = class extends StyleDictionary {}; SDExtension.registerParser({ + name: 'json-parser', pattern: /^.+\.json$/g, - parse: async ({ contents }) => { + parser: async ({ contents }) => { await sleep(10); // TODO: verify this is called return JSON.parse(contents); @@ -58,8 +59,8 @@ describe('integration', async function () { SDExtension.registerTransform({ name: 'foo-value-transform', type: 'value', - matcher: (token) => token.value === 'foo', - transformer: async () => { + filter: (token) => token.value === 'foo', + transform: async () => { await sleep(10); return 'bar'; }, @@ -67,7 +68,7 @@ describe('integration', async function () { SDExtension.registerFormat({ name: 'custom/css', - formatter: async function ({ dictionary, file, options }) { + format: async function ({ dictionary, file, options }) { await sleep(10); const { outputReferences } = options; return ( diff --git a/__integration__/customFileHeader.test.js b/__integration__/customFileHeader.test.js index f29ecf774..49a5df987 100644 --- a/__integration__/customFileHeader.test.js +++ b/__integration__/customFileHeader.test.js @@ -28,9 +28,11 @@ describe(`integration`, async () => { }); const sd = new StyleDictionary({ - fileHeader: { - configFileHeader: (defaultMessage) => { - return [...defaultMessage, 'hello, world!']; + hooks: { + fileHeaders: { + configFileHeader: (defaultMessage) => { + return [...defaultMessage, 'hello, world!']; + }, }, }, diff --git a/__integration__/customFormats.test.js b/__integration__/customFormats.test.js index 8c334ee02..b548e25c6 100644 --- a/__integration__/customFormats.test.js +++ b/__integration__/customFormats.test.js @@ -22,12 +22,14 @@ describe('integration', async () => { const sd = new StyleDictionary({ source: [`__integration__/tokens/size/padding.json`], // Adding formats directly to SD - format: { - inlineCustomFormatWithOldArgs: (dictionary, platform, file) => { - return JSON.stringify({ dictionary, platform, file }, null, 2); - }, - inlineCustomFormatWithNewArgs: (opts) => { - return JSON.stringify(opts, null, 2); + hooks: { + formats: { + inlineCustomFormatWithOldArgs: (dictionary, platform, file) => { + return JSON.stringify({ dictionary, platform, file }, null, 2); + }, + inlineCustomFormatWithNewArgs: (opts) => { + return JSON.stringify(opts, null, 2); + }, }, }, platforms: { @@ -78,7 +80,7 @@ describe('integration', async () => { sd.registerFormat({ name: 'registerCustomFormatWithNewArgs', - formatter: (opts) => { + format: (opts) => { return JSON.stringify(opts, null, 2); }, }); diff --git a/__integration__/objectValues.test.js b/__integration__/objectValues.test.js index a09151066..82fcafabb 100644 --- a/__integration__/objectValues.test.js +++ b/__integration__/objectValues.test.js @@ -78,37 +78,41 @@ describe('integration', async () => { }, }, }, - transform: { - hsl: { - type: 'value', - transitive: true, - matcher: (token) => token.original.value.h, - transformer: (token) => { - return `hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`; + hooks: { + transforms: { + hsl: { + type: 'value', + transitive: true, + filter: (token) => token.original.value.h, + transform: (token) => { + return `hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`; + }, }, - }, - hslToHex: { - type: 'value', - transitive: true, - matcher: (token) => token.original.value.h, - transformer: (token) => { - return Color(`hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`).toHexString(); + hslToHex: { + type: 'value', + transitive: true, + filter: (token) => token.original.value.h, + transform: (token) => { + return Color( + `hsl(${token.value.h}, ${token.value.s}, ${token.value.l})`, + ).toHexString(); + }, }, - }, - cssBorder: { - type: 'value', - transitive: true, - matcher: (token) => token.path[0] === `border`, - transformer: (token) => { - return `${token.value.width} ${token.value.style} ${token.value.color}`; + cssBorder: { + type: 'value', + transitive: true, + filter: (token) => token.path[0] === `border`, + transform: (token) => { + return `${token.value.width} ${token.value.style} ${token.value.color}`; + }, }, - }, - shadow: { - type: 'value', - transitive: true, - matcher: (token) => token.type === 'shadow', - transformer: (token) => { - return token.value.map((obj) => obj.color).join(', '); + shadow: { + type: 'value', + transitive: true, + filter: (token) => token.type === 'shadow', + transform: (token) => { + return token.value.map((obj) => obj.color).join(', '); + }, }, }, }, @@ -117,7 +121,7 @@ describe('integration', async () => { // with and without `outputReferences` cssHsl: { buildPath, - transforms: StyleDictionary.transformGroup.css.concat([`hsl`]), + transforms: StyleDictionary.hooks.transformGroups.css.concat([`hsl`]), files: [ { destination: `hsl.css`, @@ -137,7 +141,7 @@ describe('integration', async () => { // transformed to a hex color works with and without `outputReferences` cssHex: { buildPath, - transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`, `hslToHex`]), + transforms: StyleDictionary.hooks.transformGroups.css.concat([`cssBorder`, `hslToHex`]), files: [ { destination: 'hex.css', @@ -157,7 +161,7 @@ describe('integration', async () => { // works with and without `outputReferences` cssBorder: { buildPath, - transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`]), + transforms: StyleDictionary.hooks.transformGroups.css.concat([`cssBorder`]), files: [ { destination: 'border.css', @@ -175,7 +179,7 @@ describe('integration', async () => { cssShadow: { buildPath, - transforms: StyleDictionary.transformGroup.css.concat([`shadow`, `hslToHex`]), + transforms: StyleDictionary.hooks.transformGroups.css.concat([`shadow`, `hslToHex`]), files: [ { destination: 'shadow.css', @@ -193,7 +197,7 @@ describe('integration', async () => { scss: { buildPath, - transforms: StyleDictionary.transformGroup.css.concat([`cssBorder`, `hslToHex`]), + transforms: StyleDictionary.hooks.transformGroups.css.concat([`cssBorder`, `hslToHex`]), files: [ { destination: 'border.scss', diff --git a/__integration__/outputReferences.test.js b/__integration__/outputReferences.test.js index 51f32e8da..1fde83684 100644 --- a/__integration__/outputReferences.test.js +++ b/__integration__/outputReferences.test.js @@ -45,19 +45,21 @@ describe('integration', async () => { type: 'color', }, }, - transform: { - 'rgb-in-rgba': { - type: 'value', - transitive: true, - matcher: (token) => token.type === 'color', - // quite naive transform to support rgb inside rgba - transformer: (token) => { - const reg = /rgba\((rgb\((\d,\d,\d)\)),((0\.)?\d+?)\)/g; - const match = reg.exec(token.value); - if (match && match[1] && match[2]) { - return token.value.replace(match[1], match[2]); - } - return token.value; + hooks: { + transforms: { + 'rgb-in-rgba': { + type: 'value', + transitive: true, + filter: (token) => token.type === 'color', + // quite naive transform to support rgb inside rgba + transform: (token) => { + const reg = /rgba\((rgb\((\d,\d,\d)\)),((0\.)?\d+?)\)/g; + const match = reg.exec(token.value); + if (match && match[1] && match[2]) { + return token.value.replace(match[1], match[2]); + } + return token.value; + }, }, }, }, diff --git a/__integration__/swift.test.js b/__integration__/swift.test.js index ec1717a37..7095002f6 100644 --- a/__integration__/swift.test.js +++ b/__integration__/swift.test.js @@ -72,13 +72,6 @@ describe('integration', async () => { await expect(output).to.matchSnapshot(); }); }); - - // describe(`separate`, async () => { - // const output = fs.readFileSync(`${buildPath}style_dictionary_color.dart`); - // it(`should match snapshot`, async () => { - // await expect(output).to.matchSnapshot(); - // }); - // }); }); }); }); diff --git a/__integration__/w3c-forward-compat.test.js b/__integration__/w3c-forward-compat.test.js index 07566ab80..802d657b4 100644 --- a/__integration__/w3c-forward-compat.test.js +++ b/__integration__/w3c-forward-compat.test.js @@ -43,19 +43,21 @@ describe('integration', async () => { }, }, }, - transform: { - 'custom/css/color': { - type: 'value', - matcher: (token) => token.$type === 'color', - transformer: (token) => { - return Color(sd.usesDtcg ? token.$value : token.value).toRgbString(); + hooks: { + transforms: { + 'custom/css/color': { + type: 'value', + filter: (token) => token.$type === 'color', + transform: (token) => { + return Color(sd.usesDtcg ? token.$value : token.value).toRgbString(); + }, }, - }, - 'custom/add/px': { - type: 'value', - matcher: (token) => token.$type === 'dimension', - transformer: (token) => { - return `${sd.usesDtcg ? token.$value : token.value}px`; + 'custom/add/px': { + type: 'value', + filter: (token) => token.$type === 'dimension', + transform: (token) => { + return `${sd.usesDtcg ? token.$value : token.value}px`; + }, }, }, }, diff --git a/__tests__/buildFiles.test.js b/__tests__/buildFiles.test.js index e445ab926..d1f664531 100644 --- a/__tests__/buildFiles.test.js +++ b/__tests__/buildFiles.test.js @@ -61,7 +61,7 @@ const platformWithFilter = { ], }; -const platformWithoutFormatter = { +const platformWithoutFormat = { buildPath: '__tests__/__output/', files: [ { @@ -98,7 +98,7 @@ describe('buildFiles', () => { }); it('should throw if missing a format', async () => { - await expect(buildFiles(dictionary, platformWithoutFormatter)).to.eventually.rejectedWith( + await expect(buildFiles(dictionary, platformWithoutFormat)).to.eventually.rejectedWith( 'Please supply a format', ); }); diff --git a/__tests__/common/transforms.test.js b/__tests__/common/transforms.test.js index 1bdd57ca2..dd1802b0a 100644 --- a/__tests__/common/transforms.test.js +++ b/__tests__/common/transforms.test.js @@ -20,7 +20,7 @@ describe('common', () => { describe('name/camel', () => { it('should handle prefix', () => { expect( - transforms['name/camel'].transformer( + transforms['name/camel'].transform( { path: ['one', 'two', 'three'], }, @@ -33,7 +33,7 @@ describe('common', () => { it('should handle no prefix', () => { expect( - transforms['name/camel'].transformer( + transforms['name/camel'].transform( { path: ['one', 'two', 'three'], }, @@ -46,7 +46,7 @@ describe('common', () => { describe('name/kebab', () => { it('should handle prefix', () => { expect( - transforms['name/kebab'].transformer( + transforms['name/kebab'].transform( { path: ['one', 'two', 'three'], }, @@ -59,7 +59,7 @@ describe('common', () => { it('should handle no prefix', () => { expect( - transforms['name/kebab'].transformer( + transforms['name/kebab'].transform( { path: ['one', 'two', 'three'], }, @@ -72,7 +72,7 @@ describe('common', () => { describe('name/snake', () => { it('should handle prefix', () => { expect( - transforms['name/snake'].transformer( + transforms['name/snake'].transform( { path: ['one', 'two', 'three'], }, @@ -85,7 +85,7 @@ describe('common', () => { it('should handle no prefix', () => { expect( - transforms['name/snake'].transformer( + transforms['name/snake'].transform( { path: ['one', 'two', 'three'], }, @@ -98,7 +98,7 @@ describe('common', () => { describe('name/constant', () => { it('should handle prefix', () => { expect( - transforms['name/constant'].transformer( + transforms['name/constant'].transform( { path: ['one', 'two', 'three'], }, @@ -111,7 +111,7 @@ describe('common', () => { it('should handle no prefix', () => { expect( - transforms['name/constant'].transformer( + transforms['name/constant'].transform( { path: ['one', 'two', 'three'], }, @@ -123,7 +123,7 @@ describe('common', () => { describe('attribute/color', () => { it('should handle normal colors', () => { - const attributes = transforms['attribute/color'].transformer( + const attributes = transforms['attribute/color'].transform( { value: '#aaaaaa', }, @@ -135,14 +135,14 @@ describe('common', () => { expect(attributes).to.have.nested.property('hsl.s', 0); }); it('should handle colors with transparency', () => { - const attributes = transforms['attribute/color'].transformer( + const attributes = transforms['attribute/color'].transform( { value: '#aaaaaa99', }, {}, {}, ); - const attributes2 = transforms['attribute/color'].transformer( + const attributes2 = transforms['attribute/color'].transform( { value: 'rgba(170,170,170,0.6)', }, @@ -169,9 +169,9 @@ describe('common', () => { attributes: { category: 'size', component: 'button' }, }; - const attrs = transforms['attribute/cti'].transformer(prop, {}, {}); - const attrsShort = transforms['attribute/cti'].transformer(propShort, {}, {}); - const attrsOverride = transforms['attribute/cti'].transformer(propOverride, {}, {}); + const attrs = transforms['attribute/cti'].transform(prop, {}, {}); + const attrsShort = transforms['attribute/cti'].transform(propShort, {}, {}); + const attrsOverride = transforms['attribute/cti'].transform(propOverride, {}, {}); it('should assign attributes correctly', () => { expect(attrs).eql({ @@ -202,7 +202,7 @@ describe('common', () => { describe('color/hex', () => { it('should handle hex colors', () => { - const value = transforms['color/hex'].transformer( + const value = transforms['color/hex'].transform( { value: '#aaaaaa', }, @@ -213,7 +213,7 @@ describe('common', () => { }); it('should handle hex8 colors', () => { - const value = transforms['color/hex'].transformer( + const value = transforms['color/hex'].transform( { value: '#aaaaaaaa', }, @@ -224,7 +224,7 @@ describe('common', () => { }); it('should handle rgb colors', () => { - const value = transforms['color/hex'].transformer( + const value = transforms['color/hex'].transform( { value: 'rgb(170,170,170)', }, @@ -235,7 +235,7 @@ describe('common', () => { }); it('should handle rgb (object) colors', () => { - const value = transforms['color/hex'].transformer( + const value = transforms['color/hex'].transform( { value: { r: '170', @@ -246,7 +246,7 @@ describe('common', () => { {}, {}, ); - const value2 = transforms['color/hex'].transformer( + const value2 = transforms['color/hex'].transform( { value: 'rgb(170,170,170)', }, @@ -258,7 +258,7 @@ describe('common', () => { }); it('should handle hsl colors', () => { - const value = transforms['color/hex'].transformer( + const value = transforms['color/hex'].transform( { value: { h: '0', @@ -269,7 +269,7 @@ describe('common', () => { {}, {}, ); - const value2 = transforms['color/hex'].transformer( + const value2 = transforms['color/hex'].transform( { value: 'hsl(0,0,0.5)', }, @@ -283,7 +283,7 @@ describe('common', () => { describe('color/hex8', () => { it('should handle hex colors', () => { - const value = transforms['color/hex8'].transformer( + const value = transforms['color/hex8'].transform( { value: '#aaaaaa', }, @@ -294,7 +294,7 @@ describe('common', () => { }); it('should handle rgb colors', () => { - const value = transforms['color/hex8'].transformer( + const value = transforms['color/hex8'].transform( { value: 'rgb(170,170,170)', }, @@ -305,7 +305,7 @@ describe('common', () => { }); it('should handle rgba colors', () => { - const value = transforms['color/hex8'].transformer( + const value = transforms['color/hex8'].transform( { value: 'rgba(170,170,170,0.6)', }, @@ -318,7 +318,7 @@ describe('common', () => { describe('color/hex8android', () => { it('should handle colors without alpha', () => { - const value = transforms['color/hex8android'].transformer( + const value = transforms['color/hex8android'].transform( { value: '#aaaaaa', }, @@ -329,7 +329,7 @@ describe('common', () => { }); it('should handle colors with alpha', () => { - const value = transforms['color/hex8android'].transformer( + const value = transforms['color/hex8android'].transform( { value: '#aaaaaa99', }, @@ -342,7 +342,7 @@ describe('common', () => { describe('color/rgb', () => { it('should handle normal colors', () => { - const value = transforms['color/rgb'].transformer( + const value = transforms['color/rgb'].transform( { value: '#aaaaaa', }, @@ -353,7 +353,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/rgb'].transformer( + const value = transforms['color/rgb'].transform( { value: '#aaaaaa99', }, @@ -366,7 +366,7 @@ describe('common', () => { describe('color/hsl-4', () => { it('should handle normal colors', () => { - const value = transforms['color/hsl-4'].transformer( + const value = transforms['color/hsl-4'].transform( { value: '#009688', }, @@ -377,7 +377,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/hsl-4'].transformer( + const value = transforms['color/hsl-4'].transform( { value: '#00968899', }, @@ -390,7 +390,7 @@ describe('common', () => { describe('color/hsl', () => { it('should handle normal colors', () => { - const value = transforms['color/hsl'].transformer( + const value = transforms['color/hsl'].transform( { value: '#009688', }, @@ -401,7 +401,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/hsl'].transformer( + const value = transforms['color/hsl'].transform( { value: '#00968899', }, @@ -414,7 +414,7 @@ describe('common', () => { describe('color/composeColor', () => { it('should handle color without alpha', () => { - const value = transforms['color/composeColor'].transformer( + const value = transforms['color/composeColor'].transform( { value: '#aaaaaa', }, @@ -425,7 +425,7 @@ describe('common', () => { }); it('should handle color with alpha', () => { - const value = transforms['color/composeColor'].transformer( + const value = transforms['color/composeColor'].transform( { value: '#aaaaaaff', }, @@ -438,7 +438,7 @@ describe('common', () => { describe('color/UIColor', () => { it('should handle normal colors', () => { - const value = transforms['color/UIColor'].transformer( + const value = transforms['color/UIColor'].transform( { value: '#aaaaaa', }, @@ -451,7 +451,7 @@ describe('common', () => { }); it('should retain enough precision when converting to decimal', () => { - const value = transforms['color/UIColor'].transformer( + const value = transforms['color/UIColor'].transform( { value: '#1d1d1d', }, @@ -464,7 +464,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/UIColor'].transformer( + const value = transforms['color/UIColor'].transform( { value: '#aaaaaa99', }, @@ -479,7 +479,7 @@ describe('common', () => { describe('color/UIColorSwift', () => { it('should handle normal colors', () => { - const value = transforms['color/UIColorSwift'].transformer( + const value = transforms['color/UIColorSwift'].transform( { value: '#aaaaaa', }, @@ -490,7 +490,7 @@ describe('common', () => { }); it('should retain enough precision when converting to decimal', () => { - const value = transforms['color/UIColorSwift'].transformer( + const value = transforms['color/UIColorSwift'].transform( { value: '#1d1d1d', }, @@ -501,7 +501,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/UIColorSwift'].transformer( + const value = transforms['color/UIColorSwift'].transform( { value: '#aaaaaa99', }, @@ -514,7 +514,7 @@ describe('common', () => { describe('color/ColorSwiftUI', () => { it('should handle normal colors', () => { - const value = transforms['color/ColorSwiftUI'].transformer( + const value = transforms['color/ColorSwiftUI'].transform( { value: '#aaaaaa', }, @@ -525,7 +525,7 @@ describe('common', () => { }); it('should retain enough precision when converting to decimal', () => { - const value = transforms['color/ColorSwiftUI'].transformer( + const value = transforms['color/ColorSwiftUI'].transform( { value: '#1d1d1d', }, @@ -536,7 +536,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/ColorSwiftUI'].transformer( + const value = transforms['color/ColorSwiftUI'].transform( { value: '#aaaaaa99', }, @@ -549,7 +549,7 @@ describe('common', () => { describe('color/hex8flutter', () => { it('should handle colors without alpha', () => { - const value = transforms['color/hex8flutter'].transformer( + const value = transforms['color/hex8flutter'].transform( { value: '#aaaaaa', }, @@ -560,7 +560,7 @@ describe('common', () => { }); it('should handle colors with alpha', () => { - const value = transforms['color/hex8flutter'].transformer( + const value = transforms['color/hex8flutter'].transform( { value: '#aaaaaa99', }, @@ -573,7 +573,7 @@ describe('common', () => { describe('color/css', () => { it('should handle normal colors', () => { - const value = transforms['color/css'].transformer( + const value = transforms['color/css'].transform( { value: 'rgb(170, 170, 170)', }, @@ -584,7 +584,7 @@ describe('common', () => { }); it('should handle colors with transparency', () => { - const value = transforms['color/css'].transformer( + const value = transforms['color/css'].transform( { value: '#aaaaaa99', }, @@ -598,7 +598,7 @@ describe('common', () => { describe('color/sketch', () => { it('should retain hex specificity', () => { const originalHex = '#0b7dbb'; - const value = transforms['color/sketch'].transformer( + const value = transforms['color/sketch'].transform( { value: originalHex, }, @@ -616,14 +616,14 @@ describe('common', () => { describe('size/sp', () => { it('should work', () => { - const value = transforms['size/sp'].transformer( + const value = transforms['size/sp'].transform( { value: '12px', }, {}, {}, ); - const value2 = transforms['size/sp'].transformer( + const value2 = transforms['size/sp'].transform( { value: '12', }, @@ -634,20 +634,20 @@ describe('common', () => { expect(value2).to.equal('12.00sp'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/sp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/sp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/dp', () => { it('should work', () => { - const value = transforms['size/dp'].transformer( + const value = transforms['size/dp'].transform( { value: '12px', }, {}, {}, ); - const value2 = transforms['size/dp'].transformer( + const value2 = transforms['size/dp'].transform( { value: '12', }, @@ -658,13 +658,13 @@ describe('common', () => { expect(value2).to.equal('12.00dp'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' })).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' })).to.throw(); }); }); describe('size/object', () => { it('should work', () => { - const value = transforms['size/object'].transformer( + const value = transforms['size/object'].transform( { value: '1px', }, @@ -677,7 +677,7 @@ describe('common', () => { expect(value.scale).to.equal(16); }); it('should work with custom base font', () => { - const value = transforms['size/object'].transformer( + const value = transforms['size/object'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -688,13 +688,13 @@ describe('common', () => { expect(value.scale).to.equal(14); }); it('should throw an error if prop value is NaN', () => { - expect(() => transforms['size/object'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/object'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/remToSp', () => { it('should work', () => { - const value = transforms['size/remToSp'].transformer( + const value = transforms['size/remToSp'].transform( { value: '1', }, @@ -704,7 +704,7 @@ describe('common', () => { expect(value).to.equal('16.00sp'); }); it('converts rem to sp using custom base font', () => { - const value = transforms['size/remToSp'].transformer( + const value = transforms['size/remToSp'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -712,13 +712,13 @@ describe('common', () => { expect(value).to.equal('14.00sp'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/remToDp', () => { it('should work', () => { - const value = transforms['size/remToDp'].transformer( + const value = transforms['size/remToDp'].transform( { value: '1', }, @@ -728,7 +728,7 @@ describe('common', () => { expect(value).to.equal('16.00dp'); }); it('converts rem to dp using custom base font', () => { - const value = transforms['size/remToDp'].transformer( + const value = transforms['size/remToDp'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -736,13 +736,13 @@ describe('common', () => { expect(value).to.equal('14.00dp'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/px', () => { it('should work', () => { - const value = transforms['size/px'].transformer( + const value = transforms['size/px'].transform( { value: '10', }, @@ -752,13 +752,13 @@ describe('common', () => { expect(value).to.equal('10px'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/remToPt', () => { it('should work', () => { - const value = transforms['size/remToPt'].transformer( + const value = transforms['size/remToPt'].transform( { value: '1', }, @@ -768,7 +768,7 @@ describe('common', () => { expect(value).to.equal('16.00f'); }); it('converts rem to pt using custom base font', () => { - const value = transforms['size/remToPt'].transformer( + const value = transforms['size/remToPt'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -776,13 +776,13 @@ describe('common', () => { expect(value).to.equal('14.00f'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/compose/remToSp', () => { it('should work', () => { - const value = transforms['size/compose/remToSp'].transformer( + const value = transforms['size/compose/remToSp'].transform( { value: '1', }, @@ -792,7 +792,7 @@ describe('common', () => { expect(value).to.equal('16.00.sp'); }); it('converts rem to sp using custom base font', () => { - const value = transforms['size/compose/remToSp'].transformer( + const value = transforms['size/compose/remToSp'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -801,14 +801,14 @@ describe('common', () => { }); it('should throw an error if prop value is Nan', () => { expect(() => - transforms['size/compose/remToSp'].transformer({ value: 'a' }, {}, {}), + transforms['size/compose/remToSp'].transform({ value: 'a' }, {}, {}), ).to.throw(); }); }); describe('size/compose/em', () => { it('should work', () => { - const value = transforms['size/compose/em'].transformer( + const value = transforms['size/compose/em'].transform( { value: '10', }, @@ -818,13 +818,13 @@ describe('common', () => { expect(value).to.equal('10.em'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/compose/em'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/compose/em'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/compose/remToDp', () => { it('should work', () => { - const value = transforms['size/compose/remToDp'].transformer( + const value = transforms['size/compose/remToDp'].transform( { value: '1', }, @@ -834,7 +834,7 @@ describe('common', () => { expect(value).to.equal('16.00.dp'); }); it('converts rem to dp using custom base font', () => { - const value = transforms['size/compose/remToDp'].transformer( + const value = transforms['size/compose/remToDp'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -843,14 +843,14 @@ describe('common', () => { }); it('should throw an error if prop value is Nan', () => { expect(() => - transforms['size/compose/remToDp'].transformer({ value: 'a' }, {}, {}), + transforms['size/compose/remToDp'].transform({ value: 'a' }, {}, {}), ).to.throw(); }); }); describe('size/swift/remToCGFloat', () => { it('should work', () => { - const value = transforms['size/swift/remToCGFloat'].transformer( + const value = transforms['size/swift/remToCGFloat'].transform( { value: '1', }, @@ -860,7 +860,7 @@ describe('common', () => { expect(value).to.equal('CGFloat(16.00)'); }); it('converts rem to CGFloat using custom base font', () => { - const value = transforms['size/swift/remToCGFloat'].transformer( + const value = transforms['size/swift/remToCGFloat'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -869,14 +869,14 @@ describe('common', () => { }); it('should throw an error if prop value is Nan', () => { expect(() => - transforms['size/rem/remToCGFloat'].transformer({ value: 'a' }, {}, {}), + transforms['size/rem/remToCGFloat'].transform({ value: 'a' }, {}, {}), ).to.throw(); }); }); describe('size/remToPx', () => { it('should work', () => { - const value = transforms['size/remToPx'].transformer( + const value = transforms['size/remToPx'].transform( { value: '1', }, @@ -886,7 +886,7 @@ describe('common', () => { expect(value).to.equal('16px'); }); it('converts rem to px using custom base font', () => { - const value = transforms['size/remToPx'].transformer( + const value = transforms['size/remToPx'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -894,34 +894,34 @@ describe('common', () => { expect(value).to.equal('14px'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/pxToRem', () => { - const pxToRemTransformer = transforms['size/pxToRem'].transformer; + const pxToRemtransform = transforms['size/pxToRem'].transform; ['12', '12px', '12rem'].forEach((value) => { it(`ignoring unit, scales "${value}" to rem`, () => { - expect(pxToRemTransformer({ value }, {}, {})).to.equal('0.75rem'); + expect(pxToRemtransform({ value }, {}, {})).to.equal('0.75rem'); }); }); it('converts pixel to rem using custom base font', () => { - expect(pxToRemTransformer({ value: '14px' }, { basePxFontSize: 14 }, {})).to.equal('1rem'); + expect(pxToRemtransform({ value: '14px' }, { basePxFontSize: 14 }, {})).to.equal('1rem'); }); ['0', '0px', '0rem'].forEach((value) => { it(`zero value "${value}" is returned without a unit`, () => { - expect(pxToRemTransformer({ value }, {}, {})).to.equal('0'); + expect(pxToRemtransform({ value }, {}, {})).to.equal('0'); }); }); it('should throw an error if prop value is Nan', () => { - expect(() => pxToRemTransformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => pxToRemtransform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/rem', () => { it('should work', () => { - const value = transforms['size/rem'].transformer( + const value = transforms['size/rem'].transform( { value: '1', }, @@ -931,13 +931,13 @@ describe('common', () => { expect(value).to.equal('1rem'); }); it('should throw an error if prop value is Nan', () => { - expect(() => transforms['size/dp'].transformer({ value: 'a' }, {}, {})).to.throw(); + expect(() => transforms['size/dp'].transform({ value: 'a' }, {}, {})).to.throw(); }); }); describe('size/flutter/remToDouble', () => { it('should work', () => { - const value = transforms['size/flutter/remToDouble'].transformer( + const value = transforms['size/flutter/remToDouble'].transform( { value: '1', }, @@ -947,7 +947,7 @@ describe('common', () => { expect(value).to.equal('16.00'); }); it('converts rem to double using custom base font', () => { - const value = transforms['size/flutter/remToDouble'].transformer( + const value = transforms['size/flutter/remToDouble'].transform( { value: '1' }, { basePxFontSize: 14 }, {}, @@ -958,7 +958,7 @@ describe('common', () => { describe('content/quote', () => { it('should work', () => { - const value = transforms['content/quote'].transformer( + const value = transforms['content/quote'].transform( { value: 'hello', }, @@ -971,7 +971,7 @@ describe('common', () => { describe('html/icon', () => { it('should work', () => { - const value = transforms['html/icon'].transformer( + const value = transforms['html/icon'].transform( { value: '', }, @@ -984,7 +984,7 @@ describe('common', () => { describe('content/objC/literal', () => { it('should work', () => { - const value = transforms['content/objC/literal'].transformer( + const value = transforms['content/objC/literal'].transform( { value: 'hello', }, @@ -997,7 +997,7 @@ describe('common', () => { describe('asset/url', () => { it('should work', () => { - const value = transforms['asset/url'].transformer( + const value = transforms['asset/url'].transform( { value: 'https://example.com', }, @@ -1008,7 +1008,7 @@ describe('common', () => { }); it('should escape double quotes', () => { - const value = transforms['asset/url'].transformer( + const value = transforms['asset/url'].transform( { value: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="90" height="45"%3E%3Cpath d="M10 10h60" stroke="%2300F" stroke-width="5"/%3E%3Cpath d="M10 20h60" stroke="%230F0" stroke-width="5"/%3E%3Cpath d="M10 30h60" stroke="red" stroke-width="5"/%3E%3C/svg%3E"', @@ -1024,7 +1024,7 @@ describe('common', () => { describe('asset/objC/literal', () => { it('should work', () => { - const value = transforms['asset/objC/literal'].transformer( + const value = transforms['asset/objC/literal'].transform( { value: 'hello', }, @@ -1037,7 +1037,7 @@ describe('common', () => { describe('time/seconds', () => { it('should work', () => { - const value = transforms['time/seconds'].transformer( + const value = transforms['time/seconds'].transform( { value: '1000', }, @@ -1053,7 +1053,7 @@ describe('common', () => { // the filePath of the token to determine where the asset is located relative to the token that refers to it describe.skip('asset/path', () => { it('should work', () => { - const value = transforms['asset/path'].transformer( + const value = transforms['asset/path'].transform( { value: 'foo.json', }, diff --git a/__tests__/exportPlatform.test.js b/__tests__/exportPlatform.test.js index 49d3da7b7..fbb505467 100644 --- a/__tests__/exportPlatform.test.js +++ b/__tests__/exportPlatform.test.js @@ -19,8 +19,9 @@ const config = fileToJSON('__tests__/__configs/test.json'); describe('exportPlatform', () => { let styleDictionary; - beforeEach(() => { + beforeEach(async () => { styleDictionary = new StyleDictionary(config); + await styleDictionary.hasInitialized; }); it('should throw if not given a platform', async () => { @@ -72,10 +73,10 @@ describe('exportPlatform', () => { type: 'value', name: 'color/darken', transitive: true, - matcher: function (prop) { + filter: function (prop) { return !!prop.original.transformColor; }, - transformer: function (prop) { + transform: function (prop) { return prop.value + '-darker'; }, }); @@ -97,11 +98,13 @@ describe('exportPlatform', () => { eight: { value: '{one.value}' }, nine: { value: '{eight.value}' }, }, - transform: { - transitive: { - type: 'value', - transitive: true, - transformer: (token) => `${token.value}-bar`, + hooks: { + transforms: { + transitive: { + type: 'value', + transitive: true, + transform: (token) => `${token.value}-bar`, + }, }, }, platforms: { @@ -148,8 +151,8 @@ describe('exportPlatform', () => { type: 'value', name: 'color/darken', transitive: true, - matcher: (token) => token.type === 'color', - transformer: (token) => { + filter: (token) => token.type === 'color', + transform: (token) => { const darkenMod = token?.$extensions?.['bar.foo']?.darken; if (usesReferences(darkenMod)) { // defer this transform, because our darken value is a ref @@ -353,14 +356,16 @@ describe('exportPlatform', () => { }; // making the css/color transform transitive so we can be sure the references // get resolved properly and transformed. - const transitiveTransform = Object.assign({}, StyleDictionary.transform['color/css'], { + const transitiveTransform = Object.assign({}, StyleDictionary.hooks.transforms['color/css'], { transitive: true, }); const sd = new StyleDictionary({ tokens, - transform: { - transitiveTransform, + hooks: { + transforms: { + transitiveTransform, + }, }, platforms: { css: { @@ -471,14 +476,16 @@ Use log.verbosity "verbose" or use CLI option --verbose for more details.`; reftest2: { $value: '{one}' }, one: { $value: '1' }, }, - transform: { - 'custom/add/px': { - type: 'value', - matcher: (token) => { - return token.$type === 'dimension'; - }, - transformer: (token) => { - return `${sd.usesDtcg ? token.$value : token.value}px`; + hooks: { + transforms: { + 'custom/add/px': { + type: 'value', + filter: (token) => { + return token.$type === 'dimension'; + }, + transform: (token) => { + return `${sd.usesDtcg ? token.$value : token.value}px`; + }, }, }, }, diff --git a/__tests__/formats/androidResources.test.js b/__tests__/formats/androidResources.test.js index 4f09b0598..fdb01385f 100644 --- a/__tests__/formats/androidResources.test.js +++ b/__tests__/formats/androidResources.test.js @@ -111,7 +111,7 @@ describe('formats', () => { }); it('with resourceType override should match snapshot', async () => { - const file = { resourceType: 'dimen' }; + const file = { options: { resourceType: 'dimen' } }; const f = await format( createFormatArgs({ dictionary: { tokens, allTokens: flattenTokens(tokens) }, @@ -126,10 +126,12 @@ describe('formats', () => { it('with resourceMap override should match snapshot', async () => { const file = { - resourceMap: { - color: 'color', - fontColor: 'color', - backgroundColor: 'color', + options: { + resourceMap: { + color: 'color', + fontColor: 'color', + backgroundColor: 'color', + }, }, }; const f = await format( diff --git a/__tests__/formats/javascriptObject.test.js b/__tests__/formats/javascriptObject.test.js index 386a83c0c..e46ee869d 100644 --- a/__tests__/formats/javascriptObject.test.js +++ b/__tests__/formats/javascriptObject.test.js @@ -18,7 +18,7 @@ import flattenTokens from '../../lib/utils/flattenTokens.js'; const file = { destination: '__output/', format: 'javascript/object', - name: 'foo', + options: { name: 'foo' }, }; const tokens = { diff --git a/__tests__/register/action.test.js b/__tests__/register/action.test.js index 43aafc9aa..2167b62ca 100644 --- a/__tests__/register/action.test.js +++ b/__tests__/register/action.test.js @@ -20,7 +20,7 @@ registerSuite({ undo: () => {}, }, registerMethod: 'registerAction', - prop: 'action', + prop: 'actions', }); describe('register', () => { @@ -97,7 +97,7 @@ describe('register', () => { name: 'scss', do: function () {}, }); - expect(typeof StyleDictionaryExtended.action['scss'].do).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.actions['scss'].do).to.equal('function'); }); it('should handle an undo function', () => { @@ -106,12 +106,12 @@ describe('register', () => { do: function () {}, undo: function () {}, }); - expect(typeof StyleDictionaryExtended.action['scss'].undo).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.actions['scss'].undo).to.equal('function'); }); it('should properly pass the registered format to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.action['scss'].do).to.equal('function'); + expect(typeof SDE2.hooks.actions['scss'].do).to.equal('function'); }); }); }); diff --git a/__tests__/register/fileHeader.test.js b/__tests__/register/fileHeader.test.js index 53cc83ee3..5a30bbfa8 100644 --- a/__tests__/register/fileHeader.test.js +++ b/__tests__/register/fileHeader.test.js @@ -19,7 +19,7 @@ registerSuite({ fileHeader: () => {}, }, registerMethod: 'registerFileHeader', - prop: 'fileHeader', + prop: 'fileHeaders', }); describe('register', () => { @@ -50,7 +50,7 @@ describe('register', () => { expect(() => { StyleDictionaryExtended.registerFileHeader({ name: {}, - matcher: function () {}, + fileHeader: function () {}, }); }).to.throw('name must be a string'); }); @@ -91,17 +91,19 @@ describe('register', () => { }).to.throw('fileHeader must be a function'); }); - it('should work if name and matcher are good', () => { + it('should work if name and fileHeader are good', () => { StyleDictionaryExtended.registerFileHeader({ name: 'myCustomHeader', fileHeader: function () {}, }); - expect(typeof StyleDictionaryExtended.fileHeader['myCustomHeader']).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.fileHeaders['myCustomHeader']).to.equal( + 'function', + ); }); it('should properly pass the registered fileHeader to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.fileHeader['myCustomHeader']).to.equal('function'); + expect(typeof SDE2.hooks.fileHeaders['myCustomHeader']).to.equal('function'); }); }); }); diff --git a/__tests__/register/filter.test.js b/__tests__/register/filter.test.js index 57dcd00a5..b6b06be0f 100644 --- a/__tests__/register/filter.test.js +++ b/__tests__/register/filter.test.js @@ -16,10 +16,10 @@ import { registerSuite } from './register.suite.js'; registerSuite({ config: { - matcher: () => {}, + filter: () => {}, }, registerMethod: 'registerFilter', - prop: 'filter', + prop: 'filters', }); describe('register', () => { @@ -29,79 +29,79 @@ describe('register', () => { it('should error if name is not a string', () => { expect(() => { StyleDictionaryExtended.registerFilter({ - matcher: function () {}, + filter: function () {}, }); }).to.throw('name must be a string'); expect(() => { StyleDictionaryExtended.registerFilter({ name: 1, - matcher: function () {}, + filter: function () {}, }); }).to.throw('name must be a string'); expect(() => { StyleDictionaryExtended.registerFilter({ name: [], - matcher: function () {}, + filter: function () {}, }); }).to.throw('name must be a string'); expect(() => { StyleDictionaryExtended.registerFilter({ name: {}, - matcher: function () {}, + filter: function () {}, }); }).to.throw('name must be a string'); }); - it('should error if matcher is not a function', () => { + it('should error if filter is not a function', () => { expect(() => { StyleDictionaryExtended.registerFilter({ name: 'test', }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); expect(() => { StyleDictionaryExtended.registerFilter({ name: 'test', - matcher: 1, + filter: 1, }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); expect(() => { StyleDictionaryExtended.registerFilter({ name: 'test', - matcher: 'name', + filter: 'name', }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); expect(() => { StyleDictionaryExtended.registerFilter({ name: 'test', - matcher: [], + filter: [], }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); expect(() => { StyleDictionaryExtended.registerFilter({ name: 'test', - matcher: {}, + filter: {}, }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); }); - it('should work if name and matcher are good', () => { + it('should work if name and filter are good', () => { StyleDictionaryExtended.registerFilter({ name: 'scss', - matcher: function () {}, + filter: function () {}, }); - expect(typeof StyleDictionaryExtended.filter['scss']).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.filters['scss']).to.equal('function'); }); it('should properly pass the registered filter to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.filter['scss']).to.equal('function'); + expect(typeof SDE2.hooks.filters['scss']).to.equal('function'); }); }); }); diff --git a/__tests__/register/format.test.js b/__tests__/register/format.test.js index 4db414eeb..80dd6872d 100644 --- a/__tests__/register/format.test.js +++ b/__tests__/register/format.test.js @@ -16,10 +16,10 @@ import { registerSuite } from './register.suite.js'; registerSuite({ config: { - formatter: () => {}, + format: () => {}, }, registerMethod: 'registerFormat', - prop: 'format', + prop: 'formats', }); describe('register', () => { @@ -30,34 +30,34 @@ describe('register', () => { const errorMessage = `Can't register format; format.name must be a string`; expect(() => { StyleDictionaryExtended.registerFormat({ - formatter: function () {}, + format: function () {}, }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: 1, - formatter: function () {}, + format: function () {}, }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: [], - formatter: function () {}, + format: function () {}, }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: {}, - formatter: function () {}, + format: function () {}, }); }).to.throw(errorMessage); }); it('should error if format is not a function', () => { - const errorMessage = `Can't register format; format.formatter must be a function`; + const errorMessage = `Can't register format; format.format must be a function`; expect(() => { StyleDictionaryExtended.registerFormat({ name: 'test', @@ -67,28 +67,28 @@ describe('register', () => { expect(() => { StyleDictionaryExtended.registerFormat({ name: 'test', - formatter: 1, + format: 1, }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: 'test', - formatter: 'name', + format: 'name', }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: 'test', - formatter: [], + format: [], }); }).to.throw(errorMessage); expect(() => { StyleDictionaryExtended.registerFormat({ name: 'test', - formatter: {}, + format: {}, }); }).to.throw(errorMessage); }); @@ -96,14 +96,14 @@ describe('register', () => { it('should work if name and format are good', () => { StyleDictionaryExtended.registerFormat({ name: 'scss', - formatter: function () {}, + format: function () {}, }); - expect(typeof StyleDictionaryExtended.format['scss']).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.formats['scss']).to.equal('function'); }); it('should properly pass the registered format to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.format['scss']).to.equal('function'); + expect(typeof SDE2.hooks.formats['scss']).to.equal('function'); }); }); }); diff --git a/__tests__/register/parser.test.js b/__tests__/register/parser.test.js index 0c21bbf4c..b52291d5a 100644 --- a/__tests__/register/parser.test.js +++ b/__tests__/register/parser.test.js @@ -12,46 +12,67 @@ */ import { expect } from 'chai'; import StyleDictionary from 'style-dictionary'; -// import { registerSuite } from './register.suite.js'; +import { registerSuite } from './register.suite.js'; -// Skipping for now because parsers signature doesn't really match the other things you can register -// registerSuite({ -// config: { -// parse: () => {}, -// }, -// registerMethod: 'registerParser', -// prop: 'parsers', -// }); +registerSuite({ + config: { + pattern: /\.json/g, + parser: () => {}, + }, + registerMethod: 'registerParser', + prop: 'parsers', +}); describe('register', () => { describe('parser', async () => { const StyleDictionaryExtended = new StyleDictionary({}); + it('should error if name is not a string', () => { + expect(() => { + StyleDictionaryExtended.registerParser({ + pattern: /$.json/, + parser: function () {}, + }); + }).to.throw("Can't register parser; parser.name must be a string"); + + expect(() => { + StyleDictionaryExtended.registerParser({ + name: {}, + pattern: /$.json/, + parser: function () {}, + }); + }).to.throw("Can't register parser; parser.name must be a string"); + }); + it('should error if pattern is not a regex', () => { expect(() => { StyleDictionaryExtended.registerParser({ - parse: function () {}, + name: 'json-parser', + parser: function () {}, }); }).to.throw('pattern must be a regular expression'); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: 1, - parse: function () {}, + parser: function () {}, }); }).to.throw('pattern must be a regular expression'); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: [], - parse: function () {}, + parser: function () {}, }); }).to.throw('pattern must be a regular expression'); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: {}, - parse: function () {}, + parser: function () {}, }); }).to.throw('pattern must be a regular expression'); }); @@ -59,50 +80,58 @@ describe('register', () => { it('should error if parser is not a function', () => { expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, }); - }).to.throw('parse must be a function'); + }).to.throw("Can't register parser; parser.parser must be a function"); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, - parse: 1, + parser: 1, }); - }).to.throw('parse must be a function'); + }).to.throw("Can't register parser; parser.parser must be a function"); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, - parse: 'name', + parser: 'name', }); - }).to.throw('parse must be a function'); + }).to.throw("Can't register parser; parser.parser must be a function"); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, - parse: [], + parser: [], }); - }).to.throw('parse must be a function'); + }).to.throw("Can't register parser; parser.parser must be a function"); expect(() => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, - parse: {}, + parser: {}, }); - }).to.throw('parse must be a function'); + }).to.throw("Can't register parser; parser.parser must be a function"); }); it('should work if pattern and parser are good', () => { StyleDictionaryExtended.registerParser({ + name: 'json-parser', pattern: /$.json/, - parse: function () {}, + parser: function () {}, }); - expect(typeof StyleDictionaryExtended.parsers[0].parse).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.parsers['json-parser'].parser).to.equal( + 'function', + ); }); it('should properly pass the registered filter to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.parsers[0].parse).to.equal('function'); + expect(typeof SDE2.hooks.parsers['json-parser'].parser).to.equal('function'); }); }); }); diff --git a/__tests__/register/preprocessor.test.js b/__tests__/register/preprocessor.test.js index fbecb92c5..f0f314ac6 100644 --- a/__tests__/register/preprocessor.test.js +++ b/__tests__/register/preprocessor.test.js @@ -20,7 +20,6 @@ registerSuite({ }, registerMethod: 'registerPreprocessor', prop: 'preprocessors', - hooks: true, }); describe('register/transformGroup', async () => { diff --git a/__tests__/register/register.suite.js b/__tests__/register/register.suite.js index 74134fcf5..021425d13 100644 --- a/__tests__/register/register.suite.js +++ b/__tests__/register/register.suite.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; export function registerSuite(opts) { /** * opts example: { - * config: { transformer: () => {} }, + * config: { transform: () => {} }, * registerMethod: 'registerTransform', * prop: 'transform', * } @@ -17,11 +17,7 @@ export function registerSuite(opts) { describe('Register Test Suite', () => { const reset = () => { - if (opts.hooks) { - StyleDictionary.hooks[prop] = defaultPropVal; - } else { - StyleDictionary[prop] = defaultPropVal; - } + StyleDictionary.hooks[prop] = defaultPropVal; }; beforeEach(() => { reset(); @@ -37,15 +33,9 @@ export function registerSuite(opts) { const sd1 = new StyleDictionary(); const sd2 = new StyleDictionary(); const sd3 = await sd2.extend(); - if (opts.hooks) { - expect(sd1.hooks[prop][configFoo.name]).to.not.be.undefined; - expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; - expect(sd3.hooks[prop][configFoo.name]).to.not.be.undefined; - } else { - expect(sd1[prop][configFoo.name]).to.not.be.undefined; - expect(sd2[prop][configFoo.name]).to.not.be.undefined; - expect(sd3[prop][configFoo.name]).to.not.be.undefined; - } + expect(sd1.hooks[prop][configFoo.name]).to.not.be.undefined; + expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; + expect(sd3.hooks[prop][configFoo.name]).to.not.be.undefined; }); it(`should allow registering ${prop} on instance, affecting only that instance`, async () => { @@ -54,15 +44,9 @@ export function registerSuite(opts) { const sd3 = await sd2.extend(); sd2[registerMethod](configFoo); - if (opts.hooks) { - expect(sd1.hooks[prop][configFoo.name]).to.be.undefined; - expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; - expect(sd3.hooks[prop][configFoo.name]).to.be.undefined; - } else { - expect(sd1[prop][configFoo.name]).to.be.undefined; - expect(sd2[prop][configFoo.name]).to.not.be.undefined; - expect(sd3[prop][configFoo.name]).to.be.undefined; - } + expect(sd1.hooks[prop][configFoo.name]).to.be.undefined; + expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; + expect(sd3.hooks[prop][configFoo.name]).to.be.undefined; }); it(`should combine class and instance registrations for ${prop} on the instance`, async () => { @@ -73,25 +57,14 @@ export function registerSuite(opts) { sd2[registerMethod](configBar); const sd3 = await sd2.extend(); - if (opts.hooks) { - expect(sd1.hooks[prop][configFoo.name]).to.not.be.undefined; - expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; - expect(sd3.hooks[prop][configFoo.name]).to.not.be.undefined; - // should not be registered on sd1, because we registered only on sd2 - expect(sd1.hooks[prop][configBar.name]).to.be.undefined; - expect(sd2.hooks[prop][configBar.name]).to.not.be.undefined; - // should be registered because sd3 extends sd2 - expect(sd3.hooks[prop][configBar.name]).to.not.be.undefined; - } else { - expect(sd1[prop][configFoo.name]).to.not.be.undefined; - expect(sd2[prop][configFoo.name]).to.not.be.undefined; - expect(sd3[prop][configFoo.name]).to.not.be.undefined; - // should not be registered on sd1, because we registered only on sd2 - expect(sd1[prop][configBar.name]).to.be.undefined; - expect(sd2[prop][configBar.name]).to.not.be.undefined; - // should be registered because sd3 extends sd2 - expect(sd3[prop][configBar.name]).to.not.be.undefined; - } + expect(sd1.hooks[prop][configFoo.name]).to.not.be.undefined; + expect(sd2.hooks[prop][configFoo.name]).to.not.be.undefined; + expect(sd3.hooks[prop][configFoo.name]).to.not.be.undefined; + // should not be registered on sd1, because we registered only on sd2 + expect(sd1.hooks[prop][configBar.name]).to.be.undefined; + expect(sd2.hooks[prop][configBar.name]).to.not.be.undefined; + // should be registered because sd3 extends sd2 + expect(sd3.hooks[prop][configBar.name]).to.not.be.undefined; }); }); }); diff --git a/__tests__/register/transform.test.js b/__tests__/register/transform.test.js index 48b1af38d..4a45b1bf0 100644 --- a/__tests__/register/transform.test.js +++ b/__tests__/register/transform.test.js @@ -13,41 +13,41 @@ import { expect } from 'chai'; import StyleDictionary from 'style-dictionary'; import { registerSuite } from './register.suite.js'; -import transform from '../../lib/common/transforms.js'; +import transformBuiltins from '../../lib/common/transforms.js'; -const transformerPxAppender = { +const transformPxAppender = { name: 'px-appender', type: 'value', - transformer: (token) => `${token.value}px`, + transform: (token) => `${token.value}px`, }; -const transformerValueIncrementer = { +const transformValueIncrementer = { name: 'value-incrementer', type: 'value', - matcher: (token) => typeof token.value === 'number', - transformer: (token) => token.value + 1, + filter: (token) => typeof token.value === 'number', + transform: (token) => token.value + 1, }; registerSuite({ config: { type: 'value', - transformer: () => {}, + transform: () => {}, }, registerMethod: 'registerTransform', - prop: 'transform', + prop: 'transforms', }); describe('register', () => { beforeEach(() => { - StyleDictionary.transform = transform; + StyleDictionary.hooks.transforms = transformBuiltins; }); afterEach(() => { - StyleDictionary.transform = transform; + StyleDictionary.hooks.transforms = transformBuiltins; }); describe('instance vs class registration', () => { it('should allow registering on class, affecting all instances', async () => { - StyleDictionary.registerTransform(transformerPxAppender); + StyleDictionary.registerTransform(transformPxAppender); const baseCfg = { platforms: { @@ -89,9 +89,9 @@ describe('register', () => { [sd1, sd2, sd3].map((sd) => sd.exportPlatform('test')), ); - expect(sd1.transform['px-appender']).to.not.be.undefined; - expect(sd2.transform['px-appender']).to.not.be.undefined; - expect(sd3.transform['px-appender']).to.not.be.undefined; + expect(sd1.hooks.transforms['px-appender']).to.not.be.undefined; + expect(sd2.hooks.transforms['px-appender']).to.not.be.undefined; + expect(sd3.hooks.transforms['px-appender']).to.not.be.undefined; expect(sd1After.size1.value).to.equal('1px'); expect(sd2After.size2.value).to.equal('2px'); @@ -104,15 +104,15 @@ describe('register', () => { const sd2 = new StyleDictionary(); const sd3 = await sd2.extend(); - sd2.registerTransform(transformerPxAppender); + sd2.registerTransform(transformPxAppender); - expect(sd1.transform['px-appender']).to.be.undefined; - expect(sd2.transform['px-appender']).to.not.be.undefined; - expect(sd3.transform['px-appender']).to.be.undefined; + expect(sd1.hooks.transforms['px-appender']).to.be.undefined; + expect(sd2.hooks.transforms['px-appender']).to.not.be.undefined; + expect(sd3.hooks.transforms['px-appender']).to.be.undefined; }); it('should combine class and instance registrations on the instance', async () => { - StyleDictionary.registerTransform(transformerPxAppender); + StyleDictionary.registerTransform(transformPxAppender); const sd1 = new StyleDictionary({ platforms: { @@ -141,7 +141,7 @@ describe('register', () => { }, }, }); - sd2.registerTransform(transformerValueIncrementer); + sd2.registerTransform(transformValueIncrementer); const sd3 = await sd2.extend({ tokens: { @@ -156,14 +156,14 @@ describe('register', () => { [sd1, sd2, sd3].map((sd) => sd.exportPlatform('test')), ); - expect(sd1.transform['px-appender']).to.not.be.undefined; - expect(sd2.transform['px-appender']).to.not.be.undefined; - expect(sd3.transform['px-appender']).to.not.be.undefined; + expect(sd1.hooks.transforms['px-appender']).to.not.be.undefined; + expect(sd2.hooks.transforms['px-appender']).to.not.be.undefined; + expect(sd3.hooks.transforms['px-appender']).to.not.be.undefined; // should not be registered on sd1, because we registered only on sd2 - expect(sd1.transform['value-incrementer']).to.be.undefined; - expect(sd2.transform['value-incrementer']).to.not.be.undefined; + expect(sd1.hooks.transforms['value-incrementer']).to.be.undefined; + expect(sd2.hooks.transforms['value-incrementer']).to.not.be.undefined; // should be registered because sd3 extends sd2 - expect(sd3.transform['value-incrementer']).to.not.be.undefined; + expect(sd3.hooks.transforms['value-incrementer']).to.not.be.undefined; expect(sd1After.size1.value).to.equal('1px'); expect(sd2After.size2.value).to.equal('3px'); @@ -199,52 +199,52 @@ describe('register', () => { }).to.throw('name must be a string'); }); - it('should error if matcher is not a function', () => { + it('should error if filter is not a function', () => { expect(() => { StyleDictionaryExtended.registerTransform({ type: 'name', name: 'name', - matcher: 'foo', + filter: 'foo', }); - }).to.throw('matcher must be a function'); + }).to.throw('filter must be a function'); }); - it('should error if transformer is not a function', () => { + it('should error if transform is not a function', () => { expect(() => { StyleDictionaryExtended.registerTransform({ type: 'name', name: 'name', - matcher: function () { + filter: function () { return true; }, - transformer: 'foo', + transform: 'foo', }); - }).to.throw('transformer must be a function'); + }).to.throw('transform must be a function'); }); - it('should work if type, matcher, and transformer are all proper', () => { + it('should work if type, filter, and transform are all proper', () => { StyleDictionaryExtended.registerTransform({ type: 'name', name: 'foo', - matcher: function () { + filter: function () { return true; }, - transformer: function () { + transform: function () { return true; }, }); - expect(typeof StyleDictionaryExtended.transform.foo).to.equal('object'); - expect(StyleDictionaryExtended).to.have.nested.property('transform.foo.type', 'name'); - expect(typeof StyleDictionaryExtended.transform.foo.matcher).to.equal('function'); - expect(typeof StyleDictionaryExtended.transform.foo.transformer).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.transforms.foo).to.equal('object'); + expect(StyleDictionaryExtended).to.have.nested.property('hooks.transforms.foo.type', 'name'); + expect(typeof StyleDictionaryExtended.hooks.transforms.foo.filter).to.equal('function'); + expect(typeof StyleDictionaryExtended.hooks.transforms.foo.transform).to.equal('function'); }); it('should properly pass the registered transform to instances', async () => { const SDE2 = await StyleDictionaryExtended.extend({}); - expect(typeof SDE2.transform.foo).to.equal('object'); - expect(SDE2).to.have.nested.property('transform.foo.type', 'name'); - expect(typeof SDE2.transform.foo.matcher).to.equal('function'); - expect(typeof SDE2.transform.foo.transformer).to.equal('function'); + expect(typeof SDE2.hooks.transforms.foo).to.equal('object'); + expect(SDE2).to.have.nested.property('hooks.transforms.foo.type', 'name'); + expect(typeof SDE2.hooks.transforms.foo.filter).to.equal('function'); + expect(typeof SDE2.hooks.transforms.foo.transform).to.equal('function'); }); }); }); diff --git a/__tests__/register/transformGroup.test.js b/__tests__/register/transformGroup.test.js index 4f651e596..9410e273f 100644 --- a/__tests__/register/transformGroup.test.js +++ b/__tests__/register/transformGroup.test.js @@ -21,7 +21,7 @@ registerSuite({ transforms: ['size/px'], }, registerMethod: 'registerTransformGroup', - prop: 'transformGroup', + prop: 'transformGroups', }); describe('register/transformGroup', async () => { @@ -106,9 +106,9 @@ describe('register/transformGroup', async () => { name: 'foo', transforms: ['size/px'], }); - expect(Array.isArray(StyleDictionaryExtended.transformGroup.foo)).to.be.true; - expect(typeof StyleDictionaryExtended.transformGroup.foo[0]).to.equal('string'); - expect(StyleDictionaryExtended.transformGroup.foo[0]).to.equal('size/px'); + expect(Array.isArray(StyleDictionaryExtended.hooks.transformGroups.foo)).to.be.true; + expect(typeof StyleDictionaryExtended.hooks.transformGroups.foo[0]).to.equal('string'); + expect(StyleDictionaryExtended.hooks.transformGroups.foo[0]).to.equal('size/px'); }); it('should properly pass the registered transformGroup to instances when extending', async () => { @@ -118,8 +118,8 @@ describe('register/transformGroup', async () => { transforms: ['size/px'], }); const SDE2 = await StyleDictionaryBase.extend({}); - expect(Array.isArray(SDE2.transformGroup.bar)).to.be.true; - expect(typeof SDE2.transformGroup.bar[0]).to.equal('string'); - expect(SDE2.transformGroup.bar[0]).to.equal('size/px'); + expect(Array.isArray(SDE2.hooks.transformGroups.bar)).to.be.true; + expect(typeof SDE2.hooks.transformGroups.bar[0]).to.equal('string'); + expect(SDE2.hooks.transformGroups.bar[0]).to.equal('size/px'); }); }); diff --git a/__tests__/transform/config.test.js b/__tests__/transform/config.test.js index 6c522c165..5319ac760 100644 --- a/__tests__/transform/config.test.js +++ b/__tests__/transform/config.test.js @@ -16,16 +16,18 @@ import transformConfig from '../../lib/transform/config.js'; import chalk from 'chalk'; const dictionary = { - transformGroup: { - fooTransformGroup: ['barTransform'], - }, - transform: { - fooTransform: { - type: 'attribute', - transformer: function () { - return { bar: 'foo' }; + hooks: { + transforms: { + fooTransform: { + type: 'attribute', + transform: function () { + return { bar: 'foo' }; + }, }, }, + transformGroups: { + fooTransformGroup: ['barTransform'], + }, }, }; @@ -59,31 +61,33 @@ None of "barTransform", "bazTransform" match the name of a registered transform. it('allows combining transformGroup with transforms', () => { const cfg = { - transformGroup: { - foobarTransformGroup: ['fooTransform', 'barTransform'], - }, - transform: { - fooTransform: { - name: 'fooTransform', - type: 'attribute', - transformer: function () { - return { foo: 'foo' }; + hooks: { + transforms: { + fooTransform: { + name: 'fooTransform', + type: 'attribute', + transform: function () { + return { foo: 'foo' }; + }, }, - }, - barTransform: { - name: 'barTransform', - type: 'attribute', - transformer: function () { - return { bar: 'bar' }; + barTransform: { + name: 'barTransform', + type: 'attribute', + transform: function () { + return { bar: 'bar' }; + }, }, - }, - quxTransform: { - name: 'quxTransform', - type: 'attribute', - transformer: function () { - return { qux: 'qux' }; + quxTransform: { + name: 'quxTransform', + type: 'attribute', + transform: function () { + return { qux: 'qux' }; + }, }, }, + transformGroups: { + foobarTransformGroup: ['fooTransform', 'barTransform'], + }, }, }; @@ -101,8 +105,10 @@ None of "barTransform", "bazTransform" match the name of a registered transform. it('warns the user if an action is used without a clean function', () => { const cfg = { - action: { - foo: {}, + hooks: { + actions: { + foo: {}, + }, }, }; const platformCfg = { @@ -121,8 +127,10 @@ None of "barTransform", "bazTransform" match the name of a registered transform. it('throws if an action is used without a clean function with log.warnings set to error', () => { const cfg = { log: { warnings: 'error' }, - action: { - foo: {}, + hooks: { + actions: { + foo: {}, + }, }, }; const platformCfg = { @@ -137,8 +145,10 @@ None of "barTransform", "bazTransform" match the name of a registered transform. it('does not warn user at all when log.verbosity silent is used', () => { const cfg = { log: { verbosity: 'silent' }, - action: { - foo: {}, + hooks: { + actions: { + foo: {}, + }, }, }; const platformCfg = { diff --git a/__tests__/transform/object.test.js b/__tests__/transform/object.test.js index 5a11c90e0..fc22eb960 100644 --- a/__tests__/transform/object.test.js +++ b/__tests__/transform/object.test.js @@ -17,35 +17,35 @@ const config = { transforms: [ { type: 'attribute', - transformer: function () { + transform: function () { return { foo: 'bar' }; }, }, { type: 'attribute', // verify async transforms to also work properly - transformer: async function () { + transform: async function () { await new Promise((resolve) => setTimeout(resolve, 100)); return { bar: 'foo' }; }, }, { type: 'name', - matcher: function (token) { + filter: function (token) { return token.attributes.foo === 'bar'; }, // verify async transforms to also work properly - transformer: async function () { + transform: async function () { await new Promise((resolve) => setTimeout(resolve, 100)); - return 'transformer result'; + return 'transform result'; }, }, { type: 'value', - matcher: function (token) { + filter: function (token) { return token.path[0] === 'spacing'; }, - transformer: function (val) { + transform: function (val) { return val + 'px'; }, }, @@ -86,7 +86,7 @@ describe('transform', () => { base: { attributes: { bar: 'foo', foo: 'bar' }, comment: 'the base size of the font', - name: 'transformer result', + name: 'transform result', original: { comment: 'the base size of the font', value: '16', diff --git a/__tests__/transform/transformToken.test.js b/__tests__/transform/transformToken.test.js index fc60fbe9d..ff281c975 100644 --- a/__tests__/transform/transformToken.test.js +++ b/__tests__/transform/transformToken.test.js @@ -17,7 +17,7 @@ const config = { transforms: [ { type: 'attribute', - transformer: function () { + transform: function () { return { foo: 'bar', }; @@ -25,16 +25,16 @@ const config = { }, { type: 'attribute', - transformer: function () { + transform: function () { return { bar: 'foo' }; }, }, { type: 'name', - matcher: function (prop) { + filter: function (prop) { return prop.attributes.foo === 'bar'; }, - transformer: function () { + transform: function () { return 'hello'; }, }, @@ -50,7 +50,7 @@ describe('transform', () => { }); // This allows transformObject utility to then consider this token's transformation undefined and thus "deferred" - it('returns a token as undefined if transitive transformer dictates that the transformation has to be deferred', async () => { + it('returns a token as undefined if transitive transform dictates that the transformation has to be deferred', async () => { const result = await transformToken( { value: '16', @@ -63,7 +63,7 @@ describe('transform', () => { { type: 'value', transitive: true, - transformer: () => { + transform: () => { return undefined; }, }, diff --git a/__tests__/utils/combineJSON.test.js b/__tests__/utils/combineJSON.test.js index 124522a45..ea3361678 100644 --- a/__tests__/utils/combineJSON.test.js +++ b/__tests__/utils/combineJSON.test.js @@ -95,13 +95,13 @@ describe('utils', () => { describe('custom parsers', () => { it('should support yaml.parse', async () => { - const parsers = [ - { + const parsers = { + 'yaml-parser': { pattern: /\.yaml$/, // yaml.parse function matches the intended function signature - parse: ({ contents }) => yaml.parse(contents), + parser: ({ contents }) => yaml.parse(contents), }, - ]; + }; const { tokens } = await combineJSON( [`__tests__/__json_files/yaml.yaml`], false, @@ -115,20 +115,20 @@ describe('utils', () => { it('should multiple parsers on the same file', async () => { const testOutput = { test: 'test' }; - const parsers = [ - { + const parsers = { + 'json-foo': { pattern: /.json$/, - parse: () => { + parser: () => { return { test: 'foo' }; }, }, - { + 'json-return': { pattern: /.json$/, - parse: () => { + parser: () => { return testOutput; }, }, - ]; + }; const { tokens } = await combineJSON( [`__tests__/__json_files/simple.json`], false, @@ -140,15 +140,15 @@ describe('utils', () => { }); it('should support asynchronous parsers', async () => { - const parsers = [ - { + const parsers = { + 'json-test': { pattern: /.json$/, - parse: async () => { + parser: async () => { await new Promise((resolve) => setTimeout(resolve, 10)); return { test: 'foo' }; }, }, - ]; + }; const { tokens } = await combineJSON( [`__tests__/__json_files/simple.json`], false, diff --git a/__tests__/utils/preprocess.test.js b/__tests__/utils/preprocess.test.js index f64ffff6a..53486a51e 100644 --- a/__tests__/utils/preprocess.test.js +++ b/__tests__/utils/preprocess.test.js @@ -24,11 +24,9 @@ describe('utils', () => { }, ['preprocessorA'], { - preprocessorA: { - preprocessor: (tokens) => { - tokens.bar = tokens.foo; - return tokens; - }, + preprocessorA: (tokens) => { + tokens.bar = tokens.foo; + return tokens; }, }, ); @@ -46,24 +44,19 @@ describe('utils', () => { }, ['preprocessorA', 'preprocessorB', 'preprocessorC'], { - preprocessorA: { - preprocessor: (tokens) => { - tokens.bar = tokens.foo; - return tokens; - }, + preprocessorA: (tokens) => { + tokens.bar = tokens.foo; + return tokens; }, - preprocessorB: { - preprocessor: async (tokens) => { - await new Promise((resolve) => setTimeout(resolve, 100)); - tokens.baz = tokens.bar; - return tokens; - }, + preprocessorB: async (tokens) => { + await new Promise((resolve) => setTimeout(resolve, 100)); + tokens.baz = tokens.bar; + return tokens; }, - preprocessorC: { - preprocessor: (tokens) => { - tokens.qux = tokens.baz; - return tokens; - }, + + preprocessorC: (tokens) => { + tokens.qux = tokens.baz; + return tokens; }, }, ); diff --git a/docs/src/content/docs/info/architecture.md b/docs/src/content/docs/info/architecture.md index c12a54b2a..614b00491 100644 --- a/docs/src/content/docs/info/architecture.md +++ b/docs/src/content/docs/info/architecture.md @@ -16,7 +16,7 @@ Style Dictionary is a configuration based framework, you tell it what to do in a In your [config](/reference/config) file can define `include` and `source`, which are arrays of file path globs. These tell Style Dictionary where to find your token files. You can have them anywhere and in any folder structure as long as you tell Style Dictionary where to find them. -If there are [custom parsers](/reference/hooks/parsers) defined, Style Dictionary will run those on files the parsers match. +If there are [custom parsers](/reference/hooks/parsers) defined and applied in the config, Style Dictionary will run those on files the applied parsers match. ## 3. Deep merge token files @@ -25,10 +25,12 @@ Style Dictionary takes all the files it found and performs a deep merge. This al ## 4. Run preprocessors over the dictionary Allows users to configure [custom preprocessors](/reference/hooks/preprocessors), to process the merged dictionary as a whole, rather than per token file individually. +These preprocessors have to be applied in the config, either on a global or platform level. ## 5. Iterate over the platforms For each platform defined in your [config](/reference/config), Style Dictionary will do a few steps to get it ready to be consumed on that platform. You don't need to worry about one platform affecting another because everything that happens in a platform is non-destructive. +Platform-applied preprocessors run now instead of beforehand in step 4. ## 5a. Transform the tokens diff --git a/docs/src/content/docs/reference/Hooks/Formats/helpers.md b/docs/src/content/docs/reference/Hooks/Formats/helpers.md index 177f6e10f..b5efff8c1 100644 --- a/docs/src/content/docs/reference/Hooks/Formats/helpers.md +++ b/docs/src/content/docs/reference/Hooks/Formats/helpers.md @@ -14,7 +14,7 @@ import { fileHeader, formattedVariables } from 'style-dictionary/utils'; StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: async ({ dictionary, file, options }) => { + format: async ({ dictionary, file, options }) => { const { outputReferences } = options; const header = await fileHeader({ file }); return ( diff --git a/docs/src/content/docs/reference/Hooks/Formats/index.md b/docs/src/content/docs/reference/Hooks/Formats/index.md index de474074f..3c4b07fa6 100644 --- a/docs/src/content/docs/reference/Hooks/Formats/index.md +++ b/docs/src/content/docs/reference/Hooks/Formats/index.md @@ -205,7 +205,7 @@ By default Style Dictionary adds a file header comment in the top of files built You can remove these comments with the option: `showFileHeader: false` if you do not want them in your generated files. You can also create your own file header or extend the default one. This could be useful if you want to put a version number or hash of the source files rather than a timestamp. -Custom file headers can be added the same way you would add a custom format, either by using the [`registerFileHeader`](/reference/api#registerfileheader) function or adding the [`fileHeader`](/reference/hooks/file_headers) object directly in the Style Dictionary [configuration](/reference/config). Your custom file header can be used in built-in formats as well as custom formats. To use a custom file header in a custom format see the [`fileHeader`](/reference/hooks/formats#fileheader) format helper method. +Custom file headers can be added the same way you would add a custom format, either by using the [`registerFileHeader`](/reference/api#registerfileheader) function or adding the [`fileHeader`](/reference/hooks/file-headers) object directly in the Style Dictionary [configuration](/reference/config). Your custom file header can be used in built-in formats as well as custom formats. To use a custom file header in a custom format see the [`fileHeader`](/reference/hooks/formats#fileheader) format helper method. ```js title="build-tokens.js" import StyleDictionary from 'style-dictionary'; @@ -261,13 +261,13 @@ For an in-depth example see the [custom-file-header](https://github.com/amzn/sty ## Custom formats -You can create custom formats using the [`registerFormat`](/reference/api#registerformat) function or by directly including them in your [configuration](/reference/config). A format has a name and a formatter function, which takes an object as the argument and should return a string which is then written to a file. +You can create custom formats using the [`registerFormat`](/reference/api#registerformat) function or by directly including them in your [configuration](/reference/config). A format has a name and a format function, which takes an object as the argument and should return a string which is then written to a file. -### formatter +### format -`format.formatter(args)` ⇒ `string` +`format.format(args)` ⇒ `string` -The formatter function that is called when Style Dictionary builds files. +The format function that is called when Style Dictionary builds files. | Param | Type | Description | | ---------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | @@ -278,14 +278,14 @@ The formatter function that is called when Style Dictionary builds files. | `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#attributes) & [FormatOptions](#format-configuration) | +| `args.options` | `Object` | Merged object with SD [Config](/reference/config#properties) & [FormatOptions](#format-configuration) | Example: ```js StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, platform, options, file }) { + format: function ({ dictionary, platform, options, file }) { return JSON.stringify(dictionary.tokens, null, 2); }, }); @@ -318,16 +318,16 @@ To use your custom format, you call it by name in the file configuration object: } ``` -It is recommended for any configuration needed for your custom format to use the `options` object. Style Dictionary will merge platform and file options so that in your Style Dictionary configuration you can specify options at a platform or file level. In the configuration above, the `options` object passed to the formatter would have `showFileHeader: false`. +It is recommended for any configuration needed for your custom format to use the `options` object. Style Dictionary will merge platform and file options so that in your Style Dictionary configuration you can specify options at a platform or file level. In the configuration above, the `options` object passed to the format would have `showFileHeader: false`. ## Custom format with output references -To take advantage of outputting references in your custom formats there are 2 helper methods in the `dictionary` argument passed to your formatter function: `usesReference(value)` and `getReferences(value)`. Here is an example using those: +To take advantage of outputting references in your custom formats there are 2 helper methods in the `dictionary` argument passed to your format function: `usesReference(value)` and `getReferences(value)`. Here is an example using those: ```javascript title="build-tokens.js" StyleDictionary.registerFormat({ name: `es6WithReferences`, - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return dictionary.allTokens .map((token) => { let value = JSON.stringify(token.value); @@ -357,7 +357,7 @@ StyleDictionary.registerFormat({ ## Using a template / templating engine to create a format -Formatters are functions and created easily with most templating engines. Formats can be built using templates if there is a lot of boilerplate code to insert (e.g. ObjectiveC files). If the output consists of only the values (e.g. a flat SCSS variables file), writing a formatter function directly may be easier. +Formats are functions and created easily with most templating engines. Formats can be built using templates if there is a lot of boilerplate code to insert (e.g. ObjectiveC files). If the output consists of only the values (e.g. a flat SCSS variables file), writing a format function directly may be easier. Any templating language can work as long as there is a node module for it. All you need to do is register a format that calls your template and returns a string. @@ -374,7 +374,7 @@ ${dictionary.allTokens.map(token => ` ${token.name}`: `"${token.value}"`).join( StyleDictionary.registerFormat({ name: 'my/format', - formatter: template, + format: template, }); // format: 'my/format' is now available for use... @@ -391,7 +391,7 @@ const template = _.template(fs.readFileSync('templates/myFormat.template')); StyleDictionary.registerFormat({ name: 'my/format', - formatter: template, + format: template, }); // format: 'my/format' is now available for use... @@ -407,7 +407,7 @@ const template = Handlebars.compile(fs.readFileSync('templates/MyTemplate.hbs'). StyleDictionary.registerFormat({ name: 'my/format', - formatter: function ({ dictionary, platform }) { + format: function ({ dictionary, platform }) { return template({ tokens: dictionary.tokens, options: platform, diff --git a/docs/src/content/docs/reference/Hooks/Formats/predefined.md b/docs/src/content/docs/reference/Hooks/Formats/predefined.md index 8efa67946..3c43a7021 100644 --- a/docs/src/content/docs/reference/Hooks/Formats/predefined.md +++ b/docs/src/content/docs/reference/Hooks/Formats/predefined.md @@ -17,7 +17,7 @@ Creates a CSS file with variable definitions based on the style dictionary | `options` | `Object` | | | `options.showFileHeader` | `boolean` | Whether or not to include a comment that has the build date. Defaults to `true` | | `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to keep [references](#references-in-output-files) (a -> b -> c) in the output. Defaults to `false`. Also allows passing a function to conditionally output references on a per token basis. | -| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. | +| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. | | `options.selector` | `string` | Override the root css selector | | `options.formatting` | `FormattingOptions` | Custom formatting properties that define parts of a declaration line in code. The configurable strings are: `prefix`, `indentation`, `separator`, `suffix`, `lineSeparator`, `fileHeaderTimestamp`, `header`, `footer`, `commentStyle` and `commentPosition`. Those are used to generate a line like this: `${indentation}${prefix}${token.name}${separator} ${prop.value}${suffix}`. The remaining formatting options are used for the fileHeader helper. | @@ -36,7 +36,7 @@ Example: Creates a SCSS file with a flat map based on the style dictionary -Name the map by adding a 'mapName' attribute on the file object in your config. +Name the map by adding a `mapName` property on the `options` object property on the `file` object property in your config. Example: @@ -52,13 +52,13 @@ $tokens: ( Creates a SCSS file with a deep map based on the style dictionary. -Name the map by adding a 'mapName' attribute on the file `options` object in your config. +Name the map by adding a `mapName` property on the `options` object property on the `file` object property in your config. | Param | Type | Description | | ---------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `options` | `Object` | | | `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to keep [references](#references-in-output-files) (a -> b -> c) in the output. Defaults to `false`. Also allows passing a function to conditionally output references on a per token basis. | -| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. | +| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. | | `options.themeable` | `boolean` | Whether or not tokens should default to being themeable, if not otherwise specified per token. Defaults to `false`. | | `options.mapName` | `string` | Name of your SCSS map. | | `options.formatting` | `FormattingOptions` | Custom formatting properties that define parts of a declaration line in code. The configurable strings are: `prefix`, `indentation`, `separator`, `suffix`, `lineSeparator`, `fileHeaderTimestamp`, `header`, `footer`, `commentStyle` and `commentPosition`. Those are used to generate a line like this: `${indentation}${prefix}${token.name}${separator} ${prop.value}${suffix}`. The remaining formatting options are used for the fileHeader helper. | @@ -92,7 +92,7 @@ Add `!default` to any variable by setting a `themeable: true` attribute in the t | `options` | `Object` | | | `options.showFileHeader` | `boolean` | Whether or not to include a comment that has the build date. Defaults to `true` | | `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to keep [references](#references-in-output-files) (a -> b -> c) in the output. Defaults to `false`. Also allows passing a function to conditionally output references on a per token basis. | -| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. | +| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. | | `options.themeable` | `boolean` | Whether or not tokens should default to being themeable, if not otherwise specified per token. Defaults to `false`. | | `options.formatting` | `FormattingOptions` | Custom formatting properties that define parts of a declaration line in code. The configurable strings are: `prefix`, `indentation`, `separator`, `suffix`, `lineSeparator`, `fileHeaderTimestamp`, `header`, `footer`, `commentStyle` and `commentPosition`. Those are used to generate a line like this: `${indentation}${prefix}${token.name}${separator} ${prop.value}${suffix}`. The remaining formatting options are used for the fileHeader helper. | @@ -129,7 +129,7 @@ Creates a LESS file with variable definitions based on the style dictionary | `options` | `Object` | | | `options.showFileHeader` | `boolean` | Whether or not to include a comment that has the build date. Defaults to `true` | | `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to keep [references](#references-in-output-files) (a -> b -> c) in the output. Defaults to `false`. Also allows passing a function to conditionally output references on a per token basis. | -| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. | +| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. | | `options.formatting` | `FormattingOptions` | Custom formatting properties that define parts of a declaration line in code. The configurable strings are: `prefix`, `indentation`, `separator`, `suffix`, `lineSeparator`, `fileHeaderTimestamp`, `header`, `footer`, `commentStyle` and `commentPosition`. Those are used to generate a line like this: `${indentation}${prefix}${token.name}${separator} ${prop.value}${suffix}`. The remaining formatting options are used for the fileHeader helper. | Example: @@ -206,7 +206,7 @@ module.exports = { ### javascript/object Creates a JS file a global var that is a plain javascript object of the style dictionary. -Name the variable by adding a 'name' attribute on the file object in your config. +Name the variable by adding a `name` property on the `options` object property of the `file` object property in your config. Example: @@ -227,8 +227,7 @@ var StyleDictionary = { ### javascript/umd Creates a [UMD](https://github.com/umdjs/umd) module of the style -dictionary. Name the module by adding a 'name' attribute on the file object -in your config. +dictionary. Name the module by adding a `name` property on the `options` object property of the `file` object property in your config. Example @@ -380,7 +379,7 @@ import JsonToTS from 'json-to-ts'; StyleDictionaryPackage.registerFormat({ name: 'typescript/accurate-module-declarations', - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return ( 'declare const root: RootObject\n' + 'export default root\n' + @@ -401,8 +400,9 @@ with mixed resources will still work. This format will try to use the proper resource type for each token based on the category (color => color, size => dimen, etc.). However if you want to -force a particular resource type you can provide a 'resourceType' attribute -on the file configuration. You can also provide a 'resourceMap' if you +force a particular resource type you can provide a `resourceType` property on the `options` +object property on the `file` object property configuration. +You can also provide a `resourceMap` if you don't use Style Dictionary's built-in CTI structure. | Param | Type | Description | diff --git a/docs/src/content/docs/reference/Hooks/Transforms/index.md b/docs/src/content/docs/reference/Hooks/Transforms/index.md index 6b8ab97f5..96906fea6 100644 --- a/docs/src/content/docs/reference/Hooks/Transforms/index.md +++ b/docs/src/content/docs/reference/Hooks/Transforms/index.md @@ -25,14 +25,14 @@ You use transforms in your config file under `platforms` > `[platform]` > `trans } ``` -A transform consists of 4 parts: `type`, `name`, `matcher`, and `transformer`. Transforms are run on all design tokens where the matcher returns true. +A transform consists of 4 parts: `type`, `name`, `filter`, and `transform`. Transforms are run on all design tokens where the filter returns true. :::tip -If you don't provide a matcher function, it will match all tokens. +If you don't provide a filter function, it will match all tokens. ::: :::note -`transformer` functions can be async as well. +`transform` functions can be async as well. ::: ## Transform Types @@ -41,9 +41,9 @@ There are 3 types of transforms: `attribute`, `name`, and `value`. **Attribute:** An attribute transform adds to the attributes object on a design token. This is for including any meta-data about a design token such as it's CTI attributes or other information. -**Name:** A name transform transforms the name of a design token. You should really only be applying one name transformer because they will override each other if you use more than one. +**Name:** A name transform transforms the name of a design token. You should really only be applying one name transform because they will override each other if you use more than one. -**Value:** The value transform is the most important as this is the one that modifies the value or changes the representation of the value. Colors can be turned into hex values, rgb, hsl, hsv, etc. Value transforms have a matcher function that filter which tokens that transform runs on. This allows us to only run a color transform on only the colors and not every design token. +**Value:** The value transform is the most important as this is the one that modifies the value or changes the representation of the value. Colors can be turned into hex values, rgb, hsl, hsv, etc. Value transforms have a filter function that filter which tokens that transform runs on. This allows us to only run a color transform on only the colors and not every design token. ## Defining Custom Transforms @@ -60,8 +60,8 @@ StyleDictionary.registerTransform({ type: `value`, transitive: true, name: `myTransitiveTransform`, - matcher: (token) => {}, - transformer: (token) => { + filter: (token) => {}, + transform: (token) => { // token.value will be resolved and transformed at this point }, }); @@ -97,7 +97,7 @@ Using a custom transitive transform you could have `color.danger` darken `color. ### Defer transitive transformation manually -It's also possible to control, inside a transitive transform's `transformer` function, whether the transformation should be deferred until a later cycle of references resolution. +It's also possible to control, inside a transitive transform's `transform` function, whether the transformation should be deferred until a later cycle of references resolution. This is done by returning `undefined`, which basically means "I cannot currently do the transform due to a reference not yet being resolved". Imagine the following transform: @@ -110,7 +110,7 @@ StyleDictionary.registerTransform({ name: '', type: 'value', transitive: true, - transformer: (token) => { + transform: (token) => { const darkenModifier = token.darken; if (usesReferences(darkenModifier)) { // defer this transform, because our darken value is a reference @@ -133,7 +133,7 @@ Combined with the following tokens: } ``` -Due to `token.darken` being a property that uses a reference, we need the ability to defer its transformation from within the transformer, -since the transformer is the only place where we know which token properties the transformation is reliant upon. +Due to `token.darken` being a property that uses a reference, we need the ability to defer its transformation from within the transform, +since the transform is the only place where we know which token properties the transformation is reliant upon. If you want to learn more about transitive transforms, take a look at the [transitive transforms example](https://github.com/amzn/style-dictionary/tree/main/examples/advanced/transitive-transforms). diff --git a/docs/src/content/docs/reference/Hooks/file-headers.md b/docs/src/content/docs/reference/Hooks/file-headers.md new file mode 100644 index 000000000..f303a49f6 --- /dev/null +++ b/docs/src/content/docs/reference/Hooks/file-headers.md @@ -0,0 +1,145 @@ +--- +title: File Headers +--- + +File headers is a hook that provides a way to configure the header of output files. + +```css title="_variables.css" +/** + * This is a file header! + */ +:root { + --foo-bar: 4px; +} +``` + +--- + +## File header structure + +A file header is an object with two props: + +- `name`: the name of the file header +- `fileHeader`: a callback function that receives the default message array of strings, usually set by the format, and returns an array of strings (message lines) or a Promise with an array of strings. + +The array of strings will be concatenated using newline separator. + +```javascript title="my-fileheader.js" +const myFileHeader = { + name: 'my-file-header', + // async is optional + fileHeader: async (defaultMessages = []) => { + return [...defaultMessages, 'Do not edit please', 'Auto-generated on...']; + }, +}; +``` + +--- + +## Using file headers + +First you will need to tell Style Dictionary about your file header. You can do this in two ways: + +1. Using the [`.registerFileHeader`](/reference/api#registerfileheader) method +1. Inline in the [configuration](/reference/config#properties) `hooks.fileHeaders` property + +### .registerFileHeader + +```javascript +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerFileHeader(myFileHeader); +``` + +### Inline + +```javascript +export default { + hooks: { + fileHeaders: { + 'my-file-header': myFileHeader, + }, + }, + // ... the rest of the configuration +}; +``` + +### Applying it in config + +File-specific: + +```json +{ + "source": ["**/*.tokens.json"], + "platforms": { + "css": { + "transformGroup": "css", + "files": [ + { + "format": "css/variables", + "destination": "_variables.css", + "options": { + "fileHeader": "my-file-header" + } + } + ] + } + } +} +``` + +or platform-specific: + +```json +{ + "source": ["**/*.tokens.json"], + "platforms": { + "css": { + "transformGroup": "css", + "options": { + "fileHeader": "my-file-header" + }, + "files": [ + { + "format": "css/variables", + "destination": "_variables.css" + } + ] + } + } +} +``` + +--- + +### Example + +~ sd-playground + +```js config +export default { + hooks: { + fileHeaders: { + 'my-file-header': (defaultMessages = []) => [ + 'Ola, planet!', + ...defaultMessages, + 'Hello, World!', + ], + }, + }, + platforms: { + css: { + transformGroup: 'css', + options: { + fileHeader: 'my-file-header', + }, + files: [ + { + format: 'css/variables', + destination: '_variables.css', + }, + ], + }, + }, +}; +``` diff --git a/docs/src/content/docs/reference/Hooks/filters.md b/docs/src/content/docs/reference/Hooks/filters.md new file mode 100644 index 000000000..11c20421a --- /dev/null +++ b/docs/src/content/docs/reference/Hooks/filters.md @@ -0,0 +1,138 @@ +--- +title: Filters +--- + +Filters is a hook that provides a way to filter your tokens prior to formatting them to the final output. + +Common use cases for filtering are: + +- Not outputting your primitive/option tokens such as a color palette +- Splitting component tokens into separate files for each component + +--- + +## Filter structure + +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. + +```javascript title="my-filter.js" +const myFilter = { + name: 'my-filter', + // async is optional + filter: async (token) => { + return !token.filePath.endsWith('core.json'); + }, +}; +``` + +--- + +## Using filters + +First you will need to tell Style Dictionary about your filter. You can do this in two ways: + +1. Using the [`.registerFilter`](/reference/api#registerfilter) method +1. Inline in the [configuration](/reference/config#properties) `hooks.filters` property + +### .registerFilter + +```javascript +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerFilter(myFilter); +``` + +### Inline + +```javascript +export default { + hooks: { + filters: { + 'my-filter': myFilter, + }, + }, + // ... the rest of the configuration +}; +``` + +### Applying it in config + +```json +{ + "source": ["**/*.tokens.json"], + "platforms": { + "css": { + "transformGroup": "css", + "files": [ + { + "format": "css/variables", + "destination": "_variables.css", + "filter": "my-filter" + } + ] + } + } +} +``` + +--- + +### Example + +~ sd-playground + +```json tokens +{ + "colors": { + "red": { + "type": "color", + "value": "#ff0000" + }, + "blue": { + "type": "color", + "value": "#0000ff" + } + }, + "spacing": { + "0": { + "type": "dimension", + "value": "0px" + }, + "1": { + "type": "dimension", + "value": "4px" + }, + "2": { + "type": "dimension", + "value": "8px" + } + } +} +``` + +```js config +export default { + hooks: { + filters: { + 'no-colors': (token) => { + return token.type !== 'color'; + }, + }, + }, + platforms: { + css: { + transformGroup: 'css', + files: [ + { + format: 'css/variables', + destination: '_variables.css', + filter: 'no-colors', + }, + ], + }, + }, +}; +``` diff --git a/docs/src/content/docs/reference/Hooks/parsers.md b/docs/src/content/docs/reference/Hooks/parsers.md index 397621936..9efcbae9c 100644 --- a/docs/src/content/docs/reference/Hooks/parsers.md +++ b/docs/src/content/docs/reference/Hooks/parsers.md @@ -9,7 +9,7 @@ A custom parser matches design token files based on a file path regular expressi Custom parsers can be used to keep design token files in other languages like YAML, but they can also be used to add extra metadata or modify the design tokens themselves before they get to Style Dictionary. For example, you could modify the token object based on its file path or programmatically generate tokens based on the data in certain files. :::note -`parse` function can be async as well. +`parser` function can be async as well. ::: --- @@ -20,8 +20,9 @@ A parser has 2 parts: a pattern which is a regular expression to match against a ```javascript title="my-parser.js" const myParser = { + name: 'json-parser', pattern: /\.json$/, - parse: ({ filePath, contents }) => { + parser: ({ filePath, contents }) => { return JSON.parse(contents); }, }; @@ -34,7 +35,17 @@ const myParser = { First you will need to tell Style Dictionary about your parser. You can do this in two ways: 1. Using the [`.registerParser`](/reference/api#registerparser) method -1. Inline in the [configuration](/reference/config#attributes) +2. Inline in the [configuration](/reference/config#properties) + +You will then have to apply the parser by name in the [config](/reference/config): + +```json +{ + "source": ["tokens/*.json"], + "parsers": ["json-parser"], + "platforms": {} +} +``` ### .registerParser @@ -42,8 +53,9 @@ First you will need to tell Style Dictionary about your parser. You can do this import StyleDictionary from 'style-dictionary'; StyleDictionary.registerParser({ + name: 'json-parser', pattern: /\.json$/, - parse: ({ filePath, contents }) => { + parser: ({ filePath, contents }) => { return JSON.parse(contents); }, }); @@ -53,14 +65,16 @@ StyleDictionary.registerParser({ ```javascript title="config.js" export default { - parsers: [ - { - pattern: /\.json$/, - parse: ({ filePath, contents }) => { - return JSON.parse(contents); + hooks: { + parsers: { + 'json-parser': { + pattern: /\.json$/, + parser: ({ filePath, contents }) => { + return JSON.parse(contents); + }, }, }, - ], + }, // ... the rest of the configuration }; ``` diff --git a/docs/src/content/docs/reference/Hooks/preprocessors.md b/docs/src/content/docs/reference/Hooks/preprocessors.md index 7fdc5c492..e7564019d 100644 --- a/docs/src/content/docs/reference/Hooks/preprocessors.md +++ b/docs/src/content/docs/reference/Hooks/preprocessors.md @@ -57,7 +57,7 @@ const myPreprocessor = { First you will need to tell Style Dictionary about your parser. You can do this in two ways: 1. Using the [`.registerPreprocessor`](/reference/api#registerpreprocessor) method -1. Inline in the [configuration](/reference/config#attributes) +1. Inline in the [configuration](/reference/config#properties) ### .registerPreprocessor @@ -71,7 +71,7 @@ StyleDictionary.registerPreprocessor(myPreprocessor); ```javascript export default { - registeredHooks: { + hooks: { preprocessors: { 'strip-props': myPreprocessor, }, diff --git a/docs/src/content/docs/reference/Utils/format-helpers.md b/docs/src/content/docs/reference/Utils/format-helpers.md index d941cc74e..bf5e72eb3 100644 --- a/docs/src/content/docs/reference/Utils/format-helpers.md +++ b/docs/src/content/docs/reference/Utils/format-helpers.md @@ -12,7 +12,7 @@ import { fileHeader, formattedVariables } from 'style-dictionary/utils'; StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: async ({ dictionary, file, options }) => { + format: async ({ dictionary, file, options }) => { const { outputReferences } = options; const header = await fileHeader({ file }); return ( @@ -37,8 +37,8 @@ which uses: prefix, indentation, separator, suffix, and commentStyle. | Param | Type | Description | | ------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `options` | `Object` | A single argument to support named parameters and destructuring. | -| `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to output references. You will want to pass this from the `options` object sent to the formatter function. Also allows passing a function to conditionally output references on a per token basis. | -| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. | +| `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to output references. You will want to pass this from the `options` object sent to the format function. Also allows passing a function to conditionally output references on a per token basis. | +| `options.outputReferenceFallbacks` | `boolean` | Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. | | `options.dictionary` | `Dictionary` | Transformed Dictionary object containing allTokens, tokens and unfilteredTokens. | | `options.dictionary.allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | | `options.dictionary.tokens` | `TransformedTokens` | All tokens, still in unflattened object format. | @@ -52,7 +52,7 @@ Example: ```javascript title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, options }) { + format: function ({ dictionary, options }) { const { outputReferences } = options; const formatProperty = createPropertyFormatter({ outputReferences, @@ -75,7 +75,7 @@ default file header. | Param | Type | Description | | ------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `options` | `Object` | | -| `options.file` | [`File`](/reference/config#file) | The file object that is passed to the formatter. | +| `options.file` | [`File`](/reference/config#file) | The file object that is passed to the format. | | `options.commentStyle` | `string` | The only options are 'short', 'xml' and 'long', which will use the `//`, `` or `/*` style comments respectively. Defaults to 'long'. | | `options.commentPosition` | `string` | 'above' or 'inline', so either above the token or inline with the token | | `options.formatting` | `Object` | Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, footer and fileHeaderTimestamp. | @@ -85,7 +85,7 @@ Example: ```js title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: async ({ dictionary, file }) => { + format: async ({ dictionary, file }) => { const header = await fileHeader({ file, commentStyle: 'short' }); return ( header + dictionary.allTokens.map((token) => `${token.name} = ${token.value};`).join('\n') @@ -104,24 +104,24 @@ and not wanting to create redundant git diffs just because of the timestamp chan This is used to create lists of variables like Sass variables or CSS custom properties -| Param | Type | Description | -| ------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `options` | `Object` | | -| `options.format` | `string` | What type of variables to output. Options are: 'css', 'sass', 'less', and 'stylus'. | -| `options.dictionary` | `Dictionary` | Transformed Dictionary object containing allTokens, tokens and unfilteredTokens. | -| `options.dictionary.allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | -| `options.dictionary.tokens` | `TransformedTokens` | All tokens, still in unflattened object format. | -| `options.dictionary.unfilteredTokens` | `TransformedTokens` | All tokens, still in unflattened object format, including tokens that were filtered out by filters. | -| `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to output references. You will want to pass this from the `options` object sent to the formatter function. Also allows passing a function to conditionally output references on a per token basis. | -| `options.formatting` | `Object` | Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, and footer. | -| `options.themeable` | `boolean` | Whether tokens should default to being themeable. Defaults to false. | +| Param | Type | Description | +| ------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `options` | `Object` | | +| `options.format` | `string` | What type of variables to output. Options are: 'css', 'sass', 'less', and 'stylus'. | +| `options.dictionary` | `Dictionary` | Transformed Dictionary object containing allTokens, tokens and unfilteredTokens. | +| `options.dictionary.allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | +| `options.dictionary.tokens` | `TransformedTokens` | All tokens, still in unflattened object format. | +| `options.dictionary.unfilteredTokens` | `TransformedTokens` | All tokens, still in unflattened object format, including tokens that were filtered out by filters. | +| `options.outputReferences` | `boolean \| OutputReferencesFunction` | Whether or not to output references. You will want to pass this from the `options` object sent to the format function. Also allows passing a function to conditionally output references on a per token basis. | +| `options.formatting` | `Object` | Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, and footer. | +| `options.themeable` | `boolean` | Whether tokens should default to being themeable. Defaults to false. | Example: ```js title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, options }) { + format: function ({ dictionary, options }) { return formattedVariables({ format: 'less', dictionary, @@ -151,7 +151,7 @@ Example: ```javascript title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, options }) { + format: function ({ dictionary, options }) { return dictionary.allTokens .map(function (prop) { var to_ret_prop = 'export const ' + prop.name + ' : ' + getTypeScriptType(prop.value) + ';'; @@ -176,14 +176,14 @@ a unicode character. | ----------- | -------------------- | -------------------------------------------------------------------------------- | | `prefix` | `string` | Character to prefix variable names, like `$` for Sass | | `allTokens` | `TransformedToken[]` | Flattened array of all tokens, easiest to loop over and export to a flat format. | -| `options` | `Object` | options object passed to the formatter function. | +| `options` | `Object` | options object passed to the format function. | Example: ```js title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, options }) { + format: function ({ dictionary, options }) { return iconsWithPrefix('$', dictionary.allTokens, options); }, }); @@ -204,7 +204,7 @@ Example: ```js title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return JSON.stringify(minifyDictionary(dictionary.tokens)); }, }); @@ -251,7 +251,7 @@ Example: ```javascript title="build-tokens.js" StyleDictionary.registerFormat({ name: 'myCustomFormat', - formatter: function ({ dictionary, options }) { + format: function ({ dictionary, options }) { return dictionary.allTokens .sort(sortByName) .map((token) => `${token.name} = ${token.value}`) diff --git a/docs/src/content/docs/reference/Utils/references.md b/docs/src/content/docs/reference/Utils/references.md index 3c8f1cd9f..315c0b744 100644 --- a/docs/src/content/docs/reference/Utils/references.md +++ b/docs/src/content/docs/reference/Utils/references.md @@ -163,7 +163,7 @@ const sd = new StyleDictionary({ // Note that this example format does not account for token values that are arrays or objects StyleDictionary.registerFormat({ name: 'es6', - formatter: async (dictionary) => { + format: async (dictionary) => { const { allTokens, options, file } = dictionary; const isNumeric = (str) => { if (typeof str !== 'string') return false; @@ -239,7 +239,7 @@ export const Border = `solid ${Spacing2} ${SemanticBgPrimary}`; :::note The above example does not support DTCG syntax, but this could be quite easily added, -since you can query `sd.usesDtcg` or inside a formatter functions `dictionary.options.usesDtcg`. +since you can query `sd.usesDtcg` or inside a format functions `dictionary.options.usesDtcg`. ::: ## outputReferencesFilter diff --git a/docs/src/content/docs/reference/api.md b/docs/src/content/docs/reference/api.md index a19c123cc..d8e25f6e7 100644 --- a/docs/src/content/docs/reference/api.md +++ b/docs/src/content/docs/reference/api.md @@ -287,16 +287,16 @@ StyleDictionary.registerAction({ ### registerFileHeader -`StyleDictionary.registerFileHeader(options) ⇒ StyleDictionary` +`StyleDictionary.registerFileHeader(fileHeader) ⇒ StyleDictionary` -Add a custom [fileHeader](/reference/hooks/file_headers) to the Style Dictionary. File headers are used in +Add a custom [fileHeader](/reference/hooks/file-headers) to the Style Dictionary. File headers are used in formats to display some information about how the file was built in a comment. -| Param | Type | Description | -| ------------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| options | `Object` | | -| options.name | `string` | Name of the format to be referenced in your config.json | -| options.fileHeader | `function` | Function that returns an array of strings, which will be mapped to comment lines. It takes a single argument which is the default message array. See [file headers](/references/hooks/formats#file-headers) for more information. Can be async. | +| Param | Type | Description | +| ------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| options | `Object` | | +| options.name | `string` | Name of the format to be referenced in your config.json | +| options.fileHeader | `function` | Function that returns an array of strings, which will be mapped to comment lines. It takes a single argument which is the default message array. See [file headers](/references/hooks/file-headers) for more information. Can be async. | Example: @@ -317,18 +317,18 @@ StyleDictionary.registerFileHeader({ Add a custom [filter](/reference/hooks/filters) to the Style Dictionary. -| Param | Type | Description | -| -------------- | ---------- | ------------------------------------------------------------------------------ | -| filter | `Object` | | -| filter.name | `string` | Name of the filter to be referenced in your config.json | -| filter.matcher | `function` | Matcher function, return boolean if the token should be included. Can be async | +| Param | Type | Description | +| ------------- | ---------- | ----------------------------------------------------------------------------- | +| Filter | `Object` | | +| Filter.name | `string` | Name of the filter to be referenced in your config.json | +| Filter.filter | `function` | Filter function, return boolean if the token should be included. Can be async | Example: ```js StyleDictionary.registerFilter({ name: 'isColor', - matcher: function (token) { + filter: function (token) { return token.type === 'color'; }, }); @@ -342,18 +342,18 @@ StyleDictionary.registerFilter({ Add a custom [format](/reference/hooks/formats) to the Style Dictionary. -| Param | Type | Description | -| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| format | `Object` | | -| format.name | `string` | Name of the format to be referenced in your config.json | -| format.formatter | `function` | Function to perform the format. Takes a single argument. See [creating custom formats](/references/hooks/formats#creating-formats) Must return a string, which is then written to a file. Can be async | +| Param | Type | Description | +| ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| format | `Object` | | +| format.name | `string` | Name of the format to be referenced in your config.json | +| format.format | `function` | Function to perform the format. Takes a single argument. See [creating custom formats](/references/hooks/formats#creating-formats) Must return a string, which is then written to a file. Can be async | Example: ```js StyleDictionary.registerFormat({ name: 'json', - formatter: function ({ dictionary, platform, options, file }) { + format: function ({ dictionary, platform, options, file }) { return JSON.stringify(dictionary.tokens, null, 2); }, }); @@ -363,21 +363,23 @@ StyleDictionary.registerFormat({ ### registerParser -`StyleDictionary.registerParser(pattern, parse) ⇒ StyleDictionary` +`StyleDictionary.registerParser(parser) ⇒ StyleDictionary` Adds a custom [parser](/reference/hooks/parsers) to parse style dictionary files. -| Param | Type | Description | -| ------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| pattern | `Regex` | A file path regular expression to match which files this parser should be be used on. This is similar to how webpack loaders work. `/\.json$/` will match any file ending in '.json', for example. | -| parse | `function` | Function to parse the file contents. Takes 1 argument, which is an object with 2 attributes: contents wich is the string of the file contents and filePath. The function should return a plain Javascript object. Can be async. | +| Param | Type | Description | +| -------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Parser.name | `string` | Name of the parser to be referenced in your config.json | +| Parser.pattern | `Regex` | A file path regular expression to match which files this parser should be be used on. This is similar to how webpack loaders work. `/\.json$/` will match any file ending in '.json', for example. | +| Parser.parser | `function` | Function to parse the file contents. Takes 1 argument, which is an object with 2 properties: `contents` wich is the string of the file contents and `filePath`. The function should return a plain JavaScript object. Can be async. | Example: ```js StyleDictionary.registerParser({ + name: 'json-parser', pattern: /\.json$/, - parse: ({ contents, filePath }) => { + parser: ({ contents, filePath }) => { return JSON.parse(contents); }, }); @@ -418,14 +420,14 @@ StyleDictionary.registerPreprocessor({ Add a custom [transform](/reference/hooks/transforms) to the Style Dictionary. Transforms can manipulate a token's name, value, or attributes. -| Param | Type | Description | -| --------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| transform | `Object` | Transform object | -| transform.type | `string` | Type of transform, can be: name, attribute, or value | -| transform.name | `string` | Name of the transformer (used by transformGroup to call a list of transforms). | -| transform.transitive | `boolean` | If the value transform should be applied transitively, i.e. should be applied to referenced values as well as absolute values. | -| [transform.matcher] | `function` | Matcher function, return boolean if transform should be applied. If you omit the matcher function, it will match all tokens. | -| transform.transformer | `function` | Modifies a design token object. The transformer function will receive the token and the platform configuration as its arguments. The transformer function should return a string for name transforms, an object for attribute transforms, and same type of value for a value transform. Can be async. | +| Param | Type | Description | +| -------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| transform | `Object` | Transform object | +| transform.type | `string` | Type of transform, can be: name, attribute, or value | +| transform.name | `string` | Name of the transform (used by transformGroup to call a list of transforms). | +| transform.transitive | `boolean` | If the value transform should be applied transitively, i.e. should be applied to referenced values as well as absolute values. | +| transform.filter | `function` | [Filter](/reference/hooks/filters) function, return boolean if transform should be applied. If you omit the filter function, it will match all tokens. | +| transform.transform | `function` | Modifies a design token object. The transform function will receive the token and the platform configuration as its arguments. The transform function should return a string for name transforms, an object for attribute transforms, and same type of value for a value transform. Can be async. | Example: @@ -433,10 +435,10 @@ Example: StyleDictionary.registerTransform({ name: 'time/seconds', type: 'value', - matcher: function (token) { + filter: function (token) { return token.type === 'time'; }, - transformer: function (token) { + transform: function (token) { // Note the use of prop.original.value, // before any transforms are performed, the build system // clones the original token to the 'original' attribute. diff --git a/docs/src/content/docs/reference/config.md b/docs/src/content/docs/reference/config.md index fd4495b8c..f8bc81a53 100644 --- a/docs/src/content/docs/reference/config.md +++ b/docs/src/content/docs/reference/config.md @@ -64,7 +64,7 @@ export default { // Now we can use the transform 'myTransform' below myTransform: { type: 'name', - transformer: (token) => token.path.join('_').toUpperCase(), + transform: (token) => token.path.join('_').toUpperCase(), }, }, // Same with formats, you can now write them directly to this config @@ -142,24 +142,20 @@ You would then change your npm script or CLI command to run that file with Node: ## Properties -| Property | Type | Description | -| :--------------- | :------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `log` | `Log` | [Configure logging behavior](/reference/logging) to either reduce/silence logs or to make them more verbose for debugging purposes. | -| `source` | `string[]` | An array of file path [globs](https://github.com/isaacs/node-glob) to design token files. Style Dictionary will do a deep merge of all of the token files, allowing you to organize your files however you want. | -| `include` | `string[]` | An array of file path [globs](https://github.com/isaacs/node-glob) to design token files that contain default styles. Style Dictionary uses this as a base collection of design tokens. The tokens found using the "source" attribute will overwrite tokens found using include. | -| `tokens` | `Object` | The tokens object is a way to include inline design tokens as opposed to using the `source` and `include` arrays. | -| `expand` | `ExpandConfig` | Configures whether and how composite (object-value) tokens will be expanded into separate tokens. `false` by default. Supports either `boolean`, `ExpandFilter` function or an Object containing a `typesMap` property and optionally an `include` OR `exclude` property. | -| `platforms` | `Record` | An object containing [platform](#platform) config objects that describe how the Style Dictionary should build for that platform. You can add any arbitrary attributes on this object that will get passed to formats and actions (more on these in a bit). This is useful for things like build paths, name prefixes, variable names, etc. | -| `hooks` | `Hooks` object | Object that contains all configured custom hooks: `preprocessors`. Note: `parsers`, `transforms`, `transformGroups`, `formats`, `fileHeaders`, `filters`, `actions` will be moved under property this later. Can be used to define hooks inline as an alternative to using `register<'Hook'>` methods. | -| `parsers` | `Parser[]` | Configured custom [file parsers](/reference/hooks/parsers) to run on input files | -| `preprocessors` | `string[]` | Which [preprocessors](/reference/hooks/preprocessors) (by name) to run on the full token dictionary, before any transforms run, can be registered using `.registerPreprocessor`. You can also configure this on the platform config level if you need to run it on the dictionary only for specific platforms. | -| `transform` | `Record` | Custom [transforms](/reference/hooks/transforms) you can include inline rather than using `.registerTransform`. The keys in this object will be the transform's name, the value should be an object with `type` | -| `transformGroup` | `Record` | Custom [transformGroups](/reference/hooks/transform_groups) you can include inline rather than using `.registerTransformGroup`. The keys in this object will be the transformGroup's name, the value should be an array with `transform`s | -| `format` | `Record` | Custom [formats](/reference/hooks/formats) you can include inline in the configuration rather than using `.registerFormat`. The keys in this object will be for format's name and value should be the formatter function. | -| `action` | `Record` | Custom inline [actions](/reference/hooks/actions). The keys in this object will be the action's name and the value should be an object containing `do` and `undo` methods. | -| `filter` | `Record` | Custom [filters](/reference/hooks/filters). The keys in this object will be the filters' names and the values should be Filter functions. | -| `fileHeader` | `Record` | Custom [fileHeaders](/reference/hooks/file_headers). The keys in this object will be the fileHeaders' names and the values should be FileHeader functions. | -| `usesDtcg` | `boolean` | Whether the tokens are using [DTCG Format](https://tr.designtokens.org/format/) or not. Usually you won't need to configure this, as style-dictionary will auto-detect this format. | +| Property | Type | Description | +| :-------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `log` | `Log` | [Configure logging behavior](/reference/logging) to either reduce/silence logs or to make them more verbose for debugging purposes. | +| `source` | `string[]` | An array of file path [globs](https://github.com/isaacs/node-glob) to design token files. Style Dictionary will do a deep merge of all of the token files, allowing you to organize your files however you want. | +| `include` | `string[]` | An array of file path [globs](https://github.com/isaacs/node-glob) to design token files that contain default styles. Style Dictionary uses this as a base collection of design tokens. The tokens found using the "source" attribute will overwrite tokens found using include. | +| `tokens` | `Object` | The tokens object is a way to include inline design tokens as opposed to using the `source` and `include` arrays. | +| `expand` | `ExpandConfig` | Configures whether and how composite (object-value) tokens will be expanded into separate tokens. `false` by default. Supports either `boolean`, `ExpandFilter` function or an Object containing a `typesMap` property and optionally an `include` OR `exclude` property. | +| `platforms` | `Record` | An object containing [platform](#platform) config objects that describe how the Style Dictionary should build for that platform. You can add any arbitrary attributes on this object that will get passed to formats and actions (more on these in a bit). This is useful for things like build paths, name prefixes, variable names, etc. | +| `hooks` | `Hooks` object | Object that contains all configured custom hooks: `preprocessors`. Note: `parsers`, `transforms`, `transformGroups`, `formats`, `fileHeaders`, `filters`, `actions` will be moved under property this later. Can be used to define hooks inline as an alternative to using `register` methods. | +| `parsers` | `string[]` | Names of custom [file parsers](/reference/hooks/parsers) to run on input files | +| `preprocessors` | `string[]` | Which [preprocessors](/reference/hooks/preprocessors) (by name) to run on the full token dictionary, before any transforms run, can be registered using `.registerPreprocessor`. You can also configure this on the platform config level if you need to run it on the dictionary only for specific platforms. | +| `transform` | `Record` | Custom [transforms](/reference/hooks/transforms) you can include inline rather than using `.registerTransform`. The keys in this object will be the transform's name, the value should be an object with `type` | +| `format` | `Record` | Custom [formats](/reference/hooks/formats) you can include inline in the configuration rather than using `.registerFormat`. The keys in this object will be for format's name and value should be the format function. | +| `usesDtcg` | `boolean` | Whether the tokens are using [DTCG Format](https://tr.designtokens.org/format/) or not. Usually you won't need to configure this, as style-dictionary will auto-detect this format. | ### Log diff --git a/docs/src/content/docs/version-4/migration.md b/docs/src/content/docs/version-4/migration.md index 810efb2b2..5e2e55ce6 100644 --- a/docs/src/content/docs/version-4/migration.md +++ b/docs/src/content/docs/version-4/migration.md @@ -96,7 +96,7 @@ StyleDictionary.registerFormat({ name: 'custom/css', // this can be async now, usually it is if you use fileHeader format helper, since that now always returns a Promise formatter: function ({ dictionary, file, options }) { - formatter: async function ({ dictionary, file, options }) { + format: async function ({ dictionary, file, options }) { const { outputReferences } = options; return ( fileHeader({ file }) + @@ -117,16 +117,50 @@ Available hooks are: `parsers`, `preprocessors`, `transformGroups`, `transforms` :::note The other hooks are also going to change similarly to preprocessors, in an effort to align these APIs and make them consistent across. -They will all be grouped under the `hooks` property, they will all use plural form vs singular (e.g. `transforms` vs `transform`), and lastly, -they will all use the same signature, with a `name` property and a handler function name that is the same as the hook name (e.g. `transformer` will be `transform`). -Parsers will also have to be applied explicitly similarly to preprocessors. +Hooks are now all grouped under the `hooks` property, they all use plural form vs singular (e.g. `transforms` vs `transform`), and lastly, +they will all use the same signature, with a `name` property and a handler function name that is the same as the hook name (e.g. `transformer` has become `transform`). +Parsers and preprocessors now also have to be applied explicitly in the config. ::: +### Parsers + +Parsers, when registered, would always apply on a global level, without explicitly applying them in the config. +They are put inside the `hooks.parsers` property now, as opposed to `parsers`. +Lastly, the `parse` function is now `parser`, for consistency. + +Changes: + +```js title="config.js" del={3-10} ins={9-24} /parse(r): (/ +export default { + // register it inline or by SD.registerPreprocessor + parsers: [ + { + pattern: /\.json5$/, + parse: ({ contents, filePath }) => { + return JSON5.parse(contents); + }, + }, + ], + hooks: { + parsers: { + name: 'json5-parser', + pattern: /\.json5$/, + parser: ({ contents, filePath }) => { + return JSON5.parse(contents); + }, + }, + }, + // apply it globally by name reference + parsers: ['json5-parser'], +}; +``` + ### Preprocessors Preprocessors, when registered, would always apply on a global level, without explicitly applying them in the config. +They are put inside the `hooks.preprocessors` property now, as opposed to `preprocessors`. -This has been changed now: +Changes: ```js title="config.js" del={3-8} ins={9-24} export default { @@ -149,18 +183,241 @@ export default { preprocessors: ['foo'], platforms: { css: { - // or apply is per platform + // or apply it per platform preprocessors: ['foo'], }, }, }; ``` +### Transform Groups + +Transform groups, when registered, are put inside the `hooks.transformGroups` property now, as opposed to `transformGroup`. +Note the change from singular to plural form here. + +Changes: + +```js title="config.js" del={3-5} ins={6-10} /transformGroup(s): {/ +export default { + // register it inline or by SD.registerTransformGroup + transformGroup: { + foo: ['foo-transform'], + }, + hooks: { + transformGroups: { + foo: ['foo-transform'], + }, + }, + platforms: { + css: { + // apply it per platform + transformGroup: ['foo'], + }, + }, +}; +``` + +### Transforms + +Transforms, when registered, are put inside the `hooks.transforms` property now, as opposed to `transform`. +Note the change from singular to plural form here. + +The name of the filter function is now `filter` instead of `matcher`: + +```js title="build-tokens.js" del={6} ins={7} /filter/ /matcher/ +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerTransform({ + name: 'color-transform', + type: 'value', + matcher: (token) => token.type === 'color', + filter: (token) => token.type === 'color', + transform: (token) => token.value, +}); +``` + +Lastly, the `transformer` handler function has been renamed to `transform` for consistency. + +Changes: + +```js title="config.js" del={3-9} ins={10-18} /transform(s): {/ /filter/ /matcher/ /transform(er)/ /(transform): (/ +export default { + // register it inline or by SD.registerTransform + transform: { + 'color-transform': { + type: 'value', + matcher: (token) => token.type === 'color', + transformer: (token) => token.value, + }, + }, + hooks: { + transforms: { + 'color-transform': { + type: 'value', + filter: (token) => token.type === 'color', + transform: (token) => token.value, + }, + }, + }, + platforms: { + css: { + // apply it per platform + transforms: ['color-transform'], + }, + }, +}; +``` + +### Formats + +Formats, when registered, are put inside the `hooks.formats` property now, as opposed to `format`. +Note the change from singular to plural form here. + +The `formatter` handler function has been renamed to `format` for consistency. + +Lastly, some importable type interfaces have been renamed as well. + +Changes: + +```js title="config.js" del={2,8,14-16} ins={3,9,17-21} /format(s): {/ /format(ter)/ +import StyleDictionary from 'style-dictionary'; +import type { Formatter, FormatterArguments } from 'style-dictionary/types'; +import type { FormatFn, FormatFnArguments } from 'style-dictionary/types'; + +// register it with register method +StyleDictionary.registerFormat({ + name: 'custom/json', + formatter: ({ dictionary }) => JSON.stringify(dictionary, 2, null), + format: ({ dictionary }) => JSON.stringify(dictionary, 2, null), +}) + +export default { + // OR define it inline + format: { + 'custom/json': ({ dictionary }) => JSON.stringify(dictionary, 2, null), + }, + hooks: { + formats: { + 'custom/json': ({ dictionary }) => JSON.stringify(dictionary, 2, null), + }, + }, + platforms: { + json: { + files: [{ + destination: 'output.json', + format: 'custom/json' + }], + }, + }, +}; +``` + +### File headers + +File headers, when registered, are put inside the `hooks.fileHeaders` property now, as opposed to `fileHeader`. +Note the change from singular to plural form here. + +```js title="config.js" del={2-4} ins={5-9} /fileHeader(s)/ +export default { + fileHeader: { + foo: (defaultMessages = []) => ['Ola, planet!', ...defaultMessages, 'Hello, World!'], + }, + hooks: { + fileHeaders: { + foo: (defaultMessages = []) => ['Ola, planet!', ...defaultMessages, 'Hello, World!'], + }, + }, + platforms: { + css: { + options: { + fileHeader: 'foo', + }, + }, + }, +}; +``` + +### Filters + +Filters, when registered, are put inside the `hooks.filters` property now, as opposed to `filter`. +Note the change from singular to plural form here. + +```js title="config.js" del={2-4} ins={5-9} /filter(s)/ +export default { + filter: { + 'colors-only': (token) => token.type === 'color, + }, + hooks: { + filters: { + 'colors-only': (token) => token.type === 'color, + }, + }, + platforms: { + css: { + files: [{ + format: 'css/variables', + destination: '_variables.css', + filter: 'colors-only', + }], + }, + }, +}; +``` + +In addition, when using [`registerFilter`](/reference/api#registerfilter) method, the name of the filter function is now `filter` instead of `matcher`: + +```js title="build-tokens.js" del={5} ins={6} +import StyleDictionary from 'style-dictionary'; + +StyleDictionary.registerFilter({ + name: 'colors-only', + matcher: (token) => token.type === 'color', + filter: (token) => token.type === 'color', +}); +``` + +:::note +These changes also apply for the [filter function inside transforms](#transforms). +::: + +### Actions + +Actions, when registered, are put inside the `hooks.actions` property now, as opposed to `action`. +Note the change from singular to plural form here. + +```js title="config.js" del={2-7} ins={8-15} /action(s): {/ +export default { + action: { + 'copy-assets': { + do: () => {} + undo: () => {} + } + }, + hooks: { + actions: { + 'copy-assets': { + do: () => {} + undo: () => {} + } + }, + }, + platforms: { + css: { + actions: ['copy-assets'], + files: [{ + format: 'css/variables', + destination: '_variables.css', + }], + }, + }, +}; +``` + ## CTI reliance [CTI or Category / Type / Item](/info/tokens/#category--type--item) used to be the default way of structuring design tokens in Style Dictionary. Often, what type of token a token is, would be determined by looking at the "category" part of the token taxonomy. -Most of the [Built-in transforms](/reference/hooks/transforms/predefined) `matcher` function would rely on the token's `attributes.category` property. +Most of the [Built-in transforms](/reference/hooks/transforms/predefined) `matcher`/`filter` (filter being the new name for this) function would rely on the token's `attributes.category` property. This in turn would rely on applying the [`attribute/cti` transform](/reference/hooks/transforms/predefined#attributecti) so that this attribute was set on a token. In version 4, we have removed almost all hard-coupling/reliances on CTI structure and instead we will look for a `token.type` property to determine what type of token a design token is. @@ -292,6 +549,13 @@ In v3, the following options were put on the file properties level itself next t - `mapName` -> for formats: - `scss/map-deep` - `scss/map-flat` +- `name` -> for formats: + - `javascript/object` + - `javascript/umd` +- `resourceType` -> for formats: + - `android/resources` +- `resourceMap` -> for formats: + - `android/resources` ```json title="config.json" del={9} ins={10-13} { @@ -379,7 +643,7 @@ import { usesReferences, getReferences } from 'style-dictionary/utils'; StyleDictionary.registerFormat({ name: `myCustomFormat`, - formatter: function({ dictionary }) { + format: function({ dictionary }) { return dictionary.allTokens.map(token => { let value = JSON.stringify(token.value); if (dictionary.usesReference(token.original.value)) { @@ -410,7 +674,7 @@ If you are maintaining a custom format that allows `outputReferences` option, yo ```js title="build-tokens.js" del={12} ins={13-17} StyleDictionary.registerFormat({ name: 'custom/es6', - formatter: async (dictionary) => { + format: async (dictionary) => { const { allTokens, options, file } = dictionary; const { usesDtcg } = options; @@ -475,7 +739,7 @@ console.log(sd.allProperties, sd.properties); console.log(sd.allTokens, sd.tokens); ``` -- `format.formatter` old function signature of `(dictionary, platform, file)` in favor of `({ dictionary, platform, options, file })`. +- `format.format` old function signature of `(dictionary, platform, file)` in favor of `({ dictionary, platform, options, file })`. ```js title="build-tokens.js" del={8,10} ins={9,11} import StyleDictionary from 'style-dictionary'; @@ -486,7 +750,7 @@ import { fileHeader } from 'style-dictionary/utils'; StyleDictionary.registerFormat({ name: 'custom/css', formatter: async function (dictionary, platform, file) { - formatter: async function ({ dictionary, file, options }) { + format: async function ({ dictionary, file, options }) { const { outputReferences } = file.options; const { outputReferences } = options; return ( diff --git a/docs/starlight-config.ts b/docs/starlight-config.ts index c3e6aa4f4..8f7906ab0 100644 --- a/docs/starlight-config.ts +++ b/docs/starlight-config.ts @@ -89,6 +89,8 @@ export default { }, ], }, + { label: 'Filters', link: '/reference/hooks/filters' }, + { label: 'File Headers', link: '/reference/hooks/file-headers' }, { label: 'Actions', link: '/reference/hooks/actions' }, ], }, diff --git a/examples/advanced/component-cti/README.md b/examples/advanced/component-cti/README.md index 2a19440f8..819566c1c 100644 --- a/examples/advanced/component-cti/README.md +++ b/examples/advanced/component-cti/README.md @@ -72,7 +72,7 @@ const StyleDictionary = require('style-dictionary'); const cti = StyleDictionary.transform['attribute/cti']; // { // type: 'attribute', -// transformer: f () {} +// transform: f () {} // } ``` @@ -84,5 +84,5 @@ For example, if the key of the token (the last part of the object path) is "back - `config.js`: - `propertiesToCTI`: A plain object where we map the CSS property name to the proper category and type. - - `CTITransform`: A transform object that defines a transformer method, which will override the default `attribute/cti` transform. This is similar to creating a child class with some custom logic and then calling `super`. In the transformer function it first looks at the top-level namespace, the first item in the object path, and if it is 'component' we run our custom logic using the `propertiesToCTI` map. If it is not 'component', use the built-in `attribute/cti`. + - `CTITransform`: A transform object that defines a transform method, which will override the default `attribute/cti` transform. This is similar to creating a child class with some custom logic and then calling `super`. In the transform function it first looks at the top-level namespace, the first item in the object path, and if it is 'component' we run our custom logic using the `propertiesToCTI` map. If it is not 'component', use the built-in `attribute/cti`. - `tokens/component/button.json`: Take a look at how it defines the component tokens and uses the last part of the object path as the CSS property. Notice how we can define token values in here that are not references, but they still get transformed properly: font-size uses 'rem' and background-color uses hex in the output. diff --git a/examples/advanced/component-cti/config.js b/examples/advanced/component-cti/config.js index 0d5ec3cba..c72c60399 100644 --- a/examples/advanced/component-cti/config.js +++ b/examples/advanced/component-cti/config.js @@ -1,5 +1,5 @@ const StyleDictionary = require('style-dictionary'); -const transformer = StyleDictionary.transform['attribute/cti'].transformer; +const transform = StyleDictionary.transform['attribute/cti'].transform; const propertiesToCTI = { width: { category: 'size', type: 'dimension' }, @@ -25,15 +25,15 @@ const propertiesToCTI = { const CTITransform = { type: `attribute`, - transformer: (prop) => { + transform: (prop) => { // Only do this custom functionality in the 'component' top-level namespace. if (prop.path[0] === 'component') { // When defining component tokens, the key of the token is the relevant CSS property // The key of the token is the last element in the path array return propertiesToCTI[prop.path[prop.path.length - 1]]; } else { - // Fallback to the original 'attribute/cti' transformer - return transformer(prop); + // Fallback to the original 'attribute/cti' transform + return transform(prop); } }, }; @@ -44,7 +44,7 @@ const CTITransform = { // StyleDictionary.registerTransform({ // name: 'attribute/cti', // type: 'attribute', -// transformer: CTITransform.transformer +// transform: CTITransform.transform // }); module.exports = { diff --git a/examples/advanced/custom-file-header/README.md b/examples/advanced/custom-file-header/README.md index 9ce5e6cf1..97b1017cf 100644 --- a/examples/advanced/custom-file-header/README.md +++ b/examples/advanced/custom-file-header/README.md @@ -23,7 +23,7 @@ There are 3 ways to add a custom file header: This example codebase has all 3 methods for you to see which works best for you. All 3 methods are in the [**build.js**](/build.js) file. -A file header is a function that returns an array of strings. This array of strings will get mapped to lines in a comment at the beginning of a file generated by a formatter. The formatter will take care of how to format the lines in the proper comment style (`//`, `/* */`). +A file header is a function that returns an array of strings. This array of strings will get mapped to lines in a comment at the beginning of a file generated by a format. The format will take care of how to format the lines in the proper comment style (`//`, `/* */`). You can reference the file header in a custom format as well by using the `fileHeader` function in `StyleDictionary.formatHelpers`, as shown below: diff --git a/examples/advanced/custom-filters/README.md b/examples/advanced/custom-filters/README.md index 871a55ae0..2b819959a 100644 --- a/examples/advanced/custom-filters/README.md +++ b/examples/advanced/custom-filters/README.md @@ -60,7 +60,7 @@ To declare a custom **filter**, you have to call the `registerFilter` method: ```js StyleDictionary.registerFilter({ name: 'isTextTransform', - matcher: function (token) { + filter: function (token) { return token.attributes.category === 'font' && token.value.includes[('lowercase', 'uppercase')]; }, }); diff --git a/examples/advanced/custom-filters/build.js b/examples/advanced/custom-filters/build.js index 0eb0fe851..e31fbcf9f 100644 --- a/examples/advanced/custom-filters/build.js +++ b/examples/advanced/custom-filters/build.js @@ -7,7 +7,7 @@ console.log('\n=============================================='); StyleDictionary.registerFilter({ name: 'removeBigSpacing', - matcher: function (token) { + filter: function (token) { return token.group === 'spacing' && token.value < 0.5; }, }); diff --git a/examples/advanced/custom-formats-with-templates/README.md b/examples/advanced/custom-formats-with-templates/README.md index b180ba480..9675e721d 100644 --- a/examples/advanced/custom-formats-with-templates/README.md +++ b/examples/advanced/custom-formats-with-templates/README.md @@ -17,7 +17,7 @@ The "build" command will run the custom script `build.js`, that contains three d ``` StyleDictionary.registerFormat({ name: 'custom/format/android-xml', - formatter: _.template(fs.readFileSync(__dirname + '/templates/android-xml.template')) + format: _.template(fs.readFileSync(__dirname + '/templates/android-xml.template')) }); ``` diff --git a/examples/advanced/custom-formats-with-templates/build.js b/examples/advanced/custom-formats-with-templates/build.js index 035ae1f61..2fe5fb309 100644 --- a/examples/advanced/custom-formats-with-templates/build.js +++ b/examples/advanced/custom-formats-with-templates/build.js @@ -21,17 +21,17 @@ console.log('\n=============================================='); sd.registerFormat({ name: 'custom/format/scss', - formatter: ({ dictionary }) => webScssTemplate({ allTokens: dictionary.allTokens }), + format: ({ dictionary }) => webScssTemplate({ allTokens: dictionary.allTokens }), }); sd.registerFormat({ name: 'custom/format/ios-plist', - formatter: async ({ dictionary }) => plistTemplate({ dictionary }), + format: async ({ dictionary }) => plistTemplate({ dictionary }), }); sd.registerFormat({ name: 'custom/format/android-xml', - formatter: async ({ dictionary }) => androidTemplate({ dictionary }), + format: async ({ dictionary }) => androidTemplate({ dictionary }), }); // In this case we are using an alternative templating engine (Handlebars) @@ -41,11 +41,11 @@ const templateCustomXml = handlebars.compile( sd.registerFormat({ name: 'custom/format/android-xml-alt', - formatter: function ({ dictionary, platform }) { + format: function ({ dictionary, platform }) { return templateCustomXml({ - // this is to show that the formatter function only takes a "dictionary" and "platform" parameters + // this is to show that the format function only takes a "dictionary" and "platform" parameters // (and dictionary has "tokens" and "allTokens" attributes) - // and returns a string. for more details about the "formatter" function refer to the documentation + // and returns a string. for more details about the "format" function refer to the documentation allTokens: dictionary.allTokens, tokens: dictionary.tokens, options: platform, @@ -60,7 +60,7 @@ const templateCustomPlist = pug.compileFile(__dirname + '/templates/ios-plist_al sd.registerFormat({ name: 'custom/format/ios-plist-alt', - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return templateCustomPlist({ allTokens: dictionary.allTokens, }); diff --git a/examples/advanced/custom-transforms/README.md b/examples/advanced/custom-transforms/README.md index b0fc41e6d..7d4a167e9 100644 --- a/examples/advanced/custom-transforms/README.md +++ b/examples/advanced/custom-transforms/README.md @@ -21,16 +21,16 @@ At this point, if you want to build the tokens you can run `npm run build`. This To declare a custom **transform**, you have to call the `registerTransform` method: -``` +```js StyleDictionary.registerTransform({ name: 'ratio/%', type: 'value', - matcher: function(token) { - return token.group === 'ratio'; + filter: function (token) { + return token.group === 'ratio'; + }, + transform: function (token) { + return `${Math.floor(100 * token.value)}%`; }, - transformer: function(token) { - return `${Math.floor(100 * token.value)}%`; - } }); ``` @@ -75,15 +75,15 @@ in your code and look the array of transforms associated with the it, directly i Open the `config.json` file and see how for each platform there is a `transformGroup` declaration. In this specific example, all the transformGroups applied to the platforms are custom. -Now open a token file (eg. `tokens/colors|font|spacing.json`) to see how we have associated custom attributes to the tokens, to be used later in the matchers functions. See also how the values are unit-less, where the units are applied at build time accordingly to the destination platform. Compare the values in the input JSON files, and the values that appear in the files generated in `build`, and you will see where and how the transformations have been applied. +Now open a token file (eg. `tokens/colors|font|spacing.json`) to see how we have associated custom attributes to the tokens, to be used later in the filter functions. See also how the values are unit-less, where the units are applied at build time accordingly to the destination platform. Compare the values in the input JSON files, and the values that appear in the files generated in `build`, and you will see where and how the transformations have been applied. Now open the `build.js` script and look at how these custom transforms/transformGroups are declared and registered via the `registerTransform` and `registerTransform` API methods. We have added as many comments as possible to make the code clear and show the interesting parts of it. A few things to notice in the file: - the name of a custom "transform" can be the same as an existing pre-defined method; in that case, the pre-defined method is overwritten -- beyond the existing attributes, you can use custom attributes to create **matcher** functions, used to filter the tokens and apply the transform only to those that match the filter condition. -- if you don't specify a **matcher**, the transformation will be applied to all the tokens +- beyond the existing attributes, you can use custom attributes to create **filter** functions, used to filter the tokens and apply the transform only to those that match the filter condition. +- if you don't specify a **filter**, the transformation will be applied to all the tokens - the transformation can be applied not only to the **value** of a token, but also to its **name** (and also to its attributes) **IMPORTANT**: the registration of custom transforms needs to be done _before_ applying the configuration (the methods needs to be already declared and registered in Style Dictionary to be used when extending it with the configuration). See the code in the `build.js` for more details. diff --git a/examples/advanced/custom-transforms/build.js b/examples/advanced/custom-transforms/build.js index cd135c959..370c4f310 100644 --- a/examples/advanced/custom-transforms/build.js +++ b/examples/advanced/custom-transforms/build.js @@ -12,11 +12,11 @@ console.log('\n=============================================='); StyleDictionary.registerTransform({ name: 'size/px', // notice: the name is an override of an existing predefined method (yes, you can do it) type: 'value', - matcher: function (token) { - // this is an example of a possible filter (based on the "cti" values) to show how a "matcher" works + filter: function (token) { + // this is an example of a possible filter (based on the "cti" values) to show how a "filter" works return token.attributes.category === 'font' || token.attributes.category === 'margin'; }, - transformer: function (token) { + transform: function (token) { return `${token.value}px`; }, }); @@ -24,11 +24,11 @@ StyleDictionary.registerTransform({ StyleDictionary.registerTransform({ name: 'ratio/%', type: 'value', - matcher: function (token) { + filter: function (token) { // here we are using a custom attribute, declared in the token, to match the values where apply the transform return token.group === 'ratio'; }, - transformer: function (token) { + transform: function (token) { return `${Math.floor(100 * token.value)}%`; }, }); @@ -36,10 +36,10 @@ StyleDictionary.registerTransform({ StyleDictionary.registerTransform({ name: 'hexRGB/hexARGB', type: 'value', - matcher: function (token) { + filter: function (token) { return token.group === 'color'; }, - transformer: function (token) { + transform: function (token) { // for sake of simplicity, in this example we assume colors are always in the format #xxxxxx return token.value.replace(/^#/, '#FF'); }, @@ -48,10 +48,10 @@ StyleDictionary.registerTransform({ StyleDictionary.registerTransform({ name: 'unitless/dp-sp', type: 'value', - matcher: function (token) { + filter: function (token) { return token.group === 'typography' || token.group === 'spacing'; }, - transformer: function (token) { + transform: function (token) { // in Android font sizes are expressed in "sp" units let unit = token.group === 'typography' ? 'sp' : 'dp'; return `${token.value}${unit}`; @@ -62,8 +62,8 @@ StyleDictionary.registerTransform({ // this is a silly example, to show how you can apply transform to names name: 'name/squiggle', type: 'name', - // notice: if you don't specify a matcher, the transformation will be applied to all the tokens - transformer: function (token) { + // notice: if you don't specify a filter, the transformation will be applied to all the tokens + transform: function (token) { return token.path.join('~'); }, }); @@ -89,7 +89,7 @@ StyleDictionary.registerTransformGroup({ StyleDictionary.registerTransformGroup({ name: 'custom/android', // as you can see, here we are completely ignoring the "attribute/cti" transform (it's totally possible), - // because we are relying on custom attributes for the matchers and the custom format for the output + // because we are relying on custom attributes for the filters and the custom format for the output transforms: ['name/squiggle', 'hexRGB/hexARGB', 'unitless/dp-sp'], }); @@ -97,7 +97,7 @@ StyleDictionary.registerTransformGroup({ StyleDictionary.registerFormat({ name: 'custom/android/xml', - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return dictionary.allTokens .map(function (token) { return `${token.value}`; diff --git a/examples/advanced/font-face-rules/sd.config.js b/examples/advanced/font-face-rules/sd.config.js index 96917cc13..9888f4579 100644 --- a/examples/advanced/font-face-rules/sd.config.js +++ b/examples/advanced/font-face-rules/sd.config.js @@ -5,7 +5,7 @@ const StyleDictionary = require('style-dictionary'); StyleDictionary.registerTransform({ name: 'attribute/font', type: 'attribute', - transformer: (prop) => ({ + transform: (prop) => ({ category: prop.path[0], type: prop.path[1], family: prop.path[2], @@ -17,7 +17,7 @@ StyleDictionary.registerTransform({ // Register a custom format to generate @font-face rules. StyleDictionary.registerFormat({ name: 'font-face', - formatter: ({ dictionary: { allTokens }, options }) => { + format: ({ dictionary: { allTokens }, options }) => { const fontPathPrefix = options.fontPathPrefix || '../'; // https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src diff --git a/examples/advanced/matching-build-files/config.js b/examples/advanced/matching-build-files/config.js index e78ffcd5b..8e48be07d 100644 --- a/examples/advanced/matching-build-files/config.js +++ b/examples/advanced/matching-build-files/config.js @@ -81,7 +81,7 @@ module.exports = { StyleDictionary.registerFormat({ name: 'custom/cjsmodule', - formatter: function ({ dictionary }) { + format: function ({ dictionary }) { return `module.exports = {${dictionary.allTokens.map( (token) => `\n\t${token.name}: "${token.value}"`, )}\n};`; diff --git a/examples/advanced/node-modules-as-config-and-properties/config.js b/examples/advanced/node-modules-as-config-and-properties/config.js index 1952b3b44..547727362 100644 --- a/examples/advanced/node-modules-as-config-and-properties/config.js +++ b/examples/advanced/node-modules-as-config-and-properties/config.js @@ -12,13 +12,13 @@ const buildPath = 'build/'; StyleDictionary.registerTransform({ name: 'myRegisteredTransform', type: 'value', - matcher: (token) => token.attributes.category === 'size', - transformer: (token) => `${parseInt(token.value) * 16}px`, + filter: (token) => token.attributes.category === 'size', + transform: (token) => `${parseInt(token.value) * 16}px`, }); StyleDictionary.registerFormat({ name: 'myRegisteredFormat', - formatter: ({ dictionary }) => { + format: ({ dictionary }) => { return dictionary.allTokens.map((token) => token.value).join('\n'); }, }); @@ -39,7 +39,7 @@ module.exports = { // Now we can use the transform 'myTransform' below myTransform: { type: 'name', - transformer: (token) => token.path.join('_').toUpperCase(), + transform: (token) => token.path.join('_').toUpperCase(), }, }, // Same with formats, you can now write them directly to this config diff --git a/examples/advanced/tokens-deprecation/build.js b/examples/advanced/tokens-deprecation/build.js index 42057617f..1d4295a39 100644 --- a/examples/advanced/tokens-deprecation/build.js +++ b/examples/advanced/tokens-deprecation/build.js @@ -13,7 +13,7 @@ console.log('\n=============================================='); sd.registerFormat({ name: 'custom/format/scss', - formatter: async ({ dictionary, file, options }) => { + format: async ({ dictionary, file, options }) => { const { allTokens } = dictionary; const header = await fileHeader({ file, commentStyle: 'long' }); return webScssTemplate({ allTokens, file, options, header }); @@ -22,7 +22,7 @@ sd.registerFormat({ sd.registerFormat({ name: 'custom/format/ios-plist', - formatter: async ({ dictionary, file, options }) => { + format: async ({ dictionary, file, options }) => { const header = await fileHeader({ file, commentStyle: 'xml' }); return iosPlistTemplate({ dictionary, options, file, header }); }, diff --git a/examples/advanced/transitive-transforms/sd.config.js b/examples/advanced/transitive-transforms/sd.config.js index 2239051e7..d8a73e140 100644 --- a/examples/advanced/transitive-transforms/sd.config.js +++ b/examples/advanced/transitive-transforms/sd.config.js @@ -42,8 +42,8 @@ export default { // only transforms that have transitive: true will be applied to tokens // that alias/reference other tokens transitive: true, - matcher: (token) => token.attributes.category === 'color' && token.modify, - transformer: colorTransform, + filter: (token) => token.attributes.category === 'color' && token.modify, + transform: colorTransform, }, // For backwards compatibility, all built-in transforms are not transitive diff --git a/lib/Register.js b/lib/Register.js index 52bbde15c..2a8c48a47 100644 --- a/lib/Register.js +++ b/lib/Register.js @@ -1,24 +1,47 @@ -import transform from './common/transforms.js'; -import transformGroup from './common/transformGroups.js'; -import format from './common/formats.js'; -import action from './common/actions.js'; -import filter from './common/filters.js'; +import transformBuiltins from './common/transforms.js'; +import transformGroupBuiltins from './common/transformGroups.js'; +import formatBuiltins from './common/formats.js'; +import actionBuiltins from './common/actions.js'; +import filterBuiltins from './common/filters.js'; import { deepmerge } from './utils/deepmerge.js'; /** * @typedef {import('../types/File.d.ts').FileHeader} FileHeader * @typedef {import('../types/Parser.d.ts').Parser} Parser * @typedef {import('../types/Preprocessor.d.ts').Preprocessor} Preprocessor - * @typedef {import('../types/Preprocessor.d.ts').preprocessor} preprocessor * @typedef {import('../types/Transform.d.ts').Transform} Transform * @typedef {import('../types/Filter.d.ts').Filter} Filter - * @typedef {import('../types/Filter.d.ts').Matcher} Matcher * @typedef {import('../types/Format.d.ts').Format} Format - * @typedef {import('../types/Format.d.ts').Formatter} Formatter * @typedef {import('../types/Action.d.ts').Action} Action - * @typedef {{ preprocessors: Record>}} Hooks + * @typedef {import('../types/Config.d.ts').Hooks} Hooks */ +/** + * @return {Required} + */ +function getBuiltinHooks() { + return { + parsers: {}, + preprocessors: {}, + transformGroups: { + ...transformGroupBuiltins, + }, + transforms: { + ...transformBuiltins, + }, + formats: { + ...formatBuiltins, + }, + fileHeaders: {}, + filters: { + ...filterBuiltins, + }, + actions: { + ...actionBuiltins, + }, + }; +} + export class Register { /** * Below is a ton of boilerplate. Explanation: @@ -28,29 +51,16 @@ export class Register { * * Therefore, we have to make use of static props vs instance props and use getters and setters to merge these together. */ - /** @type {Hooks} */ - static hooks = { - preprocessors: {}, - }; + static hooks = getBuiltinHooks(); - static transform = transform; - static transformGroup = transformGroup; - static format = format; - static action = action; - static filter = filter; - /** @type {Record} */ - static fileHeader = {}; - /** @type {Parser[]} */ - static parsers = []; // we need to initialise the array, since we don't have built-in parsers - - /** @type {Hooks} */ + /** @type {Required} */ get hooks() { const ctor = /** @type {typeof Register} */ (this.constructor); return deepmerge(ctor.hooks, this._hooks ?? {}); } /** - * @param {Hooks} v + * @param {Required} v */ set hooks(v) { this._hooks = v; @@ -78,37 +88,28 @@ export class Register { */ static __registerTransform(transform, target) { const transformTypes = ['name', 'value', 'attribute']; - const { type, name, matcher, transitive, transformer } = transform; + const { type, name, filter, transitive, transform: transformFn } = transform; if (typeof type !== 'string') throw new Error('type must be a string'); if (transformTypes.indexOf(type) < 0) throw new Error(type + ' type is not one of: ' + transformTypes.join(', ')); if (typeof name !== 'string') throw new Error('name must be a string'); - if (matcher && typeof matcher !== 'function') throw new Error('matcher must be a function'); - if (typeof transformer !== 'function') throw new Error('transformer must be a function'); + if (filter && typeof filter !== 'function') throw new Error('filter must be a function'); + if (typeof transformFn !== 'function') throw new Error('transform must be a function'); // make sure to trigger the setter - target.transform = { - ...target.transform, - [name]: { - type, - matcher, - transitive: !!transitive, - transformer, + target.hooks = deepmerge(target.hooks, { + transforms: { + [name]: { + type, + filter, + transitive: !!transitive, + transform: transformFn, + }, }, - }; + }); return this; } - get transform() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.transform, ...this._transform }; - } - - /** @param {Record>} v */ - set transform(v) { - this._transform = v; - } - /** * @param {{ name: string; transforms: string[]; }} cfg */ @@ -136,27 +137,18 @@ export class Register { throw new Error('transforms must be an array of registered value transforms'); transforms.forEach((t) => { - if (!(t in target.transform)) + if (!target.hooks.transforms || !(t in target.hooks.transforms)) throw new Error('transforms must be an array of registered value transforms'); }); // make sure to trigger the setter - target.transformGroup = { - ...target.transformGroup, - [name]: transforms, - }; + target.hooks = deepmerge(target.hooks, { + transformGroups: { + [name]: transforms, + }, + }); return target; } - get transformGroup() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.transformGroup, ...this._transformGroup }; - } - - /** @param {Record} v */ - set transformGroup(v) { - this._transformGroup = v; - } - /** * @param {Format} cfg */ @@ -178,29 +170,20 @@ export class Register { * @param {typeof Register | Register} target */ static __registerFormat(format, target) { - const { name, formatter } = format; + const { name, format: formatFn } = format; if (typeof name !== 'string') throw new Error("Can't register format; format.name must be a string"); - if (typeof formatter !== 'function') - throw new Error("Can't register format; format.formatter must be a function"); + if (typeof formatFn !== 'function') + throw new Error("Can't register format; format.format must be a function"); // make sure to trigger the setter - target.format = { - ...target.format, - [name]: formatter, - }; + target.hooks = deepmerge(target.hooks, { + formats: { + [name]: formatFn, + }, + }); return target; } - get format() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.format, ...this._format }; - } - - /** @param {Record} v */ - set format(v) { - this._format = v; - } - /** * @param {Action} cfg */ @@ -226,26 +209,17 @@ export class Register { if (typeof name !== 'string') throw new Error('name must be a string'); if (typeof _do !== 'function') throw new Error('do must be a function'); // make sure to trigger the setter - target.action = { - ...target.action, - [name]: { - do: _do, - undo, + target.hooks = deepmerge(target.hooks, { + actions: { + [name]: { + do: _do, + undo, + }, }, - }; + }); return target; } - get action() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.action, ...this._action }; - } - - /** @param {Record>} v */ - set action(v) { - this._action = v; - } - /** * @param {Filter} cfg */ @@ -267,29 +241,20 @@ export class Register { * @param {typeof Register | Register} target */ static __registerFilter(filter, target) { - const { name, matcher } = filter; + const { name, filter: filterFn } = filter; if (typeof name !== 'string') throw new Error("Can't register filter; filter.name must be a string"); - if (typeof matcher !== 'function') - throw new Error("Can't register filter; filter.matcher must be a function"); + if (typeof filterFn !== 'function') + throw new Error("Can't register filter; filter.filter must be a function"); // make sure to trigger the setter - target.filter = { - ...target.filter, - [name]: matcher, - }; + target.hooks = deepmerge(target.hooks, { + filters: { + [name]: filterFn, + }, + }); return target; } - get filter() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.filter, ...this._filter }; - } - - /** @param {Record} v */ - set filter(v) { - this._filter = v; - } - /** * @param {Parser} cfg */ @@ -311,26 +276,25 @@ export class Register { * @param {typeof Register | Register} target */ static __registerParser(parser, target) { - const { pattern, parse } = parser; + const { name, pattern, parser: parserFn } = parser; + if (typeof name !== 'string') + throw new Error("Can't register parser; parser.name must be a string"); if (!(pattern instanceof RegExp)) throw new Error(`Can't register parser; parser.pattern must be a regular expression`); - if (typeof parse !== 'function') - throw new Error("Can't register parser; parser.parse must be a function"); + if (typeof parserFn !== 'function') + throw new Error("Can't register parser; parser.parser must be a function"); // make sure to trigger the setter - target.parsers = [...target.parsers, parser]; + target.hooks = deepmerge(target.hooks, { + parsers: { + [name]: { + pattern, + parser: parserFn, + }, + }, + }); return target; } - get parsers() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return [...ctor.parsers, ...(this._parsers ?? [])]; - } - - /** @param {Parser[]} v */ - set parsers(v) { - this._parsers = v; - } - /** * @param {Preprocessor} cfg */ @@ -360,15 +324,11 @@ export class Register { throw new Error(`${errorPrefix} Preprocessor.preprocessor must be a function`); } // make sure to trigger the setter - target.hooks = { - ...target.hooks, + target.hooks = deepmerge(target.hooks, { preprocessors: { - ...target.hooks.preprocessors, - [name]: { - preprocessor, - }, + [name]: preprocessor, }, - }; + }); return target; } @@ -400,34 +360,15 @@ export class Register { throw new Error("Can't register file header; options.fileHeader must be a function"); // make sure to trigger the setter - target.fileHeader = { - ...target.fileHeader, - [name]: fileHeader, - }; + target.hooks = deepmerge(target.hooks, { + fileHeaders: { + [name]: fileHeader, + }, + }); return target; } - get fileHeader() { - const ctor = /** @type {typeof Register} */ (this.constructor); - return { ...ctor.fileHeader, ...this._fileHeader }; - } - - /** @param {Record} v */ - set fileHeader(v) { - this._fileHeader = v; - } - constructor() { - this.transform = {}; - this.transformGroup = {}; - this.format = {}; - this.action = {}; - this.filter = {}; - this.fileHeader = {}; - this.parsers = []; // we need to initialise the array, since we don't have built-in parsers - - this.hooks = { - preprocessors: {}, - }; + this.hooks = getBuiltinHooks(); } } diff --git a/lib/StyleDictionary.js b/lib/StyleDictionary.js index c637bc6e1..c1a531e04 100644 --- a/lib/StyleDictionary.js +++ b/lib/StyleDictionary.js @@ -79,13 +79,6 @@ export default class StyleDictionary extends Register { // so that when we extend, we include registered things const opts = deepmerge( { - transform: this.transform, - transformGroup: this.transformGroup, - format: this.format, - action: this.action, - filter: this.filter, - fileHeader: this.fileHeader, - parsers: this.parsers, hooks: this.hooks, }, this._options ?? {}, @@ -127,11 +120,11 @@ export default class StyleDictionary extends Register { this.include = []; /** @type {ExpandConfig|undefined} */ this.expand = undefined; - /** @type {Record} */ - this.expandTypesMap = {}; /** @type {Record} */ this.platforms = {}; /** @type {string[]} */ + this.parsers = []; + /** @type {string[]} */ this.preprocessors = []; if (volume) { // when a user sets a custom FS shim, mark it for later reference @@ -250,6 +243,11 @@ export default class StyleDictionary extends Register { if (Object.entries(this.tokens).length > 0 && this.usesDtcg === undefined) { this.usesDtcg = detectDtcgSyntax(this.tokens); } + + const appliedParsers = Object.fromEntries( + Object.entries(this.hooks.parsers ?? {}).filter(([key]) => this.parsers.includes(key)), + ); + // grab the inline tokens, ones either defined in the configuration object // or that already exist from extending another style dictionary instance // with `tokens` keys @@ -258,13 +256,12 @@ export default class StyleDictionary extends Register { // Update tokens with includes from dependencies if (this.include) { if (!Array.isArray(this.include)) throw new Error('include must be an array'); - const result = await combineJSON( this.include, true, undefined, false, - this.parsers, + appliedParsers, this.usesDtcg, this.volume, ); @@ -294,7 +291,7 @@ export default class StyleDictionary extends Register { ); }, true, - this.parsers, + appliedParsers, this.usesDtcg, this.volume, ); @@ -328,7 +325,7 @@ export default class StyleDictionary extends Register { if (this.usesDtcg) { tokens = typeDtcgDelegate(tokens); } - if (this.shouldRunExpansion(this.options.expand)) { + if (this.shouldRunExpansion(this.expand)) { tokens = expandTokens(tokens, this.options); } this.tokens = await preprocess(tokens, this.preprocessors, this.hooks.preprocessors); diff --git a/lib/buildFile.js b/lib/buildFile.js index 6dd206dd5..6d766c057 100644 --- a/lib/buildFile.js +++ b/lib/buildFile.js @@ -26,8 +26,7 @@ import createFormatArgs from './utils/createFormatArgs.js'; * @typedef {import('../types/Config.d.ts').PlatformConfig} PlatformConfig * @typedef {import('../types/Config.d.ts').Config} Config * @typedef {import('../types/File.d.ts').File} File - * @typedef {import('../types/Filter.d.ts').Matcher} Matcher - * @typedef {import('../types/Format.d.ts').FormatterArguments} FormatterArguments + * @typedef {import('../types/Filter.d.ts').Filter} Filter */ /** @@ -47,7 +46,7 @@ export default async function buildFile(file, platform = {}, dictionary, options success: [], }; const { destination } = file || {}; - const filter = /** @type {Matcher|undefined} */ (file.filter); + const filter = /** @type {Filter['filter']|undefined} */ (file.filter); let { format } = file || {}; if (typeof format !== 'function') throw new Error('Please enter a valid file format'); diff --git a/lib/cleanFiles.js b/lib/cleanFiles.js index 435ee97c9..230de0c64 100644 --- a/lib/cleanFiles.js +++ b/lib/cleanFiles.js @@ -38,7 +38,7 @@ export default async function cleanFiles(platform, vol) { if (file.format) { return cleanFile(file, platform, vol); } else { - throw new Error('Please supply a template or formatter'); + throw new Error('Please supply a template or format'); } }), ); diff --git a/lib/common/filters.js b/lib/common/filters.js index fff8154c7..00aadbb02 100644 --- a/lib/common/filters.js +++ b/lib/common/filters.js @@ -12,14 +12,14 @@ */ /** - * @typedef {import('../../types/Filter.d.ts').Matcher} FilterMatcher + * @typedef {import('../../types/Filter.d.ts').Filter} Filter */ /** * @namespace Filters */ -/** @type {Record} */ +/** @type {Record} */ export default { /** * Remove a token from the ditribution output if it contains a key `private` set to true diff --git a/lib/common/formatHelpers/createPropertyFormatter.js b/lib/common/formatHelpers/createPropertyFormatter.js index 44dcba864..10fc39c85 100644 --- a/lib/common/formatHelpers/createPropertyFormatter.js +++ b/lib/common/formatHelpers/createPropertyFormatter.js @@ -97,7 +97,7 @@ function addComment(to_ret_token, comment, options) { * ```javascript * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { + * format: function({ dictionary, options }) { * const { outputReferences } = options; * const formatProperty = createPropertyFormatter({ * outputReferences, @@ -109,9 +109,9 @@ function addComment(to_ret_token, comment, options) { * }); * ``` * @param {Object} options - * @param {OutputReferences} [options.outputReferences] - Whether or not to output references. You will want to pass this from the `options` object sent to the formatter function. - * @param {boolean} [options.outputReferenceFallbacks] - Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the formatter function. - * @param {Dictionary} options.dictionary - The dictionary object sent to the formatter function + * @param {OutputReferences} [options.outputReferences] - Whether or not to output references. You will want to pass this from the `options` object sent to the format function. + * @param {boolean} [options.outputReferenceFallbacks] - Whether or not to output css variable fallback values when using output references. You will want to pass this from the `options` object sent to the format function. + * @param {Dictionary} options.dictionary - The dictionary object sent to the format function * @param {string} [options.format] - Available formats are: 'css', 'sass', 'less', and 'stylus'. If you want to customize the format and can't use one of those predefined formats, use the `formatting` option * @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a declaration line in code. The configurable strings are: `prefix`, `indentation`, `separator`, `suffix`, `lineSeparator`, `fileHeaderTimestamp`, `header`, `footer`, `commentStyle` and `commentPosition`. Those are used to generate a line like this: `${indentation}${prefix}${token.name}${separator} ${prop.value}${suffix}`. The remaining formatting options are used for the fileHeader helper. * @param {boolean} [options.themeable] [false] - Whether tokens should default to being themeable. diff --git a/lib/common/formatHelpers/fileHeader.js b/lib/common/formatHelpers/fileHeader.js index 48eb9dab6..a63da210b 100644 --- a/lib/common/formatHelpers/fileHeader.js +++ b/lib/common/formatHelpers/fileHeader.js @@ -35,7 +35,7 @@ const defaultFormatting = { * @memberof module:formatHelpers * @name fileHeader * @param {Object} options - * @param {File} [options.file] - The file object that is passed to the formatter. + * @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. * @returns {Promise} @@ -43,7 +43,7 @@ const defaultFormatting = { * ```js * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, file }) { + * format: function({ dictionary, file }) { * return fileHeader({file, commentStyle: 'short'}) + * dictionary.allTokens.map(token => `${token.name} = ${token.value}`) * .join('\n'); @@ -65,7 +65,7 @@ export default async function fileHeader({ file, commentStyle, formatting = {} } * @type {FileHeader} */ let fn = (arr) => arr; - if (typeof file?.options?.fileHeader === 'function') { + if (file?.options?.fileHeader && typeof file?.options?.fileHeader !== 'string') { fn = file.options.fileHeader; } diff --git a/lib/common/formatHelpers/formattedVariables.js b/lib/common/formatHelpers/formattedVariables.js index 42236d87c..95231e615 100644 --- a/lib/common/formatHelpers/formattedVariables.js +++ b/lib/common/formatHelpers/formattedVariables.js @@ -33,10 +33,10 @@ const defaultFormatting = { * @name formattedVariables * @param {Object} options * @param {String} options.format - What type of variables to output. Options are: css, sass, less, and stylus - * @param {Dictionary} options.dictionary - The dictionary object that gets passed to the formatter method. + * @param {Dictionary} options.dictionary - The dictionary object that gets passed to the format method. * @param {OutputReferences} [options.outputReferences] - Whether or not to output references * @param {Boolean} [options.outputReferenceFallbacks] - Whether or not to output a faLLback value for output references - * @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a declaration line in code. This will get passed to `formatHelpers` -> `createPropertyFormatter` and used for the `lineSeparator` between lines of code. + * @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a declaration line in code. This will get passed to `formatHelpers` -> `createPropertyformat` and used for the `lineSeparator` between lines of code. * @param {Boolean} [options.themeable] [false] - Whether tokens should default to being themeable. * @param {boolean} [options.usesDtcg] [false] - Whether DTCG token syntax should be uses. * @returns {String} @@ -44,7 +44,7 @@ const defaultFormatting = { * ```js * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { + * format: function({ dictionary, options }) { * return formattedVariables({ * format: 'less', * dictionary, diff --git a/lib/common/formatHelpers/getTypeScriptType.js b/lib/common/formatHelpers/getTypeScriptType.js index a63683403..969015369 100644 --- a/lib/common/formatHelpers/getTypeScriptType.js +++ b/lib/common/formatHelpers/getTypeScriptType.js @@ -26,7 +26,7 @@ * ```javascript * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { + * format: function({ dictionary, options }) { * return dictionary.allTokens.map(function(prop) { * var to_ret_prop = 'export const ' + prop.name + ' : ' + getTypeScriptType(prop.value) + ';'; * if (prop.comment) diff --git a/lib/common/formatHelpers/iconsWithPrefix.js b/lib/common/formatHelpers/iconsWithPrefix.js index ec393d018..47167b6f1 100644 --- a/lib/common/formatHelpers/iconsWithPrefix.js +++ b/lib/common/formatHelpers/iconsWithPrefix.js @@ -26,15 +26,15 @@ * @memberof module:formatHelpers * @name iconsWithPrefix * @param {String} prefix - Character to prefix variable names, like '$' for Sass - * @param {Token[]} allTokens - allTokens array on the dictionary object passed to the formatter function. - * @param {Options} options - options object passed to the formatter function. + * @param {Token[]} allTokens - allTokens array on the dictionary object passed to the format function. + * @param {Options} options - options object passed to the format function. * @param {PlatformConfig} platform - platform specific options * @returns {String} * @example * ```js * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { + * format: function({ dictionary, options }) { * return iconsWithPrefix('$', dictionary.allTokens, options); * } * }); diff --git a/lib/common/formatHelpers/minifyDictionary.js b/lib/common/formatHelpers/minifyDictionary.js index 88b2896f8..506bbf1f4 100644 --- a/lib/common/formatHelpers/minifyDictionary.js +++ b/lib/common/formatHelpers/minifyDictionary.js @@ -26,7 +26,7 @@ * ```js * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary }) { + * format: function({ dictionary }) { * return JSON.stringify(minifyDictionary(dictionary.tokens)); * } * }); diff --git a/lib/common/formatHelpers/setSwiftFileProperties.js b/lib/common/formatHelpers/setSwiftFileProperties.js index 530b23889..79766a033 100644 --- a/lib/common/formatHelpers/setSwiftFileProperties.js +++ b/lib/common/formatHelpers/setSwiftFileProperties.js @@ -22,7 +22,6 @@ * @param {{objectType?:string; import?: string[]; accessControl?: string;}} options - The options object declared at configuration * @param {String} [objectType] - The type of the object in the final file. Could be a class, enum, struct, etc. * @param {String} [transformGroup] - The transformGroup of the file, so it can be applied proper import - * @returns {Object} */ export default function setSwiftFileProperties(options, objectType, transformGroup) { if (options.objectType == null) { diff --git a/lib/common/formatHelpers/sortByName.js b/lib/common/formatHelpers/sortByName.js index db6d25329..084db370e 100644 --- a/lib/common/formatHelpers/sortByName.js +++ b/lib/common/formatHelpers/sortByName.js @@ -24,7 +24,7 @@ * ```javascript * StyleDictionary.registerFormat({ * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { + * format: function({ dictionary, options }) { * return dictionary.allTokens.sort(sortByName) * .map(token => `${token.name} = ${token.value}`) * .join('\n'); diff --git a/lib/common/formats.js b/lib/common/formats.js index b85185b69..0734654b1 100644 --- a/lib/common/formats.js +++ b/lib/common/formats.js @@ -48,8 +48,7 @@ import plistTemplate from './templates/ios/plist.template.js'; /** * @typedef {import('../../types/Format.d.ts').Format} Format - * @typedef {import('../../types/Format.d.ts').Formatter} Formatter - * @typedef {import('../../types/Format.d.ts').FormatterArguments} FormatArgs + * @typedef {import('../../types/Format.d.ts').FormatFnArguments} FormatArgs * @typedef {import('../../types/File').FormattingOptions} FormattingOptions * @typedef {import('../../types/Format.d.ts').OutputReferences} OutputReferences * @typedef {import('../../types/DesignToken.d.ts').TransformedToken} Token @@ -71,7 +70,7 @@ function getFormattingCloneWithoutPrefix(formatting) { } /** - * @type {Record} + * @type {Record} */ const formats = { /** @@ -117,7 +116,6 @@ const formats = { * * @memberof Formats * @kind member - * @param {FormatArgs} options * @example * ```scss * $tokens: ( @@ -399,7 +397,7 @@ const formats = { return ( header + 'var ' + - (file.name || '_styleDictionary') + + (file.options?.name || '_styleDictionary') + ' = ' + JSON.stringify(dictionary.tokens, null, 2) + ';\n' @@ -437,7 +435,7 @@ const formats = { * ``` */ 'javascript/umd': async function ({ dictionary, file, options }) { - const name = file.name || '_styleDictionary'; + const name = file.options?.name || '_styleDictionary'; const { formatting } = options; const header = await fileHeader({ file, @@ -623,7 +621,7 @@ const formats = { * const JsonToTS = require('json-to-ts'); * StyleDictionaryPackage.registerFormat({ * name: 'typescript/accurate-module-declarations', - * formatter: function({ dictionary }) { + * format: function({ dictionary }) { * return 'declare const root: RootObject\n' + * 'export default root\n' + * JsonToTS(dictionary.tokens).join('\n'); diff --git a/lib/common/templates/android/resources.template.js b/lib/common/templates/android/resources.template.js index a75971479..f65c2b365 100644 --- a/lib/common/templates/android/resources.template.js +++ b/lib/common/templates/android/resources.template.js @@ -33,9 +33,9 @@ import { usesReferences, getReferences } from 'style-dictionary/utils'; export default (opts) => { const { file, header, dictionary, options } = opts; - const resourceType = file?.resourceType || null; + const resourceType = file?.options?.resourceType || null; - const resourceMap = file?.resourceMap || { + const resourceMap = file?.options?.resourceMap || { dimension: 'dimen', fontSize: 'dimen', color: 'color', diff --git a/lib/common/transforms.js b/lib/common/transforms.js index bdb3c2df9..51ac18822 100644 --- a/lib/common/transforms.js +++ b/lib/common/transforms.js @@ -134,7 +134,7 @@ export default { */ 'attribute/cti': { type: 'attribute', - transformer: function (token) { + transform: function (token) { const attrNames = ['category', 'type', 'item', 'subitem', 'state']; const originalAttrs = token.attributes || {}; /** @type {Record} */ @@ -166,8 +166,8 @@ export default { */ 'attribute/color': { type: 'attribute', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const color = Color(options.usesDtcg ? token.$value : token.value); return { hex: color.toHex(), @@ -191,7 +191,7 @@ export default { */ 'name/human': { type: 'name', - transformer: function (token) { + transform: function (token) { return [token.attributes?.item, token.attributes?.subitem].join(' '); }, }, @@ -210,7 +210,7 @@ export default { */ 'name/camel': { type: 'name', - transformer: function (token, config) { + transform: function (token, config) { return camelCase([config.prefix].concat(token.path).join(' '), camelOpts); }, }, @@ -229,7 +229,7 @@ export default { */ 'name/kebab': { type: 'name', - transformer: function (token, config) { + transform: function (token, config) { return kebabCase([config.prefix].concat(token.path).join(' ')); }, }, @@ -248,7 +248,7 @@ export default { */ 'name/snake': { type: 'name', - transformer: function (token, config) { + transform: function (token, config) { return snakeCase([config.prefix].concat(token.path).join(' ')); }, }, @@ -267,7 +267,7 @@ export default { */ 'name/constant': { type: 'name', - transformer: function (token, config) { + transform: function (token, config) { return snakeCase([config.prefix].concat(token.path).join(' ')).toUpperCase(); }, }, @@ -286,7 +286,7 @@ export default { */ 'name/pascal': { type: 'name', - transformer: function (token, config) { + transform: function (token, config) { /** @param {string} str */ const upperFirst = function (str) { return str ? str[0].toUpperCase() + str.slice(1) : ''; @@ -308,8 +308,8 @@ export default { */ 'color/rgb': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { return Color(options.usesDtcg ? token.$value : token.value).toRgbString(); }, }, @@ -328,8 +328,8 @@ export default { */ 'color/hsl': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { return Color(options.usesDtcg ? token.$value : token.value).toHslString(); }, }, @@ -348,8 +348,8 @@ export default { */ 'color/hsl-4': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const color = Color(options.usesDtcg ? token.$value : token.value); const o = color.toHsl(); const vals = `${Math.round(o.h)} ${Math.round(o.s * 100)}% ${Math.round(o.l * 100)}%`; @@ -374,8 +374,8 @@ export default { */ 'color/hex': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { return Color(options.usesDtcg ? token.$value : token.value).toHexString(); }, }, @@ -393,8 +393,8 @@ export default { */ 'color/hex8': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { return Color(options.usesDtcg ? token.$value : token.value).toHex8String(); }, }, @@ -412,8 +412,8 @@ export default { */ 'color/hex8android': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const str = Color(options.usesDtcg ? token.$value : token.value).toHex8(); return '#' + str.slice(6) + str.slice(0, 6); }, @@ -432,8 +432,8 @@ export default { */ 'color/composeColor': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const str = Color(options.usesDtcg ? token.$value : token.value).toHex8(); return 'Color(0x' + str.slice(6) + str.slice(0, 6) + ')'; }, @@ -452,8 +452,8 @@ export default { */ 'color/UIColor': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const rgb = Color(options.usesDtcg ? token.$value : token.value).toRgb(); return ( '[UIColor colorWithRed:' + @@ -485,8 +485,8 @@ export default { */ 'color/UIColorSwift': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const { r, g, b, a } = Color(options.usesDtcg ? token.$value : token.value).toRgb(); const rFixed = (r / 255.0).toFixed(3); const gFixed = (g / 255.0).toFixed(3); @@ -508,8 +508,8 @@ export default { */ 'color/ColorSwiftUI': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const { r, g, b, a } = Color(options.usesDtcg ? token.$value : token.value).toRgb(); const rFixed = (r / 255.0).toFixed(3); const gFixed = (g / 255.0).toFixed(3); @@ -532,8 +532,8 @@ export default { */ 'color/css': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const color = Color(options.usesDtcg ? token.$value : token.value); if (color.getAlpha() === 1) { return color.toHexString(); @@ -563,8 +563,8 @@ export default { */ 'color/sketch': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { let color = Color(options.usesDtcg ? token.$value : token.value).toRgb(); return { red: (color.r / 255).toFixed(5), @@ -588,8 +588,8 @@ export default { */ 'size/sp': { type: 'value', - matcher: isFontSize, - transformer: function (token, _, options) { + filter: isFontSize, + transform: function (token, _, options) { const nonParsedVal = options.usesDtcg ? token.$value : token.value; const val = parseFloat(nonParsedVal); if (isNaN(val)) throwSizeError(token.name, nonParsedVal, 'sp'); @@ -610,8 +610,8 @@ export default { */ 'size/dp': { type: 'value', - matcher: isDimension, - transformer: function (token, _, options) { + filter: isDimension, + transform: function (token, _, options) { const nonParsedVal = options.usesDtcg ? token.$value : token.value; const val = parseFloat(nonParsedVal); if (isNaN(val)) throwSizeError(token.name, nonParsedVal, 'dp'); @@ -637,8 +637,8 @@ export default { */ 'size/object': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, config, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); if (isNaN(parsedVal)) throwSizeError(token.name, value, 'object'); @@ -665,8 +665,8 @@ export default { */ 'size/remToSp': { type: 'value', - matcher: isFontSize, - transformer: function (token, config, options) { + filter: isFontSize, + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -688,8 +688,8 @@ export default { */ 'size/remToDp': { type: 'value', - matcher: isDimension, - transformer: function (token, config, options) { + filter: isDimension, + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -711,8 +711,8 @@ export default { */ 'size/px': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, _, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, _, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); if (isNaN(parsedVal)) throwSizeError(token.name, value, 'px'); @@ -733,8 +733,8 @@ export default { */ 'size/rem': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, _, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, _, options) { const nonParsed = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(nonParsed); if (isNaN(parsedVal)) throwSizeError(token.name, nonParsed, 'rem'); @@ -755,8 +755,8 @@ export default { */ 'size/remToPt': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, config, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -778,8 +778,8 @@ export default { */ 'size/compose/remToSp': { type: 'value', - matcher: isFontSize, - transformer: function (token, config, options) { + filter: isFontSize, + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -801,8 +801,8 @@ export default { */ 'size/compose/remToDp': { type: 'value', - matcher: isDimension, - transformer: function (token, config, options) { + filter: isDimension, + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -824,8 +824,8 @@ export default { */ 'size/compose/em': { type: 'value', - matcher: isFontSize, - transformer: function (token, _, options) { + filter: isFontSize, + transform: function (token, _, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); if (isNaN(parsedVal)) throwSizeError(token.name, value, 'em'); @@ -845,8 +845,8 @@ export default { */ 'size/swift/remToCGFloat': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, config, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -868,8 +868,8 @@ export default { */ 'size/remToPx': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, config, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, config, options) { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -892,8 +892,8 @@ export default { */ 'size/pxToRem': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: (token, config, options) => { + filter: (token) => isDimension(token) || isFontSize(token), + transform: (token, config, options) => { const value = options.usesDtcg ? token.$value : token.value; const parsedVal = parseFloat(value); const baseFont = getBasePxFontSize(config); @@ -923,10 +923,10 @@ export default { */ 'html/icon': { type: 'value', - matcher: function (token) { + filter: function (token) { return token.type === 'html'; }, - transformer: function (token, _, options) { + transform: function (token, _, options) { return (options.usesDtcg ? token.$value : token.value).replace( UNICODE_PATTERN, /** @@ -952,8 +952,8 @@ export default { */ 'content/quote': { type: 'value', - matcher: isContent, - transformer: function (token, _, options) { + filter: isContent, + transform: function (token, _, options) { return wrapValueWith("'", token, options); }, }, @@ -971,8 +971,8 @@ export default { */ 'content/objC/literal': { type: 'value', - matcher: isContent, - transformer: function (token, _, options) { + filter: isContent, + transform: function (token, _, options) { return '@' + wrapValueWithDoubleQuote(token, options); }, }, @@ -990,8 +990,8 @@ export default { */ 'content/swift/literal': { type: 'value', - matcher: isContent, - transformer: (token, _, options) => wrapValueWithDoubleQuote(token, options), + filter: isContent, + transform: (token, _, options) => wrapValueWithDoubleQuote(token, options), }, /** @@ -1007,10 +1007,10 @@ export default { */ 'time/seconds': { type: 'value', - matcher: function (token) { + filter: function (token) { return token.type === 'time'; }, - transformer: function (token, _, options) { + transform: function (token, _, options) { return (parseFloat(options.usesDtcg ? token.$value : token.value) / 1000).toFixed(2) + 's'; }, }, @@ -1028,8 +1028,8 @@ export default { */ 'asset/url': { type: 'value', - matcher: isAsset, - transformer: function (token, _, options) { + filter: isAsset, + transform: function (token, _, options) { return `url("${(options.usesDtcg ? token.$value : token.value).replace(/"/g, `\\"`)}")`; }, }, @@ -1047,8 +1047,8 @@ export default { */ 'asset/base64': { type: 'value', - matcher: isAsset, - transformer: function (token, _, options, vol) { + filter: isAsset, + transform: function (token, _, options, vol) { return convertToBase64(options.usesDtcg ? token.$value : token.value, vol); }, }, @@ -1066,8 +1066,8 @@ export default { */ 'asset/path': { type: 'value', - matcher: isAsset, - transformer: function (token, _, options) { + filter: isAsset, + transform: function (token, _, options) { return join(process?.cwd() ?? '/', options.usesDtcg ? token.$value : token.value); }, }, @@ -1084,8 +1084,8 @@ export default { */ 'asset/objC/literal': { type: 'value', - matcher: isAsset, - transformer: function (token, _, options) { + filter: isAsset, + transform: function (token, _, options) { return '@' + wrapValueWithDoubleQuote(token, options); }, }, @@ -1102,8 +1102,8 @@ export default { */ 'asset/swift/literal': { type: 'value', - matcher: isAsset, - transformer: (token, _, options) => wrapValueWithDoubleQuote(token, options), + filter: isAsset, + transform: (token, _, options) => wrapValueWithDoubleQuote(token, options), }, /** @@ -1118,8 +1118,8 @@ export default { */ 'color/hex8flutter': { type: 'value', - matcher: isColor, - transformer: function (token, _, options) { + filter: isColor, + transform: function (token, _, options) { const str = Color(options.usesDtcg ? token.$value : token.value) .toHex8() .toUpperCase(); @@ -1139,8 +1139,8 @@ export default { */ 'content/flutter/literal': { type: 'value', - matcher: (token) => isContent(token), - transformer: (token, _, options) => wrapValueWithDoubleQuote(token, options), + filter: (token) => isContent(token), + transform: (token, _, options) => wrapValueWithDoubleQuote(token, options), }, /** @@ -1155,8 +1155,8 @@ export default { */ 'asset/flutter/literal': { type: 'value', - matcher: isAsset, - transformer: (token, _, options) => wrapValueWithDoubleQuote(token, options), + filter: isAsset, + transform: (token, _, options) => wrapValueWithDoubleQuote(token, options), }, /** @@ -1171,8 +1171,8 @@ export default { */ 'size/flutter/remToDouble': { type: 'value', - matcher: (token) => isDimension(token) || isFontSize(token), - transformer: function (token, config, options) { + filter: (token) => isDimension(token) || isFontSize(token), + transform: function (token, config, options) { const baseFont = getBasePxFontSize(config); return (parseFloat(options.usesDtcg ? token.$value : token.value) * baseFont).toFixed(2); }, diff --git a/lib/filterTokens.js b/lib/filterTokens.js index 242dac598..73fc608aa 100644 --- a/lib/filterTokens.js +++ b/lib/filterTokens.js @@ -16,13 +16,13 @@ import isPlainObject from 'is-plain-obj'; * @typedef {import('../types/DesignToken.d.ts').Dictionary} Dictionary * @typedef {import('../types/DesignToken.d.ts').TransformedTokens} Tokens * @typedef {import('../types/DesignToken.d.ts').TransformedToken} Token - * @typedef {import('../types/Filter.d.ts').Matcher} Matcher + * @typedef {import('../types/Filter.d.ts').Filter} Filter * @typedef {import('../types/Config.d.ts').Config} Config */ /** * @param {Token[]} arr - * @param {Matcher} predicate + * @param {Filter['filter']} predicate */ async function asyncFilter(arr, predicate) { return Promise.all(arr.map(predicate)).then((results) => @@ -35,7 +35,7 @@ async function asyncFilter(arr, predicate) { * function. * * @param {Tokens} tokens - * @param {Matcher} filter - A function that receives a property object and + * @param {Filter['filter']} filter - A function that receives a property object and * returns `true` if the property should be included in the output or `false` * if the property should be excluded from the output. * @param {Config} options @@ -76,7 +76,7 @@ async function filterTokenObject(tokens, filter, options) { * object using a function provided by the user. * * @param {Dictionary} dictionary - * @param {Matcher} [filter] - A function that receives a token object + * @param {Filter['filter']} [filter] - A function that receives a token object * and returns `true` if the token should be included in the output * or `false` if the token should be excluded from the output * @param {Config} [options] diff --git a/lib/transform/config.js b/lib/transform/config.js index d0e9512cd..40f933e91 100644 --- a/lib/transform/config.js +++ b/lib/transform/config.js @@ -21,7 +21,7 @@ import chalk from 'chalk'; * @typedef {import('../StyleDictionary.js').default} StyleDictionary * @typedef {import('../../types/Transform.d.ts').Transform} Transform * @typedef {import('../../types/File.d.ts').File} File - * @typedef {import('../../types/Filter.d.ts').Matcher} Matcher + * @typedef {import('../../types/Action.d.ts').Action} Action * @typedef {import('../../types/Config.d.ts').PlatformConfig} PlatformConfig */ @@ -49,8 +49,8 @@ export default function transformConfig(platformConfig, dictionary, platformName /** @type {string[]} */ let transforms = []; if (to_ret.transformGroup) { - if (dictionary.transformGroup[to_ret.transformGroup]) { - transforms = dictionary.transformGroup[to_ret.transformGroup]; + if (dictionary.hooks.transformGroups?.[to_ret.transformGroup]) { + transforms = dictionary.hooks.transformGroups[to_ret.transformGroup]; } else { let err = ` Unknown transformGroup "${to_ret.transformGroup}" found in platform "${platformName}": @@ -69,10 +69,10 @@ Unknown transformGroup "${to_ret.transformGroup}" found in platform "${platformN // the StyleDictionary module. We need to map the strings to // the actual functions. to_ret.transforms = transforms.map(function (name) { - if (!dictionary.transform[name]) { + if (!dictionary.hooks.transforms?.[name]) { GroupMessages.add(MISSING_TRANSFORM_ERRORS, `"${name}"`); } - return dictionary.transform[name]; + return dictionary.hooks.transforms[name]; }); let missingTransformCount = GroupMessages.count(MISSING_TRANSFORM_ERRORS); @@ -99,8 +99,8 @@ None of ${transform_warnings} match the name of a registered transform. if (to_ret.options?.fileHeader) { const fileHeader = to_ret.options.fileHeader; if (typeof fileHeader === 'string') { - if (dictionary.fileHeader[fileHeader]) { - to_ret.options.fileHeader = dictionary.fileHeader[fileHeader]; + if (dictionary.hooks.fileHeaders?.[fileHeader]) { + to_ret.options.fileHeader = dictionary.hooks.fileHeaders[fileHeader]; } else { throw new Error(`Can't find fileHeader: ${fileHeader}`); } @@ -116,8 +116,8 @@ None of ${transform_warnings} match the name of a registered transform. if (file.options && file.options.fileHeader && ext.options) { const fileHeader = file.options.fileHeader; if (typeof fileHeader === 'string') { - if (dictionary.fileHeader[fileHeader]) { - ext.options.fileHeader = dictionary.fileHeader[fileHeader]; + if (dictionary.hooks.fileHeaders?.[fileHeader]) { + ext.options.fileHeader = dictionary.hooks.fileHeaders[fileHeader]; } else { throw new Error(`Can't find fileHeader: ${fileHeader}`); } @@ -130,8 +130,8 @@ None of ${transform_warnings} match the name of a registered transform. if (file.filter) { if (typeof file.filter === 'string') { - if (dictionary.filter[file.filter]) { - ext.filter = dictionary.filter[file.filter]; + if (dictionary.hooks.filters?.[file.filter]) { + ext.filter = dictionary.hooks.filters[file.filter]; } else { throw new Error("Can't find filter: " + file.filter); } @@ -172,11 +172,11 @@ None of ${transform_warnings} match the name of a registered transform. if (file.format) { /** * We know at this point it should be a string - * Only later will it be transformed to contain the formatter function + * Only later will it be transformed to contain the format function */ const format = /** @type {string} */ (file.format); - if (dictionary.format[format]) { - ext.format = dictionary.format[format]; + if (dictionary.hooks.formats[format]) { + ext.format = dictionary.hooks.formats[format]; } else { throw new Error("Can't find format: " + format); } @@ -193,7 +193,7 @@ None of ${transform_warnings} match the name of a registered transform. const actions = /** @type {string[]|undefined} */ (to_ret.actions) || []; to_ret.actions = actions.map( /** @param {string} action */ function (action) { - if (typeof dictionary.action[action].undo !== 'function') { + if (typeof dictionary.hooks.actions?.[action].undo !== 'function') { const message = `${action} action does not have a clean function!`; if (to_ret.log?.warnings === 'error') { throw new Error(message); @@ -202,7 +202,8 @@ None of ${transform_warnings} match the name of a registered transform. console.log(chalk.rgb(255, 140, 0).bold(message)); } } - return dictionary.action[action]; + // TODO: we assume it exists, but perhaps we should check and throw error if action cannot be found + return /** @type {Omit} */ (dictionary.hooks.actions?.[action]); }, ); diff --git a/lib/transform/token.js b/lib/transform/token.js index f091db386..fedab0442 100644 --- a/lib/transform/token.js +++ b/lib/transform/token.js @@ -40,9 +40,9 @@ export default async function transformToken(token, config, options, vol) { for (let i = 0; i < transforms.length; i++) { const transform = transforms[i]; - if (!transform.matcher || transform.matcher(to_ret)) { + if (!transform.filter || transform.filter(to_ret)) { if (transform.type === 'name') { - to_ret.name = await /** @type {Omit} */ (transform).transformer( + to_ret.name = await /** @type {Omit} */ (transform).transform( to_ret, config, options, @@ -64,7 +64,7 @@ export default async function transformToken(token, config, options, vol) { ) || transform.transitive ) { - const transformedValue = await transform.transformer(to_ret, config, options, vol); + const transformedValue = await transform.transform(to_ret, config, options, vol); if (transformedValue === undefined) { return undefined; } @@ -76,7 +76,7 @@ export default async function transformToken(token, config, options, vol) { to_ret.attributes = Object.assign( {}, to_ret.attributes, - await transform.transformer(to_ret, config, options, vol), + await transform.transform(to_ret, config, options, vol), ); } } diff --git a/lib/utils/combineJSON.js b/lib/utils/combineJSON.js index 2fdbcafa0..f98888617 100644 --- a/lib/utils/combineJSON.js +++ b/lib/utils/combineJSON.js @@ -47,7 +47,7 @@ function traverseObj(obj, fn) { * @param {Boolean} [deep=false] - If it should perform a deep merge * @param {Function} [collision] - A function to be called when a name collision happens that isn't a normal deep merge of objects * @param {boolean} [source] - If json files are "sources", tag tokens - * @param {Parser[]} [parsers] - Custom file parsers + * @param {Record>} [parsers] - Custom file parsers * @param {boolean} [usesDtcg] - Whether or not tokens are using DTCG syntax. * @param {Volume} [vol] - Filesystem volume to use * @returns {Promise<{tokens: Tokens, usesDtcg: boolean|undefined }>} @@ -57,7 +57,7 @@ export default async function combineJSON( deep = false, collision, source = true, - parsers = [], + parsers = {}, usesDtcg, vol, ) { @@ -83,9 +83,9 @@ export default async function combineJSON( const resolvedPath = resolve(filePath, vol?.__custom_fs__); let file_content = null; try { - for (const { pattern, parse } of parsers) { + for (const { pattern, parser } of Object.values(parsers)) { if (filePath.match(pattern)) { - file_content = await parse({ + file_content = await parser({ contents: /** @type {string} */ (volume.readFileSync(resolvedPath, 'utf-8')), filePath: resolvedPath, }); diff --git a/lib/utils/createFormatArgs.js b/lib/utils/createFormatArgs.js index dceaf36a3..e68b6dff0 100644 --- a/lib/utils/createFormatArgs.js +++ b/lib/utils/createFormatArgs.js @@ -18,18 +18,11 @@ import deepExtend from './deepExtend.js'; * @typedef {import('../../types/Config.d.ts').PlatformConfig} PlatformConfig * @typedef {import('../../types/Config.d.ts').Config} Options * @typedef {import('../../types/File.d.ts').File} File - * @typedef {import('../../types/Format.d.ts').FormatterArguments} FormatterArguments + * @typedef {import('../../types/Format.d.ts').FormatFnArguments} FormatFnArguments * /** - * - * @param {{ - * dictionary: Dictionary; - * platform: PlatformConfig; - * options: Options; - * file: File; - * }} param0 - * @returns {FormatterArguments} + * @param {FormatFnArguments} param0 */ export default function createFormatArgs({ dictionary, platform, options, file }) { const { allTokens, tokens } = dictionary; @@ -42,7 +35,7 @@ export default function createFormatArgs({ dictionary, platform, options, file } // always has the destination prop, then result will be File rather than Partial, so we just typecast it. file = /** @type {File} */ (deepExtend([{}, fileOptsTakenFromPlatform, file])); - return /** @type {FormatterArguments} */ ({ + return /** @type {FormatFnArguments & Dictionary} */ ({ dictionary, allTokens, tokens, diff --git a/lib/utils/preprocess.js b/lib/utils/preprocess.js index 2584a3970..1a17b5345 100644 --- a/lib/utils/preprocess.js +++ b/lib/utils/preprocess.js @@ -22,7 +22,7 @@ * * @param {DesignTokens} tokens * @param {string[]} [appliedPreprocessors] - * @param {Record>} [preprocessorObj] + * @param {Record} [preprocessorObj] * @returns {Promise} */ export async function preprocess(tokens, appliedPreprocessors = [], preprocessorObj = {}) { @@ -30,9 +30,9 @@ export async function preprocess(tokens, appliedPreprocessors = [], preprocessor const preprocessors = Object.entries(preprocessorObj); if (preprocessors.length > 0) { - for (const [key, pre] of preprocessors) { + for (const [key, preprocessor] of preprocessors) { if (appliedPreprocessors.includes(key)) { - processedTokens = await pre.preprocessor(processedTokens); + processedTokens = await preprocessor(processedTokens); } } } diff --git a/package-lock.json b/package-lock.json index b3cfee1e7..c42a36f28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "style-dictionary", - "version": "4.0.0-prerelease.25", + "version": "4.0.0-prerelease.26", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "style-dictionary", - "version": "4.0.0-prerelease.25", + "version": "4.0.0-prerelease.26", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 68b240df8..31c72987d 100644 --- a/package.json +++ b/package.json @@ -54,11 +54,10 @@ "lint:eslint": "eslint \"**/*.js\"", "lint:prettier": "prettier \"**/*.{js,md}\" \"package.json\" --list-different || (echo '↑↑ these files are not prettier formatted ↑↑' && exit 1)", "lint:types": "tsc --noEmit", - "test": "npm run test:browser && npm run test:node", - "test:browser": "web-test-runner --coverage", - "test:browser:coverage": "cd coverage/lcov-report && npx http-server -o -c-1", - "test:browser:watch": "web-test-runner --watch", - "test:browser:update-snapshots": "web-test-runner --update-snapshots", + "test": "web-test-runner --coverage", + "test:watch": "web-test-runner --watch", + "test:coverage": "cd coverage/lcov-report && npx http-server -o -c-1", + "test:update-snapshots": "web-test-runner --update-snapshots", "test:node": "mocha -r mocha-hooks.mjs './__integration__/**/*.test.js' './__tests__/**/*.test.js' './__node_tests__/**/*.test.js'", "install-cli": "npm install -g $(npm pack)", "release": "npm run build && changeset publish", diff --git a/types/Config.d.ts b/types/Config.d.ts index 6cd23a3dd..367190e1e 100644 --- a/types/Config.d.ts +++ b/types/Config.d.ts @@ -12,14 +12,24 @@ */ import type { DesignToken, DesignTokens, TransformedToken } from './DesignToken.d.ts'; -import type { Filter, Matcher } from './Filter.d.ts'; +import type { Filter } from './Filter.d.ts'; import type { FileHeader, File, FormattingOptions } from './File.d.ts'; import type { Parser } from './Parser.d.ts'; import type { Preprocessor } from './Preprocessor.d.ts'; import type { Transform } from './Transform.d.ts'; -import type { Formatter, OutputReferences } from './Format.d.ts'; +import type { Format, OutputReferences } from './Format.d.ts'; import type { Action } from './Action.d.ts'; -import type { Hooks } from '../lib/Register.js'; + +export interface Hooks { + parsers?: Record>; + preprocessors?: Record; + transformGroups?: Record; + transforms?: Record>; + formats?: Record; + fileHeaders?: Record; + filters?: Record; + actions?: Record>; +} export interface LocalOptions { showFileHeader?: boolean; @@ -74,10 +84,6 @@ export interface Expand { export type ExpandConfig = Expand | boolean | ExpandFilter; -export interface Hooks { - preprocessors?: Record>; -} - export interface PlatformConfig extends RegexOptions { log?: LogConfig; transformGroup?: string; @@ -102,11 +108,5 @@ export interface Config { platforms?: Record; parsers?: Parser[]; preprocessors?: string[]; - transform?: Record; - transformGroup?: Record; - format?: Record; - filter?: Record; - fileHeader?: Record; - action?: Record; usesDtcg?: boolean; } diff --git a/types/File.d.ts b/types/File.d.ts index 879158b78..d974cdbc5 100644 --- a/types/File.d.ts +++ b/types/File.d.ts @@ -1,7 +1,7 @@ import type { TransformedToken } from './DesignToken.d.ts'; -import type { Formatter } from './Format.d.ts'; +import type { FormatFn } from './Format.d.ts'; import type { LocalOptions } from './Config.d.ts'; -import type { Matcher } from './Filter.d.ts'; +import type { Filter } from './Filter.d.ts'; export interface FormattingOptions { prefix?: string; @@ -20,10 +20,7 @@ export type FileHeader = (defaultMessage: string[]) => Promise | strin export interface File { destination: string; - format?: string | Formatter; - filter?: string | Partial | Matcher; + format?: string | FormatFn; + filter?: string | Partial | Filter['filter']; options?: LocalOptions; - resourceType?: string; - resourceMap?: Record; - name?: string; } diff --git a/types/Filter.d.ts b/types/Filter.d.ts index 42c85f443..9d5cac918 100644 --- a/types/Filter.d.ts +++ b/types/Filter.d.ts @@ -14,7 +14,5 @@ import type { TransformedToken } from './DesignToken.d.ts'; export interface Filter { name: string; - matcher: Matcher; + filter: (token: TransformedToken) => boolean | Promise; } - -export type Matcher = (token: TransformedToken) => boolean | Promise; diff --git a/types/Format.d.ts b/types/Format.d.ts index 44f7abd92..56ff447b7 100644 --- a/types/Format.d.ts +++ b/types/Format.d.ts @@ -15,7 +15,7 @@ import type { Dictionary, TransformedToken } from './DesignToken.d.ts'; import type { File } from './File.d.ts'; import type { LocalOptions, Config, PlatformConfig } from './Config.d.ts'; -export interface FormatterArguments { +export interface FormatFnArguments { /** * The transformed and resolved dictionary object */ @@ -35,16 +35,16 @@ export interface FormatterArguments { } /** - * The formatter function receives an overloaded object as its arguments and + * The format function receives an overloaded object as its arguments and * it should return a string, which will be written to a file. */ -export type Formatter = ((arguments: FormatterArguments) => string | Promise) & { +export type FormatFn = ((arguments: FormatFnArguments) => string | Promise) & { nested?: boolean; }; export interface Format { name: string; - formatter: Formatter; + format: FormatFn; } export type OutputReferences = diff --git a/types/Parser.d.ts b/types/Parser.d.ts index 2070b7e9b..4bc8d9416 100644 --- a/types/Parser.d.ts +++ b/types/Parser.d.ts @@ -19,6 +19,7 @@ export interface ParserOptions { } export interface Parser { + name: string; pattern: RegExp; - parse: (options: ParserOptions) => DesignTokens | Promise; + parser: (options: ParserOptions) => DesignTokens | Promise; } diff --git a/types/Preprocessor.d.ts b/types/Preprocessor.d.ts index f4d4ecbe3..9d2277f13 100644 --- a/types/Preprocessor.d.ts +++ b/types/Preprocessor.d.ts @@ -15,7 +15,5 @@ import type { DesignTokens } from './DesignToken.d.ts'; export type Preprocessor = { name: string; - preprocessor: preprocessor; + preprocessor: (dictionary: DesignTokens) => DesignTokens | Promise; }; - -export type preprocessor = (dictionary: DesignTokens) => DesignTokens | Promise; diff --git a/types/Transform.d.ts b/types/Transform.d.ts index 7e895f7c3..17acebdc7 100644 --- a/types/Transform.d.ts +++ b/types/Transform.d.ts @@ -11,7 +11,7 @@ * and limitations under the License. */ -import type { Matcher } from './Filter.d.ts'; +import type { Filter } from './Filter.d.ts'; import type { TransformedToken } from './DesignToken.d.ts'; import type { PlatformConfig, Config } from './Config.d.ts'; import type { Volume } from './Volume.d.ts'; @@ -19,9 +19,9 @@ import type { Volume } from './Volume.d.ts'; interface BaseTransform { name: string; type: Type; - matcher?: Matcher; + filter?: Filter['filter']; transitive?: boolean; - transformer: ( + transform: ( token: TransformedToken, config: PlatformConfig, options: Config, diff --git a/types/index.d.ts b/types/index.d.ts index 8c3872a75..9db8298dc 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -12,9 +12,9 @@ export type { export type { FileHeader, File, FormattingOptions } from './File.d.ts'; -export type { Filter, Matcher } from './Filter.d.ts'; +export type { Filter } from './Filter.d.ts'; -export type { Format, FormatterArguments, Formatter, OutputReferences } from './Format.d.ts'; +export type { Format, FormatFnArguments, FormatFn, OutputReferences } from './Format.d.ts'; export type { Parser, ParserOptions } from './Parser.d.ts';