@typespec/prettier-plugin-typespec 0.46.0-dev.3 → 0.46.0

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/dist/index.js CHANGED
@@ -1542,6 +1542,33 @@ const diagnostics = {
1542
1542
  default: paramMessage `Emitter '${"emitterName"}' requires '${"requiredImport"}' to be imported. Add 'import "${"requiredImport"}".`,
1543
1543
  },
1544
1544
  },
1545
+ /**
1546
+ * Linter
1547
+ */
1548
+ "invalid-rule-ref": {
1549
+ severity: "error",
1550
+ messages: {
1551
+ default: paramMessage `Reference "${"ref"}" is not a valid reference to a rule or ruleset. It must be in the following format: "<library-name>:<rule-name>"`,
1552
+ },
1553
+ },
1554
+ "unknown-rule": {
1555
+ severity: "error",
1556
+ messages: {
1557
+ default: paramMessage `Rule "${"ruleName"}" is not found in library "${"libraryName"}"`,
1558
+ },
1559
+ },
1560
+ "unknown-rule-set": {
1561
+ severity: "error",
1562
+ messages: {
1563
+ default: paramMessage `Rule set "${"ruleSetName"}" is not found in library "${"libraryName"}"`,
1564
+ },
1565
+ },
1566
+ "rule-enabled-disabled": {
1567
+ severity: "error",
1568
+ messages: {
1569
+ default: paramMessage `Rule "${"ruleName"}" has been enabled and disabled in the same ruleset.`,
1570
+ },
1571
+ },
1545
1572
  /**
1546
1573
  * Decorator
1547
1574
  */
@@ -2947,6 +2974,8 @@ function addStatementDecoratorComment({ comment }) {
2947
2974
  enclosingNode.kind === SyntaxKind.ModelStatement ||
2948
2975
  enclosingNode.kind === SyntaxKind.EnumStatement ||
2949
2976
  enclosingNode.kind === SyntaxKind.OperationStatement ||
2977
+ enclosingNode.kind === SyntaxKind.ScalarStatement ||
2978
+ enclosingNode.kind === SyntaxKind.InterfaceStatement ||
2950
2979
  enclosingNode.kind === SyntaxKind.ModelProperty ||
2951
2980
  enclosingNode.kind === SyntaxKind.EnumMember ||
2952
2981
  enclosingNode.kind === SyntaxKind.UnionStatement)) {
@@ -3032,6 +3061,14 @@ function needsParens(path, options) {
3032
3061
 
3033
3062
  const { align, breakParent, group, hardline, ifBreak, indent, join, line, softline } = prettier.doc.builders;
3034
3063
  const { isNextLineEmpty } = prettier.util;
3064
+ /**
3065
+ * If the decorators for that node should try to be kept inline.
3066
+ */
3067
+ const DecoratorsTryInline = {
3068
+ modelProperty: true,
3069
+ enumMember: true,
3070
+ unionVariant: true,
3071
+ };
3035
3072
  const typespecPrinter = {
3036
3073
  print: printTypeSpec,
3037
3074
  canAttachComment: canAttachComment,
@@ -3042,10 +3079,11 @@ function printTypeSpec(
3042
3079
  // Path to the AST node to print
3043
3080
  path, options, print) {
3044
3081
  const node = path.getValue();
3082
+ const docs = printDocComments(path, options, print);
3045
3083
  const directives = shouldPrintDirective(node) ? printDirectives(path, options, print) : "";
3046
3084
  const printedNode = printNode(path, options, print);
3047
3085
  const value = needsParens(path) ? ["(", printedNode, ")"] : printedNode;
3048
- const parts = [directives, value];
3086
+ const parts = [docs, directives, value];
3049
3087
  if (node.kind === SyntaxKind.TypeSpecScript) {
3050
3088
  // For TypeSpecScript(root of typespec document) we had a new line at the end.
3051
3089
  // This must be done here so the hardline entry can be the last item of the doc array returned by the printer
@@ -3062,7 +3100,6 @@ function printNode(
3062
3100
  // Path to the AST node to print
3063
3101
  path, options, print) {
3064
3102
  const node = path.getValue();
3065
- printDirectives(path, options, print);
3066
3103
  switch (node.kind) {
3067
3104
  // Root
3068
3105
  case SyntaxKind.TypeSpecScript:
@@ -3205,6 +3242,7 @@ path, options, print) {
3205
3242
  case SyntaxKind.Return:
3206
3243
  return printReturnExpression(path, options, print);
3207
3244
  case SyntaxKind.Doc:
3245
+ return printDoc(path, options);
3208
3246
  case SyntaxKind.DocText:
3209
3247
  case SyntaxKind.DocParamTag:
3210
3248
  case SyntaxKind.DocTemplateTag:
@@ -3258,6 +3296,12 @@ function canAttachComment(node) {
3258
3296
  kind !== SyntaxKind.LineComment &&
3259
3297
  kind !== SyntaxKind.BlockComment &&
3260
3298
  kind !== SyntaxKind.EmptyStatement &&
3299
+ kind !== SyntaxKind.Doc &&
3300
+ kind !== SyntaxKind.DocParamTag &&
3301
+ kind !== SyntaxKind.DocReturnsTag &&
3302
+ kind !== SyntaxKind.DocTemplateTag &&
3303
+ kind !== SyntaxKind.DocText &&
3304
+ kind !== SyntaxKind.DocUnknownTag &&
3261
3305
  !(node.flags & 8 /* NodeFlags.Synthetic */));
3262
3306
  }
3263
3307
  function printComment(commentPath, options) {
@@ -3275,11 +3319,10 @@ function printComment(commentPath, options) {
3275
3319
  function printBlockComment(commentPath, options) {
3276
3320
  const comment = commentPath.getValue();
3277
3321
  const rawComment = options.originalText.slice(comment.pos + 2, comment.end - 2);
3278
- if (isIndentableBlockComment(rawComment)) {
3279
- const printed = printIndentableBlockComment(rawComment);
3280
- return printed;
3281
- }
3282
- return ["/*", rawComment, "*/"];
3322
+ const printed = isIndentableBlockComment(rawComment)
3323
+ ? printIndentableBlockCommentContent(rawComment)
3324
+ : rawComment;
3325
+ return ["/*", printed, "*/"];
3283
3326
  }
3284
3327
  function isIndentableBlockComment(rawComment) {
3285
3328
  // If the comment has multiple lines and every line starts with a star
@@ -3289,28 +3332,42 @@ function isIndentableBlockComment(rawComment) {
3289
3332
  const lines = `*${rawComment}*`.split("\n");
3290
3333
  return lines.length > 1 && lines.every((line) => line.trim()[0] === "*");
3291
3334
  }
3292
- function printIndentableBlockComment(rawComment) {
3335
+ function printIndentableBlockCommentContent(rawComment) {
3293
3336
  const lines = rawComment.split("\n");
3294
3337
  return [
3295
- "/*",
3296
3338
  join(hardline, lines.map((line, index) => index === 0
3297
3339
  ? line.trimEnd()
3298
3340
  : " " + (index < lines.length - 1 ? line.trim() : line.trimStart()))),
3299
- "*/",
3300
3341
  ];
3301
3342
  }
3343
+ /** Print a doc comment. */
3344
+ function printDoc(path, options, print) {
3345
+ const node = path.getValue();
3346
+ const rawComment = options.originalText.slice(node.pos + 3, node.end - 2);
3347
+ const printed = isIndentableBlockComment(rawComment)
3348
+ ? printIndentableBlockCommentContent(rawComment)
3349
+ : rawComment.includes("\n")
3350
+ ? rawComment
3351
+ : ` ${rawComment.trim()} `;
3352
+ return ["/**", printed, "*/"];
3353
+ }
3302
3354
  function printDecorators(path, options, print, { tryInline }) {
3303
3355
  const node = path.getValue();
3304
3356
  if (node.decorators.length === 0) {
3305
3357
  return { decorators: "", multiline: false };
3306
3358
  }
3307
- const shouldBreak = !tryInline || node.decorators.length >= 3 || hasNewlineBetweenOrAfterDecorators(node, options);
3359
+ const shouldBreak = shouldDecoratorBreakLine(path, options, { tryInline });
3308
3360
  const decorators = path.map((x) => [print(x), ifBreak(line, " ")], "decorators");
3309
3361
  return {
3310
3362
  decorators: group([shouldBreak ? breakParent : "", decorators]),
3311
3363
  multiline: shouldBreak,
3312
3364
  };
3313
3365
  }
3366
+ /** Check if the decorators of the given node should be broken in sparate line */
3367
+ function shouldDecoratorBreakLine(path, options, { tryInline }) {
3368
+ const node = path.getValue();
3369
+ return (!tryInline || node.decorators.length >= 3 || hasNewlineBetweenOrAfterDecorators(node, options));
3370
+ }
3314
3371
  /**
3315
3372
  * Check if there is already new lines in between the decorators of the node.
3316
3373
  */
@@ -3338,6 +3395,14 @@ function printAugmentDecoratorArgs(path, options, print) {
3338
3395
  ")",
3339
3396
  ];
3340
3397
  }
3398
+ function printDocComments(path, options, print) {
3399
+ const node = path.getValue();
3400
+ if (node.docs === undefined || node.docs.length === 0) {
3401
+ return "";
3402
+ }
3403
+ const docs = path.map((x) => [print(x), line], "docs");
3404
+ return group([...docs, breakParent]);
3405
+ }
3341
3406
  function printDirectives(path, options, print) {
3342
3407
  const node = path.getValue();
3343
3408
  if (node.directives === undefined || node.directives.length === 0) {
@@ -3409,7 +3474,9 @@ function printEnumMember(path, options, print) {
3409
3474
  const node = path.getValue();
3410
3475
  const id = path.call(print, "id");
3411
3476
  const value = node.value ? [": ", path.call(print, "value")] : "";
3412
- const { decorators, multiline } = printDecorators(path, options, print, { tryInline: true });
3477
+ const { decorators, multiline } = printDecorators(path, options, print, {
3478
+ tryInline: DecoratorsTryInline.enumMember,
3479
+ });
3413
3480
  const propertyIndex = path.stack[path.stack.length - 2];
3414
3481
  const isNotFirst = typeof propertyIndex === "number" && propertyIndex > 0;
3415
3482
  return [multiline && isNotFirst ? hardline : "", decorators, id, value];
@@ -3441,7 +3508,9 @@ function printUnionVariantsBlock(path, options, print) {
3441
3508
  function printUnionVariant(path, options, print) {
3442
3509
  const id = path.call(print, "id");
3443
3510
  const value = [": ", path.call(print, "value")];
3444
- const { decorators } = printDecorators(path, options, print, { tryInline: true });
3511
+ const { decorators } = printDecorators(path, options, print, {
3512
+ tryInline: DecoratorsTryInline.unionVariant,
3513
+ });
3445
3514
  return [decorators, id, value];
3446
3515
  }
3447
3516
  function printInterfaceStatement(path, options, print) {
@@ -3565,18 +3634,19 @@ function printTuple(path, options, print) {
3565
3634
  }
3566
3635
  function printMemberExpression(path, options, print) {
3567
3636
  const node = path.getValue();
3568
- return [node.base ? [path.call(print, "base"), "."] : "", path.call(print, "id")];
3637
+ return [node.base ? [path.call(print, "base"), node.selector] : "", path.call(print, "id")];
3569
3638
  }
3570
3639
  function printModelExpression(path, options, print) {
3571
3640
  const inBlock = isModelExpressionInBlock(path);
3641
+ const node = path.getValue();
3572
3642
  if (inBlock) {
3573
3643
  return group(printModelPropertiesBlock(path, options, print));
3574
3644
  }
3575
3645
  else {
3576
- return group([
3577
- indent(join(", ", path.map((arg) => [softline, print(arg)], "properties"))),
3578
- softline,
3579
- ]);
3646
+ const properties = node.properties.length === 0
3647
+ ? ""
3648
+ : indent(joinPropertiesInBlock(path, options, print, ifBreak(",", ", "), softline));
3649
+ return group([properties, softline]);
3580
3650
  }
3581
3651
  }
3582
3652
  function printModelStatement(path, options, print) {
@@ -3610,16 +3680,54 @@ function printModelPropertiesBlock(path, options, print) {
3610
3680
  const tryInline = ((_a = path.getParentNode()) === null || _a === void 0 ? void 0 : _a.kind) === SyntaxKind.TemplateParameterDeclaration;
3611
3681
  const lineDoc = tryInline ? softline : hardline;
3612
3682
  const seperator = isModelAValue(path) ? "," : ";";
3613
- const body = [
3614
- lineDoc,
3615
- join([seperator, lineDoc], path.map((x) => [print(x)], "properties")),
3616
- hasProperties ? ifBreak(seperator) : "",
3617
- ];
3683
+ const body = [joinPropertiesInBlock(path, options, print, seperator, lineDoc)];
3618
3684
  if (nodeHasComments) {
3619
3685
  body.push(printDanglingComments(path, options, { sameIndent: true }));
3620
3686
  }
3621
3687
  return group(["{", indent(body), lineDoc, "}"]);
3622
3688
  }
3689
+ function joinPropertiesInBlock(path, options, print, separator, regularLine) {
3690
+ const doc = [regularLine];
3691
+ const propertyContainerNode = path.getValue();
3692
+ let newLineBeforeNextProp = false;
3693
+ path.each((item, propertyIndex) => {
3694
+ const isFirst = propertyIndex === 0;
3695
+ const isLast = propertyIndex === propertyContainerNode.properties.length - 1;
3696
+ const shouldWrapInNewLines = shouldWrapPropertyInNewLines(item, options);
3697
+ if ((newLineBeforeNextProp || shouldWrapInNewLines) && !isFirst) {
3698
+ doc.push(hardline);
3699
+ newLineBeforeNextProp = false;
3700
+ }
3701
+ doc.push(print(item));
3702
+ if (isLast) {
3703
+ doc.push(ifBreak(separator));
3704
+ }
3705
+ else {
3706
+ doc.push(separator);
3707
+ doc.push(regularLine);
3708
+ if (shouldWrapInNewLines) {
3709
+ newLineBeforeNextProp = true;
3710
+ }
3711
+ }
3712
+ }, "properties");
3713
+ return doc;
3714
+ }
3715
+ /**
3716
+ * Check if property item (PropertyNode, SpreadProperty) should be wrapped in new lines.
3717
+ * It can be wrapped for the following reasons:
3718
+ * - has decorators on lines above
3719
+ * - has leading comments
3720
+ */
3721
+ function shouldWrapPropertyInNewLines(path, options) {
3722
+ var _a;
3723
+ const node = path.getValue();
3724
+ return (((node.kind === SyntaxKind.ModelProperty || node.kind === SyntaxKind.ProjectionModelProperty) &&
3725
+ shouldDecoratorBreakLine(path, options, {
3726
+ tryInline: DecoratorsTryInline.modelProperty,
3727
+ })) ||
3728
+ hasComments(node, CommentCheckFlags.Leading) ||
3729
+ (node.docs && ((_a = node.docs) === null || _a === void 0 ? void 0 : _a.length) > 0));
3730
+ }
3623
3731
  /**
3624
3732
  * Figure out if this model is being used as a definition or value.
3625
3733
  * @returns true if the model is used as a value(e.g. decorator value), false if it is used as a model definition.
@@ -3641,14 +3749,11 @@ function isModelAValue(path) {
3641
3749
  }
3642
3750
  function printModelProperty(path, options, print) {
3643
3751
  const node = path.getValue();
3644
- const propertyIndex = path.stack[path.stack.length - 2];
3645
- const isNotFirst = typeof propertyIndex === "number" && propertyIndex > 0;
3646
- const { decorators, multiline } = printDecorators(path, options, print, {
3647
- tryInline: true,
3752
+ const { decorators } = printDecorators(path, options, print, {
3753
+ tryInline: DecoratorsTryInline.modelProperty,
3648
3754
  });
3649
3755
  const id = printIdentifier(node.id);
3650
3756
  return [
3651
- multiline && isNotFirst ? hardline : "",
3652
3757
  printDirectives(path, options, print),
3653
3758
  decorators,
3654
3759
  id,
@@ -4243,15 +4348,42 @@ function createParser(code, options = {}) {
4243
4348
  ...finishNode(0),
4244
4349
  };
4245
4350
  }
4351
+ /** Try to parse doc comments, directives and decorators in any order. */
4352
+ function parseAnnotations({ skipParsingDocNodes } = {}) {
4353
+ const directives = [];
4354
+ const decorators = [];
4355
+ const docs = [];
4356
+ let pos = tokenPos();
4357
+ if (!skipParsingDocNodes) {
4358
+ const [firstPos, addedDocs] = parseDocList();
4359
+ pos = firstPos;
4360
+ for (const doc of addedDocs) {
4361
+ docs.push(doc);
4362
+ }
4363
+ }
4364
+ while (token() === Token.Hash || token() === Token.At) {
4365
+ if (token() === Token.Hash) {
4366
+ directives.push(parseDirectiveExpression());
4367
+ }
4368
+ else if (token() === Token.At) {
4369
+ decorators.push(parseDecoratorExpression());
4370
+ }
4371
+ if (!skipParsingDocNodes) {
4372
+ const [_, addedDocs] = parseDocList();
4373
+ for (const doc of addedDocs) {
4374
+ docs.push(doc);
4375
+ }
4376
+ }
4377
+ }
4378
+ return { pos, docs, directives, decorators };
4379
+ }
4246
4380
  function parseTypeSpecScriptItemList() {
4247
4381
  const stmts = [];
4248
4382
  let seenBlocklessNs = false;
4249
4383
  let seenDecl = false;
4250
4384
  let seenUsing = false;
4251
4385
  while (token() !== Token.EndOfFile) {
4252
- const [pos, docs] = parseDocList();
4253
- const directives = parseDirectiveList();
4254
- const decorators = parseDecoratorList();
4386
+ const { pos, docs, directives, decorators } = parseAnnotations();
4255
4387
  const tok = token();
4256
4388
  let item;
4257
4389
  switch (tok) {
@@ -4339,9 +4471,7 @@ function createParser(code, options = {}) {
4339
4471
  function parseStatementList() {
4340
4472
  const stmts = [];
4341
4473
  while (token() !== Token.CloseBrace) {
4342
- const [pos, docs] = parseDocList();
4343
- const directives = parseDirectiveList();
4344
- const decorators = parseDecoratorList();
4474
+ const { pos, docs, directives, decorators } = parseAnnotations();
4345
4475
  const tok = token();
4346
4476
  let item;
4347
4477
  switch (tok) {
@@ -5876,13 +6006,15 @@ function createParser(code, options = {}) {
5876
6006
  }
5877
6007
  const docs = [];
5878
6008
  for (const range of docRanges) {
5879
- const doc = parseRange(1 /* ParseMode.Doc */, innerDocRange(range), parseDoc);
6009
+ const doc = parseRange(1 /* ParseMode.Doc */, innerDocRange(range), () => parseDoc(range));
5880
6010
  docs.push(doc);
6011
+ if (range.comment) {
6012
+ mutate(range.comment).parsedAsDocs = true;
6013
+ }
5881
6014
  }
5882
6015
  return [docRanges[0].pos, docs];
5883
6016
  }
5884
- function parseDoc() {
5885
- const pos = tokenPos();
6017
+ function parseDoc(range) {
5886
6018
  const content = [];
5887
6019
  const tags = [];
5888
6020
  loop: while (true) {
@@ -5902,7 +6034,8 @@ function createParser(code, options = {}) {
5902
6034
  kind: SyntaxKind.Doc,
5903
6035
  content,
5904
6036
  tags,
5905
- ...finishNode(pos),
6037
+ ...finishNode(range.pos),
6038
+ end: range.end,
5906
6039
  };
5907
6040
  }
5908
6041
  function parseDocContent() {
@@ -5915,12 +6048,12 @@ function createParser(code, options = {}) {
5915
6048
  switch (token()) {
5916
6049
  case Token.DocCodeFenceDelimiter:
5917
6050
  inCodeFence = !inCodeFence;
5918
- nextDocToken();
6051
+ nextToken();
5919
6052
  break;
5920
6053
  case Token.NewLine:
5921
6054
  parts.push(source.substring(start, tokenPos()));
5922
6055
  parts.push("\n"); // normalize line endings
5923
- nextDocToken();
6056
+ nextToken();
5924
6057
  start = tokenPos();
5925
6058
  while (parseOptional(Token.Whitespace))
5926
6059
  ;
@@ -5936,10 +6069,10 @@ function createParser(code, options = {}) {
5936
6069
  if (!inCodeFence) {
5937
6070
  break loop;
5938
6071
  }
5939
- nextDocToken();
6072
+ nextToken();
5940
6073
  break;
5941
6074
  default:
5942
- nextDocToken();
6075
+ nextToken();
5943
6076
  break;
5944
6077
  }
5945
6078
  }
@@ -6000,7 +6133,7 @@ function createParser(code, options = {}) {
6000
6133
  let sv;
6001
6134
  if (token() === Token.Identifier) {
6002
6135
  sv = tokenValue();
6003
- nextDocToken();
6136
+ nextToken();
6004
6137
  }
6005
6138
  else {
6006
6139
  sv = "";
@@ -6043,19 +6176,22 @@ function createParser(code, options = {}) {
6043
6176
  if (!newLineIsTrivia && token() === Token.NewLine) {
6044
6177
  break;
6045
6178
  }
6046
- if (tokenFlags() & TokenFlags.DocComment) {
6047
- docRanges.push({
6048
- pos: tokenPos(),
6049
- end: tokenEnd(),
6050
- });
6051
- }
6179
+ let comment = undefined;
6052
6180
  if (options.comments && isComment(token())) {
6053
- comments.push({
6181
+ comment = {
6054
6182
  kind: token() === Token.SingleLineComment
6055
6183
  ? SyntaxKind.LineComment
6056
6184
  : SyntaxKind.BlockComment,
6057
6185
  pos: tokenPos(),
6058
6186
  end: tokenEnd(),
6187
+ };
6188
+ comments.push(comment);
6189
+ }
6190
+ if (tokenFlags() & TokenFlags.DocComment) {
6191
+ docRanges.push({
6192
+ pos: tokenPos(),
6193
+ end: tokenEnd(),
6194
+ comment: comment,
6059
6195
  });
6060
6196
  }
6061
6197
  }
@@ -6111,13 +6247,9 @@ function createParser(code, options = {}) {
6111
6247
  }
6112
6248
  const items = [];
6113
6249
  while (true) {
6114
- let pos = tokenPos();
6115
- let docs;
6116
- if (!kind.invalidAnnotationTarget) {
6117
- [pos, docs] = parseDocList();
6118
- }
6119
- const directives = parseDirectiveList();
6120
- const decorators = parseDecoratorList();
6250
+ const { pos, docs, directives, decorators } = parseAnnotations({
6251
+ skipParsingDocNodes: Boolean(kind.invalidAnnotationTarget),
6252
+ });
6121
6253
  if (kind.invalidAnnotationTarget) {
6122
6254
  reportInvalidDecorators(decorators, kind.invalidAnnotationTarget);
6123
6255
  reportInvalidDirective(directives, kind.invalidAnnotationTarget);
@@ -6598,12 +6730,14 @@ function isBlocklessNamespace(node) {
6598
6730
  }
6599
6731
 
6600
6732
  function parse(text, parsers, opts) {
6601
- const result = parse$1(text, { comments: true, docs: false });
6733
+ const result = parse$1(text, { comments: true, docs: true });
6602
6734
  flattenNamespaces(result);
6603
6735
  const errors = result.parseDiagnostics.filter((x) => x.severity === "error");
6604
6736
  if (errors.length > 0 && !result.printable) {
6605
6737
  throw new PrettierParserError(errors[0]);
6606
6738
  }
6739
+ // Remove doc comments as those are handled directly.
6740
+ mutate(result).comments = result.comments.filter((x) => !(x.kind === SyntaxKind.BlockComment && x.parsedAsDocs));
6607
6741
  return result;
6608
6742
  }
6609
6743
  /**