| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 | /* * Copyright (c) 2015-present, Vitaly Tomilov * * See the LICENSE file at the top-level directory of this distribution * for licensing information. * * Removal or modification of this copyright notice is prohibited. */const {assert} = require('../assert');const npm = {    fs: require('fs'),    path: require('path'),    utils: require('./'),    package: require('../../package.json')};/** * @method utils.camelize * @description * Camelizes a text string. * * Case-changing characters include: * - _hyphen_ * - _underscore_ * - _period_ * - _space_ * * @param {string} text * Input text string. * * @returns {string} * Camelized text string. * * @see * {@link utils.camelizeVar camelizeVar} * */function camelize(text) {    text = text.replace(/[-_\s.]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');    return text.substr(0, 1).toLowerCase() + text.substr(1);}/** * @method utils.camelizeVar * @description * Camelizes a text string, while making it compliant with JavaScript variable names: * - contains symbols `a-z`, `A-Z`, `0-9`, `_` and `$` * - cannot have leading digits * * First, it removes all symbols that do not meet the above criteria, except for _hyphen_, _period_ and _space_, * and then it forwards into {@link utils.camelize camelize}. * * @param {string} text * Input text string. * * If it doesn't contain any symbols to make up a valid variable name, the result will be an empty string. * * @returns {string} * Camelized text string that can be used as an open property name. * * @see * {@link utils.camelize camelize} * */function camelizeVar(text) {    text = text.replace(/[^a-zA-Z0-9$_\-\s.]/g, '').replace(/^[0-9_\-\s.]+/, '');    return camelize(text);}function _enumSql(dir, options, cb, namePath) {    const tree = {};    npm.fs.readdirSync(dir).forEach(file => {        let stat;        const fullPath = npm.path.join(dir, file);        try {            stat = npm.fs.statSync(fullPath);        } catch (e) {            // while it is very easy to test manually, it is very difficult to test for            // access-denied errors automatically; therefore excluding from the coverage:            // istanbul ignore next            if (options.ignoreErrors) {                return; // on to the next file/folder;            }            // istanbul ignore next            throw e;        }        if (stat.isDirectory()) {            if (options.recursive) {                const dirName = camelizeVar(file);                const np = namePath ? (namePath + '.' + dirName) : dirName;                const t = _enumSql(fullPath, options, cb, np);                if (Object.keys(t).length) {                    if (!dirName.length || dirName in tree) {                        if (!options.ignoreErrors) {                            throw new Error('Empty or duplicate camelized folder name: ' + fullPath);                        }                    }                    tree[dirName] = t;                }            }        } else {            if (npm.path.extname(file).toLowerCase() === '.sql') {                const name = camelizeVar(file.replace(/\.[^/.]+$/, ''));                if (!name.length || name in tree) {                    if (!options.ignoreErrors) {                        throw new Error('Empty or duplicate camelized file name: ' + fullPath);                    }                }                tree[name] = fullPath;                if (cb) {                    const result = cb(fullPath, name, namePath ? (namePath + '.' + name) : name);                    if (result !== undefined) {                        tree[name] = result;                    }                }            }        }    });    return tree;}/** * @method utils.enumSql * @description * Synchronously enumerates all SQL files (within a given directory) into a camelized SQL tree. * * All property names within the tree are camelized via {@link utils.camelizeVar camelizeVar}, * so they can be used in the code directly, as open property names. * * @param {string} dir * Directory path where SQL files are located, either absolute or relative to the current directory. * * SQL files are identified by using `.sql` extension (case-insensitive). * * @param {{}} [options] * Search options. * * @param {boolean} [options.recursive=false] * Include sub-directories into the search. * * Sub-directories without SQL files will be skipped from the result. * * @param {boolean} [options.ignoreErrors=false] * Ignore the following types of errors: * - access errors, when there is no read access to a file or folder * - empty or duplicate camelized property names * * This flag does not affect errors related to invalid input parameters, or if you pass in a * non-existing directory. * * @param {function} [cb] * A callback function that takes three arguments: * - `file` - SQL file path, relative or absolute, according to how you specified the search directory * - `name` - name of the property that represents the SQL file * - `path` - property resolution path (full property name) * * If the function returns anything other than `undefined`, it overrides the corresponding property value in the tree. * * @returns {object} * Camelized SQL tree object, with each value being an SQL file path (unless changed via the callback). * * @example * * // simple SQL tree generation for further processing: * const tree = pgp.utils.enumSql('../sql', {recursive: true}); * * @example * * // generating an SQL tree for dynamic use of names: * const sql = pgp.utils.enumSql(__dirname, {recursive: true}, file => { *     return new pgp.QueryFile(file, {minify: true}); * }); * * @example * * const {join: joinPath} = require('path'); * * // replacing each relative path in the tree with a full one: * const tree = pgp.utils.enumSql('../sql', {recursive: true}, file => { *     return joinPath(__dirname, file); * }); * */function enumSql(dir, options, cb) {    if (!npm.utils.isText(dir)) {        throw new TypeError('Parameter \'dir\' must be a non-empty text string.');    }    options = assert(options, ['recursive', 'ignoreErrors']);    cb = (typeof cb === 'function') ? cb : null;    return _enumSql(dir, options, cb, '');}/** * @method utils.taskArgs * @description * Normalizes/prepares arguments for tasks and transactions. * * Its main purpose is to simplify adding custom methods {@link Database#task task}, {@link Database#taskIf taskIf}, * {@link Database#tx tx} and {@link Database#txIf txIf} within event {@link event:extend extend}, as the those methods use fairly * complex logic for parsing inputs. * * @param args {Object} * Array-like object of `arguments` that was passed into the method. It is expected that the `arguments` * are always made of two parameters - `(options, cb)`, same as all the default task/transaction methods. * * And if your custom method needs additional parameters, they should be passed in as extra properties within `options`. * * @returns {Array} * Array of arguments that can be passed into a task or transaction. * * It is extended with properties `options` and `cb` to access the corresponding array elements `[0]` and `[1]` by name. * * @example * * // Registering a custom transaction method that assigns a default Transaction Mode: * * const initOptions = { *     extend: obj => { *         obj.myTx = function(options, cb) { *             const args = pgp.utils.taskArgs(arguments); // prepare arguments * *             if (!('mode' in args.options)) { *                 // if no 'mode' was specified, set default for transaction mode: *                 args.options.mode = myTxModeObject; // of type pgp.txMode.TransactionMode *             } * *             return obj.tx.apply(this, args); *             // or explicitly, if needed: *             // return obj.tx.call(this, args.options, args.cb); *         } *     } * }; * */function taskArgs(args) {    if (!args || typeof args.length !== 'number') {        throw new TypeError('Parameter \'args\' must be an array-like object of arguments.');    }    let options = args[0], cb;    if (typeof options === 'function') {        cb = options;        options = {};        if (cb.name) {            options.tag = cb.name;        }    } else {        if (typeof args[1] === 'function') {            cb = args[1];        }        if (typeof options === 'string' || typeof options === 'number') {            options = {tag: options};        } else {            options = (typeof options === 'object' && options) || {};            if (!('tag' in options) && cb && cb.name) {                options.tag = cb.name;            }        }    }    const res = [options, cb];    Object.defineProperty(res, 'options', {        get: function () {            return this[0];        },        set: function (newValue) {            this[0] = newValue;        },        enumerable: true    });    Object.defineProperty(res, 'cb', {        get: function () {            return this[1];        },        set: function (newValue) {            this[1] = newValue;        },        enumerable: true    });    return res;}/** * @namespace utils * * @description * Namespace for general-purpose static functions, available as `pgp.utils`, before and after initializing the library. * * @property {function} camelize * {@link utils.camelize camelize} - camelizes a text string * * @property {function} camelizeVar * {@link utils.camelizeVar camelizeVar} - camelizes a text string as a variable * * @property {function} enumSql * {@link utils.enumSql enumSql} - enumerates SQL files in a directory * * @property {function} taskArgs * {@link utils.taskArgs taskArgs} - prepares arguments for tasks and transactions */module.exports = {    camelize,    camelizeVar,    enumSql,    taskArgs};
 |