1 | 'use strict';
|
2 |
|
3 | const errorLib = require('./error');
|
4 | const utils = require('./utils');
|
5 |
|
6 | const PEC = errorLib.parsingErrorCode;
|
7 |
|
8 |
|
9 | const compressors = '.,;:()[]=<>+-*/|!?@#';
|
10 |
|
11 |
|
12 |
|
13 | function minify(sql, options) {
|
14 |
|
15 | if (typeof sql !== 'string') {
|
16 | throw new TypeError('Input SQL must be a text string.');
|
17 | }
|
18 |
|
19 | if (!sql.length) {
|
20 | return '';
|
21 | }
|
22 |
|
23 | options = options || {};
|
24 |
|
25 | let idx = 0,
|
26 | result = '',
|
27 | space = false;
|
28 |
|
29 | const len = sql.length,
|
30 | EOL = utils.getEOL(sql);
|
31 |
|
32 | do {
|
33 | const s = sql[idx],
|
34 | s1 = sql[idx + 1];
|
35 |
|
36 | if (isGap(s)) {
|
37 | while (++idx < len && isGap(sql[idx]));
|
38 | if (idx < len) {
|
39 | space = true;
|
40 | }
|
41 | idx--;
|
42 | continue;
|
43 | }
|
44 |
|
45 | if (s === '-' && s1 === '-') {
|
46 | const lb = sql.indexOf(EOL, idx + 2);
|
47 | if (lb < 0) {
|
48 | break;
|
49 | }
|
50 | idx = lb - 1;
|
51 | skipGaps();
|
52 | continue;
|
53 | }
|
54 |
|
55 | if (s === '/' && s1 === '*') {
|
56 | let c = idx + 1, open = 0, close = 0, lastOpen, lastClose;
|
57 | while (++c < len - 1 && close <= open) {
|
58 | if (sql[c] === '/' && sql[c + 1] === '*') {
|
59 | lastOpen = c;
|
60 | open++;
|
61 | c++;
|
62 | } else {
|
63 | if (sql[c] === '*' && sql[c + 1] === '/') {
|
64 | lastClose = c;
|
65 | close++;
|
66 | c++;
|
67 | }
|
68 | }
|
69 | }
|
70 | if (close <= open) {
|
71 | idx = lastOpen;
|
72 | throwError(PEC.unclosedMLC);
|
73 | }
|
74 | if (sql[idx + 2] === '!' && !options.removeAll) {
|
75 | if (options.compress) {
|
76 | space = false;
|
77 | }
|
78 | addSpace();
|
79 | result += sql.substring(idx, lastClose + 2);
|
80 | }
|
81 | idx = lastClose + 1;
|
82 | skipGaps();
|
83 | continue;
|
84 | }
|
85 |
|
86 | let closeIdx, text;
|
87 |
|
88 | if (s === '"') {
|
89 | closeIdx = sql.indexOf('"', idx + 1);
|
90 | if (closeIdx < 0) {
|
91 | throwError(PEC.unclosedQI);
|
92 | }
|
93 | text = sql.substring(idx, closeIdx + 1);
|
94 | if (text.indexOf(EOL) > 0) {
|
95 | throwError(PEC.multiLineQI);
|
96 | }
|
97 | if (options.compress) {
|
98 | space = false;
|
99 | }
|
100 | addSpace();
|
101 | result += text;
|
102 | idx = closeIdx;
|
103 | skipGaps();
|
104 | continue;
|
105 | }
|
106 |
|
107 | if (s === '\'') {
|
108 | closeIdx = idx;
|
109 | do {
|
110 | closeIdx = sql.indexOf('\'', closeIdx + 1);
|
111 | if (closeIdx > 0) {
|
112 | let i = closeIdx;
|
113 | while (sql[--i] === '\\');
|
114 | if ((closeIdx - i) % 2) {
|
115 | let step = closeIdx;
|
116 | while (++step < len && sql[step] === '\'');
|
117 | if ((step - closeIdx) % 2) {
|
118 | closeIdx = step - 1;
|
119 | break;
|
120 | }
|
121 | closeIdx = step === len ? -1 : step;
|
122 | }
|
123 | }
|
124 | } while (closeIdx > 0);
|
125 | if (closeIdx < 0) {
|
126 | throwError(PEC.unclosedText);
|
127 | }
|
128 | if (options.compress) {
|
129 | space = false;
|
130 | }
|
131 | addSpace();
|
132 | text = sql.substring(idx, closeIdx + 1);
|
133 | const hasLB = text.indexOf(EOL) > 0;
|
134 | if (hasLB) {
|
135 | text = text.split(EOL).map(m => {
|
136 | return m.replace(/^\s+|\s+$/g, '');
|
137 | }).join('\\n');
|
138 | }
|
139 | const hasTabs = text.indexOf('\t') > 0;
|
140 | if (hasLB || hasTabs) {
|
141 | const prev = idx ? sql[idx - 1] : '';
|
142 | if (prev !== 'E' && prev !== 'e') {
|
143 | const r = result ? result[result.length - 1] : '';
|
144 | if (r && r !== ' ' && compressors.indexOf(r) < 0) {
|
145 | result += ' ';
|
146 | }
|
147 | result += 'E';
|
148 | }
|
149 | if (hasTabs) {
|
150 | text = text.replace(/\t/g, '\\t');
|
151 | }
|
152 | }
|
153 | result += text;
|
154 | idx = closeIdx;
|
155 | skipGaps();
|
156 | continue;
|
157 | }
|
158 |
|
159 | if (options.compress && compressors.indexOf(s) >= 0) {
|
160 | space = false;
|
161 | skipGaps();
|
162 | }
|
163 |
|
164 | addSpace();
|
165 | result += s;
|
166 |
|
167 | } while (++idx < len);
|
168 |
|
169 | return result;
|
170 |
|
171 | function skipGaps() {
|
172 | if (options.compress) {
|
173 | while (idx < len - 1 && isGap(sql[idx + 1]) && idx++);
|
174 | }
|
175 | }
|
176 |
|
177 | function addSpace() {
|
178 | if (space) {
|
179 | if (result.length) {
|
180 | result += ' ';
|
181 | }
|
182 | space = false;
|
183 | }
|
184 | }
|
185 |
|
186 | function throwError(code) {
|
187 | const position = utils.getIndexPos(sql, idx, EOL);
|
188 | throw new errorLib.SQLParsingError(code, position);
|
189 | }
|
190 | }
|
191 |
|
192 |
|
193 |
|
194 | function isGap(s) {
|
195 | return s === ' ' || s === '\t' || s === '\r' || s === '\n';
|
196 | }
|
197 |
|
198 | module.exports = minify;
|