parity.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. const parseJson = require('parse-json');
  2. const { cachePrefix } = require('.');
  3. class ParityRoot {
  4. constructor() {
  5. this.children = [];
  6. }
  7. add(name) {
  8. const bits = new ParityCache(name);
  9. this.children.push(bits);
  10. return bits;
  11. }
  12. verify() {
  13. const firstChild = this.children[0];
  14. if (!this.children.some(child => child.root)) {
  15. return true;
  16. }
  17. for (const child of this.children) {
  18. if (!child.verify()) {
  19. this.reason = {
  20. cache: child,
  21. cacheName: child.name,
  22. cacheReason: child.reason,
  23. message: `Cache ${child.name} is not complete. ${
  24. child.reason.message
  25. }`,
  26. };
  27. return false;
  28. }
  29. if (child !== firstChild && child.root.token !== firstChild.root.token) {
  30. this.reason = {
  31. firstCache: firstChild,
  32. firstCacheName: firstChild.name,
  33. firstCacheReason: firstChild.reason,
  34. secondCache: child,
  35. secondCacheName: child.name,
  36. secondCacheReason: child.reason,
  37. message: `Cache ${firstChild.name} and ${child.name} disagree.`,
  38. };
  39. return false;
  40. }
  41. }
  42. return true;
  43. }
  44. }
  45. class ParityCache {
  46. constructor(name) {
  47. this.name = name;
  48. this.root = null;
  49. this.bits = {};
  50. this.reason = null;
  51. }
  52. add(_token) {
  53. const token = ParityToken.fromJson(_token);
  54. if (token.isRoot) {
  55. this.root = token;
  56. }
  57. this.bits[token.id] = token;
  58. }
  59. verify() {
  60. if (this.root === null) {
  61. this.reason = {
  62. message: 'Root compilation not found.',
  63. };
  64. return false;
  65. }
  66. for (const id of this.root.ids) {
  67. if (typeof this.bits[id] === 'undefined') {
  68. this.reason = {
  69. message: `Compilation '${id}' not found.`,
  70. };
  71. return false;
  72. } else if (this.root.token !== this.bits[id].token) {
  73. this.reason = {
  74. message: `Root and '${id}' compilation disagree.`,
  75. };
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. }
  82. const createParityToken = (id, ids = null) => {
  83. const token = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c =>
  84. c === 'x'
  85. ? ((Math.random() * 16) | 0).toString(16)
  86. : (((Math.random() * 4) | 0) + 8).toString(16),
  87. );
  88. return new ParityToken(id, token, ids);
  89. };
  90. class ParityToken {
  91. constructor(id, token, ids = null) {
  92. this.id = id;
  93. this.token = token;
  94. this.isRoot = ids !== null;
  95. this.ids = ids;
  96. }
  97. static fromCompilation(compilation) {
  98. let parentCompilation = compilation;
  99. while (parentCompilation.compiler.parentCompilation) {
  100. parentCompilation = parentCompilation.compiler.parentCompilation;
  101. }
  102. if (!parentCompilation.__hardSource_parityToken) {
  103. parentCompilation.__hardSource_parityToken = createParityToken(
  104. cachePrefix(parentCompilation),
  105. [],
  106. );
  107. }
  108. if (compilation !== parentCompilation) {
  109. return parentCompilation.__hardSource_parityToken.createChild(
  110. cachePrefix(compilation),
  111. );
  112. }
  113. return parentCompilation.__hardSource_parityToken;
  114. }
  115. static fromJson(json) {
  116. return new ParityToken(json.id, json.token, json.ids);
  117. }
  118. createChild(id) {
  119. this.ids.push(id);
  120. return new ParityToken(id, this.token);
  121. }
  122. toJSON() {
  123. return {
  124. type: 'CacheParityToken',
  125. id: this.id,
  126. ids: this.ids,
  127. token: this.token,
  128. };
  129. }
  130. }
  131. const parseIfString = item => {
  132. if (typeof item === 'string') {
  133. return parseJson(item);
  134. }
  135. return item;
  136. };
  137. const parityCacheFromCache = (name, parityRoot, cache) => {
  138. const parityCache = parityRoot.add(name);
  139. if (cache.__hardSource_parityToken_root) {
  140. const rootCompilation = parseIfString(cache.__hardSource_parityToken_root);
  141. parityCache.add(rootCompilation);
  142. rootCompilation.ids.forEach(id => {
  143. if (cache[`__hardSource_parityToken_${id}`]) {
  144. parityCache.add(parseIfString(cache[`__hardSource_parityToken_${id}`]));
  145. }
  146. });
  147. }
  148. };
  149. const pushParityWriteOps = (compilation, ops) => {
  150. if (compilation.compiler.parentCompilation) {
  151. ops.push({
  152. key: `__hardSource_parityToken_${cachePrefix(compilation)}`,
  153. value: JSON.stringify(ParityToken.fromCompilation(compilation)),
  154. });
  155. } else {
  156. ops.push({
  157. key: `__hardSource_parityToken_root`,
  158. value: JSON.stringify(ParityToken.fromCompilation(compilation)),
  159. });
  160. }
  161. };
  162. module.exports = {
  163. ParityRoot,
  164. ParityCache,
  165. ParityToken,
  166. parityCacheFromCache,
  167. pushParityWriteOps,
  168. };