├── CODEOWNERS ├── .travis.yml ├── .npmignore ├── __fixtures__ ├── props.yml ├── b.json └── a.json ├── CONTRIBUTING-ARCHIVED.md ├── .editorconfig ├── .gitignore ├── __tests__ ├── __snapshots__ │ └── index.js.snap └── index.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── index.js ├── package.json ├── LICENSE.txt └── README.md /CODEOWNERS: -------------------------------------------------------------------------------- 1 | #ECCN:EAR99,Open Source -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | script: 5 | - npm test 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /* 2 | /*/ 3 | !/index.js 4 | !README.md 5 | !package.json 6 | !CHANGELOG.md 7 | !LICENSE.txt 8 | -------------------------------------------------------------------------------- /__fixtures__/props.yml: -------------------------------------------------------------------------------- 1 | global: 2 | category: 'web' 3 | type: 'color' 4 | props: 5 | foo: 6 | value: '#ff0000' 7 | baz: 8 | value: '#0000ff' 9 | -------------------------------------------------------------------------------- /CONTRIBUTING-ARCHIVED.md: -------------------------------------------------------------------------------- 1 | # ARCHIVED 2 | 3 | This project is `Archived` and is no longer actively maintained; 4 | We are not accepting contributions or Pull Requests. 5 | 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all hidden dotfiles 2 | .* 3 | 4 | # Whitelist useful dotfiles 5 | !.editorconfig 6 | !.eslintrc.yml 7 | !.eslintignore 8 | !.gitignore 9 | !.npmignore 10 | !.travis.yml 11 | 12 | # Add a .gitkeep file in an empty 13 | # directory to check it in the repository 14 | !.gitkeep 15 | 16 | # Ignore modules 17 | node_modules 18 | 19 | # Ignore logs 20 | *.log 21 | -------------------------------------------------------------------------------- /__fixtures__/b.json: -------------------------------------------------------------------------------- 1 | { 2 | "props": { 3 | "TOKEN_B": { 4 | "value": "{!white}" 5 | } 6 | }, 7 | "aliases": { 8 | "white": { 9 | "value": "#FFFFFF" 10 | }, 11 | "TOKEN_SAME_NAME_AS_ALIAS": { 12 | "value": "foo" 13 | } 14 | }, 15 | "global": { 16 | "type": "token", 17 | "category": "test", 18 | "meta": { 19 | "fixture": "b" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`gulp-theo transforms Design Tokens as 1337v4r5 1`] = ` 4 | " 5 | 😀a-1337: 6 | 'token-b': 😇(#FFFFFF) 7 | 'token-a': 😇(#FF0000) 8 | 'token-a-a': 😇(#FF0000 #FF0000) 9 | 'token-a-b': 😇(#FFFFFF) 10 | 'token-same-name-as-alias': 😇(foo) 11 | 'sizing-a': 😇(20px) 12 | 'color-a': 😇(rgb(186, 218, 85)) 13 | " 14 | `; 15 | 16 | exports[`gulp-theo transforms Design Tokens as Sass 1`] = ` 17 | " 18 | $token-b: #FFFFFF; 19 | $token-a: #FF0000; 20 | $token-a-a: #FF0000 #FF0000; 21 | $token-a-b: #FFFFFF; 22 | $token-same-name-as-alias: foo; 23 | $sizing-a: 20px; 24 | $color-a: rgb(186, 218, 85); 25 | " 26 | `; 27 | 28 | exports[`gulp-theo transforms multiple files as Sass 1`] = ` 29 | " 30 | $foo: rgb(255, 0, 0); 31 | $baz: rgb(0, 0, 255); 32 | " 33 | `; 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [2.0.0] 9 | 10 | ### ⚠️ Breaking changes 11 | 12 | * `theo` is now a `peerDependency` 13 | * `gulp-theo` now just exports a function instead of an object with a `plugin` property 14 | 15 | ### Migration guide 16 | 17 | ```sh 18 | npm install theo gulp-theo --save-dev 19 | ``` 20 | 21 | ```js 22 | const gulp = require('gulp'); 23 | const theo = require('gulp-theo'); 24 | 25 | gulp 26 | .src('design/props.yml') 27 | .pipe( 28 | theo({ 29 | transform: { type: 'web' }, 30 | format: { type: 'scss' } 31 | }) 32 | ) 33 | .pipe(gulp.dest('dist')); 34 | ``` 35 | 36 | ## 1.0.0 37 | 38 | * Initial release. 39 | 40 | [2.0.0]: https://github.com/salesforce-ux/gulp-theo/compare/v1.0.0...2.0.0 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Code 2 | 3 | External contributors are required to sign a Contributor’s License Agreement. 4 | You will be prompted to sign it when you open a pull request. 5 | 6 | 1. Create a new issue before starting your project so that we can keep 7 | track of what you are trying to add/fix. That way, we can also offer 8 | suggestions or let you know if there is already an effort in progress. 9 | 2. Fork off this repository. 10 | 3. Create a topic branch for the issue that you are trying to add. 11 | When possible, you should branch off the default branch. 12 | 4. Edit the code in your fork. 13 | 5. Send us a well documented pull request when you are done. 14 | 15 | The **GitHub pull requests** should meet the following criteria: 16 | 17 | - Descriptive title 18 | - Brief summary 19 | - @mention several relevant people to review the code 20 | - Add helpful GitHub comments on lines that you have questions / concerns about 21 | 22 | We’ll review your code, suggest any needed changes, and merge it in. Thank you. 23 | -------------------------------------------------------------------------------- /__fixtures__/a.json: -------------------------------------------------------------------------------- 1 | { 2 | "props": { 3 | "TOKEN_A": { 4 | "value": "{!red}" 5 | }, 6 | "TOKEN_A_A": { 7 | "value": "{!scarlet} {!crimson}", 8 | "meta": { 9 | "fixture": "override" 10 | } 11 | }, 12 | "TOKEN_A_B": { 13 | "value": "{!white}" 14 | }, 15 | "TOKEN_SAME_NAME_AS_ALIAS": { 16 | "value": "{!TOKEN_SAME_NAME_AS_ALIAS}" 17 | }, 18 | "sizingA": { 19 | "value": "20px", 20 | "category": "spacing", 21 | "type": "size" 22 | }, 23 | "colorA": { 24 | "value": "#bada55", 25 | "category": "background-color", 26 | "type": "color" 27 | } 28 | }, 29 | "aliases": { 30 | "red": { 31 | "value": "#FF0000" 32 | }, 33 | "scarlet": { 34 | "value": "{!red}" 35 | }, 36 | "crimson": { 37 | "value": "{!scarlet}" 38 | } 39 | }, 40 | "imports": [ 41 | "./b.json" 42 | ], 43 | "global": { 44 | "type": "token", 45 | "category": "test", 46 | "meta": { 47 | "fixture": "a", 48 | "baseFontPixel": 16, 49 | "baseFontPercentage": 100 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | const PluginError = require('plugin-error'); 5 | const theo = require('theo'); 6 | const through = require('through2'); 7 | 8 | module.exports = function(options) { 9 | return through.obj(function(file, enc, callback) { 10 | if (file.isStream()) { 11 | const err = this.emit( 12 | 'error', 13 | new PluginError('gulp-theo', 'Streaming not supported') 14 | ); 15 | return callback(err); 16 | } 17 | const opts = Object.assign({}, options, { 18 | transform: Object.assign({}, options.transform, { 19 | file: file.path, 20 | data: file.contents.toString('utf8') 21 | }) 22 | }); 23 | theo 24 | .convert(opts) 25 | .then(res => { 26 | file.path = file.path.replace(/(json|yml)$/, options.format.type); 27 | file.contents = Buffer.from(res); 28 | callback(null, file); 29 | }) 30 | .catch(e => { 31 | const err = new PluginError('gulp-theo', e, { trace: true }); 32 | callback(err, file); 33 | }); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-theo", 3 | "version": "2.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "description": "Gulp-theo is a plugin that transforms and formats Design Tokens", 9 | "engines": { 10 | "node": ">=6.3.1" 11 | }, 12 | "license": "BSD-3-Clause", 13 | "keywords": [ 14 | "gulp", 15 | "css", 16 | "design", 17 | "properties", 18 | "tokens", 19 | "sass", 20 | "scss", 21 | "stylus", 22 | "less", 23 | "iOS", 24 | "Android", 25 | "aura" 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/salesforce-ux/gulp-theo.git" 30 | }, 31 | "authors": [ 32 | "Salesforce and contributors", 33 | "Adam Putinski", 34 | "Sönke Rohde " 35 | ], 36 | "bugs": { 37 | "url": "https://github.com/salesforce-ux/gulp-theo/issues" 38 | }, 39 | "homepage": "https://github.com/salesforce-ux/gulp-theo", 40 | "dependencies": { 41 | "through2": "^3.0.1" 42 | }, 43 | "peerDependencies": { 44 | "theo": "^8.1.3" 45 | }, 46 | "devDependencies": { 47 | "gulp": "^4.0.2", 48 | "jest": "^26.0.1", 49 | "plugin-error": "^1.0.1", 50 | "theo": "^8.1.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-present, Salesforce.com, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Salesforce.com nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /__tests__/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | const gulp = require('gulp'); 5 | const through = require('through2'); 6 | const theo = require('theo'); 7 | 8 | const gulpTheo = require('../'); 9 | 10 | describe('gulp-theo', () => { 11 | it('transforms Design Tokens as Sass', done => { 12 | gulp 13 | .src('__fixtures__/a.json') 14 | .pipe( 15 | gulpTheo({ 16 | transform: { 17 | type: 'web' 18 | }, 19 | format: { 20 | type: 'scss' 21 | } 22 | }) 23 | ) 24 | .pipe( 25 | through.obj((file, enc, next) => { 26 | expect(file.contents.toString('utf8')).toMatchSnapshot(); 27 | next(); 28 | done(); 29 | }) 30 | ); 31 | }); 32 | it('transforms multiple files as Sass', done => { 33 | gulp 34 | .src(['__fixtures__/a.json', '__fixtures__/props.yml']) 35 | .pipe( 36 | gulpTheo({ 37 | transform: { 38 | type: 'web' 39 | }, 40 | format: { 41 | type: 'scss' 42 | } 43 | }) 44 | ) 45 | .pipe( 46 | through.obj((file, enc, next) => { 47 | if (file.relative === 'a.json') { 48 | expect(file.contents.toString('utf8')).toMatchSnapshot(); 49 | } 50 | if (file.relative === 'props.scss') { 51 | expect(file.contents.toString('utf8')).toMatchSnapshot(); 52 | done(); 53 | } 54 | next(); 55 | }) 56 | ); 57 | }); 58 | it('throws an error if a bad configuration is passed', done => { 59 | gulp 60 | .src('__fixtures__/a.json') 61 | .pipe( 62 | gulpTheo({ 63 | transform: { 64 | type: undefined 65 | }, 66 | format: { 67 | type: 'scss' 68 | } 69 | }) 70 | ) 71 | .on('error', e => { 72 | expect(e.message).toBe('Transform "undefined" is not registered'); 73 | done(); 74 | }); 75 | }); 76 | it('transforms Design Tokens as 1337v4r5', done => { 77 | theo.registerFormat( 78 | '1337v4r5', 79 | ` 80 | 😀{{stem meta.file}}-1337: 81 | {{#each props as |prop|}} 82 | '{{kebabcase prop.name}}': 😇({{prop.value}}) 83 | {{/each}} 84 | ` 85 | ); 86 | gulp 87 | .src('__fixtures__/a.json') 88 | .pipe( 89 | gulpTheo({ 90 | transform: { 91 | type: 'web' 92 | }, 93 | format: { 94 | type: '1337v4r5' 95 | } 96 | }) 97 | ) 98 | .pipe( 99 | through.obj((file, enc, next) => { 100 | expect(file.contents.toString('utf8')).toMatchSnapshot(); 101 | next(); 102 | done(); 103 | }) 104 | ); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Theo logo gulp-theo 2 | 3 | [![Build Status][travis-image]][travis-url] 4 | [![NPM version][npm-image]][npm-url] 5 | 6 | Theo is a [gulp](http://gulpjs.com) plugin for 7 | transforming and formatting [Design Tokens](https://npmjs.org/package/theo/#overview) 8 | with [Theo](https://npmjs.org/package/theo). 9 | 10 | ## Install 11 | 12 | ```sh 13 | npm install theo gulp-theo --save-dev 14 | ``` 15 | 16 | ## Usage 17 | 18 | Here is a simple example with one file (`props.yml`) 19 | that gets transformed into its Sass (SCSS) equivalent: 20 | 21 | ```yml 22 | # design/props.yml 23 | global: 24 | category: 'web' 25 | type: 'color' 26 | props: 27 | foo: 28 | value: '#ff0000' 29 | baz: 30 | value: '#0000ff' 31 | ``` 32 | 33 | ```js 34 | // gulpfile.js 35 | const gulp = require('gulp') 36 | const theo = require('gulp-theo') 37 | 38 | // Transform design/props.yml to dist/props.scss: 39 | gulp.task('tokens:scss', () => 40 | gulp.src('design/props.yml') 41 | .pipe(theo({ 42 | transform: { type: 'web' }, 43 | format: { type: 'scss' } 44 | })) 45 | .pipe(gulp.dest('dist')) 46 | ) 47 | ``` 48 | 49 | Running `gulp run tokens:scss` outputs: 50 | 51 | ```scss 52 | // dist/props.scss 53 | $foo: rgb(255, 0, 0); 54 | $baz: rgb(0, 0, 255); 55 | ``` 56 | 57 | ## Advanced Usage 58 | 59 | In this example (available in the [/example](https://github.com/salesforce-ux/gulp-theo/tree/master/example) folder), gulp-theo generates multiple files, transformed using a custom format (`array.js`). 60 | 61 | ```yml 62 | # tokens/_aliases.yml 63 | aliases: 64 | red: 'rgb(255, 0, 0)' 65 | blue: 'rgb(0, 0, 255)' 66 | ``` 67 | 68 | ```yml 69 | # tokens/_colors.yml 70 | global: 71 | category: 'web' 72 | type: 'color' 73 | imports: 74 | - _aliases.yml 75 | props: 76 | brand: 77 | value: '{!blue}' 78 | comment: 'ACME Inc. brand color.' 79 | primary: 80 | value: '{!red}' 81 | comment: 'Use the primary color on call-to-action buttons. e.g. "Save", "Log In"…' 82 | ``` 83 | 84 | ```yml 85 | # tokens/_mobile-overrides.yml 86 | global: 87 | category: 'web' 88 | type: 'mobile' 89 | props: 90 | large: 91 | value: '3rem' 92 | ``` 93 | 94 | ```yml 95 | # tokens/_sizes.yml 96 | global: 97 | category: 'web' 98 | type: 'size' 99 | imports: 100 | - _aliases.yml 101 | props: 102 | medium: 103 | value: '1rem' 104 | large: 105 | value: '2rem' 106 | ``` 107 | 108 | ```yml 109 | # tokens/default.yml 110 | imports: 111 | - _colors.yml 112 | - _sizes.yml 113 | ``` 114 | 115 | ```yml 116 | # tokens/mobile.yml 117 | imports: 118 | - _colors.yml 119 | - _sizes.yml 120 | - _mobile-overrides.yml 121 | ``` 122 | 123 | ```js 124 | // gulpfile.js 125 | const gulp = require('gulp') 126 | const gulpTheo = require('gulp-theo') 127 | const theo = require('theo') 128 | 129 | theo.registerFormat('array.js', ` 130 | // Source: {{stem meta.file}} 131 | module.exports = [ 132 | {{#each props as |prop|}} 133 | {{#if prop.comment}}// {{{prop.comment}}}{{/if}} 134 | ['{{camelcase prop.name}}', '{{prop.value}}'], 135 | {{/each}} 136 | ] 137 | `) 138 | 139 | gulp.task('tokens:array', () => 140 | gulp.src([ 141 | // Look for yml files 142 | 'tokens/*.yml', 143 | // Exclude partials (files prefixed with _) 144 | '!tokens/_*' 145 | ]) 146 | .pipe(gulpTheo( 147 | { 148 | transform: { 149 | type: 'web' 150 | }, 151 | format: { 152 | type: 'array.js' 153 | } 154 | } 155 | )) 156 | .pipe(gulp.dest('dist')) 157 | ) 158 | 159 | ``` 160 | 161 | Running `gulp tokens:array` will output: 162 | 163 | ```js 164 | // dist/default.array.js 165 | 166 | // Source: default 167 | module.exports = [ 168 | // ACME Inc. brand color. 169 | ['brand', 'rgb(0, 0, 255)'], 170 | // Use the primary color on call-to-action buttons. e.g. "Save", "Log In"… 171 | ['primary', 'rgb(255, 0, 0)'], 172 | ['medium', '1rem'], 173 | ['large', '2rem'], 174 | ] 175 | ``` 176 | 177 | ```js 178 | // dist/mobile.array.js 179 | 180 | // Source: mobile 181 | module.exports = [ 182 | // ACME Inc. brand color. 183 | ['brand', 'rgb(0, 0, 255)'], 184 | // Use the primary color on call-to-action buttons. e.g. "Save", "Log In"… 185 | ['primary', 'rgb(255, 0, 0)'], 186 | ['medium', '1rem'], 187 | ['large', '3rem'], 188 | ] 189 | ``` 190 | 191 | ---- 192 | 193 | Another example is available at . 194 | 195 | For any other options, refer to [Theo’s documentation](https://travis-ci.org/salesforce-ux/gulp-theo). 196 | 197 | [npm-url]: https://npmjs.org/package/gulp-theo 198 | [npm-image]: http://img.shields.io/npm/v/gulp-theo.svg 199 | 200 | [travis-url]: https://travis-ci.org/salesforce-ux/gulp-theo 201 | [travis-image]: http://img.shields.io/travis/salesforce-ux/gulp-theo.svg 202 | --------------------------------------------------------------------------------