UNPKG

divanator

Version:

Compile CouchDB design doc written in modern JavaScript.

191 lines (173 loc) 5.4 kB
'use strict' // core const path = require('path') const fs = require('fs') // npm const UglifyJS = require('uglify-js') const babel = require('babel-core') const _ = require('lodash') const glob = require('glob') const nano = require('nano') const pify = require('pify') const fileType = require('file-type') const mime = require('mime') const lodashCli = require('lodash-cli') const readFile = (fn, bin) => new Promise((resolve, reject) => fs.readFile( fn, bin ? null : 'utf-8', (err, ok) => err ? reject(new Error(err)) : resolve(ok) )) const globNoLib = (w, p) => new Promise((resolve, reject) => glob( p, { cwd: w, ignore: 'views/lib/**' }, (err, ok) => err ? reject(new Error(err)) : resolve(ok) )) const globAtts = (w) => new Promise((resolve, reject) => glob( 'files/*', { cwd: w, nodir: true }, (err, ok) => err ? reject(new Error(err)) : resolve(ok) )) const compileTemplates = () => globNoLib('templates', '*.html') .then((a) => { if (a.length) { return a } throw new Error('No files.') }) .then(() => new Promise((resolve, reject) => { const cb = (data) => { if (data.message) { throw new Error(data.message) } const re = /^\/\*\*[^]+\*\/\n;([^]+);$/m const z = data.source.match(re) if (z[1]) { return resolve(z[1]) } reject(new Error('No code.')) } lodashCli('-p moduleId=none settings={variable:"data"} template=templates/*.html exports=node strict'.split(' '), cb) })) const getFiles = (w) => { const g = globNoLib.bind(null, w) return Promise.all([ g('*(rewrites.json|rewrites.js|validate_doc_update.js)'), g('*(shows|lists|filters|updates)/*.js'), g('views/*/map.js'), g('views/*/reduce'), g('views/*/reduce.js') ]).then(_.flatten) } const divanatorFile = (() => { const exported = (() => { const re = /module.exports = (function \([^]+\})/ return (str) => str.code.match(re)[1] })() const minjs = (() => { const repin = (str) => str.replace('function (', 'function fn(') const repout = (str) => str.replace('function fn(', 'function(') return (str) => repout(UglifyJS.minify(repin(exported(str)), { fromString: true }).code) })() const transform = (fn, resolve, reject) => babel.transformFile( fn, { presets: ['es2015'] }, (err, ok) => err ? reject(new Error(err)) : resolve(minjs(ok)) ) return (fn2, resolver) => { const fn = resolver(fn2) let out switch (path.extname(fn)) { case '.js': out = new Promise(transform.bind(null, fn)) break case '.json': out = require(fn) break case '': out = readFile(fn) break // default: // throw new Error('unknown extension') } return Promise.resolve(out) .then((a) => { const z = path.parse(fn2) z.base = z.name // remove extension const y = path.format(z) const x = y.split('/') const xl = x.length const ddoc = { _id: '_design/' + fn.split('/').slice(-xl - 1, -xl)[0] } switch (xl) { case 1: ddoc[x[0]] = a break case 2: ddoc[x[0]] = { } ddoc[x[0]][x[1]] = a break case 3: ddoc[x[0]] = { } ddoc[x[0]][x[1]] = { } ddoc[x[0]][x[1]][x[2]] = a break // default: // throw new Error('In too deep.') } return ddoc }) } })() module.exports = (ddocPath, dbURL) => { const resolver = path.resolve.bind(null, ddocPath) const p = getFiles(ddocPath) .then((f) => Promise.all(f.map((z) => divanatorFile(z, resolver)))) .then((g) => { const ddoc = _.merge.apply(null, g) if (!ddoc._id) { throw new Error('Are you sure ' + ddocPath + ' is a design doc?') } return ddoc }) .then((ddoc) => compileTemplates() .then((a) => { if (!a) { return ddoc } if (!ddoc.views) { ddoc.views = { } } if (!ddoc.views.lib) { ddoc.views.lib = { } } ddoc.views.lib.templates = a return ddoc }) .catch((e) => ddoc) ) if (!dbURL) { return p } const db = nano(dbURL) return p .then((ddoc) => { const info = pify(db.info) return Promise.all([ddoc, info()]) }) .then((gg) => { const ddoc = gg[0] const head = (docid) => new Promise((resolve, reject) => db.head(docid, (err, body, headers) => resolve(err ? { } : headers)) ) return Promise.all([ddoc, head(ddoc._id)]) }) .then((gg) => { const ddoc = gg[0] const a = gg[1] if (a.etag) { ddoc._rev = a.etag.slice(1, -1) } return ddoc }) .then((ddoc) => Promise.all([ddoc, globAtts(ddocPath)])) .then((gg) => { const ddoc = gg[0] const atts = gg[1] const attObj = (fn, f) => { let ft = fileType(f) ft = ft && ft.mime ? ft.mime : mime.lookup(fn) return { name: path.basename(fn), data: f, content_type: ft } } const ha = atts.map((fn) => readFile(resolver(fn), true).then(attObj.bind(null, fn))) ha.unshift(ddoc) return Promise.all(ha) }) .then((tt) => { const ddoc = tt[0] const atts = tt.slice(1) const insert = pify(db.multipart.insert) return insert(ddoc, atts, ddoc._id) }) }