sync.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // @ts-check
  2. import { WalkerBase } from './walker.js';
  3. /** @typedef { import('estree').BaseNode} BaseNode */
  4. /** @typedef { import('./walker.js').WalkerContext} WalkerContext */
  5. /** @typedef {(
  6. * this: WalkerContext,
  7. * node: BaseNode,
  8. * parent: BaseNode,
  9. * key: string,
  10. * index: number
  11. * ) => void} SyncHandler */
  12. export class SyncWalker extends WalkerBase {
  13. /**
  14. *
  15. * @param {SyncHandler} enter
  16. * @param {SyncHandler} leave
  17. */
  18. constructor(enter, leave) {
  19. super();
  20. /** @type {SyncHandler} */
  21. this.enter = enter;
  22. /** @type {SyncHandler} */
  23. this.leave = leave;
  24. }
  25. /**
  26. *
  27. * @param {BaseNode} node
  28. * @param {BaseNode} parent
  29. * @param {string} [prop]
  30. * @param {number} [index]
  31. * @returns {BaseNode}
  32. */
  33. visit(node, parent, prop, index) {
  34. if (node) {
  35. if (this.enter) {
  36. const _should_skip = this.should_skip;
  37. const _should_remove = this.should_remove;
  38. const _replacement = this.replacement;
  39. this.should_skip = false;
  40. this.should_remove = false;
  41. this.replacement = null;
  42. this.enter.call(this.context, node, parent, prop, index);
  43. if (this.replacement) {
  44. node = this.replacement;
  45. this.replace(parent, prop, index, node);
  46. }
  47. if (this.should_remove) {
  48. this.remove(parent, prop, index);
  49. }
  50. const skipped = this.should_skip;
  51. const removed = this.should_remove;
  52. this.should_skip = _should_skip;
  53. this.should_remove = _should_remove;
  54. this.replacement = _replacement;
  55. if (skipped) return node;
  56. if (removed) return null;
  57. }
  58. for (const key in node) {
  59. const value = node[key];
  60. if (typeof value !== "object") {
  61. continue;
  62. } else if (Array.isArray(value)) {
  63. for (let i = 0; i < value.length; i += 1) {
  64. if (value[i] !== null && typeof value[i].type === 'string') {
  65. if (!this.visit(value[i], node, key, i)) {
  66. // removed
  67. i--;
  68. }
  69. }
  70. }
  71. } else if (value !== null && typeof value.type === "string") {
  72. this.visit(value, node, key, null);
  73. }
  74. }
  75. if (this.leave) {
  76. const _replacement = this.replacement;
  77. const _should_remove = this.should_remove;
  78. this.replacement = null;
  79. this.should_remove = false;
  80. this.leave.call(this.context, node, parent, prop, index);
  81. if (this.replacement) {
  82. node = this.replacement;
  83. this.replace(parent, prop, index, node);
  84. }
  85. if (this.should_remove) {
  86. this.remove(parent, prop, index);
  87. }
  88. const removed = this.should_remove;
  89. this.replacement = _replacement;
  90. this.should_remove = _should_remove;
  91. if (removed) return null;
  92. }
  93. }
  94. return node;
  95. }
  96. }