Skip to content

Instantly share code, notes, and snippets.

@petekp
Last active January 9, 2019 04:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petekp/c8a538c54127165a95dd6a40ad81d57f to your computer and use it in GitHub Desktop.
Save petekp/c8a538c54127165a95dd6a40ad81d57f to your computer and use it in GitHub Desktop.
Node build script for creating distributable multi-format themes with Theo
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