rawsql-ts 0.24.2 → 0.24.4

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.
Files changed (83) hide show
  1. package/README.md +10 -1
  2. package/dist/esm/index.min.js +9 -9
  3. package/dist/esm/index.min.js.map +2 -2
  4. package/dist/esm/models/ValueComponent.d.ts +2 -1
  5. package/dist/esm/models/ValueComponent.js +2 -1
  6. package/dist/esm/models/ValueComponent.js.map +1 -1
  7. package/dist/esm/parsers/CommandExpressionParser.d.ts +2 -0
  8. package/dist/esm/parsers/CommandExpressionParser.js +49 -4
  9. package/dist/esm/parsers/CommandExpressionParser.js.map +1 -1
  10. package/dist/esm/parsers/HavingParser.js +6 -0
  11. package/dist/esm/parsers/HavingParser.js.map +1 -1
  12. package/dist/esm/parsers/JoinOnClauseParser.js +6 -0
  13. package/dist/esm/parsers/JoinOnClauseParser.js.map +1 -1
  14. package/dist/esm/parsers/LimitClauseParser.js +6 -0
  15. package/dist/esm/parsers/LimitClauseParser.js.map +1 -1
  16. package/dist/esm/parsers/OffsetClauseParser.js +6 -0
  17. package/dist/esm/parsers/OffsetClauseParser.js.map +1 -1
  18. package/dist/esm/parsers/OrderByClauseParser.d.ts +2 -0
  19. package/dist/esm/parsers/OrderByClauseParser.js +42 -29
  20. package/dist/esm/parsers/OrderByClauseParser.js.map +1 -1
  21. package/dist/esm/parsers/ParameterDecorator.d.ts +2 -2
  22. package/dist/esm/parsers/ParameterDecorator.js +1 -1
  23. package/dist/esm/parsers/ParameterDecorator.js.map +1 -1
  24. package/dist/esm/parsers/ParameterExpressionParser.js +2 -1
  25. package/dist/esm/parsers/ParameterExpressionParser.js.map +1 -1
  26. package/dist/esm/parsers/SqlPrintTokenParser.d.ts +18 -4
  27. package/dist/esm/parsers/SqlPrintTokenParser.js +135 -18
  28. package/dist/esm/parsers/SqlPrintTokenParser.js.map +1 -1
  29. package/dist/esm/parsers/ValueParser.js +5 -3
  30. package/dist/esm/parsers/ValueParser.js.map +1 -1
  31. package/dist/esm/transformers/FormatOptionResolver.js +1 -0
  32. package/dist/esm/transformers/FormatOptionResolver.js.map +1 -1
  33. package/dist/esm/transformers/LinePrinter.d.ts +1 -1
  34. package/dist/esm/transformers/OnelineFormattingHelper.d.ts +1 -0
  35. package/dist/esm/transformers/OnelineFormattingHelper.js.map +1 -1
  36. package/dist/esm/transformers/SqlFormatter.d.ts +9 -5
  37. package/dist/esm/transformers/SqlFormatter.js +4 -4
  38. package/dist/esm/transformers/SqlFormatter.js.map +1 -1
  39. package/dist/esm/transformers/SqlPrinter.d.ts +26 -2
  40. package/dist/esm/transformers/SqlPrinter.js +325 -26
  41. package/dist/esm/transformers/SqlPrinter.js.map +1 -1
  42. package/dist/index.min.js +9 -9
  43. package/dist/index.min.js.map +2 -2
  44. package/dist/models/ValueComponent.js +2 -1
  45. package/dist/models/ValueComponent.js.map +1 -1
  46. package/dist/parsers/CommandExpressionParser.js +49 -4
  47. package/dist/parsers/CommandExpressionParser.js.map +1 -1
  48. package/dist/parsers/HavingParser.js +6 -0
  49. package/dist/parsers/HavingParser.js.map +1 -1
  50. package/dist/parsers/JoinOnClauseParser.js +6 -0
  51. package/dist/parsers/JoinOnClauseParser.js.map +1 -1
  52. package/dist/parsers/LimitClauseParser.js +6 -0
  53. package/dist/parsers/LimitClauseParser.js.map +1 -1
  54. package/dist/parsers/OffsetClauseParser.js +6 -0
  55. package/dist/parsers/OffsetClauseParser.js.map +1 -1
  56. package/dist/parsers/OrderByClauseParser.js +42 -29
  57. package/dist/parsers/OrderByClauseParser.js.map +1 -1
  58. package/dist/parsers/ParameterDecorator.js +1 -1
  59. package/dist/parsers/ParameterDecorator.js.map +1 -1
  60. package/dist/parsers/ParameterExpressionParser.js +2 -1
  61. package/dist/parsers/ParameterExpressionParser.js.map +1 -1
  62. package/dist/parsers/SqlPrintTokenParser.js +136 -18
  63. package/dist/parsers/SqlPrintTokenParser.js.map +1 -1
  64. package/dist/parsers/ValueParser.js +5 -3
  65. package/dist/parsers/ValueParser.js.map +1 -1
  66. package/dist/src/models/ValueComponent.d.ts +2 -1
  67. package/dist/src/parsers/CommandExpressionParser.d.ts +2 -0
  68. package/dist/src/parsers/OrderByClauseParser.d.ts +2 -0
  69. package/dist/src/parsers/ParameterDecorator.d.ts +2 -2
  70. package/dist/src/parsers/SqlPrintTokenParser.d.ts +18 -4
  71. package/dist/src/transformers/LinePrinter.d.ts +1 -1
  72. package/dist/src/transformers/OnelineFormattingHelper.d.ts +1 -0
  73. package/dist/src/transformers/SqlFormatter.d.ts +9 -5
  74. package/dist/src/transformers/SqlPrinter.d.ts +26 -2
  75. package/dist/transformers/FormatOptionResolver.js +1 -0
  76. package/dist/transformers/FormatOptionResolver.js.map +1 -1
  77. package/dist/transformers/OnelineFormattingHelper.js.map +1 -1
  78. package/dist/transformers/SqlFormatter.js +5 -3
  79. package/dist/transformers/SqlFormatter.js.map +1 -1
  80. package/dist/transformers/SqlPrinter.js +325 -26
  81. package/dist/transformers/SqlPrinter.js.map +1 -1
  82. package/dist/tsconfig.browser.tsbuildinfo +1 -1
  83. package/package.json +2 -2
@@ -9,11 +9,14 @@ import { CreateTableQuery } from "../models/CreateTableQuery";
9
9
  export declare enum ParameterStyle {
10
10
  Anonymous = "anonymous",
11
11
  Indexed = "indexed",
12
- Named = "named"
12
+ Named = "named",
13
+ Original = "original"
13
14
  }
14
15
  export type CastStyle = 'postgres' | 'standard';
15
16
  export type ConstraintStyle = 'postgres' | 'mysql';
16
- export type SourceAliasStyle = 'as' | 'implicit';
17
+ export type AliasKeywordStyle = 'explicit' | 'omit' | 'as' | 'implicit';
18
+ export type SourceAliasStyle = AliasKeywordStyle;
19
+ export type ColumnAliasStyle = AliasKeywordStyle;
17
20
  export type OrderByDefaultDirectionStyle = 'omit' | 'explicit';
18
21
  export interface FormatterConfig {
19
22
  identifierEscape?: {
@@ -26,7 +29,7 @@ export interface FormatterConfig {
26
29
  end: string;
27
30
  };
28
31
  /**
29
- * Parameter style: anonymous (?), indexed ($1), or named (:name)
32
+ * Parameter style: anonymous (?), indexed ($1), named (:name), or original parsed spelling.
30
33
  */
31
34
  parameterStyle?: ParameterStyle;
32
35
  /** Controls how CAST expressions are rendered */
@@ -35,6 +38,8 @@ export interface FormatterConfig {
35
38
  constraintStyle?: ConstraintStyle;
36
39
  /** Controls whether source aliases are rendered with an explicit AS keyword */
37
40
  sourceAliasStyle?: SourceAliasStyle;
41
+ /** Controls whether column aliases are rendered with an explicit AS keyword */
42
+ columnAliasStyle?: ColumnAliasStyle;
38
43
  /** Controls whether the default ASC direction is omitted or rendered explicitly */
39
44
  orderByDefaultDirectionStyle?: OrderByDefaultDirectionStyle;
40
45
  }
@@ -55,8 +60,10 @@ export declare class SqlPrintTokenParser implements SqlComponentVisitor<SqlPrint
55
60
  private castStyle;
56
61
  private constraintStyle;
57
62
  private sourceAliasStyle;
63
+ private columnAliasStyle;
58
64
  private orderByDefaultDirectionStyle;
59
65
  private readonly normalizeJoinConditionOrder;
66
+ private readonly listContinuationCommentComponents;
60
67
  private joinConditionContexts;
61
68
  constructor(options?: {
62
69
  preset?: FormatterConfig;
@@ -69,10 +76,11 @@ export declare class SqlPrintTokenParser implements SqlComponentVisitor<SqlPrint
69
76
  start: string;
70
77
  end: string;
71
78
  };
72
- parameterStyle?: 'anonymous' | 'indexed' | 'named';
79
+ parameterStyle?: 'anonymous' | 'indexed' | 'named' | 'original';
73
80
  castStyle?: CastStyle;
74
81
  constraintStyle?: ConstraintStyle;
75
82
  sourceAliasStyle?: SourceAliasStyle;
83
+ columnAliasStyle?: ColumnAliasStyle;
76
84
  orderByDefaultDirectionStyle?: OrderByDefaultDirectionStyle;
77
85
  joinConditionOrderByDeclaration?: boolean;
78
86
  });
@@ -218,6 +226,8 @@ export declare class SqlPrintTokenParser implements SqlComponentVisitor<SqlPrint
218
226
  private visitParenExpression;
219
227
  private visitCastExpression;
220
228
  private visitCaseExpression;
229
+ private removeCaseAfterCommentsDuplicatedOnFirstWhen;
230
+ private normalizeCommentSignature;
221
231
  private extractSwitchAfterComments;
222
232
  private collectCaseLeadingCommentsFromSwitch;
223
233
  private findCaseKeyToken;
@@ -249,6 +259,8 @@ export declare class SqlPrintTokenParser implements SqlComponentVisitor<SqlPrint
249
259
  */
250
260
  private visitWindowFrameBoundStatic;
251
261
  private visitSelectItem;
262
+ private collectCaseSelectItemDuplicateAfterComments;
263
+ private filterCaseSelectItemDuplicateAfterComments;
252
264
  private visitSelectClause;
253
265
  private flattenTokenText;
254
266
  private visitHintClause;
@@ -257,6 +269,8 @@ export declare class SqlPrintTokenParser implements SqlComponentVisitor<SqlPrint
257
269
  private visitTableSource;
258
270
  private visitSourceExpression;
259
271
  private appendSourceAliasKeyword;
272
+ private shouldPrintColumnAliasKeyword;
273
+ private normalizeAliasKeywordStyle;
260
274
  visitFromClause(arg: FromClause): SqlPrintToken;
261
275
  visitJoinClause(arg: JoinClause): SqlPrintToken;
262
276
  visitJoinOnClause(arg: JoinOnClause): SqlPrintToken;
@@ -18,6 +18,7 @@ export var ParameterStyle;
18
18
  ParameterStyle["Anonymous"] = "anonymous";
19
19
  ParameterStyle["Indexed"] = "indexed";
20
20
  ParameterStyle["Named"] = "named";
21
+ ParameterStyle["Original"] = "original";
21
22
  })(ParameterStyle || (ParameterStyle = {}));
22
23
  export const PRESETS = {
23
24
  mysql: {
@@ -138,6 +139,7 @@ export class SqlPrintTokenParser {
138
139
  this._selfHandlingComponentTypes = new Set([
139
140
  SimpleSelectQuery.kind,
140
141
  SelectItem.kind,
142
+ OrderByItem.kind,
141
143
  CaseKeyValuePair.kind,
142
144
  SwitchCaseArgument.kind,
143
145
  ColumnReference.kind,
@@ -154,9 +156,10 @@ export class SqlPrintTokenParser {
154
156
  return this._selfHandlingComponentTypes;
155
157
  }
156
158
  constructor(options) {
157
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
159
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
158
160
  this.handlers = new Map();
159
161
  this.index = 1;
162
+ this.listContinuationCommentComponents = new WeakSet();
160
163
  this.joinConditionContexts = [];
161
164
  if (options === null || options === void 0 ? void 0 : options.preset) {
162
165
  const preset = options.preset;
@@ -174,9 +177,10 @@ export class SqlPrintTokenParser {
174
177
  });
175
178
  this.castStyle = (_j = options === null || options === void 0 ? void 0 : options.castStyle) !== null && _j !== void 0 ? _j : 'standard';
176
179
  this.constraintStyle = (_k = options === null || options === void 0 ? void 0 : options.constraintStyle) !== null && _k !== void 0 ? _k : 'postgres';
177
- this.sourceAliasStyle = (_l = options === null || options === void 0 ? void 0 : options.sourceAliasStyle) !== null && _l !== void 0 ? _l : 'as';
178
- this.orderByDefaultDirectionStyle = (_m = options === null || options === void 0 ? void 0 : options.orderByDefaultDirectionStyle) !== null && _m !== void 0 ? _m : 'omit';
179
- this.normalizeJoinConditionOrder = (_o = options === null || options === void 0 ? void 0 : options.joinConditionOrderByDeclaration) !== null && _o !== void 0 ? _o : false;
180
+ this.sourceAliasStyle = this.normalizeAliasKeywordStyle(options === null || options === void 0 ? void 0 : options.sourceAliasStyle);
181
+ this.columnAliasStyle = this.normalizeAliasKeywordStyle(options === null || options === void 0 ? void 0 : options.columnAliasStyle);
182
+ this.orderByDefaultDirectionStyle = (_l = options === null || options === void 0 ? void 0 : options.orderByDefaultDirectionStyle) !== null && _l !== void 0 ? _l : 'omit';
183
+ this.normalizeJoinConditionOrder = (_m = options === null || options === void 0 ? void 0 : options.joinConditionOrderByDeclaration) !== null && _m !== void 0 ? _m : false;
180
184
  this.handlers.set(ValueList.kind, (expr) => this.visitValueList(expr));
181
185
  this.handlers.set(ColumnReference.kind, (expr) => this.visitColumnReference(expr));
182
186
  this.handlers.set(QualifiedName.kind, (expr) => this.visitQualifiedName(expr));
@@ -346,6 +350,7 @@ export class SqlPrintTokenParser {
346
350
  }
347
351
  visitQualifiedName(arg) {
348
352
  const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.QualifiedName);
353
+ const hasOwnComments = this.hasPositionedComments(arg) || this.hasLegacyComments(arg);
349
354
  if (arg.namespaces) {
350
355
  for (let i = 0; i < arg.namespaces.length; i++) {
351
356
  token.innerTokens.push(arg.namespaces[i].accept(this));
@@ -366,7 +371,7 @@ export class SqlPrintTokenParser {
366
371
  arg.name.positionedComments = originalNameComments;
367
372
  arg.name.comments = originalNameLegacyComments;
368
373
  // Apply the name's comments to the qualified name token
369
- if (this.hasPositionedComments(arg.name) || this.hasLegacyComments(arg.name)) {
374
+ if (!hasOwnComments && (this.hasPositionedComments(arg.name) || this.hasLegacyComments(arg.name))) {
370
375
  this.addComponentComments(token, arg.name);
371
376
  }
372
377
  // Also handle any comments directly on the QualifiedName itself
@@ -387,7 +392,13 @@ export class SqlPrintTokenParser {
387
392
  for (let i = 0; i < arg.order.length; i++) {
388
393
  if (i > 0)
389
394
  token.innerTokens.push(...SqlPrintTokenParser.commaSpaceTokens());
390
- token.innerTokens.push(this.visit(arg.order[i]));
395
+ const item = arg.order[i];
396
+ token.innerTokens.push(this.visit(item));
397
+ if (!(item instanceof OrderByItem) &&
398
+ this.orderByDefaultDirectionStyle === 'explicit') {
399
+ token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
400
+ token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'asc'));
401
+ }
391
402
  }
392
403
  return token;
393
404
  }
@@ -416,6 +427,10 @@ export class SqlPrintTokenParser {
416
427
  token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'nulls last'));
417
428
  }
418
429
  }
430
+ if (arg.comments && arg.comments.length > 0) {
431
+ token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
432
+ token.innerTokens.push(...this.createInlineCommentSequence(arg.comments));
433
+ }
419
434
  return token;
420
435
  }
421
436
  parse(arg) {
@@ -424,7 +439,7 @@ export class SqlPrintTokenParser {
424
439
  const token = this.visit(arg);
425
440
  const paramsRaw = ParameterCollector.collect(arg).sort((a, b) => { var _a, _b; return ((_a = a.index) !== null && _a !== void 0 ? _a : 0) - ((_b = b.index) !== null && _b !== void 0 ? _b : 0); });
426
441
  const style = this.parameterDecorator.style;
427
- if (style === ParameterStyle.Named) {
442
+ if (style === ParameterStyle.Named || style === ParameterStyle.Original) {
428
443
  // Named: { name: value, ... }
429
444
  const paramsObj = {};
430
445
  for (const p of paramsRaw) {
@@ -894,7 +909,18 @@ export class SqlPrintTokenParser {
894
909
  }
895
910
  visitColumnReference(arg) {
896
911
  const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.ColumnReference);
912
+ const hasOwnComments = this.hasPositionedComments(arg) || this.hasLegacyComments(arg);
913
+ const originalNamePositionedComments = arg.qualifiedName.name.positionedComments;
914
+ const originalNameComments = arg.qualifiedName.name.comments;
915
+ if (hasOwnComments) {
916
+ arg.qualifiedName.name.positionedComments = null;
917
+ arg.qualifiedName.name.comments = null;
918
+ }
897
919
  token.innerTokens.push(arg.qualifiedName.accept(this));
920
+ if (hasOwnComments) {
921
+ arg.qualifiedName.name.positionedComments = originalNamePositionedComments;
922
+ arg.qualifiedName.name.comments = originalNameComments;
923
+ }
898
924
  this.addComponentComments(token, arg);
899
925
  return token;
900
926
  }
@@ -1060,7 +1086,9 @@ export class SqlPrintTokenParser {
1060
1086
  visitParameterExpression(arg) {
1061
1087
  // Create a parameter token and decorate it using the parameterDecorator
1062
1088
  arg.index = this.index;
1063
- const text = this.parameterDecorator.decorate(arg.name.value, arg.index);
1089
+ const text = this.parameterDecorator.style === ParameterStyle.Original && arg.sourceText
1090
+ ? arg.sourceText
1091
+ : this.parameterDecorator.decorate(arg.name.value, arg.index);
1064
1092
  const token = new SqlPrintToken(SqlPrintTokenType.parameter, text);
1065
1093
  this.addComponentComments(token, arg);
1066
1094
  this.index++;
@@ -1166,6 +1194,7 @@ export class SqlPrintTokenParser {
1166
1194
  return token;
1167
1195
  }
1168
1196
  visitParenExpression(arg) {
1197
+ var _a, _b;
1169
1198
  const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.ParenExpression);
1170
1199
  // Handle positioned comments for ParenExpression - check both self and inner expression
1171
1200
  const hasOwnComments = arg.positionedComments && arg.positionedComments.length > 0;
@@ -1178,6 +1207,11 @@ export class SqlPrintTokenParser {
1178
1207
  innerAfterComments = arg.expression.getPositionedComments('after');
1179
1208
  arg.expression.positionedComments = null;
1180
1209
  }
1210
+ if (hasOwnComments && innerBeforeComments.length > 0) {
1211
+ const innerBeforeSet = new Set(innerBeforeComments);
1212
+ arg.positionedComments = (_b = (_a = arg.positionedComments) === null || _a === void 0 ? void 0 : _a.map(comment => comment.position === 'after'
1213
+ ? Object.assign(Object.assign({}, comment), { comments: comment.comments.filter(value => !innerBeforeSet.has(value)) }) : comment).filter(comment => comment.comments.length > 0)) !== null && _b !== void 0 ? _b : null;
1214
+ }
1181
1215
  // Build basic structure first
1182
1216
  token.innerTokens.push(SqlPrintTokenParser.PAREN_OPEN_TOKEN);
1183
1217
  token.innerTokens.push(this.visit(arg.expression));
@@ -1231,6 +1265,7 @@ export class SqlPrintTokenParser {
1231
1265
  const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CaseExpression);
1232
1266
  // Handle positioned comments for CaseExpression (unified spec: positioned comments only)
1233
1267
  if (arg.positionedComments && arg.positionedComments.length > 0) {
1268
+ this.removeCaseAfterCommentsDuplicatedOnFirstWhen(arg);
1234
1269
  this.addPositionedCommentsToToken(token, arg);
1235
1270
  // Clear positioned comments to prevent duplicate processing
1236
1271
  arg.positionedComments = null;
@@ -1243,7 +1278,6 @@ export class SqlPrintTokenParser {
1243
1278
  promotedComments.push(...this.collectCaseLeadingCommentBlocks(conditionToken));
1244
1279
  }
1245
1280
  const switchToken = this.visit(arg.switchCase);
1246
- promotedComments.push(...this.collectCaseLeadingCommentsFromSwitch(switchToken));
1247
1281
  if (promotedComments.length > 0) {
1248
1282
  token.innerTokens.push(...promotedComments);
1249
1283
  }
@@ -1266,6 +1300,32 @@ export class SqlPrintTokenParser {
1266
1300
  }
1267
1301
  return token;
1268
1302
  }
1303
+ removeCaseAfterCommentsDuplicatedOnFirstWhen(arg) {
1304
+ var _a, _b;
1305
+ if (!arg.positionedComments || arg.positionedComments.length === 0) {
1306
+ return;
1307
+ }
1308
+ const firstWhenComments = new Set(((_b = (_a = arg.switchCase.cases[0]) === null || _a === void 0 ? void 0 : _a.getPositionedComments('before')) !== null && _b !== void 0 ? _b : [])
1309
+ .map(comment => this.normalizeCommentSignature(comment)));
1310
+ if (firstWhenComments.size === 0) {
1311
+ return;
1312
+ }
1313
+ const retained = [];
1314
+ for (const entry of arg.positionedComments) {
1315
+ if (entry.position !== 'after') {
1316
+ retained.push(entry);
1317
+ continue;
1318
+ }
1319
+ const comments = entry.comments.filter(comment => !firstWhenComments.has(this.normalizeCommentSignature(comment)));
1320
+ if (comments.length > 0) {
1321
+ retained.push(Object.assign(Object.assign({}, entry), { comments }));
1322
+ }
1323
+ }
1324
+ arg.positionedComments = retained.length > 0 ? retained : null;
1325
+ }
1326
+ normalizeCommentSignature(comment) {
1327
+ return comment.replace(/\s+/g, ' ').trim();
1328
+ }
1269
1329
  extractSwitchAfterComments(arg) {
1270
1330
  if (!arg.positionedComments || arg.positionedComments.length === 0) {
1271
1331
  return [];
@@ -1587,11 +1647,13 @@ export class SqlPrintTokenParser {
1587
1647
  // Add positioned comments in recorded order
1588
1648
  const beforeComments = arg.getPositionedComments('before');
1589
1649
  const afterComments = arg.getPositionedComments('after');
1650
+ const duplicateCaseAfterComments = arg.value instanceof CaseExpression
1651
+ ? this.collectCaseSelectItemDuplicateAfterComments(arg.value)
1652
+ : new Set();
1590
1653
  if (beforeComments.length > 0) {
1591
- if (arg.value instanceof CaseExpression) {
1654
+ if (arg.value instanceof CaseExpression || this.listContinuationCommentComponents.has(arg)) {
1592
1655
  const commentBlocks = this.createCommentBlocks(beforeComments);
1593
1656
  token.innerTokens.push(...commentBlocks);
1594
- token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
1595
1657
  }
1596
1658
  else {
1597
1659
  const commentTokens = this.createInlineCommentSequence(beforeComments);
@@ -1600,9 +1662,12 @@ export class SqlPrintTokenParser {
1600
1662
  }
1601
1663
  }
1602
1664
  token.innerTokens.push(this.visit(arg.value));
1603
- if (afterComments.length > 0 && !isParenExpression) {
1665
+ const visibleAfterComments = arg.value instanceof CaseExpression
1666
+ ? this.filterCaseSelectItemDuplicateAfterComments(afterComments, duplicateCaseAfterComments)
1667
+ : afterComments;
1668
+ if (visibleAfterComments.length > 0 && !isParenExpression) {
1604
1669
  token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1605
- const commentTokens = this.createInlineCommentSequence(afterComments);
1670
+ const commentTokens = this.createInlineCommentSequence(visibleAfterComments);
1606
1671
  token.innerTokens.push(...commentTokens);
1607
1672
  }
1608
1673
  // Restore original positioned comments to avoid side effects
@@ -1622,6 +1687,8 @@ export class SqlPrintTokenParser {
1622
1687
  token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1623
1688
  // Handle AS keyword positioned comments (before AS)
1624
1689
  const asKeywordPositionedComments = 'asKeywordPositionedComments' in arg ? arg.asKeywordPositionedComments : null;
1690
+ const asKeywordComments = 'asKeywordComments' in arg ? arg.asKeywordComments : null;
1691
+ const shouldPrintAsKeyword = this.shouldPrintColumnAliasKeyword(asKeywordPositionedComments, asKeywordComments);
1625
1692
  if (asKeywordPositionedComments) {
1626
1693
  const beforeComments = asKeywordPositionedComments.filter((pc) => pc.position === 'before');
1627
1694
  if (beforeComments.length > 0) {
@@ -1632,9 +1699,11 @@ export class SqlPrintTokenParser {
1632
1699
  }
1633
1700
  }
1634
1701
  }
1635
- token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'as'));
1702
+ if (shouldPrintAsKeyword) {
1703
+ token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'as'));
1704
+ }
1636
1705
  // Handle AS keyword positioned comments (after AS)
1637
- if (asKeywordPositionedComments) {
1706
+ if (shouldPrintAsKeyword && asKeywordPositionedComments) {
1638
1707
  const afterComments = asKeywordPositionedComments.filter((pc) => pc.position === 'after');
1639
1708
  if (afterComments.length > 0) {
1640
1709
  token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
@@ -1645,13 +1714,14 @@ export class SqlPrintTokenParser {
1645
1714
  }
1646
1715
  }
1647
1716
  // Fallback: Add AS keyword legacy comments if present
1648
- const asKeywordComments = 'asKeywordComments' in arg ? arg.asKeywordComments : null;
1649
1717
  if (asKeywordComments && asKeywordComments.length > 0) {
1650
1718
  token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1651
1719
  const commentTokens = this.createInlineCommentSequence(asKeywordComments);
1652
1720
  token.innerTokens.push(...commentTokens);
1653
1721
  }
1654
- token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1722
+ if (shouldPrintAsKeyword) {
1723
+ token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1724
+ }
1655
1725
  // Visit identifier to get alias with proper spacing
1656
1726
  const identifierToken = this.visit(arg.identifier);
1657
1727
  token.innerTokens.push(identifierToken);
@@ -1676,6 +1746,34 @@ export class SqlPrintTokenParser {
1676
1746
  }
1677
1747
  return token;
1678
1748
  }
1749
+ collectCaseSelectItemDuplicateAfterComments(value) {
1750
+ const promoted = new Set();
1751
+ if (value.comments) {
1752
+ for (const comment of value.comments) {
1753
+ promoted.add(comment);
1754
+ }
1755
+ }
1756
+ const firstCase = value.switchCase.cases[0];
1757
+ if (firstCase === null || firstCase === void 0 ? void 0 : firstCase.positionedComments) {
1758
+ for (const positionedComment of firstCase.positionedComments) {
1759
+ if (positionedComment.position === 'before') {
1760
+ for (const comment of positionedComment.comments) {
1761
+ promoted.add(comment);
1762
+ }
1763
+ }
1764
+ }
1765
+ }
1766
+ return promoted;
1767
+ }
1768
+ filterCaseSelectItemDuplicateAfterComments(afterComments, promoted) {
1769
+ if (afterComments.length === 0) {
1770
+ return afterComments;
1771
+ }
1772
+ if (promoted.size === 0) {
1773
+ return afterComments;
1774
+ }
1775
+ return afterComments.filter(comment => !promoted.has(comment));
1776
+ }
1679
1777
  visitSelectClause(arg) {
1680
1778
  const token = new SqlPrintToken(SqlPrintTokenType.keyword, 'select', SqlPrintTokenContainerType.SelectClause);
1681
1779
  // Handle positioned comments for SelectClause (unified spec)
@@ -1711,6 +1809,14 @@ export class SqlPrintTokenParser {
1711
1809
  for (let i = 0; i < arg.items.length; i++) {
1712
1810
  if (i > 0) {
1713
1811
  token.innerTokens.push(...SqlPrintTokenParser.commaSpaceTokens());
1812
+ this.listContinuationCommentComponents.add(arg.items[i]);
1813
+ try {
1814
+ token.innerTokens.push(this.visit(arg.items[i]));
1815
+ }
1816
+ finally {
1817
+ this.listContinuationCommentComponents.delete(arg.items[i]);
1818
+ }
1819
+ continue;
1714
1820
  }
1715
1821
  token.innerTokens.push(this.visit(arg.items[i]));
1716
1822
  }
@@ -1790,12 +1896,23 @@ export class SqlPrintTokenParser {
1790
1896
  }
1791
1897
  }
1792
1898
  appendSourceAliasKeyword(token) {
1793
- if (this.sourceAliasStyle === 'implicit') {
1899
+ if (this.sourceAliasStyle === 'omit') {
1794
1900
  return;
1795
1901
  }
1796
1902
  token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'as'));
1797
1903
  token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
1798
1904
  }
1905
+ shouldPrintColumnAliasKeyword(asKeywordPositionedComments, asKeywordComments) {
1906
+ if (this.columnAliasStyle === 'explicit') {
1907
+ return true;
1908
+ }
1909
+ const hasPositionedComments = Array.isArray(asKeywordPositionedComments) && asKeywordPositionedComments.length > 0;
1910
+ const hasLegacyComments = Array.isArray(asKeywordComments) && asKeywordComments.length > 0;
1911
+ return hasPositionedComments || hasLegacyComments;
1912
+ }
1913
+ normalizeAliasKeywordStyle(style) {
1914
+ return style === 'implicit' || style === 'omit' ? 'omit' : 'explicit';
1915
+ }
1799
1916
  visitFromClause(arg) {
1800
1917
  // Build a declaration order map so JOIN ON operands can be normalized later.
1801
1918
  let contextPushed = false;