autolink.mjs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. var autolink = {
  2. name: 'autolink',
  3. tokenize: tokenizeAutolink
  4. }
  5. export default autolink
  6. import assert from 'assert'
  7. import asciiAlpha from '../character/ascii-alpha.mjs'
  8. import asciiAlphanumeric from '../character/ascii-alphanumeric.mjs'
  9. import asciiAtext from '../character/ascii-atext.mjs'
  10. import asciiControl from '../character/ascii-control.mjs'
  11. import codes from '../character/codes.mjs'
  12. import constants from '../constant/constants.mjs'
  13. import types from '../constant/types.mjs'
  14. function tokenizeAutolink(effects, ok, nok) {
  15. var size = 1
  16. return start
  17. function start(code) {
  18. assert(code === codes.lessThan, 'expected `<`')
  19. effects.enter(types.autolink)
  20. effects.enter(types.autolinkMarker)
  21. effects.consume(code)
  22. effects.exit(types.autolinkMarker)
  23. effects.enter(types.autolinkProtocol)
  24. return open
  25. }
  26. function open(code) {
  27. if (asciiAlpha(code)) {
  28. effects.consume(code)
  29. return schemeOrEmailAtext
  30. }
  31. return asciiAtext(code) ? emailAtext(code) : nok(code)
  32. }
  33. function schemeOrEmailAtext(code) {
  34. return code === codes.plusSign ||
  35. code === codes.dash ||
  36. code === codes.dot ||
  37. asciiAlphanumeric(code)
  38. ? schemeInsideOrEmailAtext(code)
  39. : emailAtext(code)
  40. }
  41. function schemeInsideOrEmailAtext(code) {
  42. if (code === codes.colon) {
  43. effects.consume(code)
  44. return urlInside
  45. }
  46. if (
  47. (code === codes.plusSign ||
  48. code === codes.dash ||
  49. code === codes.dot ||
  50. asciiAlphanumeric(code)) &&
  51. size++ < constants.autolinkSchemeSizeMax
  52. ) {
  53. effects.consume(code)
  54. return schemeInsideOrEmailAtext
  55. }
  56. return emailAtext(code)
  57. }
  58. function urlInside(code) {
  59. if (code === codes.greaterThan) {
  60. effects.exit(types.autolinkProtocol)
  61. return end(code)
  62. }
  63. if (code === codes.space || code === codes.lessThan || asciiControl(code)) {
  64. return nok(code)
  65. }
  66. effects.consume(code)
  67. return urlInside
  68. }
  69. function emailAtext(code) {
  70. if (code === codes.atSign) {
  71. effects.consume(code)
  72. size = 0
  73. return emailAtSignOrDot
  74. }
  75. if (asciiAtext(code)) {
  76. effects.consume(code)
  77. return emailAtext
  78. }
  79. return nok(code)
  80. }
  81. function emailAtSignOrDot(code) {
  82. return asciiAlphanumeric(code) ? emailLabel(code) : nok(code)
  83. }
  84. function emailLabel(code) {
  85. if (code === codes.dot) {
  86. effects.consume(code)
  87. size = 0
  88. return emailAtSignOrDot
  89. }
  90. if (code === codes.greaterThan) {
  91. // Exit, then change the type.
  92. effects.exit(types.autolinkProtocol).type = types.autolinkEmail
  93. return end(code)
  94. }
  95. return emailValue(code)
  96. }
  97. function emailValue(code) {
  98. if (
  99. (code === codes.dash || asciiAlphanumeric(code)) &&
  100. size++ < constants.autolinkDomainSizeMax
  101. ) {
  102. effects.consume(code)
  103. return code === codes.dash ? emailValue : emailLabel
  104. }
  105. return nok(code)
  106. }
  107. function end(code) {
  108. assert.equal(code, codes.greaterThan, 'expected `>`')
  109. effects.enter(types.autolinkMarker)
  110. effects.consume(code)
  111. effects.exit(types.autolinkMarker)
  112. effects.exit(types.autolink)
  113. return ok
  114. }
  115. }