CacheModuleResolver.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. const nodeObjectHash = require('node-object-hash');
  2. const parseJson = require('parse-json');
  3. const serial = require('./util/serial');
  4. const pluginCompat = require('./util/plugin-compat');
  5. const relateContext = require('./util/relate-context');
  6. const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
  7. const serialJsonKey = {
  8. freeze(arg, value, extra) {
  9. return JSON.parse(arg);
  10. },
  11. thaw(arg, frozen, extra) {
  12. return JSON.stringify(arg);
  13. },
  14. };
  15. const serialJson = {
  16. freeze(arg, value, extra) {
  17. return JSON.stringify(arg);
  18. },
  19. thaw(arg, frozen, extra) {
  20. return JSON.parse(arg);
  21. },
  22. };
  23. const serialObjectAssign = serial.objectAssign;
  24. const serialResolveOptionsKey = serialObjectAssign({
  25. context: serial.path,
  26. userRequest: serial.request,
  27. options: serialObjectAssign({
  28. request: serial.request,
  29. }),
  30. });
  31. const serialResolveKey = serialObjectAssign({
  32. context: serial.path,
  33. request: serial.request,
  34. });
  35. const serialNormalModuleResolveKey = serial.pipe(
  36. serialJsonKey,
  37. {
  38. freeze(arg, key, extra) {
  39. if (Array.isArray(arg)) {
  40. return [
  41. arg[0],
  42. serial.path.freeze(arg[1], arg[1], extra),
  43. serial.request.freeze(arg[2], arg[2], extra),
  44. ];
  45. } else if (!arg.request) {
  46. return serialResolveOptionsKey.freeze(arg, arg, extra);
  47. } else {
  48. return serialResolveKey.freeze(arg, arg, extra);
  49. }
  50. },
  51. thaw(arg, frozen, extra) {
  52. if (Array.isArray(arg)) {
  53. return [
  54. arg[0],
  55. serial.path.thaw(arg[1], arg[1], extra),
  56. serial.request.thaw(arg[2], arg[2], extra),
  57. ];
  58. } else if (!arg.request) {
  59. return serialResolveOptionsKey.thaw(arg, arg, extra);
  60. } else {
  61. return serialResolveKey.thaw(arg, arg, extra);
  62. }
  63. },
  64. },
  65. serialJson,
  66. );
  67. const serialNormalModuleId = {
  68. freeze(arg, module, extra) {
  69. return (
  70. id.substring(0, 24) + serial.request.freeze(id.substring(24), id, extra)
  71. );
  72. },
  73. thaw(arg, frozen, extra) {
  74. return (
  75. id.substring(0, 24) + serial.request.thaw(id.substring(24), id, extra)
  76. );
  77. },
  78. };
  79. const serialResolveContext = serialObjectAssign({
  80. identifier: serialNormalModuleId,
  81. resource: serialNormalModuleId,
  82. });
  83. const serialResolveNormal = serialObjectAssign({
  84. context: serial.path,
  85. request: serial.request,
  86. userRequest: serial.request,
  87. rawRequest: serial.request,
  88. resource: serial.request,
  89. loaders: serial.loaders,
  90. resourceResolveData: serial.objectAssign({
  91. context: serial.created({
  92. issuer: serial.request,
  93. resolveOptions: serial.identity,
  94. }),
  95. path: serial.path,
  96. request: serial.request,
  97. descriptionFilePath: serial.path,
  98. descriptionFileRoot: serial.path,
  99. }),
  100. });
  101. const serialResolve = {
  102. freeze(arg, module, extra) {
  103. if (arg.type === 'context') {
  104. return serialResolveContext.freeze(arg, arg, extra);
  105. }
  106. return serialResolveNormal.freeze(arg, arg, extra);
  107. },
  108. thaw(arg, frozen, extra) {
  109. if (arg.type === 'context') {
  110. return serialResolveContext.thaw(arg, arg, extra);
  111. }
  112. return serialResolveNormal.thaw(arg, arg, extra);
  113. },
  114. };
  115. class ModuleResolverCache {
  116. apply(compiler) {
  117. let moduleResolveCache = {};
  118. let parityCache = {};
  119. let moduleResolveCacheChange = [];
  120. let moduleResolveCacheSerializer;
  121. const compilerHooks = pluginCompat.hooks(compiler);
  122. compilerHooks._hardSourceCreateSerializer.tap(
  123. 'HardSource - ModuleResolverCache',
  124. (cacheSerializerFactory, cacheDirPath) => {
  125. moduleResolveCacheSerializer = cacheSerializerFactory.create({
  126. name: 'module-resolve',
  127. type: 'data',
  128. autoParse: true,
  129. cacheDirPath,
  130. });
  131. },
  132. );
  133. compilerHooks._hardSourceResetCache.tap(
  134. 'HardSource - ModuleResolverCache',
  135. () => {
  136. moduleResolveCache = {};
  137. parityCache = {};
  138. },
  139. );
  140. compilerHooks._hardSourceReadCache.tapPromise(
  141. 'HardSource - ModuleResolverCache',
  142. ({
  143. contextKeys,
  144. contextValues,
  145. contextNormalPath,
  146. contextNormalRequest,
  147. contextNormalModuleId,
  148. copyWithDeser,
  149. }) => {
  150. function contextNormalModuleResolveKey(compiler, key) {
  151. if (key.startsWith('__hardSource_parityToken')) {
  152. return key;
  153. }
  154. const parsed = parseJson(key);
  155. if (Array.isArray(parsed)) {
  156. return JSON.stringify([
  157. parsed[0],
  158. contextNormalPath(compiler, parsed[1]),
  159. parsed[2],
  160. ]);
  161. } else {
  162. return JSON.stringify(
  163. Object.assign({}, parsed, {
  164. context: contextNormalPath(compiler, parsed.context),
  165. }),
  166. );
  167. }
  168. }
  169. function contextNormalModuleResolve(compiler, resolved, key) {
  170. if (key.startsWith('__hardSource_parityToken')) {
  171. parityCache[key] = resolved;
  172. return;
  173. }
  174. if (typeof resolved === 'string') {
  175. resolved = parseJson(resolved);
  176. }
  177. if (resolved.type === 'context') {
  178. return Object.assign({}, resolved, {
  179. identifier: contextNormalModuleId(compiler, resolved.identifier),
  180. resource: contextNormalRequest(compiler, resolved.resource),
  181. });
  182. }
  183. return serialResolveNormal.thaw(resolved, resolved, {
  184. compiler,
  185. });
  186. }
  187. return moduleResolveCacheSerializer
  188. .read()
  189. .then(contextKeys(compiler, contextNormalModuleResolveKey))
  190. .then(contextValues(compiler, contextNormalModuleResolve))
  191. .then(copyWithDeser.bind(null, moduleResolveCache));
  192. },
  193. );
  194. compilerHooks._hardSourceParityCache.tap(
  195. 'HardSource - ModuleResolverCache',
  196. parityRoot => {
  197. parityCacheFromCache('ModuleResolver', parityRoot, parityCache);
  198. },
  199. );
  200. compilerHooks._hardSourceVerifyCache.tapPromise(
  201. 'HardSource - ModuleResolverCache',
  202. () =>
  203. compiler.__hardSource_missingVerify.then(() => {
  204. const missingCache = compiler.__hardSource_missingCache;
  205. // Invalidate resolve cache items.
  206. Object.keys(moduleResolveCache).forEach(key => {
  207. const resolveKey = parseJson(key);
  208. const resolveItem = moduleResolveCache[key];
  209. let normalId = 'normal';
  210. if (resolveItem.resolveOptions) {
  211. normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
  212. resolveItem.resolveOptions,
  213. )}`;
  214. }
  215. if (resolveItem.type === 'context') {
  216. const contextMissing =
  217. missingCache.context[
  218. JSON.stringify([
  219. resolveKey.context,
  220. resolveItem.resource.split('?')[0],
  221. ])
  222. ];
  223. if (!contextMissing || contextMissing.invalid) {
  224. resolveItem.invalid = true;
  225. resolveItem.invalidReason = 'resolved context invalid';
  226. }
  227. } else {
  228. const normalMissing =
  229. missingCache[normalId] &&
  230. missingCache[normalId][
  231. JSON.stringify([
  232. resolveKey[1],
  233. resolveItem.resource.split('?')[0],
  234. ])
  235. ];
  236. if (!normalMissing || normalMissing.invalid) {
  237. resolveItem.invalid = true;
  238. resolveItem.invalidReason = `resolved normal invalid${
  239. normalMissing
  240. ? ` ${normalMissing.invalidReason}`
  241. : ': resolve entry not in cache'
  242. }`;
  243. }
  244. resolveItem.loaders.forEach(loader => {
  245. if (typeof loader === 'object') {
  246. if (loader.loader != null) {
  247. loader = loader.loader;
  248. } else {
  249. // Convert { "0": "b", "1": "a", "2": "r" } into "bar"
  250. loader = Object.assign([], loader).join('');
  251. }
  252. }
  253. // Loaders specified in a dependency are searched for from the
  254. // context of the module containing that dependency.
  255. let loaderMissing =
  256. missingCache.loader[
  257. JSON.stringify([resolveKey[1], loader.split('?')[0]])
  258. ];
  259. if (!loaderMissing) {
  260. // webpack searches for rule based loaders from the project
  261. // context.
  262. loaderMissing =
  263. missingCache.loader[
  264. JSON.stringify([
  265. // compiler may be a Watching instance, which refers to the
  266. // compiler
  267. (compiler.options || compiler.compiler.options).context,
  268. loader.split('?')[0],
  269. ])
  270. ];
  271. }
  272. if (!loaderMissing || loaderMissing.invalid) {
  273. resolveItem.invalid = true;
  274. resolveItem.invalidReason = 'resolved loader invalid';
  275. }
  276. });
  277. }
  278. });
  279. }),
  280. );
  281. compilerHooks.compilation.tap(
  282. 'HardSource - ModuleResolverCache',
  283. compilation => {
  284. compilation.__hardSourceModuleResolveCache = moduleResolveCache;
  285. compilation.__hardSourceModuleResolveCacheChange = moduleResolveCacheChange;
  286. },
  287. );
  288. compilerHooks._hardSourceWriteCache.tapPromise(
  289. 'HardSource - ModuleResolverCache',
  290. (
  291. compilation,
  292. { relateNormalPath, relateNormalModuleId, relateNormalRequest },
  293. ) => {
  294. if (compilation.compiler.parentCompilation) {
  295. const moduleResolveOps = [];
  296. pushParityWriteOps(compilation, moduleResolveOps);
  297. return moduleResolveCacheSerializer.write(moduleResolveOps);
  298. }
  299. const moduleResolveOps = [];
  300. function relateNormalModuleResolveKey(compiler, key) {
  301. const parsed = parseJson(key);
  302. if (Array.isArray(parsed)) {
  303. return JSON.stringify([
  304. parsed[0],
  305. relateNormalPath(compiler, parsed[1]),
  306. relateContext.relateAbsoluteRequest(parsed[1], parsed[2]),
  307. ]);
  308. } else {
  309. if (!parsed.request) {
  310. return JSON.stringify(
  311. Object.assign({}, parsed, {
  312. context: relateNormalPath(compiler, parsed.context),
  313. userRequest: relateContext.relateAbsoluteRequest(
  314. parsed.context,
  315. parsed.userRequest,
  316. ),
  317. options: Object.assign({}, parsed.options, {
  318. request: relateContext.relateAbsoluteRequest(
  319. parsed.context,
  320. parsed.options.request,
  321. ),
  322. }),
  323. }),
  324. );
  325. } else {
  326. return JSON.stringify(
  327. Object.assign({}, parsed, {
  328. context: relateNormalPath(compiler, parsed.context),
  329. request: relateContext.relateAbsoluteRequest(
  330. parsed.context,
  331. parsed.request,
  332. ),
  333. }),
  334. );
  335. }
  336. }
  337. }
  338. function relateNormalModuleResolve(compiler, resolved) {
  339. if (resolved.type === 'context') {
  340. return Object.assign({}, resolved, {
  341. identifier: relateNormalModuleId(compiler, resolved.identifier),
  342. resource: relateNormalRequest(compiler, resolved.resource),
  343. });
  344. }
  345. return serialResolveNormal.freeze(resolved, resolved, {
  346. compiler,
  347. });
  348. }
  349. moduleResolveCacheChange
  350. .reduce((carry, value) => {
  351. if (!carry.includes(value)) {
  352. carry.push(value);
  353. }
  354. return carry;
  355. }, [])
  356. .forEach(key => {
  357. // console.log(key, moduleResolveCache[key]);
  358. // moduleResolveCache[key] && console.log(relateNormalModuleResolveKey(compiler, key));
  359. // moduleResolveCache[key] && console.log(relateNormalModuleResolve(compiler, moduleResolveCache[key]));
  360. moduleResolveOps.push({
  361. key: relateNormalModuleResolveKey(compiler, key),
  362. value: moduleResolveCache[key]
  363. ? relateNormalModuleResolve(compiler, moduleResolveCache[key])
  364. : null,
  365. });
  366. });
  367. moduleResolveCacheChange = [];
  368. pushParityWriteOps(compilation, moduleResolveOps);
  369. return moduleResolveCacheSerializer.write(moduleResolveOps);
  370. },
  371. );
  372. }
  373. }
  374. module.exports = ModuleResolverCache;