Skip to content

Commit

Permalink
feat: update library to work with Tailwind 1.x
Browse files Browse the repository at this point in the history
Complete rewrite of the format subclasses. Move to ES6+

BREAKING CHANGE: Works with new tailwind config structure
  • Loading branch information
dobromir-hristov committed Apr 23, 2019
1 parent 46e8de5 commit e005737
Show file tree
Hide file tree
Showing 24 changed files with 17,543 additions and 11,453 deletions.
8 changes: 8 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
node_modules
dist
2 changes: 1 addition & 1 deletion src/cli.js → cli.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

const yargs = require('yargs')
const ConvertTo = require('./index.js')
const ConvertTo = require('./dist')
const chalk = require('chalk').default
const log = console.log
const error = (msg) => chalk.bold.bgRed('\n' + chalk.white(msg) + '\n')
Expand Down
11,110 changes: 7,817 additions & 3,293 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 22 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
"name": "tailwindcss-export-config",
"version": "1.0.4",
"description": "Export Tailwindcss config options to SASS, SCSS, LESS and Stylus",
"main": "src/index.js",
"bin": "src/cli.js",
"main": "dist/index.js",
"bin": "cli.js",
"scripts": {
"build": "bili src/index.js",
"test": "jest",
"release": "standard-version"
"release": "npm run build && standard-version"
},
"files": [
"dist",
"cli.js"
],
"repository": {
"type": "git",
"url": "git+https://github.com/dobromir-hristov/tailwindcss-export-config.git"
Expand All @@ -30,17 +35,28 @@
"engines": {
"node": ">=6.14.3"
},
"browserslist": [
"node 10"
],
"homepage": "https://github.com/dobromir-hristov/tailwindcss-export-config#readme",
"dependencies": {
"chalk": "^2.4.1",
"fs-extra": "^6.0.1",
"lodash.foreach": "^4.5.0",
"lodash.reduce": "^4.6.0",
"yargs": "^11.0.0"
},
"devDependencies": {
"jest": "^23.1.0",
"@babel/plugin-proposal-class-properties": "^7.4.0",
"@babel/preset-env": "^7.4.3",
"babel-jest": "^24.7.1",
"bili": "^4.7.3",
"jest": "^24.7.1",
"standard-version": "^4.4.0",
"tailwindcss": "^0.5.3"
"tailwindcss": "^1.0.0-beta.4"
},
"jest": {
"transform": {
"^.+\\.js$": "babel-jest"
}
}
}
150 changes: 133 additions & 17 deletions src/converters/Converter.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,132 @@
import reduce from 'lodash.reduce'
import { indentWith } from './utils.js'
import { isObject } from './utils'

/**
* General converter class. To be extended by any specific format converter.
*/
class Converter {

/** @type {string} - the format and file extension */
format

/** @type {string} - the symbol that starts a map */
mapOpener = '(\n'
/** @type {string} - the symbol that ends a map */
mapCloser = ')'

/**
* @param opts
* @param {Object} opts.config - Tailwind config object
* @param {Boolean} opts.flat - Is flat or not
* @param {String} opts.prefix - If we want a variable prefix
*/
constructor (opts) {
this.ignored = ['modules', 'plugins', 'options']

this.config = opts.config
this.config = opts.config.theme
this.flat = opts.flat

this.prefix = opts.prefix || ''
}

/**
* Returns a variable format for the style class
* @param {string} name
* @param {string} value
* @private
*/
_buildVar (name, value) {}

/**
* Converts the supplied data to a list of variables
* @param prop
* @param data
* @private
*/
_convertObjectToVar (prop, data) {}
_convertObjectToVar (prop, data) {
return reduce(data, (all, value, metric) => {
if (isObject(value)) {
return all + Object.entries(value).map(([propKey, propValue]) => {
return this._buildVar(
this._propertyNameSanitizer(prop, `${metric}-${propKey}`),
this._sanitizePropValue(propValue)
)
}).join('')
} else {
return all + this._buildVar(
this._propertyNameSanitizer(prop, metric),
this._sanitizePropValue(value)
)
}
}, '')
}

/**
* Converts the supplied data to a nested map
* @param prop
* Converts the supplied data to a list of nested map objects
* @private
* @param {string} property
* @param {object} data
* @return {string}
*/
_convertObjectToMap (property, data) {
return this._buildVar(
this._propertyNameSanitizer(property),
this._buildMap(data)
)
}

/**
* Builds a map object with indentation
* @param data
* @param indent
* @return {string}
* @private
*/
_buildMap (data, indent = 0) {
// open map
return [
`${this.mapOpener}`,
// loop over each element
...Object.entries(data).map(([metric, value], index) => {
return this._buildMapData(metric, value, indent, index)
}),
// close map
indentWith(this.mapCloser, indent)
].join('')
}

/**
* Builds the body data of a map
* @param {string} metric - colors, backgroundColor, etc
* @param {object|string} value - the metric value, usually an object
* @param {number} indent - the number of indents to apply
* @param {number} metricIndex - the metric index it is in
* @return {string|*}
* @private
*/
_convertObjectToMap (prop, data) {}
_buildMapData (metric, value, indent, metricIndex) {
if (!isObject(value)) {
// not an object so we can directly build an entry
return this._buildObjectEntry(metric, value, indent, metricIndex)
}
// its an object so we need to flatten it out
return Object.entries(value).map(([propertyName, propertyValue], index) => {
return this._buildObjectEntry(`${metric}-${propertyName}`, propertyValue, indent, index, metricIndex)
}).join('')
}

/**
* Creates a single map entry
* @param {string} key - the key of the entry. Usually concatenated prefixed string
* @param {string | array} value - the value if the entry. Should be either array or a string
* @param {number} indent - the number of indents
* @param {number} index - the current item index
* @param {number} metricIndex - the current metric's index
* @return {string}
* @private
*/
_buildObjectEntry (key, value, indent, index = 0, metricIndex) {
return indentWith(`${this._objectEntryKeySanitizer(key)}: ${this._sanitizePropValue(value)},\n`, indent + 2)
}

/**
* Converts the options config to the required format.
Expand All @@ -41,35 +136,56 @@ class Converter {
let metric
let buffer = ''
for (metric in this.config) {
if (this.config.hasOwnProperty(metric) && !this.ignored.includes(metric)) {
if (this.config.hasOwnProperty(metric)) {
const data = this.config[metric]
const body = this.flat ? this._convertObjectToVar(metric, data) : this._convertObjectToMap(metric, data)
buffer += '\n\n'

const body = this.flat
? this._convertObjectToVar(metric, data)
: this._convertObjectToMap(metric, data)

buffer += '\n'
buffer += body
}
}
return buffer
}

/**
* Sanitizes a value, escaping and removing symbols
* @param {*} value
* @return {string|*}
* @private
*/
_sanitizePropValue (value) {
if (Array.isArray(value)) return `(${value})`
if (Array.isArray(value)) return `(${value})`.replace(/\\"/g, '"')
if (typeof value === 'string' && value.includes(',')) return `(${value})`
return value
}

/**
* @return string
* Sanitizes a property name by escaping characters
* Adds prefix
* @param {string} property - the property (colors, backgroundColors)
* @param {string} [metric] - the property's metric (purple, red, 1/4, 24 etc..)
* @return {string}
* @private
*/
getFormat () {
throw new Error('Implement getFormat function')
}

_propertyNameSanitizer (property, metric = '') {
if (metric) {
metric = metric.replace('/', '\\/')
}
return [this.prefix, property, metric].filter(v => v).join('-')
}

/**
* Sanitizes object keys
* @param {string} key
* @return {string}
* @private
*/
_objectEntryKeySanitizer (key) {
return key
}
}

module.exports = Converter
export default Converter
18 changes: 6 additions & 12 deletions src/converters/Less.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
const reduce = require('lodash.reduce')
const Converter = require('./Converter.js')
import Converter from './Converter.js'

class LessConverter extends Converter {
_convertObjectToVar (property, data) {
return reduce(data, (all, value, metric) => {
all += `@${this._propertyNameSanitizer(property, metric)}: ${this._sanitizePropValue(value)};\n`
return all
}, '')
format = 'less'

_buildVar (name, value) {
return `@${name}: ${value};\n`
}

_convertObjectToMap (prop, data) {
return this._convertObjectToVar(prop, data)
}

getFormat () {
return 'less'
}

_sanitizePropValue (value) {
if (Array.isArray(value)) return value.join(', ')
return value
}
}

module.exports = LessConverter
export default LessConverter
31 changes: 11 additions & 20 deletions src/converters/Sass.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
const reduce = require('lodash.reduce')
const foreach = require('lodash.foreach')

const Converter = require('./Converter')
import Converter from './Converter'
import { indentWith } from './utils'

class SassConverter extends Converter {
_convertObjectToMap (property, data) {
let buffer = '(\n'
foreach(data, (value, metric) => {
buffer += ` ${metric}: ${this._sanitizePropValue(value)},\n`
})
buffer += ')'
return `$${this._propertyNameSanitizer(property)}: ${buffer}`
}
format = 'sass'

mapOpener = '('
mapCloser = ')'

_convertObjectToVar (property, data) {
return reduce(data, (all, value, metric) => {
all += `$${this._propertyNameSanitizer(property, metric)}: ${this._sanitizePropValue(value)}\n`
return all
}, '')
_buildVar (name, value) {
return `$${name}: ${value}\n`
}

getFormat () {
return 'sass'
_buildObjectEntry (key, value, indent, index, metricIndex = 0) {
return indentWith(`${key}: ${this._sanitizePropValue(value)},`, indent + ((!index && !metricIndex) ? 0 : 1))
}
}

module.exports = SassConverter
export default SassConverter
28 changes: 7 additions & 21 deletions src/converters/Scss.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
const reduce = require('lodash.reduce')
const foreach = require('lodash.foreach')

const Converter = require('./Converter')
import Converter from './Converter'

/**
* @extends Converter
*/
class ScssConverter extends Converter {
_convertObjectToMap (property, data) {
let buffer = '(\n'
foreach(data, (value, metric) => {
buffer += ` ${metric}: ${this._sanitizePropValue(value)},\n`
})
buffer += ')'
return `$${this._propertyNameSanitizer(property)}: ${buffer};`
}
format = 'scss'

_convertObjectToVar (prop, data) {
return reduce(data, (all, value, metric) => {
all += `$${this._propertyNameSanitizer(prop, metric)}: ${this._sanitizePropValue(value)};\n`
return all
}, '')
}
mapOpener = '(\n'
mapCloser = ')'

getFormat () {
return 'scss'
_buildVar (name, value) {
return `$${name}: ${value};\n`
}
}

module.exports = ScssConverter
export default ScssConverter

0 comments on commit e005737

Please sign in to comment.