UNPKG

24.2 kBPlain TextView Raw
1import { Include } from './include';
2import { Erro, Severity } from './models/Erro';
3import { Fonte, Tipos } from './fonte';
4import { version } from './package.json';
5
6export class ValidaAdvpl {
7 public comentFontPad: string[];
8 public error: number;
9 public warning: number;
10 public information: number;
11 public hint: number;
12 public includes: any[];
13 public aErros: Erro[];
14 public ownerDb: string[];
15 public empresas: string[];
16 public fonte: Fonte;
17 public version: string;
18 public conteudoFonte: string;
19 private local;
20 constructor(
21 comentFontePad: string[],
22 local: string,
23 private log: boolean = true
24 ) {
25 this.local = local;
26 this.aErros = [];
27 this.includes = [];
28 this.error = 0;
29 this.warning = 0;
30 this.information = 0;
31 this.hint = 0;
32 //Se não está preenchido seta com valor padrão
33 this.comentFontPad = comentFontePad;
34 this.ownerDb = [];
35 this.empresas = [];
36 this.version = version;
37 }
38
39 public validacao(texto: string, path: string): Promise<ValidaAdvpl> {
40 return new Promise((resolve: Function, reject: Function) => {
41 try {
42 let objeto: ValidaAdvpl = this;
43
44 objeto.conteudoFonte = texto;
45 objeto.aErros = [];
46 objeto.includes = [];
47 objeto.fonte = new Fonte(path);
48 let conteudoSComentario: string = '';
49 let linhas: String[] = texto.split('\n');
50 //Pega as linhas do documento ativo e separa o array por linha
51
52 let comentFuncoes: any[] = new Array();
53 let funcoes: any[] = new Array();
54 let cBeginSql: boolean = false;
55 let FromQuery: boolean = false;
56 let JoinQuery: boolean = false;
57 let cSelect: boolean = false;
58 let ProtheusDoc: boolean = false;
59 let emComentario: boolean = false;
60 //Percorre todas as linhas
61 for (var key in linhas) {
62 //seta linha atual em caixa alta
63 let linha: String = linhas[key].toLocaleUpperCase();
64 let linhaClean: String = '';
65 //se estiver no PotheusDoc vê se está fechando
66 if (ProtheusDoc && linha.search('\\/\\*\\/') !== -1) {
67 ProtheusDoc = false;
68 }
69 //verifica se é protheusDoc
70 if (linha.search(/\/\*\/+( |)+\{PROTHEUS\.DOC\}/) !== -1) {
71 ProtheusDoc = true;
72 //reseta todas as ariáveis de controle pois se entrou em ProtheusDoc está fora de qualquer função
73 cBeginSql = false;
74 FromQuery = false;
75 JoinQuery = false;
76 cSelect = false;
77 //verifica se é um comentário de função e adiciona no array
78 comentFuncoes.push([
79 linha
80 .trim()
81 .replace(/\/\*\/+( |)+\{PROTHEUS\.DOC\}/, '')
82 .trim()
83 .toLocaleUpperCase(),
84 key
85 ]);
86 }
87
88 //verifica se a linha está toda comentada
89 let posComentLinha: number = linha.search(/\/\//);
90 let posComentBloco: number = linha.search(/\/\*/);
91 posComentBloco = posComentBloco === -1 ? 999999 : posComentBloco;
92 posComentLinha = posComentLinha === -1 ? 999999 : posComentLinha;
93 if (!emComentario && posComentLinha < posComentBloco) {
94 linha = linha.split('//')[0];
95 }
96
97 //Verifica se está em comentário de bloco
98 //trata comentários dentro da linha
99 linha = linha.replace(/\/\*+.+\*\//, '');
100 if (linha.search(/\*\//) !== -1 && emComentario) {
101 emComentario = false;
102 linha = linha.split(/\*\//)[1];
103 }
104
105 //se não estiver dentro do Protheus DOC valida linha
106 if (!emComentario) {
107 if (
108 linha
109 .replace(/\"+.+\"/, '')
110 .replace(/\'+.+\'/, '')
111 .search(/\/\*/) !== -1
112 ) {
113 emComentario = true;
114 linha = linha.split(/\/\*/)[0];
115 }
116
117 //Se não estiver em comentário verifica se o último caracter da linha é ;
118 if (!emComentario && linha.charAt(linha.length - 1) === ';') {
119 linhas[parseInt(key) + 1] =
120 linha + ' ' + linhas[parseInt(key) + 1];
121 linha = '';
122 }
123
124 //trata comentários em linha ou strings em aspas simples ou duplas
125 //não remove aspas quando for include
126 linha = linha.split('//')[0];
127 linhaClean = linha;
128 if (linha.search(/#INCLUDE/) === -1) {
129 while (
130 linhaClean.search(/\"+.+\"/) !== -1 ||
131 linhaClean.search(/\'+.+\'/) !== -1
132 ) {
133 let colunaDupla: number = linhaClean.search(/\"+.+\"/);
134 let colunaSimples: number = linhaClean.search(/\'+.+\'/);
135 //se a primeira for a dupla
136 if (
137 colunaDupla !== -1 &&
138 (colunaDupla < colunaSimples || colunaSimples === -1)
139 ) {
140 let quebra: string[] = linhaClean.split('"');
141 linhaClean = linhaClean.replace('"' + quebra[1] + '"', '');
142 } else {
143 let quebra: string[] = linhaClean.split("'");
144 linhaClean = linhaClean.replace("'" + quebra[1] + "'", '');
145 }
146 }
147 }
148
149 //Remove espaços ou tabulações seguidas
150 linhaClean = linhaClean.replace(/\t/g, ' ');
151 linhaClean = linhaClean.replace(/\:\=/g, ' :=');
152 let conteudos: string[] = linhaClean.split(' ');
153 linhaClean = '';
154 for (const key in conteudos) {
155 if (conteudos[key]) {
156 linhaClean += conteudos[key] + ' ';
157 }
158 }
159
160 conteudoSComentario = conteudoSComentario + linhaClean + '\n';
161 let firstWord: string = linhaClean.split(' ')[0].split('\t')[0];
162
163 //verifica se é função e adiciona no array
164 if (
165 linhaClean.search(/(STATIC|USER|)+(\ |\t)+FUNCTION+(\ |\t)/) !==
166 -1 &&
167 linhaClean
168 .trim()
169 .split(' ')[0]
170 .match(/STATIC|USER|FUNCTION/)
171 ) {
172 //reseta todas as ariáveis de controle pois está fora de qualquer função
173 cBeginSql = false;
174 FromQuery = false;
175 JoinQuery = false;
176 cSelect = false;
177 let nomeFuncao: string = linhaClean
178 .replace('\t', ' ')
179 .trim()
180 .split(' ')[2]
181 .split('(')[0];
182 //verifica se é um função e adiciona no array
183 funcoes.push([nomeFuncao, key]);
184 //verifica o TIPO
185 if (linhaClean.search(/(USER)+(\ |\t)+FUNCTION+(\ |\t)/) !== -1) {
186 objeto.fonte.addFunction(
187 Tipos['User Function'],
188 nomeFuncao,
189 parseInt(key)
190 );
191 } else if (
192 linhaClean.search(/(STATIC)+(\ |\t)+FUNCTION+(\ |\t)/) !== -1
193 ) {
194 //verifica se a primeira palavra é FUNCTION
195 objeto.fonte.addFunction(
196 Tipos['Static Function'],
197 nomeFuncao,
198 parseInt(key)
199 );
200 } else if (firstWord === 'FUNCTION') {
201 //verifica se a primeira palavra é FUNCTION
202 objeto.fonte.addFunction(
203 Tipos.Function,
204 nomeFuncao,
205 parseInt(key)
206 );
207 }
208 }
209 //Verifica se é CLASSE ou WEBSERVICE
210 if (
211 linhaClean.search('METHOD\\ .*?CLASS') !== -1 ||
212 firstWord === 'CLASS' ||
213 linhaClean.search('WSMETHOD.*?WSSERVICE') !== -1 ||
214 linhaClean.search('WSSERVICE\\ ') !== -1
215 ) {
216 //reseta todas as ariáveis de controle pois está fora de qualquer função
217 cBeginSql = false;
218 FromQuery = false;
219 JoinQuery = false;
220 cSelect = false;
221 //verifica se é um função e adiciona no array
222 funcoes.push([
223 linhaClean
224 .trim()
225 .split(' ')[1]
226 .split('(')[0],
227 key
228 ]);
229 if (firstWord === 'CLASS') {
230 objeto.fonte.addFunction(
231 Tipos.Class,
232 linhaClean
233 .trim()
234 .split(' ')[1]
235 .split('(')[0],
236 parseInt(key)
237 );
238 }
239 if (firstWord.match(/METHOD/)) {
240 let palavras: string[] = linhaClean.split(/,| |\t|\(/);
241 let metodo: string = palavras[1];
242 let classe: string;
243 for (var i = 0; i < palavras.length; i++) {
244 let key2 = palavras[i];
245 if (key2 === 'WSSERVICE' || key2 === 'CLASS') {
246 classe = palavras[i + 1];
247 break;
248 }
249 }
250
251 objeto.fonte.addFunction(
252 Tipos.Method,
253 classe + '|' + metodo,
254 parseInt(key)
255 );
256 }
257 }
258 //Adiciona no objeto as variáveis locais
259 if (firstWord === 'LOCAL') {
260 //remove o LOCAL
261 let variaveis: string[] = linhaClean.split(/,| |\t|\r/);
262 for (var key2 of variaveis) {
263 if (key2 !== 'LOCAL' && key2 !== '') {
264 // se terminar as variáveis
265 if (key2.match(/\:\=/)) {
266 break;
267 }
268 //objeto.fonte.addVariavel(key2);
269 }
270 }
271 }
272
273 //Verifica se adicionou o include TOTVS.CH
274 if (linha.search(/#INCLUDE/) !== -1) {
275 //REMOVE as aspas a palavra #include e os espacos e tabulações
276 objeto.includes.push({
277 include: linha
278 .replace(/#INCLUDE/g, '')
279 .replace(/\t/g, '')
280 .replace(/\'/g, '')
281 .replace(/\"/g, '')
282 .trim(),
283 linha: parseInt(key)
284 });
285 }
286 if (linhaClean.search(/BEGINSQL+(\ |\t)+ALIAS/) !== -1) {
287 cBeginSql = true;
288 }
289 if (
290 linha.match(/(\ |\t|\'|\"|)+SELECT+(\ |\t)/) ||
291 linha.match(/(\ |\t|\'|\"|)+DELETE+(\ |\t)/) ||
292 linha.match(/(\ |\t|\'|\"|)+UPDATE+(\ |\t)/)
293 ) {
294 cSelect = true;
295 }
296 if (
297 !cBeginSql &&
298 (linha.search(
299 /(\ |\t|\'|\"|)+DBUSEAREA+(\ |\t|)+\(+.+TOPCONN+.+TCGENQRY/
300 ) !== -1 ||
301 linhaClean.search(/TCQUERY+(\ |\t)/) !== -1)
302 ) {
303 objeto.aErros.push(
304 new Erro(
305 parseInt(key),
306 parseInt(key),
307 traduz('validaAdvpl.queryNoEmbedded', objeto.local),
308 Severity.Warning
309 )
310 );
311 FromQuery = false;
312 cSelect = false;
313 }
314 if (
315 linha.search(/(\ |\t|\'|\")+DELETE+(\ |\t)+FROM+(\ |\t)/) !== -1
316 ) {
317 objeto.aErros.push(
318 new Erro(
319 parseInt(key),
320 parseInt(key),
321 traduz('validaAdvpl.deleteFrom', objeto.local),
322 Severity.Warning
323 )
324 );
325 }
326 if (linhaClean.search(/MSGBOX\(/) !== -1) {
327 objeto.aErros.push(
328 new Erro(
329 parseInt(key),
330 parseInt(key),
331 traduz('validaAdvpl.msgBox', objeto.local),
332 Severity.Information
333 )
334 );
335 }
336 if (
337 linha.search(
338 /GETMV\(+(\ |\t|)+(\"|\')+MV_FOLMES+(\"|\')+(\ |\t|)\)/
339 ) !== -1
340 ) {
341 objeto.aErros.push(
342 new Erro(
343 parseInt(key),
344 parseInt(key),
345 traduz('validaAdvpl.folMes', objeto.local),
346 Severity.Information
347 )
348 );
349 }
350 if (linha.search('\\<\\<\\<\\<\\<\\<\\<\\ HEAD') !== -1) {
351 //Verifica linha onde terminou o conflito
352 let nFim: string = key;
353 for (var key2 in linhas) {
354 if (
355 linhas[key2].search('\\>\\>\\>\\>\\>\\>\\>') !== -1 &&
356 nFim === key &&
357 key2 > key
358 ) {
359 nFim = key2;
360 }
361 }
362 objeto.aErros.push(
363 new Erro(
364 parseInt(key),
365 parseInt(nFim),
366 traduz('validaAdvpl.conflictMerge', objeto.local),
367 Severity.Error
368 )
369 );
370 }
371 if (
372 linha.search(/(\ |\t|\'|\"|)+SELECT+(\ |\t)/) !== -1 &&
373 linha.search('\\ \\*\\ ') !== -1
374 ) {
375 objeto.aErros.push(
376 new Erro(
377 parseInt(key),
378 parseInt(key),
379 traduz('validaAdvpl.selectAll', objeto.local),
380 Severity.Warning
381 )
382 );
383 }
384 if (
385 linha.search('CHR\\(13\\)') !== -1 &&
386 linha.search('CHR\\(10\\)') !== -1
387 ) {
388 objeto.aErros.push(
389 new Erro(
390 parseInt(key),
391 parseInt(key),
392 traduz('validaAdvpl.crlf', objeto.local),
393 Severity.Warning
394 )
395 );
396 }
397 if (cSelect && linha.search('FROM') !== -1) {
398 FromQuery = true;
399 }
400 if (cSelect && FromQuery && linha.search('JOIN') !== -1) {
401 JoinQuery = true;
402 }
403 if (
404 linha.search('ENDSQL') !== -1 ||
405 linha.search('WHERE') !== -1 ||
406 linha.search('TCQUERY') !== -1
407 ) {
408 FromQuery = false;
409 cSelect = false;
410 }
411 //Implementação para aceitar vários bancos de dados
412 objeto.ownerDb.forEach(banco => {
413 if (cSelect && FromQuery && linha.search(banco) !== -1) {
414 objeto.aErros.push(
415 new Erro(
416 parseInt(key),
417 parseInt(key),
418 traduz('validaAdvpl.noSchema', objeto.local) +
419 banco +
420 traduz('validaAdvpl.inQuery', objeto.local),
421 Severity.Error
422 )
423 );
424 }
425 });
426 if (
427 cSelect &&
428 (FromQuery || JoinQuery || linha.search('SET') !== -1) &&
429 linha.search('exp:cTable') === -1
430 ) {
431 //procura códigos de empresas nas queryes
432 objeto.empresas.forEach(empresa => {
433 //para melhorar a análise vou quebrar a string por espaços
434 //e removendo as quebras de linhas, vou varrer os itens do array e verificar o tamanho
435 //e o código da empresa chumbado
436 let palavras: string[] = linha
437 .replace(/\r/g, '')
438 .replace(/\t/g, '')
439 .split(' ');
440 palavras.forEach(palavra => {
441 if (
442 palavra.search(empresa + '0') !== -1 &&
443 palavra.length === 6
444 ) {
445 objeto.aErros.push(
446 new Erro(
447 parseInt(key),
448 parseInt(key),
449 traduz('validaAdvpl.tableFixed', objeto.local),
450 Severity.Error
451 )
452 );
453 }
454 });
455 });
456 }
457 if (cSelect && JoinQuery && linha.search('ON') !== -1) {
458 JoinQuery = false;
459 }
460 if (linhaClean.search(/CONOUT\(/) !== -1) {
461 objeto.aErros.push(
462 new Erro(
463 parseInt(key),
464 parseInt(key),
465 traduz('validaAdvpl.conout', objeto.local),
466 Severity.Warning
467 )
468 );
469 }
470 // PUTSX1
471 if (linhaClean.search(/PUTSX1\(/) !== -1) {
472 objeto.aErros.push(
473 new Erro(
474 parseInt(key),
475 parseInt(key),
476 traduz('validaAdvpl.PutSX1', objeto.local),
477 Severity.Error
478 )
479 );
480 }
481 // Uso de Dicionários Fora do BeginSql
482 let posicaoDic: number = (' ' + linhaClean).search(
483 /(,| |\t|\>|\()+X+(1|2|3|5|6|7|9|A|B|D|G)+\_/gim
484 );
485 if (
486 !cBeginSql &&
487 posicaoDic !== -1 &&
488 (' ' + linhaClean).substring(posicaoDic + 1).split(" ")[0].split("\t")[0].search(/\(/) === -1
489 ) {
490 objeto.aErros.push(
491 new Erro(
492 parseInt(key),
493 parseInt(key),
494 traduz('validaAdvpl.Dictionary', objeto.local),
495 Severity.Error
496 )
497 );
498 }
499 if (
500 linhaClean.search(
501 /(,| |\t||\()+(MSFILE|MSFILE|DBCREATE|DBUSEAREA|CRIATRAB)+( \(|\t\(|\()/gim
502 ) !== -1 ||
503 linhaClean.search(
504 /( |)+(MSCOPYFILE|MSERASE|COPY TO)+( |\t)/gim
505 ) !== -1
506 ) {
507 objeto.aErros.push(
508 new Erro(
509 parseInt(key),
510 parseInt(key),
511 traduz('validaAdvpl.Isam', objeto.local),
512 Severity.Error
513 )
514 );
515 }
516 //recomendação para melhorar identificação de problemas em queryes
517 if (
518 (linha.match(/(\ |\t|)+SELECT+(\ |\t)/) ||
519 linha.match(/(\ |\t|)+DELETE+(\ |\t)/) ||
520 linha.match(/(\ |\t|)+UPDATE+(\ |\t)/) ||
521 linha.match(/(\ |\t|)+JOIN+(\ |\t)/)) &&
522 (linha.match(/(\ |\t|)+FROM+(\ |\t)/) ||
523 linha.match(/(\ |\t|)+ON+(\ |\t)/) ||
524 linha.match(/(\ |\t|)+WHERE+(\ |\t)/)) &&
525 linha.search(/(\ |\t)+TCSQLEXEC+\(/) === -1
526 ) {
527 //verifica o caracter anterior tem que ser ou ESPACO ou ' ou " ou nada
528 let itens1: string[] = ['FROM', 'ON', 'WHERE'];
529 let addErro: boolean = false;
530 itens1.forEach(item => {
531 addErro = addErro || linha.search("\\'" + item) !== -1;
532 addErro = addErro || linha.search('\\"' + item) !== -1;
533 addErro = addErro || linha.search('\\ ' + item) !== -1;
534 });
535
536 if (addErro) {
537 objeto.aErros.push(
538 new Erro(
539 parseInt(key),
540 parseInt(key),
541 traduz('validaAdvpl.bestAnalitc', objeto.local) +
542 ' SELECT, DELETE, UPDATE, JOIN, FROM, ON, WHERE.',
543 Severity.Information
544 )
545 );
546 }
547 }
548 } else {
549 conteudoSComentario += '\n';
550 }
551 }
552
553 //Validação de padrão de comentáris de fontes
554 let comentariosFonte: boolean = true;
555 for (var _i = 0; _i < objeto.comentFontPad.length; _i++) {
556 let cExpressao: string = objeto.comentFontPad[_i] as string;
557 let linha: string = linhas[_i] as string;
558 comentariosFonte =
559 comentariosFonte && linha.search(cExpressao) !== -1;
560 }
561
562 if (!comentariosFonte) {
563 objeto.aErros.push(
564 new Erro(
565 0,
566 0,
567 traduz('validaAdvpl.padComment', objeto.local),
568 Severity.Information
569 )
570 );
571 }
572
573 //Validação funções sem comentários
574 funcoes.forEach(funcao => {
575 let achou: boolean = false;
576 comentFuncoes.forEach(comentario => {
577 achou = achou || comentario[0] === funcao[0];
578 });
579
580 if (!achou) {
581 objeto.aErros.push(
582 new Erro(
583 parseInt(funcao[1]),
584 parseInt(funcao[1]),
585 traduz('validaAdvpl.functionNoCommented', objeto.local),
586 Severity.Warning
587 )
588 );
589 }
590 });
591 //Validação comentários sem funções
592 comentFuncoes.forEach(comentario => {
593 let achou: boolean = false;
594 funcoes.forEach(funcao => {
595 achou = achou || comentario[0] === funcao[0];
596 });
597
598 if (!achou) {
599 objeto.aErros.push(
600 new Erro(
601 parseInt(comentario[1]),
602 parseInt(comentario[1]),
603 traduz('validaAdvpl.CommentNoFunction', objeto.local),
604 Severity.Warning
605 )
606 );
607 }
608 });
609
610 //Validador de includes
611 let oInclude: Include = new Include(objeto.local);
612 oInclude.valida(objeto, conteudoSComentario);
613 //Conta os erros por tipo e totaliza no objeto
614 objeto.hint = 0;
615 objeto.information = 0;
616 objeto.warning = 0;
617 objeto.error = 0;
618 objeto.aErros.forEach((erro: any) => {
619 if (erro.severity === Severity.Hint) {
620 objeto.hint++;
621 }
622 if (erro.severity === Severity.Information) {
623 objeto.information++;
624 }
625 if (erro.severity === Severity.Warning) {
626 objeto.warning++;
627 }
628 if (erro.severity === Severity.Error) {
629 objeto.error++;
630 }
631 });
632 if (
633 objeto.error + objeto.hint + objeto.warning + objeto.information >
634 0 &&
635 this.log
636 ) {
637 if (objeto.error > 0) {
638 console.log(`\t\t${objeto.error} Errors .`);
639 }
640 if (objeto.warning > 0) {
641 console.log(`\t\t${objeto.warning} Warnings .`);
642 }
643 if (objeto.information > 0) {
644 console.log(`\t\t${objeto.information} Informations .`);
645 }
646 if (objeto.hint > 0) {
647 console.log(`\t\t${objeto.hint} Hints .`);
648 }
649 }
650 resolve(objeto);
651 } catch (e) {
652 reject(e);
653 }
654 });
655 }
656}
657
658function traduz(key, local) {
659 let locales: string[] = ['en', 'pt-br'];
660 let i18n = require('i18n');
661 i18n.configure({
662 locales: locales,
663 directory: __dirname + '/locales'
664 });
665 i18n.setLocale(locales.indexOf(local) + 1 ? local : 'en');
666 return i18n.__(key);
667}