index.mjs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { tokenizer } from 'acorn';
  2. function stripLiteralAcorn(code) {
  3. const FILL = " ";
  4. let result = "";
  5. function fulfill(index) {
  6. if (index > result.length)
  7. result += code.slice(result.length, index).replace(/[^\n]/g, FILL);
  8. }
  9. const tokens = tokenizer(code, {
  10. ecmaVersion: "latest",
  11. sourceType: "module",
  12. allowHashBang: true,
  13. allowAwaitOutsideFunction: true,
  14. allowImportExportEverywhere: true
  15. });
  16. const inter = tokens[Symbol.iterator]();
  17. while (true) {
  18. const { done, value: token } = inter.next();
  19. if (done)
  20. break;
  21. fulfill(token.start);
  22. if (token.type.label === "string")
  23. result += code[token.start] + FILL.repeat(token.end - token.start - 2) + code[token.end - 1];
  24. else if (token.type.label === "template")
  25. result += FILL.repeat(token.end - token.start);
  26. else
  27. result += code.slice(token.start, token.end);
  28. }
  29. fulfill(code.length);
  30. return result;
  31. }
  32. function createIsLiteralPositionAcorn(code) {
  33. const positionList = [];
  34. const tokens = tokenizer(code, {
  35. ecmaVersion: "latest",
  36. sourceType: "module",
  37. allowHashBang: true,
  38. allowAwaitOutsideFunction: true,
  39. allowImportExportEverywhere: true,
  40. onComment(_isBlock, _text, start, end) {
  41. positionList.push(start);
  42. positionList.push(end);
  43. }
  44. });
  45. const inter = tokens[Symbol.iterator]();
  46. while (true) {
  47. const { done, value: token } = inter.next();
  48. if (done)
  49. break;
  50. if (token.type.label === "string") {
  51. positionList.push(token.start + 1);
  52. positionList.push(token.end - 1);
  53. } else if (token.type.label === "template") {
  54. positionList.push(token.start);
  55. positionList.push(token.end);
  56. }
  57. }
  58. return (position) => {
  59. const i = binarySearch(positionList, (v) => position < v);
  60. return (i - 1) % 2 === 0;
  61. };
  62. }
  63. function binarySearch(array, pred) {
  64. let low = -1;
  65. let high = array.length;
  66. while (1 + low < high) {
  67. const mid = low + (high - low >> 1);
  68. if (pred(array[mid]))
  69. high = mid;
  70. else
  71. low = mid;
  72. }
  73. return high;
  74. }
  75. const multilineCommentsRE = /\/\*.*?\*\//gms;
  76. const singlelineCommentsRE = /(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/gm;
  77. const templateLiteralRE = /\$\{(\s*(?:(?!\$\{).|\n|\r)*?\s*)\}/g;
  78. const quotesRE = [
  79. /(["'`])((?:\\\1|(?!\1)|.|\r)*?)\1/gm,
  80. /([`])((?:\\\1|(?!\1)|.|\n|\r)*?)\1/gm
  81. ];
  82. function stripLiteralRegex(code) {
  83. code = code.replace(multilineCommentsRE, (s) => " ".repeat(s.length)).replace(singlelineCommentsRE, (s) => " ".repeat(s.length));
  84. let expanded = code;
  85. for (let i = 0; i < 16; i++) {
  86. const before = expanded;
  87. expanded = expanded.replace(templateLiteralRE, "` $1`");
  88. if (expanded === before)
  89. break;
  90. }
  91. quotesRE.forEach((re) => {
  92. expanded = expanded.replace(re, (s, quote, body, index) => {
  93. code = code.slice(0, index + 1) + " ".repeat(s.length - 2) + code.slice(index + s.length - 1);
  94. return quote + " ".repeat(s.length - 2) + quote;
  95. });
  96. });
  97. return code;
  98. }
  99. function stripLiteral(code) {
  100. try {
  101. return stripLiteralAcorn(code);
  102. } catch (e) {
  103. return stripLiteralRegex(code);
  104. }
  105. }
  106. export { createIsLiteralPositionAcorn, stripLiteral, stripLiteralAcorn, stripLiteralRegex };