UNPKG

@ztl-uwu/nuxt-content

Version:

Write your content inside your Nuxt app

308 lines (307 loc) 8.51 kB
import { markdownLineEnding } from "micromark-util-character"; import { push, splice } from "micromark-util-chunked"; import { resolveAll } from "micromark-util-resolve-all"; export function createTokenizer(parser, initialize, from) { let point = Object.assign( from ? Object.assign({}, from) : { line: 1, column: 1, offset: 0 }, { _index: 0, _bufferIndex: -1 } ); const columnStart = {}; const resolveAllConstructs = []; let chunks = []; let stack = []; let consumed = true; const effects = { consume, enter, exit, attempt: constructFactory(onsuccessfulconstruct), check: constructFactory(onsuccessfulcheck), interrupt: constructFactory(onsuccessfulcheck, { interrupt: true }) }; const context = { previous: null, code: null, containerState: {}, events: [], parser, sliceStream, sliceSerialize, now, defineSkip, write }; let state = initialize.tokenize.call(context, effects); let expectedCode; if (initialize.resolveAll) { resolveAllConstructs.push(initialize); } return context; function write(slice) { chunks = push(chunks, slice); main(); if (chunks[chunks.length - 1] !== null) { return []; } addResult(initialize, 0); context.events = resolveAll(resolveAllConstructs, context.events, context); return context.events; } function sliceSerialize(token, expandTabs) { return serializeChunks(sliceStream(token), expandTabs); } function sliceStream(token) { return sliceChunks(chunks, token); } function now() { return Object.assign({}, point); } function defineSkip(value) { columnStart[value.line] = value.column; accountForPotentialSkip(); } function main() { let chunkIndex; while (point._index < chunks.length) { const chunk = chunks[point._index]; if (typeof chunk === "string") { chunkIndex = point._index; if (point._bufferIndex < 0) { point._bufferIndex = 0; } while (point._index === chunkIndex && point._bufferIndex < chunk.length) { go(chunk.charCodeAt(point._bufferIndex)); } } else { go(chunk); } } } function go(code) { consumed = void 0; expectedCode = code; state = state(code); } function consume(code) { if (markdownLineEnding(code)) { point.line++; point.column = 1; point.offset += code === -3 ? 2 : 1; accountForPotentialSkip(); } else if (code !== -1) { point.column++; point.offset++; } if (point._bufferIndex < 0) { point._index++; } else { point._bufferIndex++; if (point._bufferIndex === chunks[point._index].length) { point._bufferIndex = -1; point._index++; } } context.previous = code; consumed = true; } function enter(type, fields) { const token = fields || {}; token.type = type; token.start = now(); context.events.push(["enter", token, context]); stack.push(token); return token; } function exit(type) { const token = stack.pop(); token.end = now(); context.events.push(["exit", token, context]); return token; } function onsuccessfulconstruct(construct, info) { addResult(construct, info.from); } function onsuccessfulcheck(_, info) { info.restore(); } function constructFactory(onreturn, fields) { return hook; function hook(constructs, returnState, bogusState) { let listOfConstructs; let constructIndex; let currentConstruct; let info; return Array.isArray(constructs) ? ( /* c8 ignore next 1 */ handleListOfConstructs(constructs) ) : "tokenize" in constructs ? handleListOfConstructs([constructs]) : handleMapOfConstructs(constructs); function handleMapOfConstructs(map) { return start; function start(code) { const def = code !== null && map[code]; const all = code !== null && map.null; const list = [ // To do: add more extension tests. /* c8 ignore next 2 */ ...Array.isArray(def) ? def : def ? [def] : [], ...Array.isArray(all) ? all : all ? [all] : [] ]; return handleListOfConstructs(list)(code); } } function handleListOfConstructs(list) { listOfConstructs = list; constructIndex = 0; if (list.length === 0) { return bogusState; } return handleConstruct(list[constructIndex]); } function handleConstruct(construct) { return start; function start(code) { info = store(); currentConstruct = construct; if (!construct.partial) { context.currentConstruct = construct; } if (construct.name && context.parser.constructs.disable.null.includes(construct.name)) { return nok(code); } return construct.tokenize.call( // If we do have fields, create an object w/ `context` as its // prototype. // This allows a “live binding”, which is needed for `interrupt`. fields ? Object.assign(Object.create(context), fields) : context, effects, ok, nok )(code); } } function ok(code) { consumed = true; onreturn(currentConstruct, info); return returnState; } function nok(code) { consumed = true; info.restore(); if (++constructIndex < listOfConstructs.length) { return handleConstruct(listOfConstructs[constructIndex]); } return bogusState; } } } function addResult(construct, from2) { if (construct.resolveAll && !resolveAllConstructs.includes(construct)) { resolveAllConstructs.push(construct); } if (construct.resolve) { splice( context.events, from2, context.events.length - from2, construct.resolve(context.events.slice(from2), context) ); } if (construct.resolveTo) { context.events = construct.resolveTo(context.events, context); } } function store() { const startPoint = now(); const startPrevious = context.previous; const startCurrentConstruct = context.currentConstruct; const startEventsIndex = context.events.length; const startStack = Array.from(stack); return { restore, from: startEventsIndex }; function restore() { point = startPoint; context.previous = startPrevious; context.currentConstruct = startCurrentConstruct; context.events.length = startEventsIndex; stack = startStack; accountForPotentialSkip(); } } function accountForPotentialSkip() { if (point.line in columnStart && point.column < 2) { point.column = columnStart[point.line]; point.offset += columnStart[point.line] - 1; } } } function sliceChunks(chunks, token) { const startIndex = token.start._index; const startBufferIndex = token.start._bufferIndex; const endIndex = token.end._index; const endBufferIndex = token.end._bufferIndex; let view; if (startIndex === endIndex) { view = [chunks[startIndex].slice(startBufferIndex, endBufferIndex)]; } else { view = chunks.slice(startIndex, endIndex); if (startBufferIndex > -1) { view[0] = view[0].slice(startBufferIndex); } if (endBufferIndex > 0) { view.push(chunks[endIndex].slice(0, endBufferIndex)); } } return view; } function serializeChunks(chunks, expandTabs) { let index = -1; const result = []; let atTab; while (++index < chunks.length) { const chunk = chunks[index]; let value; if (typeof chunk === "string") { value = chunk; } else switch (chunk) { case -5: { value = "\r"; break; } case -4: { value = "\n"; break; } case -3: { value = "\r\n"; break; } case -2: { value = expandTabs ? " " : " "; break; } case -1: { if (!expandTabs && atTab) continue; value = " "; break; } default: { value = String.fromCharCode(chunk); } } atTab = chunk === -2; result.push(value); } return result.join(""); }