123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- // Super fast memoize for single argument functions.
- function memoize(func) {
- const cache = new Map();
- return input => {
- let output = cache.get(input);
- if (output === undefined) {
- output = func(input);
- cache.set(input, output);
- }
- return output;
- };
- }
- // camelCase to snake_case converter that also works with
- // non-ascii characters.
- function snakeCase(str) {
- if (str.length === 0) {
- return str;
- }
- const upper = str.toUpperCase();
- const lower = str.toLowerCase();
- let out = lower[0];
- for (let i = 1, l = str.length; i < l; ++i) {
- const char = str[i];
- const prevChar = str[i - 1];
- const upperChar = upper[i];
- const prevUpperChar = upper[i - 1];
- const lowerChar = lower[i];
- const prevLowerChar = lower[i - 1];
- // Test if `char` is an upper-case character and that the character
- // actually has different upper and lower case versions.
- if (char === upperChar && upperChar !== lowerChar) {
- // Multiple consecutive upper case characters shouldn't add underscores.
- // For example "fooBAR" should be converted to "foo_bar".
- if (prevChar === prevUpperChar && prevUpperChar !== prevLowerChar) {
- out += lowerChar;
- } else {
- out += '_' + lowerChar;
- }
- } else {
- out += char;
- }
- }
- return out;
- }
- // snake_case to camelCase converter that simply reverses
- // the actions done by `snakeCase` function.
- function camelCase(str) {
- if (str.length === 0) {
- return str;
- }
- let out = str[0];
- for (let i = 1, l = str.length; i < l; ++i) {
- const char = str[i];
- const prevChar = str[i - 1];
- if (char !== '_') {
- if (prevChar === '_') {
- out += char.toUpperCase();
- } else {
- out += char;
- }
- }
- }
- return out;
- }
- // Returns a function that splits the inputs string into pieces using `separator`,
- // only calls `mapper` for the last part and concatenates the string back together.
- // If no separators are found, `mapper` is called for the entire string.
- function mapLastPart(mapper, separator) {
- return str => {
- const idx = str.lastIndexOf(separator);
- const mapped = mapper(str.slice(idx + separator.length));
- return str.slice(0, idx + separator.length) + mapped;
- };
- }
- // Returns a function that takes an object as an input and maps the object's keys
- // using `mapper`. If the input is not an object, the input is returned unchanged.
- function keyMapper(mapper) {
- return obj => {
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
- return obj;
- }
- const keys = Object.keys(obj);
- const out = {};
- for (let i = 0, l = keys.length; i < l; ++i) {
- const key = keys[i];
- out[mapper(key)] = obj[key];
- }
- return out;
- };
- }
- function snakeCaseMappers() {
- return {
- parse: keyMapper(memoize(camelCase)),
- format: keyMapper(memoize(snakeCase))
- };
- }
- function knexIdentifierMappers({ parse, format, idSeparator = ':' } = {}) {
- const formatId = memoize(mapLastPart(format, idSeparator));
- const parseId = memoize(mapLastPart(parse, idSeparator));
- const parseKeys = keyMapper(parseId);
- return {
- wrapIdentifier(identifier, origWrap) {
- return origWrap(formatId(identifier));
- },
- postProcessResponse(result) {
- if (Array.isArray(result)) {
- const output = new Array(result.length);
- for (let i = 0, l = result.length; i < l; ++i) {
- output[i] = parseKeys(result[i]);
- }
- return output;
- } else {
- return parseKeys(result);
- }
- }
- };
- }
- function knexSnakeCaseMappers() {
- return knexIdentifierMappers({
- parse: camelCase,
- format: snakeCase
- });
- }
- function knexIdentifierMapping(colToProp) {
- const propToCol = Object.keys(colToProp).reduce((propToCol, column) => {
- propToCol[colToProp[column]] = column;
- return propToCol;
- }, {});
- return knexIdentifierMappers({
- parse: column => colToProp[column] || column,
- format: prop => propToCol[prop] || prop
- });
- }
- module.exports = {
- snakeCase,
- camelCase,
- snakeCaseMappers,
- knexSnakeCaseMappers,
- knexIdentifierMappers,
- knexIdentifierMapping,
- camelCaseKeys: keyMapper(memoize(camelCase)),
- snakeCaseKeys: keyMapper(memoize(snakeCase))
- };
|