diff --git a/index.js b/index.js index 8d36dfb..bcd62f2 100755 --- a/index.js +++ b/index.js @@ -1,9 +1,8 @@ #!/usr/bin/env node -import path from 'path' - +import path from 'node:path' +import { text } from 'node:stream/consumers' import prettyHrtime from 'pretty-hrtime' -import { text } from 'stream/consumers' import read from 'read-cache' import pc from 'picocolors' import { glob } from 'tinyglobby' @@ -27,35 +26,6 @@ const { dir, output } = argv if (argv.map) argv.map = { inline: false } -let cliConfig - -async function buildCliConfig() { - cliConfig = { - options: { - map: argv.map !== undefined ? argv.map : { inline: true }, - parser: argv.parser ? await import(argv.parser) : undefined, - syntax: argv.syntax ? await import(argv.syntax) : undefined, - stringifier: argv.stringifier - ? await import(argv.stringifier) - : undefined, - }, - plugins: argv.use - ? await Promise.all( - argv.use.map(async (plugin) => { - try { - return (await import(plugin)).default() - } catch (e) { - const msg = e.message || `Unknown error in '${plugin}'` - let prefix = msg.includes(plugin) ? '' : ` (${plugin})` - if (e.name && e.name !== 'Error') prefix += `: ${e.name}` - error(`Plugin Error${prefix}: ${msg}`) - } - }), - ) - : [], - } -} - let configFile let argvConfigSet = false @@ -81,89 +51,103 @@ if (parseInt(postcss().version) < 8) { error('Please install PostCSS 8 or above') } -buildCliConfig() - .then(() => { - if (argv.watch && !(argv.output || argv.replace || argv.dir)) { - error('Cannot write to stdout in watch mode') - } - - if (input && input.length) { - return glob( - input.map((i) => slash(String(i))), - { dot: argv.includeDotfiles }, +const cliConfig = { + options: { + map: argv.map !== undefined ? argv.map : { inline: true }, + parser: argv.parser ? await import(argv.parser) : undefined, + syntax: argv.syntax ? await import(argv.syntax) : undefined, + stringifier: argv.stringifier ? await import(argv.stringifier) : undefined, + }, + plugins: argv.use + ? await Promise.all( + argv.use.map(async (plugin) => { + try { + return (await import(plugin)).default() + } catch (e) { + const msg = e.message || `Unknown error in '${plugin}'` + let prefix = msg.includes(plugin) ? '' : ` (${plugin})` + if (e.name && e.name !== 'Error') prefix += `: ${e.name}` + error(`Plugin Error${prefix}: ${msg}`) + } + }), ) - } + : [], +} - if (argv.replace || argv.dir) { - error( - 'Input Error: Cannot use --dir or --replace when reading from stdin', - ) - } +if (argv.watch && !(argv.output || argv.replace || argv.dir)) { + error('Cannot write to stdout in watch mode') +} - if (argv.watch) { - error('Input Error: Cannot run in watch mode when reading from stdin') - } +if (input && input.length) { + input = await glob( + input.map((i) => slash(String(i))), + { dot: argv.includeDotfiles }, + ) + if (!input.length) { + error('Input Error: You must pass a valid list of files to parse') + } - return ['stdin'] - }) - .then((i) => { - if (!i || !i.length) { - error('Input Error: You must pass a valid list of files to parse') - } + if (input.length > 1 && !argv.dir && !argv.replace) { + error('Input Error: Must use --dir or --replace with multiple input files') + } - if (i.length > 1 && !argv.dir && !argv.replace) { - error( - 'Input Error: Must use --dir or --replace with multiple input files', - ) - } + input = input.map((i) => path.resolve(i)) +} else { + if (argv.replace || argv.dir) { + error('Input Error: Cannot use --dir or --replace when reading from stdin') + } - if (i[0] !== 'stdin') i = i.map((i) => path.resolve(i)) + if (argv.watch) { + error('Input Error: Cannot run in watch mode when reading from stdin') + } - input = i + input = ['stdin'] +} - return files(input) - }) - .then((results) => { - if (argv.watch) { - const printMessage = () => - printVerbose(pc.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, - }, - }) +try { + const results = await files(input) + + if (argv.watch) { + const printMessage = () => + printVerbose(pc.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) + if (configFile) watcher.add(configFile) - watcher.on('ready', printMessage).on('change', (file) => { - let recompile = [] + watcher.on('ready', printMessage).on('change', (file) => { + let recompile = [] - if (input.includes(file)) recompile.push(file) + if (input.includes(file)) recompile.push(file) - const dependants = depGraph - .dependantsOf(file) - .concat(getAncestorDirs(file).flatMap(depGraph.dependantsOf)) + const dependants = depGraph + .dependantsOf(file) + .concat(getAncestorDirs(file).flatMap(depGraph.dependantsOf)) - recompile = recompile.concat( - dependants.filter((file) => input.includes(file)), - ) + recompile = recompile.concat( + dependants.filter((file) => input.includes(file)), + ) - if (!recompile.length) recompile = input + if (!recompile.length) recompile = input - return files([...new Set(recompile)]) - .then((results) => watcher.add(dependencies(results))) - .then(printMessage) - .catch((err) => { - // Watch mode shouldn't exit on file processing error - error(err, argv.watch) - }) - }) - } - }) - .catch(error) + return files([...new Set(recompile)]) + .then((results) => watcher.add(dependencies(results))) + .then(printMessage) + .catch((err) => { + // Watch mode shouldn't exit on file processing error + error(err, argv.watch) + }) + }) + } +} catch (err) { + error(err) +} function rc(ctx, path) { if (argv.use) return Promise.resolve(cliConfig) @@ -187,8 +171,6 @@ function rc(ctx, path) { } function files(files) { - if (typeof files === 'string') files = [files] - return Promise.all( files.map((file) => { if (file === 'stdin') { @@ -203,7 +185,7 @@ function files(files) { ) } -function css(css, file) { +async function css(css, file) { const ctx = { options: cliConfig.options } if (file !== 'stdin') { @@ -225,69 +207,57 @@ function css(css, file) { printVerbose(pc.cyan(`Processing ${pc.bold(relativePath)}...`)) - return rc(ctx, argv.config) - .then((config) => { - config = config || cliConfig - const options = { ...config.options } + const config = (await rc(ctx, argv.config)) || cliConfig + const options = { ...config.options } - if (file === 'stdin' && output) file = output + if (file === 'stdin' && output) file = output - // TODO: Unit test this - options.from = file === 'stdin' ? path.join(process.cwd(), 'stdin') : file + // 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 (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) - } + if (argv.ext) { + options.to = options.to.replace(path.extname(options.to), argv.ext) + } - options.to = path.resolve(options.to) - } + 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', - ) - } + 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(outputFile(options.to, result.css)) - - if (result.map) { - const mapfile = getMapfile(options) - tasks.push(outputFile(mapfile, result.map.toString())) - } - } else process.stdout.write(result.css, 'utf8') - - return Promise.all(tasks).then(() => { - const prettyTime = prettyHrtime(process.hrtime(time)) - printVerbose( - pc.green( - `Finished ${pc.bold(relativePath)} in ${pc.bold(prettyTime)}`, - ), - ) - - const messages = result.warnings() - if (messages.length) { - console.warn(reporter({ ...result, messages })) - } - - return result - }) - }) - }) - .catch((err) => { - throw err - }) + const result = await postcss(config.plugins).process(css, options) + const tasks = [] + + if (options.to) { + tasks.push(outputFile(options.to, result.css)) + + if (result.map) { + const mapfile = getMapfile(options) + tasks.push(outputFile(mapfile, result.map.toString())) + } + } else process.stdout.write(result.css, 'utf8') + + await Promise.all(tasks) + const prettyTime = prettyHrtime(process.hrtime(time)) + printVerbose( + pc.green(`Finished ${pc.bold(relativePath)} in ${pc.bold(prettyTime)}`), + ) + + const messages = result.warnings() + if (messages.length) { + console.warn(reporter({ ...result, messages })) + } + + return result } function dependencies(results) { diff --git a/lib/DependencyGraph.js b/lib/DependencyGraph.js index efb7ac3..76b5949 100644 --- a/lib/DependencyGraph.js +++ b/lib/DependencyGraph.js @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import { DepGraph } from 'dependency-graph' export default function createDependencyGraph() { diff --git a/lib/DependencyGraph.test.js b/lib/DependencyGraph.test.js index d54393c..618f923 100644 --- a/lib/DependencyGraph.test.js +++ b/lib/DependencyGraph.test.js @@ -1,5 +1,5 @@ import test from 'ava' -import path from 'path' +import path from 'node:path' import createDependencyGraph from './DependencyGraph.js' function resolveArray(arr) { diff --git a/lib/getMapfile.js b/lib/getMapfile.js index e4d0c93..615c4ed 100644 --- a/lib/getMapfile.js +++ b/lib/getMapfile.js @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' export default function getMapfile(options) { if (options.map && typeof options.map.annotation === 'string') { return `${path.dirname(options.to)}/${options.map.annotation}` diff --git a/test/base.js b/test/base.js index a0f965c..50e8c93 100644 --- a/test/base.js +++ b/test/base.js @@ -1,5 +1,5 @@ import test from 'ava' -import path from 'path' +import path from 'node:path' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' diff --git a/test/config.js b/test/config.js index b1d7c2c..7666b83 100644 --- a/test/config.js +++ b/test/config.js @@ -1,5 +1,5 @@ import test from 'ava' -import path from 'path' +import path from 'node:path' import ENV from './helpers/env.js' diff --git a/test/dir.js b/test/dir.js index 695f198..42c9311 100644 --- a/test/dir.js +++ b/test/dir.js @@ -1,5 +1,5 @@ import test from 'ava' -import path from 'path' +import path from 'node:path' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' diff --git a/test/ext.js b/test/ext.js index 675939d..e8cf4ca 100644 --- a/test/ext.js +++ b/test/ext.js @@ -1,7 +1,7 @@ import test from 'ava' import fs from 'node:fs/promises' -import path from 'path' +import path from 'node:path' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' diff --git a/test/glob.js b/test/glob.js index 38743f7..4849cdc 100644 --- a/test/glob.js +++ b/test/glob.js @@ -1,5 +1,5 @@ import test from 'ava' -import path from 'path' +import path from 'node:path' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' diff --git a/test/helpers/cli.js b/test/helpers/cli.js index 838d5a6..c4b76f3 100644 --- a/test/helpers/cli.js +++ b/test/helpers/cli.js @@ -1,5 +1,5 @@ -import path from 'path' -import { exec } from 'child_process' +import path from 'node:path' +import { exec } from 'node:child_process' export default function (args, cwd) { return new Promise((resolve) => { diff --git a/test/helpers/env.js b/test/helpers/env.js index 0fd9549..88b2816 100644 --- a/test/helpers/env.js +++ b/test/helpers/env.js @@ -1,5 +1,5 @@ import fs from 'node:fs/promises' -import path from 'path' +import path from 'node:path' import { glob } from 'tinyglobby' import tmp from './tmp.js' diff --git a/test/helpers/tmp.js b/test/helpers/tmp.js index bfa1369..e5e084a 100644 --- a/test/helpers/tmp.js +++ b/test/helpers/tmp.js @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import { v4 as uuid } from 'uuid' export default function (ext) { diff --git a/test/replace.js b/test/replace.js index 7032493..24fefde 100644 --- a/test/replace.js +++ b/test/replace.js @@ -1,7 +1,7 @@ import test from 'ava' import fs from 'node:fs/promises' -import path from 'path' +import path from 'node:path' import cli from './helpers/cli.js' import tmp from './helpers/tmp.js' diff --git a/test/stdin.js b/test/stdin.js index 9cdf928..5137694 100644 --- a/test/stdin.js +++ b/test/stdin.js @@ -1,8 +1,8 @@ import test from 'ava' import { createReadStream } from 'node:fs' -import path from 'path' -import { exec } from 'child_process' +import path from 'node:path' +import { exec } from 'node:child_process' import tmp from './helpers/tmp.js' import read from './helpers/read.js' diff --git a/test/stdout.js b/test/stdout.js index b53de43..6336cc7 100644 --- a/test/stdout.js +++ b/test/stdout.js @@ -1,8 +1,8 @@ import test from 'ava' import { createReadStream } from 'node:fs' -import path from 'path' -import { exec } from 'child_process' +import path from 'node:path' +import { exec } from 'node:child_process' import read from './helpers/read.js' diff --git a/test/watch.js b/test/watch.js index 915ac46..5b7b682 100644 --- a/test/watch.js +++ b/test/watch.js @@ -1,8 +1,8 @@ import test from 'ava' import fs from 'node:fs/promises' -import path from 'path' -import { spawn } from 'child_process' +import path from 'node:path' +import { spawn } from 'node:child_process' import chokidar from 'chokidar' import ENV from './helpers/env.js'