From b71cfe711e4c214f194219581214b9f5e98dba2c Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Thu, 13 Sep 2018 21:37:10 -0500 Subject: [PATCH 1/8] CLI tool rewrite --- __tests__/cli.test.js | 58 +++++++--- __tests__/customConfig.test.js | 2 +- .../{customConfig.js => custom-config.js} | 0 __tests__/fixtures/tailwind-cli-input.css | 3 - __tests__/fixtures/tailwind-cli-output.css | 3 - package.json | 9 +- src/cli.js | 106 +++-------------- src/cli/commands/build.js | 72 ++++++++++++ src/cli/commands/help.js | 73 ++++++++++++ src/cli/commands/index.js | 5 + src/cli/commands/init.js | 29 +++++ src/cli/constants.js | 6 + src/cli/emoji.js | 9 ++ src/cli/main.js | 22 ++++ src/cli/utils.js | 107 ++++++++++++++++++ src/processTailwindFeatures.js | 25 ++-- 16 files changed, 397 insertions(+), 132 deletions(-) rename __tests__/fixtures/{customConfig.js => custom-config.js} (100%) delete mode 100644 __tests__/fixtures/tailwind-cli-input.css delete mode 100644 __tests__/fixtures/tailwind-cli-output.css create mode 100644 src/cli/commands/build.js create mode 100644 src/cli/commands/help.js create mode 100644 src/cli/commands/index.js create mode 100644 src/cli/commands/init.js create mode 100644 src/cli/constants.js create mode 100644 src/cli/emoji.js create mode 100644 src/cli/main.js create mode 100644 src/cli/utils.js diff --git a/__tests__/cli.test.js b/__tests__/cli.test.js index 19364ca08a80..7bf005ee4cdd 100644 --- a/__tests__/cli.test.js +++ b/__tests__/cli.test.js @@ -1,21 +1,49 @@ -import { spawnSync } from 'child_process' -import fs from 'fs' import path from 'path' -function runCli(task, options) { - return spawnSync('node', [`${path.join(process.cwd(), 'lib/cli.js')}`, `${task}`, ...options]) -} +import cli from '../src/cli/main.js' +import constants from '../src/cli/constants.js' +import * as utils from '../src/cli/utils.js' -function pathToFixture(fixture) { - return path.resolve(`${__dirname}/fixtures/${fixture}`) -} +describe('cli', () => { + const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css') + const customConfigPath = path.resolve(__dirname, 'fixtures/custom-config.js') -function readFixture(fixture) { - return fs.readFileSync(pathToFixture(fixture), 'utf8') -} + beforeEach(() => { + utils.log = jest.fn() + utils.writeFile = jest.fn() + }) -test('stdout only contains processed output', () => { - const expected = readFixture('tailwind-cli-output.css') - const result = runCli('build', [pathToFixture('tailwind-cli-input.css')]) - expect(result.stdout.toString()).toEqual(expected) + describe('init', () => { + it('creates a Tailwind config file', () => { + cli(['init']) + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + }) + + it('creates a Tailwind config file in a custom location', () => { + cli(['init', 'custom.js']) + expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js') + expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + }) + }) + + describe('build', () => { + it('compiles CSS file', () => { + cli(['build', inputCssPath]) + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + }) + + it('compiles CSS file using custom configuration', () => { + cli(['build', inputCssPath, '--config', customConfigPath]) + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('400px') + }) + + it('creates compiled CSS file in a custom location', () => { + cli(['build', inputCssPath, '--output', 'custom.css']) + expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.css') + expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + }) + }) }) diff --git a/__tests__/customConfig.test.js b/__tests__/customConfig.test.js index f96ea9a5b4aa..1c3c9a4f39f4 100644 --- a/__tests__/customConfig.test.js +++ b/__tests__/customConfig.test.js @@ -3,7 +3,7 @@ import postcss from 'postcss' import tailwind from '../src/index' test('it uses the values from the custom config file', () => { - return postcss([tailwind(path.resolve(`${__dirname}/fixtures/customConfig.js`))]) + return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-config.js`))]) .process( ` @responsive { diff --git a/__tests__/fixtures/customConfig.js b/__tests__/fixtures/custom-config.js similarity index 100% rename from __tests__/fixtures/customConfig.js rename to __tests__/fixtures/custom-config.js diff --git a/__tests__/fixtures/tailwind-cli-input.css b/__tests__/fixtures/tailwind-cli-input.css deleted file mode 100644 index 563d20e99ce8..000000000000 --- a/__tests__/fixtures/tailwind-cli-input.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - color: green; -} diff --git a/__tests__/fixtures/tailwind-cli-output.css b/__tests__/fixtures/tailwind-cli-output.css deleted file mode 100644 index 563d20e99ce8..000000000000 --- a/__tests__/fixtures/tailwind-cli-output.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - color: green; -} diff --git a/package.json b/package.json index 07fa4a67812e..b75571b5a000 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "test": "jest && eslint . && nsp check" }, "devDependencies": { - "autoprefixer": "^7.1.6", "babel-cli": "^6.6.5", "babel-core": "^6.7.2", "babel-jest": "^20.0.3", @@ -43,16 +42,20 @@ "rimraf": "^2.6.1" }, "dependencies": { - "commander": "^2.11.0", + "autoprefixer": "^7.1.6", + "bytes": "^3.0.0", + "chalk": "^2.4.1", "css.escape": "^1.5.1", "fs-extra": "^4.0.2", "lodash": "^4.17.5", + "node-emoji": "^1.8.1", "perfectionist": "^2.4.0", "postcss": "^6.0.9", "postcss-functions": "^3.0.0", "postcss-js": "^1.0.1", "postcss-nested": "^3.0.0", - "postcss-selector-parser": "^3.1.1" + "postcss-selector-parser": "^3.1.1", + "pretty-hrtime": "^1.0.3" }, "browserslist": [ "> 1%" diff --git a/src/cli.js b/src/cli.js index 5433800b6360..c127e88740b5 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,96 +1,16 @@ -#!/usr/bin/env node -/* eslint-disable no-process-exit */ - -import path from 'path' -import fs from 'fs-extra' -import tailwind from '..' -import postcss from 'postcss' -import process from 'process' -import program from 'commander' - -function writeStrategy(options) { - if (options.output === undefined) { - return output => { - process.stdout.write(output) - } - } - return output => { - fs.outputFileSync(options.output, output) +import main from './cli/main' +import { log, die } from './cli/utils' + +/** + * Runs the CLI application. + */ +function run() { + try { + main(process.argv.slice(2)) + log() + } catch (e) { + die(e.stack) } } -function buildTailwind(inputFile, config, write) { - console.warn('Building Tailwind!') - - const input = fs.readFileSync(inputFile, 'utf8') - - return postcss([tailwind(config)]) - .process(input, { from: inputFile }) - .then(result => { - write(result.css) - console.warn('Finished building Tailwind!') - }) - .catch(error => console.error(error)) -} - -const packageJson = require(path.resolve(__dirname, '../package.json')) - -program.version(packageJson.version).usage(' []') - -program - .command('init [filename]') - .usage('[options] [filename]') - .action((filename = 'tailwind.js') => { - let destination = path.resolve(filename) - - if (!path.extname(filename).includes('.js')) { - destination += '.js' - } - - if (fs.existsSync(destination)) { - console.error(`Destination ${destination} already exists, aborting.`) - process.exit(1) - } - - const output = fs.readFileSync(path.resolve(__dirname, '../defaultConfig.stub.js'), 'utf8') - fs.outputFileSync(destination, output.replace('// let defaultConfig', 'let defaultConfig')) - fs.outputFileSync( - destination, - output.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')") - ) - console.warn(`Generated Tailwind config: ${destination}`) - process.exit() - }) - -program - .command('build') - .usage('[options] ') - .option('-c, --config [path]', 'Path to config file') - .option('-o, --output [path]', 'Output file') - .action((file, options) => { - let inputFile = program.args[0] - - if (!inputFile) { - console.error('No input file given!') - process.exit(1) - } - - buildTailwind(inputFile, options.config, writeStrategy(options)).then(() => { - process.exit() - }) - }) - -program - .command('*', null, { - noHelp: true, - }) - .action(() => { - program.help() - }) - -program.parse(process.argv) - -if (program.args.length === 0) { - program.help() - process.exit() -} +run() diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js new file mode 100644 index 000000000000..5636c6afba24 --- /dev/null +++ b/src/cli/commands/build.js @@ -0,0 +1,72 @@ +import autoprefixer from 'autoprefixer' +import bytes from 'bytes' +import chalk from 'chalk' +import postcss from 'postcss' +import prettyHrtime from 'pretty-hrtime' + +import commands from '.' +import constants from '../constants' +import emoji from '../emoji' +import tailwind from '../..' +import { error, exists, die, log, readFile, writeFile } from '../utils' + +export const usage = 'build [options]' +export const description = 'Compiles Tailwind CSS file.' + +export const options = [ + { + usage: '-c --config ', + description: 'Tailwind config file.', + }, + { + usage: '-o --output ', + description: 'Compiled CSS file. Default: ' + chalk.bold.magenta(constants.defaultOutputFile), + }, +] + +export const optionMap = { + config: ['c', 'config'], + output: ['o', 'output'], +} + +/** + * Runs the command. + * + * @param {string[]} cliParams + * @param {object} cliOptions + */ +export function run(cliParams, cliOptions) { + const time = process.hrtime() + const inputFile = cliParams[1] + const configFile = cliOptions.config && cliOptions.config[0] + const outputFile = (cliOptions.output && cliOptions.output[0]) || constants.defaultOutputFile + + if (!inputFile) { + error('CSS file is required.') + commands.help.forCommand(this) + die() + } + + !exists(inputFile) && die(chalk.bold.magenta(inputFile), 'does not exist.') + configFile && !exists(configFile) && die(chalk.bold.magenta(configFile), 'does not exist.') + + log() + log(emoji.go, 'Building', chalk.bold.cyan(inputFile)) + + const css = readFile(inputFile) + const plugins = [tailwind(configFile), autoprefixer] + + const result = postcss(plugins).process(css, { + from: inputFile, + to: outputFile, + }) + + writeFile(outputFile, result.css) + + const prettyTime = prettyHrtime(process.hrtime(time)) + + log() + log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) + log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) + log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) +} diff --git a/src/cli/commands/help.js b/src/cli/commands/help.js new file mode 100644 index 000000000000..d56ab1352783 --- /dev/null +++ b/src/cli/commands/help.js @@ -0,0 +1,73 @@ +import chalk from 'chalk' +import { forEach, map } from 'lodash' + +import commands from '.' +import constants from '../constants' +import { error, die, log } from '../utils' + +export const usage = 'help [command]' +export const description = 'More information about the command.' + +/** + * Prints general help. + */ +export function forApp() { + const pad = Math.max(...map(commands, 'usage.length')) + 3 + + log() + log('Usage:') + log(' ', chalk.bold(constants.cli + ' [options]')) + log() + log('Commands:') + forEach(commands, command => { + log(' ', chalk.bold(command.usage.padEnd(pad)), command.description) + }) +} + +/** + * Prints help for a command. + * + * @param {object} command + */ +export function forCommand(command) { + log() + log('Usage:') + log(' ', chalk.bold(constants.cli, command.usage)) + log() + log('Description:') + log(' ', chalk.bold(command.description)) + + if (command.options) { + const pad = Math.max(...map(command.options, 'usage.length')) + 3 + + log() + log('Options:') + forEach(command.options, option => { + log(' ', chalk.bold(option.usage.padEnd(pad)), option.description) + }) + } +} + +/** + * Prints invalid command error and general help. Kills the process. + * + * @param {string} commandName + */ +export function invalidCommand(commandName) { + error('Invalid command:', chalk.bold.magenta(commandName)) + forApp() + die() +} + +/** + * Runs the command. + * + * @param {string[]} cliParams + */ +export function run(cliParams) { + const command = cliParams[1] + + !command && forApp() + command && commands[command] && forCommand(commands[command]) + command && !commands[command] && invalidCommand(command) +} diff --git a/src/cli/commands/index.js b/src/cli/commands/index.js new file mode 100644 index 000000000000..180121917cac --- /dev/null +++ b/src/cli/commands/index.js @@ -0,0 +1,5 @@ +import * as help from './help' +import * as init from './init' +import * as build from './build' + +export default { help, init, build } diff --git a/src/cli/commands/init.js b/src/cli/commands/init.js new file mode 100644 index 000000000000..b4f8be305864 --- /dev/null +++ b/src/cli/commands/init.js @@ -0,0 +1,29 @@ +import chalk from 'chalk' + +import constants from '../constants' +import emoji from '../emoji' +import { exists, die, log, readFile, writeFile } from '../utils' + +export const usage = 'init [file]' +export const description = + 'Creates Tailwind config file. Default: ' + chalk.bold.magenta(constants.defaultConfigFile) + +/** + * Runs the command. + * + * @param {string[]} cliParams + */ +export function run(cliParams) { + const file = cliParams[1] || constants.defaultConfigFile + + exists(file) && die(chalk.bold.magenta(file), 'already exists.') + + let stub = readFile(constants.configStubFile) + stub = stub.replace('// let defaultConfig', 'let defaultConfig') + stub = stub.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')") + + writeFile(file, stub) + + log() + log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) +} diff --git a/src/cli/constants.js b/src/cli/constants.js new file mode 100644 index 000000000000..4de42bf35af9 --- /dev/null +++ b/src/cli/constants.js @@ -0,0 +1,6 @@ +export default { + cli: 'yarn tailwind', + defaultConfigFile: 'tailwind.js', + defaultOutputFile: 'output.css', + configStubFile: 'defaultConfig.stub.js', +} diff --git a/src/cli/emoji.js b/src/cli/emoji.js new file mode 100644 index 000000000000..f6c8b028c05e --- /dev/null +++ b/src/cli/emoji.js @@ -0,0 +1,9 @@ +import { get } from 'node-emoji' + +export default { + yes: get('white_check_mark'), + no: get('no_entry_sign'), + go: get('rocket'), + pack: get('package'), + disk: get('floppy_disk'), +} diff --git a/src/cli/main.js b/src/cli/main.js new file mode 100644 index 000000000000..4f347e304ec9 --- /dev/null +++ b/src/cli/main.js @@ -0,0 +1,22 @@ +import chalk from 'chalk' + +import commands from './commands' +import packageJson from '../../package.json' +import { log, parseCliOptions, parseCliParams } from './utils' + +/** + * CLI application entrypoint. + */ +export default function run(args) { + log() + log(chalk.bold(packageJson.name), chalk.bold.cyan(packageJson.version)) + + const params = parseCliParams(args) + const commandName = params[0] || 'help' + + !commands[commandName] && commands.help.invalidCommand(commandName) + + const options = parseCliOptions(args, commands[commandName].optionMap) + + commands[commandName].run(params, options) +} diff --git a/src/cli/utils.js b/src/cli/utils.js new file mode 100644 index 000000000000..fb4d2e6dac07 --- /dev/null +++ b/src/cli/utils.js @@ -0,0 +1,107 @@ +import chalk from 'chalk' +import { ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra' +import { findKey, mapValues, trimStart } from 'lodash' + +import emoji from './emoji' + +/** + * Gets CLI parameters. + * + * @param {string[]} args CLI arguments + * @return {string[]} + */ +export function parseCliParams(args) { + const firstOptionIndex = args.findIndex(arg => arg.startsWith('-')) + + return firstOptionIndex > -1 ? args.slice(0, firstOptionIndex) : args +} + +/** + * Gets mapped CLI options. + * + * @param {string[]} args CLI arguments + * @param {object} [optionMap] + * @return {object} + */ +export function parseCliOptions(args, optionMap = {}) { + let options = {} + let currentOption = [] + + args.forEach(arg => { + const option = arg.startsWith('-') && trimStart(arg, '-').toLowerCase() + const resolvedOption = findKey(optionMap, aliases => aliases.includes(option)) + + if (resolvedOption) { + currentOption = options[resolvedOption] || (options[resolvedOption] = []) + } else if (option) { + currentOption = [] + } else { + currentOption.push(arg) + } + }) + + return { ...mapValues(optionMap, () => undefined), ...options } +} + +/** + * Prints messages to console. + * + * @param {...string} msgs + */ +export function log(...msgs) { + console.log(' ', ...msgs) +} + +/** + * Prints error messages to console. + * + * @param {...string} msgs + */ +export function error(...msgs) { + log() + console.error(' ', emoji.no, chalk.bold.red(msgs.join(' '))) +} + +/** + * Kills the process. Optionally prints error messages to console. + * + * @param {...string} [msgs] + */ +export function die(...msgs) { + msgs.length && error(...msgs) + log() + process.exit(1) // eslint-disable-line +} + +/** + * Checks if path exists. + * + * @param {string} path + * @return {boolean} + */ +export function exists(path) { + return existsSync(path) +} + +/** + * Gets file content. + * + * @param {string} path + * @return {string} + */ +export function readFile(path) { + return readFileSync(path, 'utf-8') +} + +/** + * Writes content to file. + * + * @param {string} path + * @param {string} content + * @return {string} + */ +export function writeFile(path, content) { + ensureFileSync(path) + + return outputFileSync(path, content) +} diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index 51595f6f0093..2726d58c585f 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -1,4 +1,3 @@ -import _ from 'lodash' import postcss from 'postcss' import substituteTailwindAtRules from './lib/substituteTailwindAtRules' @@ -12,18 +11,16 @@ import generateUtilities from './util/generateUtilities' import processPlugins from './util/processPlugins' export default function(getConfig) { - return function(css) { - const config = getConfig() - const processedPlugins = processPlugins(config) - const utilities = generateUtilities(config, processedPlugins.utilities) + const config = getConfig() + const processedPlugins = processPlugins(config) + const utilities = generateUtilities(config, processedPlugins.utilities) - return postcss([ - substituteTailwindAtRules(config, processedPlugins, utilities), - evaluateTailwindFunctions(config), - substituteVariantsAtRules(config, processedPlugins), - substituteResponsiveAtRules(config), - substituteScreenAtRules(config), - substituteClassApplyAtRules(config, utilities), - ]).process(css, { from: _.get(css, 'source.input.file') }) - } + return postcss([ + substituteTailwindAtRules(config, processedPlugins, utilities), + evaluateTailwindFunctions(config), + substituteVariantsAtRules(config, processedPlugins), + substituteResponsiveAtRules(config), + substituteScreenAtRules(config), + substituteClassApplyAtRules(config, utilities), + ]) } From a20b4519d42cd64179aec5a6f74e52175524dc91 Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Fri, 14 Sep 2018 06:38:13 -0500 Subject: [PATCH 2/8] Node hashbang is required for npm bin files --- src/cli.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cli.js b/src/cli.js index c127e88740b5..aa3da12d0c12 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import main from './cli/main' import { log, die } from './cli/utils' From ad695e9c1330d10431bbfe5ad4a9732ed886b590 Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Fri, 14 Sep 2018 06:42:33 -0500 Subject: [PATCH 3/8] CLI tool will now print the correct usage information --- src/cli/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/constants.js b/src/cli/constants.js index 4de42bf35af9..947c3caa5939 100644 --- a/src/cli/constants.js +++ b/src/cli/constants.js @@ -1,5 +1,5 @@ export default { - cli: 'yarn tailwind', + cli: 'tailwind', defaultConfigFile: 'tailwind.js', defaultOutputFile: 'output.css', configStubFile: 'defaultConfig.stub.js', From 6e448ea70af015530c3dcda8e9987b3e014a9a3d Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Fri, 14 Sep 2018 06:46:16 -0500 Subject: [PATCH 4/8] Config stub path was incorrect --- src/cli/constants.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/constants.js b/src/cli/constants.js index 947c3caa5939..ec81cac5a31e 100644 --- a/src/cli/constants.js +++ b/src/cli/constants.js @@ -1,6 +1,8 @@ +import path from 'path'; + export default { cli: 'tailwind', defaultConfigFile: 'tailwind.js', defaultOutputFile: 'output.css', - configStubFile: 'defaultConfig.stub.js', + configStubFile: path.resolve(__dirname, '../../defaultConfig.stub.js') } From 933b557e876b2c71968cd7cd77940f8300c2d6a8 Mon Sep 17 00:00:00 2001 From: mattstypa Date: Fri, 14 Sep 2018 08:28:03 -0500 Subject: [PATCH 5/8] Code style fixes --- src/cli/constants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/constants.js b/src/cli/constants.js index ec81cac5a31e..8ebbaf9346bc 100644 --- a/src/cli/constants.js +++ b/src/cli/constants.js @@ -1,8 +1,8 @@ -import path from 'path'; +import path from 'path' export default { cli: 'tailwind', defaultConfigFile: 'tailwind.js', defaultOutputFile: 'output.css', - configStubFile: path.resolve(__dirname, '../../defaultConfig.stub.js') + configStubFile: path.resolve(__dirname, '../../defaultConfig.stub.js'), } From d91eea88fb779d30490117cef7dd143015a17e28 Mon Sep 17 00:00:00 2001 From: mattstypa Date: Fri, 14 Sep 2018 11:07:23 -0500 Subject: [PATCH 6/8] Reverted changes to processTailwindFeatures that would impact Webpack watch ability. As a result all commands also became Promise based --- __tests__/cli.test.js | 35 +++++++++++--------- src/cli.js | 9 ++---- src/cli/commands/build.js | 58 +++++++++++++++++++--------------- src/cli/commands/help.js | 13 +++++--- src/cli/commands/init.js | 24 +++++++++----- src/cli/main.js | 2 +- src/processTailwindFeatures.js | 25 ++++++++------- 7 files changed, 96 insertions(+), 70 deletions(-) diff --git a/__tests__/cli.test.js b/__tests__/cli.test.js index 7bf005ee4cdd..dd78c90b46a3 100644 --- a/__tests__/cli.test.js +++ b/__tests__/cli.test.js @@ -15,35 +15,40 @@ describe('cli', () => { describe('init', () => { it('creates a Tailwind config file', () => { - cli(['init']) - expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile) - expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + cli(['init']).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + }) }) it('creates a Tailwind config file in a custom location', () => { - cli(['init', 'custom.js']) - expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js') - expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + cli(['init', 'custom.js']).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js') + expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig') + }) }) }) describe('build', () => { it('compiles CSS file', () => { - cli(['build', inputCssPath]) - expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) - expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + cli(['build', inputCssPath]).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + }) }) it('compiles CSS file using custom configuration', () => { - cli(['build', inputCssPath, '--config', customConfigPath]) - expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) - expect(utils.writeFile.mock.calls[0][1]).toContain('400px') + cli(['build', inputCssPath, '--config', customConfigPath]).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) + expect(utils.writeFile.mock.calls[0][1]).toContain('400px') + }) }) it('creates compiled CSS file in a custom location', () => { - cli(['build', inputCssPath, '--output', 'custom.css']) - expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.css') - expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + cli(['build', inputCssPath, '--output', 'custom.css']).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.css') + expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + }) }) }) }) diff --git a/src/cli.js b/src/cli.js index aa3da12d0c12..cb834236807e 100755 --- a/src/cli.js +++ b/src/cli.js @@ -7,12 +7,9 @@ import { log, die } from './cli/utils' * Runs the CLI application. */ function run() { - try { - main(process.argv.slice(2)) - log() - } catch (e) { - die(e.stack) - } + main(process.argv.slice(2)) + .then(() => log()) + .catch(error => die(error.stack)) } run() diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 5636c6afba24..1623baf3d021 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -34,39 +34,47 @@ export const optionMap = { * * @param {string[]} cliParams * @param {object} cliOptions + * @return {Promise} */ export function run(cliParams, cliOptions) { - const time = process.hrtime() - const inputFile = cliParams[1] - const configFile = cliOptions.config && cliOptions.config[0] - const outputFile = (cliOptions.output && cliOptions.output[0]) || constants.defaultOutputFile + return new Promise((resolve, reject) => { + const time = process.hrtime() + const inputFile = cliParams[1] + const configFile = cliOptions.config && cliOptions.config[0] + const outputFile = (cliOptions.output && cliOptions.output[0]) || constants.defaultOutputFile - if (!inputFile) { - error('CSS file is required.') - commands.help.forCommand(this) - die() - } + if (!inputFile) { + error('CSS file is required.') + commands.help.forCommand(this) + die() + } - !exists(inputFile) && die(chalk.bold.magenta(inputFile), 'does not exist.') - configFile && !exists(configFile) && die(chalk.bold.magenta(configFile), 'does not exist.') + !exists(inputFile) && die(chalk.bold.magenta(inputFile), 'does not exist.') + configFile && !exists(configFile) && die(chalk.bold.magenta(configFile), 'does not exist.') - log() - log(emoji.go, 'Building', chalk.bold.cyan(inputFile)) + log() + log(emoji.go, 'Building', chalk.bold.cyan(inputFile)) - const css = readFile(inputFile) - const plugins = [tailwind(configFile), autoprefixer] + const css = readFile(inputFile) - const result = postcss(plugins).process(css, { - from: inputFile, - to: outputFile, - }) + const postcssPromise = postcss([tailwind(configFile), autoprefixer]).process(css, { + from: inputFile, + to: outputFile, + }) + + postcssPromise + .then(result => { + writeFile(outputFile, result.css) - writeFile(outputFile, result.css) + const prettyTime = prettyHrtime(process.hrtime(time)) - const prettyTime = prettyHrtime(process.hrtime(time)) + log() + log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) + log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) + log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) - log() - log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) - log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) - log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) + resolve() + }) + .catch(reject) + }) } diff --git a/src/cli/commands/help.js b/src/cli/commands/help.js index d56ab1352783..c382ed0e5204 100644 --- a/src/cli/commands/help.js +++ b/src/cli/commands/help.js @@ -63,11 +63,16 @@ export function invalidCommand(commandName) { * Runs the command. * * @param {string[]} cliParams + * @return {Promise} */ export function run(cliParams) { - const command = cliParams[1] + return new Promise(resolve => { + const command = cliParams[1] - !command && forApp() - command && commands[command] && forCommand(commands[command]) - command && !commands[command] && invalidCommand(command) + !command && forApp() + command && commands[command] && forCommand(commands[command]) + command && !commands[command] && invalidCommand(command) + + resolve() + }) } diff --git a/src/cli/commands/init.js b/src/cli/commands/init.js index b4f8be305864..d3924b53abff 100644 --- a/src/cli/commands/init.js +++ b/src/cli/commands/init.js @@ -12,18 +12,26 @@ export const description = * Runs the command. * * @param {string[]} cliParams + * @return {Promise} */ export function run(cliParams) { - const file = cliParams[1] || constants.defaultConfigFile + return new Promise(resolve => { + const file = cliParams[1] || constants.defaultConfigFile - exists(file) && die(chalk.bold.magenta(file), 'already exists.') + exists(file) && die(chalk.bold.magenta(file), 'already exists.') - let stub = readFile(constants.configStubFile) - stub = stub.replace('// let defaultConfig', 'let defaultConfig') - stub = stub.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')") + let stub = readFile(constants.configStubFile) + stub = stub.replace('// let defaultConfig', 'let defaultConfig') + stub = stub.replace( + "require('./plugins/container')", + "require('tailwindcss/plugins/container')" + ) - writeFile(file, stub) + writeFile(file, stub) - log() - log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) + log() + log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) + + resolve() + }) } diff --git a/src/cli/main.js b/src/cli/main.js index 4f347e304ec9..41cf205b9289 100644 --- a/src/cli/main.js +++ b/src/cli/main.js @@ -18,5 +18,5 @@ export default function run(args) { const options = parseCliOptions(args, commands[commandName].optionMap) - commands[commandName].run(params, options) + return commands[commandName].run(params, options) } diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index 2726d58c585f..51595f6f0093 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -1,3 +1,4 @@ +import _ from 'lodash' import postcss from 'postcss' import substituteTailwindAtRules from './lib/substituteTailwindAtRules' @@ -11,16 +12,18 @@ import generateUtilities from './util/generateUtilities' import processPlugins from './util/processPlugins' export default function(getConfig) { - const config = getConfig() - const processedPlugins = processPlugins(config) - const utilities = generateUtilities(config, processedPlugins.utilities) + return function(css) { + const config = getConfig() + const processedPlugins = processPlugins(config) + const utilities = generateUtilities(config, processedPlugins.utilities) - return postcss([ - substituteTailwindAtRules(config, processedPlugins, utilities), - evaluateTailwindFunctions(config), - substituteVariantsAtRules(config, processedPlugins), - substituteResponsiveAtRules(config), - substituteScreenAtRules(config), - substituteClassApplyAtRules(config, utilities), - ]) + return postcss([ + substituteTailwindAtRules(config, processedPlugins, utilities), + evaluateTailwindFunctions(config), + substituteVariantsAtRules(config, processedPlugins), + substituteResponsiveAtRules(config), + substituteScreenAtRules(config), + substituteClassApplyAtRules(config, utilities), + ]).process(css, { from: _.get(css, 'source.input.file') }) + } } From 1b1ae8abc91450f4482151730a631f5f1f13dd5e Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Sun, 23 Sep 2018 22:00:50 -0500 Subject: [PATCH 7/8] When no output file is specified for the build command, the result will be piped to stdout --- __tests__/cli.test.js | 21 +++--- src/cli.js | 13 +--- src/cli/commands/build.js | 141 +++++++++++++++++++++++++++----------- src/cli/commands/help.js | 20 +++--- src/cli/commands/init.js | 16 ++--- src/cli/constants.js | 1 - src/cli/main.js | 26 ++++--- src/cli/utils.js | 40 +++++++---- 8 files changed, 172 insertions(+), 106 deletions(-) diff --git a/__tests__/cli.test.js b/__tests__/cli.test.js index dd78c90b46a3..c3aec78c7ff5 100644 --- a/__tests__/cli.test.js +++ b/__tests__/cli.test.js @@ -1,15 +1,16 @@ import path from 'path' -import cli from '../src/cli/main.js' -import constants from '../src/cli/constants.js' -import * as utils from '../src/cli/utils.js' +import cli from '../src/cli/main' +import constants from '../src/cli/constants' +import * as utils from '../src/cli/utils' describe('cli', () => { const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css') const customConfigPath = path.resolve(__dirname, 'fixtures/custom-config.js') beforeEach(() => { - utils.log = jest.fn() + console.log = jest.fn() + process.stdout.write = jest.fn() utils.writeFile = jest.fn() }) @@ -32,21 +33,19 @@ describe('cli', () => { describe('build', () => { it('compiles CSS file', () => { cli(['build', inputCssPath]).then(() => { - expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) - expect(utils.writeFile.mock.calls[0][1]).toContain('.example') + expect(process.stdout.write.mock.calls[0][0]).toContain('.example') }) }) it('compiles CSS file using custom configuration', () => { cli(['build', inputCssPath, '--config', customConfigPath]).then(() => { - expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultOutputFile) - expect(utils.writeFile.mock.calls[0][1]).toContain('400px') + expect(process.stdout.write.mock.calls[0][0]).toContain('400px') }) }) - it('creates compiled CSS file in a custom location', () => { - cli(['build', inputCssPath, '--output', 'custom.css']).then(() => { - expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.css') + it('creates compiled CSS file', () => { + cli(['build', inputCssPath, '--output', 'output.css']).then(() => { + expect(utils.writeFile.mock.calls[0][0]).toEqual('output.css') expect(utils.writeFile.mock.calls[0][1]).toContain('.example') }) }) diff --git a/src/cli.js b/src/cli.js index cb834236807e..24fb66824a26 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,15 +1,6 @@ #!/usr/bin/env node import main from './cli/main' -import { log, die } from './cli/utils' +import { die } from './cli/utils' -/** - * Runs the CLI application. - */ -function run() { - main(process.argv.slice(2)) - .then(() => log()) - .catch(error => die(error.stack)) -} - -run() +main(process.argv.slice(2)).catch(error => die(error.stack)) diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 1623baf3d021..7b97d5ec46a4 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -5,76 +5,135 @@ import postcss from 'postcss' import prettyHrtime from 'pretty-hrtime' import commands from '.' -import constants from '../constants' import emoji from '../emoji' import tailwind from '../..' -import { error, exists, die, log, readFile, writeFile } from '../utils' +import { die, error, exists, footer, header, log, readFile, writeFile } from '../utils' export const usage = 'build [options]' export const description = 'Compiles Tailwind CSS file.' export const options = [ { - usage: '-c --config ', - description: 'Tailwind config file.', + usage: '-o, --output ', + description: 'Output file.', }, { - usage: '-o --output ', - description: 'Compiled CSS file. Default: ' + chalk.bold.magenta(constants.defaultOutputFile), + usage: '-c, --config ', + description: 'Tailwind config file.', }, ] export const optionMap = { - config: ['c', 'config'], - output: ['o', 'output'], + output: ['output', 'o'], + config: ['config', 'c'], } /** - * Runs the command. + * Prints the error message and stops the process. * - * @param {string[]} cliParams - * @param {object} cliOptions + * @param {...string} [msgs] + */ +function stop(...msgs) { + header() + error(...msgs) + die() +} + +/** + * Prints the error message and help for this command, then stops the process. + * + * @param {...string} [msgs] + */ +function stopWithHelp(...msgs) { + header() + error(...msgs) + commands.help.forCommand(commands.build) + die() +} + +/** + * Compiles CSS file. + * + * @param {string} inputFile + * @param {string} configFile + * @param {string} outputFile * @return {Promise} */ -export function run(cliParams, cliOptions) { +function build(inputFile, configFile, outputFile) { + const css = readFile(inputFile) + return new Promise((resolve, reject) => { - const time = process.hrtime() - const inputFile = cliParams[1] - const configFile = cliOptions.config && cliOptions.config[0] - const outputFile = (cliOptions.output && cliOptions.output[0]) || constants.defaultOutputFile + postcss([tailwind(configFile), autoprefixer]) + .process(css, { + from: inputFile, + to: outputFile, + }) + .then(resolve) + .catch(reject) + }) +} - if (!inputFile) { - error('CSS file is required.') - commands.help.forCommand(this) - die() - } +/** + * Compiles CSS file and writes it to stdout. + * + * @param {string} inputFile + * @param {string} configFile + * @param {string} outputFile + * @return {Promise} + */ +function buildToStdout(inputFile, configFile, outputFile) { + return build(inputFile, configFile, outputFile).then(result => process.stdout.write(result.css)) +} - !exists(inputFile) && die(chalk.bold.magenta(inputFile), 'does not exist.') - configFile && !exists(configFile) && die(chalk.bold.magenta(configFile), 'does not exist.') +/** + * Compiles CSS file and writes it to a file. + * + * @param {string} inputFile + * @param {string} configFile + * @param {string} outputFile + * @param {int[]} startTime + * @return {Promise} + */ +function buildToFile(inputFile, configFile, outputFile, startTime) { + header() + log() + log(emoji.go, 'Building...', chalk.bold.cyan(inputFile)) - log() - log(emoji.go, 'Building', chalk.bold.cyan(inputFile)) + return build(inputFile, configFile, outputFile).then(result => { + writeFile(outputFile, result.css) - const css = readFile(inputFile) + const prettyTime = prettyHrtime(process.hrtime(startTime)) - const postcssPromise = postcss([tailwind(configFile), autoprefixer]).process(css, { - from: inputFile, - to: outputFile, - }) + log() + log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) + log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) + log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) + footer() + }) +} - postcssPromise - .then(result => { - writeFile(outputFile, result.css) +/** + * Runs the command. + * + * @param {string[]} cliParams + * @param {object} cliOptions + * @return {Promise} + */ +export function run(cliParams, cliOptions) { + return new Promise((resolve, reject) => { + const startTime = process.hrtime() + const inputFile = cliParams[0] + const configFile = cliOptions.config && cliOptions.config[0] + const outputFile = cliOptions.output && cliOptions.output[0] - const prettyTime = prettyHrtime(process.hrtime(time)) + !inputFile && stopWithHelp('CSS file is required.') + !exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.') + configFile && !exists(configFile) && stop(chalk.bold.magenta(configFile), 'does not exist.') - log() - log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) - log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) - log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) + const promise = outputFile + ? buildToFile(inputFile, configFile, outputFile, startTime) + : buildToStdout(inputFile, configFile, outputFile) - resolve() - }) - .catch(reject) + promise.then(resolve).catch(reject) }) } diff --git a/src/cli/commands/help.js b/src/cli/commands/help.js index c382ed0e5204..35642756f43b 100644 --- a/src/cli/commands/help.js +++ b/src/cli/commands/help.js @@ -1,9 +1,9 @@ import chalk from 'chalk' -import { forEach, map } from 'lodash' +import { forEach, map, padEnd } from 'lodash' import commands from '.' import constants from '../constants' -import { error, die, log } from '../utils' +import { die, error, footer, header, log } from '../utils' export const usage = 'help [command]' export const description = 'More information about the command.' @@ -20,7 +20,7 @@ export function forApp() { log() log('Commands:') forEach(commands, command => { - log(' ', chalk.bold(command.usage.padEnd(pad)), command.description) + log(' ', chalk.bold(padEnd(command.usage, pad)), command.description) }) } @@ -43,7 +43,7 @@ export function forCommand(command) { log() log('Options:') forEach(command.options, option => { - log(' ', chalk.bold(option.usage.padEnd(pad)), option.description) + log(' ', chalk.bold(padEnd(option.usage, pad)), option.description) }) } } @@ -67,12 +67,16 @@ export function invalidCommand(commandName) { */ export function run(cliParams) { return new Promise(resolve => { - const command = cliParams[1] + header() - !command && forApp() - command && commands[command] && forCommand(commands[command]) - command && !commands[command] && invalidCommand(command) + const commandName = cliParams[0] + const command = commands[commandName] + !commandName && forApp() + commandName && command && forCommand(command) + commandName && !command && invalidCommand(commandName) + + footer() resolve() }) } diff --git a/src/cli/commands/init.js b/src/cli/commands/init.js index d3924b53abff..719198d6ac76 100644 --- a/src/cli/commands/init.js +++ b/src/cli/commands/init.js @@ -2,7 +2,7 @@ import chalk from 'chalk' import constants from '../constants' import emoji from '../emoji' -import { exists, die, log, readFile, writeFile } from '../utils' +import { die, exists, footer, header, log, readFile, writeFile } from '../utils' export const usage = 'init [file]' export const description = @@ -16,22 +16,22 @@ export const description = */ export function run(cliParams) { return new Promise(resolve => { - const file = cliParams[1] || constants.defaultConfigFile + header() + + const file = cliParams[0] || constants.defaultConfigFile exists(file) && die(chalk.bold.magenta(file), 'already exists.') - let stub = readFile(constants.configStubFile) - stub = stub.replace('// let defaultConfig', 'let defaultConfig') - stub = stub.replace( - "require('./plugins/container')", - "require('tailwindcss/plugins/container')" - ) + const stub = readFile(constants.configStubFile) + .replace('// let defaultConfig', 'let defaultConfig') + .replace("require('./plugins/container')", "require('tailwindcss/plugins/container')") writeFile(file, stub) log() log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) + footer() resolve() }) } diff --git a/src/cli/constants.js b/src/cli/constants.js index 8ebbaf9346bc..3d15c9340d94 100644 --- a/src/cli/constants.js +++ b/src/cli/constants.js @@ -3,6 +3,5 @@ import path from 'path' export default { cli: 'tailwind', defaultConfigFile: 'tailwind.js', - defaultOutputFile: 'output.css', configStubFile: path.resolve(__dirname, '../../defaultConfig.stub.js'), } diff --git a/src/cli/main.js b/src/cli/main.js index 41cf205b9289..05a4f83a9f1b 100644 --- a/src/cli/main.js +++ b/src/cli/main.js @@ -1,22 +1,20 @@ -import chalk from 'chalk' - import commands from './commands' -import packageJson from '../../package.json' -import { log, parseCliOptions, parseCliParams } from './utils' +import { parseCliOptions, parseCliParams } from './utils' /** * CLI application entrypoint. + * + * @param {string[]} cliArgs + * @return {Promise} */ -export default function run(args) { - log() - log(chalk.bold(packageJson.name), chalk.bold.cyan(packageJson.version)) - - const params = parseCliParams(args) - const commandName = params[0] || 'help' - - !commands[commandName] && commands.help.invalidCommand(commandName) +export default function run(cliArgs) { + return new Promise((resolve, reject) => { + const params = parseCliParams(cliArgs) + const command = commands[params[0]] + const options = command ? parseCliOptions(cliArgs, command.optionMap) : {} - const options = parseCliOptions(args, commands[commandName].optionMap) + const promise = command ? command.run(params.slice(1), options) : commands.help.run(params) - return commands[commandName].run(params, options) + promise.then(resolve).catch(reject) + }) } diff --git a/src/cli/utils.js b/src/cli/utils.js index fb4d2e6dac07..096b0ac4b758 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -3,32 +3,33 @@ import { ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-ext import { findKey, mapValues, trimStart } from 'lodash' import emoji from './emoji' +import packageJson from '../../package.json' /** * Gets CLI parameters. * - * @param {string[]} args CLI arguments + * @param {string[]} cliArgs * @return {string[]} */ -export function parseCliParams(args) { - const firstOptionIndex = args.findIndex(arg => arg.startsWith('-')) +export function parseCliParams(cliArgs) { + const firstOptionIndex = cliArgs.findIndex(cliArg => cliArg.startsWith('-')) - return firstOptionIndex > -1 ? args.slice(0, firstOptionIndex) : args + return firstOptionIndex > -1 ? cliArgs.slice(0, firstOptionIndex) : cliArgs } /** * Gets mapped CLI options. * - * @param {string[]} args CLI arguments + * @param {string[]} cliArgs * @param {object} [optionMap] * @return {object} */ -export function parseCliOptions(args, optionMap = {}) { +export function parseCliOptions(cliArgs, optionMap = {}) { let options = {} let currentOption = [] - args.forEach(arg => { - const option = arg.startsWith('-') && trimStart(arg, '-').toLowerCase() + cliArgs.forEach(cliArg => { + const option = cliArg.startsWith('-') && trimStart(cliArg, '-').toLowerCase() const resolvedOption = findKey(optionMap, aliases => aliases.includes(option)) if (resolvedOption) { @@ -36,7 +37,7 @@ export function parseCliOptions(args, optionMap = {}) { } else if (option) { currentOption = [] } else { - currentOption.push(arg) + currentOption.push(cliArg) } }) @@ -46,16 +47,31 @@ export function parseCliOptions(args, optionMap = {}) { /** * Prints messages to console. * - * @param {...string} msgs + * @param {...string} [msgs] */ export function log(...msgs) { console.log(' ', ...msgs) } +/** + * Prints application header to console. + */ +export function header() { + log() + log(chalk.bold(packageJson.name), chalk.bold.cyan(packageJson.version)) +} + +/** + * Prints application footer to console. + */ +export function footer() { + log() +} + /** * Prints error messages to console. * - * @param {...string} msgs + * @param {...string} [msgs] */ export function error(...msgs) { log() @@ -69,7 +85,7 @@ export function error(...msgs) { */ export function die(...msgs) { msgs.length && error(...msgs) - log() + footer() process.exit(1) // eslint-disable-line } From eb4da80ede78e6e581c5363ae8fb89559801db1a Mon Sep 17 00:00:00 2001 From: Matt Stypa Date: Mon, 24 Sep 2018 07:55:24 -0500 Subject: [PATCH 8/8] Code style updates --- __tests__/cli.test.js | 2 +- src/cli.js | 4 ++-- src/cli/commands/build.js | 50 +++++++++++++++++++++------------------ src/cli/commands/help.js | 49 ++++++++++++++++++++------------------ src/cli/commands/init.js | 22 +++++++++-------- src/cli/constants.js | 8 +++---- src/cli/emoji.js | 12 ++++------ src/cli/main.js | 12 ++++++---- src/cli/utils.js | 2 +- 9 files changed, 84 insertions(+), 77 deletions(-) diff --git a/__tests__/cli.test.js b/__tests__/cli.test.js index c3aec78c7ff5..5dcbcf69a465 100644 --- a/__tests__/cli.test.js +++ b/__tests__/cli.test.js @@ -1,7 +1,7 @@ import path from 'path' import cli from '../src/cli/main' -import constants from '../src/cli/constants' +import * as constants from '../src/cli/constants' import * as utils from '../src/cli/utils' describe('cli', () => { diff --git a/src/cli.js b/src/cli.js index 24fb66824a26..312ff6c26895 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,6 +1,6 @@ #!/usr/bin/env node import main from './cli/main' -import { die } from './cli/utils' +import * as utils from './cli/utils' -main(process.argv.slice(2)).catch(error => die(error.stack)) +main(process.argv.slice(2)).catch(error => utils.die(error.stack)) diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 7b97d5ec46a4..ab3939854deb 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -4,10 +4,11 @@ import chalk from 'chalk' import postcss from 'postcss' import prettyHrtime from 'pretty-hrtime' -import commands from '.' -import emoji from '../emoji' import tailwind from '../..' -import { die, error, exists, footer, header, log, readFile, writeFile } from '../utils' + +import commands from '.' +import * as emoji from '../emoji' +import * as utils from '../utils' export const usage = 'build [options]' export const description = 'Compiles Tailwind CSS file.' @@ -34,9 +35,9 @@ export const optionMap = { * @param {...string} [msgs] */ function stop(...msgs) { - header() - error(...msgs) - die() + utils.header() + utils.error(...msgs) + utils.die() } /** @@ -45,10 +46,10 @@ function stop(...msgs) { * @param {...string} [msgs] */ function stopWithHelp(...msgs) { - header() - error(...msgs) + utils.header() + utils.error(...msgs) commands.help.forCommand(commands.build) - die() + utils.die() } /** @@ -60,7 +61,7 @@ function stopWithHelp(...msgs) { * @return {Promise} */ function build(inputFile, configFile, outputFile) { - const css = readFile(inputFile) + const css = utils.readFile(inputFile) return new Promise((resolve, reject) => { postcss([tailwind(configFile), autoprefixer]) @@ -95,20 +96,20 @@ function buildToStdout(inputFile, configFile, outputFile) { * @return {Promise} */ function buildToFile(inputFile, configFile, outputFile, startTime) { - header() - log() - log(emoji.go, 'Building...', chalk.bold.cyan(inputFile)) + utils.header() + utils.log() + utils.log(emoji.go, 'Building...', chalk.bold.cyan(inputFile)) return build(inputFile, configFile, outputFile).then(result => { - writeFile(outputFile, result.css) + utils.writeFile(outputFile, result.css) const prettyTime = prettyHrtime(process.hrtime(startTime)) - log() - log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) - log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) - log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) - footer() + utils.log() + utils.log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime)) + utils.log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length))) + utils.log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile)) + utils.footer() }) } @@ -127,13 +128,16 @@ export function run(cliParams, cliOptions) { const outputFile = cliOptions.output && cliOptions.output[0] !inputFile && stopWithHelp('CSS file is required.') - !exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.') - configFile && !exists(configFile) && stop(chalk.bold.magenta(configFile), 'does not exist.') + !utils.exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.') + + configFile && + !utils.exists(configFile) && + stop(chalk.bold.magenta(configFile), 'does not exist.') - const promise = outputFile + const buildPromise = outputFile ? buildToFile(inputFile, configFile, outputFile, startTime) : buildToStdout(inputFile, configFile, outputFile) - promise.then(resolve).catch(reject) + buildPromise.then(resolve).catch(reject) }) } diff --git a/src/cli/commands/help.js b/src/cli/commands/help.js index 35642756f43b..a0803145694b 100644 --- a/src/cli/commands/help.js +++ b/src/cli/commands/help.js @@ -2,25 +2,27 @@ import chalk from 'chalk' import { forEach, map, padEnd } from 'lodash' import commands from '.' -import constants from '../constants' -import { die, error, footer, header, log } from '../utils' +import * as constants from '../constants' +import * as utils from '../utils' export const usage = 'help [command]' export const description = 'More information about the command.' +const PADDING_SIZE = 3 + /** * Prints general help. */ export function forApp() { - const pad = Math.max(...map(commands, 'usage.length')) + 3 + const pad = Math.max(...map(commands, 'usage.length')) + PADDING_SIZE - log() - log('Usage:') - log(' ', chalk.bold(constants.cli + ' [options]')) - log() - log('Commands:') + utils.log() + utils.log('Usage:') + utils.log(' ', chalk.bold(constants.cli + ' [options]')) + utils.log() + utils.log('Commands:') forEach(commands, command => { - log(' ', chalk.bold(padEnd(command.usage, pad)), command.description) + utils.log(' ', chalk.bold(padEnd(command.usage, pad)), command.description) }) } @@ -30,20 +32,20 @@ export function forApp() { * @param {object} command */ export function forCommand(command) { - log() - log('Usage:') - log(' ', chalk.bold(constants.cli, command.usage)) - log() - log('Description:') - log(' ', chalk.bold(command.description)) + utils.log() + utils.log('Usage:') + utils.log(' ', chalk.bold(constants.cli, command.usage)) + utils.log() + utils.log('Description:') + utils.log(' ', chalk.bold(command.description)) if (command.options) { - const pad = Math.max(...map(command.options, 'usage.length')) + 3 + const pad = Math.max(...map(command.options, 'usage.length')) + PADDING_SIZE - log() - log('Options:') + utils.log() + utils.log('Options:') forEach(command.options, option => { - log(' ', chalk.bold(padEnd(option.usage, pad)), option.description) + utils.log(' ', chalk.bold(padEnd(option.usage, pad)), option.description) }) } } @@ -54,9 +56,9 @@ export function forCommand(command) { * @param {string} commandName */ export function invalidCommand(commandName) { - error('Invalid command:', chalk.bold.magenta(commandName)) + utils.error('Invalid command:', chalk.bold.magenta(commandName)) forApp() - die() + utils.die() } /** @@ -67,7 +69,7 @@ export function invalidCommand(commandName) { */ export function run(cliParams) { return new Promise(resolve => { - header() + utils.header() const commandName = cliParams[0] const command = commands[commandName] @@ -76,7 +78,8 @@ export function run(cliParams) { commandName && command && forCommand(command) commandName && !command && invalidCommand(commandName) - footer() + utils.footer() + resolve() }) } diff --git a/src/cli/commands/init.js b/src/cli/commands/init.js index 719198d6ac76..1d127240c337 100644 --- a/src/cli/commands/init.js +++ b/src/cli/commands/init.js @@ -1,8 +1,8 @@ import chalk from 'chalk' -import constants from '../constants' -import emoji from '../emoji' -import { die, exists, footer, header, log, readFile, writeFile } from '../utils' +import * as constants from '../constants' +import * as emoji from '../emoji' +import * as utils from '../utils' export const usage = 'init [file]' export const description = @@ -16,22 +16,24 @@ export const description = */ export function run(cliParams) { return new Promise(resolve => { - header() + utils.header() const file = cliParams[0] || constants.defaultConfigFile - exists(file) && die(chalk.bold.magenta(file), 'already exists.') + utils.exists(file) && utils.die(chalk.bold.magenta(file), 'already exists.') - const stub = readFile(constants.configStubFile) + const stub = utils + .readFile(constants.configStubFile) .replace('// let defaultConfig', 'let defaultConfig') .replace("require('./plugins/container')", "require('tailwindcss/plugins/container')") - writeFile(file, stub) + utils.writeFile(file, stub) - log() - log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) + utils.log() + utils.log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file)) + + utils.footer() - footer() resolve() }) } diff --git a/src/cli/constants.js b/src/cli/constants.js index 3d15c9340d94..3e9fdb42da4c 100644 --- a/src/cli/constants.js +++ b/src/cli/constants.js @@ -1,7 +1,5 @@ import path from 'path' -export default { - cli: 'tailwind', - defaultConfigFile: 'tailwind.js', - configStubFile: path.resolve(__dirname, '../../defaultConfig.stub.js'), -} +export const cli = 'tailwind' +export const defaultConfigFile = 'tailwind.js' +export const configStubFile = path.resolve(__dirname, '../../defaultConfig.stub.js') diff --git a/src/cli/emoji.js b/src/cli/emoji.js index f6c8b028c05e..58655706672b 100644 --- a/src/cli/emoji.js +++ b/src/cli/emoji.js @@ -1,9 +1,7 @@ import { get } from 'node-emoji' -export default { - yes: get('white_check_mark'), - no: get('no_entry_sign'), - go: get('rocket'), - pack: get('package'), - disk: get('floppy_disk'), -} +export const yes = get('white_check_mark') +export const no = get('no_entry_sign') +export const go = get('rocket') +export const pack = get('package') +export const disk = get('floppy_disk') diff --git a/src/cli/main.js b/src/cli/main.js index 05a4f83a9f1b..06d2947a409a 100644 --- a/src/cli/main.js +++ b/src/cli/main.js @@ -1,5 +1,5 @@ import commands from './commands' -import { parseCliOptions, parseCliParams } from './utils' +import * as utils from './utils' /** * CLI application entrypoint. @@ -9,12 +9,14 @@ import { parseCliOptions, parseCliParams } from './utils' */ export default function run(cliArgs) { return new Promise((resolve, reject) => { - const params = parseCliParams(cliArgs) + const params = utils.parseCliParams(cliArgs) const command = commands[params[0]] - const options = command ? parseCliOptions(cliArgs, command.optionMap) : {} + const options = command ? utils.parseCliOptions(cliArgs, command.optionMap) : {} - const promise = command ? command.run(params.slice(1), options) : commands.help.run(params) + const commandPromise = command + ? command.run(params.slice(1), options) + : commands.help.run(params) - promise.then(resolve).catch(reject) + commandPromise.then(resolve).catch(reject) }) } diff --git a/src/cli/utils.js b/src/cli/utils.js index 096b0ac4b758..ac138b854c25 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -2,7 +2,7 @@ import chalk from 'chalk' import { ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra' import { findKey, mapValues, trimStart } from 'lodash' -import emoji from './emoji' +import * as emoji from './emoji' import packageJson from '../../package.json' /**