#!/usr/bin/env node /* Usage: js2on2env keyFile context-files... */ var fs = require('fs') global.dump = (...args) => { for (let item of args) print(JSON.stringify(item, null, 4)) } global.print = (...args) => console.log(...args) /* js-blend - Blend objects */ function clone(src) { var result if (Array.isArray(src)) { result = src.slice(0) } else if (typeof src == 'object' && !(src instanceof Date || src instanceof RegExp || src == null)) { result = Object.create(Object.getPrototypeOf(src)) var i, descriptor, keys = Object.getOwnPropertyNames(src) for (i = 0; i < keys.length; i ++) { descriptor = Object.getOwnPropertyDescriptor(src, keys[ i ]) if (descriptor.value && typeof descriptor.value === 'object') { descriptor.value = clone(descriptor.value) } Object.defineProperty(result, keys[i], descriptor) } } else { result = src } return result } function blend(dest, src, combine = '') { if (!src) { return dest } if (!dest || typeof dest != 'object' || Array.isArray(dest)) { return dest } for (let key of Object.getOwnPropertyNames(src)) { let property = key let op = key[0] if (op == '+') { property = key.slice(1) } else if (op == '-') { property = key.slice(1) } else if (op == '?') { property = key.slice(1) } else if (op == '=') { property = key.slice(1) } else if (combine) { op = combine } else { /* Default is to blend */ op = '' } let s = src[key] if (!dest.hasOwnProperty(property)) { if (op == '-') { continue } dest[property] = clone(s) continue } else if (op == '?') { continue } let d = dest[property] if (Array.isArray(d)) { if (op == '+') { if (Array.isArray(s)) { for (let item of s) { if (d.indexOf(s) < 0) d.push(item) } } else { d.push(s) } } else if (op == '-') { if (Array.isArray(s)) { for (let item of s) { let index = d.indexOf(item) if (index >= 0) d.slice(index, 1) } } else { let index = d.indexOf(s) if (index >= 0) d.slice(index, 1) } } else { /* op == '=' */ dest[property] = clone(s) } } else if (typeof d == 'object' && d !== null && d !== undefined) { if (op == '=') { dest[property] = clone(s) } else if (op == '-') { delete dest[property] } else if (typeof s == 'object') { blend(d, s, op) } } else if (typeof d == 'string') { if (op == '+') { dest[property] += ' ' + s } else if (op == '-') { if (d == s) { delete dest[property] } else { dest[property] = d.replace(s, '') } } else { /* op == '=' */ dest[property] = s } } else if (typeof d == 'number') { if (op == '+') { dest[property] += s } else if (op == '-') { dest[property] -= s } else { /* op == '=' */ dest[property] = s } } else { if (op == '=') { dest[property] = s } else if (op == '-') { delete dest[property] } else { dest[property] = s } } } return dest } function expand(v, ...contexts) { let json = JSON.stringify(v) for (let context of contexts) { json = template(json, context) } return JSON.parse(json) } function template(v, context) { let text = v.toString() let matches = text.match(/\$\{[^}]*}/gm) if (matches) { // context = clone(context) for (let name of matches) { let word = name.slice(2).slice(0, -1) if (context[word] == undefined) { context[word] = '${' + word + '}' } } let fn = Function('_context_', 'with (_context_) { return `' + text + '`}') return fn(context) } return text } function mapEnv(obj, prefix = '', vars = {}) { for (let name of Object.keys(obj)) { let value = obj[name] if (name == 'profiles') { continue } if (typeof value == 'object') { mapEnv(value, prefix + name.toUpperCase() + '_', vars) } else { name = (prefix + name).toUpperCase().replace(/\./g, '_').replace(/-/g, '_') vars[name] = value } } return vars } function getEnv(obj, context = {}) { let vars = mapEnv(obj) for (let [index, v] of Object.entries(vars)) { vars[index] = expand(v, context) } return vars } let file = process.argv[2] if (!fs.existsSync(file)) { process.stderr.write(`Cannot locate ${file}\n`) process.exit(1) } let data = fs.readFileSync(file) let obj = eval('(' + data + ')') let context = blend({}, obj) for (let i = 3; i < process.argv.length; i++) { let path = process.argv[i] if (fs.existsSync(path)) { data = fs.readFileSync(path) let ctx = eval('(' + data + ')') context = blend(context, ctx) } } if (context.profile && context.profiles && context.profiles[context.profile]) { blend(context, context.profiles[context.profile]) } let env = getEnv(obj, context) for (let [key,value] of Object.entries(env)) { print('export ' + key + '="' + value + '"') }