@contractkit/prettier-plugin 0.13.0 → 0.14.1
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/.turbo/turbo-build$colon$ci.log +5 -5
- package/.turbo/turbo-test$colon$ci.log +15 -13
- package/CHANGELOG.md +62 -0
- package/README.md +9 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -22
- package/dist/index.js.map +1 -1
- package/dist/print-ck.d.ts +5 -4
- package/dist/print-ck.d.ts.map +1 -1
- package/dist/print-operation.d.ts +5 -2
- package/dist/print-operation.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +17 -1
- package/src/print-ck.ts +35 -16
- package/src/print-operation.ts +57 -17
- package/tests/format-plugin.test.ts +42 -0
- package/tests/print-ck.test.ts +5 -3
- package/tests/round-trip.test.ts +426 -0
|
@@ -5,9 +5,9 @@ $ tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly
|
|
|
5
5
|
[34mCLI[39m tsup v8.5.1
|
|
6
6
|
[34mCLI[39m Target: esnext
|
|
7
7
|
[34mESM[39m Build start
|
|
8
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
9
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
10
|
-
[32mESM[39m ⚡️ Build success in
|
|
8
|
+
[32mESM[39m [1mdist/index.js [22m[32m27.08 KB[39m
|
|
9
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m69.36 KB[39m
|
|
10
|
+
[32mESM[39m ⚡️ Build success in 174ms
|
|
11
11
|
[34mDTS[39m Build start
|
|
12
|
-
[32mDTS[39m ⚡️ Build success in
|
|
13
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.
|
|
12
|
+
[32mDTS[39m ⚡️ Build success in 5639ms
|
|
13
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.65 KB[39m
|
|
@@ -3,24 +3,26 @@ $ vitest run --coverage
|
|
|
3
3
|
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.5 [39m[90m/home/runner/work/ContractKit/ContractKit/apps/prettier-plugin[39m
|
|
4
4
|
[2mCoverage enabled with [22m[33mv8[39m
|
|
5
5
|
|
|
6
|
-
[32m✓[39m tests/
|
|
7
|
-
[32m✓[39m tests/
|
|
8
|
-
|
|
9
|
-
[33m[2m✓[22m[39m
|
|
6
|
+
[32m✓[39m tests/format-plugin.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 136[2mms[22m[39m
|
|
7
|
+
[32m✓[39m tests/print-ck.test.ts [2m([22m[2m81 tests[22m[2m)[22m[33m 1049[2mms[22m[39m
|
|
8
|
+
[32m✓[39m tests/round-trip.test.ts [2m([22m[2m49 tests[22m[2m)[22m[33m 2210[2mms[22m[39m
|
|
9
|
+
[33m[2m✓[22m[39m formats test.ck to itself [33m 918[2mms[22m[39m
|
|
10
|
+
[33m[2m✓[22m[39m formatting is a fixed point for source #0 [33m 515[2mms[22m[39m
|
|
10
11
|
|
|
11
|
-
[2m Test Files [22m [1m[
|
|
12
|
-
[2m Tests [22m [1m[
|
|
13
|
-
[2m Start at [22m
|
|
14
|
-
[2m Duration [22m
|
|
12
|
+
[2m Test Files [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
13
|
+
[2m Tests [22m [1m[32m133 passed[39m[22m[90m (133)[39m
|
|
14
|
+
[2m Start at [22m 17:32:00
|
|
15
|
+
[2m Duration [22m 8.78s[2m (transform 4.30s, setup 0ms, import 15.52s, tests 3.40s, environment 1ms)[22m
|
|
15
16
|
|
|
16
17
|
[34m % [39m[2mCoverage report from [22m[33mv8[39m
|
|
17
18
|
-------------------|---------|----------|---------|---------|-------------------
|
|
18
19
|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
|
|
19
20
|
-------------------|---------|----------|---------|---------|-------------------
|
|
20
|
-
All files |
|
|
21
|
+
All files | 79.5 | 74.08 | 93.47 | 86.44 |
|
|
21
22
|
indent.ts | 100 | 100 | 100 | 100 |
|
|
22
|
-
|
|
23
|
-
print-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
index.ts | 81.81 | 100 | 100 | 80 | 47-48
|
|
24
|
+
print-ck.ts | 90.59 | 88.26 | 100 | 100 | ...57-158,172,176
|
|
25
|
+
print-contract.ts | 74.28 | 60.29 | 100 | 80 | 70-76,83
|
|
26
|
+
...t-operation.ts | 78.77 | 71.42 | 95 | 87.55 | ...63,380,388-399
|
|
27
|
+
print-type.ts | 69.56 | 64.42 | 81.81 | 72 | ...,45-61,148-151
|
|
26
28
|
-------------------|---------|----------|---------|---------|-------------------
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# @contractkit/prettier-plugin-contractkit
|
|
2
2
|
|
|
3
|
+
## 0.14.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ab69718: Stop the formatter deleting comments in and around a `security { }` block, and keep the trailing newline at end of file.
|
|
8
|
+
|
|
9
|
+
The identical bug in `response { }` was fixed in 0.14.0; this position was not covered. `SecurityBody_fields` skipped standalone comments outright, so a `#` run inside a security block never reached the AST and `pnpm format` deleted it with no warning — exactly the prose that records _why_ a policy floor sits where it does. Three more positions failed the same way and now round-trip:
|
|
10
|
+
- a comment run above a body key in an operation body (the common case being a note above the `security:` key, or above `security: none`, explaining why a verb overrides the file's floor);
|
|
11
|
+
- a comment run above a verb that also carries its own inline `# ...` doc comment — the inline comment won and the run above it was discarded;
|
|
12
|
+
- a comment left after the last key in an operation body, before the closing brace.
|
|
13
|
+
|
|
14
|
+
`SecurityPolicyLine` and `SecuritySignatureLine` no longer take a trailing `comment?`. Whitespace skipping in an Ohm syntactic rule crosses newlines, so that optional comment swallowed a standalone comment written on the _next_ line and re-emitted it as the policy's inline description — turning a note to the next contract author into generated SDK documentation. `SecurityBody_fields` now collects every comment and decides which one is inline by comparing source lines, the same way `FieldList` already does.
|
|
15
|
+
|
|
16
|
+
The same newline-crossing optional appeared on `ModelBody_alias`, where it was worse: it claimed the doc comment of the _next_ declaration. `contract Status: enum(a, b)` followed by a documented contract either lost that doc comment outright, or silently re-filed it as `Status`'s own description — which then flowed into generated SDK docs describing the wrong type. Declaration-level comments are now items in `Root` (`DeclItem`) rather than a `comment*` prefix on each declaration, so their placement is decided where the previous declaration's end line is known: same line as the declaration above → its inline description; directly above the next one → its doc comment; otherwise a standalone block. A comment after the last declaration in a file used to be a parse error and is now kept as `CkRootNode.trailingComments`.
|
|
17
|
+
|
|
18
|
+
Two field positions were lost the same way. A comment on a nested object's opening brace (`rp: { # The relying party`) was offered to the first field _inside_ the object and dropped when that field had its own comment; it now belongs to the field owning the brace. A trailing comment on a field whose type wraps over several lines — `enum(\n a,\n b\n) # note` — was compared against the line the field _started_ on, so it was filed as a standalone comment for the next field; it now compares against the line the field ends on.
|
|
19
|
+
|
|
20
|
+
`SecurityFields` gains `leadingComments` / `trailingComments`, `OpOperationNode` gains `bodyLeadingComments`, `bodyTrailingComments`, and `leadingComments`, and `CkRootNode` gains `trailingComments`. All are optional and additive, so codegen plugins are unaffected.
|
|
21
|
+
|
|
22
|
+
### `#` where it is not a comment
|
|
23
|
+
|
|
24
|
+
`nameText` ended a name at any `#`, so `name: Generate C# client` silently became `Generate C` and the rest vanished with no diagnostic. It now ends at whitespace-then-`#`, matching `optionsRawChar` — a bare `#` is data, and only ` #` opens a comment. That makes the two unquoted-text positions in the language consistent with each other. The TextMate `name-decl` pattern captured the whole rest of the line, colouring a trailing comment as part of the name; it now stops at the same boundary the parser does.
|
|
25
|
+
|
|
26
|
+
Note that a `name:` containing a bare `#` now parses to its full text, and the SDK method name is derived from `name:` — so a contract that was silently relying on the truncated value will see that method renamed. Only a name with a `#` in it is affected.
|
|
27
|
+
|
|
28
|
+
Separately, the prettier plugin trimmed the printed source and returned it without re-adding a terminator, so every formatted `.ck` file lost its trailing newline — fighting any editor or lint rule that wants one. The plugin now has end-to-end tests that go through prettier itself rather than calling `printCk` directly, which is the layer where that slipped through.
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [ab69718]
|
|
31
|
+
- @contractkit/core@0.26.0
|
|
32
|
+
|
|
33
|
+
## 0.14.0
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- 85d7566: Let an operation emit more than one status, and a status serve more than one content type.
|
|
38
|
+
|
|
39
|
+
A status code could previously declare only one mime — the parser warned `Duplicate response body` and dropped the rest — and the generated router pinned both `ctx.status` and `ctx.type` to whichever response happened to be listed first with a body. An endpoint serving several formats had to declare one lying mime and let browsers sniff, and a service had no way to say which status it produced.
|
|
40
|
+
|
|
41
|
+
A status now holds every declared `mime: Type` line (`OpResponseNode.bodies`). When there is more than one, the service picks at runtime and the router sets `ctx.type` from the returned `contentType`. When an operation produces more than one status, the service returns a union discriminated on `status` and the handler switches on it, so each status writes only its own headers, mime and body. Both SDKs mirror the router: the TypeScript and Python clients return a matching union, report which mime came back, and pass the non-2xx statuses they expect to the shared fetch so a declared `304` no longer surfaces as an error. `SdkError` now takes a body type parameter, and each operation exports a `…ErrorBody` alias for the statuses that stay on the throw path.
|
|
42
|
+
|
|
43
|
+
Which statuses the service produces is derived from the declaration: **a status is emitted if it has a block, or is 2xx.** An empty block (`304: {}`) says the service returns that status carrying nothing; a bare `304:` says it is documented and something else produces it. `404(documented): { … }` is the one modifier, forcing a block-carrying status back out.
|
|
44
|
+
|
|
45
|
+
Two long-standing bugs in the same area go with it. The formatter deleted any comment written inside a `response` block — above a status code, above a mime line, above a `headers:` block, or before a closing brace — so `pnpm format` silently threw away the notes explaining why a contract looks the way it does; all four positions now round-trip. And the generated router declared a `_ZodBinary` helper for a binary _response_ body, which is a plain `Buffer` annotation with no schema behind it, leaving an unused const that tripped `noUnusedLocals` downstream; helpers are now chosen from the code that was actually generated, the same way imports already were.
|
|
46
|
+
|
|
47
|
+
**If you are already on a pre-release version, two things change under you.** A contract that declares a body on an error status — `404: { application/json: Problem }` alongside a `200` — now returns it from the service instead of throwing, and the SDK return type becomes a union; add `(documented)` to that status to keep the previous behaviour. Contracts whose error statuses are bodyless (`400:`, `404:`) are unaffected. Anything reading the AST directly should move from `OpResponseNode.contentType`/`bodyType` to `bodies`, which replaces them.
|
|
48
|
+
|
|
49
|
+
- 90d19ee: Allow a comment above `options`, and a trailing comment on an options entry.
|
|
50
|
+
|
|
51
|
+
A `#` comment above a `contract` or `operation` has always been fine, but one above the `options` keyword was a parse error — a file header is a natural thing to write, and writing it broke the build. `OptionsBlock` now owns the leading `comment*`. It lives there rather than on `Root` deliberately: on `Root` its greedy match would swallow the doc comment above a `contract` in a file that has no options block at all.
|
|
52
|
+
|
|
53
|
+
A trailing comment on a `keys`/`services` entry was worse than unsupported: it was swallowed into the value, and because an unquoted value ends at the first `}`, a comment containing one — `# interpolated as {{area}}` — closed the block early and silently mis-parsed the rest of the file. The value now ends at whitespace-then-`#`, and the comment is retained so the formatter round-trips it. A `#` with no space before it still belongs to the value, so the unquoted subpath form (`PetService: #modules/pet/pet.service.js`) is unaffected.
|
|
54
|
+
|
|
55
|
+
The TextMate grammar had the matching gap: its unquoted-value pattern accepted identifiers only, so an unquoted subpath fell through to the comment pattern and was coloured as though the parser ignored it.
|
|
56
|
+
|
|
57
|
+
An unquoted value also stays unquoted. The AST now records which entries were authored bare, so the formatter reproduces that choice instead of normalizing every value to the quoted form. The flag is formatting-only — both forms parse to the same string — and a value that could not be read back bare is still quoted, which covers values built programmatically rather than parsed.
|
|
58
|
+
|
|
59
|
+
### Patch Changes
|
|
60
|
+
|
|
61
|
+
- Updated dependencies [85d7566]
|
|
62
|
+
- Updated dependencies [90d19ee]
|
|
63
|
+
- @contractkit/core@0.25.0
|
|
64
|
+
|
|
3
65
|
## 0.13.0
|
|
4
66
|
|
|
5
67
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -51,7 +51,15 @@ Formatting a well-formed `.ck` file leaves it byte-identical. The formatter deli
|
|
|
51
51
|
- **Comment placement.** A `#` block separated from the declaration below it by a blank line stays a standalone divider; one directly above becomes that declaration's doc comment and is re-emitted above it, not folded onto the header line. A comment written inline (`contract Pet: { # ...`) stays inline.
|
|
52
52
|
- **Operation body key order.** `sdk` before `service` stays that way; the printer never sorts a user's keys into a canonical order.
|
|
53
53
|
- **Blank lines** between operations inside a route.
|
|
54
|
-
- **Single-line response blocks.** `200: { application/json: Pet }` is not expanded, and an expanded block is not collapsed.
|
|
54
|
+
- **Single-line response blocks.** `200: { application/json: Pet }` is not expanded, and an expanded block is not collapsed. A status declaring several mimes on one line stays on one line too.
|
|
55
|
+
- **Comments inside a `response` block**, in all four positions: above a status code, above a `mime: Type` line, above a `headers:` block, and before either closing brace.
|
|
56
|
+
- **An empty status block.** `304: {}` and `304:` mean different things to codegen — the first says the service returns that status with no body — so neither is normalized into the other.
|
|
57
|
+
- **Comments in the `options` block**, including a header comment above the `options` keyword and a trailing comment on a `keys`/`services` entry.
|
|
58
|
+
- **Comments in and around a `security { }` block**: inside it above or below `policy:`, above a `security:` key on a verb, and above a `security: none` line. This is where the rationale for a policy floor is written, so losing it loses the reason the contract looks the way it does.
|
|
59
|
+
- **Comments above any operation body key**, and after the last one. A `#` block above a verb survives even when the verb carries its own inline `# ...` — they are separate, and only the inline one is the description.
|
|
60
|
+
- **Comments between declarations.** A comment trailing a one-line contract stays on that line; one directly above the next declaration stays its doc comment. A comment after the last declaration in the file is kept too.
|
|
61
|
+
- **Comments on a nested object's brace** (`rp: { # ... }`) and trailing a field whose type wraps over several lines.
|
|
62
|
+
- **Unquoted options values.** `PetService: #modules/pet/pet.service.js` stays bare and a quoted value stays quoted. Both parse to the same string, so the parser records which form was written; a value that could not be read back bare is quoted regardless.
|
|
55
63
|
|
|
56
64
|
This is covered by `tests/round-trip.test.ts`, which formats every `.ck` file under `contracts/` and asserts the output is unchanged, plus checks that formatting is a fixed point. Anything that makes the printer normalize rather than preserve will fail it.
|
|
57
65
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Plugin } from 'prettier';
|
|
2
2
|
import type { CkRootNode } from '@contractkit/core';
|
|
3
|
+
/**
|
|
4
|
+
* Prettier plugin registering the `.ck` language, its parser, and its printer.
|
|
5
|
+
*
|
|
6
|
+
* The parser is `parseCk` and the printer is `printCk`, so formatting behaviour lives in
|
|
7
|
+
* `@contractkit/core` and `print-ck.ts` rather than here — this module only adapts them to
|
|
8
|
+
* prettier's interface. `tests/format-plugin.test.ts` covers that adapter layer, which a
|
|
9
|
+
* printer-level test cannot reach.
|
|
10
|
+
*/
|
|
3
11
|
declare const plugin: Plugin<CkRootNode>;
|
|
4
12
|
export default plugin;
|
|
5
13
|
export { printCk, DEFAULT_PRINT_WIDTH } from './print-ck.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAkBpD;;;;;;;GAOG;AACH,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,UAAU,CA8B9B,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -348,7 +348,7 @@ function printOperationKey(op, key) {
|
|
|
348
348
|
return lines;
|
|
349
349
|
}
|
|
350
350
|
case "responses":
|
|
351
|
-
return op.responses.length > 0 ? printResponseBlock(op.responses) : [];
|
|
351
|
+
return op.responses.length > 0 ? printResponseBlock(op.responses, op.responsesTrailingComments) : [];
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
__name(printOperationKey, "printOperationKey");
|
|
@@ -356,6 +356,7 @@ function printOperation(op) {
|
|
|
356
356
|
const lines = [];
|
|
357
357
|
const modPart = op.modifiers?.length ? `(${op.modifiers[0]})` : "";
|
|
358
358
|
const inlineDescription = op.descriptionInline ?? true;
|
|
359
|
+
for (const c of op.leadingComments ?? []) lines.push(`${I1}# ${c}`);
|
|
359
360
|
if (op.description && !inlineDescription) {
|
|
360
361
|
for (const line of op.description.split("\n")) lines.push(`${I1}# ${line}`);
|
|
361
362
|
}
|
|
@@ -363,12 +364,24 @@ function printOperation(op) {
|
|
|
363
364
|
lines.push(`${I1}${op.method}${modPart}: {${commentSuffix}`);
|
|
364
365
|
const order = op.keyOrder ?? [];
|
|
365
366
|
const rest = CANONICAL_KEY_ORDER.filter((k) => !order.includes(k));
|
|
367
|
+
const orphaned = [];
|
|
366
368
|
for (const key of [
|
|
367
369
|
...order,
|
|
368
370
|
...rest
|
|
369
371
|
]) {
|
|
370
|
-
|
|
372
|
+
const keyLines = printOperationKey(op, key);
|
|
373
|
+
const comments = op.bodyLeadingComments?.[key] ?? [];
|
|
374
|
+
if (keyLines.length === 0) {
|
|
375
|
+
orphaned.push(...comments);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
for (const c of comments) lines.push(`${I2}# ${c}`);
|
|
379
|
+
lines.push(...keyLines);
|
|
371
380
|
}
|
|
381
|
+
for (const c of [
|
|
382
|
+
...orphaned,
|
|
383
|
+
...op.bodyTrailingComments ?? []
|
|
384
|
+
]) lines.push(`${I2}# ${c}`);
|
|
372
385
|
lines.push(`${I1}}`);
|
|
373
386
|
return lines;
|
|
374
387
|
}
|
|
@@ -470,13 +483,19 @@ function printSecurity(security, indent = I2, innerIndent = I3) {
|
|
|
470
483
|
`${indent}security: none`
|
|
471
484
|
];
|
|
472
485
|
const fields = security;
|
|
473
|
-
|
|
486
|
+
const leading = fields.leadingComments ?? [];
|
|
487
|
+
const trailing = fields.trailingComments ?? [];
|
|
488
|
+
if (fields.policy === void 0 && leading.length === 0 && trailing.length === 0) return [];
|
|
474
489
|
const lines = [
|
|
475
490
|
`${indent}security: {`
|
|
476
491
|
];
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
492
|
+
for (const c of leading) lines.push(`${innerIndent}# ${c}`);
|
|
493
|
+
if (fields.policy !== void 0) {
|
|
494
|
+
const comment = fields.policyDescription ? ` # ${fields.policyDescription}` : "";
|
|
495
|
+
const value = fields.policy === false ? "none" : fields.policy;
|
|
496
|
+
lines.push(`${innerIndent}policy: ${value}${comment}`);
|
|
497
|
+
}
|
|
498
|
+
for (const c of trailing) lines.push(`${innerIndent}# ${c}`);
|
|
480
499
|
lines.push(`${indent}}`);
|
|
481
500
|
return lines;
|
|
482
501
|
}
|
|
@@ -531,21 +550,29 @@ function printContentTypeLine(contentType, bodyType, lineIndent) {
|
|
|
531
550
|
];
|
|
532
551
|
}
|
|
533
552
|
__name(printContentTypeLine, "printContentTypeLine");
|
|
534
|
-
function printResponseBlock(responses) {
|
|
553
|
+
function printResponseBlock(responses, trailingComments) {
|
|
535
554
|
const lines = [
|
|
536
555
|
`${I2}response: {`
|
|
537
556
|
];
|
|
538
557
|
for (const resp of responses) {
|
|
539
|
-
const
|
|
558
|
+
for (const comment of resp.leadingComments ?? []) lines.push(`${I3}# ${comment}`);
|
|
559
|
+
const bodies = resp.bodies;
|
|
540
560
|
const hasHeaders = resp.headers && resp.headers.length > 0;
|
|
541
561
|
const optOut = resp.headersOptOut;
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
562
|
+
const code = resp.emit ? `${resp.statusCode}(${resp.emit})` : `${resp.statusCode}`;
|
|
563
|
+
const inlinable = resp.inline && bodies.length > 0 && !hasHeaders && !optOut && bodies.every((b) => b.bodyType.kind !== "inlineObject");
|
|
564
|
+
if (inlinable) {
|
|
565
|
+
const inner = bodies.map((b) => `${b.contentType}: ${printType(b.bodyType)}`).join(" ");
|
|
566
|
+
lines.push(`${I3}${code}: { ${inner} }`);
|
|
567
|
+
} else if (bodies.length === 0 && !hasHeaders && !optOut && resp.hasBlock) {
|
|
568
|
+
lines.push(`${I3}${code}: {}`);
|
|
569
|
+
} else if (bodies.length > 0 || hasHeaders || optOut || (resp.trailingComments?.length ?? 0) > 0) {
|
|
570
|
+
lines.push(`${I3}${code}: {`);
|
|
571
|
+
for (const body of bodies) {
|
|
572
|
+
for (const comment of body.leadingComments ?? []) lines.push(`${I4}# ${comment}`);
|
|
573
|
+
lines.push(...printContentTypeLine(body.contentType, body.bodyType, I4));
|
|
548
574
|
}
|
|
575
|
+
for (const comment of resp.headersLeadingComments ?? []) lines.push(`${I4}# ${comment}`);
|
|
549
576
|
if (optOut) {
|
|
550
577
|
lines.push(`${I4}headers: none`);
|
|
551
578
|
} else if (hasHeaders) {
|
|
@@ -557,11 +584,13 @@ function printResponseBlock(responses) {
|
|
|
557
584
|
}
|
|
558
585
|
lines.push(`${I4}}`);
|
|
559
586
|
}
|
|
587
|
+
for (const comment of resp.trailingComments ?? []) lines.push(`${I4}# ${comment}`);
|
|
560
588
|
lines.push(`${I3}}`);
|
|
561
589
|
} else {
|
|
562
|
-
lines.push(`${I3}${
|
|
590
|
+
lines.push(`${I3}${code}:`);
|
|
563
591
|
}
|
|
564
592
|
}
|
|
593
|
+
for (const comment of trailingComments ?? []) lines.push(`${I3}# ${comment}`);
|
|
565
594
|
lines.push(`${I2}}`);
|
|
566
595
|
return lines;
|
|
567
596
|
}
|
|
@@ -569,15 +598,19 @@ __name(printResponseBlock, "printResponseBlock");
|
|
|
569
598
|
|
|
570
599
|
// src/print-ck.ts
|
|
571
600
|
var DEFAULT_PRINT_WIDTH = 80;
|
|
572
|
-
function quoteOptionsValue(value) {
|
|
573
|
-
|
|
601
|
+
function quoteOptionsValue(value, wasUnquoted = false) {
|
|
602
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
|
|
603
|
+
const roundTripsBare = value.length > 0 && value === value.trim() && !/[\n\r}]/.test(value) && !/[ \t]#/.test(value) && !/^["']/.test(value);
|
|
604
|
+
return wasUnquoted && roundTripsBare ? value : `"${value}"`;
|
|
574
605
|
}
|
|
575
606
|
__name(quoteOptionsValue, "quoteOptionsValue");
|
|
576
|
-
function emitOptionsEntries(lines, entries, comments) {
|
|
607
|
+
function emitOptionsEntries(lines, entries, comments, unquoted) {
|
|
577
608
|
const I22 = INDENT + INDENT;
|
|
609
|
+
const wasUnquoted = new Set(unquoted ?? []);
|
|
578
610
|
for (const [key, value] of Object.entries(entries)) {
|
|
579
611
|
for (const c of comments?.leading?.[key] ?? []) lines.push(`${I22}# ${c}`);
|
|
580
|
-
|
|
612
|
+
const inline = comments?.inline?.[key];
|
|
613
|
+
lines.push(`${I22}${key}: ${quoteOptionsValue(value, wasUnquoted.has(key))}${inline !== void 0 ? ` # ${inline}` : ""}`);
|
|
581
614
|
}
|
|
582
615
|
for (const c of comments?.trailing ?? []) lines.push(`${I22}# ${c}`);
|
|
583
616
|
}
|
|
@@ -591,6 +624,7 @@ function printOptionsBlock(ast) {
|
|
|
591
624
|
const hasBodyComments = ast.optionsComments?.body !== void 0;
|
|
592
625
|
if (!hasMeta && !hasServices && !hasSecurity && !hasRequestHeaders && !hasResponseHeaders && !hasBodyComments) return null;
|
|
593
626
|
const lines = [
|
|
627
|
+
...(ast.optionsComments?.leading ?? []).map((c) => `# ${c}`),
|
|
594
628
|
"options {"
|
|
595
629
|
];
|
|
596
630
|
const body = ast.optionsComments?.body;
|
|
@@ -600,13 +634,13 @@ function printOptionsBlock(ast) {
|
|
|
600
634
|
if (hasMeta) {
|
|
601
635
|
emitLeading("keys");
|
|
602
636
|
lines.push(`${INDENT}keys: {`);
|
|
603
|
-
emitOptionsEntries(lines, ast.meta, ast.optionsComments?.keys);
|
|
637
|
+
emitOptionsEntries(lines, ast.meta, ast.optionsComments?.keys, ast.optionsUnquoted?.keys);
|
|
604
638
|
lines.push(`${INDENT}}`);
|
|
605
639
|
}
|
|
606
640
|
if (hasServices) {
|
|
607
641
|
emitLeading("services");
|
|
608
642
|
lines.push(`${INDENT}services: {`);
|
|
609
|
-
emitOptionsEntries(lines, ast.services, ast.optionsComments?.services);
|
|
643
|
+
emitOptionsEntries(lines, ast.services, ast.optionsComments?.services, ast.optionsUnquoted?.services);
|
|
610
644
|
lines.push(`${INDENT}}`);
|
|
611
645
|
}
|
|
612
646
|
if (hasRequestHeaders) {
|
|
@@ -660,6 +694,10 @@ function printCk(ast, printWidth = DEFAULT_PRINT_WIDTH) {
|
|
|
660
694
|
const modPart = route.modifiers?.length ? `(${route.modifiers[0]})` : "";
|
|
661
695
|
parts.push(printDeclLeadIn(route.leadingComments, route.description) + `operation${modPart} ${printRoute(route, emptyBlocks, emptyIdx, Infinity)}`);
|
|
662
696
|
}
|
|
697
|
+
if (ast.trailingComments?.length) {
|
|
698
|
+
if (parts.length > 0) parts.push("");
|
|
699
|
+
for (const c of ast.trailingComments) parts.push(`# ${c}`);
|
|
700
|
+
}
|
|
663
701
|
return parts.join("\n") + "\n";
|
|
664
702
|
}
|
|
665
703
|
__name(printCk, "printCk");
|
|
@@ -680,7 +718,10 @@ __name(printDeclLeadIn, "printDeclLeadIn");
|
|
|
680
718
|
var { hardline, join } = builders;
|
|
681
719
|
function toDoc(text) {
|
|
682
720
|
const lines = text.trimEnd().split("\n");
|
|
683
|
-
return
|
|
721
|
+
return [
|
|
722
|
+
join(hardline, lines),
|
|
723
|
+
hardline
|
|
724
|
+
];
|
|
684
725
|
}
|
|
685
726
|
__name(toDoc, "toDoc");
|
|
686
727
|
var plugin = {
|