Last active
January 9, 2019 04:11
-
-
Save petekp/c8a538c54127165a95dd6a40ad81d57f to your computer and use it in GitHub Desktop.
Node build script for creating distributable multi-format themes with Theo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import chalk from 'chalk' | |
import * as path from 'path' | |
import * as rimraf from 'rimraf' | |
import { promisify } from 'util' | |
import { flattenDeep } from 'lodash' | |
import { exec } from 'child_process' | |
import { upperFirst, camelCase } from 'lodash' | |
import { promises as fsPromise, lstatSync, readdirSync } from 'fs' | |
import { Format, Transform, TheoConfig } from '../typings/theo' | |
const promiseRimraf = promisify(rimraf) | |
const promiseExec = promisify(exec) | |
const THEMES_PATH = path.join(__dirname, '..', 'src', 'themes') | |
const DIST_PATH = path.join(__dirname, '..', 'dist') | |
const NODE_MODULES_BIN = path.join( | |
__dirname, | |
'..', | |
'..', | |
'..', | |
'node_modules', | |
'.bin' | |
) | |
const THEO_PATH = path.join(NODE_MODULES_BIN, 'theo') | |
const THEO_SETUP_PATH = path.join(__dirname, '..', 'built', 'theoSetup.js') | |
const PRETTIER_PATH = path.join(NODE_MODULES_BIN, 'prettier') | |
const sourceTag = `/* Generated by 'substrate-styles/built/build.js' */` | |
const substrateColorText = chalk.bgHex('#276fab').white(' Substrate Styles ') | |
const indentationArrow = chalk.hex('#276fab').bold('›') | |
const theoConfig: TheoConfig = { | |
entry: 'index.yml', | |
setup: THEO_SETUP_PATH, | |
dest: DIST_PATH, | |
output: { | |
js: ['js', 'd.ts'], | |
web: ['cssmodules.css', 'styles.d.ts'], | |
}, | |
} | |
const formatOutput = (output: string) => (fn: (text: string) => void) => { | |
fn(`${indentationArrow} ` + output) | |
} | |
const log = (text: string) => formatOutput(text)(console.log) | |
const logError = (text: string) => formatOutput(text)(console.error) | |
const logSuccess = (text: string) => | |
formatOutput(`${chalk.green(`✔`)} ${text}`)(console.log) | |
const getSubdirectories = (source: string) => | |
readdirSync(source) | |
.map((name: string) => path.join(source, name)) | |
.filter((path: string) => lstatSync(path).isDirectory()) | |
const pascalCase = (str: string) => `${upperFirst(camelCase(str))}` | |
async function execTheo( | |
entry: string, | |
setup: string, | |
dest: string, | |
transform: Transform, | |
format: Format | |
) { | |
const { stdout, stderr } = await promiseExec( | |
`${THEO_PATH} ${entry} --setup ${setup} --dest ${dest} --transform ${transform} --format ${format}` | |
) | |
if (stdout) { | |
// log(stdout) | |
} | |
if (stderr) { | |
throw new Error(stderr) | |
} | |
} | |
async function buildThemes() { | |
await Promise.all( | |
flattenDeep( | |
getSubdirectories(THEMES_PATH).map(themePath => { | |
const { entry: themeEntry, dest, output: outputs, setup } = theoConfig | |
const entry = `${themePath}/${themeEntry}` | |
const destination = `${dest}/${path.basename(themePath)}` | |
return Object.entries(outputs).map(([transform, formats]) => | |
formats!.map(format => | |
execTheo(entry, setup, destination, transform as Transform, format) | |
) | |
) | |
}) | |
) | |
).catch(logError) | |
} | |
async function prettify(): Promise<void> { | |
const { stdout, stderr } = await promiseExec( | |
`${PRETTIER_PATH} --write --loglevel=silent ${DIST_PATH}/**/*.{ts,js,css,json}` | |
) | |
if (stdout) { | |
log(stdout) | |
} | |
if (stderr) { | |
throw new Error(stderr) | |
} | |
} | |
const themeModuleNameDirnamePairs = Object.entries( | |
getSubdirectories(THEMES_PATH).reduce( | |
(acc, themePath: string) => ({ | |
...acc, | |
[`${pascalCase(path.basename(themePath))}Theme`]: `${path.basename( | |
themePath | |
)}`, | |
}), | |
{} | |
) | |
) | |
async function createEntryIndex(): Promise<void> { | |
const moduleContent = themeModuleNameDirnamePairs.reduce( | |
(acc, [name, dirname]) => { | |
return acc.concat(`export { default as ${name} } from './${dirname}'\n`) | |
}, | |
`${sourceTag}\n` | |
) | |
const moduleDeclaration = themeModuleNameDirnamePairs.reduce( | |
(acc, [name, dirname]) => { | |
return acc.concat(`export { _default as ${name} } from './${dirname}'\n`) | |
}, | |
`` | |
) | |
await fsPromise.writeFile(`${DIST_PATH}/index.js`, moduleContent) | |
await fsPromise.writeFile(`${DIST_PATH}/index.d.ts`, moduleDeclaration) | |
} | |
async function clean(path: string): Promise<void> { | |
await promiseRimraf(path).catch(logError) | |
await fsPromise.mkdir(path).catch(logError) | |
} | |
async function main() { | |
console.log(`${substrateColorText}`) | |
log(`Building themes...`) | |
try { | |
await clean(DIST_PATH).catch(logError) | |
await buildThemes().catch(logError) | |
logSuccess(`Compiled theme modules`) | |
await createEntryIndex().catch(logError) | |
logSuccess(`Created entry point at ./dist/index.js`) | |
await prettify().catch(logError) | |
logSuccess(`Prettified output`) | |
} catch (err) { | |
throw new Error(err) | |
} | |
logSuccess('Done') | |
} | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment