thebespokepixel/palette2oco

View on GitHub
src/cli.js

Summary

Maintainability
A
0 mins
Test Coverage
/* ─────────────────────────╮
 │ @thebespokepixel/xo-tidy │ CLI Utility
 ╰──────────────────────────┴────────────────────────────────────────────────── */
/* eslint unicorn/no-process-exit:0, node/prefer-global/process: [error] */

import {resolve, dirname} from 'node:path'
import {fileURLToPath} from 'node:url'
import _ from 'lodash'
import {truwrap} from 'truwrap'
import {stripIndent, TemplateTag, replaceSubstitutionTransformer} from 'common-tags'
import {box} from '@thebespokepixel/string'
import meta from '@thebespokepixel/meta'
import yargs from 'yargs'
import {hideBin} from 'yargs/helpers' // eslint-disable-line node/file-extension-in-import
import {globby} from 'globby'
import updateNotifier from 'update-notifier'
import {simple} from 'trucolor'
import pkg from '../package.json'
import {console, paletteReader, paletteWriter} from './index.js'

const clr = simple({format: 'sgr'})
const metadata = meta(dirname(fileURLToPath(import.meta.url)))

const renderer = truwrap({
    outStream: process.stderr,
})

const colorReplacer = new TemplateTag(
    replaceSubstitutionTransformer(
        /([a-zA-Z]+?)[:/|](.+)/,
        (match, colorName, content) => `${clr[colorName]}${content}${clr[colorName].out}`,
    ),
)

const title = box(colorReplacer`${'title|palette2oco'}${`dim| │ ${metadata.version(3)}`}`, {
    borderColor: 'red',
    margin: {
        top: 1,
    },
    padding: {
        bottom: 0,
        top: 0,
        left: 2,
        right: 2,
    },
})

const usage = stripIndent(colorReplacer)`
    Convert palette data from a variety of sources into Open Color .oco format.

    Allows structured directories of pallette data to be converted into nested oco palette data.

    Formats supported:
    Sip (http://sipapp.io): Supports .sippalette and .json exports.

    Abobe Swatch Exchange (ASE): Full support of RGB, CMYK and Lab colorspaces.

    Vanilla JSON: File signature must match the following...

      {
        "name" : "Palette name",
        "colors" : [
          {
            name: "Color name",
            red: (0.0 - 1.0 | 0 - 255)
            green: (0.0 - 1.0 | 0 - 255)
            blue: (0.0 - 1.0 | 0 - 255)
            alpha: (0.0 - 1.0 | 0 - 255)
          }
          ...
        ]
      }

    Usage:
    ${'command|palette2oco'} ${'option|[options]'} ${'argument|sourceGlob'} ${'argument|outputFile'}`

const epilogue = colorReplacer`${'brightGreen|' + metadata.copyright} ${'grey|Released under the MIT License.'}`

const yargsInstance = yargs(hideBin(process.argv))
    .strictOptions()
    .help(false)
    .version(false)
    .options({
        h: {
            alias: 'help',
            describe: 'Display help.',
        },
        v: {
            alias: 'version',
            count: true,
            describe: 'Print version to stdout. -vv Print name & version.',
        },
        V: {
            alias: 'verbose',
            count: true,
            describe: 'Be verbose. -VV Be loquacious.',
        },
        o: {
            alias: 'stdout',
            type: 'boolean',
            describe: 'Print output to stdout.',
        },
        color: {
            describe: 'Force color output. Disable with --no-color',
        },
    })

const {argv} = yargsInstance

if (!(process.env.USER === 'root' && process.env.SUDO_USER !== process.env.USER)) {
    updateNotifier({pkg}).notify()
}

if (argv.help) {
    (async () => {
        const usageContent = await yargsInstance.wrap(renderer.getWidth()).getHelp()
        renderer.write(title).break(2)
        renderer.write(usage)
        renderer.break(2)
        renderer.write(usageContent)
        renderer.break()
        renderer.write(epilogue)
        renderer.break(1)
        process.exit(0)
    })()
}

if (argv.version) {
    process.stdout.write(metadata.version(argv.version))
    process.exit(0)
}

if (argv.verbose) {
    switch (argv.verbose) {
        case 1:
            console.verbosity(4)
            console.log(`${clr.title}Verbose mode${clr.title.out}:`)
            break
        case 2:
            console.verbosity(5)
            console.log(`${clr.title}Extra-Verbose mode${clr.title.out}:`)
            console.yargs(argv)
            break
        default:
            console.verbosity(3)
    }
}

async function processor(paths) {
    const root = resolve()
    const pathArray = await globby(paths)
    const pal = await paletteReader(root).load(pathArray)
    return pal.render()
}

(async () => {
    console.log(argv._.length)
    if (argv.stdout) {
        if (argv._.length > 0) {
            try {
                process.stdout.write(await processor(argv._))
            } catch (error) {
                console.error(error)
                process.exit(1)
            }
        } else {
            console.error('palette2oco needs at least a source.')
            process.exit(1)
        }
    } else if (argv._.length > 1) {
        try {
            const dest = resolve(_.tail(argv._)[0])
            paletteWriter(dest, await processor(_.initial(argv._)))
        } catch (error) {
            console.error(error)
            process.exit(1)
        }
    } else {
        console.error('palette2oco needs at least a source and a destination.')
        process.exit(1)
    }
})()