├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── LICENSE.md ├── README.md ├── bin └── cli.js ├── docs └── lab-json.md ├── examples ├── lab.json └── theme.json ├── lib ├── createIndex.js ├── index.js └── templates │ ├── composite-react.js │ ├── composite-vue.js │ ├── cxs.js │ ├── emotion.js │ ├── fela.js │ ├── glamorous.js │ ├── index.js │ ├── styledComponents.js │ ├── util.js │ └── vue.js ├── package.json └── test ├── fixture.json ├── modules.js ├── snapshots ├── modules.js.md ├── modules.js.snap ├── templates.js.md ├── templates.js.snap ├── vue.js.md └── vue.js.snap ├── templates.js └── vue.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .nyc_output 3 | coverage 4 | coverage.lcov 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | docs 3 | coverage 4 | .nyc_output 5 | test 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8.9 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | Copyright (c) 2017 Compositor, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # lab-cli 3 | 4 | Command line utilities and exporting module for [Compositor Lab][lab] 5 | 6 | [![Build Status][build-badge]][build] 7 | 8 | [build-badge]: https://img.shields.io/travis/c8r/lab-cli/master.svg?style=flat-square 9 | [build]: https://travis-ci.org/c8r/lab-cli 10 | 11 | ```sh 12 | npm install @compositor/lab 13 | ``` 14 | 15 | ## CLI Usage 16 | 17 | Compile `lab.json` to React components: 18 | 19 | ```sh 20 | lab --out-dir dist/ 21 | ``` 22 | 23 | Watch for changes: 24 | 25 | ```sh 26 | lab --out-dir dist/ --watch 27 | ``` 28 | 29 | ### Lab Packages 30 | 31 | Lab projects can be published in a way that allows them to be installed and imported into other Lab projects from the app. 32 | 33 | Export `index.js`, `lab.json`, and `theme.json` for packaging Lab projects: 34 | 35 | ```sh 36 | lab --pkg --out-dir dist/ 37 | ``` 38 | 39 | ### CLI Options 40 | 41 | ``` 42 | -d --out-dir Output directory 43 | -w --watch Watch for changes 44 | --pkg Include index.js, lab.json, and theme.json in output 45 | ``` 46 | 47 | ## Node Usage 48 | 49 | The Node API is used by the Lab app to export lab components to React and other formats. 50 | 51 | `lab(config, [options])` 52 | 53 | Returns an array of objects for writing to files. 54 | 55 | ```js 56 | const fs = require('fs') 57 | const path = require('path') 58 | const lab = require('@compositor/lab') 59 | const config = require('./lab.json') 60 | 61 | const modules = lab(config) 62 | 63 | modules.forEach(mod => { 64 | const filename = path.join(__dirname, 'dist', mod.name + '.js') 65 | fs.writeFile(filename, mod.module, err => { 66 | if (err) console.log(err) 67 | }) 68 | }) 69 | ``` 70 | 71 | ### Options 72 | 73 | #### `library` 74 | 75 | A string key to choose an output mode, one of the following: 76 | 77 | - [`styled-components`][sc] 78 | - [`glamorous`][glamorous] 79 | - [`emotion`][emotion] 80 | - [`fela`][fela] 81 | - [`cxs`][cxs] 82 | - [`vue`][vue] (alpha) 83 | 84 | #### `harmony` 85 | 86 | Boolean to export the template without transpiling to ES5 syntax. 87 | 88 | ## Templates 89 | 90 | Currently this module uses templates to output to different formats. 91 | These templates can be found in [`lib/templates/`](https://github.com/c8r/lab-cli/tree/master/lib/templates). 92 | Using an AST for output with tools like Babel is also possible, 93 | but templates were used in an attempt to make it easier to contribute to this project. 94 | 95 | ## lab.json Data Structure 96 | 97 | See [`docs/lab-json.md`](docs/lab-json.md) 98 | 99 | ## Roadmap 100 | 101 | - Vue.js export (currently in alpha) 102 | - Atomic CSS export 103 | - Support for using third party CSS libraries 104 | 105 | [Made by Compositor](https://compositor.io) 106 | | 107 | [MIT License](LICENSE.md) 108 | 109 | [lab]: https://compositor.io/lab/ 110 | [sc]: https://styled-components.com 111 | [glamorous]: https://github.com/paypal/glamorous 112 | [emotion]: https://github.com/emotion-js/emotion 113 | [fela]: http://fela.js.org 114 | [cxs]: https://github.com/jxnblk/cxs 115 | [vue]: https://vuejs.org 116 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const fs = require('fs') 3 | const path = require('path') 4 | const meow = require('meow') 5 | const createModules = require('../lib') 6 | const writeFile = require('write-file-atomic') 7 | const chokidar = require('chokidar') 8 | const findUp = require('find-up') 9 | const chalk = require('chalk') 10 | const ora = require('ora') 11 | 12 | const pkg = require('../package.json') 13 | require('update-notifier')({ pkg }).notify() 14 | 15 | const cli = meow(` 16 | Usage 17 | $ lab --out-dir src/ 18 | $ lab d src/ --watch 19 | 20 | Options 21 | -d --out-dir Output directory 22 | -w --watch Watch for changes 23 | -l --library Override library defined in lab.json 24 | --pkg Include index.js, lab.json, and theme.json in output 25 | `, { 26 | alias: { 27 | d: 'outDir', 28 | w: 'watch', 29 | l: 'library' 30 | } 31 | }) 32 | 33 | const [ file ] = cli.input 34 | 35 | const badge = chalk.black.bgCyan(' L A B ') 36 | console.log(badge, chalk.cyan('@compositor/lab')) 37 | 38 | const spinner = ora().start() 39 | const filepath = file 40 | ? path.join(process.cwd(), file) 41 | : findUp.sync('lab.json') 42 | 43 | if (!filepath || !fs.existsSync(filepath)) { 44 | spinner.fail('no lab.json file found') 45 | process.exit(1) 46 | } 47 | 48 | if (!cli.flags.outDir) { 49 | spinner.fail('no output directory specified') 50 | process.exit(1) 51 | } 52 | 53 | const config = require(filepath) 54 | 55 | const length = Array.isArray(config.components) 56 | ? config.components.length 57 | : 0 58 | 59 | if (length) { 60 | spinner.succeed(length + ' components found') 61 | } else { 62 | spinner.fail('no components found in lab.json') 63 | process.exit(0) 64 | } 65 | 66 | const write = (file, content) => { 67 | writeFile(file, content, err => { 68 | if (err) { 69 | spinner.fail(err) 70 | process.exit(1) 71 | } 72 | }) 73 | } 74 | 75 | let cache = [] 76 | 77 | const filterChanges = component => { 78 | const cached = cache.find(c => c.name === component.name) 79 | if (!cached) return true 80 | const a = JSON.stringify(cached) 81 | const b = JSON.stringify(component) 82 | return a !== b 83 | } 84 | 85 | const parseConfig = (config, options) => { 86 | const changes = config.components 87 | .filter(filterChanges) 88 | .map(c => c.name) 89 | 90 | if (options.pkg) { 91 | changes.push('index') 92 | changes.push('lab') 93 | changes.push('theme') 94 | } 95 | 96 | const modules = createModules(config, Object.assign({}, config, options)) 97 | modules 98 | .filter(m => changes.includes(m.name)) 99 | .forEach(mod => { 100 | const filename = path.join(cli.flags.outDir, mod.name + '.js') 101 | write(filename, mod.module) 102 | spinner.succeed(filename + ' written') 103 | }) 104 | 105 | // copy theme.json and lab.json to output dir 106 | if (options.pkg) copyJSON() 107 | cache = config.components 108 | } 109 | 110 | const copyJSON = () => { 111 | const themeFile = findUp.sync('theme.json') 112 | const theme = themeFile ? require(themeFile) : null 113 | const labOutPath = path.join(cli.flags.outDir, 'lab.json') 114 | const themeOutPath = path.join(cli.flags.outDir, 'theme.json') 115 | write(labOutPath, JSON.stringify(config)) 116 | write(themeOutPath, JSON.stringify(theme)) 117 | } 118 | 119 | if (!fs.existsSync(cli.flags.outDir)) { 120 | fs.mkdirSync(cli.flags.outDir) 121 | } 122 | 123 | parseConfig(config, cli.flags) 124 | 125 | if (cli.flags.watch) { 126 | const watcher = chokidar.watch(filepath) 127 | spinner.info('watching for changes') 128 | 129 | watcher.on('change', file => { 130 | spinner.start('updating...') 131 | delete require.cache[require.resolve(file)] 132 | const next = require(file) 133 | parseConfig(next, cli.flags) 134 | spinner.succeed('updated') 135 | }) 136 | } else { 137 | spinner.succeed('done') 138 | } 139 | -------------------------------------------------------------------------------- /docs/lab-json.md: -------------------------------------------------------------------------------- 1 | 2 | # lab.json 3 | 4 | These properties are used in the `lab.json` format to create style component primitives. 5 | 6 | - `name` (string) unique name for the component, must be Capitalized and a valid JavaScript variable name 7 | - `type` (string) HTML or SVG tag name or another component name to use as an extension 8 | - `style` (object) plain JavaScript object for static CSS styles 9 | - `props` (object) default props to be passed to the component 10 | - `system` (array) array of strings that reference style functions from [styled-system][styled-system] 11 | - `examples` (array) array of JSX strings for use in documentation and testing 12 | - `description` (string) human readable description of the component 13 | - `keywords` (array) array of taxonomic strings for categorization 14 | 15 | [styled-system]: https://github.com/jxnblk/styled-system 16 | -------------------------------------------------------------------------------- /examples/lab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lab-cli", 3 | "library": "styled-components", 4 | "harmony": true, 5 | "components": [ 6 | { 7 | "name": "Button", 8 | "type": "button", 9 | "props": { 10 | "fontSize": 1, 11 | "px": 4, 12 | "py": 2, 13 | "color": "white", 14 | "bg": "blue" 15 | }, 16 | "style": { 17 | "display": "inline-block", 18 | "fontFamily": "inherit", 19 | "borderWidth": 0, 20 | "borderRadius": "4px", 21 | "appearance": "none" 22 | }, 23 | "examples": [ 24 | "" 25 | ], 26 | "system": [] 27 | }, 28 | { 29 | "name": "Heading", 30 | "type": "h2", 31 | "props": { 32 | "fontSize": 5, 33 | "m": 0, 34 | "color": "blue" 35 | }, 36 | "style": { 37 | "fontWeight": "bold", 38 | "lineHeight": 1.25 39 | }, 40 | "examples": [ 41 | "Hello" 42 | ], 43 | "system": [] 44 | }, 45 | { 46 | "name": "Box", 47 | "type": "div", 48 | "props": {}, 49 | "style": {}, 50 | "examples": [ 51 | "Hello" 52 | ] 53 | }, 54 | { 55 | "name": "Header", 56 | "imports": [ 57 | "Box", 58 | "Heading" 59 | ], 60 | "jsx": "{props.heading}", 61 | "examples": [ 62 | "
" 63 | ] 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /examples/theme.json: -------------------------------------------------------------------------------- 1 | { 2 | "fonts": [], 3 | "space": [ 4 | 0, 5 | 4, 6 | 8, 7 | 16, 8 | 32, 9 | 64, 10 | 128, 11 | 256 12 | ], 13 | "fontSizes": [ 14 | 12, 15 | 14, 16 | 16, 17 | 20, 18 | 24, 19 | 32, 20 | 48, 21 | 64, 22 | 72, 23 | 96 24 | ], 25 | "colors": { 26 | "black": "#000", 27 | "blue": "#07c" 28 | } 29 | } -------------------------------------------------------------------------------- /lib/createIndex.js: -------------------------------------------------------------------------------- 1 | module.exports = components => 2 | ['lab', 'theme', ...components.map(conf => conf.name)] 3 | .map(name => `export { default as ${name} } from './${name}'`) 4 | .join('\n') 5 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const { format } = require('prettier') 2 | const { transform } = require('babel-core') 3 | const createIndex = require('./createIndex') 4 | const templates = require('./templates') 5 | const compositeReact = require('./templates/composite-react') 6 | const compositeVue = require('./templates/composite-vue') 7 | const { 8 | stringifyObject, 9 | getExtensionImport, 10 | getComp, 11 | getSystemFuncs, 12 | getSystemExpressions 13 | } = require('./templates/util') 14 | 15 | const compile = code => 16 | transform(code, { 17 | presets: [ 18 | require('babel-preset-env'), 19 | require('babel-preset-stage-0'), 20 | require('babel-preset-react') 21 | ] 22 | }).code 23 | 24 | const parse = (config) => { 25 | const { 26 | style = {}, 27 | props = {}, 28 | system = [], 29 | type = 'div' 30 | } = config 31 | return Object.assign({}, config, { 32 | style, 33 | props, 34 | systemFunctions: getSystemFuncs(system), 35 | systemExpressions: getSystemExpressions(system), 36 | extensionImport: getExtensionImport(type), 37 | type: getComp(type), 38 | styleString: stringifyObject(style), 39 | propsString: stringifyObject(props) 40 | }) 41 | } 42 | 43 | const composite = (parsed, library) => 44 | library === 'vue' ? compositeVue(parsed) : compositeReact(parsed) 45 | 46 | module.exports = (config, options = {}) => { 47 | const components = Array.isArray(config) ? config : config.components 48 | const { 49 | library = 'styled-components', 50 | harmony 51 | } = options 52 | 53 | const template = options.template || templates[library] || templates['styled-components'] 54 | 55 | const index = { 56 | name: 'index', 57 | module: harmony ? createIndex(components) : compile(createIndex(components)) 58 | } 59 | 60 | const mods = components.map(conf => { 61 | const parsed = parse(conf) 62 | 63 | const code = isComposite(parsed) ? composite(parsed, library) : template(parsed) 64 | 65 | const mod = harmony 66 | ? format(code, { 67 | semi: false, 68 | singleQuote: true 69 | }) 70 | : compile(code) 71 | 72 | return { 73 | name: conf.name, 74 | module: mod 75 | } 76 | }) 77 | 78 | return [index, ...mods] 79 | } 80 | 81 | const isComposite = comp => comp.imports && comp.jsx 82 | -------------------------------------------------------------------------------- /lib/templates/composite-react.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | jsx, 4 | imports = [] 5 | }) => `import React from 'react' 6 | ${imports.map(name => `import ${name} from './${name}'`).join('\n')} 7 | 8 | const ${name} = props => ( 9 | ${jsx} 10 | ) 11 | 12 | export default ${name}` 13 | -------------------------------------------------------------------------------- /lib/templates/composite-vue.js: -------------------------------------------------------------------------------- 1 | const uniq = require('lodash.uniq') 2 | const flatten = require('lodash.flatten') 3 | const getProps = require('@compositor/get-jsx-props') 4 | 5 | module.exports = ({ 6 | name, 7 | jsx, 8 | imports = [], 9 | examples = [] 10 | }) => { 11 | const props = uniq(flatten(examples.map(getProps)).map(s => s.prop)) 12 | 13 | return ` 14 | ${imports.map(name => `import ${name} from './${name}'`).join('\n')} 15 | 16 | export default { 17 | name: '${name}', 18 | props: ${JSON.stringify(props)}, 19 | render (h) { 20 | return ( 21 | ${jsx.replace(/props/g, 'this')} 22 | ) 23 | } 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /lib/templates/cxs.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | systemFunctions, 4 | extensionImport, 5 | type, 6 | styleString, 7 | propsString 8 | }) => `import styled from 'cxs/component' 9 | import { 10 | space, 11 | fontSize, 12 | width, 13 | color, 14 | ${systemFunctions} 15 | } from 'styled-system' 16 | ${extensionImport} 17 | 18 | const ${name} = styled(${type})(props => (${styleString}), 19 | space, 20 | fontSize, 21 | width, 22 | color, 23 | ${systemFunctions} 24 | ) 25 | 26 | ${name}.defaultProps = ${propsString} 27 | 28 | export default ${name}` 29 | -------------------------------------------------------------------------------- /lib/templates/emotion.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | type, 4 | styleString, 5 | propsString, 6 | systemFunctions, 7 | extensionImport 8 | }) => `import styled from 'react-emotion' 9 | import { withTheme } from 'theming' 10 | import { 11 | space, 12 | fontSize, 13 | width, 14 | color, 15 | ${systemFunctions} 16 | } from 'styled-system' 17 | ${extensionImport} 18 | 19 | const ${name} = withTheme(styled(${type})(props => (${styleString}), 20 | space, 21 | fontSize, 22 | width, 23 | color, 24 | ${systemFunctions} 25 | )) 26 | 27 | ${name}.defaultProps = ${propsString} 28 | 29 | export default ${name}` 30 | -------------------------------------------------------------------------------- /lib/templates/fela.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | type, 4 | propsString, 5 | styleString, 6 | system = [], 7 | systemFunctions, 8 | extensionImport 9 | }) => `import { createComponent } from 'react-fela' 10 | import { 11 | space, 12 | fontSize, 13 | width, 14 | color, 15 | ${systemFunctions} 16 | } from 'styled-system' 17 | ${extensionImport} 18 | 19 | const ${name} = createComponent(props => Object.assign(${styleString}, 20 | space(props), 21 | fontSize(props), 22 | width(props), 23 | color(props), 24 | ${system.map(fn => `${fn}(props)`).join(',\nss')} 25 | ), ${type}) 26 | 27 | ${name}.defaultProps = ${propsString} 28 | 29 | export default ${name}` 30 | -------------------------------------------------------------------------------- /lib/templates/glamorous.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | type, 4 | propsString, 5 | styleString, 6 | systemFunctions, 7 | extensionImport 8 | }) => `import styled from 'glamorous' 9 | import { 10 | space, 11 | fontSize, 12 | width, 13 | color, 14 | ${systemFunctions} 15 | } from 'styled-system' 16 | ${extensionImport} 17 | 18 | const ${name} = styled(${type})(props => (${styleString}), 19 | space, 20 | fontSize, 21 | width, 22 | color, 23 | ${systemFunctions} 24 | ) 25 | 26 | ${name}.defaultProps = ${propsString} 27 | 28 | export default ${name}` 29 | -------------------------------------------------------------------------------- /lib/templates/index.js: -------------------------------------------------------------------------------- 1 | const compositeReact = require('./composite-react') 2 | const compositeVue = require('./composite-vue') 3 | const styledComponents = require('./styledComponents') 4 | const glamorous = require('./glamorous') 5 | const emotion = require('./emotion') 6 | const cxs = require('./cxs') 7 | const fela = require('./fela') 8 | const vue = require('./vue') 9 | 10 | module.exports = { 11 | // React 12 | compositeReact, 13 | 'styled-components': styledComponents, 14 | glamorous, 15 | emotion, 16 | cxs, 17 | fela, 18 | 19 | // Vue 20 | compositeVue, 21 | vue 22 | } 23 | -------------------------------------------------------------------------------- /lib/templates/styledComponents.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | name, 3 | type, 4 | styleString, 5 | propsString, 6 | systemFunctions, 7 | extensionImport 8 | }) => `import styled from 'styled-components' 9 | import { 10 | space, 11 | fontSize, 12 | width, 13 | color, 14 | ${systemFunctions} 15 | } from 'styled-system' 16 | ${extensionImport} 17 | 18 | const ${name} = styled(${type})([], props => (${styleString}), 19 | space, 20 | fontSize, 21 | width, 22 | color, 23 | ${systemFunctions} 24 | ) 25 | 26 | ${name}.defaultProps = ${propsString} 27 | 28 | export default ${name}` 29 | -------------------------------------------------------------------------------- /lib/templates/util.js: -------------------------------------------------------------------------------- 1 | const _stringifyObject = require('stringify-object') 2 | 3 | const SREG = /\$\{.*\}/ 4 | const transform = (obj, prop, original) => { 5 | const val = obj[prop] 6 | if (SREG.test(val)) { 7 | return original 8 | .replace(/^'/, '`') 9 | .replace(/'$/, '`') 10 | .replace(/\\'/g, `'`) 11 | } 12 | return original 13 | } 14 | 15 | const stringifyOptions = { 16 | indent: ' ', 17 | transform 18 | } 19 | 20 | const stringifyObject = obj => _stringifyObject(obj, stringifyOptions) 21 | 22 | const CREG = /^[A-Z]/ 23 | const getExtensionImport = type => 24 | CREG.test(type) ? `import ${type} from './${type}'\n` : '' 25 | 26 | const getComp = type => (CREG.test(type) ? type : "'" + type + "'") 27 | const getSystemFuncs = funcs => funcs.join(',\n') 28 | const getSystemExpressions = funcs => funcs.map(f => '${' + f + '}').join('\n') 29 | 30 | const EXPRESSION_REG = /^\${(.*)}$/ 31 | const isTemplateExpression = str => EXPRESSION_REG.test(str) 32 | 33 | module.exports = { 34 | stringifyObject, 35 | getExtensionImport, 36 | getComp, 37 | getSystemFuncs, 38 | getSystemExpressions, 39 | isTemplateExpression 40 | } 41 | -------------------------------------------------------------------------------- /lib/templates/vue.js: -------------------------------------------------------------------------------- 1 | const { 2 | isTemplateExpression 3 | } = require('./util') 4 | 5 | module.exports = ({ 6 | name, 7 | systemFunctions, 8 | systemExpressions = '', 9 | extensionImport, 10 | type, 11 | style, 12 | props 13 | }) => `import styled from 'vue-styled-components' 14 | import system from 'vue-styled-system' 15 | 16 | import { 17 | space, 18 | fontSize, 19 | width, 20 | color, 21 | ${systemFunctions} 22 | } from 'styled-system' 23 | 24 | import theme from './theme.json' 25 | ${extensionImport} 26 | export default styled(${type}, system({ 27 | ${formatProps(props)} 28 | theme: { default: () => theme } 29 | }))\` 30 | ${formatStyle(style)} 31 | \${space} 32 | \${fontSize} 33 | \${width} 34 | \${color} 35 | ${systemExpressions} 36 | \` 37 | ` 38 | 39 | const formatProps = props => 40 | Object 41 | .keys(props) 42 | .reduce((acc, key) => 43 | acc.concat(`${key}: { default: ${formatValue(props[key])} },`) 44 | , []) 45 | .join('\n') 46 | 47 | const formatValue = value => { 48 | switch(typeof value) { 49 | case 'string': 50 | return isTemplateExpression(value) ? value : `'${value}'` 51 | case 'object': 52 | return `() => (${JSON.stringify(value, null, 2)})` 53 | default: 54 | return value 55 | } 56 | } 57 | 58 | const formatStyle = style => 59 | Object.keys(style).reduce((acc, key) => { 60 | const val = style[key] 61 | const parsedVal = typeof val === 'string' ? val.replace(/\${\s*/g, '${ props => ') : val 62 | return acc.concat(` ${key}: ${formatValue(parsedVal)}`) 63 | } , []).join(',\n') 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@compositor/lab", 3 | "version": "1.0.0-37", 4 | "description": "CLI utilities for Compositor Lab", 5 | "main": "lib/index.js", 6 | "bin": { 7 | "lab": "bin/cli.js" 8 | }, 9 | "scripts": { 10 | "test": "nyc ava", 11 | "cover": "nyc report --reporter=html --reporter=lcov", 12 | "build": "./bin/cli.js examples/lab.json --pkg -d examples/dist" 13 | }, 14 | "keywords": [ 15 | "compositor", 16 | "lab", 17 | "components", 18 | "design", 19 | "system" 20 | ], 21 | "author": "Brent Jackson", 22 | "license": "MIT", 23 | "dependencies": { 24 | "@compositor/get-jsx-props": "0.0.4", 25 | "babel-core": "^6.26.0", 26 | "babel-preset-env": "^1.6.1", 27 | "babel-preset-react": "^6.24.1", 28 | "babel-preset-stage-0": "^6.24.1", 29 | "chalk": "^2.1.0", 30 | "chokidar": "^1.7.0", 31 | "find-up": "^2.1.0", 32 | "indent": "0.0.2", 33 | "lodash.flatten": "^4.4.0", 34 | "lodash.uniq": "^4.5.0", 35 | "meow": "^3.7.0", 36 | "ora": "^1.3.0", 37 | "prettier": "^1.7.4", 38 | "react": "^16.0.0", 39 | "stringify-object": "^3.2.1", 40 | "update-notifier": "^2.2.0", 41 | "write-file-atomic": "^2.3.0" 42 | }, 43 | "devDependencies": { 44 | "ava": "^0.22.0", 45 | "nyc": "^11.2.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mono", 3 | "library": "glamorous", 4 | "components": [ 5 | { 6 | "name": "Avatar", 7 | "type": "img", 8 | "props": { 9 | "src": "https://pbs.twimg.com/profile_images/854537128994275328/nVZX1MEh_400x400.jpg", 10 | "size": "64px", 11 | "borderWidth": 2, 12 | "borderRadius": 9999, 13 | "px": 1, 14 | "py": 1, 15 | "borderColor": "gray1", 16 | "bg": "transparent", 17 | "color": "transparent" 18 | }, 19 | "style": { 20 | "height": "${props.size? props.size: '48px'}", 21 | "width": "${props.size? props.size: '48px'}" 22 | }, 23 | "examples": [ 24 | "", 25 | "", 26 | "", 27 | "", 28 | "", 29 | "", 30 | "", 31 | "", 32 | "", 33 | "" 34 | ], 35 | "system": [ 36 | "borderWidth", 37 | "borderRadius", 38 | "borderColor" 39 | ] 40 | }, 41 | { 42 | "name": "Button", 43 | "type": "button", 44 | "props": { 45 | "fontSize": 1, 46 | "px": 4, 47 | "py": 2, 48 | "mr": 2, 49 | "color": "white", 50 | "bg": "blue" 51 | }, 52 | "style": { 53 | "display": "inline-block", 54 | "fontFamily": "inherit", 55 | "borderWidth": 0, 56 | "borderRadius": "4px", 57 | "appearance": "none" 58 | }, 59 | "examples": [ 60 | "", 61 | "", 62 | "" 63 | ], 64 | "system": [] 65 | }, 66 | { 67 | "name": "ButtonOutline", 68 | "type": "button", 69 | "props": { 70 | "fontSize": 1, 71 | "px": 4, 72 | "py": 2, 73 | "color": "blue", 74 | "bg": "transparent", 75 | "borderWidth": 2, 76 | "borderColor": "currentColor" 77 | }, 78 | "style": { 79 | "display": "inline-block", 80 | "fontFamily": "inherit", 81 | "borderRadius": "4px", 82 | "appearance": "none", 83 | "boxSizing": "border-box" 84 | }, 85 | "examples": [ 86 | "Hello" 87 | ], 88 | "system": [ 89 | "borderWidth", 90 | "borderColor" 91 | ] 92 | }, 93 | { 94 | "name": "Link", 95 | "type": "a", 96 | "props": { 97 | "color": "inherit", 98 | "hover": { 99 | "opacity": 0.66, 100 | "transition": "opacity .25s ease-in" 101 | } 102 | }, 103 | "style": { 104 | "textAlign": "${props.center? 'center': 'left'}", 105 | "textDecoration": "${props.underline? 'underline': 'none'}", 106 | "opacity": 1, 107 | "transition": "opacity .3s ease-in", 108 | "display": "inline-block" 109 | }, 110 | "examples": [ 111 | "Read our docs", 112 | "Read our docs" 113 | ], 114 | "system": [ 115 | "hover" 116 | ] 117 | }, 118 | { 119 | "name": "LinkBox", 120 | "type": "a", 121 | "props": { 122 | "color": "black", 123 | "fontSize": 2, 124 | "borderWidth": 1, 125 | "borderColor": "black", 126 | "px": 2, 127 | "py": 1 128 | }, 129 | "style": { 130 | "textDecoration": "none" 131 | }, 132 | "examples": [ 133 | "1" 134 | ], 135 | "system": [ 136 | "borderWidth", 137 | "borderColor" 138 | ] 139 | }, 140 | { 141 | "name": "Img", 142 | "type": "img", 143 | "props": {}, 144 | "style": { 145 | "display": "block", 146 | "maxWidth": "100%", 147 | "width": "100%" 148 | }, 149 | "examples": [ 150 | "", 151 | "", 152 | "", 153 | "" 154 | ], 155 | "system": [] 156 | }, 157 | { 158 | "name": "Hr", 159 | "type": "hr", 160 | "props": { 161 | "bg": "transparent", 162 | "color": "black", 163 | "pl": 0, 164 | "pr": 0, 165 | "pb": 0, 166 | "borderRadius": 0, 167 | "borderWidth": 1, 168 | "borderTop": true 169 | }, 170 | "style": { 171 | "height": 0 172 | }, 173 | "examples": [ 174 | "
" 175 | ], 176 | "system": [ 177 | "borderRadius", 178 | "borderWidth" 179 | ] 180 | }, 181 | { 182 | "name": "Text", 183 | "type": "div", 184 | "props": {}, 185 | "style": {}, 186 | "examples": [ 187 | "Hello" 188 | ] 189 | }, 190 | { 191 | "name": "Heading", 192 | "type": "h2", 193 | "props": { 194 | "fontSize": 4, 195 | "m": 0, 196 | "fontWeight": "600" 197 | }, 198 | "style": {}, 199 | "examples": [ 200 | "Hello" 201 | ], 202 | "system": [ 203 | "fontWeight", 204 | "borderColor", 205 | "borderWidth" 206 | ] 207 | }, 208 | { 209 | "name": "P", 210 | "type": "p", 211 | "props": {}, 212 | "style": { 213 | "maxWidth": "${props.wide? '34em': '28em'}", 214 | "lineHeight": "1.5" 215 | }, 216 | "examples": [ 217 | "

\rIMMACULATE typography is certainly the most brittle of all the arts. To create a whole from many petrified, dis- connected and given parts, to make this whole appear alive and of a piece — only sculpture in stone approaches the unyielding stiffness of perfect typography. For most people, even impeccable typography does not hold any particular aesthetic appeal. In its inaccessibility, it resembles great\rmusic. Under the best of circumstances, it is gratefully , accepted. To remain nameless and without specific appre- ciation, yet to have been of service to a valuable work and to the small number of visually sensitive readers — this, as a rule, is the only compensation for the long, and indeed never- ending, indenture of the typographer.

", 218 | "

\rA very short example of a paragraph.

" 219 | ], 220 | "system": [] 221 | }, 222 | { 223 | "name": "H1", 224 | "type": "h1", 225 | "props": { 226 | "fontWeight": "600" 227 | }, 228 | "style": { 229 | "textAlign": "${props.center? 'center': 'left'}" 230 | }, 231 | "examples": [ 232 | "

Heading Number 1

", 233 | "

Heading Number 1

" 234 | ], 235 | "system": [ 236 | "fontWeight" 237 | ] 238 | }, 239 | { 240 | "name": "H2", 241 | "type": "h2", 242 | "props": { 243 | "fontWeight": "600" 244 | }, 245 | "style": { 246 | "textAlign": "${props.center? 'center': 'left'}" 247 | }, 248 | "examples": [ 249 | "

Heading Number 2

", 250 | "

Centered Heading Number 2

" 251 | ], 252 | "system": [ 253 | "fontWeight" 254 | ] 255 | }, 256 | { 257 | "name": "H3", 258 | "type": "h3", 259 | "props": { 260 | "fontWeight": "600" 261 | }, 262 | "style": { 263 | "textAlign": "${props.center? 'center': 'left'}" 264 | }, 265 | "examples": [ 266 | "

Heading Number 3

" 267 | ], 268 | "system": [ 269 | "fontWeight" 270 | ] 271 | }, 272 | { 273 | "name": "H4", 274 | "type": "h4", 275 | "props": { 276 | "fontWeight": "600" 277 | }, 278 | "style": { 279 | "textAlign": "${props.center? 'center': 'left'}" 280 | }, 281 | "examples": [ 282 | "

Heading Number 4

" 283 | ], 284 | "system": [ 285 | "fontWeight" 286 | ] 287 | }, 288 | { 289 | "name": "Div", 290 | "type": "div", 291 | "props": {}, 292 | "style": {}, 293 | "examples": [ 294 | "
A generic div component that now has the super powers of styled-system.
", 295 | "
A generic div component that now has the super powers of styled-system.
", 296 | "
A generic div component that now has the super powers of styled-system.
" 297 | ], 298 | "system": [] 299 | }, 300 | { 301 | "name": "Container", 302 | "type": "div", 303 | "props": { 304 | "mx": "auto" 305 | }, 306 | "style": { 307 | "maxWidth": "64em", 308 | "boxSizing": "border-box" 309 | }, 310 | "examples": [ 311 | "A container for centering your page content. " 312 | ], 313 | "system": [] 314 | }, 315 | { 316 | "name": "Input", 317 | "type": "input", 318 | "props": { 319 | "m": 0, 320 | "w": 1, 321 | "px": 2, 322 | "py": 2, 323 | "color": "inherit", 324 | "bg": "transparent", 325 | "borderColor": "gray4", 326 | "borderWidth": 1, 327 | "fontSize": null, 328 | "focus": { 329 | "outline": 0 330 | }, 331 | "borderRadius": 2 332 | }, 333 | "style": { 334 | "display": "block", 335 | "fontFamily": "inherit", 336 | "fontSize": "inherit" 337 | }, 338 | "examples": [ 339 | "" 340 | ], 341 | "system": [ 342 | "borderColor", 343 | "borderWidth", 344 | "focus", 345 | "borderRadius" 346 | ] 347 | }, 348 | { 349 | "name": "Label", 350 | "type": "label", 351 | "props": { 352 | "fontSize": 1, 353 | "fontWeight": "600" 354 | }, 355 | "style": { 356 | "display": "block" 357 | }, 358 | "examples": [ 359 | "" 360 | ], 361 | "system": [ 362 | "fontWeight" 363 | ] 364 | }, 365 | { 366 | "name": "TextInput", 367 | "imports": [ 368 | "Label", 369 | "Div", 370 | "Input", 371 | "P" 372 | ], 373 | "jsx": "
\n
", 374 | "examples": [ 375 | "", 376 | "", 377 | "" 378 | ], 379 | "system": [] 380 | }, 381 | { 382 | "name": "Ul", 383 | "type": "ul", 384 | "props": { 385 | "m": 0, 386 | "p": 0, 387 | "borderColor": "transparent", 388 | "borderWidth": 1 389 | }, 390 | "style": { 391 | "listStyleType": "none" 392 | }, 393 | "examples": [ 394 | "
    \n
  • hi
  • \n
  • there
  • \n
" 395 | ], 396 | "system": [ 397 | "borderColor", 398 | "borderWidth" 399 | ] 400 | }, 401 | { 402 | "name": "Li", 403 | "type": "li", 404 | "props": { 405 | "borderWidth": 1, 406 | "borderColor": "transparent" 407 | }, 408 | "style": {}, 409 | "examples": [ 410 | "
  • Hello
  • " 411 | ], 412 | "system": [ 413 | "borderWidth", 414 | "borderColor" 415 | ] 416 | } 417 | ] 418 | } -------------------------------------------------------------------------------- /test/modules.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import createModules from '../lib' 3 | 4 | const config = [ 5 | { 6 | name: 'Box', 7 | type: 'div', 8 | props: {}, 9 | style: {} 10 | }, 11 | { 12 | name: 'Text', 13 | type: 'p', 14 | props: { 15 | m: 0 16 | }, 17 | style: {} 18 | }, 19 | { 20 | name: 'Hello', 21 | imports: ['Box', 'Text'], 22 | jsx: '' 23 | }, 24 | { 25 | name: 'Beep', 26 | type: 'Box', 27 | props: { 28 | p: 3, 29 | bg: 'tomato' 30 | }, 31 | style: {} 32 | }, 33 | { 34 | name: 'Flex', 35 | type: 'Box', 36 | props: {}, 37 | style: { 38 | display: 'flex' 39 | }, 40 | system: ['alignItems', 'justifyContent', 'flexDirection', 'flexWrap'] 41 | } 42 | ] 43 | 44 | test('returns an array of objects', t => { 45 | const mods = createModules(config) 46 | t.true(Array.isArray(mods)) 47 | const [a, b, c, d, e, f] = mods 48 | t.is(typeof a, 'object') 49 | t.is(typeof b, 'object') 50 | t.is(typeof c, 'object') 51 | t.is(typeof d, 'object') 52 | t.is(typeof e, 'object') 53 | t.is(typeof f, 'object') 54 | t.is(a.name, 'index') 55 | t.is(b.name, 'Box') 56 | t.is(c.name, 'Text') 57 | t.is(d.name, 'Hello') 58 | t.is(e.name, 'Beep') 59 | t.is(f.name, 'Flex') 60 | t.is(typeof a.module, 'string') 61 | t.is(typeof b.module, 'string') 62 | t.is(typeof c.module, 'string') 63 | t.is(typeof d.module, 'string') 64 | t.is(typeof e.module, 'string') 65 | t.is(typeof f.module, 'string') 66 | t.snapshot(a) 67 | t.snapshot(b) 68 | t.snapshot(c) 69 | t.snapshot(d) 70 | t.snapshot(e) 71 | t.snapshot(f) 72 | }) 73 | 74 | test('accepts a library option', t => { 75 | const mods = createModules(config, { 76 | library: 'glamorous' 77 | }) 78 | const [a, b] = mods 79 | t.is(typeof a, 'object') 80 | t.is(typeof b, 'object') 81 | t.snapshot(a) 82 | t.snapshot(b) 83 | }) 84 | 85 | test('accepts a custom template option', t => { 86 | const template = () => `module.exports = 'hello'` 87 | const [ a, b ] = createModules(config, { 88 | harmony: true, 89 | template 90 | }) 91 | t.is(b.module.trim(), `module.exports = 'hello'`) 92 | }) 93 | -------------------------------------------------------------------------------- /test/snapshots/modules.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/modules.js` 2 | 3 | The actual snapshot is saved in `modules.js.snap`. 4 | 5 | Generated by [AVA](https://ava.li). 6 | 7 | ## accepts a library option 8 | 9 | > Snapshot 1 10 | 11 | { 12 | module: `'use strict';␊ 13 | ␊ 14 | Object.defineProperty(exports, "__esModule", {␊ 15 | value: true␊ 16 | });␊ 17 | ␊ 18 | var _lab = require('./lab');␊ 19 | ␊ 20 | Object.defineProperty(exports, 'lab', {␊ 21 | enumerable: true,␊ 22 | get: function get() {␊ 23 | return _interopRequireDefault(_lab).default;␊ 24 | }␊ 25 | });␊ 26 | ␊ 27 | var _theme = require('./theme');␊ 28 | ␊ 29 | Object.defineProperty(exports, 'theme', {␊ 30 | enumerable: true,␊ 31 | get: function get() {␊ 32 | return _interopRequireDefault(_theme).default;␊ 33 | }␊ 34 | });␊ 35 | ␊ 36 | var _Box = require('./Box');␊ 37 | ␊ 38 | Object.defineProperty(exports, 'Box', {␊ 39 | enumerable: true,␊ 40 | get: function get() {␊ 41 | return _interopRequireDefault(_Box).default;␊ 42 | }␊ 43 | });␊ 44 | ␊ 45 | var _Text = require('./Text');␊ 46 | ␊ 47 | Object.defineProperty(exports, 'Text', {␊ 48 | enumerable: true,␊ 49 | get: function get() {␊ 50 | return _interopRequireDefault(_Text).default;␊ 51 | }␊ 52 | });␊ 53 | ␊ 54 | var _Hello = require('./Hello');␊ 55 | ␊ 56 | Object.defineProperty(exports, 'Hello', {␊ 57 | enumerable: true,␊ 58 | get: function get() {␊ 59 | return _interopRequireDefault(_Hello).default;␊ 60 | }␊ 61 | });␊ 62 | ␊ 63 | var _Beep = require('./Beep');␊ 64 | ␊ 65 | Object.defineProperty(exports, 'Beep', {␊ 66 | enumerable: true,␊ 67 | get: function get() {␊ 68 | return _interopRequireDefault(_Beep).default;␊ 69 | }␊ 70 | });␊ 71 | ␊ 72 | var _Flex = require('./Flex');␊ 73 | ␊ 74 | Object.defineProperty(exports, 'Flex', {␊ 75 | enumerable: true,␊ 76 | get: function get() {␊ 77 | return _interopRequireDefault(_Flex).default;␊ 78 | }␊ 79 | });␊ 80 | ␊ 81 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }`, 82 | name: 'index', 83 | } 84 | 85 | > Snapshot 2 86 | 87 | { 88 | module: `'use strict';␊ 89 | ␊ 90 | Object.defineProperty(exports, "__esModule", {␊ 91 | value: true␊ 92 | });␊ 93 | ␊ 94 | var _glamorous = require('glamorous');␊ 95 | ␊ 96 | var _glamorous2 = _interopRequireDefault(_glamorous);␊ 97 | ␊ 98 | var _styledSystem = require('styled-system');␊ 99 | ␊ 100 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 101 | ␊ 102 | var Box = (0, _glamorous2.default)('div')(function (props) {␊ 103 | return {};␊ 104 | }, _styledSystem.space, _styledSystem.fontSize, _styledSystem.width, _styledSystem.color);␊ 105 | ␊ 106 | Box.defaultProps = {};␊ 107 | ␊ 108 | exports.default = Box;`, 109 | name: 'Box', 110 | } 111 | 112 | ## returns an array of objects 113 | 114 | > Snapshot 1 115 | 116 | { 117 | module: `'use strict';␊ 118 | ␊ 119 | Object.defineProperty(exports, "__esModule", {␊ 120 | value: true␊ 121 | });␊ 122 | ␊ 123 | var _lab = require('./lab');␊ 124 | ␊ 125 | Object.defineProperty(exports, 'lab', {␊ 126 | enumerable: true,␊ 127 | get: function get() {␊ 128 | return _interopRequireDefault(_lab).default;␊ 129 | }␊ 130 | });␊ 131 | ␊ 132 | var _theme = require('./theme');␊ 133 | ␊ 134 | Object.defineProperty(exports, 'theme', {␊ 135 | enumerable: true,␊ 136 | get: function get() {␊ 137 | return _interopRequireDefault(_theme).default;␊ 138 | }␊ 139 | });␊ 140 | ␊ 141 | var _Box = require('./Box');␊ 142 | ␊ 143 | Object.defineProperty(exports, 'Box', {␊ 144 | enumerable: true,␊ 145 | get: function get() {␊ 146 | return _interopRequireDefault(_Box).default;␊ 147 | }␊ 148 | });␊ 149 | ␊ 150 | var _Text = require('./Text');␊ 151 | ␊ 152 | Object.defineProperty(exports, 'Text', {␊ 153 | enumerable: true,␊ 154 | get: function get() {␊ 155 | return _interopRequireDefault(_Text).default;␊ 156 | }␊ 157 | });␊ 158 | ␊ 159 | var _Hello = require('./Hello');␊ 160 | ␊ 161 | Object.defineProperty(exports, 'Hello', {␊ 162 | enumerable: true,␊ 163 | get: function get() {␊ 164 | return _interopRequireDefault(_Hello).default;␊ 165 | }␊ 166 | });␊ 167 | ␊ 168 | var _Beep = require('./Beep');␊ 169 | ␊ 170 | Object.defineProperty(exports, 'Beep', {␊ 171 | enumerable: true,␊ 172 | get: function get() {␊ 173 | return _interopRequireDefault(_Beep).default;␊ 174 | }␊ 175 | });␊ 176 | ␊ 177 | var _Flex = require('./Flex');␊ 178 | ␊ 179 | Object.defineProperty(exports, 'Flex', {␊ 180 | enumerable: true,␊ 181 | get: function get() {␊ 182 | return _interopRequireDefault(_Flex).default;␊ 183 | }␊ 184 | });␊ 185 | ␊ 186 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }`, 187 | name: 'index', 188 | } 189 | 190 | > Snapshot 2 191 | 192 | { 193 | module: `'use strict';␊ 194 | ␊ 195 | Object.defineProperty(exports, "__esModule", {␊ 196 | value: true␊ 197 | });␊ 198 | ␊ 199 | var _styledComponents = require('styled-components');␊ 200 | ␊ 201 | var _styledComponents2 = _interopRequireDefault(_styledComponents);␊ 202 | ␊ 203 | var _styledSystem = require('styled-system');␊ 204 | ␊ 205 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 206 | ␊ 207 | var Box = (0, _styledComponents2.default)('div')([], function (props) {␊ 208 | return {};␊ 209 | }, _styledSystem.space, _styledSystem.fontSize, _styledSystem.width, _styledSystem.color);␊ 210 | ␊ 211 | Box.defaultProps = {};␊ 212 | ␊ 213 | exports.default = Box;`, 214 | name: 'Box', 215 | } 216 | 217 | > Snapshot 3 218 | 219 | { 220 | module: `'use strict';␊ 221 | ␊ 222 | Object.defineProperty(exports, "__esModule", {␊ 223 | value: true␊ 224 | });␊ 225 | ␊ 226 | var _styledComponents = require('styled-components');␊ 227 | ␊ 228 | var _styledComponents2 = _interopRequireDefault(_styledComponents);␊ 229 | ␊ 230 | var _styledSystem = require('styled-system');␊ 231 | ␊ 232 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 233 | ␊ 234 | var Text = (0, _styledComponents2.default)('p')([], function (props) {␊ 235 | return {};␊ 236 | }, _styledSystem.space, _styledSystem.fontSize, _styledSystem.width, _styledSystem.color);␊ 237 | ␊ 238 | Text.defaultProps = {␊ 239 | m: 0␊ 240 | };␊ 241 | ␊ 242 | exports.default = Text;`, 243 | name: 'Text', 244 | } 245 | 246 | > Snapshot 4 247 | 248 | { 249 | module: `'use strict';␊ 250 | ␊ 251 | Object.defineProperty(exports, "__esModule", {␊ 252 | value: true␊ 253 | });␊ 254 | ␊ 255 | var _react = require('react');␊ 256 | ␊ 257 | var _react2 = _interopRequireDefault(_react);␊ 258 | ␊ 259 | var _Box = require('./Box');␊ 260 | ␊ 261 | var _Box2 = _interopRequireDefault(_Box);␊ 262 | ␊ 263 | var _Text = require('./Text');␊ 264 | ␊ 265 | var _Text2 = _interopRequireDefault(_Text);␊ 266 | ␊ 267 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 268 | ␊ 269 | var Hello = function Hello(props) {␊ 270 | return _react2.default.createElement(␊ 271 | _Box2.default,␊ 272 | null,␊ 273 | _react2.default.createElement(_Text2.default, null)␊ 274 | );␊ 275 | };␊ 276 | ␊ 277 | exports.default = Hello;`, 278 | name: 'Hello', 279 | } 280 | 281 | > Snapshot 5 282 | 283 | { 284 | module: `'use strict';␊ 285 | ␊ 286 | Object.defineProperty(exports, "__esModule", {␊ 287 | value: true␊ 288 | });␊ 289 | ␊ 290 | var _styledComponents = require('styled-components');␊ 291 | ␊ 292 | var _styledComponents2 = _interopRequireDefault(_styledComponents);␊ 293 | ␊ 294 | var _styledSystem = require('styled-system');␊ 295 | ␊ 296 | var _Box = require('./Box');␊ 297 | ␊ 298 | var _Box2 = _interopRequireDefault(_Box);␊ 299 | ␊ 300 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 301 | ␊ 302 | var Beep = (0, _styledComponents2.default)(_Box2.default)([], function (props) {␊ 303 | return {};␊ 304 | }, _styledSystem.space, _styledSystem.fontSize, _styledSystem.width, _styledSystem.color);␊ 305 | ␊ 306 | Beep.defaultProps = {␊ 307 | p: 3,␊ 308 | bg: 'tomato'␊ 309 | };␊ 310 | ␊ 311 | exports.default = Beep;`, 312 | name: 'Beep', 313 | } 314 | 315 | > Snapshot 6 316 | 317 | { 318 | module: `'use strict';␊ 319 | ␊ 320 | Object.defineProperty(exports, "__esModule", {␊ 321 | value: true␊ 322 | });␊ 323 | ␊ 324 | var _styledComponents = require('styled-components');␊ 325 | ␊ 326 | var _styledComponents2 = _interopRequireDefault(_styledComponents);␊ 327 | ␊ 328 | var _styledSystem = require('styled-system');␊ 329 | ␊ 330 | var _Box = require('./Box');␊ 331 | ␊ 332 | var _Box2 = _interopRequireDefault(_Box);␊ 333 | ␊ 334 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }␊ 335 | ␊ 336 | var Flex = (0, _styledComponents2.default)(_Box2.default)([], function (props) {␊ 337 | return {␊ 338 | display: 'flex'␊ 339 | };␊ 340 | }, _styledSystem.space, _styledSystem.fontSize, _styledSystem.width, _styledSystem.color, _styledSystem.alignItems, _styledSystem.justifyContent, _styledSystem.flexDirection, _styledSystem.flexWrap);␊ 341 | ␊ 342 | Flex.defaultProps = {};␊ 343 | ␊ 344 | exports.default = Flex;`, 345 | name: 'Flex', 346 | } 347 | -------------------------------------------------------------------------------- /test/snapshots/modules.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/c8r/lab-cli/557bc57835fae30cee5465a6ac10633c628560aa/test/snapshots/modules.js.snap -------------------------------------------------------------------------------- /test/snapshots/templates.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/templates.js` 2 | 3 | The actual snapshot is saved in `templates.js.snap`. 4 | 5 | Generated by [AVA](https://ava.li). 6 | 7 | ## composite has defaults 8 | 9 | > Snapshot 1 10 | 11 | `import React from 'react'␊ 12 | ␊ 13 | ␊ 14 | const Hello = props => (␊ 15 | undefined␊ 16 | )␊ 17 | ␊ 18 | export default Hello` 19 | 20 | ## composite imports required components 21 | 22 | > Snapshot 1 23 | 24 | `import React from 'react'␊ 25 | import Box from './Box'␊ 26 | import Text from './Text'␊ 27 | ␊ 28 | const Hello = props => (␊ 29 | {props.children}␊ 30 | )␊ 31 | ␊ 32 | export default Hello` 33 | 34 | ## composite returns a string 35 | 36 | > Snapshot 1 37 | 38 | `import React from 'react'␊ 39 | import Box from './Box'␊ 40 | import Text from './Text'␊ 41 | ␊ 42 | const Hello = props => (␊ 43 | {props.children}␊ 44 | )␊ 45 | ␊ 46 | export default Hello` 47 | 48 | ## cxs imports required components 49 | 50 | > Snapshot 1 51 | 52 | `import styled from 'cxs/component'␊ 53 | import {␊ 54 | space,␊ 55 | fontSize,␊ 56 | width,␊ 57 | color,␊ 58 | ␊ 59 | } from 'styled-system'␊ 60 | import Div from './Div'␊ 61 | ␊ 62 | const Box = styled(Div)(props => ({}),␊ 63 | space,␊ 64 | fontSize,␊ 65 | width,␊ 66 | color,␊ 67 | ␊ 68 | )␊ 69 | ␊ 70 | Box.defaultProps = {}␊ 71 | ␊ 72 | export default Box` 73 | 74 | ## cxs returns a string 75 | 76 | > Snapshot 1 77 | 78 | `import styled from 'cxs/component'␊ 79 | import {␊ 80 | space,␊ 81 | fontSize,␊ 82 | width,␊ 83 | color,␊ 84 | ␊ 85 | } from 'styled-system'␊ 86 | ␊ 87 | ␊ 88 | const Hello = styled(h2)(props => ((props, theme) => ({␊ 89 | fontFamily: 'inherit',␊ 90 | color: props.theme('colors.blue')␊ 91 | })),␊ 92 | space,␊ 93 | fontSize,␊ 94 | width,␊ 95 | color,␊ 96 | ␊ 97 | )␊ 98 | ␊ 99 | Hello.defaultProps = {␊ 100 | p: 2␊ 101 | }␊ 102 | ␊ 103 | export default Hello` 104 | 105 | ## emotion imports required components 106 | 107 | > Snapshot 1 108 | 109 | `import styled from 'react-emotion'␊ 110 | import { withTheme } from 'theming'␊ 111 | import {␊ 112 | space,␊ 113 | fontSize,␊ 114 | width,␊ 115 | color,␊ 116 | ␊ 117 | } from 'styled-system'␊ 118 | import Div from './Div'␊ 119 | ␊ 120 | const Box = withTheme(styled(Div)(props => ({}),␊ 121 | space,␊ 122 | fontSize,␊ 123 | width,␊ 124 | color,␊ 125 | ␊ 126 | ))␊ 127 | ␊ 128 | Box.defaultProps = {}␊ 129 | ␊ 130 | export default Box` 131 | 132 | ## emotion returns a string 133 | 134 | > Snapshot 1 135 | 136 | `import styled from 'react-emotion'␊ 137 | import { withTheme } from 'theming'␊ 138 | import {␊ 139 | space,␊ 140 | fontSize,␊ 141 | width,␊ 142 | color,␊ 143 | ␊ 144 | } from 'styled-system'␊ 145 | ␊ 146 | ␊ 147 | const Hello = withTheme(styled(h2)(props => ((props, theme) => ({␊ 148 | fontFamily: 'inherit',␊ 149 | color: props.theme('colors.blue')␊ 150 | })),␊ 151 | space,␊ 152 | fontSize,␊ 153 | width,␊ 154 | color,␊ 155 | ␊ 156 | ))␊ 157 | ␊ 158 | Hello.defaultProps = {␊ 159 | p: 2␊ 160 | }␊ 161 | ␊ 162 | export default Hello` 163 | 164 | ## fela handles styled-system functions 165 | 166 | > Snapshot 1 167 | 168 | `import { createComponent } from 'react-fela'␊ 169 | import {␊ 170 | space,␊ 171 | fontSize,␊ 172 | width,␊ 173 | color,␊ 174 | textAlign,␊ 175 | } from 'styled-system'␊ 176 | ␊ 177 | ␊ 178 | const Hello = createComponent(props => Object.assign({},␊ 179 | space(props),␊ 180 | fontSize(props),␊ 181 | width(props),␊ 182 | color(props),␊ 183 | textAlign(props)␊ 184 | ), h2)␊ 185 | ␊ 186 | Hello.defaultProps = {}␊ 187 | ␊ 188 | export default Hello` 189 | 190 | ## fela imports required components 191 | 192 | > Snapshot 1 193 | 194 | `import { createComponent } from 'react-fela'␊ 195 | import {␊ 196 | space,␊ 197 | fontSize,␊ 198 | width,␊ 199 | color,␊ 200 | ␊ 201 | } from 'styled-system'␊ 202 | import Div from './Div'␊ 203 | ␊ 204 | const Box = createComponent(props => Object.assign({},␊ 205 | space(props),␊ 206 | fontSize(props),␊ 207 | width(props),␊ 208 | color(props),␊ 209 | ␊ 210 | ), Div)␊ 211 | ␊ 212 | Box.defaultProps = {}␊ 213 | ␊ 214 | export default Box` 215 | 216 | ## fela returns a string 217 | 218 | > Snapshot 1 219 | 220 | `import { createComponent } from 'react-fela'␊ 221 | import {␊ 222 | space,␊ 223 | fontSize,␊ 224 | width,␊ 225 | color,␊ 226 | ␊ 227 | } from 'styled-system'␊ 228 | ␊ 229 | ␊ 230 | const Hello = createComponent(props => Object.assign((props, theme) => ({␊ 231 | fontFamily: 'inherit',␊ 232 | color: props.theme('colors.blue')␊ 233 | }),␊ 234 | space(props),␊ 235 | fontSize(props),␊ 236 | width(props),␊ 237 | color(props),␊ 238 | ␊ 239 | ), h2)␊ 240 | ␊ 241 | Hello.defaultProps = {␊ 242 | p: 2␊ 243 | }␊ 244 | ␊ 245 | export default Hello` 246 | 247 | ## glamorous imports required components 248 | 249 | > Snapshot 1 250 | 251 | `import styled from 'glamorous'␊ 252 | import {␊ 253 | space,␊ 254 | fontSize,␊ 255 | width,␊ 256 | color,␊ 257 | ␊ 258 | } from 'styled-system'␊ 259 | import Div from './Div'␊ 260 | ␊ 261 | const Box = styled(Div)(props => ({}),␊ 262 | space,␊ 263 | fontSize,␊ 264 | width,␊ 265 | color,␊ 266 | ␊ 267 | )␊ 268 | ␊ 269 | Box.defaultProps = {}␊ 270 | ␊ 271 | export default Box` 272 | 273 | ## glamorous returns a string 274 | 275 | > Snapshot 1 276 | 277 | `import styled from 'glamorous'␊ 278 | import {␊ 279 | space,␊ 280 | fontSize,␊ 281 | width,␊ 282 | color,␊ 283 | ␊ 284 | } from 'styled-system'␊ 285 | ␊ 286 | ␊ 287 | const Hello = styled(h2)(props => ((props, theme) => ({␊ 288 | fontFamily: 'inherit',␊ 289 | color: props.theme('colors.blue')␊ 290 | })),␊ 291 | space,␊ 292 | fontSize,␊ 293 | width,␊ 294 | color,␊ 295 | ␊ 296 | )␊ 297 | ␊ 298 | Hello.defaultProps = {␊ 299 | p: 2␊ 300 | }␊ 301 | ␊ 302 | export default Hello` 303 | 304 | ## styled-components imports required components 305 | 306 | > Snapshot 1 307 | 308 | `import styled from 'styled-components'␊ 309 | import {␊ 310 | space,␊ 311 | fontSize,␊ 312 | width,␊ 313 | color,␊ 314 | ␊ 315 | } from 'styled-system'␊ 316 | import Div from './Div'␊ 317 | ␊ 318 | const Box = styled(Div)([], props => ({}),␊ 319 | space,␊ 320 | fontSize,␊ 321 | width,␊ 322 | color,␊ 323 | ␊ 324 | )␊ 325 | ␊ 326 | Box.defaultProps = {}␊ 327 | ␊ 328 | export default Box` 329 | 330 | ## styled-components returns a string 331 | 332 | > Snapshot 1 333 | 334 | `import styled from 'styled-components'␊ 335 | import {␊ 336 | space,␊ 337 | fontSize,␊ 338 | width,␊ 339 | color,␊ 340 | ␊ 341 | } from 'styled-system'␊ 342 | ␊ 343 | ␊ 344 | const Hello = styled(h2)([], props => ((props, theme) => ({␊ 345 | fontFamily: 'inherit',␊ 346 | color: props.theme('colors.blue')␊ 347 | })),␊ 348 | space,␊ 349 | fontSize,␊ 350 | width,␊ 351 | color,␊ 352 | ␊ 353 | )␊ 354 | ␊ 355 | Hello.defaultProps = {␊ 356 | p: 2␊ 357 | }␊ 358 | ␊ 359 | export default Hello` 360 | -------------------------------------------------------------------------------- /test/snapshots/templates.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/c8r/lab-cli/557bc57835fae30cee5465a6ac10633c628560aa/test/snapshots/templates.js.snap -------------------------------------------------------------------------------- /test/snapshots/vue.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/vue.js` 2 | 3 | The actual snapshot is saved in `vue.js.snap`. 4 | 5 | Generated by [AVA](https://ava.li). 6 | 7 | ## vue handles an entire complex library 8 | 9 | > Snapshot 1 10 | 11 | [ 12 | { 13 | module: `export { default as lab } from './lab'␊ 14 | export { default as theme } from './theme'␊ 15 | export { default as Avatar } from './Avatar'␊ 16 | export { default as Button } from './Button'␊ 17 | export { default as ButtonOutline } from './ButtonOutline'␊ 18 | export { default as Link } from './Link'␊ 19 | export { default as LinkBox } from './LinkBox'␊ 20 | export { default as Img } from './Img'␊ 21 | export { default as Hr } from './Hr'␊ 22 | export { default as Text } from './Text'␊ 23 | export { default as Heading } from './Heading'␊ 24 | export { default as P } from './P'␊ 25 | export { default as H1 } from './H1'␊ 26 | export { default as H2 } from './H2'␊ 27 | export { default as H3 } from './H3'␊ 28 | export { default as H4 } from './H4'␊ 29 | export { default as Div } from './Div'␊ 30 | export { default as Container } from './Container'␊ 31 | export { default as Input } from './Input'␊ 32 | export { default as Label } from './Label'␊ 33 | export { default as TextInput } from './TextInput'␊ 34 | export { default as Ul } from './Ul'␊ 35 | export { default as Li } from './Li'`, 36 | name: 'index', 37 | }, 38 | { 39 | module: `import styled from 'vue-styled-components'␊ 40 | import system from 'vue-styled-system'␊ 41 | ␊ 42 | import {␊ 43 | space,␊ 44 | fontSize,␊ 45 | width,␊ 46 | color,␊ 47 | borderWidth,␊ 48 | borderRadius,␊ 49 | borderColor␊ 50 | } from 'styled-system'␊ 51 | ␊ 52 | import theme from './theme.json'␊ 53 | ␊ 54 | export default styled(␊ 55 | 'img',␊ 56 | system({␊ 57 | src: {␊ 58 | default:␊ 59 | 'https://pbs.twimg.com/profile_images/854537128994275328/nVZX1MEh_400x400.jpg'␊ 60 | },␊ 61 | size: { default: '64px' },␊ 62 | borderWidth: { default: 2 },␊ 63 | borderRadius: { default: 9999 },␊ 64 | px: { default: 1 },␊ 65 | py: { default: 1 },␊ 66 | borderColor: { default: 'gray1' },␊ 67 | bg: { default: 'transparent' },␊ 68 | color: { default: 'transparent' },␊ 69 | theme: { default: () => theme }␊ 70 | })␊ 71 | )`␊ 72 | height: ${props => (props.size ? props.size : '48px')},␊ 73 | width: ${props => (props.size ? props.size : '48px')}␊ 74 | ${space}␊ 75 | ${fontSize}␊ 76 | ${width}␊ 77 | ${color}␊ 78 | ${borderWidth}␊ 79 | ${borderRadius}␊ 80 | ${borderColor}␊ 81 | `␊ 82 | `, 83 | name: 'Avatar', 84 | }, 85 | { 86 | module: `import styled from 'vue-styled-components'␊ 87 | import system from 'vue-styled-system'␊ 88 | ␊ 89 | import { space, fontSize, width, color } from 'styled-system'␊ 90 | ␊ 91 | import theme from './theme.json'␊ 92 | ␊ 93 | export default styled(␊ 94 | 'button',␊ 95 | system({␊ 96 | fontSize: { default: 1 },␊ 97 | px: { default: 4 },␊ 98 | py: { default: 2 },␊ 99 | mr: { default: 2 },␊ 100 | color: { default: 'white' },␊ 101 | bg: { default: 'blue' },␊ 102 | theme: { default: () => theme }␊ 103 | })␊ 104 | )`␊ 105 | display: 'inline-block',␊ 106 | fontFamily: 'inherit',␊ 107 | borderWidth: 0,␊ 108 | borderRadius: '4px',␊ 109 | appearance: 'none'␊ 110 | ${space}␊ 111 | ${fontSize}␊ 112 | ${width}␊ 113 | ${color}␊ 114 | ␊ 115 | `␊ 116 | `, 117 | name: 'Button', 118 | }, 119 | { 120 | module: `import styled from 'vue-styled-components'␊ 121 | import system from 'vue-styled-system'␊ 122 | ␊ 123 | import {␊ 124 | space,␊ 125 | fontSize,␊ 126 | width,␊ 127 | color,␊ 128 | borderWidth,␊ 129 | borderColor␊ 130 | } from 'styled-system'␊ 131 | ␊ 132 | import theme from './theme.json'␊ 133 | ␊ 134 | export default styled(␊ 135 | 'button',␊ 136 | system({␊ 137 | fontSize: { default: 1 },␊ 138 | px: { default: 4 },␊ 139 | py: { default: 2 },␊ 140 | color: { default: 'blue' },␊ 141 | bg: { default: 'transparent' },␊ 142 | borderWidth: { default: 2 },␊ 143 | borderColor: { default: 'currentColor' },␊ 144 | theme: { default: () => theme }␊ 145 | })␊ 146 | )`␊ 147 | display: 'inline-block',␊ 148 | fontFamily: 'inherit',␊ 149 | borderRadius: '4px',␊ 150 | appearance: 'none',␊ 151 | boxSizing: 'border-box'␊ 152 | ${space}␊ 153 | ${fontSize}␊ 154 | ${width}␊ 155 | ${color}␊ 156 | ${borderWidth}␊ 157 | ${borderColor}␊ 158 | `␊ 159 | `, 160 | name: 'ButtonOutline', 161 | }, 162 | { 163 | module: `import styled from 'vue-styled-components'␊ 164 | import system from 'vue-styled-system'␊ 165 | ␊ 166 | import { space, fontSize, width, color, hover } from 'styled-system'␊ 167 | ␊ 168 | import theme from './theme.json'␊ 169 | ␊ 170 | export default styled(␊ 171 | 'a',␊ 172 | system({␊ 173 | color: { default: 'inherit' },␊ 174 | hover: {␊ 175 | default: () => ({␊ 176 | opacity: 0.66,␊ 177 | transition: 'opacity .25s ease-in'␊ 178 | })␊ 179 | },␊ 180 | theme: { default: () => theme }␊ 181 | })␊ 182 | )`␊ 183 | textAlign: ${props => (props.center ? 'center' : 'left')},␊ 184 | textDecoration: ${props => (props.underline ? 'underline' : 'none')},␊ 185 | opacity: 1,␊ 186 | transition: 'opacity .3s ease-in',␊ 187 | display: 'inline-block'␊ 188 | ${space}␊ 189 | ${fontSize}␊ 190 | ${width}␊ 191 | ${color}␊ 192 | ${hover}␊ 193 | `␊ 194 | `, 195 | name: 'Link', 196 | }, 197 | { 198 | module: `import styled from 'vue-styled-components'␊ 199 | import system from 'vue-styled-system'␊ 200 | ␊ 201 | import {␊ 202 | space,␊ 203 | fontSize,␊ 204 | width,␊ 205 | color,␊ 206 | borderWidth,␊ 207 | borderColor␊ 208 | } from 'styled-system'␊ 209 | ␊ 210 | import theme from './theme.json'␊ 211 | ␊ 212 | export default styled(␊ 213 | 'a',␊ 214 | system({␊ 215 | color: { default: 'black' },␊ 216 | fontSize: { default: 2 },␊ 217 | borderWidth: { default: 1 },␊ 218 | borderColor: { default: 'black' },␊ 219 | px: { default: 2 },␊ 220 | py: { default: 1 },␊ 221 | theme: { default: () => theme }␊ 222 | })␊ 223 | )`␊ 224 | textdecoration: 'none' ${space} ${fontSize} ${width} ${color} ${borderWidth}␊ 225 | ${borderColor};␊ 226 | `␊ 227 | `, 228 | name: 'LinkBox', 229 | }, 230 | { 231 | module: `import styled from 'vue-styled-components'␊ 232 | import system from 'vue-styled-system'␊ 233 | ␊ 234 | import { space, fontSize, width, color } from 'styled-system'␊ 235 | ␊ 236 | import theme from './theme.json'␊ 237 | ␊ 238 | export default styled(␊ 239 | 'img',␊ 240 | system({␊ 241 | theme: { default: () => theme }␊ 242 | })␊ 243 | )`␊ 244 | display: 'block',␊ 245 | maxWidth: '100%',␊ 246 | width: '100%'␊ 247 | ${space}␊ 248 | ${fontSize}␊ 249 | ${width}␊ 250 | ${color}␊ 251 | ␊ 252 | `␊ 253 | `, 254 | name: 'Img', 255 | }, 256 | { 257 | module: `import styled from 'vue-styled-components'␊ 258 | import system from 'vue-styled-system'␊ 259 | ␊ 260 | import {␊ 261 | space,␊ 262 | fontSize,␊ 263 | width,␊ 264 | color,␊ 265 | borderRadius,␊ 266 | borderWidth␊ 267 | } from 'styled-system'␊ 268 | ␊ 269 | import theme from './theme.json'␊ 270 | ␊ 271 | export default styled(␊ 272 | 'hr',␊ 273 | system({␊ 274 | bg: { default: 'transparent' },␊ 275 | color: { default: 'black' },␊ 276 | pl: { default: 0 },␊ 277 | pr: { default: 0 },␊ 278 | pb: { default: 0 },␊ 279 | borderRadius: { default: 0 },␊ 280 | borderWidth: { default: 1 },␊ 281 | borderTop: { default: true },␊ 282 | theme: { default: () => theme }␊ 283 | })␊ 284 | )`␊ 285 | height: 0 ${space} ${fontSize} ${width} ${color} ${borderRadius}␊ 286 | ${borderWidth};␊ 287 | `␊ 288 | `, 289 | name: 'Hr', 290 | }, 291 | { 292 | module: `import styled from 'vue-styled-components'␊ 293 | import system from 'vue-styled-system'␊ 294 | ␊ 295 | import { space, fontSize, width, color } from 'styled-system'␊ 296 | ␊ 297 | import theme from './theme.json'␊ 298 | ␊ 299 | export default styled(␊ 300 | 'div',␊ 301 | system({␊ 302 | theme: { default: () => theme }␊ 303 | })␊ 304 | )`␊ 305 | ${space} ${fontSize} ${width} ${color};␊ 306 | `␊ 307 | `, 308 | name: 'Text', 309 | }, 310 | { 311 | module: `import styled from 'vue-styled-components'␊ 312 | import system from 'vue-styled-system'␊ 313 | ␊ 314 | import {␊ 315 | space,␊ 316 | fontSize,␊ 317 | width,␊ 318 | color,␊ 319 | fontWeight,␊ 320 | borderColor,␊ 321 | borderWidth␊ 322 | } from 'styled-system'␊ 323 | ␊ 324 | import theme from './theme.json'␊ 325 | ␊ 326 | export default styled(␊ 327 | 'h2',␊ 328 | system({␊ 329 | fontSize: { default: 4 },␊ 330 | m: { default: 0 },␊ 331 | fontWeight: { default: '600' },␊ 332 | theme: { default: () => theme }␊ 333 | })␊ 334 | )`␊ 335 | ${space} ${fontSize} ${width} ${color} ${fontWeight} ${borderColor} ${borderWidth};␊ 336 | `␊ 337 | `, 338 | name: 'Heading', 339 | }, 340 | { 341 | module: `import styled from 'vue-styled-components'␊ 342 | import system from 'vue-styled-system'␊ 343 | ␊ 344 | import { space, fontSize, width, color } from 'styled-system'␊ 345 | ␊ 346 | import theme from './theme.json'␊ 347 | ␊ 348 | export default styled(␊ 349 | 'p',␊ 350 | system({␊ 351 | theme: { default: () => theme }␊ 352 | })␊ 353 | )`␊ 354 | maxWidth: ${props => (props.wide ? '34em' : '28em')},␊ 355 | lineHeight: '1.5'␊ 356 | ${space}␊ 357 | ${fontSize}␊ 358 | ${width}␊ 359 | ${color}␊ 360 | ␊ 361 | `␊ 362 | `, 363 | name: 'P', 364 | }, 365 | { 366 | module: `import styled from 'vue-styled-components'␊ 367 | import system from 'vue-styled-system'␊ 368 | ␊ 369 | import { space, fontSize, width, color, fontWeight } from 'styled-system'␊ 370 | ␊ 371 | import theme from './theme.json'␊ 372 | ␊ 373 | export default styled(␊ 374 | 'h1',␊ 375 | system({␊ 376 | fontWeight: { default: '600' },␊ 377 | theme: { default: () => theme }␊ 378 | })␊ 379 | )`␊ 380 | textalign: ${props => (props.center ? 'center' : 'left')} ${space} ${fontSize}␊ 381 | ${width} ${color} ${fontWeight};␊ 382 | `␊ 383 | `, 384 | name: 'H1', 385 | }, 386 | { 387 | module: `import styled from 'vue-styled-components'␊ 388 | import system from 'vue-styled-system'␊ 389 | ␊ 390 | import { space, fontSize, width, color, fontWeight } from 'styled-system'␊ 391 | ␊ 392 | import theme from './theme.json'␊ 393 | ␊ 394 | export default styled(␊ 395 | 'h2',␊ 396 | system({␊ 397 | fontWeight: { default: '600' },␊ 398 | theme: { default: () => theme }␊ 399 | })␊ 400 | )`␊ 401 | textalign: ${props => (props.center ? 'center' : 'left')} ${space} ${fontSize}␊ 402 | ${width} ${color} ${fontWeight};␊ 403 | `␊ 404 | `, 405 | name: 'H2', 406 | }, 407 | { 408 | module: `import styled from 'vue-styled-components'␊ 409 | import system from 'vue-styled-system'␊ 410 | ␊ 411 | import { space, fontSize, width, color, fontWeight } from 'styled-system'␊ 412 | ␊ 413 | import theme from './theme.json'␊ 414 | ␊ 415 | export default styled(␊ 416 | 'h3',␊ 417 | system({␊ 418 | fontWeight: { default: '600' },␊ 419 | theme: { default: () => theme }␊ 420 | })␊ 421 | )`␊ 422 | textalign: ${props => (props.center ? 'center' : 'left')} ${space} ${fontSize}␊ 423 | ${width} ${color} ${fontWeight};␊ 424 | `␊ 425 | `, 426 | name: 'H3', 427 | }, 428 | { 429 | module: `import styled from 'vue-styled-components'␊ 430 | import system from 'vue-styled-system'␊ 431 | ␊ 432 | import { space, fontSize, width, color, fontWeight } from 'styled-system'␊ 433 | ␊ 434 | import theme from './theme.json'␊ 435 | ␊ 436 | export default styled(␊ 437 | 'h4',␊ 438 | system({␊ 439 | fontWeight: { default: '600' },␊ 440 | theme: { default: () => theme }␊ 441 | })␊ 442 | )`␊ 443 | textalign: ${props => (props.center ? 'center' : 'left')} ${space} ${fontSize}␊ 444 | ${width} ${color} ${fontWeight};␊ 445 | `␊ 446 | `, 447 | name: 'H4', 448 | }, 449 | { 450 | module: `import styled from 'vue-styled-components'␊ 451 | import system from 'vue-styled-system'␊ 452 | ␊ 453 | import { space, fontSize, width, color } from 'styled-system'␊ 454 | ␊ 455 | import theme from './theme.json'␊ 456 | ␊ 457 | export default styled(␊ 458 | 'div',␊ 459 | system({␊ 460 | theme: { default: () => theme }␊ 461 | })␊ 462 | )`␊ 463 | ${space} ${fontSize} ${width} ${color};␊ 464 | `␊ 465 | `, 466 | name: 'Div', 467 | }, 468 | { 469 | module: `import styled from 'vue-styled-components'␊ 470 | import system from 'vue-styled-system'␊ 471 | ␊ 472 | import { space, fontSize, width, color } from 'styled-system'␊ 473 | ␊ 474 | import theme from './theme.json'␊ 475 | ␊ 476 | export default styled(␊ 477 | 'div',␊ 478 | system({␊ 479 | mx: { default: 'auto' },␊ 480 | theme: { default: () => theme }␊ 481 | })␊ 482 | )`␊ 483 | maxWidth: '64em',␊ 484 | boxSizing: 'border-box'␊ 485 | ${space}␊ 486 | ${fontSize}␊ 487 | ${width}␊ 488 | ${color}␊ 489 | ␊ 490 | `␊ 491 | `, 492 | name: 'Container', 493 | }, 494 | { 495 | module: `import styled from 'vue-styled-components'␊ 496 | import system from 'vue-styled-system'␊ 497 | ␊ 498 | import {␊ 499 | space,␊ 500 | fontSize,␊ 501 | width,␊ 502 | color,␊ 503 | borderColor,␊ 504 | borderWidth,␊ 505 | focus,␊ 506 | borderRadius␊ 507 | } from 'styled-system'␊ 508 | ␊ 509 | import theme from './theme.json'␊ 510 | ␊ 511 | export default styled(␊ 512 | 'input',␊ 513 | system({␊ 514 | m: { default: 0 },␊ 515 | w: { default: 1 },␊ 516 | px: { default: 2 },␊ 517 | py: { default: 2 },␊ 518 | color: { default: 'inherit' },␊ 519 | bg: { default: 'transparent' },␊ 520 | borderColor: { default: 'gray4' },␊ 521 | borderWidth: { default: 1 },␊ 522 | fontSize: { default: () => null },␊ 523 | focus: {␊ 524 | default: () => ({␊ 525 | outline: 0␊ 526 | })␊ 527 | },␊ 528 | borderRadius: { default: 2 },␊ 529 | theme: { default: () => theme }␊ 530 | })␊ 531 | )`␊ 532 | display: 'block',␊ 533 | fontFamily: 'inherit',␊ 534 | fontSize: 'inherit'␊ 535 | ${space}␊ 536 | ${fontSize}␊ 537 | ${width}␊ 538 | ${color}␊ 539 | ${borderColor}␊ 540 | ${borderWidth}␊ 541 | ${focus}␊ 542 | ${borderRadius}␊ 543 | `␊ 544 | `, 545 | name: 'Input', 546 | }, 547 | { 548 | module: `import styled from 'vue-styled-components'␊ 549 | import system from 'vue-styled-system'␊ 550 | ␊ 551 | import { space, fontSize, width, color, fontWeight } from 'styled-system'␊ 552 | ␊ 553 | import theme from './theme.json'␊ 554 | ␊ 555 | export default styled(␊ 556 | 'label',␊ 557 | system({␊ 558 | fontSize: { default: 1 },␊ 559 | fontWeight: { default: '600' },␊ 560 | theme: { default: () => theme }␊ 561 | })␊ 562 | )`␊ 563 | display: 'block' ${space} ${fontSize} ${width} ${color} ${fontWeight};␊ 564 | `␊ 565 | `, 566 | name: 'Label', 567 | }, 568 | { 569 | module: `import Label from './Label'␊ 570 | import Div from './Div'␊ 571 | import Input from './Input'␊ 572 | import P from './P'␊ 573 | ␊ 574 | export default {␊ 575 | name: 'TextInput',␊ 576 | props: ['mb', 'label', 'text'],␊ 577 | render(h) {␊ 578 | return (␊ 579 |
    ␊ 580 |
    ␊ 588 | )␊ 589 | }␊ 590 | }␊ 591 | `, 592 | name: 'TextInput', 593 | }, 594 | { 595 | module: `import styled from 'vue-styled-components'␊ 596 | import system from 'vue-styled-system'␊ 597 | ␊ 598 | import {␊ 599 | space,␊ 600 | fontSize,␊ 601 | width,␊ 602 | color,␊ 603 | borderColor,␊ 604 | borderWidth␊ 605 | } from 'styled-system'␊ 606 | ␊ 607 | import theme from './theme.json'␊ 608 | ␊ 609 | export default styled(␊ 610 | 'ul',␊ 611 | system({␊ 612 | m: { default: 0 },␊ 613 | p: { default: 0 },␊ 614 | borderColor: { default: 'transparent' },␊ 615 | borderWidth: { default: 1 },␊ 616 | theme: { default: () => theme }␊ 617 | })␊ 618 | )`␊ 619 | liststyletype: 'none' ${space} ${fontSize} ${width} ${color} ${borderColor}␊ 620 | ${borderWidth};␊ 621 | `␊ 622 | `, 623 | name: 'Ul', 624 | }, 625 | { 626 | module: `import styled from 'vue-styled-components'␊ 627 | import system from 'vue-styled-system'␊ 628 | ␊ 629 | import {␊ 630 | space,␊ 631 | fontSize,␊ 632 | width,␊ 633 | color,␊ 634 | borderWidth,␊ 635 | borderColor␊ 636 | } from 'styled-system'␊ 637 | ␊ 638 | import theme from './theme.json'␊ 639 | ␊ 640 | export default styled(␊ 641 | 'li',␊ 642 | system({␊ 643 | borderWidth: { default: 1 },␊ 644 | borderColor: { default: 'transparent' },␊ 645 | theme: { default: () => theme }␊ 646 | })␊ 647 | )`␊ 648 | ${space} ${fontSize} ${width} ${color} ${borderWidth} ${borderColor};␊ 649 | `␊ 650 | `, 651 | name: 'Li', 652 | }, 653 | ] 654 | 655 | ## vue returns a code string for extended components 656 | 657 | > Snapshot 1 658 | 659 | `import styled from 'vue-styled-components'␊ 660 | import system from 'vue-styled-system'␊ 661 | ␊ 662 | import {␊ 663 | space,␊ 664 | fontSize,␊ 665 | width,␊ 666 | color,␊ 667 | ␊ 668 | } from 'styled-system'␊ 669 | ␊ 670 | import theme from './theme.json'␊ 671 | import Div from './Div'␊ 672 | export default styled(Div, system({␊ 673 | p: { default: 3 },␊ 674 | theme: { default: () => theme }␊ 675 | }))`␊ 676 | ␊ 677 | ${space}␊ 678 | ${fontSize}␊ 679 | ${width}␊ 680 | ${color}␊ 681 | ␊ 682 | `␊ 683 | ` 684 | -------------------------------------------------------------------------------- /test/snapshots/vue.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/c8r/lab-cli/557bc57835fae30cee5465a6ac10633c628560aa/test/snapshots/vue.js.snap -------------------------------------------------------------------------------- /test/templates.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import templates from '../lib/templates' 3 | 4 | const config = { 5 | name: 'Hello', 6 | type: 'h2', 7 | extensionImport: '', 8 | systemFunctions: '', 9 | propsString: `{ 10 | p: 2 11 | }`, 12 | styleString: `(props, theme) => ({ 13 | fontFamily: 'inherit', 14 | color: props.theme('colors.blue') 15 | })` 16 | } 17 | 18 | const compConfig = { 19 | name: 'Box', 20 | type: 'Div', 21 | propsString: '{}', 22 | styleString: '{}', 23 | extensionImport: `import Div from './Div'`, 24 | systemFunctions: '' 25 | } 26 | 27 | test('styled-components returns a string', t => { 28 | const a = templates['styled-components'](config) 29 | t.is(typeof a, 'string') 30 | t.snapshot(a) 31 | }) 32 | 33 | test('styled-components imports required components', t => { 34 | const a = templates['styled-components'](compConfig) 35 | t.is(typeof a, 'string') 36 | t.regex(a, /import\sDiv\sfrom/) 37 | t.snapshot(a) 38 | }) 39 | 40 | test('glamorous returns a string', t => { 41 | const a = templates.glamorous(config) 42 | t.is(typeof a, 'string') 43 | t.snapshot(a) 44 | }) 45 | 46 | test('glamorous imports required components', t => { 47 | const a = templates.glamorous(compConfig) 48 | t.is(typeof a, 'string') 49 | t.regex(a, /import\sDiv\sfrom/) 50 | t.snapshot(a) 51 | }) 52 | 53 | test('emotion returns a string', t => { 54 | const a = templates.emotion(config) 55 | t.is(typeof a, 'string') 56 | t.snapshot(a) 57 | }) 58 | 59 | test('emotion imports required components', t => { 60 | const a = templates.emotion(compConfig) 61 | t.is(typeof a, 'string') 62 | t.regex(a, /import\sDiv\sfrom/) 63 | t.snapshot(a) 64 | }) 65 | 66 | test('fela returns a string', t => { 67 | const a = templates.fela(config) 68 | t.is(typeof a, 'string') 69 | t.snapshot(a) 70 | }) 71 | 72 | test('fela handles styled-system functions', t => { 73 | const a = templates.fela({ 74 | name: 'Hello', 75 | type: 'h2', 76 | extensionImport: '', 77 | systemFunctions: 'textAlign,', 78 | system: ['textAlign'], 79 | propsString: '{}', 80 | styleString: '{}' 81 | }) 82 | t.is(typeof a, 'string') 83 | t.snapshot(a) 84 | }) 85 | 86 | test('fela imports required components', t => { 87 | const a = templates.fela(compConfig) 88 | t.is(typeof a, 'string') 89 | t.regex(a, /import\sDiv\sfrom/) 90 | t.snapshot(a) 91 | }) 92 | 93 | test('cxs returns a string', t => { 94 | const a = templates.cxs(config) 95 | t.is(typeof a, 'string') 96 | t.snapshot(a) 97 | }) 98 | 99 | test('cxs imports required components', t => { 100 | const a = templates.cxs(compConfig) 101 | t.is(typeof a, 'string') 102 | t.regex(a, /import\sDiv\sfrom/) 103 | t.snapshot(a) 104 | }) 105 | 106 | const macro = { 107 | name: 'Hello', 108 | imports: ['Box', 'Text'], 109 | jsx: '{props.children}' 110 | } 111 | 112 | test('composite returns a string', t => { 113 | const a = templates.compositeReact(macro) 114 | t.is(typeof a, 'string') 115 | t.snapshot(a) 116 | }) 117 | 118 | test('composite imports required components', t => { 119 | const a = templates.compositeReact(macro) 120 | t.is(typeof a, 'string') 121 | t.regex(a, /import\sBox\sfrom/) 122 | t.regex(a, /import\sText\sfrom/) 123 | t.snapshot(a) 124 | }) 125 | 126 | test('composite has defaults', t => { 127 | const a = templates.compositeReact({ name: 'Hello' }) 128 | t.snapshot(a) 129 | }) 130 | -------------------------------------------------------------------------------- /test/vue.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import test from 'ava' 3 | import { vue } from '../lib/templates' 4 | import createModules from '../lib' 5 | import fixture from './fixture.json' 6 | 7 | const config = { 8 | name: 'Hello', 9 | type: 'h2', 10 | systemFunctions: '', 11 | extensionImport: '', 12 | props: { 13 | p: 2, 14 | color: 'black' 15 | }, 16 | style: { 17 | textDecoration: 'underline' 18 | } 19 | } 20 | 21 | const extConfig = { 22 | name: 'Box', 23 | type: 'Div', 24 | props: { 25 | p: 3 26 | }, 27 | style: {}, 28 | extensionImport: `import Div from './Div'`, 29 | systemFunctions: '' 30 | } 31 | 32 | test('vue returns a code string for extended components', t => { 33 | const result = vue(extConfig) 34 | 35 | t.is(typeof result, 'string') 36 | t.snapshot(result) 37 | }) 38 | 39 | test('vue handles an entire complex library', t => { 40 | const result = createModules(fixture, { 41 | library: 'vue', 42 | harmony: true 43 | }) 44 | 45 | t.snapshot(result) 46 | }) 47 | --------------------------------------------------------------------------------