UNPKG

@gethue/sql-formatter

Version:

Format whitespace in a SQL query to make it more readable

1,183 lines (1,034 loc) 374 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["sqlFormatter"] = factory(); else root["sqlFormatter"] = factory(); })(window, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/sqlFormatter.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./src/core/Formatter.js": /*!*******************************!*\ !*** ./src/core/Formatter.js ***! \*******************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Formatter; }); /* harmony import */ var _tokenTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tokenTypes */ "./src/core/tokenTypes.js"); /* harmony import */ var _Indentation__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Indentation */ "./src/core/Indentation.js"); /* harmony import */ var _InlineBlock__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./InlineBlock */ "./src/core/InlineBlock.js"); /* harmony import */ var _Params__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Params */ "./src/core/Params.js"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils */ "./src/utils.js"); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./token */ "./src/core/token.js"); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Formatter = /*#__PURE__*/function () { /** * @param {Object} cfg * @param {String} cfg.language * @param {String} cfg.indent * @param {Boolean} cfg.uppercase * @param {Integer} cfg.linesBetweenQueries * @param {Boolean} cfg.indentQuerySeparator * @param {Object} cfg.params */ function Formatter(cfg) { _classCallCheck(this, Formatter); this.cfg = cfg; this.indentation = new _Indentation__WEBPACK_IMPORTED_MODULE_1__["default"](this.cfg.indent); this.inlineBlock = new _InlineBlock__WEBPACK_IMPORTED_MODULE_2__["default"](); this.params = new _Params__WEBPACK_IMPORTED_MODULE_3__["default"](this.cfg.params); this.previousReservedToken = {}; this.tokens = []; this.index = 0; } /** * SQL Tokenizer for this formatter, provided by subclasses. */ _createClass(Formatter, [{ key: "tokenizer", value: function tokenizer() { throw new Error('tokenizer() not implemented by subclass'); } /** * Reprocess and modify a token based on parsed context. * * @param {Object} token The token to modify * @param {String} token.type * @param {String} token.value * @return {Object} new token or the original * @return {String} token.type * @return {String} token.value */ }, { key: "tokenOverride", value: function tokenOverride(token) { // subclasses can override this to modify tokens during formatting return token; } /** * Formats whitespace in a SQL string to make it easier to read. * * @param {String} query The SQL query string * @return {String} formatted query */ }, { key: "format", value: function format(query) { this.tokens = this.tokenizer().tokenize(query); var formattedQuery = this.getFormattedQueryFromTokens(); return formattedQuery.trim(); } }, { key: "getFormattedQueryFromTokens", value: function getFormattedQueryFromTokens() { var _this = this; var formattedQuery = ''; this.tokens.forEach(function (token, index) { _this.index = index; token = _this.tokenOverride(token); if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].LINE_COMMENT) { formattedQuery = _this.formatLineComment(token, formattedQuery); } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].BLOCK_COMMENT) { formattedQuery = _this.formatBlockComment(token, formattedQuery); } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL) { formattedQuery = _this.formatTopLevelReservedWord(token, formattedQuery); _this.previousReservedToken = token; } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL_NO_INDENT) { formattedQuery = _this.formatTopLevelReservedWordNoIndent(token, formattedQuery); _this.previousReservedToken = token; } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_NEWLINE) { formattedQuery = _this.formatNewlineReservedWord(token, formattedQuery); _this.previousReservedToken = token; } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED) { formattedQuery = _this.formatWithSpaces(token, formattedQuery); _this.previousReservedToken = token; } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPEN_PAREN) { formattedQuery = _this.formatOpeningParentheses(token, formattedQuery); } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].CLOSE_PAREN) { formattedQuery = _this.formatClosingParentheses(token, formattedQuery); } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].PLACEHOLDER) { formattedQuery = _this.formatPlaceholder(token, formattedQuery); } else if (token.value === ',') { formattedQuery = _this.formatComma(token, formattedQuery); } else if (token.value === ':') { formattedQuery = _this.formatWithSpaceAfter(token, formattedQuery); } else if (token.value === '.') { formattedQuery = _this.formatWithoutSpaces(token, formattedQuery); } else if (token.value === ';') { formattedQuery = _this.formatQuerySeparator(token, formattedQuery); } else { formattedQuery = _this.formatWithSpaces(token, formattedQuery); } }); return formattedQuery; } }, { key: "formatLineComment", value: function formatLineComment(token, query) { return this.addNewline(query + this.show(token)); } }, { key: "formatBlockComment", value: function formatBlockComment(token, query) { return this.addNewline(this.addNewline(query) + this.indentComment(token.value)); } }, { key: "indentComment", value: function indentComment(comment) { return comment.replace(/\n[\t ]*/g, '\n' + this.indentation.getIndent() + ' '); } }, { key: "formatTopLevelReservedWordNoIndent", value: function formatTopLevelReservedWordNoIndent(token, query) { this.indentation.decreaseTopLevel(); query = this.addNewline(query) + this.equalizeWhitespace(this.show(token)); return this.addNewline(query); } }, { key: "formatTopLevelReservedWord", value: function formatTopLevelReservedWord(token, query) { this.indentation.decreaseTopLevel(); query = this.addNewline(query); this.indentation.increaseTopLevel(); query += this.equalizeWhitespace(this.show(token)); return this.addNewline(query); } }, { key: "formatNewlineReservedWord", value: function formatNewlineReservedWord(token, query) { if (Object(_token__WEBPACK_IMPORTED_MODULE_5__["isAnd"])(token) && Object(_token__WEBPACK_IMPORTED_MODULE_5__["isBetween"])(this.tokenLookBehind(2))) { return this.formatWithSpaces(token, query); } return this.addNewline(query) + this.equalizeWhitespace(this.show(token)) + ' '; } // Replace any sequence of whitespace characters with single space }, { key: "equalizeWhitespace", value: function equalizeWhitespace(string) { return string.replace(/[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g, ' '); } // Opening parentheses increase the block indent level and start a new line }, { key: "formatOpeningParentheses", value: function formatOpeningParentheses(token, query) { var _preserveWhitespaceFo, _this$tokenLookBehind; // Take out the preceding space unless there was whitespace there in the original query // or another opening parens or line comment var preserveWhitespaceFor = (_preserveWhitespaceFo = {}, _defineProperty(_preserveWhitespaceFo, _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPEN_PAREN, true), _defineProperty(_preserveWhitespaceFo, _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].LINE_COMMENT, true), _defineProperty(_preserveWhitespaceFo, _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPERATOR, true), _preserveWhitespaceFo); if (token.whitespaceBefore.length === 0 && !preserveWhitespaceFor[(_this$tokenLookBehind = this.tokenLookBehind()) === null || _this$tokenLookBehind === void 0 ? void 0 : _this$tokenLookBehind.type]) { query = Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query); } query += this.show(token); this.inlineBlock.beginIfPossible(this.tokens, this.index); if (!this.inlineBlock.isActive()) { this.indentation.increaseBlockLevel(); query = this.addNewline(query); } return query; } // Closing parentheses decrease the block indent level }, { key: "formatClosingParentheses", value: function formatClosingParentheses(token, query) { if (this.inlineBlock.isActive()) { this.inlineBlock.end(); return this.formatWithSpaceAfter(token, query); } else { this.indentation.decreaseBlockLevel(); return this.formatWithSpaces(token, this.addNewline(query)); } } }, { key: "formatPlaceholder", value: function formatPlaceholder(token, query) { return query + this.params.get(token) + ' '; } // Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause) }, { key: "formatComma", value: function formatComma(token, query) { query = Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query) + this.show(token) + ' '; if (this.inlineBlock.isActive()) { return query; } else if (Object(_token__WEBPACK_IMPORTED_MODULE_5__["isLimit"])(this.previousReservedToken)) { return query; } else { return this.addNewline(query); } } }, { key: "formatWithSpaceAfter", value: function formatWithSpaceAfter(token, query) { return Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query) + this.show(token) + ' '; } }, { key: "formatWithoutSpaces", value: function formatWithoutSpaces(token, query) { return Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query) + this.show(token); } }, { key: "formatWithSpaces", value: function formatWithSpaces(token, query) { return query + this.show(token) + ' '; } }, { key: "formatQuerySeparator", value: function formatQuerySeparator(token, query) { this.indentation.resetIndentation(); return Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query) + (this.cfg.indentQuerySeparator ? '\n' : '') + this.show(token) + '\n'.repeat(this.cfg.linesBetweenQueries || 1); } // Converts token to string (uppercasing it if needed) }, { key: "show", value: function show(_ref) { var type = _ref.type, value = _ref.value; if (this.cfg.uppercase && (type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL_NO_INDENT || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_NEWLINE || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPEN_PAREN || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].CLOSE_PAREN)) { return value.toUpperCase(); } else { return value; } } }, { key: "addNewline", value: function addNewline(query) { query = Object(_utils__WEBPACK_IMPORTED_MODULE_4__["trimSpacesEnd"])(query); if (!query.endsWith('\n')) { query += '\n'; } return query + this.indentation.getIndent(); } }, { key: "tokenLookBehind", value: function tokenLookBehind() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return this.tokens[this.index - n]; } }, { key: "tokenLookAhead", value: function tokenLookAhead() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return this.tokens[this.index + n]; } }]); return Formatter; }(); /***/ }), /***/ "./src/core/Indentation.js": /*!*********************************!*\ !*** ./src/core/Indentation.js ***! \*********************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Indentation; }); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./src/utils.js"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var INDENT_TYPE_TOP_LEVEL = 'top-level'; var INDENT_TYPE_BLOCK_LEVEL = 'block-level'; /** * Manages indentation levels. * * There are two types of indentation levels: * * - BLOCK_LEVEL : increased by open-parenthesis * - TOP_LEVEL : increased by RESERVED_TOP_LEVEL words */ var Indentation = /*#__PURE__*/function () { /** * @param {String} indent Indent value, default is " " (2 spaces) */ function Indentation(indent) { _classCallCheck(this, Indentation); this.indent = indent || ' '; this.indentTypes = []; } /** * Returns current indentation string. * @return {String} */ _createClass(Indentation, [{ key: "getIndent", value: function getIndent() { return this.indent.repeat(this.indentTypes.length); } /** * Increases indentation by one top-level indent. */ }, { key: "increaseTopLevel", value: function increaseTopLevel() { this.indentTypes.push(INDENT_TYPE_TOP_LEVEL); } /** * Increases indentation by one block-level indent. */ }, { key: "increaseBlockLevel", value: function increaseBlockLevel() { this.indentTypes.push(INDENT_TYPE_BLOCK_LEVEL); } /** * Decreases indentation by one top-level indent. * Does nothing when the previous indent is not top-level. */ }, { key: "decreaseTopLevel", value: function decreaseTopLevel() { if (this.indentTypes.length > 0 && Object(_utils__WEBPACK_IMPORTED_MODULE_0__["last"])(this.indentTypes) === INDENT_TYPE_TOP_LEVEL) { this.indentTypes.pop(); } } /** * Decreases indentation by one block-level indent. * If there are top-level indents within the block-level indent, * throws away these as well. */ }, { key: "decreaseBlockLevel", value: function decreaseBlockLevel() { while (this.indentTypes.length > 0) { var type = this.indentTypes.pop(); if (type !== INDENT_TYPE_TOP_LEVEL) { break; } } } }, { key: "resetIndentation", value: function resetIndentation() { this.indentTypes = []; } }]); return Indentation; }(); /***/ }), /***/ "./src/core/InlineBlock.js": /*!*********************************!*\ !*** ./src/core/InlineBlock.js ***! \*********************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return InlineBlock; }); /* harmony import */ var _tokenTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tokenTypes */ "./src/core/tokenTypes.js"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var INLINE_MAX_LENGTH = 50; /** * Bookkeeper for inline blocks. * * Inline blocks are parenthized expressions that are shorter than INLINE_MAX_LENGTH. * These blocks are formatted on a single line, unlike longer parenthized * expressions where open-parenthesis causes newline and increase of indentation. */ var InlineBlock = /*#__PURE__*/function () { function InlineBlock() { _classCallCheck(this, InlineBlock); this.level = 0; } /** * Begins inline block when lookahead through upcoming tokens determines * that the block would be smaller than INLINE_MAX_LENGTH. * @param {Object[]} tokens Array of all tokens * @param {Number} index Current token position */ _createClass(InlineBlock, [{ key: "beginIfPossible", value: function beginIfPossible(tokens, index) { if (this.level === 0 && this.isInlineBlock(tokens, index)) { this.level = 1; } else if (this.level > 0) { this.level++; } else { this.level = 0; } } /** * Finishes current inline block. * There might be several nested ones. */ }, { key: "end", value: function end() { this.level--; } /** * True when inside an inline block * @return {Boolean} */ }, { key: "isActive", value: function isActive() { return this.level > 0; } // Check if this should be an inline parentheses block // Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2) }, { key: "isInlineBlock", value: function isInlineBlock(tokens, index) { var length = 0; var level = 0; for (var i = index; i < tokens.length; i++) { var token = tokens[i]; length += token.value.length; // Overran max length if (length > INLINE_MAX_LENGTH) { return false; } if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPEN_PAREN) { level++; } else if (token.type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].CLOSE_PAREN) { level--; if (level === 0) { return true; } } if (this.isForbiddenToken(token)) { return false; } } return false; } // Reserved words that cause newlines, comments and semicolons // are not allowed inside inline parentheses block }, { key: "isForbiddenToken", value: function isForbiddenToken(_ref) { var type = _ref.type, value = _ref.value; return type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_NEWLINE || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].COMMENT || type === _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].BLOCK_COMMENT || value === ';'; } }]); return InlineBlock; }(); /***/ }), /***/ "./src/core/Params.js": /*!****************************!*\ !*** ./src/core/Params.js ***! \****************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Params; }); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /** * Handles placeholder replacement with given params. */ var Params = /*#__PURE__*/function () { /** * @param {Object} params */ function Params(params) { _classCallCheck(this, Params); this.params = params; this.index = 0; } /** * Returns param value that matches given placeholder with param key. * @param {Object} token * @param {String} token.key Placeholder key * @param {String} token.value Placeholder value * @return {String} param or token.value when params are missing */ _createClass(Params, [{ key: "get", value: function get(_ref) { var key = _ref.key, value = _ref.value; if (!this.params) { return value; } if (key) { return this.params[key]; } return this.params[this.index++]; } }]); return Params; }(); /***/ }), /***/ "./src/core/Tokenizer.js": /*!*******************************!*\ !*** ./src/core/Tokenizer.js ***! \*******************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Tokenizer; }); /* harmony import */ var _tokenTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tokenTypes */ "./src/core/tokenTypes.js"); /* harmony import */ var _regexFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./regexFactory */ "./src/core/regexFactory.js"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ "./src/utils.js"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Tokenizer = /*#__PURE__*/function () { /** * @param {Object} cfg * @param {String[]} cfg.reservedWords Reserved words in SQL * @param {String[]} cfg.reservedTopLevelWords Words that are set to new line separately * @param {String[]} cfg.reservedNewlineWords Words that are set to newline * @param {String[]} cfg.reservedTopLevelWordsNoIndent Words that are top level but have no indentation * @param {String[]} cfg.stringTypes String types to enable: "", '', ``, [], N'' * @param {String[]} cfg.openParens Opening parentheses to enable, like (, [ * @param {String[]} cfg.closeParens Closing parentheses to enable, like ), ] * @param {String[]} cfg.indexedPlaceholderTypes Prefixes for indexed placeholders, like ? * @param {String[]} cfg.namedPlaceholderTypes Prefixes for named placeholders, like @ and : * @param {String[]} cfg.lineCommentTypes Line comments to enable, like # and -- * @param {String[]} cfg.specialWordChars Special chars that can be found inside of words, like @ and # * @param {String[]} [cfg.operator] Additional operators to recognize */ function Tokenizer(cfg) { _classCallCheck(this, Tokenizer); this.WHITESPACE_REGEX = /^([\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+)/; this.NUMBER_REGEX = /^((\x2D[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*)?[0-9]+(\.[0-9]+)?([Ee]\x2D?[0-9]+(\.[0-9]+)?)?|0x[0-9A-Fa-f]+|0b[01]+)\b/; this.OPERATOR_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createOperatorRegex"](['<>', '<=', '>='].concat(_toConsumableArray(cfg.operators || []))); this.BLOCK_COMMENT_REGEX = /^(\/\*(?:(?![])[\s\S])*?(?:\*\/|$))/; this.LINE_COMMENT_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createLineCommentRegex"](cfg.lineCommentTypes); this.RESERVED_TOP_LEVEL_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createReservedWordRegex"](cfg.reservedTopLevelWords); this.RESERVED_TOP_LEVEL_NO_INDENT_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createReservedWordRegex"](cfg.reservedTopLevelWordsNoIndent); this.RESERVED_NEWLINE_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createReservedWordRegex"](cfg.reservedNewlineWords); this.RESERVED_PLAIN_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createReservedWordRegex"](cfg.reservedWords); this.WORD_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createWordRegex"](cfg.specialWordChars); this.STRING_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createStringRegex"](cfg.stringTypes); this.OPEN_PAREN_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createParenRegex"](cfg.openParens); this.CLOSE_PAREN_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createParenRegex"](cfg.closeParens); this.INDEXED_PLACEHOLDER_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createPlaceholderRegex"](cfg.indexedPlaceholderTypes, '[0-9]*'); this.IDENT_NAMED_PLACEHOLDER_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createPlaceholderRegex"](cfg.namedPlaceholderTypes, '[a-zA-Z0-9._$]+'); this.STRING_NAMED_PLACEHOLDER_REGEX = _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createPlaceholderRegex"](cfg.namedPlaceholderTypes, _regexFactory__WEBPACK_IMPORTED_MODULE_1__["createStringPattern"](cfg.stringTypes)); } /** * Takes a SQL string and breaks it into tokens. * Each token is an object with type and value. * * @param {String} input The SQL string * @return {Object[]} tokens An array of tokens. * @return {String} token.type * @return {String} token.value * @return {String} token.whitespaceBefore Preceding whitespace */ _createClass(Tokenizer, [{ key: "tokenize", value: function tokenize(input) { var tokens = []; var token; // Keep processing the string until it is empty while (input.length) { // grab any preceding whitespace var whitespaceBefore = this.getWhitespace(input); input = input.substring(whitespaceBefore.length); if (input.length) { // Get the next token and the token type token = this.getNextToken(input, token); // Advance the string input = input.substring(token.value.length); tokens.push(_objectSpread(_objectSpread({}, token), {}, { whitespaceBefore: whitespaceBefore })); } } return tokens; } }, { key: "getWhitespace", value: function getWhitespace(input) { var matches = input.match(this.WHITESPACE_REGEX); return matches ? matches[1] : ''; } }, { key: "getNextToken", value: function getNextToken(input, previousToken) { return this.getCommentToken(input) || this.getStringToken(input) || this.getOpenParenToken(input) || this.getCloseParenToken(input) || this.getPlaceholderToken(input) || this.getNumberToken(input) || this.getReservedWordToken(input, previousToken) || this.getWordToken(input) || this.getOperatorToken(input); } }, { key: "getCommentToken", value: function getCommentToken(input) { return this.getLineCommentToken(input) || this.getBlockCommentToken(input); } }, { key: "getLineCommentToken", value: function getLineCommentToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].LINE_COMMENT, regex: this.LINE_COMMENT_REGEX }); } }, { key: "getBlockCommentToken", value: function getBlockCommentToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].BLOCK_COMMENT, regex: this.BLOCK_COMMENT_REGEX }); } }, { key: "getStringToken", value: function getStringToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].STRING, regex: this.STRING_REGEX }); } }, { key: "getOpenParenToken", value: function getOpenParenToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPEN_PAREN, regex: this.OPEN_PAREN_REGEX }); } }, { key: "getCloseParenToken", value: function getCloseParenToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].CLOSE_PAREN, regex: this.CLOSE_PAREN_REGEX }); } }, { key: "getPlaceholderToken", value: function getPlaceholderToken(input) { return this.getIdentNamedPlaceholderToken(input) || this.getStringNamedPlaceholderToken(input) || this.getIndexedPlaceholderToken(input); } }, { key: "getIdentNamedPlaceholderToken", value: function getIdentNamedPlaceholderToken(input) { return this.getPlaceholderTokenWithKey({ input: input, regex: this.IDENT_NAMED_PLACEHOLDER_REGEX, parseKey: function parseKey(v) { return v.slice(1); } }); } }, { key: "getStringNamedPlaceholderToken", value: function getStringNamedPlaceholderToken(input) { var _this = this; return this.getPlaceholderTokenWithKey({ input: input, regex: this.STRING_NAMED_PLACEHOLDER_REGEX, parseKey: function parseKey(v) { return _this.getEscapedPlaceholderKey({ key: v.slice(2, -1), quoteChar: v.slice(-1) }); } }); } }, { key: "getIndexedPlaceholderToken", value: function getIndexedPlaceholderToken(input) { return this.getPlaceholderTokenWithKey({ input: input, regex: this.INDEXED_PLACEHOLDER_REGEX, parseKey: function parseKey(v) { return v.slice(1); } }); } }, { key: "getPlaceholderTokenWithKey", value: function getPlaceholderTokenWithKey(_ref) { var input = _ref.input, regex = _ref.regex, parseKey = _ref.parseKey; var token = this.getTokenOnFirstMatch({ input: input, regex: regex, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].PLACEHOLDER }); if (token) { token.key = parseKey(token.value); } return token; } }, { key: "getEscapedPlaceholderKey", value: function getEscapedPlaceholderKey(_ref2) { var key = _ref2.key, quoteChar = _ref2.quoteChar; return key.replace(new RegExp(Object(_utils__WEBPACK_IMPORTED_MODULE_2__["escapeRegExp"])('\\' + quoteChar), 'gu'), quoteChar); } // Decimal, binary, or hex numbers }, { key: "getNumberToken", value: function getNumberToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].NUMBER, regex: this.NUMBER_REGEX }); } // Punctuation and symbols }, { key: "getOperatorToken", value: function getOperatorToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].OPERATOR, regex: this.OPERATOR_REGEX }); } }, { key: "getReservedWordToken", value: function getReservedWordToken(input, previousToken) { // A reserved word cannot be preceded by a "." // this makes it so in "mytable.from", "from" is not considered a reserved word if (previousToken && previousToken.value && previousToken.value === '.') { return undefined; } return this.getTopLevelReservedToken(input) || this.getNewlineReservedToken(input) || this.getTopLevelReservedTokenNoIndent(input) || this.getPlainReservedToken(input); } }, { key: "getTopLevelReservedToken", value: function getTopLevelReservedToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL, regex: this.RESERVED_TOP_LEVEL_REGEX }); } }, { key: "getNewlineReservedToken", value: function getNewlineReservedToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_NEWLINE, regex: this.RESERVED_NEWLINE_REGEX }); } }, { key: "getTopLevelReservedTokenNoIndent", value: function getTopLevelReservedTokenNoIndent(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL_NO_INDENT, regex: this.RESERVED_TOP_LEVEL_NO_INDENT_REGEX }); } }, { key: "getPlainReservedToken", value: function getPlainReservedToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED, regex: this.RESERVED_PLAIN_REGEX }); } }, { key: "getWordToken", value: function getWordToken(input) { return this.getTokenOnFirstMatch({ input: input, type: _tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].WORD, regex: this.WORD_REGEX }); } }, { key: "getTokenOnFirstMatch", value: function getTokenOnFirstMatch(_ref3) { var input = _ref3.input, type = _ref3.type, regex = _ref3.regex; var matches = input.match(regex); return matches ? { type: type, value: matches[1] } : undefined; } }]); return Tokenizer; }(); /***/ }), /***/ "./src/core/regexFactory.js": /*!**********************************!*\ !*** ./src/core/regexFactory.js ***! \**********************************/ /*! exports provided: createOperatorRegex, createLineCommentRegex, createReservedWordRegex, createWordRegex, createStringRegex, createStringPattern, createParenRegex, createPlaceholderRegex */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createOperatorRegex", function() { return createOperatorRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createLineCommentRegex", function() { return createLineCommentRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createReservedWordRegex", function() { return createReservedWordRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createWordRegex", function() { return createWordRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createStringRegex", function() { return createStringRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createStringPattern", function() { return createStringPattern; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createParenRegex", function() { return createParenRegex; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createPlaceholderRegex", function() { return createPlaceholderRegex; }); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./src/utils.js"); function createOperatorRegex(multiLetterOperators) { return new RegExp("^(".concat(Object(_utils__WEBPACK_IMPORTED_MODULE_0__["sortByLengthDesc"])(multiLetterOperators).map(_utils__WEBPACK_IMPORTED_MODULE_0__["escapeRegExp"]).join('|'), "|.)"), 'u'); } function createLineCommentRegex(lineCommentTypes) { return new RegExp("^((?:".concat(lineCommentTypes.map(function (c) { return Object(_utils__WEBPACK_IMPORTED_MODULE_0__["escapeRegExp"])(c); }).join('|'), ").*?)(?:\r\n|\r|\n|$)"), 'u'); } function createReservedWordRegex(reservedWords) { if (reservedWords.length === 0) { return new RegExp("^\b$", 'u'); } var reservedWordsPattern = Object(_utils__WEBPACK_IMPORTED_MODULE_0__["sortByLengthDesc"])(reservedWords).join('|').replace(/ /g, '\\s+'); return new RegExp("^(".concat(reservedWordsPattern, ")\\b"), 'iu'); } function createWordRegex() { var specialChars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return new RegExp("^([\\p{Alphabetic}\\p{Mark}\\p{Decimal_Number}\\p{Connector_Punctuation}\\p{Join_Control}".concat(specialChars.join(''), "]+)"), 'u'); } function createStringRegex(stringTypes) { return new RegExp('^(' + createStringPattern(stringTypes) + ')', 'u'); } // This enables the following string patterns: // 1. backtick quoted string using `` to escape // 2. square bracket quoted string (SQL Server) using ]] to escape // 3. double quoted string using "" or \" to escape // 4. single quoted string using '' or \' to escape // 5. national character quoted string using N'' or N\' to escape // 6. Unicode single-quoted string using \' to escape // 7. Unicode double-quoted string using \" to escape // 8. PostgreSQL dollar-quoted strings function createStringPattern(stringTypes) { var patterns = { '``': '((`[^`]*($|`))+)', '{}': '((\\{[^\\}]*($|\\}))+)', '[]': '((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)', '""': '(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)', "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", "N''": "((N'[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", "U&''": "((U&'[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", 'U&""': '((U&"[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)', $$: '((?<tag>\\$\\w*\\$)[\\s\\S]*?(?:\\k<tag>|$))' }; return stringTypes.map(function (t) { return patterns[t]; }).join('|'); } function createParenRegex(parens) { return new RegExp('^(' + parens.map(escapeParen).join('|') + ')', 'iu'); } function escapeParen(paren) { if (paren.length === 1) { // A single punctuation character return Object(_utils__WEBPACK_IMPORTED_MODULE_0__["escapeRegExp"])(paren); } else { // longer word return '\\b' + paren + '\\b'; } } function createPlaceholderRegex(types, pattern) { if (Object(_utils__WEBPACK_IMPORTED_MODULE_0__["isEmpty"])(types)) { return false; } var typesRegex = types.map(_utils__WEBPACK_IMPORTED_MODULE_0__["escapeRegExp"]).join('|'); return new RegExp("^((?:".concat(typesRegex, ")(?:").concat(pattern, "))"), 'u'); } /***/ }), /***/ "./src/core/token.js": /*!***************************!*\ !*** ./src/core/token.js ***! \***************************/ /*! exports provided: isAnd, isBetween, isLimit, isSet, isBy, isWindow, isEnd */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isAnd", function() { return isAnd; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBetween", function() { return isBetween; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isLimit", function() { return isLimit; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isSet", function() { return isSet; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBy", function() { return isBy; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isWindow", function() { return isWindow; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isEnd", function() { return isEnd; }); /* harmony import */ var _tokenTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tokenTypes */ "./src/core/tokenTypes.js"); var isToken = function isToken(type, regex) { return function (token) { return (token === null || token === void 0 ? void 0 : token.type) === type && regex.test(token === null || token === void 0 ? void 0 : token.value); }; }; var isAnd = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_NEWLINE, /^AND$/i); var isBetween = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED, /^BETWEEN$/i); var isLimit = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL, /^LIMIT$/i); var isSet = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL, /^[S\u017F]ET$/i); var isBy = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED, /^BY$/i); var isWindow = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].RESERVED_TOP_LEVEL, /^WINDOW$/i); var isEnd = isToken(_tokenTypes__WEBPACK_IMPORTED_MODULE_0__["default"].CLOSE_PAREN, /^END$/i); /***/ }), /***/ "./src/core/tokenTypes.js": /*!********************************!*\ !*** ./src/core/tokenTypes.js ***! \********************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /** * Constants for token types */ /* harmony default export */ __webpack_exports__["default"] = ({ WORD: 'word', STRING: 'string', RE