prettier-plugin-bq 1.0.0-beta.1 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # prettier-plugin-bq
2
+
3
+ `prettier-plugin-bq` is a [prettier](https://prettier.io/) plugin for **GoogleSQL**, which is a dialect of BigQuery.
4
+
5
+ ## Features
6
+
7
+ - support [pipe syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/pipe-syntax)
8
+ - support [procedural language](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language) (a.k.a BigQuery Scripting)
9
+ - try to handle jinja templates (though not perfect)
10
+
11
+ ## Install
12
+
13
+ ```
14
+ npm install --save-dev prettier prettier-plugin-bq
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ You can format `.sql` and `.bq` file by the following command.
20
+
21
+ ```
22
+ npx prettier --write ./xxx.sql --plugin=prettier-plugin-bq
23
+ ```
24
+
25
+ If you want to omit `--plugin=prettier-plugin-bq`, add the plugin to your `.prettierrc`.
26
+
27
+ ```jsonc
28
+ // .prettierrc
29
+ {
30
+ "plugins": ["prettier-plugin-bq"]
31
+ }
32
+ ```
33
+
34
+ For more information, please read the prettier document.
35
+
36
+ ## Configuration
37
+
38
+ `prettier-plugin-bq` supports the options below.
39
+
40
+ | API Option | CLI Option | Default | Description |
41
+ | ------------------------ | ---------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
42
+ | indentCte | indent-cte | true | Indent CTEs in with clause. |
43
+ | printBlankLineAfterCte | print-blank-line-after-cte | false | Print blank line after CTE in with clause. |
44
+ | printKeywordsInUpperCase | print-keywords-in-upper-case | true | Print keywords, built-in functions and pseudo columns (e.g. `_PARTITIONDATE`, `_PARTITIONTIME`) in upper case. |
45
+
46
+ > [!NOTE]
47
+ >
48
+ > `printPseudoColumnsInUpperCase` was merged into printKeywordsInUpperCase.
49
+
50
+ ## Coding style
51
+
52
+ This plugin doesn't follow any famous style guides,
53
+ because none of them account for GoogleSQL's latest syntax (such as pipe syntax).
54
+
55
+ ## Feedback
56
+
57
+ I'm not ready to accept pull requests, but your feedback is always welcome.
58
+ If you find any bugs, please feel free to create an issue.
package/dist/index.d.ts CHANGED
@@ -1 +1,51 @@
1
- export {};
1
+ declare const languages: {
2
+ extensions: string[];
3
+ name: string;
4
+ parsers: string[];
5
+ }[];
6
+ declare const parsers: {
7
+ "sql-parse": {
8
+ parse: (text: string) => import("bq2cst").UnknownNode[];
9
+ astFormat: string;
10
+ };
11
+ };
12
+ declare const printers: {
13
+ "sql-ast": {
14
+ print: (path: import("prettier").AstPath, options: {
15
+ [x: string]: unknown;
16
+ }, print: (path: import("prettier").AstPath) => import("prettier").Doc) => import("prettier").Doc;
17
+ };
18
+ };
19
+ declare const options: {
20
+ formatMultilineComment: {
21
+ type: string;
22
+ category: string;
23
+ default: boolean;
24
+ description: string;
25
+ };
26
+ indentCte: {
27
+ type: string;
28
+ category: string;
29
+ default: boolean;
30
+ description: string;
31
+ };
32
+ printBlankLineAfterCte: {
33
+ type: string;
34
+ category: string;
35
+ default: boolean;
36
+ description: string;
37
+ };
38
+ printKeywordsInUpperCase: {
39
+ type: string;
40
+ category: string;
41
+ default: boolean;
42
+ description: string;
43
+ };
44
+ printPseudoColumnsInUpperCase: {
45
+ type: string;
46
+ category: string;
47
+ default: boolean;
48
+ description: string;
49
+ };
50
+ };
51
+ export { languages, parsers, printers, options };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.options = exports.printers = exports.parsers = exports.languages = void 0;
3
4
  const bq2cst_1 = require("bq2cst");
4
5
  const printer_1 = require("./printer");
5
6
  const languages = [
@@ -9,17 +10,20 @@ const languages = [
9
10
  parsers: ["sql-parse"],
10
11
  },
11
12
  ];
13
+ exports.languages = languages;
12
14
  const parsers = {
13
15
  "sql-parse": {
14
16
  parse: (text) => (0, bq2cst_1.parse)(text),
15
17
  astFormat: "sql-ast",
16
18
  },
17
19
  };
20
+ exports.parsers = parsers;
18
21
  const printers = {
19
22
  "sql-ast": {
20
23
  print: printer_1.printSQL,
21
24
  },
22
25
  };
26
+ exports.printers = printers;
23
27
  const CATEGORY_BQ = "BQ";
24
28
  const options = {
25
29
  formatMultilineComment: {
@@ -44,19 +48,14 @@ const options = {
44
48
  type: "boolean",
45
49
  category: CATEGORY_BQ,
46
50
  default: true,
47
- description: "Print reserved keywords and functions in upper case.",
51
+ description: "Print keywords, built-in functions and pseudo columns in upper case.",
48
52
  },
49
53
  printPseudoColumnsInUpperCase: {
50
54
  type: "boolean",
51
55
  category: CATEGORY_BQ,
52
56
  default: true,
53
- description: "Print pseudo columns in upper case.",
57
+ description: "Deprecated: This option was merged into printKeywordsInUpperCase.",
54
58
  },
55
59
  };
56
- module.exports = {
57
- languages,
58
- parsers,
59
- printers,
60
- options,
61
- };
60
+ exports.options = options;
62
61
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,mCAA+B;AAC/B,uCAAqC;AAErC,MAAM,SAAS,GAAG;IAChB;QACE,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,CAAC,WAAW,CAAC;KACvB;CACF,CAAC;AAEF,MAAM,OAAO,GAAG;IACd,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAA,cAAK,EAAC,IAAI,CAAC;QACpC,SAAS,EAAE,SAAS;KACrB;CACF,CAAC;AAEF,MAAM,QAAQ,GAAG;IACf,SAAS,EAAE;QACT,KAAK,EAAE,kBAAQ;KAChB;CACF,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,MAAM,OAAO,GAAG;IACd,sBAAsB,EAAE;QACtB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,2BAA2B;KACzC;IACD,SAAS,EAAE;QACT,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,4BAA4B;KAC1C;IACD,sBAAsB,EAAE;QACtB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,4CAA4C;KAC1D;IACD,wBAAwB,EAAE;QACxB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,sDAAsD;KACpE;IACD,6BAA6B,EAAE;QAC7B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,qCAAqC;KACnD;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,GAAG;IACf,SAAS;IACT,OAAO;IACP,QAAQ;IACR,OAAO;CACR,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAA+B;AAC/B,uCAAqC;AAErC,MAAM,SAAS,GAAG;IAChB;QACE,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,CAAC,WAAW,CAAC;KACvB;CACF,CAAC;AAoDO,8BAAS;AAlDlB,MAAM,OAAO,GAAG;IACd,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAA,cAAK,EAAC,IAAI,CAAC;QACpC,SAAS,EAAE,SAAS;KACrB;CACF,CAAC;AA6CkB,0BAAO;AA3C3B,MAAM,QAAQ,GAAG;IACf,SAAS,EAAE;QACT,KAAK,EAAE,kBAAQ;KAChB;CACF,CAAC;AAuC2B,4BAAQ;AArCrC,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,MAAM,OAAO,GAAG;IACd,sBAAsB,EAAE;QACtB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,2BAA2B;KACzC;IACD,SAAS,EAAE;QACT,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,4BAA4B;KAC1C;IACD,sBAAsB,EAAE;QACtB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,4CAA4C;KAC1D;IACD,wBAAwB,EAAE;QACxB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EACT,sEAAsE;KACzE;IACD,6BAA6B,EAAE;QAC7B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,IAAI;QACb,WAAW,EACT,mEAAmE;KACtE;CACF,CAAC;AAEqC,0BAAO"}
package/dist/printer.js CHANGED
@@ -207,7 +207,7 @@ class Printer {
207
207
  else if (!this.node.notGlobal && this.includedIn(keywords_1.reservedKeywords)) {
208
208
  literal = literal.toUpperCase();
209
209
  }
210
- else if (this.options.printPseudoColumnsInUpperCase &&
210
+ else if (this.options.printKeywordsInUpperCase &&
211
211
  (literal.match(/^_PARTITION/i) ||
212
212
  literal.match(/^_TABLE_/i) ||
213
213
  literal.match(/^_FILE_/i) ||
@@ -355,40 +355,57 @@ const getFirstNode = (node, includeComment = false) => {
355
355
  }
356
356
  return res;
357
357
  };
358
+ const getLastNode = (node, includeComment = false) => {
359
+ const candidates = [];
360
+ for (const [k, v] of Object.entries(node.children)) {
361
+ if (["leading_comments", "trailing_comments"].includes(k) &&
362
+ !includeComment) {
363
+ continue;
364
+ }
365
+ if ((0, exports.isNodeChild)(v)) {
366
+ candidates.push(getLastNode(v.Node, includeComment));
367
+ }
368
+ else if ((0, exports.isNodeVecChild)(v)) {
369
+ // NOTE maybe you don't have to check 2nd, 3rd, or latter node
370
+ v.NodeVec.forEach((x) => candidates.push(getLastNode(x, includeComment)));
371
+ }
372
+ }
373
+ let res = node;
374
+ for (const c of candidates) {
375
+ if (!c.token) {
376
+ continue;
377
+ }
378
+ if (!res.token) {
379
+ res = c;
380
+ continue;
381
+ }
382
+ if (res.token.line < c.token.line ||
383
+ (c.token.line === res.token.line && res.token.column < c.token.column)) {
384
+ res = c;
385
+ }
386
+ }
387
+ return res;
388
+ };
358
389
  const printSQL = (path, options, print) => {
359
390
  const node = path.node;
360
391
  if (Array.isArray(node)) {
361
392
  for (let i = 0; i < node.length - 1; i++) {
362
- const endNode = node[i];
363
- if ("semicolon" in endNode.children) {
364
- // end of statement
365
- const semicolon = endNode.children.semicolon;
366
- if (semicolon) {
367
- let endLine = semicolon.Node.token.line;
368
- const trailing_comments = semicolon.Node.children.trailing_comments;
369
- if (trailing_comments) {
370
- const comments = trailing_comments.NodeVec;
371
- const len = comments.length;
372
- const last_comments = comments[len - 1];
373
- endLine = last_comments.token.line;
374
- const endLiteral = last_comments.token.literal;
375
- const newLines = endLiteral.match(/\n/g);
376
- if (newLines) {
377
- endLine += newLines.length;
378
- }
379
- }
380
- // start of statement
381
- const startNode = getFirstNode(node[i + 1], true);
382
- let startLine;
383
- if (startNode.token) {
384
- startLine = startNode.token.line;
385
- }
386
- else {
387
- // EOF
388
- startLine = endLine + 1;
389
- }
390
- node[i].emptyLines = startLine - endLine - 1; // >= -1
393
+ if ("semicolon" in node[i].children ||
394
+ node[i].node_type === "StandAloneExpr") {
395
+ const endNode = getLastNode(node[i], true);
396
+ if (!endNode.token)
397
+ continue;
398
+ const endLine = endNode.token.line;
399
+ const startNode = getFirstNode(node[i + 1], true);
400
+ let startLine;
401
+ if (startNode.token) {
402
+ startLine = startNode.token.line;
391
403
  }
404
+ else {
405
+ // EOF
406
+ startLine = endLine + 1;
407
+ }
408
+ node[i].emptyLines = startLine - endLine - 1; // >= -1
392
409
  }
393
410
  }
394
411
  return path.map(print);
@@ -632,6 +649,8 @@ const printSQL = (path, options, print) => {
632
649
  return printSetStatement(path, options, print, node);
633
650
  case "SingleTokenStatement":
634
651
  return printSingleTokenStatement(path, options, print, node);
652
+ case "StandAloneExpr":
653
+ return printStandAloneExpr(path, options, print, node);
635
654
  case "StringLiteral":
636
655
  return printStringLiteral(path, options, print, node);
637
656
  case "StructLiteral":
@@ -644,8 +663,14 @@ const printSQL = (path, options, print) => {
644
663
  return printTableSamplePipeOperator(path, options, print, node);
645
664
  case "TableSampleRatio":
646
665
  return printTableSampleRatio(path, options, print, node);
647
- case "Template":
666
+ case "TemplateExpr":
648
667
  return printIdentifier(path, options, print, node);
668
+ case "TemplateExprContinue":
669
+ return printTemplateExprContinue(path, options, print, node);
670
+ case "TemplateExprEnd":
671
+ return printTemplateExprEnd(path, options, print, node);
672
+ case "TemplateExprStart":
673
+ return printTemplateExprStart(path, options, print, node);
649
674
  case "TransactionStatement":
650
675
  return printTransactionStatement(path, options, print, node);
651
676
  case "TrainingDataCustomHolidayClause":
@@ -4525,6 +4550,14 @@ const printSingleTokenStatement = (path, options, print, node) => {
4525
4550
  p.newLine(),
4526
4551
  ];
4527
4552
  };
4553
+ const printStandAloneExpr = (path, options, print, node) => {
4554
+ const p = new Printer(path, options, print, node);
4555
+ const docs = {
4556
+ self: "", // eslint-disable-line unicorn/no-unused-properties
4557
+ expr: p.child("expr"),
4558
+ };
4559
+ return [docs.expr, p.newLine()];
4560
+ };
4528
4561
  const printStringLiteral = (path, options, print, node) => {
4529
4562
  const p = new Printer(path, options, print, node);
4530
4563
  const docs = {
@@ -4649,6 +4682,79 @@ const printTableSampleRatio = (path, options, print, node) => {
4649
4682
  docs.rparen,
4650
4683
  ];
4651
4684
  };
4685
+ const printTemplateExprContinue = (path, options, print, node) => {
4686
+ const p = new Printer(path, options, print, node);
4687
+ p.setNotRoot("exprs");
4688
+ p.setGroupRecommended("exprs");
4689
+ const docs = {
4690
+ leading_comments: printLeadingComments(path, options, print, node),
4691
+ self: p.self("asItIs"),
4692
+ trailing_comments: printTrailingComments(path, options, print, node),
4693
+ exprs: p.child("exprs", (x) => [line, x]),
4694
+ };
4695
+ return [
4696
+ docs.leading_comments,
4697
+ docs.self,
4698
+ docs.trailing_comments,
4699
+ indent(docs.exprs),
4700
+ ];
4701
+ };
4702
+ const printTemplateExprEnd = (path, options, print, node) => {
4703
+ const p = new Printer(path, options, print, node);
4704
+ const docs = {
4705
+ leading_comments: printLeadingComments(path, options, print, node),
4706
+ self: p.self("asItIs"),
4707
+ trailing_comments: printTrailingComments(path, options, print, node),
4708
+ };
4709
+ return [docs.leading_comments, docs.self, docs.trailing_comments];
4710
+ };
4711
+ const printTemplateExprStart = (path, options, print, node) => {
4712
+ const p = new Printer(path, options, print, node);
4713
+ p.setNotRoot("exprs");
4714
+ p.setGroupRecommended("exprs");
4715
+ const docs = {
4716
+ leading_comments: printLeadingComments(path, options, print, node),
4717
+ self: p.self("asItIs"),
4718
+ trailing_comments: printTrailingComments(path, options, print, node),
4719
+ exprs: p.child("exprs", (x) => [line, x]),
4720
+ continues: p.child("continues", (x) => [line, x]),
4721
+ end: p.child("end"),
4722
+ as: "", // eslint-disable-line unicorn/no-unused-properties
4723
+ alias: printAlias(path, options, print, node),
4724
+ order: printOrder(path, options, print, node),
4725
+ null_order: "", // eslint-disable-line unicorn/no-unused-properties
4726
+ // do not use printComma here
4727
+ // additional trailing comma may break your code
4728
+ comma: p.child("comma", undefined, "all"),
4729
+ with_offset: p.child("with_offset", undefined, "all"),
4730
+ pivot: printPivotOrUnpivotOperator(path, options, print, node),
4731
+ unpivot: "", // eslint-disable-line unicorn/no-unused-properties
4732
+ match_recognize: p.child("match_recognize", undefined, "all"),
4733
+ };
4734
+ return [
4735
+ docs.leading_comments,
4736
+ group([
4737
+ docs.self,
4738
+ indent(docs.exprs),
4739
+ docs.continues,
4740
+ 0 < p.children.exprs.NodeVec.length ||
4741
+ 0 < p.children.continues.NodeVec.length
4742
+ ? line
4743
+ : "",
4744
+ docs.end,
4745
+ docs.trailing_comments,
4746
+ docs.alias,
4747
+ docs.order,
4748
+ docs.pivot,
4749
+ docs.comma,
4750
+ p.has("match_recognize") ? " " : "",
4751
+ docs.match_recognize,
4752
+ p.has("with_offset") ? " " : "",
4753
+ docs.with_offset,
4754
+ ]),
4755
+ p.newLine(),
4756
+ ];
4757
+ };
4652
4758
  const printTransactionStatement = (path, options, print, node) => {
4653
4759
  const p = new Printer(path, options, print, node);
4654
4760
  const docs = {