html-flow.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. /**
  2. * @typedef {import('micromark-util-types').Code} Code
  3. * @typedef {import('micromark-util-types').Construct} Construct
  4. * @typedef {import('micromark-util-types').Resolver} Resolver
  5. * @typedef {import('micromark-util-types').State} State
  6. * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
  7. * @typedef {import('micromark-util-types').Tokenizer} Tokenizer
  8. */
  9. import {
  10. asciiAlpha,
  11. asciiAlphanumeric,
  12. markdownLineEnding,
  13. markdownLineEndingOrSpace,
  14. markdownSpace
  15. } from 'micromark-util-character'
  16. import {htmlBlockNames, htmlRawNames} from 'micromark-util-html-tag-name'
  17. import {blankLine} from './blank-line.js'
  18. /** @type {Construct} */
  19. export const htmlFlow = {
  20. name: 'htmlFlow',
  21. tokenize: tokenizeHtmlFlow,
  22. resolveTo: resolveToHtmlFlow,
  23. concrete: true
  24. }
  25. /** @type {Construct} */
  26. const blankLineBefore = {
  27. tokenize: tokenizeBlankLineBefore,
  28. partial: true
  29. }
  30. const nonLazyContinuationStart = {
  31. tokenize: tokenizeNonLazyContinuationStart,
  32. partial: true
  33. }
  34. /** @type {Resolver} */
  35. function resolveToHtmlFlow(events) {
  36. let index = events.length
  37. while (index--) {
  38. if (events[index][0] === 'enter' && events[index][1].type === 'htmlFlow') {
  39. break
  40. }
  41. }
  42. if (index > 1 && events[index - 2][1].type === 'linePrefix') {
  43. // Add the prefix start to the HTML token.
  44. events[index][1].start = events[index - 2][1].start
  45. // Add the prefix start to the HTML line token.
  46. events[index + 1][1].start = events[index - 2][1].start
  47. // Remove the line prefix.
  48. events.splice(index - 2, 2)
  49. }
  50. return events
  51. }
  52. /**
  53. * @this {TokenizeContext}
  54. * @type {Tokenizer}
  55. */
  56. function tokenizeHtmlFlow(effects, ok, nok) {
  57. const self = this
  58. /** @type {number} */
  59. let marker
  60. /** @type {boolean} */
  61. let closingTag
  62. /** @type {string} */
  63. let buffer
  64. /** @type {number} */
  65. let index
  66. /** @type {Code} */
  67. let markerB
  68. return start
  69. /**
  70. * Start of HTML (flow).
  71. *
  72. * ```markdown
  73. * > | <x />
  74. * ^
  75. * ```
  76. *
  77. * @type {State}
  78. */
  79. function start(code) {
  80. // To do: parse indent like `markdown-rs`.
  81. return before(code)
  82. }
  83. /**
  84. * At `<`, after optional whitespace.
  85. *
  86. * ```markdown
  87. * > | <x />
  88. * ^
  89. * ```
  90. *
  91. * @type {State}
  92. */
  93. function before(code) {
  94. effects.enter('htmlFlow')
  95. effects.enter('htmlFlowData')
  96. effects.consume(code)
  97. return open
  98. }
  99. /**
  100. * After `<`, at tag name or other stuff.
  101. *
  102. * ```markdown
  103. * > | <x />
  104. * ^
  105. * > | <!doctype>
  106. * ^
  107. * > | <!--xxx-->
  108. * ^
  109. * ```
  110. *
  111. * @type {State}
  112. */
  113. function open(code) {
  114. if (code === 33) {
  115. effects.consume(code)
  116. return declarationOpen
  117. }
  118. if (code === 47) {
  119. effects.consume(code)
  120. closingTag = true
  121. return tagCloseStart
  122. }
  123. if (code === 63) {
  124. effects.consume(code)
  125. marker = 3
  126. // To do:
  127. // tokenizer.concrete = true
  128. // To do: use `markdown-rs` style interrupt.
  129. // While we’re in an instruction instead of a declaration, we’re on a `?`
  130. // right now, so we do need to search for `>`, similar to declarations.
  131. return self.interrupt ? ok : continuationDeclarationInside
  132. }
  133. // ASCII alphabetical.
  134. if (asciiAlpha(code)) {
  135. effects.consume(code)
  136. // @ts-expect-error: not null.
  137. buffer = String.fromCharCode(code)
  138. return tagName
  139. }
  140. return nok(code)
  141. }
  142. /**
  143. * After `<!`, at declaration, comment, or CDATA.
  144. *
  145. * ```markdown
  146. * > | <!doctype>
  147. * ^
  148. * > | <!--xxx-->
  149. * ^
  150. * > | <![CDATA[>&<]]>
  151. * ^
  152. * ```
  153. *
  154. * @type {State}
  155. */
  156. function declarationOpen(code) {
  157. if (code === 45) {
  158. effects.consume(code)
  159. marker = 2
  160. return commentOpenInside
  161. }
  162. if (code === 91) {
  163. effects.consume(code)
  164. marker = 5
  165. index = 0
  166. return cdataOpenInside
  167. }
  168. // ASCII alphabetical.
  169. if (asciiAlpha(code)) {
  170. effects.consume(code)
  171. marker = 4
  172. // // Do not form containers.
  173. // tokenizer.concrete = true
  174. return self.interrupt ? ok : continuationDeclarationInside
  175. }
  176. return nok(code)
  177. }
  178. /**
  179. * After `<!-`, inside a comment, at another `-`.
  180. *
  181. * ```markdown
  182. * > | <!--xxx-->
  183. * ^
  184. * ```
  185. *
  186. * @type {State}
  187. */
  188. function commentOpenInside(code) {
  189. if (code === 45) {
  190. effects.consume(code)
  191. // // Do not form containers.
  192. // tokenizer.concrete = true
  193. return self.interrupt ? ok : continuationDeclarationInside
  194. }
  195. return nok(code)
  196. }
  197. /**
  198. * After `<![`, inside CDATA, expecting `CDATA[`.
  199. *
  200. * ```markdown
  201. * > | <![CDATA[>&<]]>
  202. * ^^^^^^
  203. * ```
  204. *
  205. * @type {State}
  206. */
  207. function cdataOpenInside(code) {
  208. const value = 'CDATA['
  209. if (code === value.charCodeAt(index++)) {
  210. effects.consume(code)
  211. if (index === value.length) {
  212. // // Do not form containers.
  213. // tokenizer.concrete = true
  214. return self.interrupt ? ok : continuation
  215. }
  216. return cdataOpenInside
  217. }
  218. return nok(code)
  219. }
  220. /**
  221. * After `</`, in closing tag, at tag name.
  222. *
  223. * ```markdown
  224. * > | </x>
  225. * ^
  226. * ```
  227. *
  228. * @type {State}
  229. */
  230. function tagCloseStart(code) {
  231. if (asciiAlpha(code)) {
  232. effects.consume(code)
  233. // @ts-expect-error: not null.
  234. buffer = String.fromCharCode(code)
  235. return tagName
  236. }
  237. return nok(code)
  238. }
  239. /**
  240. * In tag name.
  241. *
  242. * ```markdown
  243. * > | <ab>
  244. * ^^
  245. * > | </ab>
  246. * ^^
  247. * ```
  248. *
  249. * @type {State}
  250. */
  251. function tagName(code) {
  252. if (
  253. code === null ||
  254. code === 47 ||
  255. code === 62 ||
  256. markdownLineEndingOrSpace(code)
  257. ) {
  258. const slash = code === 47
  259. const name = buffer.toLowerCase()
  260. if (!slash && !closingTag && htmlRawNames.includes(name)) {
  261. marker = 1
  262. // // Do not form containers.
  263. // tokenizer.concrete = true
  264. return self.interrupt ? ok(code) : continuation(code)
  265. }
  266. if (htmlBlockNames.includes(buffer.toLowerCase())) {
  267. marker = 6
  268. if (slash) {
  269. effects.consume(code)
  270. return basicSelfClosing
  271. }
  272. // // Do not form containers.
  273. // tokenizer.concrete = true
  274. return self.interrupt ? ok(code) : continuation(code)
  275. }
  276. marker = 7
  277. // Do not support complete HTML when interrupting.
  278. return self.interrupt && !self.parser.lazy[self.now().line]
  279. ? nok(code)
  280. : closingTag
  281. ? completeClosingTagAfter(code)
  282. : completeAttributeNameBefore(code)
  283. }
  284. // ASCII alphanumerical and `-`.
  285. if (code === 45 || asciiAlphanumeric(code)) {
  286. effects.consume(code)
  287. buffer += String.fromCharCode(code)
  288. return tagName
  289. }
  290. return nok(code)
  291. }
  292. /**
  293. * After closing slash of a basic tag name.
  294. *
  295. * ```markdown
  296. * > | <div/>
  297. * ^
  298. * ```
  299. *
  300. * @type {State}
  301. */
  302. function basicSelfClosing(code) {
  303. if (code === 62) {
  304. effects.consume(code)
  305. // // Do not form containers.
  306. // tokenizer.concrete = true
  307. return self.interrupt ? ok : continuation
  308. }
  309. return nok(code)
  310. }
  311. /**
  312. * After closing slash of a complete tag name.
  313. *
  314. * ```markdown
  315. * > | <x/>
  316. * ^
  317. * ```
  318. *
  319. * @type {State}
  320. */
  321. function completeClosingTagAfter(code) {
  322. if (markdownSpace(code)) {
  323. effects.consume(code)
  324. return completeClosingTagAfter
  325. }
  326. return completeEnd(code)
  327. }
  328. /**
  329. * At an attribute name.
  330. *
  331. * At first, this state is used after a complete tag name, after whitespace,
  332. * where it expects optional attributes or the end of the tag.
  333. * It is also reused after attributes, when expecting more optional
  334. * attributes.
  335. *
  336. * ```markdown
  337. * > | <a />
  338. * ^
  339. * > | <a :b>
  340. * ^
  341. * > | <a _b>
  342. * ^
  343. * > | <a b>
  344. * ^
  345. * > | <a >
  346. * ^
  347. * ```
  348. *
  349. * @type {State}
  350. */
  351. function completeAttributeNameBefore(code) {
  352. if (code === 47) {
  353. effects.consume(code)
  354. return completeEnd
  355. }
  356. // ASCII alphanumerical and `:` and `_`.
  357. if (code === 58 || code === 95 || asciiAlpha(code)) {
  358. effects.consume(code)
  359. return completeAttributeName
  360. }
  361. if (markdownSpace(code)) {
  362. effects.consume(code)
  363. return completeAttributeNameBefore
  364. }
  365. return completeEnd(code)
  366. }
  367. /**
  368. * In attribute name.
  369. *
  370. * ```markdown
  371. * > | <a :b>
  372. * ^
  373. * > | <a _b>
  374. * ^
  375. * > | <a b>
  376. * ^
  377. * ```
  378. *
  379. * @type {State}
  380. */
  381. function completeAttributeName(code) {
  382. // ASCII alphanumerical and `-`, `.`, `:`, and `_`.
  383. if (
  384. code === 45 ||
  385. code === 46 ||
  386. code === 58 ||
  387. code === 95 ||
  388. asciiAlphanumeric(code)
  389. ) {
  390. effects.consume(code)
  391. return completeAttributeName
  392. }
  393. return completeAttributeNameAfter(code)
  394. }
  395. /**
  396. * After attribute name, at an optional initializer, the end of the tag, or
  397. * whitespace.
  398. *
  399. * ```markdown
  400. * > | <a b>
  401. * ^
  402. * > | <a b=c>
  403. * ^
  404. * ```
  405. *
  406. * @type {State}
  407. */
  408. function completeAttributeNameAfter(code) {
  409. if (code === 61) {
  410. effects.consume(code)
  411. return completeAttributeValueBefore
  412. }
  413. if (markdownSpace(code)) {
  414. effects.consume(code)
  415. return completeAttributeNameAfter
  416. }
  417. return completeAttributeNameBefore(code)
  418. }
  419. /**
  420. * Before unquoted, double quoted, or single quoted attribute value, allowing
  421. * whitespace.
  422. *
  423. * ```markdown
  424. * > | <a b=c>
  425. * ^
  426. * > | <a b="c">
  427. * ^
  428. * ```
  429. *
  430. * @type {State}
  431. */
  432. function completeAttributeValueBefore(code) {
  433. if (
  434. code === null ||
  435. code === 60 ||
  436. code === 61 ||
  437. code === 62 ||
  438. code === 96
  439. ) {
  440. return nok(code)
  441. }
  442. if (code === 34 || code === 39) {
  443. effects.consume(code)
  444. markerB = code
  445. return completeAttributeValueQuoted
  446. }
  447. if (markdownSpace(code)) {
  448. effects.consume(code)
  449. return completeAttributeValueBefore
  450. }
  451. return completeAttributeValueUnquoted(code)
  452. }
  453. /**
  454. * In double or single quoted attribute value.
  455. *
  456. * ```markdown
  457. * > | <a b="c">
  458. * ^
  459. * > | <a b='c'>
  460. * ^
  461. * ```
  462. *
  463. * @type {State}
  464. */
  465. function completeAttributeValueQuoted(code) {
  466. if (code === markerB) {
  467. effects.consume(code)
  468. markerB = null
  469. return completeAttributeValueQuotedAfter
  470. }
  471. if (code === null || markdownLineEnding(code)) {
  472. return nok(code)
  473. }
  474. effects.consume(code)
  475. return completeAttributeValueQuoted
  476. }
  477. /**
  478. * In unquoted attribute value.
  479. *
  480. * ```markdown
  481. * > | <a b=c>
  482. * ^
  483. * ```
  484. *
  485. * @type {State}
  486. */
  487. function completeAttributeValueUnquoted(code) {
  488. if (
  489. code === null ||
  490. code === 34 ||
  491. code === 39 ||
  492. code === 47 ||
  493. code === 60 ||
  494. code === 61 ||
  495. code === 62 ||
  496. code === 96 ||
  497. markdownLineEndingOrSpace(code)
  498. ) {
  499. return completeAttributeNameAfter(code)
  500. }
  501. effects.consume(code)
  502. return completeAttributeValueUnquoted
  503. }
  504. /**
  505. * After double or single quoted attribute value, before whitespace or the
  506. * end of the tag.
  507. *
  508. * ```markdown
  509. * > | <a b="c">
  510. * ^
  511. * ```
  512. *
  513. * @type {State}
  514. */
  515. function completeAttributeValueQuotedAfter(code) {
  516. if (code === 47 || code === 62 || markdownSpace(code)) {
  517. return completeAttributeNameBefore(code)
  518. }
  519. return nok(code)
  520. }
  521. /**
  522. * In certain circumstances of a complete tag where only an `>` is allowed.
  523. *
  524. * ```markdown
  525. * > | <a b="c">
  526. * ^
  527. * ```
  528. *
  529. * @type {State}
  530. */
  531. function completeEnd(code) {
  532. if (code === 62) {
  533. effects.consume(code)
  534. return completeAfter
  535. }
  536. return nok(code)
  537. }
  538. /**
  539. * After `>` in a complete tag.
  540. *
  541. * ```markdown
  542. * > | <x>
  543. * ^
  544. * ```
  545. *
  546. * @type {State}
  547. */
  548. function completeAfter(code) {
  549. if (code === null || markdownLineEnding(code)) {
  550. // // Do not form containers.
  551. // tokenizer.concrete = true
  552. return continuation(code)
  553. }
  554. if (markdownSpace(code)) {
  555. effects.consume(code)
  556. return completeAfter
  557. }
  558. return nok(code)
  559. }
  560. /**
  561. * In continuation of any HTML kind.
  562. *
  563. * ```markdown
  564. * > | <!--xxx-->
  565. * ^
  566. * ```
  567. *
  568. * @type {State}
  569. */
  570. function continuation(code) {
  571. if (code === 45 && marker === 2) {
  572. effects.consume(code)
  573. return continuationCommentInside
  574. }
  575. if (code === 60 && marker === 1) {
  576. effects.consume(code)
  577. return continuationRawTagOpen
  578. }
  579. if (code === 62 && marker === 4) {
  580. effects.consume(code)
  581. return continuationClose
  582. }
  583. if (code === 63 && marker === 3) {
  584. effects.consume(code)
  585. return continuationDeclarationInside
  586. }
  587. if (code === 93 && marker === 5) {
  588. effects.consume(code)
  589. return continuationCdataInside
  590. }
  591. if (markdownLineEnding(code) && (marker === 6 || marker === 7)) {
  592. effects.exit('htmlFlowData')
  593. return effects.check(
  594. blankLineBefore,
  595. continuationAfter,
  596. continuationStart
  597. )(code)
  598. }
  599. if (code === null || markdownLineEnding(code)) {
  600. effects.exit('htmlFlowData')
  601. return continuationStart(code)
  602. }
  603. effects.consume(code)
  604. return continuation
  605. }
  606. /**
  607. * In continuation, at eol.
  608. *
  609. * ```markdown
  610. * > | <x>
  611. * ^
  612. * | asd
  613. * ```
  614. *
  615. * @type {State}
  616. */
  617. function continuationStart(code) {
  618. return effects.check(
  619. nonLazyContinuationStart,
  620. continuationStartNonLazy,
  621. continuationAfter
  622. )(code)
  623. }
  624. /**
  625. * In continuation, at eol, before non-lazy content.
  626. *
  627. * ```markdown
  628. * > | <x>
  629. * ^
  630. * | asd
  631. * ```
  632. *
  633. * @type {State}
  634. */
  635. function continuationStartNonLazy(code) {
  636. effects.enter('lineEnding')
  637. effects.consume(code)
  638. effects.exit('lineEnding')
  639. return continuationBefore
  640. }
  641. /**
  642. * In continuation, before non-lazy content.
  643. *
  644. * ```markdown
  645. * | <x>
  646. * > | asd
  647. * ^
  648. * ```
  649. *
  650. * @type {State}
  651. */
  652. function continuationBefore(code) {
  653. if (code === null || markdownLineEnding(code)) {
  654. return continuationStart(code)
  655. }
  656. effects.enter('htmlFlowData')
  657. return continuation(code)
  658. }
  659. /**
  660. * In comment continuation, after one `-`, expecting another.
  661. *
  662. * ```markdown
  663. * > | <!--xxx-->
  664. * ^
  665. * ```
  666. *
  667. * @type {State}
  668. */
  669. function continuationCommentInside(code) {
  670. if (code === 45) {
  671. effects.consume(code)
  672. return continuationDeclarationInside
  673. }
  674. return continuation(code)
  675. }
  676. /**
  677. * In raw continuation, after `<`, at `/`.
  678. *
  679. * ```markdown
  680. * > | <script>console.log(1)</script>
  681. * ^
  682. * ```
  683. *
  684. * @type {State}
  685. */
  686. function continuationRawTagOpen(code) {
  687. if (code === 47) {
  688. effects.consume(code)
  689. buffer = ''
  690. return continuationRawEndTag
  691. }
  692. return continuation(code)
  693. }
  694. /**
  695. * In raw continuation, after `</`, in a raw tag name.
  696. *
  697. * ```markdown
  698. * > | <script>console.log(1)</script>
  699. * ^^^^^^
  700. * ```
  701. *
  702. * @type {State}
  703. */
  704. function continuationRawEndTag(code) {
  705. if (code === 62) {
  706. const name = buffer.toLowerCase()
  707. if (htmlRawNames.includes(name)) {
  708. effects.consume(code)
  709. return continuationClose
  710. }
  711. return continuation(code)
  712. }
  713. if (asciiAlpha(code) && buffer.length < 8) {
  714. effects.consume(code)
  715. // @ts-expect-error: not null.
  716. buffer += String.fromCharCode(code)
  717. return continuationRawEndTag
  718. }
  719. return continuation(code)
  720. }
  721. /**
  722. * In cdata continuation, after `]`, expecting `]>`.
  723. *
  724. * ```markdown
  725. * > | <![CDATA[>&<]]>
  726. * ^
  727. * ```
  728. *
  729. * @type {State}
  730. */
  731. function continuationCdataInside(code) {
  732. if (code === 93) {
  733. effects.consume(code)
  734. return continuationDeclarationInside
  735. }
  736. return continuation(code)
  737. }
  738. /**
  739. * In declaration or instruction continuation, at `>`.
  740. *
  741. * ```markdown
  742. * > | <!-->
  743. * ^
  744. * > | <?>
  745. * ^
  746. * > | <!q>
  747. * ^
  748. * > | <!--ab-->
  749. * ^
  750. * > | <![CDATA[>&<]]>
  751. * ^
  752. * ```
  753. *
  754. * @type {State}
  755. */
  756. function continuationDeclarationInside(code) {
  757. if (code === 62) {
  758. effects.consume(code)
  759. return continuationClose
  760. }
  761. // More dashes.
  762. if (code === 45 && marker === 2) {
  763. effects.consume(code)
  764. return continuationDeclarationInside
  765. }
  766. return continuation(code)
  767. }
  768. /**
  769. * In closed continuation: everything we get until the eol/eof is part of it.
  770. *
  771. * ```markdown
  772. * > | <!doctype>
  773. * ^
  774. * ```
  775. *
  776. * @type {State}
  777. */
  778. function continuationClose(code) {
  779. if (code === null || markdownLineEnding(code)) {
  780. effects.exit('htmlFlowData')
  781. return continuationAfter(code)
  782. }
  783. effects.consume(code)
  784. return continuationClose
  785. }
  786. /**
  787. * Done.
  788. *
  789. * ```markdown
  790. * > | <!doctype>
  791. * ^
  792. * ```
  793. *
  794. * @type {State}
  795. */
  796. function continuationAfter(code) {
  797. effects.exit('htmlFlow')
  798. // // Feel free to interrupt.
  799. // tokenizer.interrupt = false
  800. // // No longer concrete.
  801. // tokenizer.concrete = false
  802. return ok(code)
  803. }
  804. }
  805. /**
  806. * @this {TokenizeContext}
  807. * @type {Tokenizer}
  808. */
  809. function tokenizeNonLazyContinuationStart(effects, ok, nok) {
  810. const self = this
  811. return start
  812. /**
  813. * At eol, before continuation.
  814. *
  815. * ```markdown
  816. * > | * ```js
  817. * ^
  818. * | b
  819. * ```
  820. *
  821. * @type {State}
  822. */
  823. function start(code) {
  824. if (markdownLineEnding(code)) {
  825. effects.enter('lineEnding')
  826. effects.consume(code)
  827. effects.exit('lineEnding')
  828. return after
  829. }
  830. return nok(code)
  831. }
  832. /**
  833. * A continuation.
  834. *
  835. * ```markdown
  836. * | * ```js
  837. * > | b
  838. * ^
  839. * ```
  840. *
  841. * @type {State}
  842. */
  843. function after(code) {
  844. return self.parser.lazy[self.now().line] ? nok(code) : ok(code)
  845. }
  846. }
  847. /**
  848. * @this {TokenizeContext}
  849. * @type {Tokenizer}
  850. */
  851. function tokenizeBlankLineBefore(effects, ok, nok) {
  852. return start
  853. /**
  854. * Before eol, expecting blank line.
  855. *
  856. * ```markdown
  857. * > | <div>
  858. * ^
  859. * |
  860. * ```
  861. *
  862. * @type {State}
  863. */
  864. function start(code) {
  865. effects.enter('lineEnding')
  866. effects.consume(code)
  867. effects.exit('lineEnding')
  868. return effects.attempt(blankLine, ok, nok)
  869. }
  870. }