@contractkit/prettier-plugin 0.14.2 → 0.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/indent.ts","../src/print-type.ts","../src/print-contract.ts","../src/print-operation.ts","../src/print-ck.ts"],"sourcesContent":["import type { Plugin } from 'prettier';\nimport { builders } from 'prettier/doc';\nimport { parseCk, DiagnosticCollector } from '@contractkit/core';\nimport type { CkRootNode } from '@contractkit/core';\nimport { printCk } from './print-ck.js';\n\nconst { hardline, join } = builders;\n\n/**\n * Wrap printed source as a prettier doc.\n *\n * `printCk` already ends the file with a newline, but splitting on `\\n` turns that into a\n * trailing empty segment, so the text is trimmed first and the final newline re-added as an\n * explicit `hardline`. Dropping it would leave the file without a terminating newline, which\n * fights every editor and lint rule that wants one.\n */\nfunction toDoc(text: string) {\n const lines = text.trimEnd().split('\\n');\n return [join(hardline, lines), hardline];\n}\n\n/**\n * Prettier plugin registering the `.ck` language, its parser, and its printer.\n *\n * The parser is `parseCk` and the printer is `printCk`, so formatting behaviour lives in\n * `@contractkit/core` and `print-ck.ts` rather than here — this module only adapts them to\n * prettier's interface. `tests/format-plugin.test.ts` covers that adapter layer, which a\n * printer-level test cannot reach.\n */\nconst plugin: Plugin<CkRootNode> = {\n languages: [\n {\n name: 'ContractDSL',\n parsers: ['contract-ck'],\n extensions: ['.ck'],\n vscodeLanguageIds: ['contract-ck'],\n },\n ],\n\n parsers: {\n 'contract-ck': {\n parse(text, _options) {\n const diag = new DiagnosticCollector();\n return parseCk(text, '<stdin>', diag);\n },\n astFormat: 'contract-ck',\n locStart: () => 0,\n locEnd: _node => 0,\n },\n },\n\n printers: {\n 'contract-ck': {\n print(path, options) {\n const node = path.node as CkRootNode;\n return toDoc(printCk(node, options.printWidth));\n },\n },\n },\n};\n\nexport default plugin;\nexport { printCk, DEFAULT_PRINT_WIDTH } from './print-ck.js';\n","export const INDENT = ' ';\n","import type { ContractTypeNode, FieldNode, InlineObjectTypeNode } from '@contractkit/core';\nimport { INDENT } from './indent.js';\n\n// ─── Type expression printer ────────────────────────────────────────────────\n\n/** Render a `ContractTypeNode` back to its `.ck` source string. */\nexport function printType(type: ContractTypeNode): string {\n switch (type.kind) {\n case 'scalar': {\n const constraints: string[] = [];\n if (type.format !== undefined) {\n // Print unquoted when format contains only safe chars; quote otherwise\n const fmt = type.format;\n constraints.push(/^[a-zA-Z0-9\\-.:/]+$/.test(fmt) ? fmt : `\"${fmt}\"`);\n }\n if (type.min !== undefined) constraints.push(`min=${type.min}`);\n if (type.max !== undefined) constraints.push(`max=${type.max}`);\n if (type.len !== undefined) constraints.push(`len=${type.len}`);\n if (type.regex !== undefined) constraints.push(`regex=/${type.regex}/`);\n return constraints.length > 0 ? `${type.name}(${constraints.join(', ')})` : type.name;\n }\n case 'array': {\n const args: string[] = [printType(type.item)];\n if (type.min !== undefined) args.push(`min=${type.min}`);\n if (type.max !== undefined) args.push(`max=${type.max}`);\n return `array(${args.join(', ')})`;\n }\n case 'tuple':\n return `tuple(${type.items.map(printType).join(', ')})`;\n case 'record':\n return `record(${printType(type.key)}, ${printType(type.value)})`;\n case 'enum':\n return `enum(${type.values.map(formatEnumValue).join(', ')})`;\n case 'literal':\n return typeof type.value === 'string' ? `literal(\"${type.value}\")` : `literal(${type.value})`;\n case 'union':\n return type.members.map(printType).join(' | ');\n case 'discriminatedUnion':\n return `discriminated(by=${type.discriminator}, ${type.members.map(printType).join(' | ')})`;\n case 'intersection':\n return type.members.map(printType).join(' & ');\n case 'ref':\n return type.name;\n case 'inlineObject':\n return printInlineObjectCompact(type);\n case 'lazy':\n return `lazy(${printType(type.inner)})`;\n }\n}\n\n/** Compact single-line form — used when inline object appears nested inside another type. */\nfunction printInlineObjectCompact(obj: InlineObjectTypeNode): string {\n const prefix = obj.mode ? `mode(${obj.mode}) ` : '';\n if (obj.fields.length === 0) return `${prefix}{}`;\n const parts = obj.fields.map(f => {\n const opt = f.optional ? '?' : '';\n let t = printType(f.type);\n if (f.nullable) t += ' | null';\n return `${f.name}${opt}: ${t}`;\n });\n return `${prefix}{ ${parts.join(', ')} }`;\n}\n\n/** Multi-line enum form — one value per line, used when single-line would exceed print width. */\nexport function printEnumExpanded(values: string[], indent: string): string {\n const innerIndent = indent + INDENT;\n return `enum(\\n${values.map(v => `${innerIndent}${formatEnumValue(v)}`).join(',\\n')}\\n${indent})`;\n}\n\n// ─── Field printer ──────────────────────────────────────────────────────────\n\n/** Print a full field declaration, including visibility, default, and inline comment.\n * Modifier order is canonical: override → deprecated → readonly|writeonly → type. */\nexport function printField(field: FieldNode, indent: string, printWidth: number = 80): string {\n const opt = field.optional ? '?' : '';\n const ovr = field.override ? 'override ' : '';\n const dep = field.deprecated ? 'deprecated ' : '';\n const vis = field.visibility !== 'normal' ? `${field.visibility} ` : '';\n const mods = `${ovr}${dep}${vis}`;\n const def = field.default !== undefined ? ` = ${formatDefault(field.default)}` : '';\n const comment = field.description ? ` # ${field.description}` : '';\n const innerIndent = indent + INDENT;\n\n // Expand inline object types to multi-line — same rule as type aliases.\n // Only when there's no default and no nullable union (those can't split cleanly).\n if (!field.nullable && field.default === undefined) {\n const trailing = extractTrailingInlineObject(field.type);\n if (trailing) {\n const { prefix, inlineObj } = trailing;\n const modePart = inlineObj.mode ? `mode(${inlineObj.mode}) ` : '';\n const header = prefix\n ? `${indent}${field.name}${opt}: ${mods}${prefix} & ${modePart}{${comment}`\n : `${indent}${field.name}${opt}: ${mods}${modePart}{${comment}`;\n return [header, ...printInlineObjectExpanded(inlineObj, innerIndent, printWidth), `${indent}}`].join('\\n');\n }\n }\n\n let typeStr = printType(field.type);\n if (field.nullable) typeStr += ' | null';\n const fullLine = `${indent}${field.name}${opt}: ${mods}${typeStr}${def}${comment}`;\n if (field.type.kind === 'enum' && !field.nullable && field.default === undefined && fullLine.length > printWidth) {\n const enumStr = printEnumExpanded(field.type.values, indent);\n return `${indent}${field.name}${opt}: ${mods}${enumStr}${comment}`;\n }\n return fullLine;\n}\n\n/** Print inline-object fields expanded (used when an inline brace object trails a type alias).\n * Any `trailingComments` (comments after the last field, before `}`) are emitted as indented\n * `# text` lines after the fields, matching how model bodies round-trip trailing comments. */\nexport function printInlineObjectExpanded(obj: InlineObjectTypeNode, indent: string, printWidth: number = 80): string[] {\n const lines = obj.fields.map(f => printField(f, indent, printWidth));\n for (const comment of obj.trailingComments ?? []) {\n lines.push(`${indent}# ${comment}`);\n }\n return lines;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\n/** Format a single enum value: bare identifier stays bare; anything else gets double-quoted. */\nexport function formatEnumValue(v: string): string {\n if (/^[a-zA-Z_$][a-zA-Z0-9_$\\-.]*$/.test(v)) return v;\n return `\"${v}\"`;\n}\n\n/** Format a default value: quote strings that aren't valid bare identifiers. */\nexport function formatDefault(val: string | number | boolean): string {\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n // If it looks like a bare identifier (enum value, unquoted token), keep it bare.\n if (/^[a-zA-Z_$][a-zA-Z0-9_$\\-.]*$/.test(val)) return val;\n return `\"${val}\"`;\n}\n\n/**\n * Detect whether the last member of a type is an inline brace object, and if so\n * return the prefix type string and the inline object for expanded printing.\n * Returns null if the type doesn't end with an inline object.\n */\nexport function extractTrailingInlineObject(type: ContractTypeNode): {\n prefix: string | null;\n inlineObj: InlineObjectTypeNode;\n} | null {\n if (type.kind === 'inlineObject') {\n return { prefix: null, inlineObj: type };\n }\n if (type.kind === 'intersection') {\n const last = type.members[type.members.length - 1];\n if (last?.kind === 'inlineObject') {\n const prefixStr = type.members.slice(0, -1).map(printType).join(' & ');\n return { prefix: prefixStr, inlineObj: last };\n }\n }\n return null;\n}\n","import type { ModelNode } from '@contractkit/core';\nimport { printField, printInlineObjectExpanded, extractTrailingInlineObject, printType, printEnumExpanded } from './print-type.js';\nimport { INDENT } from './indent.js';\n\n// ─── Model declaration ───────────────────────────────────────────────────────\n\n/**\n * Render a `contract` body from its `Name: ...` onward (the `contract ` keyword and any\n * doc comment written above the declaration are prepended by the caller). Handles both the\n * type-alias form (`Name: <type>`) and the regular field-block form, including modifiers,\n * base chain, and per-field printing. Any `model.trailingComments` (comments after the last\n * field, before `}`) are emitted as indented `# text` lines so they round-trip.\n *\n * `model.description` is emitted here only when `model.descriptionInline` says the author wrote\n * it on the header line; otherwise it belongs above the declaration and the caller emits it.\n */\nexport function printModelDecl(model: ModelNode, printWidth: number = 80): string {\n // Type alias form: Name : typeExpression\n if (model.type !== undefined) {\n return printTypeAlias(model, printWidth);\n }\n\n // Regular model with fields (possibly inherited)\n // A doc comment written above the declaration is re-emitted there by the caller; only an\n // inline one belongs on the header line.\n const commentSuffix = model.description && model.descriptionInline ? ` # ${model.description}` : '';\n const modifiers = [\n model.deprecated ? 'deprecated' : '',\n model.inputCase || model.outputCase\n ? `format(${[model.inputCase ? `input=${model.inputCase}` : '', model.outputCase ? `output=${model.outputCase}` : ''].filter(Boolean).join(', ')})`\n : '',\n model.mode ? `mode(${model.mode})` : '',\n ]\n .filter(Boolean)\n .join(' ');\n const modePrefix = modifiers ? `${modifiers} ` : '';\n const baseChain = model.bases && model.bases.length > 0 ? `${model.bases.join(' & ')} & ` : '';\n const header = `${modePrefix}${model.name}: ${baseChain}{${commentSuffix}`;\n\n const lines: string[] = [header];\n for (const field of model.fields) {\n lines.push(printField(field, INDENT, printWidth));\n }\n for (const comment of model.trailingComments ?? []) {\n lines.push(`${INDENT}# ${comment}`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction printTypeAlias(model: ModelNode, printWidth: number): string {\n const type = model.type!;\n // A doc comment written above the declaration is re-emitted there by the caller; only an\n // inline one belongs on the header line.\n const commentSuffix = model.description && model.descriptionInline ? ` # ${model.description}` : '';\n const modifiers = [\n model.deprecated ? 'deprecated' : '',\n model.inputCase || model.outputCase\n ? `format(${[model.inputCase ? `input=${model.inputCase}` : '', model.outputCase ? `output=${model.outputCase}` : ''].filter(Boolean).join(', ')})`\n : '',\n model.mode ? `mode(${model.mode})` : '',\n ]\n .filter(Boolean)\n .join(' ');\n const modePrefix = modifiers ? `${modifiers} ` : '';\n\n // If the type ends with an inline brace object, expand it as a pseudo-model block.\n const trailing = extractTrailingInlineObject(type);\n if (trailing) {\n const { prefix, inlineObj } = trailing;\n const modePart = inlineObj.mode ? `mode(${inlineObj.mode}) ` : '';\n const header = prefix\n ? `${modePrefix}${model.name}: ${prefix} & ${modePart}{${commentSuffix}`\n : `${modePrefix}${model.name}: ${modePart}{${commentSuffix}`;\n const lines: string[] = [header, ...printInlineObjectExpanded(inlineObj, INDENT, printWidth), '}'];\n return lines.join('\\n');\n }\n\n // Simple type alias — single line, unless it's a long enum.\n // Note: the contract prefix \"contract \" (9 chars) is prepended by the caller.\n const singleLine = `${modePrefix}${model.name}: ${printType(type)}${commentSuffix}`;\n if (type.kind === 'enum' && 'contract '.length + singleLine.length > printWidth) {\n return `${modePrefix}${model.name}: ${printEnumExpanded(type.values, '')}${commentSuffix}`;\n }\n return singleLine;\n}\n","import type {\n OpRouteNode,\n OpOperationNode,\n OpResponseNode,\n ParamSource,\n SecurityNode,\n SecurityFields,\n ContractTypeNode,\n ObjectMode,\n PluginValue,\n McpConfigNode,\n OpBodyKey,\n} from '@contractkit/core';\nimport { SECURITY_NONE } from '@contractkit/core';\nimport { printType, formatDefault } from './print-type.js';\nimport { INDENT } from './indent.js';\n\nconst I1 = INDENT;\nconst I2 = INDENT.repeat(2);\nconst I3 = INDENT.repeat(3);\nconst I4 = INDENT.repeat(4);\n\n// ─── Orphan comment helpers ──────────────────────────────────────────────────\n\ntype CommentEntry = { line: number; text: string };\n/** A run of consecutive-line orphan comments, keyed by the source line it starts on. */\nexport type CommentBlock = { startLine: number; lines: string[] };\n\n/** Group sorted orphan comment entries into consecutive-line blocks. */\nexport function groupComments(entries: CommentEntry[]): CommentBlock[] {\n const blocks: CommentBlock[] = [];\n let current: CommentBlock | null = null;\n for (const { line, text } of entries) {\n if (current && line === current.startLine + current.lines.length) {\n current.lines.push(text);\n } else {\n if (current) blocks.push(current);\n current = { startLine: line, lines: [text] };\n }\n }\n if (current) blocks.push(current);\n return blocks;\n}\n\n/**\n * Emit any comment blocks whose startLine is < beforeLine.\n * Lines are emitted verbatim — they already carry their original indentation.\n */\nexport function flushBlocks(out: string[], blocks: CommentBlock[], idx: { value: number }, beforeLine: number, _indent = '') {\n while (idx.value < blocks.length && blocks[idx.value]!.startLine < beforeLine) {\n for (const l of blocks[idx.value]!.lines) out.push(l);\n idx.value++;\n }\n}\n\n// ─── Route ───────────────────────────────────────────────────────────────────\n\n/**\n * Render an `operation` route body from its `path: {` onward (the `operation` keyword, any\n * modifier, and the route's leading comments are prepended by the caller). Emits the\n * params/security blocks and each HTTP operation, interleaving orphan comment `blocks` at their\n * original source positions — `idx` tracks how far through `blocks` we've consumed, and\n * `nextRouteStart` bounds the flush to comments before the following route. Any\n * `route.trailingComments` (comments after the last operation, before `}`) are emitted before\n * the closing brace so they round-trip.\n *\n * Blank lines between operations come from each operation's `blankLineBefore`, so the author's\n * spacing survives rather than being normalized to one rule or the other.\n */\nexport function printRoute(route: OpRouteNode, blocks: CommentBlock[], idx: { value: number }, nextRouteStart: number): string {\n const lines: string[] = [];\n lines.push(`${route.path}: {`);\n\n if (route.params !== undefined) {\n lines.push(...printParamsBlock(route.params, I1, route.paramsMode));\n }\n\n if (route.security !== undefined) {\n lines.push(...printSecurity(route.security, I1, I2));\n }\n\n for (const op of route.operations) {\n // Reproduce the author's spacing rather than imposing our own.\n if (op.blankLineBefore && lines.length > 1) lines.push('');\n // Flush comment blocks that appear before this operation (inside the route)\n flushBlocks(lines, blocks, idx, op.loc.line, I1);\n lines.push(...printOperation(op));\n }\n\n // Flush comment blocks between last operation and the next route\n flushBlocks(lines, blocks, idx, nextRouteStart, I1);\n\n // Trailing/orphan comments after the last operation, before the closing brace.\n for (const comment of route.trailingComments ?? []) {\n lines.push(`${I1}# ${comment}`);\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\n// ─── Params block ────────────────────────────────────────────────────────────\n\nfunction printParamsBlock(source: ParamSource, indent: string, mode?: ObjectMode): string[] {\n const prefix = mode ? `mode(${mode}) ` : '';\n if (source.kind === 'ref') {\n return [`${indent}${prefix}params: ${source.name}`];\n }\n if (source.kind === 'params') {\n const lines: string[] = [`${indent}${prefix}params: {`];\n const inner = indent + INDENT;\n for (const p of source.nodes) {\n const opt = p.optional ? '?' : '';\n let t = printType(p.type);\n if (p.nullable) t += ' | null';\n const def = p.default !== undefined ? ` = ${formatDefault(p.default)}` : '';\n const comment = p.description ? ` # ${p.description}` : '';\n lines.push(`${inner}${p.name}${opt}: ${t}${def}${comment}`);\n }\n lines.push(`${indent}}`);\n return lines;\n }\n // ContractTypeNode\n return [`${indent}${prefix}params: ${printType(source.node)}`];\n}\n\n// ─── HTTP operation ──────────────────────────────────────────────────────────\n\n/** Order the body keys are emitted in when the node carries no source order (built programmatically). */\nconst CANONICAL_KEY_ORDER: OpBodyKey[] = ['name', 'service', 'sdk', 'mcp', 'signature', 'security', 'plugins', 'query', 'headers', 'request', 'responses'];\n\n/** Render a single operation-body key. Returns `[]` when the operation doesn't carry that key. */\nfunction printOperationKey(op: OpOperationNode, key: OpBodyKey): string[] {\n switch (key) {\n case 'name':\n return op.name ? [`${I2}name: ${op.name}`] : [];\n case 'service':\n return op.service ? [`${I2}service: ${op.service}`] : [];\n case 'sdk':\n return op.sdk ? [`${I2}sdk: ${op.sdk}`] : [];\n case 'mcp':\n if (op.mcp === true) return [`${I2}mcp: true`];\n if (op.mcp === false) return [`${I2}mcp: false`];\n return op.mcp ? printMcpBlock(op.mcp) : [];\n case 'signature': {\n if (!op.signature) return [];\n const comment = op.signatureDescription ? ` # ${op.signatureDescription}` : '';\n if (op.signaturePolicy) {\n return [\n `${I2}signature: {`,\n `${I3}options: ${formatSignatureValue(op.signature)}${comment}`,\n `${I3}policy: ${op.signaturePolicy}`,\n `${I2}}`,\n ];\n }\n return [`${I2}signature: ${formatSignatureValue(op.signature)}${comment}`];\n }\n case 'security':\n return op.security !== undefined ? printSecurity(op.security) : [];\n case 'plugins': {\n if (!op.plugins || Object.keys(op.plugins).length === 0) return [];\n const lines = [`${I2}plugins: {`];\n for (const [k, val] of Object.entries(op.plugins)) lines.push(...printPluginEntry(k, val, I3));\n lines.push(`${I2}}`);\n return lines;\n }\n case 'query':\n return op.query !== undefined ? printQueryOrHeaders('query', op.query, op.queryMode) : [];\n case 'headers':\n if (op.requestHeadersOptOut) return [`${I2}headers: none`];\n return op.headers !== undefined ? printQueryOrHeaders('headers', op.headers, op.headersMode) : [];\n case 'request': {\n if (!op.request) return [];\n const lines = [`${I2}request: {`];\n for (const body of op.request.bodies) lines.push(...printContentTypeLine(body.contentType, body.bodyType, I3));\n lines.push(`${I2}}`);\n return lines;\n }\n case 'responses':\n return op.responses.length > 0 ? printResponseBlock(op.responses, op.responsesTrailingComments) : [];\n }\n}\n\nfunction printOperation(op: OpOperationNode): string[] {\n const lines: string[] = [];\n const modPart = op.modifiers?.length ? `(${op.modifiers[0]})` : '';\n\n // A doc comment written above the method line goes back above it; only an inline one is\n // re-emitted as a trailing `#` on the header. Nodes built programmatically carry no placement,\n // and default to inline — the form most `.ck` sources use and one that round-trips as written.\n const inlineDescription = op.descriptionInline ?? true;\n // Standalone prose above the verb, kept apart from the doc comment when the verb carries an\n // inline one. Emitted first so it stays above the line it was written above.\n for (const c of op.leadingComments ?? []) lines.push(`${I1}# ${c}`);\n if (op.description && !inlineDescription) {\n for (const line of op.description.split('\\n')) lines.push(`${I1}# ${line}`);\n }\n const commentSuffix = op.description && inlineDescription ? ` # ${op.description}` : '';\n lines.push(`${I1}${op.method}${modPart}: {${commentSuffix}`);\n\n // Emit in source order when the parser recorded it, so formatting never reorders a user's keys.\n // Any key the source order doesn't mention (e.g. added by a later AST pass) follows in canonical order.\n const order = op.keyOrder ?? [];\n const rest = CANONICAL_KEY_ORDER.filter(k => !order.includes(k));\n // A comment run whose key turned out to print nothing has nowhere to sit; rather than drop it,\n // it falls through to the trailing run at the end of the body.\n const orphaned: string[] = [];\n for (const key of [...order, ...rest]) {\n const keyLines = printOperationKey(op, key);\n const comments = op.bodyLeadingComments?.[key] ?? [];\n if (keyLines.length === 0) {\n orphaned.push(...comments);\n continue;\n }\n for (const c of comments) lines.push(`${I2}# ${c}`);\n lines.push(...keyLines);\n }\n\n for (const c of [...orphaned, ...(op.bodyTrailingComments ?? [])]) lines.push(`${I2}# ${c}`);\n\n lines.push(`${I1}}`);\n return lines;\n}\n\n// ─── MCP block ───────────────────────────────────────────────────────────────\n\n/**\n * Reconstruct the `hint:` token list from the four annotation booleans, in canonical\n * order. Each set boolean contributes its positive or negative token; unset hints are omitted.\n */\nfunction mcpHintTokens(mcp: McpConfigNode): string[] {\n const tokens: string[] = [];\n if (mcp.readOnlyHint !== undefined) tokens.push(mcp.readOnlyHint ? 'readOnly' : 'nonReadOnly');\n if (mcp.idempotentHint !== undefined) tokens.push(mcp.idempotentHint ? 'idempotent' : 'nonIdempotent');\n if (mcp.destructiveHint !== undefined) tokens.push(mcp.destructiveHint ? 'destructive' : 'nonDestructive');\n if (mcp.openWorldHint !== undefined) tokens.push(mcp.openWorldHint ? 'openWorld' : 'closedWorld');\n return tokens;\n}\n\n/** Print an `mcp: { ... }` settings block. Fields are emitted in canonical order; `hint:` is omitted when no hints are set. */\nfunction printMcpBlock(mcp: McpConfigNode): string[] {\n const lines: string[] = [`${I2}mcp: {`];\n if (mcp.name !== undefined) lines.push(`${I3}name: \"${escapeString(mcp.name)}\"`);\n if (mcp.title !== undefined) lines.push(`${I3}title: \"${escapeString(mcp.title)}\"`);\n if (mcp.description !== undefined) lines.push(`${I3}description: \"${escapeString(mcp.description)}\"`);\n const tokens = mcpHintTokens(mcp);\n if (tokens.length > 0) lines.push(`${I3}hint: ${tokens.join(', ')}`);\n lines.push(`${I2}}`);\n return lines;\n}\n\n// ─── Plugins block ───────────────────────────────────────────────────────────\n\nconst IDENT_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n\nfunction escapeString(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\nfunction printPluginEntry(key: string, value: PluginValue, indent: string): string[] {\n const lines: string[] = [];\n const inline = printPluginInline(value);\n if (inline !== null) {\n lines.push(`${indent}${key}: ${inline}`);\n } else {\n const head = `${indent}${key}: `;\n const block = printPluginBlock(value, indent);\n lines.push(`${head}${block[0]!.trimStart()}`);\n for (let i = 1; i < block.length; i++) lines.push(block[i]!);\n }\n return lines;\n}\n\nfunction printPluginInline(value: PluginValue): string | null {\n if (typeof value === 'string') return `\"${escapeString(value)}\"`;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n if (value === null) return 'null';\n if (Array.isArray(value) && value.length === 0) return '[]';\n if (!Array.isArray(value) && typeof value === 'object' && Object.keys(value).length === 0) return '{}';\n return null;\n}\n\nfunction printPluginBlock(value: PluginValue, indent: string): string[] {\n const inner = indent + INDENT;\n const lines: string[] = [];\n if (Array.isArray(value)) {\n lines.push(`${indent}[`);\n for (const item of value) {\n const inline = printPluginInline(item);\n if (inline !== null) {\n lines.push(`${inner}${inline}`);\n } else {\n const block = printPluginBlock(item, inner);\n for (const l of block) lines.push(l);\n }\n }\n lines.push(`${indent}]`);\n return lines;\n }\n if (typeof value === 'object' && value !== null) {\n lines.push(`${indent}{`);\n for (const [k, v] of Object.entries(value)) {\n const fieldKey = IDENT_RE.test(k) ? k : `\"${escapeString(k)}\"`;\n const inline = printPluginInline(v);\n if (inline !== null) {\n lines.push(`${inner}${fieldKey}: ${inline}`);\n } else {\n const block = printPluginBlock(v, inner);\n lines.push(`${inner}${fieldKey}: ${block[0]!.trimStart()}`);\n for (let i = 1; i < block.length; i++) lines.push(block[i]!);\n }\n }\n lines.push(`${indent}}`);\n return lines;\n }\n // Scalars are always inline; printPluginInline already handles them.\n return [`${indent}${printPluginInline(value)}`];\n}\n\n// ─── Security ────────────────────────────────────────────────────────────────\n\n/** Print a signature key: unquoted when it's a plain identifier, quoted otherwise. */\nfunction formatSignatureValue(value: string): string {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(value) ? value : `\"${value}\"`;\n}\n\n/**\n * Print a `security:` declaration. Returns `[\"${indent}security: none\"]` for the public-endpoint\n * sentinel, a multi-line block when `policy` is set or the block carries comments, or an empty\n * array when there is nothing meaningful to emit.\n *\n * A block holding only comments still prints: the rationale for a policy floor is the reason\n * authors write in here, and collapsing the braces away would delete it.\n *\n * @param indent indentation for the `security` keyword line\n * @param innerIndent indentation for field lines inside the block\n */\nexport function printSecurity(security: SecurityNode, indent = I2, innerIndent = I3): string[] {\n if (security === SECURITY_NONE) return [`${indent}security: none`];\n const fields = security as SecurityFields;\n const leading = fields.leadingComments ?? [];\n const trailing = fields.trailingComments ?? [];\n // A block with no policy still has to be emitted when it carries comments, or the author's\n // rationale disappears along with the empty braces.\n if (fields.policy === undefined && leading.length === 0 && trailing.length === 0) return [];\n const lines = [`${indent}security: {`];\n for (const c of leading) lines.push(`${innerIndent}# ${c}`);\n if (fields.policy !== undefined) {\n const comment = fields.policyDescription ? ` # ${fields.policyDescription}` : '';\n const value = fields.policy === false ? 'none' : fields.policy;\n lines.push(`${innerIndent}policy: ${value}${comment}`);\n }\n for (const c of trailing) lines.push(`${innerIndent}# ${c}`);\n lines.push(`${indent}}`);\n return lines;\n}\n\n// ─── Query / headers ─────────────────────────────────────────────────────────\n\nfunction printQueryOrHeaders(keyword: 'query' | 'headers', source: ParamSource, mode?: ObjectMode): string[] {\n const prefix = mode ? `mode(${mode}) ` : '';\n if (source.kind === 'ref') {\n return [`${I2}${prefix}${keyword}: ${source.name}`];\n }\n if (source.kind === 'params') {\n if (source.nodes.length === 0) return [];\n const lines: string[] = [`${I2}${prefix}${keyword}: {`];\n for (const p of source.nodes) {\n const opt = p.optional ? '?' : '';\n let t = printType(p.type);\n if (p.nullable) t += ' | null';\n const def = p.default !== undefined ? ` = ${formatDefault(p.default)}` : '';\n const comment = p.description ? ` # ${p.description}` : '';\n lines.push(`${I3}${p.name}${opt}: ${t}${def}${comment}`);\n }\n lines.push(`${I2}}`);\n return lines;\n }\n // ContractTypeNode (e.g. intersection)\n return [`${I2}${prefix}${keyword}: ${printType(source.node)}`];\n}\n\n// ─── Content-type line ───────────────────────────────────────────────────────\n\n/** Print a `contentType: bodyType` line, expanding inline brace objects onto separate lines. */\nfunction printContentTypeLine(contentType: string, bodyType: ContractTypeNode, lineIndent: string): string[] {\n if (bodyType.kind === 'inlineObject') {\n const fieldIndent = lineIndent + INDENT;\n const lines: string[] = [`${lineIndent}${contentType}: {`];\n for (const f of bodyType.fields) {\n const opt = f.optional ? '?' : '';\n let t = printType(f.type);\n if (f.nullable) t += ' | null';\n const def = f.default !== undefined ? ` = ${formatDefault(f.default)}` : '';\n const comment = f.description ? ` # ${f.description}` : '';\n lines.push(`${fieldIndent}${f.name}${opt}: ${t}${def}${comment}`);\n }\n lines.push(`${lineIndent}}`);\n return lines;\n }\n return [`${lineIndent}${contentType}: ${printType(bodyType)}`];\n}\n\n// ─── Response block ──────────────────────────────────────────────────────────\n\nfunction printResponseBlock(responses: OpResponseNode[], trailingComments?: string[]): string[] {\n const lines: string[] = [`${I2}response: {`];\n\n for (const resp of responses) {\n for (const comment of resp.leadingComments ?? []) lines.push(`${I3}# ${comment}`);\n const bodies = resp.bodies;\n const hasHeaders = resp.headers && resp.headers.length > 0;\n const optOut = resp.headersOptOut;\n // `404(documented):` — the modifier changes what codegen does, so it has to survive.\n const code = resp.emit ? `${resp.statusCode}(${resp.emit})` : `${resp.statusCode}`;\n const inlinable = resp.inline && bodies.length > 0 && !hasHeaders && !optOut && bodies.every(b => b.bodyType.kind !== 'inlineObject');\n if (inlinable) {\n // Written on one line in the source, so keep it there: `200: { application/json: Pet }`.\n // Several mimes on that line stay on it too, space-separated as the grammar has them.\n const inner = bodies.map(b => `${b.contentType}: ${printType(b.bodyType)}`).join(' ');\n lines.push(`${I3}${code}: { ${inner} }`);\n } else if (bodies.length === 0 && !hasHeaders && !optOut && resp.hasBlock) {\n // An empty block means \"emitted, no body\" — collapsing it to `304:` would change\n // the generated router, so it is not a formatting detail.\n lines.push(`${I3}${code}: {}`);\n } else if (bodies.length > 0 || hasHeaders || optOut || (resp.trailingComments?.length ?? 0) > 0) {\n lines.push(`${I3}${code}: {`);\n for (const body of bodies) {\n for (const comment of body.leadingComments ?? []) lines.push(`${I4}# ${comment}`);\n lines.push(...printContentTypeLine(body.contentType, body.bodyType, I4));\n }\n for (const comment of resp.headersLeadingComments ?? []) lines.push(`${I4}# ${comment}`);\n if (optOut) {\n lines.push(`${I4}headers: none`);\n } else if (hasHeaders) {\n lines.push(`${I4}headers: {`);\n for (const h of resp.headers!) {\n const opt = h.optional ? '?' : '';\n const trail = h.description ? ` # ${h.description}` : '';\n lines.push(`${I4}${INDENT}${h.name}${opt}: ${printType(h.type)}${trail}`);\n }\n lines.push(`${I4}}`);\n }\n for (const comment of resp.trailingComments ?? []) lines.push(`${I4}# ${comment}`);\n lines.push(`${I3}}`);\n } else {\n lines.push(`${I3}${code}:`);\n }\n }\n\n for (const comment of trailingComments ?? []) lines.push(`${I3}# ${comment}`);\n lines.push(`${I2}}`);\n return lines;\n}\n","import type { CkRootNode, OpResponseHeaderNode, OptionsScopeComments } from '@contractkit/core';\nimport { printModelDecl } from './print-contract.js';\nimport { printRoute, printSecurity, type CommentBlock } from './print-operation.js';\nimport { printType } from './print-type.js';\nimport { INDENT } from './indent.js';\n\n/** Default line width used for wrapping inline-object and enum types when none is supplied. */\nexport const DEFAULT_PRINT_WIDTH = 80;\n\n// ─── Options block ──────────────────────────────────────────────────────────\n\n/**\n * Render an options value in the form the author wrote it.\n *\n * `wasUnquoted` comes from the parser, so an unquoted subpath import\n * (`PetService: #modules/pet/pet.service.js`) stays unquoted instead of being normalized. The\n * conditions still gate it: a value the grammar could not read back bare — one containing a\n * newline, a `}`, whitespace-then-`#`, or leading/trailing space — is quoted regardless, which\n * matters for values built programmatically rather than parsed.\n */\nfunction quoteOptionsValue(value: string, wasUnquoted = false): string {\n if (/^[a-zA-Z_$][a-zA-Z0-9_$\\-.]*$/.test(value)) return value;\n const roundTripsBare = value.length > 0 && value === value.trim() && !/[\\n\\r}]/.test(value) && !/[ \\t]#/.test(value) && !/^[\"']/.test(value);\n return wasUnquoted && roundTripsBare ? value : `\"${value}\"`;\n}\n\n/**\n * Emit the `key: value` lines of an options `keys`/`services` sub-block, interleaving any\n * retained comments: leading comments before the entry they precede, and trailing comments\n * after the last entry (before the closing `}`). Keeps comments inside these sub-blocks lossless.\n */\nfunction emitOptionsEntries(\n lines: string[],\n entries: Record<string, string>,\n comments: OptionsScopeComments | undefined,\n unquoted: string[] | undefined,\n): void {\n const I2 = INDENT + INDENT;\n const wasUnquoted = new Set(unquoted ?? []);\n for (const [key, value] of Object.entries(entries)) {\n for (const c of comments?.leading?.[key] ?? []) lines.push(`${I2}# ${c}`);\n const inline = comments?.inline?.[key];\n lines.push(`${I2}${key}: ${quoteOptionsValue(value, wasUnquoted.has(key))}${inline !== undefined ? ` # ${inline}` : ''}`);\n }\n for (const c of comments?.trailing ?? []) lines.push(`${I2}# ${c}`);\n}\n\nfunction printOptionsBlock(ast: CkRootNode): string | null {\n const hasMeta = Object.keys(ast.meta).length > 0;\n const hasServices = Object.keys(ast.services).length > 0;\n const hasSecurity = ast.security !== undefined;\n const hasRequestHeaders = (ast.requestHeaders?.length ?? 0) > 0;\n const hasResponseHeaders = (ast.responseHeaders?.length ?? 0) > 0;\n\n const hasBodyComments = ast.optionsComments?.body !== undefined;\n if (!hasMeta && !hasServices && !hasSecurity && !hasRequestHeaders && !hasResponseHeaders && !hasBodyComments) return null;\n\n // A `#` run above the `options` keyword is the file's header comment.\n const lines: string[] = [...(ast.optionsComments?.leading ?? []).map(c => `# ${c}`), 'options {'];\n const body = ast.optionsComments?.body;\n /** Emit the comment run the author wrote directly above this sub-block. */\n const emitLeading = (scope: string) => {\n for (const c of body?.leading?.[scope] ?? []) lines.push(`${INDENT}# ${c}`);\n };\n\n if (hasMeta) {\n emitLeading('keys');\n lines.push(`${INDENT}keys: {`);\n emitOptionsEntries(lines, ast.meta, ast.optionsComments?.keys, ast.optionsUnquoted?.keys);\n lines.push(`${INDENT}}`);\n }\n\n if (hasServices) {\n emitLeading('services');\n lines.push(`${INDENT}services: {`);\n emitOptionsEntries(lines, ast.services, ast.optionsComments?.services, ast.optionsUnquoted?.services);\n lines.push(`${INDENT}}`);\n }\n\n if (hasRequestHeaders) {\n emitLeading('request');\n lines.push(...printOptionsHeaderScope('request', ast.requestHeaders!));\n }\n\n if (hasResponseHeaders) {\n emitLeading('response');\n lines.push(...printOptionsHeaderScope('response', ast.responseHeaders!));\n }\n\n if (hasSecurity) {\n emitLeading('security');\n lines.push(...printSecurity(ast.security!, INDENT, INDENT + INDENT));\n }\n\n for (const c of body?.trailing ?? []) lines.push(`${INDENT}# ${c}`);\n\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction printOptionsHeaderScope(keyword: 'request' | 'response', headers: OpResponseHeaderNode[]): string[] {\n const I2 = INDENT + INDENT;\n const I3 = INDENT + INDENT + INDENT;\n const lines = [`${INDENT}${keyword}: {`, `${I2}headers: {`];\n for (const h of headers) {\n const opt = h.optional ? '?' : '';\n const trail = h.description ? ` # ${h.description}` : '';\n lines.push(`${I3}${h.name}${opt}: ${printType(h.type)}${trail}`);\n }\n lines.push(`${I2}}`);\n lines.push(`${INDENT}}`);\n return lines;\n}\n\n// ─── CK file printer ───────────────────────────────────────────────────────\n\n/**\n * Render a parsed `.ck` AST back to source: options block first, then contracts, then\n * operations, separated by blank lines.\n *\n * Printing is the inverse of parsing — for well-formed source, `printCk(parseCk(text))` returns\n * `text` unchanged. That holds because the AST carries the author's layout alongside the\n * semantics: comment placement (`leadingComments`, `descriptionInline`, `bodyLeadingComments`,\n * the various `trailingComments`), operation body key order (`keyOrder`), blank lines\n * (`blankLineBefore`), and single-line response blocks (`inline`). Preserve those rather than\n * canonicalizing them, or `pnpm format` silently rewrites the user's file. A comment the AST\n * cannot represent is not merely unformatted — it is deleted. See `tests/round-trip.test.ts`.\n *\n * `printWidth` is forwarded to per-model printing for line wrapping inside\n * inline-object types.\n */\nexport function printCk(ast: CkRootNode, printWidth: number = DEFAULT_PRINT_WIDTH): string {\n const parts: string[] = [];\n\n // Options block\n const options = printOptionsBlock(ast);\n if (options) parts.push(options);\n\n // Contracts (models)\n for (const model of ast.models) {\n if (parts.length > 0) parts.push('');\n parts.push(printDeclLeadIn(model.leadingComments, model.descriptionInline ? undefined : model.description) + `contract ${printModelDecl(model, printWidth)}`);\n }\n\n // Operations (routes)\n const emptyBlocks: CommentBlock[] = [];\n const emptyIdx = { value: 0 };\n for (const route of ast.routes) {\n if (parts.length > 0) parts.push('');\n const modPart = route.modifiers?.length ? `(${route.modifiers[0]})` : '';\n parts.push(printDeclLeadIn(route.leadingComments, route.description) + `operation${modPart} ${printRoute(route, emptyBlocks, emptyIdx, Infinity)}`);\n }\n\n // A comment run after the last declaration. Separated by a blank line, matching how every\n // other top-level item is spaced.\n if (ast.trailingComments?.length) {\n if (parts.length > 0) parts.push('');\n for (const c of ast.trailingComments) parts.push(`# ${c}`);\n }\n\n return parts.join('\\n') + '\\n';\n}\n\n/**\n * Build the comment lines that precede a top-level declaration: any standalone block (a section\n * divider and the like, kept separated by the blank line the author wrote), then the declaration's\n * own doc comment on the lines immediately above it. Returns `''` when there is neither.\n */\nfunction printDeclLeadIn(leadingComments: string[] | undefined, description: string | undefined): string {\n const lines: string[] = [];\n if (leadingComments?.length) {\n for (const c of leadingComments) lines.push(`# ${c}`);\n lines.push('');\n }\n if (description) {\n for (const line of description.split('\\n')) lines.push(`# ${line}`);\n }\n return lines.length > 0 ? lines.join('\\n') + '\\n' : '';\n}\n"],"mappings":"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","names":["builders","parseCk","DiagnosticCollector","INDENT","printType","type","kind","constraints","format","undefined","fmt","push","test","min","max","len","regex","length","name","join","args","item","items","map","key","value","values","formatEnumValue","members","discriminator","printInlineObjectCompact","inner","obj","prefix","mode","fields","parts","f","opt","optional","t","nullable","printEnumExpanded","indent","innerIndent","INDENT","v","printField","field","printWidth","ovr","override","dep","deprecated","vis","visibility","mods","def","default","formatDefault","comment","description","trailing","extractTrailingInlineObject","inlineObj","modePart","header","printInlineObjectExpanded","typeStr","fullLine","enumStr","lines","trailingComments","val","String","last","prefixStr","slice","printModelDecl","model","printWidth","type","undefined","printTypeAlias","commentSuffix","description","descriptionInline","modifiers","deprecated","inputCase","outputCase","filter","Boolean","join","mode","modePrefix","baseChain","bases","length","header","name","lines","field","fields","push","printField","INDENT","comment","trailingComments","trailing","extractTrailingInlineObject","prefix","inlineObj","modePart","printInlineObjectExpanded","singleLine","printType","kind","printEnumExpanded","values","SECURITY_NONE","I1","INDENT","I2","repeat","I3","I4","flushBlocks","out","blocks","idx","beforeLine","_indent","value","length","startLine","l","lines","push","printRoute","route","nextRouteStart","path","params","undefined","printParamsBlock","I1","paramsMode","security","printSecurity","I2","op","operations","blankLineBefore","loc","line","printOperation","comment","trailingComments","join","source","indent","mode","prefix","kind","name","inner","INDENT","p","nodes","opt","optional","t","printType","type","nullable","def","default","formatDefault","description","node","CANONICAL_KEY_ORDER","printOperationKey","key","service","sdk","mcp","printMcpBlock","signature","signatureDescription","signaturePolicy","I3","formatSignatureValue","plugins","Object","keys","k","val","entries","printPluginEntry","query","printQueryOrHeaders","queryMode","requestHeadersOptOut","headers","headersMode","request","body","bodies","printContentTypeLine","contentType","bodyType","responses","printResponseBlock","responsesTrailingComments","modPart","modifiers","inlineDescription","descriptionInline","c","leadingComments","split","commentSuffix","method","order","keyOrder","rest","filter","includes","orphaned","keyLines","comments","bodyLeadingComments","bodyTrailingComments","mcpHintTokens","tokens","readOnlyHint","idempotentHint","destructiveHint","openWorldHint","escapeString","title","IDENT_RE","s","replace","inline","printPluginInline","head","block","printPluginBlock","trimStart","i","String","Array","isArray","item","v","fieldKey","test","innerIndent","SECURITY_NONE","fields","leading","trailing","policy","policyDescription","keyword","lineIndent","fieldIndent","f","resp","hasHeaders","optOut","headersOptOut","code","emit","statusCode","inlinable","every","b","map","hasBlock","I4","headersLeadingComments","h","trail","DEFAULT_PRINT_WIDTH","quoteOptionsValue","value","wasUnquoted","test","roundTripsBare","length","trim","emitOptionsEntries","lines","entries","comments","unquoted","I2","INDENT","Set","key","Object","c","leading","push","inline","has","undefined","trailing","printOptionsBlock","ast","hasMeta","keys","meta","hasServices","services","hasSecurity","security","hasRequestHeaders","requestHeaders","hasResponseHeaders","responseHeaders","hasBodyComments","optionsComments","body","map","emitLeading","scope","optionsUnquoted","printOptionsHeaderScope","printSecurity","join","keyword","headers","I3","h","opt","optional","trail","description","name","printType","type","printCk","printWidth","parts","options","model","models","printDeclLeadIn","leadingComments","descriptionInline","printModelDecl","emptyBlocks","emptyIdx","route","routes","modPart","modifiers","printRoute","Infinity","trailingComments","line","split","hardline","join","builders","toDoc","text","lines","trimEnd","split","plugin","languages","name","parsers","extensions","vscodeLanguageIds","parse","_options","diag","DiagnosticCollector","parseCk","astFormat","locStart","locEnd","_node","printers","print","path","options","node","printCk","printWidth"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from 'prettier';\nimport { builders } from 'prettier/doc';\nimport { parseCk, DiagnosticCollector, printCk } from '@contractkit/core';\nimport type { CkRootNode } from '@contractkit/core';\n\nconst { hardline, join } = builders;\n\n/**\n * Wrap printed source as a prettier doc.\n *\n * `printCk` already ends the file with a newline, but splitting on `\\n` turns that into a\n * trailing empty segment, so the text is trimmed first and the final newline re-added as an\n * explicit `hardline`. Dropping it would leave the file without a terminating newline, which\n * fights every editor and lint rule that wants one.\n */\nfunction toDoc(text: string) {\n const lines = text.trimEnd().split('\\n');\n return [join(hardline, lines), hardline];\n}\n\n/**\n * Prettier plugin registering the `.ck` language, its parser, and its printer.\n *\n * The parser is `parseCk` and the printer is `printCk`, both from `@contractkit/core`, so all\n * formatting behaviour lives there rather than here — this module only adapts them to\n * prettier's interface. `tests/format-plugin.test.ts` covers that adapter layer, which a\n * printer-level test cannot reach.\n */\nconst plugin: Plugin<CkRootNode> = {\n languages: [\n {\n name: 'ContractDSL',\n parsers: ['contract-ck'],\n extensions: ['.ck'],\n vscodeLanguageIds: ['contract-ck'],\n },\n ],\n\n parsers: {\n 'contract-ck': {\n parse(text, _options) {\n const diag = new DiagnosticCollector();\n return parseCk(text, '<stdin>', diag);\n },\n astFormat: 'contract-ck',\n locStart: () => 0,\n locEnd: _node => 0,\n },\n },\n\n printers: {\n 'contract-ck': {\n print(path, options) {\n const node = path.node as CkRootNode;\n return toDoc(printCk(node, options.printWidth));\n },\n },\n },\n};\n\nexport default plugin;\nexport { printCk, DEFAULT_PRINT_WIDTH } from '@contractkit/core';\n"],"mappings":";;;;AACA,SAASA,gBAAgB;AACzB,SAASC,SAASC,qBAAqBC,eAAe;AA2DtD,SAASA,WAAAA,UAASC,2BAA2B;AAxD7C,IAAM,EAAEC,UAAUC,KAAI,IAAKC;AAU3B,SAASC,MAAMC,MAAY;AACvB,QAAMC,QAAQD,KAAKE,QAAO,EAAGC,MAAM,IAAA;AACnC,SAAO;IAACN,KAAKD,UAAUK,KAAAA;IAAQL;;AACnC;AAHSG;AAaT,IAAMK,SAA6B;EAC/BC,WAAW;IACP;MACIC,MAAM;MACNC,SAAS;QAAC;;MACVC,YAAY;QAAC;;MACbC,mBAAmB;QAAC;;IACxB;;EAGJF,SAAS;IACL,eAAe;MACXG,MAAMV,MAAMW,UAAQ;AAChB,cAAMC,OAAO,IAAIC,oBAAAA;AACjB,eAAOC,QAAQd,MAAM,WAAWY,IAAAA;MACpC;MACAG,WAAW;MACXC,UAAU,6BAAM,GAAN;MACVC,QAAQC,wBAAAA,UAAS,GAATA;IACZ;EACJ;EAEAC,UAAU;IACN,eAAe;MACXC,MAAMC,MAAMC,SAAO;AACf,cAAMC,OAAOF,KAAKE;AAClB,eAAOxB,MAAMyB,QAAQD,MAAMD,QAAQG,UAAU,CAAA;MACjD;IACJ;EACJ;AACJ;AAEA,IAAA,gBAAerB;","names":["builders","parseCk","DiagnosticCollector","printCk","DEFAULT_PRINT_WIDTH","hardline","join","builders","toDoc","text","lines","trimEnd","split","plugin","languages","name","parsers","extensions","vscodeLanguageIds","parse","_options","diag","DiagnosticCollector","parseCk","astFormat","locStart","locEnd","_node","printers","print","path","options","node","printCk","printWidth"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractkit/prettier-plugin",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "Prettier plugin for ContractKit DSL",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -30,7 +30,7 @@
30
30
  "prettier": "^3.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@contractkit/core": "0.26.1"
33
+ "@contractkit/core": "0.27.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "prettier": "^3.8.3",
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { Plugin } from 'prettier';
2
2
  import { builders } from 'prettier/doc';
3
- import { parseCk, DiagnosticCollector } from '@contractkit/core';
3
+ import { parseCk, DiagnosticCollector, printCk } from '@contractkit/core';
4
4
  import type { CkRootNode } from '@contractkit/core';
5
- import { printCk } from './print-ck.js';
6
5
 
7
6
  const { hardline, join } = builders;
8
7
 
@@ -22,8 +21,8 @@ function toDoc(text: string) {
22
21
  /**
23
22
  * Prettier plugin registering the `.ck` language, its parser, and its printer.
24
23
  *
25
- * The parser is `parseCk` and the printer is `printCk`, so formatting behaviour lives in
26
- * `@contractkit/core` and `print-ck.ts` rather than here — this module only adapts them to
24
+ * The parser is `parseCk` and the printer is `printCk`, both from `@contractkit/core`, so all
25
+ * formatting behaviour lives there rather than here — this module only adapts them to
27
26
  * prettier's interface. `tests/format-plugin.test.ts` covers that adapter layer, which a
28
27
  * printer-level test cannot reach.
29
28
  */
@@ -60,4 +59,4 @@ const plugin: Plugin<CkRootNode> = {
60
59
  };
61
60
 
62
61
  export default plugin;
63
- export { printCk, DEFAULT_PRINT_WIDTH } from './print-ck.js';
62
+ export { printCk, DEFAULT_PRINT_WIDTH } from '@contractkit/core';
package/dist/indent.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare const INDENT = " ";
2
- //# sourceMappingURL=indent.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"indent.d.ts","sourceRoot":"","sources":["../src/indent.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,SAAS,CAAC"}
@@ -1,20 +0,0 @@
1
- import type { CkRootNode } from '@contractkit/core';
2
- /** Default line width used for wrapping inline-object and enum types when none is supplied. */
3
- export declare const DEFAULT_PRINT_WIDTH = 80;
4
- /**
5
- * Render a parsed `.ck` AST back to source: options block first, then contracts, then
6
- * operations, separated by blank lines.
7
- *
8
- * Printing is the inverse of parsing — for well-formed source, `printCk(parseCk(text))` returns
9
- * `text` unchanged. That holds because the AST carries the author's layout alongside the
10
- * semantics: comment placement (`leadingComments`, `descriptionInline`, `bodyLeadingComments`,
11
- * the various `trailingComments`), operation body key order (`keyOrder`), blank lines
12
- * (`blankLineBefore`), and single-line response blocks (`inline`). Preserve those rather than
13
- * canonicalizing them, or `pnpm format` silently rewrites the user's file. A comment the AST
14
- * cannot represent is not merely unformatted — it is deleted. See `tests/round-trip.test.ts`.
15
- *
16
- * `printWidth` is forwarded to per-model printing for line wrapping inside
17
- * inline-object types.
18
- */
19
- export declare function printCk(ast: CkRootNode, printWidth?: number): string;
20
- //# sourceMappingURL=print-ck.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"print-ck.d.ts","sourceRoot":"","sources":["../src/print-ck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA8C,MAAM,mBAAmB,CAAC;AAMhG,+FAA+F;AAC/F,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA6GtC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,GAAE,MAA4B,GAAG,MAAM,CA8BzF"}
@@ -1,13 +0,0 @@
1
- import type { ModelNode } from '@contractkit/core';
2
- /**
3
- * Render a `contract` body from its `Name: ...` onward (the `contract ` keyword and any
4
- * doc comment written above the declaration are prepended by the caller). Handles both the
5
- * type-alias form (`Name: <type>`) and the regular field-block form, including modifiers,
6
- * base chain, and per-field printing. Any `model.trailingComments` (comments after the last
7
- * field, before `}`) are emitted as indented `# text` lines so they round-trip.
8
- *
9
- * `model.description` is emitted here only when `model.descriptionInline` says the author wrote
10
- * it on the header line; otherwise it belongs above the declaration and the caller emits it.
11
- */
12
- export declare function printModelDecl(model: ModelNode, printWidth?: number): string;
13
- //# sourceMappingURL=print-contract.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"print-contract.d.ts","sourceRoot":"","sources":["../src/print-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAgChF"}
@@ -1,48 +0,0 @@
1
- import type { OpRouteNode, SecurityNode } from '@contractkit/core';
2
- type CommentEntry = {
3
- line: number;
4
- text: string;
5
- };
6
- /** A run of consecutive-line orphan comments, keyed by the source line it starts on. */
7
- export type CommentBlock = {
8
- startLine: number;
9
- lines: string[];
10
- };
11
- /** Group sorted orphan comment entries into consecutive-line blocks. */
12
- export declare function groupComments(entries: CommentEntry[]): CommentBlock[];
13
- /**
14
- * Emit any comment blocks whose startLine is < beforeLine.
15
- * Lines are emitted verbatim — they already carry their original indentation.
16
- */
17
- export declare function flushBlocks(out: string[], blocks: CommentBlock[], idx: {
18
- value: number;
19
- }, beforeLine: number, _indent?: string): void;
20
- /**
21
- * Render an `operation` route body from its `path: {` onward (the `operation` keyword, any
22
- * modifier, and the route's leading comments are prepended by the caller). Emits the
23
- * params/security blocks and each HTTP operation, interleaving orphan comment `blocks` at their
24
- * original source positions — `idx` tracks how far through `blocks` we've consumed, and
25
- * `nextRouteStart` bounds the flush to comments before the following route. Any
26
- * `route.trailingComments` (comments after the last operation, before `}`) are emitted before
27
- * the closing brace so they round-trip.
28
- *
29
- * Blank lines between operations come from each operation's `blankLineBefore`, so the author's
30
- * spacing survives rather than being normalized to one rule or the other.
31
- */
32
- export declare function printRoute(route: OpRouteNode, blocks: CommentBlock[], idx: {
33
- value: number;
34
- }, nextRouteStart: number): string;
35
- /**
36
- * Print a `security:` declaration. Returns `["${indent}security: none"]` for the public-endpoint
37
- * sentinel, a multi-line block when `policy` is set or the block carries comments, or an empty
38
- * array when there is nothing meaningful to emit.
39
- *
40
- * A block holding only comments still prints: the rationale for a policy floor is the reason
41
- * authors write in here, and collapsing the braces away would delete it.
42
- *
43
- * @param indent indentation for the `security` keyword line
44
- * @param innerIndent indentation for field lines inside the block
45
- */
46
- export declare function printSecurity(security: SecurityNode, indent?: string, innerIndent?: string): string[];
47
- export {};
48
- //# sourceMappingURL=print-operation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"print-operation.d.ts","sourceRoot":"","sources":["../src/print-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,WAAW,EAIX,YAAY,EAOf,MAAM,mBAAmB,CAAC;AAY3B,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACnD,wFAAwF;AACxF,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElE,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAarE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,SAAK,QAK1H;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CA8B7H;AAmOD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAK,EAAE,WAAW,SAAK,GAAG,MAAM,EAAE,CAkB7F"}
@@ -1,26 +0,0 @@
1
- import type { ContractTypeNode, FieldNode, InlineObjectTypeNode } from '@contractkit/core';
2
- /** Render a `ContractTypeNode` back to its `.ck` source string. */
3
- export declare function printType(type: ContractTypeNode): string;
4
- /** Multi-line enum form — one value per line, used when single-line would exceed print width. */
5
- export declare function printEnumExpanded(values: string[], indent: string): string;
6
- /** Print a full field declaration, including visibility, default, and inline comment.
7
- * Modifier order is canonical: override → deprecated → readonly|writeonly → type. */
8
- export declare function printField(field: FieldNode, indent: string, printWidth?: number): string;
9
- /** Print inline-object fields expanded (used when an inline brace object trails a type alias).
10
- * Any `trailingComments` (comments after the last field, before `}`) are emitted as indented
11
- * `# text` lines after the fields, matching how model bodies round-trip trailing comments. */
12
- export declare function printInlineObjectExpanded(obj: InlineObjectTypeNode, indent: string, printWidth?: number): string[];
13
- /** Format a single enum value: bare identifier stays bare; anything else gets double-quoted. */
14
- export declare function formatEnumValue(v: string): string;
15
- /** Format a default value: quote strings that aren't valid bare identifiers. */
16
- export declare function formatDefault(val: string | number | boolean): string;
17
- /**
18
- * Detect whether the last member of a type is an inline brace object, and if so
19
- * return the prefix type string and the inline object for expanded printing.
20
- * Returns null if the type doesn't end with an inline object.
21
- */
22
- export declare function extractTrailingInlineObject(type: ContractTypeNode): {
23
- prefix: string | null;
24
- inlineObj: InlineObjectTypeNode;
25
- } | null;
26
- //# sourceMappingURL=print-type.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"print-type.d.ts","sourceRoot":"","sources":["../src/print-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAK3F,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA0CxD;AAeD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG1E;AAID;qFACqF;AACrF,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,CAgC5F;AAED;;8FAE8F;AAC9F,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAE,MAAW,GAAG,MAAM,EAAE,CAMtH;AAID,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAKpE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG;IACjE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;CACnC,GAAG,IAAI,CAYP"}
package/src/indent.ts DELETED
@@ -1 +0,0 @@
1
- export const INDENT = ' ';
package/src/print-ck.ts DELETED
@@ -1,179 +0,0 @@
1
- import type { CkRootNode, OpResponseHeaderNode, OptionsScopeComments } from '@contractkit/core';
2
- import { printModelDecl } from './print-contract.js';
3
- import { printRoute, printSecurity, type CommentBlock } from './print-operation.js';
4
- import { printType } from './print-type.js';
5
- import { INDENT } from './indent.js';
6
-
7
- /** Default line width used for wrapping inline-object and enum types when none is supplied. */
8
- export const DEFAULT_PRINT_WIDTH = 80;
9
-
10
- // ─── Options block ──────────────────────────────────────────────────────────
11
-
12
- /**
13
- * Render an options value in the form the author wrote it.
14
- *
15
- * `wasUnquoted` comes from the parser, so an unquoted subpath import
16
- * (`PetService: #modules/pet/pet.service.js`) stays unquoted instead of being normalized. The
17
- * conditions still gate it: a value the grammar could not read back bare — one containing a
18
- * newline, a `}`, whitespace-then-`#`, or leading/trailing space — is quoted regardless, which
19
- * matters for values built programmatically rather than parsed.
20
- */
21
- function quoteOptionsValue(value: string, wasUnquoted = false): string {
22
- if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
23
- const roundTripsBare = value.length > 0 && value === value.trim() && !/[\n\r}]/.test(value) && !/[ \t]#/.test(value) && !/^["']/.test(value);
24
- return wasUnquoted && roundTripsBare ? value : `"${value}"`;
25
- }
26
-
27
- /**
28
- * Emit the `key: value` lines of an options `keys`/`services` sub-block, interleaving any
29
- * retained comments: leading comments before the entry they precede, and trailing comments
30
- * after the last entry (before the closing `}`). Keeps comments inside these sub-blocks lossless.
31
- */
32
- function emitOptionsEntries(
33
- lines: string[],
34
- entries: Record<string, string>,
35
- comments: OptionsScopeComments | undefined,
36
- unquoted: string[] | undefined,
37
- ): void {
38
- const I2 = INDENT + INDENT;
39
- const wasUnquoted = new Set(unquoted ?? []);
40
- for (const [key, value] of Object.entries(entries)) {
41
- for (const c of comments?.leading?.[key] ?? []) lines.push(`${I2}# ${c}`);
42
- const inline = comments?.inline?.[key];
43
- lines.push(`${I2}${key}: ${quoteOptionsValue(value, wasUnquoted.has(key))}${inline !== undefined ? ` # ${inline}` : ''}`);
44
- }
45
- for (const c of comments?.trailing ?? []) lines.push(`${I2}# ${c}`);
46
- }
47
-
48
- function printOptionsBlock(ast: CkRootNode): string | null {
49
- const hasMeta = Object.keys(ast.meta).length > 0;
50
- const hasServices = Object.keys(ast.services).length > 0;
51
- const hasSecurity = ast.security !== undefined;
52
- const hasRequestHeaders = (ast.requestHeaders?.length ?? 0) > 0;
53
- const hasResponseHeaders = (ast.responseHeaders?.length ?? 0) > 0;
54
-
55
- const hasBodyComments = ast.optionsComments?.body !== undefined;
56
- if (!hasMeta && !hasServices && !hasSecurity && !hasRequestHeaders && !hasResponseHeaders && !hasBodyComments) return null;
57
-
58
- // A `#` run above the `options` keyword is the file's header comment.
59
- const lines: string[] = [...(ast.optionsComments?.leading ?? []).map(c => `# ${c}`), 'options {'];
60
- const body = ast.optionsComments?.body;
61
- /** Emit the comment run the author wrote directly above this sub-block. */
62
- const emitLeading = (scope: string) => {
63
- for (const c of body?.leading?.[scope] ?? []) lines.push(`${INDENT}# ${c}`);
64
- };
65
-
66
- if (hasMeta) {
67
- emitLeading('keys');
68
- lines.push(`${INDENT}keys: {`);
69
- emitOptionsEntries(lines, ast.meta, ast.optionsComments?.keys, ast.optionsUnquoted?.keys);
70
- lines.push(`${INDENT}}`);
71
- }
72
-
73
- if (hasServices) {
74
- emitLeading('services');
75
- lines.push(`${INDENT}services: {`);
76
- emitOptionsEntries(lines, ast.services, ast.optionsComments?.services, ast.optionsUnquoted?.services);
77
- lines.push(`${INDENT}}`);
78
- }
79
-
80
- if (hasRequestHeaders) {
81
- emitLeading('request');
82
- lines.push(...printOptionsHeaderScope('request', ast.requestHeaders!));
83
- }
84
-
85
- if (hasResponseHeaders) {
86
- emitLeading('response');
87
- lines.push(...printOptionsHeaderScope('response', ast.responseHeaders!));
88
- }
89
-
90
- if (hasSecurity) {
91
- emitLeading('security');
92
- lines.push(...printSecurity(ast.security!, INDENT, INDENT + INDENT));
93
- }
94
-
95
- for (const c of body?.trailing ?? []) lines.push(`${INDENT}# ${c}`);
96
-
97
- lines.push('}');
98
- return lines.join('\n');
99
- }
100
-
101
- function printOptionsHeaderScope(keyword: 'request' | 'response', headers: OpResponseHeaderNode[]): string[] {
102
- const I2 = INDENT + INDENT;
103
- const I3 = INDENT + INDENT + INDENT;
104
- const lines = [`${INDENT}${keyword}: {`, `${I2}headers: {`];
105
- for (const h of headers) {
106
- const opt = h.optional ? '?' : '';
107
- const trail = h.description ? ` # ${h.description}` : '';
108
- lines.push(`${I3}${h.name}${opt}: ${printType(h.type)}${trail}`);
109
- }
110
- lines.push(`${I2}}`);
111
- lines.push(`${INDENT}}`);
112
- return lines;
113
- }
114
-
115
- // ─── CK file printer ───────────────────────────────────────────────────────
116
-
117
- /**
118
- * Render a parsed `.ck` AST back to source: options block first, then contracts, then
119
- * operations, separated by blank lines.
120
- *
121
- * Printing is the inverse of parsing — for well-formed source, `printCk(parseCk(text))` returns
122
- * `text` unchanged. That holds because the AST carries the author's layout alongside the
123
- * semantics: comment placement (`leadingComments`, `descriptionInline`, `bodyLeadingComments`,
124
- * the various `trailingComments`), operation body key order (`keyOrder`), blank lines
125
- * (`blankLineBefore`), and single-line response blocks (`inline`). Preserve those rather than
126
- * canonicalizing them, or `pnpm format` silently rewrites the user's file. A comment the AST
127
- * cannot represent is not merely unformatted — it is deleted. See `tests/round-trip.test.ts`.
128
- *
129
- * `printWidth` is forwarded to per-model printing for line wrapping inside
130
- * inline-object types.
131
- */
132
- export function printCk(ast: CkRootNode, printWidth: number = DEFAULT_PRINT_WIDTH): string {
133
- const parts: string[] = [];
134
-
135
- // Options block
136
- const options = printOptionsBlock(ast);
137
- if (options) parts.push(options);
138
-
139
- // Contracts (models)
140
- for (const model of ast.models) {
141
- if (parts.length > 0) parts.push('');
142
- parts.push(printDeclLeadIn(model.leadingComments, model.descriptionInline ? undefined : model.description) + `contract ${printModelDecl(model, printWidth)}`);
143
- }
144
-
145
- // Operations (routes)
146
- const emptyBlocks: CommentBlock[] = [];
147
- const emptyIdx = { value: 0 };
148
- for (const route of ast.routes) {
149
- if (parts.length > 0) parts.push('');
150
- const modPart = route.modifiers?.length ? `(${route.modifiers[0]})` : '';
151
- parts.push(printDeclLeadIn(route.leadingComments, route.description) + `operation${modPart} ${printRoute(route, emptyBlocks, emptyIdx, Infinity)}`);
152
- }
153
-
154
- // A comment run after the last declaration. Separated by a blank line, matching how every
155
- // other top-level item is spaced.
156
- if (ast.trailingComments?.length) {
157
- if (parts.length > 0) parts.push('');
158
- for (const c of ast.trailingComments) parts.push(`# ${c}`);
159
- }
160
-
161
- return parts.join('\n') + '\n';
162
- }
163
-
164
- /**
165
- * Build the comment lines that precede a top-level declaration: any standalone block (a section
166
- * divider and the like, kept separated by the blank line the author wrote), then the declaration's
167
- * own doc comment on the lines immediately above it. Returns `''` when there is neither.
168
- */
169
- function printDeclLeadIn(leadingComments: string[] | undefined, description: string | undefined): string {
170
- const lines: string[] = [];
171
- if (leadingComments?.length) {
172
- for (const c of leadingComments) lines.push(`# ${c}`);
173
- lines.push('');
174
- }
175
- if (description) {
176
- for (const line of description.split('\n')) lines.push(`# ${line}`);
177
- }
178
- return lines.length > 0 ? lines.join('\n') + '\n' : '';
179
- }