├── .eslintignore
├── .gitignore
├── .eslintrc
├── .travis.yml
├── .editorconfig
├── .babelrc
├── test
├── sqlFormatterTest.js
├── Db2FormatterTest.js
├── N1qlFormatterTest.js
├── PlSqlFormatterTest.js
├── StandardSqlFormatterTest.js
└── behavesLikeSqlFormatter.js
├── src
├── core
│ ├── tokenTypes.js
│ ├── Params.js
│ ├── Indentation.js
│ ├── InlineBlock.js
│ ├── Formatter.js
│ └── Tokenizer.js
├── sqlFormatter.js
└── languages
│ ├── N1qlFormatter.js
│ ├── StandardSqlFormatter.js
│ ├── PlSqlFormatter.js
│ └── Db2Formatter.js
├── lib
├── core
│ ├── tokenTypes.js
│ ├── Params.js
│ ├── Indentation.js
│ ├── InlineBlock.js
│ ├── Formatter.js
│ └── Tokenizer.js
├── sqlFormatter.js
└── languages
│ ├── N1qlFormatter.js
│ ├── StandardSqlFormatter.js
│ ├── PlSqlFormatter.js
│ └── Db2Formatter.js
├── webpack.config.js
├── LICENSE
├── package.json
├── README.md
├── index.html
└── dist
└── sql-formatter.min.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lib
2 | /dist
3 | /coverage
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | coverage
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "zt/base",
3 | "env": {
4 | "jest": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: node
3 |
4 | install:
5 | - npm install
6 | - npm install coveralls
7 | script:
8 | - npm run check
9 | - npm run build
10 | after_script:
11 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | charset = utf-8
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 4
10 |
11 | [{package.json,.travis.yml}]
12 | indent_style = space
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["es2015", {"loose": true}]
4 | ],
5 | "plugins": [
6 | "add-module-exports",
7 | "transform-class-properties",
8 | "transform-function-bind",
9 | "transform-object-rest-spread",
10 | "transform-es3-member-expression-literals",
11 | "transform-es3-property-literals"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/test/sqlFormatterTest.js:
--------------------------------------------------------------------------------
1 | import sqlFormatter from "./../src/sqlFormatter";
2 |
3 | describe("sqlFormatter", function() {
4 | it("throws error when unsupported language parameter specified", function() {
5 | expect(() => {
6 | sqlFormatter.format("SELECT *", {language: "blah"});
7 | }).toThrow("Unsupported SQL dialect: blah");
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/core/tokenTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Constants for token types
3 | */
4 | export default {
5 | WHITESPACE: "whitespace",
6 | WORD: "word",
7 | STRING: "string",
8 | RESERVED: "reserved",
9 | RESERVED_TOPLEVEL: "reserved-toplevel",
10 | RESERVED_NEWLINE: "reserved-newline",
11 | OPERATOR: "operator",
12 | OPEN_PAREN: "open-paren",
13 | CLOSE_PAREN: "close-paren",
14 | LINE_COMMENT: "line-comment",
15 | BLOCK_COMMENT: "block-comment",
16 | NUMBER: "number",
17 | PLACEHOLDER: "placeholder"
18 | };
19 |
--------------------------------------------------------------------------------
/lib/core/tokenTypes.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | /**
5 | * Constants for token types
6 | */
7 | exports["default"] = {
8 | WHITESPACE: "whitespace",
9 | WORD: "word",
10 | STRING: "string",
11 | RESERVED: "reserved",
12 | RESERVED_TOPLEVEL: "reserved-toplevel",
13 | RESERVED_NEWLINE: "reserved-newline",
14 | OPERATOR: "operator",
15 | OPEN_PAREN: "open-paren",
16 | CLOSE_PAREN: "close-paren",
17 | LINE_COMMENT: "line-comment",
18 | BLOCK_COMMENT: "block-comment",
19 | NUMBER: "number",
20 | PLACEHOLDER: "placeholder"
21 | };
22 | module.exports = exports["default"];
--------------------------------------------------------------------------------
/src/core/Params.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Handles placeholder replacement with given params.
3 | */
4 | export default class Params {
5 | /**
6 | * @param {Object} params
7 | */
8 | constructor(params) {
9 | this.params = params;
10 | this.index = 0;
11 | }
12 |
13 | /**
14 | * Returns param value that matches given placeholder with param key.
15 | * @param {Object} token
16 | * @param {String} token.key Placeholder key
17 | * @param {String} token.value Placeholder value
18 | * @return {String} param or token.value when params are missing
19 | */
20 | get({key, value}) {
21 | if (!this.params) {
22 | return value;
23 | }
24 | if (key) {
25 | return this.params[key];
26 | }
27 | return this.params[this.index ++];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | const config = {
4 | module: {
5 | loaders: [{
6 | test: /\.js$/,
7 | loader: "babel-loader",
8 | exclude: /node_modules/
9 | }]
10 | },
11 | output: {
12 | library: "sqlFormatter",
13 | libraryTarget: "umd"
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurrenceOrderPlugin(),
17 | ]
18 | };
19 |
20 | if (process.env.NODE_ENV === "production") {
21 | config.plugins.push(
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | pure_getters: true,
25 | unsafe: true,
26 | unsafe_comps: true,
27 | warnings: false
28 | }
29 | })
30 | );
31 | }
32 |
33 | module.exports = config;
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-present ZeroTurnaround LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/core/Params.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 |
5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6 |
7 | /**
8 | * Handles placeholder replacement with given params.
9 | */
10 | var Params = function () {
11 | /**
12 | * @param {Object} params
13 | */
14 | function Params(params) {
15 | _classCallCheck(this, Params);
16 |
17 | this.params = params;
18 | this.index = 0;
19 | }
20 |
21 | /**
22 | * Returns param value that matches given placeholder with param key.
23 | * @param {Object} token
24 | * @param {String} token.key Placeholder key
25 | * @param {String} token.value Placeholder value
26 | * @return {String} param or token.value when params are missing
27 | */
28 |
29 |
30 | Params.prototype.get = function get(_ref) {
31 | var key = _ref.key,
32 | value = _ref.value;
33 |
34 | if (!this.params) {
35 | return value;
36 | }
37 | if (key) {
38 | return this.params[key];
39 | }
40 | return this.params[this.index++];
41 | };
42 |
43 | return Params;
44 | }();
45 |
46 | exports["default"] = Params;
47 | module.exports = exports["default"];
--------------------------------------------------------------------------------
/src/sqlFormatter.js:
--------------------------------------------------------------------------------
1 | import Db2Formatter from "./languages/Db2Formatter";
2 | import N1qlFormatter from "./languages/N1qlFormatter";
3 | import PlSqlFormatter from "./languages/PlSqlFormatter";
4 | import StandardSqlFormatter from "./languages/StandardSqlFormatter";
5 |
6 | export default {
7 | /**
8 | * Format whitespaces in a query to make it easier to read.
9 | *
10 | * @param {String} query
11 | * @param {Object} cfg
12 | * @param {String} cfg.language Query language, default is Standard SQL
13 | * @param {String} cfg.indent Characters used for indentation, default is " " (2 spaces)
14 | * @param {Object} cfg.params Collection of params for placeholder replacement
15 | * @return {String}
16 | */
17 | format: (query, cfg) => {
18 | cfg = cfg || {};
19 |
20 | switch (cfg.language) {
21 | case "db2":
22 | return new Db2Formatter(cfg).format(query);
23 | case "n1ql":
24 | return new N1qlFormatter(cfg).format(query);
25 | case "pl/sql":
26 | return new PlSqlFormatter(cfg).format(query);
27 | case "sql":
28 | case undefined:
29 | return new StandardSqlFormatter(cfg).format(query);
30 | default:
31 | throw Error(`Unsupported SQL dialect: ${cfg.language}`);
32 | }
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/lib/sqlFormatter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 |
5 | var _Db2Formatter = require("./languages/Db2Formatter");
6 |
7 | var _Db2Formatter2 = _interopRequireDefault(_Db2Formatter);
8 |
9 | var _N1qlFormatter = require("./languages/N1qlFormatter");
10 |
11 | var _N1qlFormatter2 = _interopRequireDefault(_N1qlFormatter);
12 |
13 | var _PlSqlFormatter = require("./languages/PlSqlFormatter");
14 |
15 | var _PlSqlFormatter2 = _interopRequireDefault(_PlSqlFormatter);
16 |
17 | var _StandardSqlFormatter = require("./languages/StandardSqlFormatter");
18 |
19 | var _StandardSqlFormatter2 = _interopRequireDefault(_StandardSqlFormatter);
20 |
21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
22 |
23 | exports["default"] = {
24 | /**
25 | * Format whitespaces in a query to make it easier to read.
26 | *
27 | * @param {String} query
28 | * @param {Object} cfg
29 | * @param {String} cfg.language Query language, default is Standard SQL
30 | * @param {String} cfg.indent Characters used for indentation, default is " " (2 spaces)
31 | * @param {Object} cfg.params Collection of params for placeholder replacement
32 | * @return {String}
33 | */
34 | format: function format(query, cfg) {
35 | cfg = cfg || {};
36 |
37 | switch (cfg.language) {
38 | case "db2":
39 | return new _Db2Formatter2["default"](cfg).format(query);
40 | case "n1ql":
41 | return new _N1qlFormatter2["default"](cfg).format(query);
42 | case "pl/sql":
43 | return new _PlSqlFormatter2["default"](cfg).format(query);
44 | case "sql":
45 | case undefined:
46 | return new _StandardSqlFormatter2["default"](cfg).format(query);
47 | default:
48 | throw Error("Unsupported SQL dialect: " + cfg.language);
49 | }
50 | }
51 | };
52 | module.exports = exports["default"];
--------------------------------------------------------------------------------
/src/core/Indentation.js:
--------------------------------------------------------------------------------
1 | import repeat from "lodash/repeat";
2 | import last from "lodash/last";
3 |
4 | const INDENT_TYPE_TOP_LEVEL = "top-level";
5 | const INDENT_TYPE_BLOCK_LEVEL = "block-level";
6 |
7 | /**
8 | * Manages indentation levels.
9 | *
10 | * There are two types of indentation levels:
11 | *
12 | * - BLOCK_LEVEL : increased by open-parenthesis
13 | * - TOP_LEVEL : increased by RESERVED_TOPLEVEL words
14 | */
15 | export default class Indentation {
16 | /**
17 | * @param {String} indent Indent value, default is " " (2 spaces)
18 | */
19 | constructor(indent) {
20 | this.indent = indent || " ";
21 | this.indentTypes = [];
22 | }
23 |
24 | /**
25 | * Returns current indentation string.
26 | * @return {String}
27 | */
28 | getIndent() {
29 | return repeat(this.indent, this.indentTypes.length);
30 | }
31 |
32 | /**
33 | * Increases indentation by one top-level indent.
34 | */
35 | increaseToplevel() {
36 | this.indentTypes.push(INDENT_TYPE_TOP_LEVEL);
37 | }
38 |
39 | /**
40 | * Increases indentation by one block-level indent.
41 | */
42 | increaseBlockLevel() {
43 | this.indentTypes.push(INDENT_TYPE_BLOCK_LEVEL);
44 | }
45 |
46 | /**
47 | * Decreases indentation by one top-level indent.
48 | * Does nothing when the previous indent is not top-level.
49 | */
50 | decreaseTopLevel() {
51 | if (last(this.indentTypes) === INDENT_TYPE_TOP_LEVEL) {
52 | this.indentTypes.pop();
53 | }
54 | }
55 |
56 | /**
57 | * Decreases indentation by one block-level indent.
58 | * If there are top-level indents within the block-level indent,
59 | * throws away these as well.
60 | */
61 | decreaseBlockLevel() {
62 | while (this.indentTypes.length > 0) {
63 | const type = this.indentTypes.pop();
64 | if (type !== INDENT_TYPE_TOP_LEVEL) {
65 | break;
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/test/Db2FormatterTest.js:
--------------------------------------------------------------------------------
1 | import sqlFormatter from "./../src/sqlFormatter";
2 | import behavesLikeSqlFormatter from "./behavesLikeSqlFormatter";
3 |
4 | describe("Db2Formatter", function() {
5 | behavesLikeSqlFormatter("db2");
6 |
7 | it("formats FETCH FIRST like LIMIT", function() {
8 | expect(sqlFormatter.format(
9 | "SELECT col1 FROM tbl ORDER BY col2 DESC FETCH FIRST 20 ROWS ONLY;",
10 | {language: "db2"}
11 | )).toBe(
12 | "SELECT\n" +
13 | " col1\n" +
14 | "FROM\n" +
15 | " tbl\n" +
16 | "ORDER BY\n" +
17 | " col2 DESC\n" +
18 | "FETCH FIRST\n" +
19 | " 20 ROWS ONLY;"
20 | );
21 | });
22 |
23 | it("formats only -- as a line comment", function() {
24 | const result = sqlFormatter.format(
25 | "SELECT col FROM\n" +
26 | "-- This is a comment\n" +
27 | "MyTable;\n",
28 | {language: "db2"}
29 | );
30 | expect(result).toBe(
31 | "SELECT\n" +
32 | " col\n" +
33 | "FROM\n" +
34 | " -- This is a comment\n" +
35 | " MyTable;"
36 | );
37 | });
38 |
39 | it("recognizes @ and # as part of identifiers", function() {
40 | const result = sqlFormatter.format(
41 | "SELECT col#1, @col2 FROM tbl\n",
42 | {language: "db2"}
43 | );
44 | expect(result).toBe(
45 | "SELECT\n" +
46 | " col#1,\n" +
47 | " @col2\n" +
48 | "FROM\n" +
49 | " tbl"
50 | );
51 | });
52 |
53 | it("recognizes :variables", function() {
54 | expect(sqlFormatter.format("SELECT :variable;", {language: "db2"})).toBe(
55 | "SELECT\n" +
56 | " :variable;"
57 | );
58 | });
59 |
60 | it("replaces :variables with param values", function() {
61 | const result = sqlFormatter.format(
62 | "SELECT :variable",
63 | {language: "db2", params: {"variable": "\"variable value\""}}
64 | );
65 | expect(result).toBe(
66 | "SELECT\n" +
67 | " \"variable value\""
68 | );
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sql-formatter",
3 | "version": "2.3.3",
4 | "description": "Formats whitespaces in a SQL query to make it more readable",
5 | "license": "MIT",
6 | "main": "lib/sqlFormatter.js",
7 | "keywords": [
8 | "sql",
9 | "formatter",
10 | "format",
11 | "n1ql",
12 | "whitespaces"
13 | ],
14 | "authors": [
15 | "Rene Saarsoo",
16 | "Uku Pattak"
17 | ],
18 | "files": [
19 | "dist",
20 | "lib",
21 | "src"
22 | ],
23 | "scripts": {
24 | "clean": "rimraf lib dist",
25 | "lint": "eslint .",
26 | "test": "jest",
27 | "test:watch": "npm run test -- --watch",
28 | "check": "npm run lint && npm run test",
29 | "prepare": "npm run build",
30 | "build": "npm run build:commonjs && npm run build:umd && npm run build:umd:min",
31 | "build:commonjs": "babel src --out-dir lib",
32 | "build:umd": "cross-env NODE_ENV=development webpack src/sqlFormatter.js dist/sql-formatter.js",
33 | "build:umd:min": "cross-env NODE_ENV=production webpack src/sqlFormatter.js dist/sql-formatter.min.js",
34 | "prepublishOnly": "npm run clean && npm run check && npm run build"
35 | },
36 | "repository": {
37 | "type": "git",
38 | "url": "https://github.com/zeroturnaround/sql-formatter.git"
39 | },
40 | "bugs": {
41 | "url": "https://github.com/zeroturnaround/sql-formatter/issues"
42 | },
43 | "dependencies": {
44 | "lodash": "^4.16.0"
45 | },
46 | "devDependencies": {
47 | "babel-cli": "^6.14.0",
48 | "babel-core": "^6.11.4",
49 | "babel-eslint": "^7.1.0",
50 | "babel-jest": "^17.0.2",
51 | "babel-loader": "^6.2.4",
52 | "babel-plugin-add-module-exports": "^0.2.1",
53 | "babel-plugin-transform-class-properties": "^6.11.5",
54 | "babel-plugin-transform-es3-member-expression-literals": "^6.5.0",
55 | "babel-plugin-transform-es3-property-literals": "^6.5.0",
56 | "babel-plugin-transform-function-bind": "^6.8.0",
57 | "babel-plugin-transform-object-rest-spread": "^6.8.0",
58 | "babel-preset-es2015": "^6.14.0",
59 | "cross-env": "^3.1.3",
60 | "eslint": "^3.7.1",
61 | "eslint-config-zt": "^1.3.0",
62 | "eslint-plugin-react": "^7.6.1",
63 | "jest": "^17.0.2",
64 | "rimraf": "^2.3.4",
65 | "webpack": "^1.13.1"
66 | },
67 | "jest": {
68 | "testPathDirs": [
69 | "test"
70 | ],
71 | "testRegex": ".*Test",
72 | "collectCoverage": true
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SQL Formatter [](https://npmjs.com/package/sql-formatter) [](https://travis-ci.org/zeroturnaround/sql-formatter) [](https://coveralls.io/github/zeroturnaround/sql-formatter?branch=master)
2 |
3 | **SQL Formatter** is a JavaScript library for pretty-printing SQL queries.
4 | It started as a port of a [PHP Library][], but has since considerably diverged.
5 | It supports [Standard SQL][], [Couchbase N1QL][], [IBM DB2][] and [Oracle PL/SQL][] dialects.
6 |
7 | → [Try the demo.](https://zeroturnaround.github.io/sql-formatter/)
8 |
9 | ## Install
10 |
11 | Get the latest version from NPM:
12 |
13 | ```
14 | npm install sql-formatter
15 | ```
16 |
17 | ## Usage
18 |
19 | ```js
20 | import sqlFormatter from "sql-formatter";
21 |
22 | console.log(sqlFormatter.format("SELECT * FROM table1"));
23 | ```
24 |
25 | This will output:
26 |
27 | ```
28 | SELECT
29 | *
30 | FROM
31 | table1
32 | ```
33 |
34 | You can also pass in configuration options:
35 |
36 | ```js
37 | sqlFormatter.format("SELECT *", {
38 | language: "n1ql", // Defaults to "sql"
39 | indent: " " // Defaults to two spaces
40 | });
41 | ```
42 |
43 | Currently just four SQL dialects are supported:
44 |
45 | - **sql** - [Standard SQL][]
46 | - **n1ql** - [Couchbase N1QL][]
47 | - **db2** - [IBM DB2][]
48 | - **pl/sql** - [Oracle PL/SQL][]
49 |
50 | ### Placeholders replacement
51 |
52 | ```js
53 | // Named placeholders
54 | sqlFormatter.format("SELECT * FROM tbl WHERE foo = @foo", {
55 | params: {foo: "'bar'"}
56 | }));
57 |
58 | // Indexed placeholders
59 | sqlFormatter.format("SELECT * FROM tbl WHERE foo = ?", {
60 | params: ["'bar'"]
61 | }));
62 | ```
63 |
64 | Both result in:
65 |
66 | ```
67 | SELECT
68 | *
69 | FROM
70 | tbl
71 | WHERE
72 | foo = 'bar'
73 | ```
74 |
75 | ## Usage without NPM
76 |
77 | If you don't use a module bundler, clone the repository, run `npm install` and grab a file from `/dist` directory to use inside a `
113 |
132 |