const functions = require('firebase-functions'); const path = require('path'); const glob = require('glob'); const ENDPOINT_FOLDER = './endpoints'; const DO_NOT_DEPLOY = /^(admin|a|debug|d)$/; const IGNORE = /^(ignore|i)$/; const BREAK_ON_ERROR = true; const is = { emulating: process.env.hasOwnProperty('FIREBASE_PROJECT'), deploying: !process.env.hasOwnProperty('FUNCTION_NAME'), }; let skipped = []; glob.sync(`./**/*.js`, { cwd: path.resolve(__dirname, ENDPOINT_FOLDER), }) .map(file => ({ path: file.slice(2), components: file.slice(2, -3).split(/[\/.]/g), })) .sort((a, b) => b.components.length - a.components.length) .forEach(file => { // ignore by name if (file.components.find(c => IGNORE.test(c))) return; // firebase naming standard const FB_NAME = file.components.join('-'); // function is currently being triggered is.triggered = process.env.FUNCTION_NAME === FB_NAME; // only deploy files locally or if allowed to deploy is.deployable = is.emulating || !file.components.find(c => DO_NOT_DEPLOY.test(c)); // export module if triggered or deploying if (is.triggered || is.deploying && is.deployable) { // map the module to a deep path: { [component]: { [component]: module } } file.components.reduce((_, c, i, list) => { // get the map for each path component if (i < list.length - 1) { if (!_[c]) _[c] = {}; return _[c]; } // skip files where a naming conflict exists with a directory if (_[c]) return skipped.push(`./${file.path.slice(0, -3)}`); // export the module _[c] = require(`./${ENDPOINT_FOLDER}/${file.path}`); }, exports); } }); // don't allow conflicts to deploy if (BREAK_ON_ERROR && skipped.length) { throw new Error(`naming conflict: "${skipped.join('", "')}"`); }