sql-formatter-plus
Version:
Formats whitespace in a SQL query to make it more readable
260 lines (231 loc) • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _includes = _interopRequireDefault(require("lodash/includes"));
var _tokenTypes = _interopRequireDefault(require("./tokenTypes"));
var _Indentation = _interopRequireDefault(require("./Indentation"));
var _InlineBlock = _interopRequireDefault(require("./InlineBlock"));
var _Params = _interopRequireDefault(require("./Params"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": 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 trimSpacesEnd = function trimSpacesEnd(str) {
return str.replace(/[\t ]+$/, '');
};
var Formatter =
/*#__PURE__*/
function () {
/**
* @param {Object} cfg
* @param {String} cfg.language
* @param {String} cfg.indent
* @param {Bool} cfg.uppercase
* @param {Integer} cfg.linesBetweenQueries
* @param {Object} cfg.params
* @param {Tokenizer} tokenizer
*/
function Formatter(cfg, tokenizer, tokenOverride) {
_classCallCheck(this, Formatter);
this.cfg = cfg || {};
this.indentation = new _Indentation["default"](this.cfg.indent);
this.inlineBlock = new _InlineBlock["default"]();
this.params = new _Params["default"](this.cfg.params);
this.tokenizer = tokenizer;
this.tokenOverride = tokenOverride;
this.previousReservedWord = {};
this.tokens = [];
this.index = 0;
}
/**
* Formats whitespace in a SQL string to make it easier to read.
*
* @param {String} query The SQL query string
* @return {String} formatted query
*/
_createClass(Formatter, [{
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;
if (_this.tokenOverride) token = _this.tokenOverride(token, _this.previousReservedWord) || token;
if (token.type === _tokenTypes["default"].WHITESPACE) {// ignore (we do our own whitespace formatting)
} else if (token.type === _tokenTypes["default"].LINE_COMMENT) {
formattedQuery = _this.formatLineComment(token, formattedQuery);
} else if (token.type === _tokenTypes["default"].BLOCK_COMMENT) {
formattedQuery = _this.formatBlockComment(token, formattedQuery);
} else if (token.type === _tokenTypes["default"].RESERVED_TOP_LEVEL) {
formattedQuery = _this.formatTopLevelReservedWord(token, formattedQuery);
_this.previousReservedWord = token;
} else if (token.type === _tokenTypes["default"].RESERVED_TOP_LEVEL_NO_INDENT) {
formattedQuery = _this.formatTopLevelReservedWordNoIndent(token, formattedQuery);
_this.previousReservedWord = token;
} else if (token.type === _tokenTypes["default"].RESERVED_NEWLINE) {
formattedQuery = _this.formatNewlineReservedWord(token, formattedQuery);
_this.previousReservedWord = token;
} else if (token.type === _tokenTypes["default"].RESERVED) {
formattedQuery = _this.formatWithSpaces(token, formattedQuery);
_this.previousReservedWord = token;
} else if (token.type === _tokenTypes["default"].OPEN_PAREN) {
formattedQuery = _this.formatOpeningParentheses(token, formattedQuery);
} else if (token.type === _tokenTypes["default"].CLOSE_PAREN) {
formattedQuery = _this.formatClosingParentheses(token, formattedQuery);
} else if (token.type === _tokenTypes["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 + token.value);
}
}, {
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.formatReservedWord(token.value));
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.formatReservedWord(token.value));
return this.addNewline(query);
}
}, {
key: "formatNewlineReservedWord",
value: function formatNewlineReservedWord(token, query) {
return this.addNewline(query) + this.equalizeWhitespace(this.formatReservedWord(token.value)) + ' ';
} // 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) {
// Take out the preceding space unless there was whitespace there in the original query
// or another opening parens or line comment
var preserveWhitespaceFor = [_tokenTypes["default"].WHITESPACE, _tokenTypes["default"].OPEN_PAREN, _tokenTypes["default"].LINE_COMMENT];
if (!(0, _includes["default"])(preserveWhitespaceFor, this.previousToken().type)) {
query = trimSpacesEnd(query);
}
query += this.cfg.uppercase ? token.value.toUpperCase() : token.value;
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) {
token.value = this.cfg.uppercase ? token.value.toUpperCase() : token.value;
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 = trimSpacesEnd(query) + token.value + ' ';
if (this.inlineBlock.isActive()) {
return query;
} else if (/^LIMIT$/i.test(this.previousReservedWord.value)) {
return query;
} else {
return this.addNewline(query);
}
}
}, {
key: "formatWithSpaceAfter",
value: function formatWithSpaceAfter(token, query) {
return trimSpacesEnd(query) + token.value + ' ';
}
}, {
key: "formatWithoutSpaces",
value: function formatWithoutSpaces(token, query) {
return trimSpacesEnd(query) + token.value;
}
}, {
key: "formatWithSpaces",
value: function formatWithSpaces(token, query) {
var value = token.type === 'reserved' ? this.formatReservedWord(token.value) : token.value;
return query + value + ' ';
}
}, {
key: "formatReservedWord",
value: function formatReservedWord(value) {
return this.cfg.uppercase ? value.toUpperCase() : value;
}
}, {
key: "formatQuerySeparator",
value: function formatQuerySeparator(token, query) {
this.indentation.resetIndentation();
return trimSpacesEnd(query) + token.value + '\n'.repeat(this.cfg.linesBetweenQueries || 1);
}
}, {
key: "addNewline",
value: function addNewline(query) {
query = trimSpacesEnd(query);
if (!query.endsWith('\n')) query += '\n';
return query + this.indentation.getIndent();
}
}, {
key: "previousToken",
value: function previousToken() {
var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return this.tokens[this.index - offset] || {};
}
}]);
return Formatter;
}();
exports["default"] = Formatter;
module.exports = exports.default;