chunker.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Split the input into chunks.
  2. module.exports = function (input, fail) {
  3. var len = input.length, level = 0, parenLevel = 0,
  4. lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
  5. chunks = [], emitFrom = 0,
  6. chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
  7. function emitChunk(force) {
  8. var len = chunkerCurrentIndex - emitFrom;
  9. if (((len < 512) && !force) || !len) {
  10. return;
  11. }
  12. chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
  13. emitFrom = chunkerCurrentIndex + 1;
  14. }
  15. for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  16. cc = input.charCodeAt(chunkerCurrentIndex);
  17. if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
  18. // a-z or whitespace
  19. continue;
  20. }
  21. switch (cc) {
  22. case 40: // (
  23. parenLevel++;
  24. lastOpeningParen = chunkerCurrentIndex;
  25. continue;
  26. case 41: // )
  27. if (--parenLevel < 0) {
  28. return fail("missing opening `(`", chunkerCurrentIndex);
  29. }
  30. continue;
  31. case 59: // ;
  32. if (!parenLevel) { emitChunk(); }
  33. continue;
  34. case 123: // {
  35. level++;
  36. lastOpening = chunkerCurrentIndex;
  37. continue;
  38. case 125: // }
  39. if (--level < 0) {
  40. return fail("missing opening `{`", chunkerCurrentIndex);
  41. }
  42. if (!level && !parenLevel) { emitChunk(); }
  43. continue;
  44. case 92: // \
  45. if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; }
  46. return fail("unescaped `\\`", chunkerCurrentIndex);
  47. case 34:
  48. case 39:
  49. case 96: // ", ' and `
  50. matched = 0;
  51. currentChunkStartIndex = chunkerCurrentIndex;
  52. for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  53. cc2 = input.charCodeAt(chunkerCurrentIndex);
  54. if (cc2 > 96) { continue; }
  55. if (cc2 == cc) { matched = 1; break; }
  56. if (cc2 == 92) { // \
  57. if (chunkerCurrentIndex == len - 1) {
  58. return fail("unescaped `\\`", chunkerCurrentIndex);
  59. }
  60. chunkerCurrentIndex++;
  61. }
  62. }
  63. if (matched) { continue; }
  64. return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
  65. case 47: // /, check for comment
  66. if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; }
  67. cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
  68. if (cc2 == 47) {
  69. // //, find lnfeed
  70. for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  71. cc2 = input.charCodeAt(chunkerCurrentIndex);
  72. if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
  73. }
  74. } else if (cc2 == 42) {
  75. // /*, find */
  76. lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
  77. for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
  78. cc2 = input.charCodeAt(chunkerCurrentIndex);
  79. if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; }
  80. if (cc2 != 42) { continue; }
  81. if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; }
  82. }
  83. if (chunkerCurrentIndex == len - 1) {
  84. return fail("missing closing `*/`", currentChunkStartIndex);
  85. }
  86. chunkerCurrentIndex++;
  87. }
  88. continue;
  89. case 42: // *, check for unmatched */
  90. if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
  91. return fail("unmatched `/*`", chunkerCurrentIndex);
  92. }
  93. continue;
  94. }
  95. }
  96. if (level !== 0) {
  97. if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
  98. return fail("missing closing `}` or `*/`", lastOpening);
  99. } else {
  100. return fail("missing closing `}`", lastOpening);
  101. }
  102. } else if (parenLevel !== 0) {
  103. return fail("missing closing `)`", lastOpeningParen);
  104. }
  105. emitChunk(true);
  106. return chunks;
  107. };