@contractkit/prettier-plugin 0.12.0 → 0.12.2

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.
@@ -21,6 +21,7 @@ const I4 = INDENT.repeat(4);
21
21
  // ─── Orphan comment helpers ──────────────────────────────────────────────────
22
22
 
23
23
  type CommentEntry = { line: number; text: string };
24
+ /** A run of consecutive-line orphan comments, keyed by the source line it starts on. */
24
25
  export type CommentBlock = { startLine: number; lines: string[] };
25
26
 
26
27
  /** Group sorted orphan comment entries into consecutive-line blocks. */
@@ -52,6 +53,14 @@ export function flushBlocks(out: string[], blocks: CommentBlock[], idx: { value:
52
53
 
53
54
  // ─── Route ───────────────────────────────────────────────────────────────────
54
55
 
56
+ /**
57
+ * Render an `operation` route body from its `path: {` onward (the `operation` keyword and
58
+ * any modifier are prepended by the caller). Emits the params/security blocks and each HTTP
59
+ * operation, interleaving orphan comment `blocks` at their original source positions — `idx`
60
+ * tracks how far through `blocks` we've consumed, and `nextRouteStart` bounds the flush to
61
+ * comments before the following route. Any `route.trailingComments` (comments after the last
62
+ * operation, before `}`) are emitted before the closing brace so they round-trip.
63
+ */
55
64
  export function printRoute(route: OpRouteNode, blocks: CommentBlock[], idx: { value: number }, nextRouteStart: number): string {
56
65
  const lines: string[] = [];
57
66
  const commentSuffix = route.description ? ` # ${route.description}` : '';
@@ -74,6 +83,11 @@ export function printRoute(route: OpRouteNode, blocks: CommentBlock[], idx: { va
74
83
  // Flush comment blocks between last operation and the next route
75
84
  flushBlocks(lines, blocks, idx, nextRouteStart, I1);
76
85
 
86
+ // Trailing/orphan comments after the last operation, before the closing brace.
87
+ for (const comment of route.trailingComments ?? []) {
88
+ lines.push(`${I1}# ${comment}`);
89
+ }
90
+
77
91
  lines.push('}');
78
92
  return lines.join('\n');
79
93
  }
package/src/print-type.ts CHANGED
@@ -105,9 +105,15 @@ export function printField(field: FieldNode, indent: string, printWidth: number
105
105
  return fullLine;
106
106
  }
107
107
 
108
- /** Print inline-object fields expanded (used when an inline brace object trails a type alias). */
108
+ /** Print inline-object fields expanded (used when an inline brace object trails a type alias).
109
+ * Any `trailingComments` (comments after the last field, before `}`) are emitted as indented
110
+ * `# text` lines after the fields, matching how model bodies round-trip trailing comments. */
109
111
  export function printInlineObjectExpanded(obj: InlineObjectTypeNode, indent: string, printWidth: number = 80): string[] {
110
- return obj.fields.map(f => printField(f, indent, printWidth));
112
+ const lines = obj.fields.map(f => printField(f, indent, printWidth));
113
+ for (const comment of obj.trailingComments ?? []) {
114
+ lines.push(`${indent}# ${comment}`);
115
+ }
116
+ return lines;
111
117
  }
112
118
 
113
119
  // ─── Helpers ────────────────────────────────────────────────────────────────
@@ -800,3 +800,242 @@ contract M: {
800
800
  expect(roundTrip(source)).toBe(source);
801
801
  });
802
802
  });
803
+
804
+ describe('printCk — trailing/orphan comments (round-trip)', () => {
805
+ function roundTrip(source: string): string {
806
+ const diag = new DiagnosticCollector();
807
+ const ast = parseCk(source, 'test.ck', diag);
808
+ expect(diag.hasErrors()).toBe(false);
809
+ return printCk(ast);
810
+ }
811
+
812
+ it('preserves a comment on the last line of a contract body', () => {
813
+ const source = `\
814
+ contract Payment: {
815
+ id: uuid
816
+ # more fields to come
817
+ }
818
+ `;
819
+ const out = roundTrip(source);
820
+ expect(out).toContain('# more fields to come');
821
+ // Comment sits on its own line after the last field, before the closing brace.
822
+ expect(out).toBe(source);
823
+ // Idempotent: formatting the output again yields the same result.
824
+ expect(roundTrip(out)).toBe(out);
825
+ });
826
+
827
+ it('preserves multiple trailing comments in a contract body', () => {
828
+ const source = `\
829
+ contract Payment: {
830
+ id: uuid
831
+ # note one
832
+ # note two
833
+ }
834
+ `;
835
+ const out = roundTrip(source);
836
+ expect(out).toContain('# note one');
837
+ expect(out).toContain('# note two');
838
+ expect(out).toBe(source);
839
+ expect(roundTrip(out)).toBe(out);
840
+ });
841
+
842
+ it('preserves a comment as the only content of a contract body', () => {
843
+ const source = `\
844
+ contract Empty: {
845
+ # nothing here yet
846
+ }
847
+ `;
848
+ const out = roundTrip(source);
849
+ expect(out).toContain('# nothing here yet');
850
+ expect(out).toBe(source);
851
+ expect(roundTrip(out)).toBe(out);
852
+ });
853
+
854
+ it('preserves a comment on the last line of an operation/route body', () => {
855
+ const source = `\
856
+ operation /users: {
857
+ get: {
858
+ }
859
+ # TODO: add post
860
+ }
861
+ `;
862
+ const out = roundTrip(source);
863
+ expect(out).toContain('# TODO: add post');
864
+ expect(out).toBe(source);
865
+ expect(roundTrip(out)).toBe(out);
866
+ });
867
+
868
+ it('does not double-emit a header inline comment on a contract with no fields', () => {
869
+ const source = `\
870
+ contract Empty: { # nothing here yet
871
+ }
872
+ `;
873
+ const out = roundTrip(source);
874
+ // The inline header comment must survive exactly once, not be duplicated on a trailing line.
875
+ expect(out.match(/# nothing here yet/g)?.length).toBe(1);
876
+ expect(roundTrip(out)).toBe(out);
877
+ });
878
+ });
879
+
880
+ describe('printCk — inline object trailing comments (round-trip)', () => {
881
+ function roundTrip(source: string): string {
882
+ const diag = new DiagnosticCollector();
883
+ const ast = parseCk(source, 'test.ck', diag);
884
+ expect(diag.hasErrors()).toBe(false);
885
+ return printCk(ast);
886
+ }
887
+
888
+ it('preserves a trailing comment as the last inner item of an inline object type', () => {
889
+ const source = `\
890
+ contract Foo: {
891
+ bar: {
892
+ a: string
893
+ # trailing note
894
+ }
895
+ }
896
+ `;
897
+ const out = roundTrip(source);
898
+ expect(out).toContain('# trailing note');
899
+ expect(out).toBe(source);
900
+ expect(roundTrip(out)).toBe(out);
901
+ });
902
+
903
+ it('preserves multiple trailing comments in an inline object type', () => {
904
+ const source = `\
905
+ contract Foo: {
906
+ bar: {
907
+ a: string
908
+ # note one
909
+ # note two
910
+ }
911
+ }
912
+ `;
913
+ const out = roundTrip(source);
914
+ expect(out).toContain('# note one');
915
+ expect(out).toContain('# note two');
916
+ expect(out).toBe(source);
917
+ expect(roundTrip(out)).toBe(out);
918
+ });
919
+
920
+ it('preserves a trailing comment as the only inner item of an inline object type', () => {
921
+ const source = `\
922
+ contract Foo: {
923
+ bar: {
924
+ # placeholder
925
+ }
926
+ }
927
+ `;
928
+ const out = roundTrip(source);
929
+ expect(out).toContain('# placeholder');
930
+ expect(out).toBe(source);
931
+ expect(roundTrip(out)).toBe(out);
932
+ });
933
+
934
+ it('still attaches an inline comment on an inline field (no regression)', () => {
935
+ const source = `\
936
+ contract Foo: {
937
+ bar: {
938
+ a: string # about a
939
+ b: int
940
+ }
941
+ }
942
+ `;
943
+ const out = roundTrip(source);
944
+ expect(out).toContain('a: string # about a');
945
+ expect(out).toBe(source);
946
+ expect(roundTrip(out)).toBe(out);
947
+ });
948
+ });
949
+
950
+ describe('printCk — options block sub-block comments (round-trip)', () => {
951
+ function roundTrip(source: string): string {
952
+ const diag = new DiagnosticCollector();
953
+ const ast = parseCk(source, 'test.ck', diag);
954
+ expect(diag.hasErrors()).toBe(false);
955
+ return printCk(ast);
956
+ }
957
+
958
+ it('preserves a leading comment on a keys entry', () => {
959
+ const source = `\
960
+ options {
961
+ keys: {
962
+ # the api area
963
+ area: payments
964
+ version: v2
965
+ }
966
+ }
967
+ `;
968
+ const out = roundTrip(source);
969
+ expect(out).toContain('# the api area');
970
+ expect(out).toBe(source);
971
+ expect(roundTrip(out)).toBe(out);
972
+ });
973
+
974
+ it('preserves a trailing comment at the end of the keys sub-block', () => {
975
+ const source = `\
976
+ options {
977
+ keys: {
978
+ area: payments
979
+ # more keys to come
980
+ }
981
+ }
982
+ `;
983
+ const out = roundTrip(source);
984
+ expect(out).toContain('# more keys to come');
985
+ expect(out).toBe(source);
986
+ expect(roundTrip(out)).toBe(out);
987
+ });
988
+
989
+ it('preserves a leading comment on a services entry', () => {
990
+ const source = `\
991
+ options {
992
+ services: {
993
+ # payments backend
994
+ PaymentsService: "#src/services/payments.service.js"
995
+ }
996
+ }
997
+ `;
998
+ const out = roundTrip(source);
999
+ expect(out).toContain('# payments backend');
1000
+ expect(out).toBe(source);
1001
+ expect(roundTrip(out)).toBe(out);
1002
+ });
1003
+
1004
+ it('preserves a comment at the end of the last options sub-block (end of options block)', () => {
1005
+ const source = `\
1006
+ options {
1007
+ keys: {
1008
+ area: payments
1009
+ }
1010
+ services: {
1011
+ PaymentsService: "#src/services/payments.service.js"
1012
+ # end of options
1013
+ }
1014
+ }
1015
+ `;
1016
+ const out = roundTrip(source);
1017
+ expect(out).toContain('# end of options');
1018
+ expect(out).toBe(source);
1019
+ expect(roundTrip(out)).toBe(out);
1020
+ });
1021
+
1022
+ it('preserves both leading and trailing comments across keys and services', () => {
1023
+ const source = `\
1024
+ options {
1025
+ keys: {
1026
+ # area comment
1027
+ area: payments
1028
+ # trailing keys
1029
+ }
1030
+ services: {
1031
+ # service comment
1032
+ PaymentsService: "#src/services/payments.service.js"
1033
+ # trailing services
1034
+ }
1035
+ }
1036
+ `;
1037
+ const out = roundTrip(source);
1038
+ expect(out).toBe(source);
1039
+ expect(roundTrip(out)).toBe(out);
1040
+ });
1041
+ });
package/coverage/base.css DELETED
@@ -1,224 +0,0 @@
1
- body, html {
2
- margin:0; padding: 0;
3
- height: 100%;
4
- }
5
- body {
6
- font-family: Helvetica Neue, Helvetica, Arial;
7
- font-size: 14px;
8
- color:#333;
9
- }
10
- .small { font-size: 12px; }
11
- *, *:after, *:before {
12
- -webkit-box-sizing:border-box;
13
- -moz-box-sizing:border-box;
14
- box-sizing:border-box;
15
- }
16
- h1 { font-size: 20px; margin: 0;}
17
- h2 { font-size: 14px; }
18
- pre {
19
- font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
- margin: 0;
21
- padding: 0;
22
- -moz-tab-size: 2;
23
- -o-tab-size: 2;
24
- tab-size: 2;
25
- }
26
- a { color:#0074D9; text-decoration:none; }
27
- a:hover { text-decoration:underline; }
28
- .strong { font-weight: bold; }
29
- .space-top1 { padding: 10px 0 0 0; }
30
- .pad2y { padding: 20px 0; }
31
- .pad1y { padding: 10px 0; }
32
- .pad2x { padding: 0 20px; }
33
- .pad2 { padding: 20px; }
34
- .pad1 { padding: 10px; }
35
- .space-left2 { padding-left:55px; }
36
- .space-right2 { padding-right:20px; }
37
- .center { text-align:center; }
38
- .clearfix { display:block; }
39
- .clearfix:after {
40
- content:'';
41
- display:block;
42
- height:0;
43
- clear:both;
44
- visibility:hidden;
45
- }
46
- .fl { float: left; }
47
- @media only screen and (max-width:640px) {
48
- .col3 { width:100%; max-width:100%; }
49
- .hide-mobile { display:none!important; }
50
- }
51
-
52
- .quiet {
53
- color: #7f7f7f;
54
- color: rgba(0,0,0,0.5);
55
- }
56
- .quiet a { opacity: 0.7; }
57
-
58
- .fraction {
59
- font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
- font-size: 10px;
61
- color: #555;
62
- background: #E8E8E8;
63
- padding: 4px 5px;
64
- border-radius: 3px;
65
- vertical-align: middle;
66
- }
67
-
68
- div.path a:link, div.path a:visited { color: #333; }
69
- table.coverage {
70
- border-collapse: collapse;
71
- margin: 10px 0 0 0;
72
- padding: 0;
73
- }
74
-
75
- table.coverage td {
76
- margin: 0;
77
- padding: 0;
78
- vertical-align: top;
79
- }
80
- table.coverage td.line-count {
81
- text-align: right;
82
- padding: 0 5px 0 20px;
83
- }
84
- table.coverage td.line-coverage {
85
- text-align: right;
86
- padding-right: 10px;
87
- min-width:20px;
88
- }
89
-
90
- table.coverage td span.cline-any {
91
- display: inline-block;
92
- padding: 0 5px;
93
- width: 100%;
94
- }
95
- .missing-if-branch {
96
- display: inline-block;
97
- margin-right: 5px;
98
- border-radius: 3px;
99
- position: relative;
100
- padding: 0 4px;
101
- background: #333;
102
- color: yellow;
103
- }
104
-
105
- .skip-if-branch {
106
- display: none;
107
- margin-right: 10px;
108
- position: relative;
109
- padding: 0 4px;
110
- background: #ccc;
111
- color: white;
112
- }
113
- .missing-if-branch .typ, .skip-if-branch .typ {
114
- color: inherit !important;
115
- }
116
- .coverage-summary {
117
- border-collapse: collapse;
118
- width: 100%;
119
- }
120
- .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
- .keyline-all { border: 1px solid #ddd; }
122
- .coverage-summary td, .coverage-summary th { padding: 10px; }
123
- .coverage-summary tbody { border: 1px solid #bbb; }
124
- .coverage-summary td { border-right: 1px solid #bbb; }
125
- .coverage-summary td:last-child { border-right: none; }
126
- .coverage-summary th {
127
- text-align: left;
128
- font-weight: normal;
129
- white-space: nowrap;
130
- }
131
- .coverage-summary th.file { border-right: none !important; }
132
- .coverage-summary th.pct { }
133
- .coverage-summary th.pic,
134
- .coverage-summary th.abs,
135
- .coverage-summary td.pct,
136
- .coverage-summary td.abs { text-align: right; }
137
- .coverage-summary td.file { white-space: nowrap; }
138
- .coverage-summary td.pic { min-width: 120px !important; }
139
- .coverage-summary tfoot td { }
140
-
141
- .coverage-summary .sorter {
142
- height: 10px;
143
- width: 7px;
144
- display: inline-block;
145
- margin-left: 0.5em;
146
- background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
- }
148
- .coverage-summary .sorted .sorter {
149
- background-position: 0 -20px;
150
- }
151
- .coverage-summary .sorted-desc .sorter {
152
- background-position: 0 -10px;
153
- }
154
- .status-line { height: 10px; }
155
- /* yellow */
156
- .cbranch-no { background: yellow !important; color: #111; }
157
- /* dark red */
158
- .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
- .low .chart { border:1px solid #C21F39 }
160
- .highlighted,
161
- .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
- background: #C21F39 !important;
163
- }
164
- /* medium red */
165
- .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
- /* light red */
167
- .low, .cline-no { background:#FCE1E5 }
168
- /* light green */
169
- .high, .cline-yes { background:rgb(230,245,208) }
170
- /* medium green */
171
- .cstat-yes { background:rgb(161,215,106) }
172
- /* dark green */
173
- .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
- .high .chart { border:1px solid rgb(77,146,33) }
175
- /* dark yellow (gold) */
176
- .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
- .medium .chart { border:1px solid #f9cd0b; }
178
- /* light yellow */
179
- .medium { background: #fff4c2; }
180
-
181
- .cstat-skip { background: #ddd; color: #111; }
182
- .fstat-skip { background: #ddd; color: #111 !important; }
183
- .cbranch-skip { background: #ddd !important; color: #111; }
184
-
185
- span.cline-neutral { background: #eaeaea; }
186
-
187
- .coverage-summary td.empty {
188
- opacity: .5;
189
- padding-top: 4px;
190
- padding-bottom: 4px;
191
- line-height: 1;
192
- color: #888;
193
- }
194
-
195
- .cover-fill, .cover-empty {
196
- display:inline-block;
197
- height: 12px;
198
- }
199
- .chart {
200
- line-height: 0;
201
- }
202
- .cover-empty {
203
- background: white;
204
- }
205
- .cover-full {
206
- border-right: none !important;
207
- }
208
- pre.prettyprint {
209
- border: none !important;
210
- padding: 0 !important;
211
- margin: 0 !important;
212
- }
213
- .com { color: #999 !important; }
214
- .ignore-none { color: #999; font-weight: normal; }
215
-
216
- .wrapper {
217
- min-height: 100%;
218
- height: auto !important;
219
- height: 100%;
220
- margin: 0 auto -48px;
221
- }
222
- .footer, .push {
223
- height: 48px;
224
- }
@@ -1,87 +0,0 @@
1
- /* eslint-disable */
2
- var jumpToCode = (function init() {
3
- // Classes of code we would like to highlight in the file view
4
- var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5
-
6
- // Elements to highlight in the file listing view
7
- var fileListingElements = ['td.pct.low'];
8
-
9
- // We don't want to select elements that are direct descendants of another match
10
- var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11
-
12
- // Selector that finds elements on the page to which we can jump
13
- var selector =
14
- fileListingElements.join(', ') +
15
- ', ' +
16
- notSelector +
17
- missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18
-
19
- // The NodeList of matching elements
20
- var missingCoverageElements = document.querySelectorAll(selector);
21
-
22
- var currentIndex;
23
-
24
- function toggleClass(index) {
25
- missingCoverageElements
26
- .item(currentIndex)
27
- .classList.remove('highlighted');
28
- missingCoverageElements.item(index).classList.add('highlighted');
29
- }
30
-
31
- function makeCurrent(index) {
32
- toggleClass(index);
33
- currentIndex = index;
34
- missingCoverageElements.item(index).scrollIntoView({
35
- behavior: 'smooth',
36
- block: 'center',
37
- inline: 'center'
38
- });
39
- }
40
-
41
- function goToPrevious() {
42
- var nextIndex = 0;
43
- if (typeof currentIndex !== 'number' || currentIndex === 0) {
44
- nextIndex = missingCoverageElements.length - 1;
45
- } else if (missingCoverageElements.length > 1) {
46
- nextIndex = currentIndex - 1;
47
- }
48
-
49
- makeCurrent(nextIndex);
50
- }
51
-
52
- function goToNext() {
53
- var nextIndex = 0;
54
-
55
- if (
56
- typeof currentIndex === 'number' &&
57
- currentIndex < missingCoverageElements.length - 1
58
- ) {
59
- nextIndex = currentIndex + 1;
60
- }
61
-
62
- makeCurrent(nextIndex);
63
- }
64
-
65
- return function jump(event) {
66
- if (
67
- document.getElementById('fileSearch') === document.activeElement &&
68
- document.activeElement != null
69
- ) {
70
- // if we're currently focused on the search input, we don't want to navigate
71
- return;
72
- }
73
-
74
- switch (event.which) {
75
- case 78: // n
76
- case 74: // j
77
- goToNext();
78
- break;
79
- case 66: // b
80
- case 75: // k
81
- case 80: // p
82
- goToPrevious();
83
- break;
84
- }
85
- };
86
- })();
87
- window.addEventListener('keydown', jumpToCode);