UNPKG

sql-formatter

Version:

Format whitespace in a SQL query to make it more readable

1,222 lines (994 loc) 974 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(); })(self, () => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/core/AliasAs.ts": /*!*****************************!*\ !*** ./src/core/AliasAs.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ AliasAs) /* harmony export */ }); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./token */ "./src/core/token.ts"); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } /** Decides addition and removal of AS tokens */ var AliasAs = /*#__PURE__*/function () { function AliasAs(aliasAs, formatter) { _classCallCheck(this, AliasAs); this.aliasAs = aliasAs; this.formatter = formatter; } /** True when AS keyword should be added *before* current token */ _createClass(AliasAs, [{ key: "shouldAddBefore", value: function shouldAddBefore(token) { return this.isMissingTableAlias(token) || this.isMissingSelectColumnAlias(token); } // if table alias is missing and should be added }, { key: "isMissingTableAlias", value: function isMissingTableAlias(token) { return this.aliasAs === 'always' && token.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.WORD && this.lookBehind().value === ')'; } // if select column alias is missing and should be added }, { key: "isMissingSelectColumnAlias", value: function isMissingSelectColumnAlias(token) { var prevToken = this.lookBehind(); var nextToken = this.lookAhead(); return (this.aliasAs === 'always' || this.aliasAs === 'select') && this.formatter.isWithinSelect() && token.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.WORD && (_token__WEBPACK_IMPORTED_MODULE_0__.isToken.END(prevToken) || (prevToken.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.WORD || prevToken.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.NUMBER) && (nextToken.value === ',' || (0,_token__WEBPACK_IMPORTED_MODULE_0__.isCommand)(nextToken))); } /** True when AS keyword should be added *after* current token */ }, { key: "shouldAddAfter", value: function shouldAddAfter() { return this.isEdgeCaseCTE() || this.isEdgeCaseCreateTable() || this.isMissingTypeCastAs(); } // checks for CAST(«expression» [AS] type) }, { key: "isMissingTypeCastAs", value: function isMissingTypeCastAs() { return this.aliasAs === 'never' && this.formatter.isWithinSelect() && _token__WEBPACK_IMPORTED_MODULE_0__.isToken.CAST(this.formatter.getPreviousReservedToken()) && _token__WEBPACK_IMPORTED_MODULE_0__.isToken.AS(this.lookAhead()) && (this.lookAhead(2).type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.WORD || this.lookAhead(2).type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.RESERVED_KEYWORD) && this.lookAhead(3).value === ')'; } // checks for WITH `table` [AS] ( }, { key: "isEdgeCaseCTE", value: function isEdgeCaseCTE() { var nextToken = this.lookAhead(); return this.aliasAs === 'never' && _token__WEBPACK_IMPORTED_MODULE_0__.isToken.WITH(this.lookBehind()) && (nextToken.value === '(' || _token__WEBPACK_IMPORTED_MODULE_0__.isToken.AS(nextToken) && this.lookAhead(2).value === '('); } // checks for CREATE TABLE `table` [AS] WITH ( }, { key: "isEdgeCaseCreateTable", value: function isEdgeCaseCreateTable() { var prevToken = this.lookBehind(); var nextToken = this.lookAhead(); return this.aliasAs === 'never' && (_token__WEBPACK_IMPORTED_MODULE_0__.isToken.TABLE(prevToken) || prevToken.value.endsWith('TABLE')) && (_token__WEBPACK_IMPORTED_MODULE_0__.isToken.WITH(nextToken) || _token__WEBPACK_IMPORTED_MODULE_0__.isToken.AS(nextToken) && _token__WEBPACK_IMPORTED_MODULE_0__.isToken.WITH(this.lookAhead(2))); } /* True when the current AS token should be discarded */ }, { key: "shouldRemove", value: function shouldRemove() { return this.aliasAs === 'never' || this.aliasAs === 'select' && this.isRemovableNonSelectAs(); } }, { key: "isRemovableNonSelectAs", value: function isRemovableNonSelectAs() { return this.lookBehind().value === ')' && // ) [AS] alias but not SELECT (a) [AS] alpha !this.formatter.isWithinSelect() && this.lookAhead().value !== '(' // skip WITH foo [AS] ( ... ; } }, { key: "lookBehind", value: function lookBehind(n) { return this.formatter.tokenLookBehind(n); } }, { key: "lookAhead", value: function lookAhead(n) { return this.formatter.tokenLookAhead(n); } }]); return AliasAs; }(); /***/ }), /***/ "./src/core/AsTokenFactory.ts": /*!************************************!*\ !*** ./src/core/AsTokenFactory.ts ***! \************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ AsTokenFactory) /* harmony export */ }); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./token */ "./src/core/token.ts"); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } var AsTokenFactory = /*#__PURE__*/function () { function AsTokenFactory(keywordCase) { var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; _classCallCheck(this, AsTokenFactory); this.keywordCase = keywordCase; this.detectedCase = this.autoDetectCase(tokens); } _createClass(AsTokenFactory, [{ key: "autoDetectCase", value: function autoDetectCase(tokens) { var asTokens = tokens.filter(_token__WEBPACK_IMPORTED_MODULE_0__.isToken.AS); var upperAsTokens = asTokens.filter(function (_ref) { var value = _ref.value; return value === 'AS'; }); return upperAsTokens.length > asTokens.length / 2 ? 'upper' : 'lower'; } /** Returns AS token with either upper- or lowercase text */ }, { key: "token", value: function token() { return { type: _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.RESERVED_KEYWORD, value: this.asTokenValue() }; } }, { key: "asTokenValue", value: function asTokenValue() { var keywordCase = this.keywordCase === 'preserve' ? this.detectedCase : this.keywordCase; return keywordCase === 'upper' ? 'AS' : 'as'; } }]); return AsTokenFactory; }(); /***/ }), /***/ "./src/core/Formatter.ts": /*!*******************************!*\ !*** ./src/core/Formatter.ts ***! \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ Formatter) /* harmony export */ }); /* harmony import */ var _Indentation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Indentation */ "./src/core/Indentation.ts"); /* harmony import */ var _InlineBlock__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./InlineBlock */ "./src/core/InlineBlock.ts"); /* harmony import */ var _Params__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Params */ "./src/core/Params.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../utils */ "./src/utils.ts"); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./token */ "./src/core/token.ts"); /* harmony import */ var _formatCommaPositions__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./formatCommaPositions */ "./src/core/formatCommaPositions.ts"); /* harmony import */ var _formatAliasPositions__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./formatAliasPositions */ "./src/core/formatAliasPositions.ts"); /* harmony import */ var _tabularStyle__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./tabularStyle */ "./src/core/tabularStyle.ts"); /* harmony import */ var _AliasAs__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./AliasAs */ "./src/core/AliasAs.ts"); /* harmony import */ var _AsTokenFactory__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./AsTokenFactory */ "./src/core/AsTokenFactory.ts"); function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } 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 _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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } /** Main formatter class that produces a final output string from list of tokens */ var Formatter = /*#__PURE__*/function () { function Formatter(cfg) { _classCallCheck(this, Formatter); this.currentNewline = true; this.previousReservedToken = _token__WEBPACK_IMPORTED_MODULE_4__.EOF_TOKEN; this.previousCommandToken = _token__WEBPACK_IMPORTED_MODULE_4__.EOF_TOKEN; this.tokens = []; this.index = -1; this.cfg = cfg; this.indentation = new _Indentation__WEBPACK_IMPORTED_MODULE_0__["default"](this.indentString()); this.inlineBlock = new _InlineBlock__WEBPACK_IMPORTED_MODULE_1__["default"](this.cfg.expressionWidth); this.aliasAs = new _AliasAs__WEBPACK_IMPORTED_MODULE_8__["default"](this.cfg.aliasAs, this); this.params = new _Params__WEBPACK_IMPORTED_MODULE_2__["default"](this.cfg.params); this.asTokenFactory = new _AsTokenFactory__WEBPACK_IMPORTED_MODULE_9__["default"](this.cfg.keywordCase); } _createClass(Formatter, [{ key: "indentString", value: function indentString() { if (this.isTabularStyle()) { return ' '.repeat(10); } if (this.cfg.useTabs) { return '\t'; } return ' '.repeat(this.cfg.tabWidth); } /** * SQL Tokenizer for this formatter, provided by subclasses. */ }, { key: "tokenizer", value: function tokenizer() { throw new Error('tokenizer() not implemented by subclass'); } /** * Reprocess and modify a token based on parsed context. * Subclasses can override this to modify tokens during formatting. * @param {Token} token - The token to modify * @return {Token} new token or the original */ }, { key: "tokenOverride", value: function tokenOverride(token) { return token; } /** * Formats an SQL query. * @param {string} query - The SQL query string to be formatted * @return {string} The formatter query */ }, { key: "format", value: function format(query) { this.tokens = this.tokenizer().tokenize(query); this.asTokenFactory = new _AsTokenFactory__WEBPACK_IMPORTED_MODULE_9__["default"](this.cfg.keywordCase, this.tokens); var formattedQuery = this.getFormattedQueryFromTokens(); var finalQuery = this.postFormat(formattedQuery); return finalQuery.replace(/^\n*/, '').trimEnd(); } /** * Does post-processing on the formatted query. */ }, { key: "postFormat", value: function postFormat(query) { if (this.cfg.tabulateAlias) { query = (0,_formatAliasPositions__WEBPACK_IMPORTED_MODULE_6__["default"])(query); } if (this.cfg.commaPosition === 'before' || this.cfg.commaPosition === 'tabular') { query = (0,_formatCommaPositions__WEBPACK_IMPORTED_MODULE_5__["default"])(query, this.cfg.commaPosition, this.indentString()); } return query; } /** * Performs main construction of query from token list, delegates to other methods for formatting based on token criteria */ }, { key: "getFormattedQueryFromTokens", value: function getFormattedQueryFromTokens() { var formattedQuery = ''; for (this.index = 0; this.index < this.tokens.length; this.index++) { var token = this.tokenOverride(this.tokens[this.index]); // if token is a Reserved Keyword, Command, Binary Command, Dependent Clause, Logical Operator if ((0,_token__WEBPACK_IMPORTED_MODULE_4__.isReserved)(token)) { this.previousReservedToken = token; if (token.type !== _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_KEYWORD && token.type !== _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_JOIN_CONDITION) { // convert Reserved Command or Logical Operator to tabular format if needed token = (0,_tabularStyle__WEBPACK_IMPORTED_MODULE_7__.toTabularToken)(token, this.cfg.indentStyle); } if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_COMMAND) { this.previousCommandToken = token; } } if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.LINE_COMMENT) { formattedQuery = this.formatLineComment(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_COMMENT) { formattedQuery = this.formatBlockComment(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_COMMAND) { this.currentNewline = this.checkNewline(token); formattedQuery = this.formatCommand(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_BINARY_COMMAND) { formattedQuery = this.formatBinaryCommand(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_DEPENDENT_CLAUSE) { formattedQuery = this.formatDependentClause(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_JOIN_CONDITION) { formattedQuery = this.formatJoinCondition(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_LOGICAL_OPERATOR) { formattedQuery = this.formatLogicalOperator(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_KEYWORD) { formattedQuery = this.formatKeyword(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_START) { formattedQuery = this.formatBlockStart(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_END) { formattedQuery = this.formatBlockEnd(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.PLACEHOLDER) { formattedQuery = this.formatPlaceholder(token, formattedQuery); } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.OPERATOR) { formattedQuery = this.formatOperator(token, formattedQuery); } else { formattedQuery = this.formatWord(token, formattedQuery); } } return (0,_tabularStyle__WEBPACK_IMPORTED_MODULE_7__.replaceTabularPlaceholders)(formattedQuery); } /** * Formats word tokens + any potential AS tokens for aliases */ }, { key: "formatWord", value: function formatWord(token, query) { var finalQuery = query; if (this.aliasAs.shouldAddBefore(token)) { finalQuery = this.formatWithSpaces(this.asTokenFactory.token(), finalQuery); } finalQuery = this.formatWithSpaces(token, finalQuery); if (this.aliasAs.shouldAddAfter()) { finalQuery = this.formatWithSpaces(this.asTokenFactory.token(), finalQuery); } return finalQuery; } /** * Checks if a newline should currently be inserted */ }, { key: "checkNewline", value: function checkNewline(token) { var nextTokens = this.tokensUntilNextCommandOrQueryEnd(); // auto break if SELECT includes CASE statements if (this.isWithinSelect() && nextTokens.some(_token__WEBPACK_IMPORTED_MODULE_4__.isToken.CASE)) { return true; } switch (this.cfg.multilineLists) { case 'always': return true; case 'avoid': return false; case 'expressionWidth': return this.inlineWidth(token, nextTokens) > this.cfg.expressionWidth; default: // multilineLists mode is a number return this.countClauses(nextTokens) > this.cfg.multilineLists || this.inlineWidth(token, nextTokens) > this.cfg.expressionWidth; } } }, { key: "inlineWidth", value: function inlineWidth(token, tokens) { var tokensString = tokens.map(function (_ref) { var value = _ref.value; return value === ',' ? value + ' ' : value; }).join(''); return "".concat(token.whitespaceBefore).concat(token.value, " ").concat(tokensString).length; } /** * Counts comma-separated clauses (doesn't count commas inside blocks) * Note: There's always at least one clause. */ }, { key: "countClauses", value: function countClauses(tokens) { var count = 1; var openBlocks = 0; var _iterator = _createForOfIteratorHelper(tokens), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = _step.value, type = _step$value.type, value = _step$value.value; if (value === ',' && openBlocks === 0) { count++; } if (type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_START) { openBlocks++; } if (type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_END) { openBlocks--; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return count; } /** get all tokens between current token and next Reserved Command or query end */ }, { key: "tokensUntilNextCommandOrQueryEnd", value: function tokensUntilNextCommandOrQueryEnd() { var tail = this.tokens.slice(this.index + 1); return tail.slice(0, tail.length ? tail.findIndex(function (token) { return (0,_token__WEBPACK_IMPORTED_MODULE_4__.isCommand)(token) || token.value === ';'; }) : undefined); } /** Formats a line comment onto query */ }, { key: "formatLineComment", value: function formatLineComment(token, query) { return this.addNewline(query + this.show(token)); } /** Formats a block comment onto query */ }, { key: "formatBlockComment", value: function formatBlockComment(token, query) { return this.addNewline(this.addNewline(query) + this.indentComment(token.value)); } /** Aligns comment to current indentation level */ }, { key: "indentComment", value: function indentComment(comment) { return comment.replace(/\n[\t ]*/g, '\n' + this.indentation.getIndent() + ' '); } /** * Formats a Reserved Command onto query, increasing indentation level where necessary */ }, { key: "formatCommand", value: function formatCommand(token, query) { this.indentation.decreaseTopLevel(); query = this.addNewline(query); // indent tabular formats, except when preceding a ( if (this.isTabularStyle()) { if (this.tokenLookAhead().value !== '(') { this.indentation.increaseTopLevel(); } } else { this.indentation.increaseTopLevel(); } query += this.equalizeWhitespace(this.show(token)); // print token onto query if (this.currentNewline && !this.isTabularStyle()) { query = this.addNewline(query); } else { query += ' '; } return query; } /** * Formats a Reserved Binary Command onto query, joining neighbouring tokens */ }, { key: "formatBinaryCommand", value: function formatBinaryCommand(token, query) { var isJoin = /JOIN/i.test(token.value); // check if token contains JOIN if (!isJoin || this.isTabularStyle()) { // decrease for boolean set operators or in tabular mode this.indentation.decreaseTopLevel(); } query = this.addNewline(query) + this.equalizeWhitespace(this.show(token)); return isJoin ? query + ' ' : this.addNewline(query); } /** * Formats a Reserved Keyword onto query, skipping AS if disabled */ }, { key: "formatKeyword", value: function formatKeyword(token, query) { if (_token__WEBPACK_IMPORTED_MODULE_4__.isToken.AS(token) && this.aliasAs.shouldRemove()) { return query; } return this.formatWithSpaces(token, query); } /** * Formats a Reserved Dependent Clause token onto query, supporting the keyword that precedes it */ }, { key: "formatDependentClause", value: function formatDependentClause(token, query) { return this.addNewline(query) + this.equalizeWhitespace(this.show(token)) + ' '; } // Formats ON and USING keywords }, { key: "formatJoinCondition", value: function formatJoinCondition(token, query) { return query + this.equalizeWhitespace(this.show(token)) + ' '; } /** * Formats an Operator onto query, following rules for specific characters */ }, { key: "formatOperator", value: function formatOperator(token, query) { // special operator if (token.value === ',') { return this.formatComma(token, query); } else if (token.value === ';') { return this.formatQuerySeparator(token, query); } else if (['$', '['].includes(token.value)) { return this.formatWithSpaces(token, query, 'before'); } else if ([':', ']'].includes(token.value)) { return this.formatWithSpaces(token, query, 'after'); } else if (['.', '{', '}', '`'].includes(token.value)) { return this.formatWithoutSpaces(token, query); } // regular operator if (this.cfg.denseOperators && this.tokenLookBehind().type !== _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.RESERVED_COMMAND) { // do not trim whitespace if SELECT * return this.formatWithoutSpaces(token, query); } return this.formatWithSpaces(token, query); } /** * Formats a Logical Operator onto query, joining boolean conditions */ }, { key: "formatLogicalOperator", value: function formatLogicalOperator(token, query) { // ignore AND when BETWEEN x [AND] y if (_token__WEBPACK_IMPORTED_MODULE_4__.isToken.AND(token) && _token__WEBPACK_IMPORTED_MODULE_4__.isToken.BETWEEN(this.tokenLookBehind(2))) { return this.formatWithSpaces(token, query); } if (this.isTabularStyle()) { this.indentation.decreaseTopLevel(); } if (this.cfg.logicalOperatorNewline === 'before') { return (this.currentNewline ? this.addNewline(query) : query) + this.equalizeWhitespace(this.show(token)) + ' '; } else { query += this.show(token); return this.currentNewline ? this.addNewline(query) : query; } } /** 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, ' '); } /** * Formats a Block Start token (left paren/bracket/brace, CASE) onto query, beginning an Inline Block or increasing indentation where necessary */ }, { key: "formatBlockStart", value: function formatBlockStart(token, query) { var _a; if (_token__WEBPACK_IMPORTED_MODULE_4__.isToken.CASE(token)) { query = this.formatWithSpaces(token, query); } else { // Take out the preceding space unless there was whitespace there in the original query // or another opening parens or line comment var preserveWhitespaceFor = [_token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_START, _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.LINE_COMMENT, _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.OPERATOR]; if (((_a = token.whitespaceBefore) === null || _a === void 0 ? void 0 : _a.length) === 0 && !preserveWhitespaceFor.includes(this.tokenLookBehind().type)) { query = (0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query); } else if (!this.cfg.newlineBeforeOpenParen) { query = query.trimEnd() + ' '; } query += this.show(token); this.inlineBlock.beginIfPossible(this.tokens, this.index); } if (!this.inlineBlock.isActive()) { this.indentation.increaseBlockLevel(); if (!_token__WEBPACK_IMPORTED_MODULE_4__.isToken.CASE(token) || this.cfg.multilineLists === 'always') { query = this.addNewline(query); } } return query; } /** * Formats a Block End token (right paren/bracket/brace, END) onto query, closing an Inline Block or decreasing indentation where necessary */ }, { key: "formatBlockEnd", value: function formatBlockEnd(token, query) { if (this.inlineBlock.isActive()) { this.inlineBlock.end(); if (_token__WEBPACK_IMPORTED_MODULE_4__.isToken.END(token)) { return this.formatWithSpaces(token, query); // add space before END when closing inline block } return this.formatWithSpaces(token, query, 'after'); // do not add space before ) } else { this.indentation.decreaseBlockLevel(); if (this.isTabularStyle()) { // +1 extra indentation step for the closing paren query = this.addNewline(query) + this.indentation.getSingleIndent(); } else if (this.cfg.newlineBeforeCloseParen) { query = this.addNewline(query); } else { query = query.trimEnd() + ' '; } return this.formatWithSpaces(token, query); } } /** * Formats a Placeholder item onto query, to be replaced with the value of the placeholder */ }, { key: "formatPlaceholder", value: function formatPlaceholder(token, query) { return query + this.params.get(token) + ' '; } /** * Formats a comma Operator onto query, ending line unless in an Inline Block */ }, { key: "formatComma", value: function formatComma(token, query) { query = (0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query) + this.show(token) + ' '; if (this.inlineBlock.isActive()) { return query; } else if (_token__WEBPACK_IMPORTED_MODULE_4__.isToken.LIMIT(this.getPreviousReservedToken())) { return query; } else if (this.currentNewline) { return this.addNewline(query); } else { return query; } } /** Simple append of token onto query */ }, { key: "formatWithoutSpaces", value: function formatWithoutSpaces(token, query) { return (0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query) + this.show(token); } /** * Add token onto query with spaces - either before, after, or both */ }, { key: "formatWithSpaces", value: function formatWithSpaces(token, query) { var addSpace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'both'; var before = addSpace === 'after' ? (0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query) : query; var after = addSpace === 'before' ? '' : ' '; return before + this.show(token) + after; } }, { key: "formatQuerySeparator", value: function formatQuerySeparator(token, query) { this.indentation.resetIndentation(); return [(0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query), this.cfg.newlineBeforeSemicolon ? '\n' : '', this.show(token), '\n'.repeat(this.cfg.linesBetweenQueries + 1)].join(''); } /** Converts token to string, uppercasing if enabled */ }, { key: "show", value: function show(token) { if ((0,_token__WEBPACK_IMPORTED_MODULE_4__.isReserved)(token) || token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_START || token.type === _token__WEBPACK_IMPORTED_MODULE_4__.TokenType.BLOCK_END) { switch (this.cfg.keywordCase) { case 'preserve': return token.value; case 'upper': return token.value.toUpperCase(); case 'lower': return token.value.toLowerCase(); } } else { return token.value; } } /** Inserts a newline onto the query */ }, { key: "addNewline", value: function addNewline(query) { query = (0,_utils__WEBPACK_IMPORTED_MODULE_3__.trimSpacesEnd)(query); if (!query.endsWith('\n')) { query += '\n'; } return query + this.indentation.getIndent(); } }, { key: "isTabularStyle", value: function isTabularStyle() { return this.cfg.indentStyle === 'tabularLeft' || this.cfg.indentStyle === 'tabularRight'; } /** Returns the latest encountered reserved keyword token */ }, { key: "getPreviousReservedToken", value: function getPreviousReservedToken() { return this.previousReservedToken; } /** True when currently within SELECT command */ }, { key: "isWithinSelect", value: function isWithinSelect() { return _token__WEBPACK_IMPORTED_MODULE_4__.isToken.SELECT(this.previousCommandToken); } /** Fetches nth previous token from the token stream */ }, { key: "tokenLookBehind", value: function tokenLookBehind() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return this.tokens[this.index - n] || _token__WEBPACK_IMPORTED_MODULE_4__.EOF_TOKEN; } /** Fetches nth next token from the token stream */ }, { key: "tokenLookAhead", value: function tokenLookAhead() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return this.tokens[this.index + n] || _token__WEBPACK_IMPORTED_MODULE_4__.EOF_TOKEN; } }]); return Formatter; }(); /***/ }), /***/ "./src/core/Indentation.ts": /*!*********************************!*\ !*** ./src/core/Indentation.ts ***! \*********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ Indentation) /* harmony export */ }); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./src/utils.ts"); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); 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_COMMAND words */ var Indentation = /*#__PURE__*/function () { /** * @param {string} indent A string to indent with */ function Indentation(indent) { _classCallCheck(this, Indentation); this.indent = indent; this.indentTypes = []; } /** * Returns indentation string for single indentation step. */ _createClass(Indentation, [{ key: "getSingleIndent", value: function getSingleIndent() { return this.indent; } /** * Returns current indentation string. * @return {string} indentation string based on indentTypes */ }, { 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 && (0,_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; } } } /** Clears all indentation */ }, { key: "resetIndentation", value: function resetIndentation() { this.indentTypes = []; } }]); return Indentation; }(); /***/ }), /***/ "./src/core/InlineBlock.ts": /*!*********************************!*\ !*** ./src/core/InlineBlock.ts ***! \*********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ InlineBlock) /* harmony export */ }); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./token */ "./src/core/token.ts"); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } /** * Bookkeeper for inline blocks. * * Inline blocks are parenthesised expressions that are shorter than INLINE_MAX_LENGTH. * These blocks are formatted on a single line, unlike longer parenthesised * expressions where open-parenthesis causes newline and increase of indentation. */ var InlineBlock = /*#__PURE__*/function () { function InlineBlock(expressionWidth) { _classCallCheck(this, InlineBlock); this.level = 0; this.expressionWidth = expressionWidth; } /** * Begins inline block when lookahead through upcoming tokens determines * that the block would be smaller than INLINE_MAX_LENGTH. * @param {Token[]} 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 */ }, { 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; if (this.isForbiddenToken(token)) { return false; } // Overran max length if (length > this.expressionWidth) { return false; } if (token.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.BLOCK_START) { level++; } else if (token.type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.BLOCK_END) { level--; if (level === 0) { return true; } } } 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 === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.RESERVED_COMMAND || type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.RESERVED_LOGICAL_OPERATOR || // type === TokenType.LINE_COMMENT || type === _token__WEBPACK_IMPORTED_MODULE_0__.TokenType.BLOCK_COMMENT || value === ';' || _token__WEBPACK_IMPORTED_MODULE_0__.isToken.CASE({ type: type, value: value }) // CASE cannot have inline blocks ; } }]); return InlineBlock; }(); /***/ }), /***/ "./src/core/Params.ts": /*!****************************!*\ !*** ./src/core/Params.ts ***! \****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ Params) /* harmony export */ }); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } /** * Handles placeholder replacement with given params. */ var Params = /*#__PURE__*/function () { function Params(params) { _classCallCheck(this, Params); this.params = params; this.index = 0; } /** * Returns param value that matches given placeholder with param key. * @param {Token} token * @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.ts": /*!*******************************!*\ !*** ./src/core/Tokenizer.ts ***! \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "WHITESPACE_REGEX": () => (/* binding */ WHITESPACE_REGEX), /* harmony export */ "default": () => (/* binding */ Tokenizer) /* harmony export */ }); /* harmony import */ var _regexFactory__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./regexFactory */ "./src/core/regexFactory.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ "./src/utils.ts"); /* harmony import */ var _token__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./token */ "./src/core/token.ts"); 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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } // convert to partial type import in TS 4.5 var WHITESPACE_REGEX = /^([\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+)/; var NULL_REGEX = /(?!)/; // zero-width negative lookahead, matches nothing /** Converts SQL language string into a token stream */ var Tokenizer = /*#__PURE__*/function () { /** * @param {TokenizerOptions} cfg * @param {string[]} cfg.reservedKeywords - Reserved words in SQL * @param {string[]} cfg.reservedDependentClauses - Words that following a specific Statement and must have data attached * @param {string[]} cfg.reservedLogicalOperators - Words that are set to newline * @param {string[]} cfg.reservedCommands - Words that are set to new line separately * @param {string[]} cfg.reservedBinaryCommands - Words that are top level but have no indentation * @param {string[]} cfg.reservedJoinConditions - ON and USING * @param {string[]} cfg.stringTypes - string types to enable - "", '', ``, [], N'' * @param {string[]} cfg.blockStart - Opening parentheses to enable, like (, [ * @param {string[]} cfg.blockEnd - 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.operators - Additional operators to recognize */ function Tokenizer(cfg) { var _this = this, _this$REGEX_MAP; _classCallCheck(this, Tokenizer); var _a, _b, _c, _d; /** Curried function of `getTokenOnFirstMatch` that allows token type to be passed first */ this.matchToken = function (tokenType) { return function (input) { return _this.getTokenOnFirstMatch({ input: input, type: tokenType, regex: _this.REGEX_MAP[tokenType] }); }; }; var specialWordCharsAll = Object.values((_a = cfg.specialWordChars) !== null && _a !== void 0 ? _a : {}).join(''); this.REGEX_MAP = (_this$REGEX_MAP = {}, _defineProperty(_this$REGEX_MAP, _token__WEBPACK_IMPORTED_MODULE_2__.TokenType.WORD, _regexFactory__WEBPACK_IMPORTED_MODULE_0__.createWordRegex(cfg.specialWordChars)), _defineProperty(_this$REGEX_MAP, _token__WEBPACK_IMPORTED_MODULE_2__.TokenType.STRING, _regexFactory__WEBPACK_IMPORTED_MODULE_0__.createStringRegex(cfg.stringTypes)), _defineProperty(_this$REGEX_MAP, _token__WEBPACK_IMPORTED_MODULE_2__.TokenType.RESERVED_KEYWORD, _regexFactory__WEBP