Merge pull request #793 from MattStypa/next-cli-tweaks

CLI tweaks
This commit is contained in:
Adam Wathan 2019-03-20 10:01:36 -04:00 committed by GitHub
commit b8e7c12cfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 154 additions and 65 deletions

View File

@ -3,6 +3,7 @@ import path from 'path'
import cli from '../src/cli/main'
import * as constants from '../src/constants'
import * as utils from '../src/cli/utils'
import runInTempDirectory from '../jest/runInTempDirectory'
describe('cli', () => {
const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css')
@ -13,37 +14,30 @@ describe('cli', () => {
beforeEach(() => {
console.log = jest.fn()
process.stdout.write = jest.fn()
utils.writeFile = jest.fn()
})
describe('init', () => {
it('creates a Tailwind config file', () => {
return cli(['init']).then(() => {
expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile)
})
})
it('creates a Tailwind config file in a custom location', () => {
return cli(['init', 'custom.js']).then(() => {
expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js')
})
})
it('creates a Tailwind config file without comments', () => {
return cli(['init', '--no-comments']).then(() => {
expect(utils.writeFile.mock.calls[0][1]).not.toContain('/**')
})
})
it('creates a simple Tailwind config file', () => {
return cli(['init']).then(() => {
expect(utils.writeFile.mock.calls[0][1]).toEqual(simpleConfigFixture)
return runInTempDirectory(() => {
return cli(['init']).then(() => {
expect(utils.readFile(constants.defaultConfigFile)).toEqual(simpleConfigFixture)
})
})
})
it('creates a full Tailwind config file', () => {
return cli(['init', '--full']).then(() => {
expect(utils.writeFile.mock.calls[0][1]).toEqual(defaultConfigFixture)
return runInTempDirectory(() => {
return cli(['init', '--full']).then(() => {
expect(utils.readFile(constants.defaultConfigFile)).toEqual(defaultConfigFixture)
})
})
})
it('creates a Tailwind config file in a custom location', () => {
return runInTempDirectory(() => {
return cli(['init', 'custom.js']).then(() => {
expect(utils.exists('custom.js')).toEqual(true)
})
})
})
})
@ -62,9 +56,10 @@ describe('cli', () => {
})
it('creates compiled CSS file', () => {
return 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')
return runInTempDirectory(() => {
return cli(['build', inputCssPath, '--output', 'output.css']).then(() => {
expect(utils.readFile('output.css')).toContain('.example')
})
})
})

View File

@ -56,4 +56,18 @@ describe('cli utils', () => {
expect(result).toEqual({ test: ['c', 'd', 'h'] })
})
})
describe('getSimplePath', () => {
it('strips leading ./', () => {
const result = utils.getSimplePath('./test')
expect(result).toEqual('test')
})
it('returns unchanged path if it does not begin with ./', () => {
const result = utils.getSimplePath('../test')
expect(result).toEqual('../test')
})
})
})

View File

@ -1,21 +1,9 @@
import fs from 'fs'
import path from 'path'
import rimraf from 'rimraf'
import postcss from 'postcss'
import tailwind from '../src/index'
import { defaultConfigFile } from '../src/constants'
function inTempDirectory(callback) {
return new Promise(resolve => {
rimraf.sync('./__tmp')
fs.mkdirSync(path.resolve('./__tmp'))
process.chdir(path.resolve('./__tmp'))
callback().then(() => {
process.chdir(path.resolve('../'))
rimraf('./__tmp', resolve)
})
})
}
import inTempDirectory from '../jest/runInTempDirectory'
test('it uses the values from the custom config file', () => {
return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-config.js`))])

View File

@ -0,0 +1,21 @@
import fs from 'fs'
import path from 'path'
import rimraf from 'rimraf'
const tmpPath = path.resolve(__dirname, '../__tmp')
export default function(callback) {
return new Promise(resolve => {
const currentPath = process.cwd()
rimraf.sync(tmpPath)
fs.mkdirSync(tmpPath)
process.chdir(tmpPath)
callback().then(() => {
process.chdir(currentPath)
rimraf(tmpPath, resolve)
})
})
}

46
src/cli/colors.js Normal file
View File

@ -0,0 +1,46 @@
import chalk from 'chalk'
/**
* Applies colors to emphasize
*
* @param {...string} msgs
*/
export function bold(...msgs) {
return chalk.bold(...msgs)
}
/**
* Applies colors to inform
*
* @param {...string} msgs
*/
export function info(...msgs) {
return chalk.bold.cyan(...msgs)
}
/**
* Applies colors to signify error
*
* @param {...string} msgs
*/
export function error(...msgs) {
return chalk.bold.red(...msgs)
}
/**
* Applies colors to represent a command
*
* @param {...string} msgs
*/
export function command(...msgs) {
return chalk.bold.magenta(...msgs)
}
/**
* Applies colors to represent a file
*
* @param {...string} msgs
*/
export function file(...msgs) {
return chalk.bold.magenta(...msgs)
}

View File

@ -1,12 +1,12 @@
import autoprefixer from 'autoprefixer'
import bytes from 'bytes'
import chalk from 'chalk'
import prettyHrtime from 'pretty-hrtime'
import tailwind from '../..'
import commands from '.'
import compile from '../compile'
import * as colors from '../colors'
import * as emoji from '../emoji'
import * as utils from '../utils'
@ -75,9 +75,12 @@ function buildToStdout(compileOptions) {
* @return {Promise}
*/
function buildToFile(compileOptions, startTime) {
const inputFileSimplePath = utils.getSimplePath(compileOptions.inputFile)
const outputFileSimplePath = utils.getSimplePath(compileOptions.outputFile)
utils.header()
utils.log()
utils.log(emoji.go, 'Building...', chalk.bold.cyan(compileOptions.inputFile))
utils.log(emoji.go, 'Building...', colors.file(inputFileSimplePath))
return compile(compileOptions).then(result => {
utils.writeFile(compileOptions.outputFile, result.css)
@ -85,9 +88,9 @@ function buildToFile(compileOptions, startTime) {
const prettyTime = prettyHrtime(process.hrtime(startTime))
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(compileOptions.outputFile))
utils.log(emoji.yes, 'Finished in', colors.info(prettyTime))
utils.log(emoji.pack, 'Size:', colors.info(bytes(result.css.length)))
utils.log(emoji.disk, 'Saved to', colors.file(outputFileSimplePath))
utils.footer()
})
}
@ -106,13 +109,15 @@ export function run(cliParams, cliOptions) {
const configFile = cliOptions.config && cliOptions.config[0]
const outputFile = cliOptions.output && cliOptions.output[0]
const autoprefix = !cliOptions.noAutoprefixer
const inputFileSimplePath = utils.getSimplePath(inputFile)
const configFileSimplePath = utils.getSimplePath(configFile)
!inputFile && stopWithHelp('CSS file is required.')
!utils.exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.')
!utils.exists(inputFile) && stop(colors.file(inputFileSimplePath), 'does not exist.')
configFile &&
!utils.exists(configFile) &&
stop(chalk.bold.magenta(configFile), 'does not exist.')
stop(colors.file(configFileSimplePath), 'does not exist.')
const compileOptions = {
inputFile,

View File

@ -1,8 +1,8 @@
import chalk from 'chalk'
import { forEach, map, padEnd } from 'lodash'
import commands from '.'
import * as constants from '../../constants'
import * as colors from '../colors'
import * as utils from '../utils'
export const usage = 'help [command]'
@ -18,11 +18,11 @@ export function forApp() {
utils.log()
utils.log('Usage:')
utils.log(' ', chalk.bold(constants.cli + ' <command> [options]'))
utils.log(' ', colors.bold(constants.cli + ' <command> [options]'))
utils.log()
utils.log('Commands:')
forEach(commands, command => {
utils.log(' ', chalk.bold(padEnd(command.usage, pad)), command.description)
utils.log(' ', colors.bold(padEnd(command.usage, pad)), command.description)
})
}
@ -34,10 +34,10 @@ export function forApp() {
export function forCommand(command) {
utils.log()
utils.log('Usage:')
utils.log(' ', chalk.bold(constants.cli, command.usage))
utils.log(' ', colors.bold(constants.cli, command.usage))
utils.log()
utils.log('Description:')
utils.log(' ', chalk.bold(command.description))
utils.log(' ', colors.bold(command.description))
if (command.options) {
const pad = Math.max(...map(command.options, 'usage.length')) + PADDING_SIZE
@ -45,7 +45,7 @@ export function forCommand(command) {
utils.log()
utils.log('Options:')
forEach(command.options, option => {
utils.log(' ', chalk.bold(padEnd(option.usage, pad)), option.description)
utils.log(' ', colors.bold(padEnd(option.usage, pad)), option.description)
})
}
}
@ -56,7 +56,7 @@ export function forCommand(command) {
* @param {string} commandName
*/
export function invalidCommand(commandName) {
utils.error('Invalid command:', chalk.bold.magenta(commandName))
utils.error('Invalid command:', colors.command(commandName))
forApp()
utils.die()
}

View File

@ -1,12 +1,12 @@
import chalk from 'chalk'
import * as constants from '../../constants'
import * as colors from '../colors'
import * as emoji from '../emoji'
import * as utils from '../utils'
export const usage = 'init [file]'
export const description =
'Creates Tailwind config file. Default: ' + chalk.bold.magenta(constants.defaultConfigFile)
'Creates Tailwind config file. Default: ' +
colors.file(utils.getSimplePath(constants.defaultConfigFile))
export const options = [
{
@ -32,16 +32,16 @@ export function run(cliParams, cliOptions) {
const full = cliOptions.full
const file = cliParams[0] || constants.defaultConfigFile
const simplePath = utils.getSimplePath(file)
utils.exists(file) && utils.die(chalk.bold.magenta(file), 'already exists.')
utils.exists(file) && utils.die(colors.file(simplePath), 'already exists.')
const stubFile = full ? constants.defaultConfigStubFile : constants.simpleConfigStubFile
const stub = utils.readFile(stubFile)
utils.writeFile(file, stub)
utils.copyFile(stubFile, file)
utils.log()
utils.log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file))
utils.log(emoji.yes, 'Created Tailwind config file:', colors.file(simplePath))
utils.footer()

View File

@ -1,7 +1,7 @@
import chalk from 'chalk'
import { ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra'
import { findKey, mapValues, trimStart } from 'lodash'
import { copyFileSync, ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra'
import { findKey, mapValues, startsWith, trimStart } from 'lodash'
import * as colors from './colors'
import * as emoji from './emoji'
import packageJson from '../../package.json'
@ -58,7 +58,7 @@ export function log(...msgs) {
*/
export function header() {
log()
log(chalk.bold(packageJson.name), chalk.bold.cyan(packageJson.version))
log(colors.bold(packageJson.name), colors.info(packageJson.version))
}
/**
@ -75,7 +75,7 @@ export function footer() {
*/
export function error(...msgs) {
log()
console.error(' ', emoji.no, chalk.bold.red(msgs.join(' ')))
console.error(' ', emoji.no, colors.error(msgs.join(' ')))
}
/**
@ -99,6 +99,16 @@ export function exists(path) {
return existsSync(path)
}
/**
* Copies file source to destination.
*
* @param {string} source
* @param {string} destination
*/
export function copyFile(source, destination) {
copyFileSync(source, destination)
}
/**
* Gets file content.
*
@ -121,3 +131,13 @@ export function writeFile(path, content) {
return outputFileSync(path, content)
}
/**
* Strips leading ./ from path
*
* @param {string} path
* @return {string}
*/
export function getSimplePath(path) {
return startsWith(path, './') ? path.slice(2) : path
}