1 | {"version":3,"file":"literate.js","sourceRoot":"","sources":["../src/literate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A tiny module to include annotated (working!) code snippets into the documentation\n *\n * Not using 'literate-programming' or 'erasumus' projects because they work\n * the other way around: take code from MarkDown, save it as a file, then\n * execute that.\n *\n * We do the opposite: start from source code annotated with MarkDown and\n * extract it into (larger) MarkDown files.\n *\n * Including into README\n * ---------------------\n *\n * To include the examples directly into the README, make a link to the\n * annotated TypeScript file on a line by itself, and make sure the\n * extension of the file ends in `.lit.ts`.\n *\n * For example:\n *\n * [example](test/integ.bucket.lit.ts)\n *\n * Annotating source\n * -----------------\n *\n * We use the triple-slash comment for our directives, since it's valid TypeScript\n * and are treated as regular comments if not the very first thing in the file.\n *\n * By default, the whole file is included, unless the source contains the statement\n * \"/// !show\". For example:\n *\n * a\n * /// !show\n * b\n * /// !hide\n * c\n *\n * In this example, only 'b' would be included in the output. A single file may\n * switching including and excluding on and off multiple times in the same file.\n *\n * Other lines starting with triple slashes will be rendered as Markdown in between\n * the source lines. For example:\n *\n * const x = 1;\n * /// Now we're going to print x:\n * console.log(x);\n *\n * Will be rendered as:\n *\n * ```ts\n * const x = 1;\n * ```\n *\n * Now we're going to print x:\n *\n * ```ts\n * console.log(x);\n * ```\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Convert an annotated TypeScript source file to MarkDown\n */\nexport function typescriptSourceToMarkdown(lines: string[], codeBlockAnnotations: string[]): string[] {\n const relevantLines = findRelevantLines(lines);\n const markdownLines = markdownify(relevantLines, codeBlockAnnotations);\n return markdownLines;\n}\n\nexport interface LoadedFile {\n readonly fullPath: string;\n readonly lines: string[];\n}\n\nexport type FileLoader = (relativePath: string) => LoadedFile;\n\n/**\n * Given MarkDown source, find source files to include and render\n *\n * We recognize links on a line by themselves if the link text starts\n * with the string \"example\" (case insensitive). For example:\n *\n * [example](test/integ.bucket.ts)\n */\nexport function includeAndRenderExamples(lines: string[], loader: FileLoader, projectRoot: string): string[] {\n const ret: string[] = [];\n\n const regex = /^\\[([^\\]]*)\\]\\(([^)]+\\.lit\\.ts)\\)/i;\n for (const line of lines) {\n const m = regex.exec(line);\n if (m) {\n // Found an include\n const filename = m[2];\n // eslint-disable-next-line no-await-in-loop\n const { lines: source, fullPath } = loader(filename);\n // 'lit' source attribute will make snippet compiler know to extract the same source\n // Needs to be relative to the project root.\n const imported = typescriptSourceToMarkdown(source, [`lit=${toUnixPath(path.relative(projectRoot, fullPath))}`]);\n ret.push(...imported);\n } else {\n ret.push(line);\n }\n }\n\n return ret;\n}\n\n/**\n * Load a file into a string array\n */\nexport function loadFromFile(fileName: string): string[] {\n const content = fs.readFileSync(fileName, { encoding: 'utf-8' });\n return contentToLines(content);\n}\n\n/**\n * Turn file content string into an array of lines ready for processing using the other functions\n */\nexport function contentToLines(content: string): string[] {\n return content.split('\\n').map((x) => x.trimRight());\n}\n\n/**\n * Return a file system loader given a base directory\n */\nexport function fileSystemLoader(directory: string): FileLoader {\n return (fileName) => {\n const fullPath = path.resolve(directory, fileName);\n return { fullPath, lines: loadFromFile(fullPath) };\n };\n}\n\nconst RELEVANT_TAG = '/// !show';\nconst DETAIL_TAG = '/// !hide';\nconst INLINE_MD_REGEX = /^\\s*\\/\\/\\/ (.*)$/;\n\n/**\n * Find the relevant lines of the input source\n *\n * Respects switching tags, returns everything if no switching found.\n *\n * Strips common indentation from the blocks it finds.\n */\nfunction findRelevantLines(lines: string[]): string[] {\n let inRelevant = false;\n let didFindRelevant = false;\n const ret: string[] = [];\n\n for (const line of lines) {\n if (line.trim() === RELEVANT_TAG) {\n inRelevant = true;\n didFindRelevant = true;\n } else if (line.trim() === DETAIL_TAG) {\n inRelevant = false;\n } else {\n if (inRelevant) {\n ret.push(line);\n }\n }\n }\n\n // Return full lines list if no switching found\n return stripCommonIndent(didFindRelevant ? ret : lines);\n}\n\n/**\n * Remove common leading whitespace from the given lines\n */\nfunction stripCommonIndent(lines: string[]): string[] {\n const leadingWhitespace = /^(\\s*)/;\n const indents = lines.map((l) => leadingWhitespace.exec(l)![1].length);\n const commonIndent = Math.min(...indents);\n return lines.map((l) => l.slice(commonIndent));\n}\n\n/**\n * Turn source lines into Markdown, starting in TypeScript mode\n */\nfunction markdownify(lines: string[], codeBlockAnnotations: string[]): string[] {\n const typescriptLines: string[] = [];\n const ret: string[] = [];\n\n for (const line of lines) {\n const m = INLINE_MD_REGEX.exec(line);\n if (m) {\n // Literal MarkDown line\n flushTS();\n ret.push(m[1]);\n } else {\n typescriptLines.push(line);\n }\n }\n\n flushTS();\n\n return ret;\n\n /**\n * Flush typescript lines with a triple-backtick-ts block around it.\n */\n function flushTS() {\n if (typescriptLines.length !== 0) {\n // eslint-disable-next-line prefer-template\n ret.push(\n `\\`\\`\\`ts${codeBlockAnnotations.length > 0 ? ` ${codeBlockAnnotations.join(' ')}` : ''}`,\n ...typescriptLines,\n '```',\n );\n typescriptLines.splice(0); // Clear\n }\n }\n}\n\nfunction toUnixPath(x: string) {\n return x.replace(/\\\\/g, '/');\n}\n"]} |