UNPKG

remark-code-snippets

Version:

Import snippets of source code from files as code blocks

132 lines (131 loc) 5.35 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getReferencedFiles = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const unist_util_visit_1 = __importDefault(require("unist-util-visit")); const { parseArgs } = require('./arguments'); const referencedFiles = new Set(); function codeImport(options = {}) { return function transformer(tree, file) { var _a; const codes = []; const promises = []; unist_util_visit_1.default(tree, 'code', (node, index, parent) => { codes.push([node, index, parent]); }); for (const [node] of codes) { // If someone tries to import a file, but forgets to add a language tag e.g ```json // then the meta string will be interpreted as the language. So check the lang prop for file= // and show a helpful error if this is the case, or else importing wont work for them. if (hasLang(node) && node.lang.startsWith('file=')) { throw new Error(`Language tag missing on code block snippet in ${file.history}`); } if (!node.meta) { continue; } const args = parseArgs(node.meta); if (!args.file) { continue; } const fileAbsPath = path_1.default.resolve((_a = options.baseDir) !== null && _a !== void 0 ? _a : (file.dirname || ''), args.file); logReferencedFile(fileAbsPath); if (options.async) { promises.push(new Promise((resolve, reject) => { fs_1.default.readFile(fileAbsPath, 'utf8', (err, fileContent) => { if (err) { if (options.ignoreMissingFiles) { node.value = `Referenced file from ${file.name} (${args.file}) not found.`; resolve(); return; } reject(err); return; } node.value = getSnippet(fileContent, args); resolve(); }); })); } else { if (!fs_1.default.existsSync(fileAbsPath)) { if (options.ignoreMissingFiles) { node.value = `Referenced file from ${file.name} (${args.file}) not found.`; continue; } throw new Error(`File not found: ${args.file}`); } const fileContent = fs_1.default.readFileSync(fileAbsPath, 'utf8'); node.value = getSnippet(fileContent, args); } } if (promises.length) { return Promise.all(promises).then(() => { }); } }; } exports.default = codeImport; function getSnippet(fileContent, args) { let lines = fileContent.trim().split('\n'); let startingLine = 0; let endingLine = undefined; if (args.start) { const numbers = getLineNumbersOfOccurrence(lines, args.start); if (numbers.length === 0) { throw new Error(`Code block start marker "${args.start}" not found in file ${args.file}`); } if (numbers.length > 1) { throw new Error(`Ambiguous code block start marker. Found more than once in ${args.file}, at lines ${numbers}`); } startingLine = numbers[0] + 1; } if (args.end) { const numbers = getLineNumbersOfOccurrence(lines, args.end); if (numbers.length === 0) { throw new Error(`Code block end marker "${args.end}" not found in file ${args.file}`); } if (numbers.length > 1) { throw new Error(`Ambiguous code block end marker. Found more than once in ${args.file}, at lines ${numbers}`); } endingLine = numbers[0]; } lines = lines.slice(startingLine, endingLine); return removeCommonIndentation(lines).join('\n'); } function removeCommonIndentation(lines) { const commonIndentation = lines.reduce((minIndentation, line) => { if (line === '') { return minIndentation; } const m = line.match(/^( *)/); if (!m) { return 0; } return Math.min(m[1].length, minIndentation); }, Number.MAX_VALUE); return lines.map(line => line.slice(commonIndentation)); } function getLineNumbersOfOccurrence(lines, searchTerm) { let lineNumbers = []; lines.forEach((line, index) => { const startIndex = line.indexOf(searchTerm); if (startIndex > -1) { lineNumbers.push(index); } }); return lineNumbers; } function hasLang(node) { return Boolean(node.lang) && typeof node.lang === 'string'; } function logReferencedFile(filepath) { const relativePath = path_1.default.relative(process.cwd(), filepath); referencedFiles.add(relativePath); } function getReferencedFiles() { return Array.from(referencedFiles); } exports.getReferencedFiles = getReferencedFiles;