From f9fadd50b38ddf90c3ca754eab610e8865b8b4f6 Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Tue, 9 May 2023 21:48:21 +0200 Subject: [PATCH 1/5] Add 'output-styles-as-style-dictionary' package --- .../.npmignore | 3 + .../README.md | 66 +++ .../package.json | 34 ++ .../src/index.test.ts | 376 ++++++++++++++++++ .../src/index.ts | 88 ++++ .../src/types.ts | 1 + .../src/utils.test.ts | 91 +++++ .../src/utils.ts | 46 +++ .../tsconfig.json | 11 + yarn.lock | 11 +- 10 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 packages/output-styles-as-style-dictionary/.npmignore create mode 100644 packages/output-styles-as-style-dictionary/README.md create mode 100644 packages/output-styles-as-style-dictionary/package.json create mode 100644 packages/output-styles-as-style-dictionary/src/index.test.ts create mode 100644 packages/output-styles-as-style-dictionary/src/index.ts create mode 100644 packages/output-styles-as-style-dictionary/src/types.ts create mode 100644 packages/output-styles-as-style-dictionary/src/utils.test.ts create mode 100644 packages/output-styles-as-style-dictionary/src/utils.ts create mode 100644 packages/output-styles-as-style-dictionary/tsconfig.json diff --git a/packages/output-styles-as-style-dictionary/.npmignore b/packages/output-styles-as-style-dictionary/.npmignore new file mode 100644 index 00000000..97f4f5a1 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/.npmignore @@ -0,0 +1,3 @@ +src +tsconfig.json +tsconfig.tsbuildinfo diff --git a/packages/output-styles-as-style-dictionary/README.md b/packages/output-styles-as-style-dictionary/README.md new file mode 100644 index 00000000..cfa44e50 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/README.md @@ -0,0 +1,66 @@ +# @figma-export/output-styles-as-style-dictionary + +> Styles Outputter for [@figma-export](https://github.com/marcomontalbano/figma-export) that exports styles to [Style Dictionary](https://amzn.github.io/style-dictionary/#/) JSON format. + +With this outputter you can export all the styles as variables inside a `.json`. + +This is a sample of the output: + +```sh +$ tree output/ +# output/ +# └── base.json +``` + + +## .figmaexportrc.js + +You can easily add this outputter to your `.figmaexportrc.js`: + +```js +module.exports = { + commands: [ + ['styles', { + fileId: 'fzYhvQpqwhZDUImRz431Qo', + outputters: [ + require('@figma-export/output-styles-as-style-dictionary')({ + output: './output' + }) + ] + }], + ] +} +``` + +`output` is **mandatory**. + +`getExtension`, `getFilename` and `getVariableName` are **optional**. + +```js +const { kebabCase } = require('@figma-export/utils'); + +... + +require('@figma-export/output-styles-as-style-dictionary')({ + output: './output', + getExtension: () => 'JSON', + getFilename: () => 'base', + getVariableName: (style) => kebabCase(style.name).toLowerCase(), +}) +``` + +> *defaults may change, please refer to `./src/index.ts`* + +## Install + +Using npm: + +```sh +npm install --save-dev @figma-export/output-styles-as-style-dictionary +``` + +or using yarn: + +```sh +yarn add @figma-export/output-styles-as-style-dictionary --dev +``` diff --git a/packages/output-styles-as-style-dictionary/package.json b/packages/output-styles-as-style-dictionary/package.json new file mode 100644 index 00000000..100535d8 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/package.json @@ -0,0 +1,34 @@ +{ + "name": "@figma-export/output-styles-as-style-dictionary", + "version": "4.5.0", + "description": "Outputter for @figma-export that exports styles to style-dictionary tokens", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/marcomontalbano/figma-exporter.git", + "directory": "packages/output-styles-as-style-dictionary" + }, + "author": "Marco Montalbano", + "keywords": [ + "figma", + "export", + "design", + "icons", + "typography", + "components", + "style-dictionary", + "design tokens" + ], + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@figma-export/types": "^4.5.0", + "@figma-export/utils": "^4.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } +} diff --git a/packages/output-styles-as-style-dictionary/src/index.test.ts b/packages/output-styles-as-style-dictionary/src/index.test.ts new file mode 100644 index 00000000..95939949 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/src/index.test.ts @@ -0,0 +1,376 @@ +import sinon from 'sinon'; +import { expect } from 'chai'; +import { + StyleNode, + FillStyle, + EffectStyle, + Style, +} from '@figma-export/types'; +import { camelCase } from '@figma-export/utils'; + +import fs from 'fs'; +import outputter from './index'; + +const mockFill = (fills: FillStyle[], { visible = true, name = 'variable/name', comment = 'lorem ipsum' } = {}): Style => ({ + fills, + visible, + name, + comment, + styleType: 'FILL', + originalNode: { ...({} as StyleNode) }, +}); + +const mockSolid = (value: string, visible = true): FillStyle => ({ + value, + visible, + type: 'SOLID', + color: { + a: 1, r: 255, g: 255, b: 255, rgba: 'rgba(255, 255, 255, 1)', + }, +}); + +const mockGradientLinear = (value: string, visible = true): FillStyle => ({ + value, + visible, + type: 'GRADIENT_LINEAR', + gradientStops: [], + angle: '100deg', +}); + +const mockEffect = (effects: EffectStyle[] = [], visible = true): Style => ({ + effects, + visible, + name: 'variable-name', + comment: '', + styleType: 'EFFECT', + originalNode: { ...({} as StyleNode) }, +}); + +const mockShadow = (value: string, visible = true): EffectStyle => ({ + value, + visible, + type: 'INNER_SHADOW', + color: { + a: 1, r: 255, g: 255, b: 255, rgba: 'rgba(255, 255, 255, 1)', + }, + inset: false, + blurRadius: 10, + spreadRadius: 10, + offset: { x: 10, y: 10 }, +}); + +const mockBlur = (value: string, visible = true): EffectStyle => ({ + value, + visible, + type: 'LAYER_BLUR', + blurRadius: 10, +}); + +const mockText = (visible = true): Style => ({ + style: { + fontFamily: 'Roboto Condensed', + fontSize: 12, + fontStyle: 'italic', + fontVariant: 'normal', + fontWeight: 100, + letterSpacing: 10, + lineHeight: 12, + textAlign: 'left', + textDecoration: 'none', + textTransform: 'uppercase', + verticalAlign: 'middle', + }, + visible, + name: 'variable-name', + comment: '', + styleType: 'TEXT', + originalNode: { ...({} as StyleNode) }, +}); + +describe('style output as style-dictionary json', () => { + let writeFileSync; + + beforeEach(() => { + sinon.stub(fs, 'mkdirSync').returnsArg(0); + writeFileSync = sinon.stub(fs, 'writeFileSync'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should not print anything if style is not visible', async () => { + await outputter({ + output: 'output-folder', + })([ + mockFill([ + mockSolid('solid-1'), + ], { visible: false }), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch('/output-folder/base.json', ''); + }); + + it('should be able to change the filename, the extension and output folder', async () => { + await outputter({ + output: 'output-folder', + getExtension: () => 'JSON', + getFilename: () => 'base-file', + })([]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch('/output-folder/base-file.json'); + }); + + it('should sanitize variable names', async () => { + await outputter({ + output: 'output-folder', + })([ + mockFill([ + mockSolid('rgba(26, 26, 26, 1)', true), + ], { name: 'Grey/900' }), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + grey: { + 900: { + comment: 'lorem ipsum', + value: 'rgba(26, 26, 26, 1)', + }, + }, + }, undefined, 2), + ); + }); + + it('should be able to change the way a variable name is defined', async () => { + await outputter({ + output: 'output-folder', + getVariableName: (style) => camelCase(style.name), + })([ + mockFill([ + mockSolid('rgba(26, 26, 26, 1)', true), + ], { name: 'Grey/Dark' }), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + greyDark: { + comment: 'lorem ipsum', + value: 'rgba(26, 26, 26, 1)', + }, + }, undefined, 2), + ); + }); + + describe('colors', () => { + it('should not print anything if fill is not visible', async () => { + await outputter({ + output: 'output-folder', + })([ + mockFill([ + mockSolid('solid-1', false), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch('/output-folder/base.json', ''); + }); + + it('should be able to extract a solid color', async () => { + await outputter({ + output: 'output-folder', + })([ + mockFill([ + mockSolid('rgba(solid-1)', true), + mockSolid('rgba(solid-2)', false), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + comment: 'lorem ipsum', + value: 'rgba(solid-1)', + }, + }, + }, undefined, 2), + ); + }); + + it('should be able to extract a linear gradient', async () => { + await outputter({ + output: 'output-folder', + })([ + mockFill([ + mockGradientLinear('linear-gradient-1'), + mockGradientLinear('linear-gradient-2'), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + comment: 'lorem ipsum', + value: 'linear-gradient-1, linear-gradient-2', + }, + }, + }, undefined, 2), + ); + }); + }); + + describe('effects', () => { + it('should be able to extract a box-shadow', async () => { + await outputter({ + output: 'output-folder', + })([ + mockEffect([ + mockShadow('shadow-effect-1'), + mockShadow('shadow-effect-2'), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + value: 'shadow-effect-1, shadow-effect-2', + }, + }, + }, undefined, 2), + ); + }); + + it('should be able to extract a filter: blur()', async () => { + await outputter({ + output: 'output-folder', + })([ + mockEffect([ + mockBlur('blur-effect'), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWithMatch( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + value: 'blur-effect', + }, + }, + }, undefined, 2), + ); + }); + + it('should not combine shadow and blur effects', async () => { + await outputter({ + output: 'output-folder', + })([ + mockEffect([ + mockShadow('shadow-effect-1'), + mockBlur('blur-effect-1'), + mockShadow('shadow-effect-2'), + ]), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWith( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + value: 'shadow-effect-1, shadow-effect-2', + }, + }, + }, undefined, 2), + ); + }); + }); + + describe('texts', () => { + it('should be able to extract a text', async () => { + await outputter({ + output: 'output-folder', + })([ + mockText(), + ]); + + expect(writeFileSync).to.be.calledOnce; + expect(writeFileSync).to.be.calledWith( + sinon.match.any, + + JSON.stringify({ + variable: { + name: { + font: { + family: { + value: '"Roboto Condensed"', + }, + size: { + value: '12px', + }, + style: { + value: 'italic', + }, + variant: { + value: 'normal', + }, + weight: { + value: '100', + }, + }, + letter: { + spacing: { + value: '10px', + }, + }, + line: { + height: { + value: '12px', + }, + }, + text: { + align: { + value: 'left', + }, + decoration: { + value: 'none', + }, + transform: { + value: 'uppercase', + }, + }, + vertical: { + align: { + value: 'middle', + }, + }, + }, + }, + }, undefined, 2), + ); + }); + }); +}); diff --git a/packages/output-styles-as-style-dictionary/src/index.ts b/packages/output-styles-as-style-dictionary/src/index.ts new file mode 100644 index 00000000..6ab0c0d8 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/src/index.ts @@ -0,0 +1,88 @@ +import * as FigmaExport from '@figma-export/types'; +import { kebabCase } from '@figma-export/utils'; + +import { writeVariable } from './utils'; +import { Extension } from './types'; + +import fs = require('fs'); +import path = require('path'); + +type Options = { + output: string; + getExtension?: () => Extension; + getFilename?: () => string; + getVariableName?: (style: FigmaExport.Style) => string; +} + +export = ({ + output, + getExtension = () => 'JSON', + getFilename = () => 'base', + getVariableName = (style) => kebabCase(style.name).toLowerCase(), +}: Options): FigmaExport.StyleOutputter => { + return async (styles) => { + const extension = getExtension(); + + const result = {}; + + styles.forEach((style) => { + if (style.visible) { + const variableName = getVariableName(style); + + // eslint-disable-next-line default-case + switch (style.styleType) { + case 'FILL': { + const value = style.fills + .filter((fill) => fill.visible) + .map((fill) => fill.value) + .join(', '); + + writeVariable(result, style.comment, variableName, value); + + break; + } + + case 'EFFECT': { + const visibleEffects = style.effects.filter((effect) => effect.visible); + + const boxShadowValue = visibleEffects + .filter((effect) => effect.type === 'INNER_SHADOW' || effect.type === 'DROP_SHADOW') + .map((effect) => effect.value) + .join(', '); + + const filterBlurValue = visibleEffects + .filter((effect) => effect.type === 'LAYER_BLUR') + .map((effect) => effect.value) + .join(', '); + + // Shadow and Blur effects cannot be combined together since they use two different CSS properties. + writeVariable(result, style.comment, variableName, boxShadowValue || filterBlurValue); + + break; + } + + case 'TEXT': { + writeVariable(result, style.comment, `${variableName}-font-family`, `"${style.style.fontFamily}"`); + writeVariable(result, style.comment, `${variableName}-font-size`, `${style.style.fontSize}px`); + writeVariable(result, style.comment, `${variableName}-font-style`, `${style.style.fontStyle}`); + writeVariable(result, style.comment, `${variableName}-font-variant`, `${style.style.fontVariant}`); + writeVariable(result, style.comment, `${variableName}-font-weight`, `${style.style.fontWeight}`); + writeVariable(result, style.comment, `${variableName}-letter-spacing`, `${style.style.letterSpacing}px`); + writeVariable(result, style.comment, `${variableName}-line-height`, `${style.style.lineHeight}px`); + writeVariable(result, style.comment, `${variableName}-text-align`, `${style.style.textAlign}`); + writeVariable(result, style.comment, `${variableName}-text-decoration`, `${style.style.textDecoration}`); + writeVariable(result, style.comment, `${variableName}-text-transform`, `${style.style.textTransform}`); + writeVariable(result, style.comment, `${variableName}-vertical-align`, `${style.style.verticalAlign}`); + + break; + } + } + } + }); + + const filePath = path.resolve(output); + + fs.mkdirSync(filePath, { recursive: true }); + fs.writeFileSync(path.resolve(filePath, `${getFilename()}.${extension.toLowerCase()}`), JSON.stringify(result, undefined, 2)); + }; +}; diff --git a/packages/output-styles-as-style-dictionary/src/types.ts b/packages/output-styles-as-style-dictionary/src/types.ts new file mode 100644 index 00000000..711064d8 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/src/types.ts @@ -0,0 +1 @@ +export type Extension = 'JSON'; diff --git a/packages/output-styles-as-style-dictionary/src/utils.test.ts b/packages/output-styles-as-style-dictionary/src/utils.test.ts new file mode 100644 index 00000000..551778ef --- /dev/null +++ b/packages/output-styles-as-style-dictionary/src/utils.test.ts @@ -0,0 +1,91 @@ +import { expect } from 'chai'; + +import { writeVariable } from './utils'; + +describe('utils', () => { + describe('writeVariable', () => { + describe('JSON', () => { + it('should return empty string is the value is empty', () => { + const obj = {}; + writeVariable( + obj, + 'This is a comment', + 'variable-name', + '', + ); + + expect(obj).to.eql({}); + }); + + it('should be able to print-out simple variable', () => { + const obj = {}; + writeVariable( + obj, + 'This is a comment', + 'variable-name', + '#fff', + ); + + expect(obj).to.eql({ + variable: { + name: { + value: '#fff', + comment: 'This is a comment', + }, + }, + }); + }); + + it('should be able to print-out simple variable with an empty comment', () => { + const obj = {}; + writeVariable(obj, '', 'variable-name', '#fff'); + expect(obj).to.eql({ + variable: { + name: { + value: '#fff', + comment: undefined, + }, + }, + }); + }); + + it('should be able to print-out a comment with special chars', () => { + const obj = {}; + writeVariable( + obj, + 'Incomprehensible comment :) !"£$%&/()=?^*/\n/*/*/*/*////\n§°ç*é_:;][¶#@–•…*/„Ω€®™æ¨œøπ¬ºª∆∞ƒ∂ßå≤∑†©√∫˜µ*/', + 'variable-name', + '#fff', + ); + + expect(obj).to.eql({ + variable: { + name: { + value: '#fff', + comment: 'Incomprehensible comment :) !"£$%&/()=?^ //// §°ç*é_:;][¶#@–•…„Ω€®™æ¨œøπ¬ºª∆∞ƒ∂ßå≤∑†©√∫˜µ', + }, + }, + }); + }); + + it('should be able to print-out a complex variable', () => { + const obj = {}; + writeVariable( + obj, + 'This is a comment\nin two lines', + 'variable-name', + 'linear-gradient(104.57deg, rgba(184, 89, 255, 1) 0%, rgba(10, 207, 131, 0) 100%)', + ); + + expect(obj).to.eql({ + variable: { + name: { + value: 'linear-gradient(104.57deg, rgba(184, 89, 255, 1) 0%, rgba(10, 207, 131, 0) 100%)', + comment: 'This is a comment in two lines', + }, + }, + }); + }); + }); + }); +}); diff --git a/packages/output-styles-as-style-dictionary/src/utils.ts b/packages/output-styles-as-style-dictionary/src/utils.ts new file mode 100644 index 00000000..9a1d644f --- /dev/null +++ b/packages/output-styles-as-style-dictionary/src/utils.ts @@ -0,0 +1,46 @@ +type StyleDictionaryValue = { + comment?: string + value: string +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function set(object: Record, key: string, value: StyleDictionaryValue): void { + const keys = key.split('.'); + let currentObject = object; + + for (let i = 0; i < keys.length; i += 1) { + const currentKey = keys[i]; + if (i === keys.length - 1) { + currentObject[currentKey] = value; + } else { + if (!currentObject[currentKey]) { + currentObject[currentKey] = {}; + } + currentObject = currentObject[currentKey]; + } + } +} + +const writeComment = (message: string): string | undefined => { + if (message === '') { + return undefined; + } + + return message + .replace(/\*\//g, '') + .split('\n') + .join(' '); +}; + +export const writeVariable = (obj: Record, comment: string, name: string, value: string): void => { + if (value) { + set( + obj, + name.replace(/-/g, '.'), + { + comment: writeComment(comment), + value, + }, + ); + } +}; diff --git a/packages/output-styles-as-style-dictionary/tsconfig.json b/packages/output-styles-as-style-dictionary/tsconfig.json new file mode 100644 index 00000000..5abf3ab1 --- /dev/null +++ b/packages/output-styles-as-style-dictionary/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "rootDir": "src", + "outDir": "dist" + }, + "references": [ + {"path": "../types"}, + ] +} diff --git a/yarn.lock b/yarn.lock index f04c9b1b..6944c3df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -382,6 +382,15 @@ __metadata: languageName: unknown linkType: soft +"@figma-export/output-styles-as-style-dictionary@workspace:packages/output-styles-as-style-dictionary": + version: 0.0.0-use.local + resolution: "@figma-export/output-styles-as-style-dictionary@workspace:packages/output-styles-as-style-dictionary" + dependencies: + "@figma-export/types": ^4.5.0 + "@figma-export/utils": ^4.0.0 + languageName: unknown + linkType: soft + "@figma-export/transform-svg-with-svgo@workspace:packages/transform-svg-with-svgo": version: 0.0.0-use.local resolution: "@figma-export/transform-svg-with-svgo@workspace:packages/transform-svg-with-svgo" @@ -392,7 +401,7 @@ __metadata: languageName: unknown linkType: soft -"@figma-export/types@^4.6.0, @figma-export/types@workspace:packages/types": +"@figma-export/types@^4.5.0, @figma-export/types@^4.6.0, @figma-export/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@figma-export/types@workspace:packages/types" dependencies: From f506c00181838fef7e576c8e99da5bfa87c6b1ac Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Tue, 23 May 2023 22:43:42 +0200 Subject: [PATCH 2/5] Add 'descriptor' arg to 'getVariableName' --- .../output-styles-as-css/src/index.test.ts | 5 +- packages/output-styles-as-css/src/index.ts | 34 ++++---- .../output-styles-as-less/src/index.test.ts | 29 ++++++- packages/output-styles-as-less/src/index.ts | 26 ++++-- .../output-styles-as-sass/src/index.test.ts | 29 ++++++- packages/output-styles-as-sass/src/index.ts | 81 +++++++++++++++++-- .../src/index.ts | 34 ++++---- packages/types/src/styles/index.ts | 16 ++++ 8 files changed, 191 insertions(+), 63 deletions(-) diff --git a/packages/output-styles-as-css/src/index.test.ts b/packages/output-styles-as-css/src/index.test.ts index 90549d4b..acb24e75 100644 --- a/packages/output-styles-as-css/src/index.test.ts +++ b/packages/output-styles-as-css/src/index.test.ts @@ -8,9 +8,8 @@ import { } from '@figma-export/types'; import { camelCase } from '@figma-export/utils'; -// eslint-disable-next-line import/order -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; const mockFill = (fills: FillStyle[], { visible = true, name = 'variable/name', comment = 'lorem ipsum' } = {}): Style => ({ fills, diff --git a/packages/output-styles-as-css/src/index.ts b/packages/output-styles-as-css/src/index.ts index 6a6ce63c..7a150c6f 100644 --- a/packages/output-styles-as-css/src/index.ts +++ b/packages/output-styles-as-css/src/index.ts @@ -1,7 +1,7 @@ import * as FigmaExport from '@figma-export/types'; import { kebabCase } from '@figma-export/utils'; -import { writeVariable, writeComment, sanitizeText } from './utils'; +import { sanitizeText, writeComment, writeVariable } from './utils'; import fs = require('fs'); import path = require('path'); @@ -9,21 +9,19 @@ import path = require('path'); type Options = { output: string; getFilename?: () => string; - getVariableName?: (style: FigmaExport.Style) => string; + getVariableName?: FigmaExport.GetVariableName; } export = ({ output, getFilename = () => '_variables', - getVariableName = (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }: Options): FigmaExport.StyleOutputter => { return async (styles) => { let text = ''; styles.forEach((style) => { if (style.visible) { - const variableName = getVariableName(style); - // eslint-disable-next-line default-case switch (style.styleType) { case 'FILL': { @@ -34,7 +32,7 @@ export = ({ if (value) { text += writeComment(style.comment); - text += writeVariable(variableName, value); + text += writeVariable(getVariableName(style), value); } break; @@ -58,7 +56,7 @@ export = ({ if (value) { text += writeComment(style.comment); - text += writeVariable(variableName, value); + text += writeVariable(getVariableName(style), value); } break; @@ -66,17 +64,17 @@ export = ({ case 'TEXT': { text += writeComment(style.comment); - text += writeVariable(`${variableName}-font-family`, `"${style.style.fontFamily}"`); - text += writeVariable(`${variableName}-font-size`, `${style.style.fontSize}px`); - text += writeVariable(`${variableName}-font-style`, `${style.style.fontStyle}`); - text += writeVariable(`${variableName}-font-variant`, `${style.style.fontVariant}`); - text += writeVariable(`${variableName}-font-weight`, `${style.style.fontWeight}`); - text += writeVariable(`${variableName}-letter-spacing`, `${style.style.letterSpacing}px`); - text += writeVariable(`${variableName}-line-height`, `${style.style.lineHeight}px`); - text += writeVariable(`${variableName}-text-align`, `${style.style.textAlign}`); - text += writeVariable(`${variableName}-text-decoration`, `${style.style.textDecoration}`); - text += writeVariable(`${variableName}-text-transform`, `${style.style.textTransform}`); - text += writeVariable(`${variableName}-vertical-align`, `${style.style.verticalAlign}`); + text += writeVariable(getVariableName(style, 'font-family'), `"${style.style.fontFamily}"`); + text += writeVariable(getVariableName(style, 'font-size'), `${style.style.fontSize}px`); + text += writeVariable(getVariableName(style, 'font-style'), `${style.style.fontStyle}`); + text += writeVariable(getVariableName(style, 'font-variant'), `${style.style.fontVariant}`); + text += writeVariable(getVariableName(style, 'font-weight'), `${style.style.fontWeight}`); + text += writeVariable(getVariableName(style, 'letter-spacing'), `${style.style.letterSpacing}px`); + text += writeVariable(getVariableName(style, 'line-height'), `${style.style.lineHeight}px`); + text += writeVariable(getVariableName(style, 'text-align'), `${style.style.textAlign}`); + text += writeVariable(getVariableName(style, 'text-decoration'), `${style.style.textDecoration}`); + text += writeVariable(getVariableName(style, 'text-transform'), `${style.style.textTransform}`); + text += writeVariable(getVariableName(style, 'vertical-align'), `${style.style.verticalAlign}`); break; } diff --git a/packages/output-styles-as-less/src/index.test.ts b/packages/output-styles-as-less/src/index.test.ts index f1a930c4..d6e0e468 100644 --- a/packages/output-styles-as-less/src/index.test.ts +++ b/packages/output-styles-as-less/src/index.test.ts @@ -8,9 +8,8 @@ import { } from '@figma-export/types'; import { camelCase } from '@figma-export/utils'; -// eslint-disable-next-line import/order -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; const mockFill = (fills: FillStyle[], { visible = true, name = 'variable/name', comment = 'lorem ipsum' } = {}): Style => ({ fills, @@ -317,7 +316,29 @@ describe('style output as less', () => { + 'text-decoration: none;\n' + 'text-transform: uppercase;\n' + 'vertical-align: middle;\n' - + '};\n', + + '};\n' + + '\n\n' + + '@variable-name-font-family: "Verdana";\n' + + '\n\n' + + '@variable-name-font-size: 12px;\n' + + '\n\n' + + '@variable-name-font-style: italic;\n' + + '\n\n' + + '@variable-name-font-variant: normal;\n' + + '\n\n' + + '@variable-name-font-weight: 100;\n' + + '\n\n' + + '@variable-name-letter-spacing: 10px;\n' + + '\n\n' + + '@variable-name-line-height: 12px;\n' + + '\n\n' + + '@variable-name-text-align: left;\n' + + '\n\n' + + '@variable-name-text-decoration: none;\n' + + '\n\n' + + '@variable-name-text-transform: uppercase;\n' + + '\n\n' + + '@variable-name-vertical-align: middle;\n', ); }); }); diff --git a/packages/output-styles-as-less/src/index.ts b/packages/output-styles-as-less/src/index.ts index ee1e0f83..1bf77fd6 100644 --- a/packages/output-styles-as-less/src/index.ts +++ b/packages/output-styles-as-less/src/index.ts @@ -1,7 +1,7 @@ import * as FigmaExport from '@figma-export/types'; import { kebabCase } from '@figma-export/utils'; -import { writeVariable, writeMap } from './utils'; +import { writeMap, writeVariable } from './utils'; import fs = require('fs'); import path = require('path'); @@ -9,21 +9,19 @@ import path = require('path'); type Options = { output: string; getFilename?: () => string; - getVariableName?: (style: FigmaExport.Style) => string; + getVariableName?: FigmaExport.GetVariableName; } export = ({ output, getFilename = () => '_variables', - getVariableName = (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }: Options): FigmaExport.StyleOutputter => { return async (styles) => { let text = ''; styles.forEach((style) => { if (style.visible) { - const variableName = getVariableName(style); - // eslint-disable-next-line default-case switch (style.styleType) { case 'FILL': { @@ -32,7 +30,7 @@ export = ({ .map((fill) => fill.value) .join(', '); - text += writeVariable(style.comment, variableName, value); + text += writeVariable(style.comment, getVariableName(style), value); break; } @@ -51,7 +49,7 @@ export = ({ .join(', '); // Shadow and Blur effects cannot be combined together since they use two different CSS properties. - text += writeVariable(style.comment, variableName, boxShadowValue || filterBlurValue); + text += writeVariable(style.comment, getVariableName(style), boxShadowValue || filterBlurValue); break; } @@ -71,7 +69,19 @@ export = ({ vertical-align: ${style.style.verticalAlign}; }`; - text += writeMap(style.comment, variableName, value); + text += writeMap(style.comment, getVariableName(style), value); + + text += writeVariable(style.comment, getVariableName(style, 'font-family'), `"${style.style.fontFamily}"`); + text += writeVariable(style.comment, getVariableName(style, 'font-size'), `${style.style.fontSize}px`); + text += writeVariable(style.comment, getVariableName(style, 'font-style'), `${style.style.fontStyle}`); + text += writeVariable(style.comment, getVariableName(style, 'font-variant'), `${style.style.fontVariant}`); + text += writeVariable(style.comment, getVariableName(style, 'font-weight'), `${style.style.fontWeight}`); + text += writeVariable(style.comment, getVariableName(style, 'letter-spacing'), `${style.style.letterSpacing}px`); + text += writeVariable(style.comment, getVariableName(style, 'line-height'), `${style.style.lineHeight}px`); + text += writeVariable(style.comment, getVariableName(style, 'text-align'), `${style.style.textAlign}`); + text += writeVariable(style.comment, getVariableName(style, 'text-decoration'), `${style.style.textDecoration}`); + text += writeVariable(style.comment, getVariableName(style, 'text-transform'), `${style.style.textTransform}`); + text += writeVariable(style.comment, getVariableName(style, 'vertical-align'), `${style.style.verticalAlign}`); break; } diff --git a/packages/output-styles-as-sass/src/index.test.ts b/packages/output-styles-as-sass/src/index.test.ts index a1b1e782..b5a9d39e 100644 --- a/packages/output-styles-as-sass/src/index.test.ts +++ b/packages/output-styles-as-sass/src/index.test.ts @@ -8,9 +8,8 @@ import { } from '@figma-export/types'; import { camelCase } from '@figma-export/utils'; -// eslint-disable-next-line import/order -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; const mockFill = (fills: FillStyle[], { visible = true, name = 'variable/name', comment = 'lorem ipsum' } = {}): Style => ({ fills, @@ -318,7 +317,29 @@ describe('style output as scss', () => { + ' "text-decoration": none,\n' + ' "text-transform": uppercase,\n' + ' "vertical-align": middle\n' - + ');\n', + + ');\n' + + '\n\n' + + '$variable-name-font-family: "Verdana";\n' + + '\n\n' + + '$variable-name-font-size: 12px;\n' + + '\n\n' + + '$variable-name-font-style: italic;\n' + + '\n\n' + + '$variable-name-font-variant: normal;\n' + + '\n\n' + + '$variable-name-font-weight: 100;\n' + + '\n\n' + + '$variable-name-letter-spacing: 10px;\n' + + '\n\n' + + '$variable-name-line-height: 12px;\n' + + '\n\n' + + '$variable-name-text-align: left;\n' + + '\n\n' + + '$variable-name-text-decoration: none;\n' + + '\n\n' + + '$variable-name-text-transform: uppercase;\n' + + '\n\n' + + '$variable-name-vertical-align: middle;\n', ); }); }); diff --git a/packages/output-styles-as-sass/src/index.ts b/packages/output-styles-as-sass/src/index.ts index 075768df..5c610779 100644 --- a/packages/output-styles-as-sass/src/index.ts +++ b/packages/output-styles-as-sass/src/index.ts @@ -1,8 +1,8 @@ import * as FigmaExport from '@figma-export/types'; import { kebabCase } from '@figma-export/utils'; -import { writeVariable } from './utils'; import { Extension } from './types'; +import { writeVariable } from './utils'; import fs = require('fs'); import path = require('path'); @@ -11,14 +11,14 @@ type Options = { output: string; getExtension?: () => Extension; getFilename?: () => string; - getVariableName?: (style: FigmaExport.Style) => string; + getVariableName?: FigmaExport.GetVariableName } export = ({ output, getExtension = () => 'SCSS', getFilename = () => '_variables', - getVariableName = (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }: Options): FigmaExport.StyleOutputter => { return async (styles) => { const extension = getExtension(); @@ -27,8 +27,6 @@ export = ({ styles.forEach((style) => { if (style.visible) { - const variableName = getVariableName(style); - // eslint-disable-next-line default-case switch (style.styleType) { case 'FILL': { @@ -37,7 +35,7 @@ export = ({ .map((fill) => fill.value) .join(', '); - text += writeVariable(style.comment, variableName, value, extension); + text += writeVariable(style.comment, getVariableName(style), value, extension); break; } @@ -56,7 +54,7 @@ export = ({ .join(', '); // Shadow and Blur effects cannot be combined together since they use two different CSS properties. - text += writeVariable(style.comment, variableName, boxShadowValue || filterBlurValue, extension); + text += writeVariable(style.comment, getVariableName(style), boxShadowValue || filterBlurValue, extension); break; } @@ -76,7 +74,74 @@ export = ({ "vertical-align": ${style.style.verticalAlign} )`; - text += writeVariable(style.comment, variableName, value, extension); + text += writeVariable(style.comment, getVariableName(style), value, extension); + + text += writeVariable( + style.comment, + getVariableName(style, 'font-family'), + `"${style.style.fontFamily}"`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'font-size'), + `${style.style.fontSize}px`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'font-style'), + `${style.style.fontStyle}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'font-variant'), + `${style.style.fontVariant}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'font-weight'), + `${style.style.fontWeight}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'letter-spacing'), + `${style.style.letterSpacing}px`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'line-height'), + `${style.style.lineHeight}px`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'text-align'), + `${style.style.textAlign}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'text-decoration'), + `${style.style.textDecoration}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'text-transform'), + `${style.style.textTransform}`, + extension, + ); + text += writeVariable( + style.comment, + getVariableName(style, 'vertical-align'), + `${style.style.verticalAlign}`, + extension, + ); break; } diff --git a/packages/output-styles-as-style-dictionary/src/index.ts b/packages/output-styles-as-style-dictionary/src/index.ts index 6ab0c0d8..1e056f5b 100644 --- a/packages/output-styles-as-style-dictionary/src/index.ts +++ b/packages/output-styles-as-style-dictionary/src/index.ts @@ -1,8 +1,8 @@ import * as FigmaExport from '@figma-export/types'; import { kebabCase } from '@figma-export/utils'; -import { writeVariable } from './utils'; import { Extension } from './types'; +import { writeVariable } from './utils'; import fs = require('fs'); import path = require('path'); @@ -11,14 +11,14 @@ type Options = { output: string; getExtension?: () => Extension; getFilename?: () => string; - getVariableName?: (style: FigmaExport.Style) => string; + getVariableName?: FigmaExport.GetVariableName; } export = ({ output, getExtension = () => 'JSON', getFilename = () => 'base', - getVariableName = (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }: Options): FigmaExport.StyleOutputter => { return async (styles) => { const extension = getExtension(); @@ -27,8 +27,6 @@ export = ({ styles.forEach((style) => { if (style.visible) { - const variableName = getVariableName(style); - // eslint-disable-next-line default-case switch (style.styleType) { case 'FILL': { @@ -37,7 +35,7 @@ export = ({ .map((fill) => fill.value) .join(', '); - writeVariable(result, style.comment, variableName, value); + writeVariable(result, style.comment, getVariableName(style), value); break; } @@ -56,23 +54,23 @@ export = ({ .join(', '); // Shadow and Blur effects cannot be combined together since they use two different CSS properties. - writeVariable(result, style.comment, variableName, boxShadowValue || filterBlurValue); + writeVariable(result, style.comment, getVariableName(style), boxShadowValue || filterBlurValue); break; } case 'TEXT': { - writeVariable(result, style.comment, `${variableName}-font-family`, `"${style.style.fontFamily}"`); - writeVariable(result, style.comment, `${variableName}-font-size`, `${style.style.fontSize}px`); - writeVariable(result, style.comment, `${variableName}-font-style`, `${style.style.fontStyle}`); - writeVariable(result, style.comment, `${variableName}-font-variant`, `${style.style.fontVariant}`); - writeVariable(result, style.comment, `${variableName}-font-weight`, `${style.style.fontWeight}`); - writeVariable(result, style.comment, `${variableName}-letter-spacing`, `${style.style.letterSpacing}px`); - writeVariable(result, style.comment, `${variableName}-line-height`, `${style.style.lineHeight}px`); - writeVariable(result, style.comment, `${variableName}-text-align`, `${style.style.textAlign}`); - writeVariable(result, style.comment, `${variableName}-text-decoration`, `${style.style.textDecoration}`); - writeVariable(result, style.comment, `${variableName}-text-transform`, `${style.style.textTransform}`); - writeVariable(result, style.comment, `${variableName}-vertical-align`, `${style.style.verticalAlign}`); + writeVariable(result, style.comment, getVariableName(style, 'font-family'), `"${style.style.fontFamily}"`); + writeVariable(result, style.comment, getVariableName(style, 'font-size'), `${style.style.fontSize}px`); + writeVariable(result, style.comment, getVariableName(style, 'font-style'), `${style.style.fontStyle}`); + writeVariable(result, style.comment, getVariableName(style, 'font-variant'), `${style.style.fontVariant}`); + writeVariable(result, style.comment, getVariableName(style, 'font-weight'), `${style.style.fontWeight}`); + writeVariable(result, style.comment, getVariableName(style, 'letter-spacing'), `${style.style.letterSpacing}px`); + writeVariable(result, style.comment, getVariableName(style, 'line-height'), `${style.style.lineHeight}px`); + writeVariable(result, style.comment, getVariableName(style, 'text-align'), `${style.style.textAlign}`); + writeVariable(result, style.comment, getVariableName(style, 'text-decoration'), `${style.style.textDecoration}`); + writeVariable(result, style.comment, getVariableName(style, 'text-transform'), `${style.style.textTransform}`); + writeVariable(result, style.comment, getVariableName(style, 'vertical-align'), `${style.style.verticalAlign}`); break; } diff --git a/packages/types/src/styles/index.ts b/packages/types/src/styles/index.ts index 63a3bb06..9fe155eb 100644 --- a/packages/types/src/styles/index.ts +++ b/packages/types/src/styles/index.ts @@ -22,4 +22,20 @@ export type Style = { | GridStyle.StyleTypeGrid ) +export type GetVariableName = ( + style: Style, + descriptor?: + | 'font-family' + | 'font-size' + | 'font-style' + | 'font-variant' + | 'font-weight' + | 'letter-spacing' + | 'line-height' + | 'text-align' + | 'text-decoration' + | 'text-transform' + | 'vertical-align' +) => string; + export type StyleOutputter = (styles: Style[]) => Promise From aa1d4e5e8d27da061448a9d9d54d9539305cca20 Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Tue, 23 May 2023 23:04:57 +0200 Subject: [PATCH 3/5] Update website and documentation --- .figmaexportrc.example.local.ts | 4 ++ README.md | 2 +- packages/cli/README.md | 1 + .../src/index.test.ts | 4 +- .../src/index.test.ts | 4 +- .../src/index.test.ts | 4 +- .../src/index.test.ts | 5 +-- packages/output-styles-as-css/README.md | 2 +- packages/output-styles-as-less/README.md | 2 +- packages/output-styles-as-sass/README.md | 2 +- .../README.md | 2 +- .../src/output-styles/AsStyleDictionary.jsx | 42 +++++++++++++++++++ .../website/src/output-styles/FigmaStyles.jsx | 2 + 13 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 packages/website/src/output-styles/AsStyleDictionary.jsx diff --git a/.figmaexportrc.example.local.ts b/.figmaexportrc.example.local.ts index 7ca72a33..0b9c2ca7 100755 --- a/.figmaexportrc.example.local.ts +++ b/.figmaexportrc.example.local.ts @@ -11,6 +11,7 @@ import { FigmaExportRC, StylesCommandOptions, ComponentsCommandOptions } from '. import outputStylesAsCss from './packages/output-styles-as-css'; import outputStylesAsLess from './packages/output-styles-as-less'; import outputStylesAsSass from './packages/output-styles-as-sass'; +import outputStylesAsStyleDictionary from './packages/output-styles-as-style-dictionary'; import transformSvgWithSvgo from './packages/transform-svg-with-svgo'; import outputComponentsAsEs6 from './packages/output-components-as-es6'; import outputComponentsAsSvg from './packages/output-components-as-svg'; @@ -29,6 +30,9 @@ const styleOptions: StylesCommandOptions = { }), outputStylesAsSass({ output: './output/styles/sass' + }), + outputStylesAsStyleDictionary({ + output: './output/styles/style-dictionary' }) ] }; diff --git a/README.md b/README.md index 5ed7145b..d9e336cc 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ You can export your Figma Components as SVG and use them inside your website. ### Styles -You can export your Figma Styles into different output like `.sass` format, `.scss` format or you can create your own outputter. +You can export your Figma Styles into different output like `.sass` format, `.scss` format, [`Style Dictionary`](https://amzn.github.io/style-dictionary/#/) tokens or you can create your own outputter. > If you want to keep the style of your Figma file in-sync with the `.css` of your website, this is a must-have. diff --git a/packages/cli/README.md b/packages/cli/README.md index ac67d12c..f43f918a 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -157,5 +157,6 @@ or install an official outputter: | Package | Version | |---------|---------| | [`@figma-export/output-styles-as-css`](/packages/output-styles-as-css) | [![npm](https://img.shields.io/npm/v/@figma-export/output-styles-as-css.svg?maxAge=3600)](https://www.npmjs.com/package/@figma-export/output-styles-as-css) | +| [`@figma-export/output-styles-as-style-dictionary`](/packages/output-styles-as-style-dictionary) | [![npm](https://img.shields.io/npm/v/@figma-export/output-styles-as-style-dictionary.svg?maxAge=3600)](https://www.npmjs.com/package/@figma-export/output-styles-as-style-dictionary) | | [`@figma-export/output-styles-as-sass`](/packages/output-styles-as-sass) | [![npm](https://img.shields.io/npm/v/@figma-export/output-styles-as-sass.svg?maxAge=3600)](https://www.npmjs.com/package/@figma-export/output-styles-as-sass) | | [`@figma-export/output-styles-as-less`](/packages/output-styles-as-less) | [![npm](https://img.shields.io/npm/v/@figma-export/output-styles-as-less.svg?maxAge=3600)](https://www.npmjs.com/package/@figma-export/output-styles-as-less) | diff --git a/packages/output-components-as-es6/src/index.test.ts b/packages/output-components-as-es6/src/index.test.ts index 1d12f681..bcd7ba3b 100644 --- a/packages/output-components-as-es6/src/index.test.ts +++ b/packages/output-components-as-es6/src/index.test.ts @@ -11,8 +11,8 @@ import * as FigmaExport from '@figma-export/types'; import * as figmaDocument from '../../core/src/lib/_config.test'; import * as figma from '../../core/src/lib/figma'; -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; describe('outputter as es6', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/output-components-as-svg/src/index.test.ts b/packages/output-components-as-svg/src/index.test.ts index e14ad396..2af3f1de 100644 --- a/packages/output-components-as-svg/src/index.test.ts +++ b/packages/output-components-as-svg/src/index.test.ts @@ -2,9 +2,9 @@ import sinon from 'sinon'; import { expect } from 'chai'; import * as figmaDocument from '../../core/src/lib/_config.test'; import * as figma from '../../core/src/lib/figma'; -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; describe('outputter as svg', () => { beforeEach(() => { diff --git a/packages/output-components-as-svgr/src/index.test.ts b/packages/output-components-as-svgr/src/index.test.ts index a7e9c347..bd550d03 100644 --- a/packages/output-components-as-svgr/src/index.test.ts +++ b/packages/output-components-as-svgr/src/index.test.ts @@ -5,9 +5,9 @@ import nock from 'nock'; import { camelCase, kebabCase } from '@figma-export/utils'; import * as figmaDocument from '../../core/src/lib/_config.test'; import * as figma from '../../core/src/lib/figma'; -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; describe('outputter as svgr', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/output-components-as-svgstore/src/index.test.ts b/packages/output-components-as-svgstore/src/index.test.ts index 807a00ee..7815171b 100644 --- a/packages/output-components-as-svgstore/src/index.test.ts +++ b/packages/output-components-as-svgstore/src/index.test.ts @@ -4,9 +4,8 @@ import { expect } from 'chai'; import * as figmaDocument from '../../core/src/lib/_config.test'; import * as figma from '../../core/src/lib/figma'; -// eslint-disable-next-line import/order -import fs = require('fs'); -import outputter = require('./index'); +import fs from 'fs'; +import outputter from './index'; describe('outputter as svgstore', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/output-styles-as-css/README.md b/packages/output-styles-as-css/README.md index f67ea565..db68fafd 100644 --- a/packages/output-styles-as-css/README.md +++ b/packages/output-styles-as-css/README.md @@ -44,7 +44,7 @@ const { kebabCase } = require('@figma-export/utils'); require('@figma-export/output-styles-as-css')({ output: './output', getFilename: () => '_variables', - getVariableName: (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }) ``` diff --git a/packages/output-styles-as-less/README.md b/packages/output-styles-as-less/README.md index ff7095b8..392b6f48 100644 --- a/packages/output-styles-as-less/README.md +++ b/packages/output-styles-as-less/README.md @@ -44,7 +44,7 @@ const { kebabCase } = require('@figma-export/utils'); require('@figma-export/output-styles-as-less')({ output: './output', getFilename: () => '_variables', - getVariableName: (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }) ``` diff --git a/packages/output-styles-as-sass/README.md b/packages/output-styles-as-sass/README.md index 754d1394..8b0611d2 100644 --- a/packages/output-styles-as-sass/README.md +++ b/packages/output-styles-as-sass/README.md @@ -45,7 +45,7 @@ require('@figma-export/output-styles-as-sass')({ output: './output', getExtension: () => 'SCSS', getFilename: () => '_variables', - getVariableName: (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }) ``` diff --git a/packages/output-styles-as-style-dictionary/README.md b/packages/output-styles-as-style-dictionary/README.md index cfa44e50..d1ad3019 100644 --- a/packages/output-styles-as-style-dictionary/README.md +++ b/packages/output-styles-as-style-dictionary/README.md @@ -45,7 +45,7 @@ require('@figma-export/output-styles-as-style-dictionary')({ output: './output', getExtension: () => 'JSON', getFilename: () => 'base', - getVariableName: (style) => kebabCase(style.name).toLowerCase(), + getVariableName = (style, descriptor) => `${kebabCase(style.name).toLowerCase()}${descriptor != null ? `-${descriptor}` : ''}`, }) ``` diff --git a/packages/website/src/output-styles/AsStyleDictionary.jsx b/packages/website/src/output-styles/AsStyleDictionary.jsx new file mode 100644 index 00000000..806fd8a0 --- /dev/null +++ b/packages/website/src/output-styles/AsStyleDictionary.jsx @@ -0,0 +1,42 @@ +import Code from '../components/Code'; +import CodeBlock from '../components/CodeBlock'; + +const props = { + title: ( + <> + Export your styles as Style Dictionary tokens + + ), + description: ( + <> +
+ Once exported, you can configure a{' '} + + Style Dictionary + {' '} + project and use the generated base.json. +
+ + ), + code: ` + module.exports = { + commands: [ + ['styles', { + fileId: 'fzYhvQpqwhZDUImRz431Qo', + outputters: [ + // https://www.npmjs.com/package/@figma-export/output-styles-as-style-dictionary + require('@figma-export/output-styles-as-style-dictionary')({ + output: './output/style-dictionary', + }) + ] + }] + ] + } +`, +}; + +const AsStyleDictionary = () => ( + +); + +export default AsStyleDictionary; diff --git a/packages/website/src/output-styles/FigmaStyles.jsx b/packages/website/src/output-styles/FigmaStyles.jsx index 7c4c2ef5..9393f68b 100644 --- a/packages/website/src/output-styles/FigmaStyles.jsx +++ b/packages/website/src/output-styles/FigmaStyles.jsx @@ -1,6 +1,7 @@ import AsCss from './AsCss'; import AsSass from './AsSass'; import AsLess from './AsLess'; +import AsStyleDictionary from './AsStyleDictionary'; const FigmaStyles = () => (
@@ -55,6 +56,7 @@ const FigmaStyles = () => (
+ From d792bdcd375c6c2ffba8d64e631284e136290465 Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Tue, 23 May 2023 23:12:14 +0200 Subject: [PATCH 4/5] Update test workflow dependencies --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a5a44fd..91bb6c33 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,14 +14,14 @@ jobs: strategy: matrix: - node: [12.22.0, 12.x, 14.x, 16.x, 18.x] + node: [12.22.0, 12.x, 14.x, 16.x, 18.x, 20.x] steps: - name: Checkout 🛎️ - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Node 🧰 - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} @@ -41,10 +41,10 @@ jobs: steps: - name: Checkout 🛎️ - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Node 🧰 - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: 16.x @@ -61,7 +61,7 @@ jobs: path-to-lcov: ./coverage/lcov.info # optional (default value) - name: Upload `coverage` artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: coverage path: coverage/lcov-report @@ -73,10 +73,10 @@ jobs: # steps: # - name: Checkout 🛎️ - # uses: actions/checkout@v2 + # uses: actions/checkout@v3 # - name: Node 🧰 - # uses: actions/setup-node@v2 + # uses: actions/setup-node@v3 # with: # node-version: 14.x @@ -87,7 +87,7 @@ jobs: # run: yarn stryker # - name: Upload `stryker` artifact - # uses: actions/upload-artifact@v1 + # uses: actions/upload-artifact@v3 # with: # name: stryker # path: reports/mutation/html From 6fabc2ec22f31e39bcde5dd14d23483665f6b66c Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Wed, 24 May 2023 00:10:34 +0200 Subject: [PATCH 5/5] Update workflow dependencies --- .github/workflows/publish.yml | 6 +++--- .github/workflows/release.yml | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 189d934f..3106574e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,12 +10,12 @@ jobs: steps: - name: Checkout 🛎️ - uses: actions/checkout@v1 + uses: actions/checkout@v3 - name: Node 🧰 - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - name: Install 📦 run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 725a2fc7..0fa40064 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v1.0.0 + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Get tag info 🏷 id: tags