coldstitch
Version:
A code generation library, that helps you craft code snippets in code for multiple languages
1 lines • 16.2 kB
Source Map (JSON)
{"version":3,"file":"index-lPwhCPkI.cjs","sources":["../src/format.ts","../src/types.ts","../src/utils.ts","../src/code.ts"],"sourcesContent":["export type CodeIndentOptions = {\n indentSize: number;\n indentChar: \" \" | \"\\t\";\n};\n\nexport type CodeFormatOptions = CodeIndentOptions & {\n stringQuote: '\"' | \"'\";\n};\n\nexport const TWO_SPACES: CodeFormatOptions = {\n indentSize: 2,\n indentChar: \" \",\n stringQuote: '\"',\n};\n\nexport const FOUR_SPACES: CodeFormatOptions = {\n indentSize: 4,\n indentChar: \" \",\n stringQuote: '\"',\n};\n\nexport const DEFAULT_FORMAT_OPTIONS = TWO_SPACES;\n\nconst LANGUAGE_OPTIONS: Record<string, Partial<CodeFormatOptions>> = {\n js: TWO_SPACES,\n python: FOUR_SPACES,\n swift: FOUR_SPACES,\n java: FOUR_SPACES,\n kotlin: FOUR_SPACES,\n};\n\nexport function formatOptionsForLanguage(language?: string): CodeFormatOptions {\n return {\n ...DEFAULT_FORMAT_OPTIONS,\n ...(LANGUAGE_OPTIONS[language ?? \"\"] ?? {}),\n };\n}\n","export interface TypeRef {\n namespace: string;\n name: string;\n language: string;\n toString(): string;\n}\n\nexport class ImportResolver<T extends TypeRef = TypeRef> {\n resolve(types: T[]): string {\n throw new Error(\"Not implemented\");\n }\n}\n\nexport function isTypeRef(value: any): value is TypeRef {\n return value && typeof value === \"object\" && \"namespace\" in value && \"name\" in value;\n}\n\nexport type CustomFormatter = (value: any) => string | undefined;\n","import { CodeFormatOptions, CodeIndentOptions, DEFAULT_FORMAT_OPTIONS } from \"./format\";\nimport { TypeRef } from \"./types\";\n\ntype ObjectBoundaryTokens = [string, string];\n\nexport interface ObjectFormatter {\n objectTokens: ObjectBoundaryTokens;\n arrayTokens: ObjectBoundaryTokens;\n inlineArrayElements?: boolean;\n assignToken: string;\n formatKey(key: string): string;\n formatValue(value: any): string;\n}\n\ntype IndentLevel = {\n indentLevel?: number;\n};\n\ntype Reference = Record<string, any> | any[];\n\nexport function stringify(\n reference: Reference,\n formatter: ObjectFormatter,\n options: Partial<CodeFormatOptions> & IndentLevel = DEFAULT_FORMAT_OPTIONS,\n): string {\n const isArray = Array.isArray(reference);\n const [startToken, endToken] = isArray ? formatter.arrayTokens : formatter.objectTokens;\n const {\n indentSize,\n indentChar,\n indentLevel = 0,\n inlineArrayElements = true,\n } = {\n ...DEFAULT_FORMAT_OPTIONS,\n ...options,\n };\n const indent = indentChar.repeat(indentSize * indentLevel);\n const childIndent = indentChar.repeat(indentSize * (indentLevel + 1));\n\n const processValue = (value: any, key?: string): string => {\n let formattedValue: string;\n\n if (Array.isArray(value)) {\n formattedValue = stringify(value, formatter, {\n ...options,\n indentLevel: inlineArrayElements ? indentLevel : indentLevel + 1,\n });\n } else if (isPlainObject(value)) {\n formattedValue = stringify(value, formatter, {\n ...options,\n indentLevel: indentLevel + 1,\n });\n } else if (typeof value === \"function\") {\n formattedValue = `\"${value.name} function reference\"`;\n } else {\n formattedValue = formatter.formatValue(value);\n }\n\n if (isArray) {\n return formattedValue;\n } else {\n const formattedKey = formatter.formatKey(key!);\n return `${formattedKey}${formatter.assignToken}${formattedValue}`;\n }\n };\n\n if (isArray) {\n const values = (reference as any[]).map((value) => processValue(value));\n if (inlineArrayElements) {\n return `${startToken}${values.join(\", \")}${endToken}`;\n } else {\n if (values.length === 0) {\n return `${startToken}${endToken}`;\n }\n return `${startToken}\\n${childIndent}${values.join(\",\\n\" + childIndent)}\\n${indent}${endToken}`;\n }\n } else {\n const lines: string[] = [];\n for (const key in reference as Record<string, any>) {\n lines.push(processValue((reference as Record<string, any>)[key], key));\n }\n if (lines.length === 0) {\n return `${startToken}${endToken}`;\n }\n return `${startToken}\\n${childIndent}${lines.join(\",\\n\" + childIndent)}\\n${indent}${endToken}`;\n }\n}\n\nexport function createTemplateStringsArray(literals: string[]): TemplateStringsArray {\n const templateStringsArray = literals;\n Object.defineProperty(templateStringsArray, \"raw\", {\n value: literals,\n });\n return templateStringsArray as unknown as TemplateStringsArray;\n}\n\nexport function resolveLineIndentation(line: string): CodeIndentOptions {\n let indentChar: \"\\t\" | \" \" = \" \";\n let indentSize: number = 0;\n\n if (line.startsWith(\"\\t\")) {\n indentChar = \"\\t\";\n // force non-null is safe because we already checked if the line starts with a tab\n indentSize = line.match(/^\\t+/)![0].length;\n }\n\n // Check if the line starts with a space\n if (line.startsWith(\" \")) {\n indentChar = \" \";\n // force non-null is safe because we already checked if the line starts with a space\n indentSize = line.match(/^ +/)![0].length;\n }\n\n return { indentChar, indentSize };\n}\n\nexport function getLineIndentation(line: string): string {\n return /^(\\s*)/.exec(line)?.[1] ?? \"\";\n}\n\nexport function resolveIndentation(code: string): CodeIndentOptions {\n const lines = code.split(\"\\n\");\n\n const indentedLine = lines.find((line) => line.trimStart() !== line);\n if (!indentedLine) {\n // TODO better default per language. Ingerit from toCodeString options\n return { indentChar: \" \", indentSize: 2 };\n }\n return resolveLineIndentation(indentedLine);\n}\n\nexport function lastItem<T>(array: T[]): T | undefined {\n return array[array.length - 1];\n}\n\nexport function groupTypesByNamespace<T extends TypeRef = TypeRef>(types: T[]): Record<string, T[]> {\n const packages: Record<string, T[]> = {};\n types.forEach((type) => {\n if (!packages[type.namespace]) {\n packages[type.namespace] = [];\n }\n packages[type.namespace].push(type);\n });\n return packages;\n}\n\nexport function escapeStringQuotes(value: string, quoteChar: string = '\"'): string {\n return value.replace(new RegExp(quoteChar, \"g\"), `\\\\${quoteChar}`);\n}\n\nexport function isPlainObject(value: any): boolean {\n return value !== null && typeof value === \"object\" && value.constructor === Object;\n}\n\nexport function isScalar(value: any): boolean {\n return value === null || (typeof value !== \"object\" && typeof value !== \"function\" && !Array.isArray(value));\n}\n","import { CodeFormatOptions, formatOptionsForLanguage } from \"./format\";\nimport { ImportResolver, TypeRef, isTypeRef } from \"./types\";\nimport { createTemplateStringsArray, getLineIndentation, lastItem } from \"./utils\";\n\nconst omitLineSymbol = Symbol(\"omitLine\");\n\nconst OMIT_LINE_TOKEN = \"__COLDSNIP_OMIT_LINE__\";\n\n/**\n * A symbol that can be used in a code template to indicate that the line should be omitted.\n * Useful when doing logic that may or not include a line in the output.\n *\n * ```ts\n * const code = code`\n * ${isStrict ? \"'useStrict';\" : omitLine()}\n * console.log(\"Hello, world!\");\n * `;\n *\n * @returns symbol that omits the line when present.\n */\nexport function omitLine() {\n return omitLineSymbol;\n}\n\n/**\n * The interface that represents a code template public API.\n */\nexport interface Code {\n readonly imports: TypeRef[];\n toString(): string;\n toCodeString(options?: CodeFormatOptions): string;\n}\n\nexport class CodeImpl implements Code {\n private literals: TemplateStringsArray;\n private values: any[];\n\n constructor(literals: TemplateStringsArray, values: any[] = []) {\n this.literals = literals;\n this.values = values;\n }\n\n static fromString(value: string): Code {\n return new CodeImpl(createTemplateStringsArray([value]));\n }\n\n get imports(): TypeRef[] {\n const imports: TypeRef[] = [];\n this.values.forEach((value) => {\n if (isTypeRef(value)) {\n imports.push(value);\n }\n });\n return imports;\n }\n\n private resolveNestedCode(code: Code, currentIndent: string, options: CodeFormatOptions): string {\n const codeString = code.toCodeString(options);\n const lines = codeString.split(\"\\n\");\n const firstLine = lines.shift() ?? \"\";\n const rest = lines.map((line) => currentIndent + line).join(\"\\n\");\n return firstLine + \"\\n\" + rest;\n }\n\n private resolveValues(options: CodeFormatOptions): any[] {\n const imports = this.imports;\n\n const resolveValue = (value: any, index: number): any => {\n if (value instanceof ImportResolver) {\n return value.resolve(imports);\n }\n if (value === omitLineSymbol) {\n // TODO: we could improve this by actually manipulating the literals to remove\n // the content that represents the omitted line, but for now it just adds a token\n // to the line that indicates it should be filtered out later\n return OMIT_LINE_TOKEN;\n }\n if (value instanceof CodeImpl) {\n // get the line right before the value interpolation and resolve its indentation\n const currentLine = lastItem(this.literals[index]?.split(\"\\n\") ?? []) ?? \"\";\n const currentIndent = getLineIndentation(currentLine);\n return this.resolveNestedCode(value, currentIndent, options).trim();\n }\n if (Array.isArray(value)) {\n return value.map((item) => resolveValue(item, index)).join(\"\\n\");\n }\n if (isTypeRef(value)) {\n return value.toString();\n }\n if (value === false || value === undefined) {\n return \"\";\n }\n return value;\n };\n\n return this.values.map(resolveValue);\n }\n\n private get unindent(): string {\n const first = this.literals[0];\n const firstLine = first.startsWith(\"\\n\") ? first.slice(1) : first;\n return getLineIndentation(firstLine);\n }\n\n toString(): string {\n return this.toCodeString();\n }\n\n toCodeString(options?: CodeFormatOptions): string {\n const language = this.imports[0]?.language;\n const formatOptions = options ?? formatOptionsForLanguage(language);\n\n const unindent = this.unindent;\n const resolvedValues = this.resolveValues(formatOptions);\n return String.raw(this.literals, ...resolvedValues)\n .split(\"\\n\")\n .filter((line) => line.includes(OMIT_LINE_TOKEN) === false)\n .map((line) => (line.startsWith(unindent) ? line.substring(unindent.length).trimEnd() : line))\n .join(\"\\n\")\n .trim();\n }\n}\n\n/**\n * A tagged template literal that represents code templates.\n *\n * ```ts\n * const code = code`\n * console.log(\"hello coldsnip!\");\n * `;\n *\n * @param literals The template literals.\n * @param values The values to interpolate.\n * @returns a `Code` instance that can be serialized to string using `toCodeString` or `toString`.\n */\nexport function code(literals: TemplateStringsArray, ...values: any[]): Code {\n return new CodeImpl(literals, values);\n}\n"],"names":["TWO_SPACES","FOUR_SPACES","DEFAULT_FORMAT_OPTIONS","LANGUAGE_OPTIONS","formatOptionsForLanguage","language","ImportResolver","types","isTypeRef","value","stringify","reference","formatter","options","isArray","startToken","endToken","indentSize","indentChar","indentLevel","inlineArrayElements","indent","childIndent","processValue","key","formattedValue","isPlainObject","values","lines","createTemplateStringsArray","literals","templateStringsArray","getLineIndentation","line","_a","lastItem","array","groupTypesByNamespace","packages","type","escapeStringQuotes","quoteChar","isScalar","omitLineSymbol","OMIT_LINE_TOKEN","omitLine","CodeImpl","imports","code","currentIndent","firstLine","rest","resolveValue","index","currentLine","item","first","formatOptions","unindent","resolvedValues"],"mappings":"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"}