UNPKG

gulp-rollup-2

Version:

Gulp plugin for Rollup JavaScript Module Bundler

324 lines (245 loc) 6.72 kB
const path = require('path'); const vinyl = require('vinyl'); const vinylSa = require('vinyl-sourcemaps-apply'); const through = require('through2'); const rollup = require('rollup'); const hash = require('object-hash'); const root = require('njfs').root; const PLUGIN_NAME = 'gulp-rollup-2'; let cache = {}; const modules = ['es', 'amd', 'cjs', 'iife', 'umd', 'system']; const isInMod = (format) => modules.indexOf(format) > -1; const isArray = arg => Object.prototype.toString.call(arg) === '[object Array]'; const unique = (obj) => Array.prototype.filter.call(obj, (v, i) => obj.indexOf(v) === i); const isEqual = (a, b) => { const oka = Object.keys(a); const okb = Object.keys(b); const ola = oka.length; const olb = okb.length; if (ola !== olb) { return false; } let i = 0; for (; i < ola; i += 1) { const ka = oka[i]; if (b[ka] === undefined) { return false; } const va = a[ka]; const vb = b[ka]; if (va == null || typeof va === 'string' || typeof va === 'number') { if (va !== vb) { return false; } } else if (typeof va === 'function') { if (va.name !== vb.name) { return false; } } else if (va.constructor !== vb.constructor) { return false; } else { return isEqual(va, vb); } } return true; }; const sanitize = (opts, out) => { if (!opts) { throw new Error(`${PLUGIN_NAME}: Missing rollup config options!`); } if (typeof opts === 'string') { opts = [{ output: { format: opts } }]; } else if (!isArray(opts)) { opts = [opts]; } opts.filter(e => { if (out && !e.input) { throw new Error(`${PLUGIN_NAME}: Input option required!`); } else if (!e.output) { throw new Error(`${PLUGIN_NAME}: Output option required!`); } else if (isArray(e.output)) { if (e.output.filter((i) => !i.file || !i.format || !isInMod(i.format)).length) { throw new Error(`${PLUGIN_NAME}: Missing options file, format or unknown format type!`); } } else if (!e.output.file || !e.output.format || !isInMod(e.output.format)) { throw new Error(`${PLUGIN_NAME}: Missing options file, format or unknown format type!`); } }); const options = []; opts.forEach((opt, i) => { const io = Object.assign({}, opt); const oo = io.output; delete io.output; options[i] = {}; options[i]['io'] = io; options[i]['oo'] = isArray(oo) ? oo : [oo]; }); opts = options; opts.forEach((opt, i) => { const currI = opt.io; opts.forEach((e, j) => { if (i !== j && isEqual(currI, e.io)) { throw new Error(`${PLUGIN_NAME}: Repetitive input options!`); } }); }); const of = []; opts.forEach((opt) => { const oo = opt.oo; oo.forEach((o, i) => { const curr = o; oo.forEach((e, j) => { if (i !== j) { if (isEqual(curr, e) || curr.file === e.file) { throw new Error(`${PLUGIN_NAME}: Two or more output have same file option!`); } else { of .push(curr.file); } } }); }); }); if ( of .length !== unique( of ).length) { throw new Error(`${PLUGIN_NAME}: Two or more output have same file option!`); } return opts; }; const inside = (opts) => { opts = sanitize(opts); return through({ objectMode: true }, async (file, encoding, callback) => { if (file.isStream()) { this.emit('Error', Error(`${PLUGIN_NAME}: Unsupported file type: Stream!`)); callback(); } const _map = !!file.sourceMap; const _dir = file.cwd; const _pat = file.path; const _inp = path.relative(_dir, _pat); const bundles = await Promise.all(opts.map(async opt => { const io = opt.io; const oo = opt.oo; const ch = hash(io); cache[ch] = {}; io.input = io.input ? path.relative(_dir, io.input) : _inp; io.cache = io.cache ? cache[ch] : false; const bundle = await rollup.rollup(io); cache[ch] = bundle.cache; return { bundle, oo, input: io.input }; })); let start = 2; await Promise.all(bundles.map(async bundle => { const oo = bundle.oo; const input = bundle.input; bundle = bundle.bundle; await Promise.all(oo.map(async o => { start--; const f = o.format; const s = start > 0; if ((f === 'umd' || f === 'iife') && !o.name) { o.name = input; } if ((f === 'umd' || f === 'amd') && !o.amd || !!o.amd && !o.amd.id) { o.amd = Object.assign({}, o.amd, { id: o.name }); } const build = await bundle.generate(o); const [output] = build.output; const { code } = output; const buffer = Buffer.from(code, encoding); const oBase = path.dirname(path.join(_dir, input)); const oName = path.basename(o.file); const oPath = path.resolve(oBase, oName); let oFile = s ? file : new vinyl(); let map = output.map; oFile.path = oPath; oFile.contents = buffer; oFile.base = oBase; if (map !== null) { vinylSa(oFile, map); } else if (_map && s) { map = file.sourceMap; map.file = input; map.sources = map.sources.map(src => path.relative(_dir, path.join(oBase, src))); vinylSa(oFile, map); } return s ? oFile : this.push(oFile); })); })); callback(null, file); }); }; const outside = async (opts) => { opts = sanitize(opts, true); const _dir = root(); const _obj = through.obj(); const bundles = await Promise.all(opts.map(async (opt) => { const io = opt.io; const oo = opt.oo; const ch = hash(io); cache[ch] = {}; io.input = path.relative(_dir, io.input); io.cache = io.cache ? cache[ch] : false; const bundle = await rollup.rollup(io); cache[ch] = bundle.cache; return { bundle, oo, input: io.input }; })); await Promise.all(bundles.map(async bundle => { const oo = bundle.oo; const input = bundle.input; bundle = bundle.bundle; await Promise.all(oo.map(async o => { const f = o.format; if ((f === 'umd' || f === 'iife') && !o.name) { o.name = input; } if ((f === 'umd' || f === 'amd') && !o.amd || !!o.amd && !o.amd.id) { o.amd = Object.assign({}, o.amd, { id: o.name }); } const build = await bundle.generate(o); const [output] = build.output; const { code, map } = output; const buffer = Buffer.from(code, "utf8"); const oBase = path.dirname(path.join(_dir, input)); const oName = path.basename(o.file); const oPath = path.resolve(oBase, oName); const oFile = new vinyl({ base: oBase, path: oPath, contents: buffer }); if (map !== null) { vinylSa(oFile, map); } _obj.push(oFile); })); })); _obj.push(null); return _obj; }; module.exports = { rollup: inside, src: outside };