/** * @typedef {import('micromark-util-types').Construct} Construct * @typedef {import('micromark-util-types').Tokenizer} Tokenizer * @typedef {import('micromark-util-types').State} State */ import { asciiAlpha, asciiAlphanumeric, asciiAtext, asciiControl } from 'micromark-util-character' /** @type {Construct} */ export const autolink = { name: 'autolink', tokenize: tokenizeAutolink } /** @type {Tokenizer} */ function tokenizeAutolink(effects, ok, nok) { let size = 1 return start /** @type {State} */ function start(code) { effects.enter('autolink') effects.enter('autolinkMarker') effects.consume(code) effects.exit('autolinkMarker') effects.enter('autolinkProtocol') return open } /** @type {State} */ function open(code) { if (asciiAlpha(code)) { effects.consume(code) return schemeOrEmailAtext } return asciiAtext(code) ? emailAtext(code) : nok(code) } /** @type {State} */ function schemeOrEmailAtext(code) { return code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code) ? schemeInsideOrEmailAtext(code) : emailAtext(code) } /** @type {State} */ function schemeInsideOrEmailAtext(code) { if (code === 58) { effects.consume(code) return urlInside } if ( (code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code)) && size++ < 32 ) { effects.consume(code) return schemeInsideOrEmailAtext } return emailAtext(code) } /** @type {State} */ function urlInside(code) { if (code === 62) { effects.exit('autolinkProtocol') return end(code) } if (code === null || code === 32 || code === 60 || asciiControl(code)) { return nok(code) } effects.consume(code) return urlInside } /** @type {State} */ function emailAtext(code) { if (code === 64) { effects.consume(code) size = 0 return emailAtSignOrDot } if (asciiAtext(code)) { effects.consume(code) return emailAtext } return nok(code) } /** @type {State} */ function emailAtSignOrDot(code) { return asciiAlphanumeric(code) ? emailLabel(code) : nok(code) } /** @type {State} */ function emailLabel(code) { if (code === 46) { effects.consume(code) size = 0 return emailAtSignOrDot } if (code === 62) { // Exit, then change the type. effects.exit('autolinkProtocol').type = 'autolinkEmail' return end(code) } return emailValue(code) } /** @type {State} */ function emailValue(code) { if ((code === 45 || asciiAlphanumeric(code)) && size++ < 63) { effects.consume(code) return code === 45 ? emailValue : emailLabel } return nok(code) } /** @type {State} */ function end(code) { effects.enter('autolinkMarker') effects.consume(code) effects.exit('autolinkMarker') effects.exit('autolink') return ok } }