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