UNPKG

core-types

Version:

Generic type declarations for e.g. TypeScript, GraphQL and JSON Schema

158 lines (157 loc) 5.68 kB
import { mergeLocations } from './location.js'; import { ensureArray, uniq } from './util.js'; export function mergeAnnotations(nodes) { const nonEmpty = (t) => !!t; const join = (t, separator = '\n') => uniq(t.filter(nonEmpty)).join(separator).trim(); const name = nodes.find(n => n.name)?.name; const title = join(nodes.map(n => n.title), ', '); const description = join(nodes.map(n => n.description)); const examples = uniq([].concat(...nodes.map(n => ensureArray(n.examples))) .filter(nonEmpty)); const _default = join(nodes.map(n => n.default)); const see = uniq([].concat(...nodes.map(n => ensureArray(n.see))) .filter(nonEmpty)); const comment = join(nodes.map(n => n.comment)); const loc = mergeLocations(nodes.map(n => n.loc)); return { ...(name ? { name } : {}), ...(title ? { title } : {}), ...(description ? { description } : {}), ...(examples.length > 0 ? { examples: arrayOrSingle(examples) } : {}), ...(_default ? { default: _default } : {}), ...(see.length > 0 ? { see: arrayOrSingle(see) } : {}), ...(comment ? { comment } : {}), ...(loc ? { loc } : {}), }; } export function extractAnnotations(node) { const { title, description, examples, default: _default, comment, see, } = node; return { ...(title ? { title } : {}), ...(description ? { description } : {}), ...(examples ? { examples } : {}), ...(_default ? { default: _default } : {}), ...(comment ? { comment } : {}), ...(see ? { see } : {}), }; } export function wrapWhitespace(text) { if (!text.includes("\n")) return text.startsWith(" ") ? text : ` ${text}`; return [ "*", text.split("\n").map(line => ` * ${line}`).join("\n"), " " ].join("\n"); } function makeSafeComment(text) { return text.replace(/\*\//g, '*\\/'); } export function stringifyAnnotations(node, { includeComment = false, formatWhitespace = false, } = {}) { const { description, examples, default: _default, comment, see } = node; const fullComment = makeSafeComment([ description, ...(examples == undefined ? [] : [ formatExamples(ensureArray(examples)) ]), ...(_default === undefined ? [] : [ formatDefault(_default) ]), ...(see == undefined ? [] : [ formatSee(ensureArray(see)) ]), ...(includeComment ? [comment] : []), ] .filter(v => v) .join("\n\n") .trim()); return formatWhitespace && fullComment ? wrapWhitespace(fullComment) : fullComment; } export function stripAnnotations(node, recursive = true) { const { comment, description, default: _default, examples, see, title, ...rest } = node; const filteredNode = rest; if (recursive) { if (filteredNode.type === 'and') return { ...filteredNode, and: filteredNode.and.map(n => stripAnnotations(n, true)), }; else if (filteredNode.type === 'or') return { ...filteredNode, or: filteredNode.or.map(n => stripAnnotations(n, true)), }; else if (filteredNode.type === 'array') return { ...filteredNode, elementType: stripAnnotations(filteredNode.elementType, true), }; else if (filteredNode.type === 'tuple') return { ...filteredNode, elementTypes: filteredNode.elementTypes.map(n => stripAnnotations(n, true)), additionalItems: typeof filteredNode.additionalItems === 'object' ? stripAnnotations(filteredNode.additionalItems, true) : filteredNode.additionalItems, }; else if (filteredNode.type === 'object') return { ...filteredNode, properties: Object.fromEntries(Object.keys(filteredNode.properties).map(key => [ key, { ...filteredNode.properties[key], node: stripAnnotations(filteredNode.properties[key].node, true), } ])), additionalProperties: typeof filteredNode.additionalProperties === 'object' ? stripAnnotations(filteredNode.additionalProperties, true) : filteredNode.additionalProperties, }; } return filteredNode; } export function arrayOrSingle(arr) { if (arr.length === 1) return arr[0]; return arr; } export function formatExamples(examples) { const lines = examples.map(example => "@example\n" + indent(stringify(example).split("\n"), 4)) .join("\n"); return lines.trim(); } export function formatDefault(_default) { const lines = [ "@default", indent(stringify(_default).split("\n"), 4) ] .join("\n"); return lines.trim(); } export function formatSee(see) { const lines = see.map(see => "@see " + stringify(see)) .join("\n"); return lines.trim(); } export function stringify(value) { return typeof value === "string" ? value : JSON.stringify(value, null, 2); } function indent(lines, indent, bullet = false) { return lines .map((line, index) => { const prefix = index === 0 && bullet ? (' '.repeat(indent - 2) + "* ") : ' '.repeat(indent); return prefix + line; }) .join("\n"); }