themer
Version:
Customizable theme creator for editors, terminals, wallpaper, and more.
123 lines • 5.32 kB
JavaScript
import Color from 'color';
import { Command } from 'commander';
import flatten from 'lodash/flatten.js';
import { parse } from 'yaml';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import themer, { allBuiltInColorSetIdentifiers, allBuiltInTemplateIdentifiers, } from './index.js';
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
const { version, description } = JSON.parse((await readFile(packageJsonPath)).toString('utf8'));
const program = new Command();
program
.name('themer')
.description(description)
.version(version, '-v, --version', 'display the current version')
.option('-c, --color-set <built-in color set name or file path...>', 'the color set(s) to render', 'default')
.option('-t, --template <built-in template name or file path...>', 'the theme template(s) to render', '*')
.option('-s, --size <wallpaper resolution...>', 'resolution to render in pixels, in the format [width]x[height]', '2880x1800')
.option('-o, --output <path>', 'the output directory', 'themer-output');
program.parse();
const options = program.opts();
const expandedColorSets = flatten((typeof options['colorSet'] === 'string'
? [options['colorSet']]
: options['colorSet']).map((value) => value === '*' ? allBuiltInColorSetIdentifiers : value));
function isBuiltInColorSet(value) {
return allBuiltInColorSetIdentifiers.includes(value);
}
console.log('resolving color set(s)...');
const resolvedColorSets = await Promise.all(expandedColorSets.map(async (value) => {
if (isBuiltInColorSet(value))
return value;
else {
const absolutePath = resolve(value);
console.log(`loading color set ${value} (path: ${absolutePath})...`);
try {
return (await import(absolutePath)).default;
}
catch {
console.log(`color set ${absolutePath} does not appear to be a themer color set JavaScript file...`);
}
try {
const content = await readFile(absolutePath, 'utf8');
const base16 = parse(content);
const variant = {
shade0: `#${base16.base00}`,
shade1: `#${base16.base01}`,
shade2: `#${base16.base02}`,
shade3: `#${base16.base03}`,
shade4: `#${base16.base04}`,
shade5: `#${base16.base05}`,
shade6: `#${base16.base06}`,
shade7: `#${base16.base07}`,
accent0: `#${base16.base08}`,
accent1: `#${base16.base09}`,
accent2: `#${base16.base0A}`,
accent3: `#${base16.base0B}`,
accent4: `#${base16.base0C}`,
accent5: `#${base16.base0D}`,
accent6: `#${base16.base0E}`,
accent7: `#${base16.base0F}`,
};
const isDark = Color(variant.shade0).luminosity() <
Color(variant.shade7).luminosity();
const colors = {
name: base16.scheme,
variants: isDark ? { dark: variant } : { light: variant },
};
return colors;
}
catch (e) {
console.error(e.message);
console.log(`color set ${absolutePath} does not appear to be a base16 theme...`);
}
console.error(`...unable to load color set ${value}`);
process.exit(1);
}
}));
const expandedTemplates = flatten((typeof options['template'] === 'string'
? [options['template']]
: options['template']).map((value) => value === '*' ? allBuiltInTemplateIdentifiers : value));
function isBuiltInTemplate(value) {
return allBuiltInTemplateIdentifiers.includes(value);
}
const resolvedTemplates = await Promise.all(expandedTemplates.map(async (value) => {
if (isBuiltInTemplate(value))
return value;
else {
const absolutePath = resolve(value);
console.log(`loading template ${value} (path: ${absolutePath})...`);
try {
return (await import(absolutePath)).default;
}
catch {
console.log(`template ${absolutePath} does not appear to be a themer template file...`);
}
}
console.error(`...unable to resolve template: ${value}`);
process.exit(1);
}));
function isTuplePair(arr) {
return arr.length === 2;
}
const resolvedResolutions = (typeof options['size'] === 'string' ? [options['size']] : options['size']).map((opt) => {
const dimensions = opt.split('x').map((n) => parseInt(n));
if (isTuplePair(dimensions) && dimensions.every((n) => !isNaN(n))) {
const [w, h] = dimensions;
return { w, h };
}
console.error(`...size option doesn't appear to be a valid WxH resolution specifier: ${opt}`);
process.exit(1);
});
const renderOptions = {
wallpaperSizes: resolvedResolutions,
};
for await (const file of themer(resolvedColorSets, resolvedTemplates, renderOptions)) {
const path = join(options['output'], file.path);
console.log(`writing ${path}...`);
await mkdir(dirname(path), { recursive: true });
await writeFile(path, file.content);
}
console.log('done!');
//# sourceMappingURL=bin.js.map