import { path } from '@vitebook/core/node/utils'; import type { RuleBlock } from 'markdown-it/lib/parser_block'; import type { ImportCodePluginOptions } from './importCodePlugin'; import type { ImportCodeTokenMeta } from './ImportCodeTokenMeta'; // Min length of the import code syntax, i.e. '@[code]()' const MIN_LENGTH = 9; // Char codes of '@[code' const START_CODES = [64, 91, 99, 111, 100, 101]; // Regexp to match the import syntax. const SYNTAX_RE = /^@\[code(?:{(?:(\d+)?-(\d+)?)})?(?: ([^\]]+))?\]\(([^)]*)\)/; export const createImportCodeBlockRule = ({ handleImportPath = (str) => str }: ImportCodePluginOptions): RuleBlock => (state, startLine, endLine, silent): boolean => { // If it's indented more than 3 spaces, it should be a code block. /* istanbul ignore if */ if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } const pos = state.bMarks[startLine] + state.tShift[startLine]; const max = state.eMarks[startLine]; // Return false if the length is shorter than min length. if (pos + MIN_LENGTH > max) return false; // Check if it's matched the start. for (let i = 0; i < START_CODES.length; i += 1) { if (state.src.charCodeAt(pos + i) !== START_CODES[i]) { return false; } } // Check if it's matched the syntax. const match = state.src.slice(pos, max).match(SYNTAX_RE); if (!match) return false; // Return true as we have matched the syntax. if (silent) return true; const [, lineStart, lineEnd, info, importPath] = match; const meta: ImportCodeTokenMeta = { importPath: handleImportPath(importPath), lineStart: lineStart ? Number.parseInt(lineStart, 10) : 0, lineEnd: lineEnd ? Number.parseInt(lineEnd, 10) : undefined, }; // Create a import_code token. const token = state.push('import_code', 'code', 0); // Use user specified info, or fallback to file ext. token.info = info ?? path.extname(meta.importPath).slice(1); token.markup = '```'; token.map = [startLine, startLine + 1]; // Store token meta to be used in renderer rule. token.meta = meta; state.line = startLine + 1; return true; };