Last active
May 17, 2019 20:17
-
-
Save mildsunrise/5adece4bf728af4c16cb496176d9a499 to your computer and use it in GitHub Desktop.
Quick script to write many config files in a single file
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
const util = require('util') | |
const path = require('path') | |
const fs = require('fs') | |
const mkdir = util.promisify(fs.mkdir) | |
const readFile = util.promisify(fs.readFile) | |
const writeFile = util.promisify(fs.writeFile) | |
const trimLines = text => text.replace(/^[ \t]*\n?|\n?[ \t]*$/g, '') | |
const dedent = text => { | |
const lines = text.split('\n').map(line => line.trim() ? line : '') | |
let indent = 0, indentLine | |
for (const line of lines.filter(x => x)) { | |
if (!indentLine) { | |
indentLine = line | |
indent = /^\s*/.exec(line)[0].length | |
} else { | |
let n = 0 | |
while (n < line.length && n < indent && line[n] === indentLine[n]) n++ | |
indent = n | |
} | |
} | |
return lines.map(line => line.substring(indent)).join('\n') | |
} | |
const prepare = text => dedent(trimLines(text)) + '\n' | |
async function main() { | |
const args = process.argv.slice(2) | |
if (args.length < 1 || args.length > 2) { | |
console.error('Usage: unpack-config <source file> [<destination dir>]') | |
process.exit(1) | |
} | |
const base = args[1] || '.' | |
const source = await readFile(args[0], 'utf8') | |
const files = parse(source.replace(/\r\n?/g, '\n')) | |
const writtenFiles = new Map() | |
for (const [name, contents] of files) { | |
const file = path.join(base, path.normalize(name)) | |
if (writtenFiles.has(file)) | |
throw new Error(`Duplicate file specified: ${util.inspect(name)}`) | |
console.log(`Writing: ${name}`) | |
writtenFiles.set(file, (async () => { | |
await mkdir(path.dirname(file), { recursive: true }) | |
.catch(err => err.code === 'EEXIST' || Promise.reject(err)) | |
await writeFile(file, prepare(contents), 'utf8') | |
})()) | |
} | |
await Promise.all(writtenFiles.values()) | |
} | |
main().catch(err => { | |
console.error(`Error: ${(err && err.stack) || err}`) | |
process.exit(1) | |
}) | |
// PARSER | |
// ------ | |
interface StackFrame { | |
parent?: StackFrame | |
file?: string | |
contents: string | |
} | |
function parse(data: string): Map<String, String> { | |
const ESCAPABLE_CHARS = '\\{}=' | |
const size = data.length | |
const files: Map<String, String> = new Map() | |
let frame: StackFrame = { contents: '' } | |
let i = 0 | |
while (i < size) { | |
if (data[i] === '\\' && i+1 < size && | |
ESCAPABLE_CHARS.indexOf(data[i+1]) !== -1) { | |
frame.contents += data[i+1] | |
i += 2 | |
continue | |
} else if (data[i] === '{') { | |
let file = null | |
const m = /(^|\s)([^ \x00-\x1F\x7F]+)\s*\=\s*$/.exec(frame.contents) | |
if (m) { | |
const remove = m[0].length - m[1].length | |
frame.contents = frame.contents.substring(frame.contents.length - remove) | |
file = m[2] | |
} | |
frame = { parent: frame, contents: '', file } | |
} else if (data[i] === '}') { | |
if (!frame.parent) | |
throw new Error('Unexpected closing bracket') | |
if (frame.file) | |
files.set(frame.file, frame.contents) | |
frame = frame.parent | |
} else { | |
frame.contents += data[i] | |
} | |
i++ | |
} | |
if (frame.parent) | |
throw new Error('Unclosed blocks!') | |
return files | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment