nil-remark-code-snippets
Version:
Import snippets of source code from files as code blocks
156 lines (155 loc) • 6.29 kB
JavaScript
;
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 (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 + 1);
let joinedResult = removeCommonIndentation(lines.slice(0, -1)).join('\n');
return retrieveExactSnippet(joinedResult);
}
function retrieveExactSnippet(snippet) {
const CLI_PATTERN = /\$\{NIL_GLOBAL\}|solc|\$\{process.env.SOLC\}/;
const CONFIG_PATTERN = /--config\s+\S+/g;
const ARGS_PATTERN = /\$\{([^}]+)\}/g;
const match = snippet.match(CLI_PATTERN);
if (match != null) {
const startIndex = match.index || 0;
let resultString = snippet.substring(startIndex);
resultString = resultString.replace(/\$\{NIL_GLOBAL\}/g, 'nil');
resultString = resultString.replace(CONFIG_PATTERN, '').replace(/['`]/g, "");
resultString = resultString.replace(/\$\{process.env.SOLC\}/g, 'solc');
resultString = resultString.replace(ARGS_PATTERN, (fullMatch, s) => {
return s === 'NIL_GLOBAL' ? 'nil' : s.toUpperCase();
});
resultString = handlePathing(resultString).replace(/CONFIG_FLAG/, '').trim().replace(';', '').replace('NODE_MODULES', '');
return resultString;
}
else {
return snippet;
}
}
function handlePathing(snippet) {
const PATTERN = /(\S*\/)(\S+)/g;
let resultString = snippet.replace(PATTERN, (match, p1, p2) => `path/to/${p2}`);
return resultString;
}
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;