Syllabus/themes/piratecare/node_modules/postcss-cli/index.js

286 lines
7.3 KiB
JavaScript

'use strict'
const fs = require('fs-extra')
const path = require('path')
const prettyHrtime = require('pretty-hrtime')
const stdin = require('get-stdin')
const read = require('read-cache')
const chalk = require('chalk')
const globber = require('globby')
const chokidar = require('chokidar')
const postcss = require('postcss')
const postcssrc = require('postcss-load-config')
const reporter = require('postcss-reporter/lib/formatter')()
const argv = require('./lib/args')
const depGraph = require('./lib/depGraph')
const getMapfile = require('./lib/getMapfile')
let input = argv._
const { dir, output } = argv
if (argv.map) argv.map = { inline: false }
const cliConfig = {
options: {
map: argv.map !== undefined ? argv.map : { inline: true },
parser: argv.parser ? require(argv.parser) : undefined,
syntax: argv.syntax ? require(argv.syntax) : undefined,
stringifier: argv.stringifier ? require(argv.stringifier) : undefined
},
plugins: argv.use
? argv.use.map(plugin => {
try {
return require(plugin)()
} catch (e) {
const msg = e.message || `Cannot find module '${plugin}'`
let prefix = msg.includes(plugin) ? '' : ` (${plugin})`
if (e.name && e.name !== 'Error') prefix += `: ${e.name}`
return error(`Plugin Error${prefix}: ${msg}'`)
}
})
: []
}
let configFile
if (argv.env) process.env.NODE_ENV = argv.env
if (argv.config) argv.config = path.resolve(argv.config)
Promise.resolve()
.then(() => {
if (argv.watch && !(argv.output || argv.replace || argv.dir)) {
error('Cannot write to stdout in watch mode')
}
if (input && input.length) {
return globber(input, { dot: argv.includeDotfiles })
}
if (argv.replace || argv.dir) {
error(
'Input Error: Cannot use --dir or --replace when reading from stdin'
)
}
if (argv.watch) {
error('Input Error: Cannot run in watch mode when reading from stdin')
}
return ['stdin']
})
.then(i => {
if (!i || !i.length) {
error('Input Error: You must pass a valid list of files to parse')
}
if (i.length > 1 && !argv.dir && !argv.replace) {
error(
'Input Error: Must use --dir or --replace with multiple input files'
)
}
if (i[0] !== 'stdin') i = i.map(i => path.resolve(i))
input = i
return files(input)
})
.then(results => {
if (argv.watch) {
const printMessage = () =>
printVerbose(chalk.dim('\nWaiting for file changes...'))
const watcher = chokidar.watch(input.concat(dependencies(results)), {
usePolling: argv.poll,
interval: argv.poll && typeof argv.poll === 'number' ? argv.poll : 100,
awaitWriteFinish: {
stabilityThreshold: 50,
pollInterval: 10
}
})
if (configFile) watcher.add(configFile)
watcher.on('ready', printMessage).on('change', file => {
let recompile = []
if (input.includes(file)) recompile.push(file)
recompile = recompile.concat(
depGraph.dependantsOf(file).filter(file => input.includes(file))
)
if (!recompile.length) recompile = input
return files(recompile)
.then(results => watcher.add(dependencies(results)))
.then(printMessage)
.catch(error)
})
}
})
.catch(err => {
error(err)
process.exit(1)
})
function rc(ctx, path) {
if (argv.use) return Promise.resolve(cliConfig)
return postcssrc(ctx, path)
.then(rc => {
if (rc.options.from || rc.options.to) {
error(
'Config Error: Can not set from or to options in config file, use CLI arguments instead'
)
}
configFile = rc.file
return rc
})
.catch(err => {
if (!err.message.includes('No PostCSS Config found')) throw err
})
}
function files(files) {
if (typeof files === 'string') files = [files]
return Promise.all(
files.map(file => {
if (file === 'stdin') {
return stdin().then(content => {
if (!content) return error('Input Error: Did not receive any STDIN')
return css(content, 'stdin')
})
}
return read(file).then(content => css(content, file))
})
)
}
function css(css, file) {
const ctx = { options: cliConfig.options }
if (file !== 'stdin') {
ctx.file = {
dirname: path.dirname(file),
basename: path.basename(file),
extname: path.extname(file)
}
if (!argv.config) argv.config = path.dirname(file)
}
const relativePath =
file !== 'stdin' ? path.relative(path.resolve(), file) : file
if (!argv.config) argv.config = process.cwd()
const time = process.hrtime()
printVerbose(chalk`{cyan Processing {bold ${relativePath}}...}`)
return rc(ctx, argv.config)
.then(config => {
config = config || cliConfig
const options = { ...config.options }
if (file === 'stdin' && output) file = output
// TODO: Unit test this
options.from = file === 'stdin' ? path.join(process.cwd(), 'stdin') : file
if (output || dir || argv.replace) {
const base = argv.base
? file.replace(path.resolve(argv.base), '')
: path.basename(file)
options.to = output || (argv.replace ? file : path.join(dir, base))
if (argv.ext) {
options.to = options.to.replace(path.extname(options.to), argv.ext)
}
options.to = path.resolve(options.to)
}
if (!options.to && config.options.map && !config.options.map.inline) {
error(
'Output Error: Cannot output external sourcemaps when writing to STDOUT'
)
}
return postcss(config.plugins)
.process(css, options)
.then(result => {
const tasks = []
if (options.to) {
tasks.push(fs.outputFile(options.to, result.css))
if (result.map) {
const mapfile = getMapfile(options)
tasks.push(fs.outputFile(mapfile, result.map))
}
} else process.stdout.write(result.css, 'utf8')
return Promise.all(tasks).then(() => {
const prettyTime = prettyHrtime(process.hrtime(time))
printVerbose(
chalk`{green Finished {bold ${relativePath}} in {bold ${prettyTime}}}`
)
const messages = result.warnings()
if (messages.length) {
console.warn(reporter({ ...result, messages }))
}
return result
})
})
})
.catch(err => {
throw err
})
}
function dependencies(results) {
if (!Array.isArray(results)) results = [results]
const messages = []
results.forEach(result => {
if (result.messages <= 0) return
result.messages
.filter(msg => (msg.type === 'dependency' ? msg : ''))
.map(depGraph.add)
.forEach(dependency => messages.push(dependency.file))
})
return messages
}
function printVerbose(message) {
if (argv.verbose) console.warn(message)
}
function error(err) {
// Seperate error from logging output
if (argv.verbose) console.error()
if (typeof err === 'string') {
console.error(chalk.red(err))
} else if (err.name === 'CssSyntaxError') {
console.error(err.toString())
} else {
console.error(err)
}
// Watch mode shouldn't exit on error
if (argv.watch) return
process.exit(1)
}