TransformNormalModulePlugin.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. const NormalModule = require('webpack/lib/NormalModule');
  2. const Module = require('webpack/lib/Module');
  3. const nodeObjectHash = require('node-object-hash');
  4. const logMessages = require('./util/log-messages');
  5. const {
  6. relateNormalPath,
  7. relateNormalRequest,
  8. relateNormalPathSet,
  9. relateNormalLoaders,
  10. } = require('./util/relate-context');
  11. const pluginCompat = require('./util/plugin-compat');
  12. const serial = require('./util/serial');
  13. const serialResolveRequest = serial.created({
  14. context: serial.path,
  15. request: serial.request,
  16. });
  17. const serialResolved = serial.created({
  18. // context: serial.path,
  19. // request: serial.request,
  20. // userRequest: serial.request,
  21. // rawRequest: serial.request,
  22. resource: serial.request,
  23. resolveOptions: serial.identity,
  24. // loaders: serial.loaders,
  25. });
  26. const serialJson = {
  27. freeze(arg, value, extra) {
  28. return JSON.parse(arg);
  29. },
  30. thaw(arg, frozen, extra) {
  31. return JSON.stringify(arg);
  32. },
  33. };
  34. const serialMap = serial.map;
  35. const serialResolvedMap = serial.map(
  36. serial.pipe(
  37. { freeze: serialJson.freeze, thaw: serial.identity.thaw },
  38. serialResolveRequest,
  39. { freeze: serial.identity.freeze, thaw: serialJson.thaw },
  40. ),
  41. serialResolved,
  42. );
  43. const serialResourceHashMap = serial.map(serial.request, serial.identity);
  44. const serialNormalConstructor4 = serial.constructed(NormalModule, {
  45. data: serial.pipe(
  46. { freeze: (arg, module) => module, thaw: arg => arg },
  47. serial.created({
  48. type: serial.identity,
  49. request: serial.request,
  50. userRequest: serial.request,
  51. rawRequest: serial.request,
  52. loaders: serial.loaders,
  53. resource: serial.path,
  54. parser: serial.parser,
  55. generator: serial.generator,
  56. resolveOptions: serial.identity,
  57. }),
  58. ),
  59. });
  60. const serialNormalModuleExtra4 = {
  61. freeze() {},
  62. thaw(arg, frozen, extra, methods) {
  63. extra.module = arg;
  64. return arg;
  65. },
  66. };
  67. const serialNormalIdentifier4 = {
  68. freeze(arg, module, extra, methods) {
  69. return serial.request.freeze(module.identifier(), null, extra, methods);
  70. },
  71. thaw(arg) {
  72. return arg;
  73. },
  74. };
  75. const serialNormalAssigned4 = serial.assigned({
  76. factoryMeta: serial.identity,
  77. issuer: serial.pipe(
  78. {
  79. freeze(arg, { issuer }) {
  80. return issuer && typeof issuer === 'object'
  81. ? issuer.identifier()
  82. : issuer;
  83. },
  84. thaw(arg, frozen, extra) {
  85. return arg;
  86. },
  87. },
  88. serial.request,
  89. {
  90. freeze(arg) {
  91. return arg;
  92. },
  93. thaw(arg, frozen, { compilation }) {
  94. if (compilation.modules) {
  95. for (const module of compilation.modules) {
  96. if (
  97. module &&
  98. typeof module.identifier === 'function' &&
  99. module.identifier() === arg
  100. ) {
  101. return module;
  102. }
  103. }
  104. for (const cacheId in compilation.cache) {
  105. const module = compilation.cache[cacheId];
  106. if (
  107. module &&
  108. typeof module.identifier === 'function' &&
  109. module.identifier() === arg
  110. ) {
  111. return module;
  112. }
  113. }
  114. }
  115. return arg;
  116. },
  117. },
  118. ),
  119. useSourceMap: serial.identity,
  120. lineToLine: serial.identity,
  121. });
  122. const serialNormalOriginExtra4 = {
  123. freeze() {},
  124. thaw(arg, frozen, extra) {
  125. if (typeof arg.issuer === 'object') {
  126. extra.origin = arg.issuer;
  127. }
  128. return arg;
  129. },
  130. };
  131. const serialNormalBuild4 = serial.assigned({
  132. built: serial.identity,
  133. buildTimestamp: serial.identity,
  134. buildMeta: serial.identity,
  135. buildInfo: serial.created({
  136. assets: serial.moduleAssets,
  137. cacheable: serial.identity,
  138. contextDependencies: serial.pathSet,
  139. exportsArgument: serial.identity,
  140. fileDependencies: serial.pathSet,
  141. harmonyModule: serial.identity,
  142. jsonData: serial.identity,
  143. strict: serial.identity,
  144. }),
  145. warnings: serial.moduleWarning,
  146. errors: serial.moduleError,
  147. _source: serial.source,
  148. _buildHash: serial.identity,
  149. hash: serial.identity,
  150. _lastSuccessfulBuildMeta: serial.identity,
  151. __hardSource_resolved: serialResolvedMap,
  152. __hardSource_oldHashes: serial.pipe(
  153. {
  154. freeze(arg, module, extra) {
  155. const obj = {};
  156. const cachedMd5s = extra.compilation.__hardSourceFileMd5s;
  157. for (const file of module.buildInfo.fileDependencies) {
  158. obj[file] = cachedMd5s[file];
  159. }
  160. for (const dir of module.buildInfo.contextDependencies) {
  161. obj[dir] = cachedMd5s[dir];
  162. }
  163. return obj;
  164. },
  165. thaw: serial.identity.thaw,
  166. },
  167. serialResourceHashMap,
  168. ),
  169. });
  170. const serialNormalError4 = {
  171. freeze() {},
  172. thaw(arg, module, extra) {
  173. arg.error = arg.errors[0] || null;
  174. return arg;
  175. },
  176. };
  177. const serialNormalSourceExtra4 = {
  178. freeze() {},
  179. thaw(arg, module, extra) {
  180. extra.source = arg._source;
  181. return arg;
  182. },
  183. };
  184. const serialNormalSource4 = serial.assigned({
  185. _cachedSource: serial.source,
  186. _cachedSourceHash: serial.identity,
  187. renderedHash: serial.identity,
  188. });
  189. const serialNormalModule4PreBuild = serial.serial('NormalModule', {
  190. constructor: serialNormalConstructor4,
  191. setModuleExtra: serialNormalModuleExtra4,
  192. identifier: serialNormalIdentifier4,
  193. assigned: serialNormalAssigned4,
  194. setOriginExtra: serialNormalOriginExtra4,
  195. });
  196. const serialNormalModule4PostBuild = serial.serial('NormalModule', {
  197. build: serialNormalBuild4,
  198. dependencyBlock: serial.dependencyBlock,
  199. setError: serialNormalError4,
  200. setSourceExtra: serialNormalSourceExtra4,
  201. source: serialNormalSource4,
  202. });
  203. const serialNormalModule4 = serial.serial('NormalModule', {
  204. constructor: serialNormalConstructor4,
  205. setModuleExtra: serialNormalModuleExtra4,
  206. identifier: serialNormalIdentifier4,
  207. assigned: serialNormalAssigned4,
  208. setOriginExtra: serialNormalOriginExtra4,
  209. build: serialNormalBuild4,
  210. dependencyBlock: serial.dependencyBlock,
  211. setError: serialNormalError4,
  212. setSourceExtra: serialNormalSourceExtra4,
  213. source: serialNormalSource4,
  214. });
  215. const needRebuild4 = function() {
  216. if (this.error) {
  217. this.cacheItem.invalid = true;
  218. this.cacheItem.invalidReason = 'error building';
  219. return true;
  220. }
  221. const fileHashes = this.__hardSourceFileMd5s;
  222. const cachedHashes = this.__hardSourceCachedMd5s;
  223. const resolvedLast = this.__hardSource_resolved;
  224. const missingCache = this.__hardSource_missingCache;
  225. for (const file of this.buildInfo.fileDependencies) {
  226. if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) {
  227. this.cacheItem.invalid = true;
  228. this.cacheItem.invalidReason = 'md5 mismatch';
  229. return true;
  230. }
  231. }
  232. for (const dir of this.buildInfo.contextDependencies) {
  233. if (!cachedHashes[dir] || fileHashes[dir] !== cachedHashes[dir]) {
  234. this.cacheItem.invalid = true;
  235. this.cacheItem.invalidReason = 'md5 mismatch';
  236. return true;
  237. }
  238. }
  239. let resolvedNeedRebuild = false;
  240. for (const _resolveKey in resolvedLast) {
  241. const resolveKey = JSON.parse(_resolveKey);
  242. const resolved = resolvedLast[_resolveKey];
  243. let normalId = 'normal';
  244. if (resolved.resolveOptions) {
  245. normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
  246. resolved.resolveOptions,
  247. )}`;
  248. }
  249. const resolvedMissing =
  250. missingCache[normalId] &&
  251. missingCache[normalId][
  252. JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]])
  253. ];
  254. if (!resolvedMissing || resolvedMissing.invalid) {
  255. resolved.invalid = true;
  256. resolved.invalidReason = `resolved normal invalid${
  257. resolvedMissing
  258. ? ` ${resolvedMissing.invalidReason}`
  259. : ': resolve entry not in cache'
  260. }`;
  261. resolvedNeedRebuild = true;
  262. }
  263. }
  264. return resolvedNeedRebuild;
  265. };
  266. const serialNormalModule3 = serial.serial('NormalModule', {
  267. constructor: serial.constructed(NormalModule, {
  268. request: serial.request,
  269. userRequest: serial.request,
  270. rawRequest: serial.request,
  271. loaders: serial.loaders,
  272. resource: serial.path,
  273. parser: serial.parser,
  274. }),
  275. setModuleExtra: serialNormalModuleExtra4,
  276. // Used internally by HardSource
  277. identifier: serialNormalIdentifier4,
  278. assigned: serial.assigned({
  279. issuer: serial.pipe(
  280. {
  281. freeze(arg, { issuer }) {
  282. return issuer && typeof issuer === 'object'
  283. ? issuer.identifier()
  284. : issuer;
  285. },
  286. thaw(arg, frozen, extra) {
  287. return arg;
  288. },
  289. },
  290. serial.request,
  291. {
  292. freeze(arg) {
  293. return arg;
  294. },
  295. thaw(arg, frozen, { compilation }) {
  296. if (compilation.modules) {
  297. for (const module of compilation.modules) {
  298. if (
  299. module &&
  300. typeof module.identifier === 'function' &&
  301. module.identifier() === arg
  302. ) {
  303. return module;
  304. }
  305. }
  306. for (const cacheId in compilation.cache) {
  307. const module = compilation.cache[cacheId];
  308. if (
  309. module &&
  310. typeof module.identifier === 'function' &&
  311. module.identifier() === arg
  312. ) {
  313. return module;
  314. }
  315. }
  316. }
  317. return arg;
  318. },
  319. },
  320. ),
  321. useSourceMap: serial.identity,
  322. lineToLine: serial.identity,
  323. }),
  324. setOriginExtra: {
  325. freeze() {},
  326. thaw(arg, frozen, extra) {
  327. if (typeof arg.issuer === 'object') {
  328. extra.origin = arg.issuer;
  329. }
  330. return arg;
  331. },
  332. },
  333. build: serial.assigned({
  334. built: serial.identity,
  335. buildTimestamp: serial.identity,
  336. cacheable: serial.identity,
  337. meta: serial.identity,
  338. assets: serial.moduleAssets,
  339. fileDependencies: serial.pathArray,
  340. contextDependencies: serial.pathArray,
  341. harmonyModule: serial.identity,
  342. strict: serial.identity,
  343. exportsArgument: serial.identity,
  344. warnings: serial.moduleWarning,
  345. errors: serial.moduleError,
  346. _source: serial.source,
  347. __hardSource_resolved: serialResolvedMap,
  348. __hardSource_oldHashes: serial.pipe(
  349. {
  350. freeze(arg, module, extra) {
  351. const obj = {};
  352. const cachedMd5s = extra.compilation.__hardSourceCachedMd5s;
  353. for (const file of module.fileDependencies) {
  354. obj[file] = cachedMd5s[file];
  355. }
  356. for (const dir of module.contextDependencies) {
  357. obj[dir] = cachedMd5s[dir];
  358. }
  359. return obj;
  360. },
  361. thaw: serial.identity.thaw,
  362. },
  363. serialResourceHashMap,
  364. ),
  365. }),
  366. hash: {
  367. freeze(arg, module, { compilation }, methods) {
  368. return module.getHashDigest(compilation.dependencyTemplates);
  369. },
  370. thaw(arg) {
  371. return arg;
  372. },
  373. },
  374. dependencyBlock: serial.dependencyBlock,
  375. setError: {
  376. freeze() {},
  377. thaw(arg, module, extra) {
  378. arg.error = arg.errors[0] || null;
  379. return arg;
  380. },
  381. },
  382. setSourceExtra: {
  383. freeze() {},
  384. thaw(arg, module, extra) {
  385. extra.source = arg._source;
  386. return arg;
  387. },
  388. },
  389. source: serial.assigned({
  390. _cachedSource: serial.created({
  391. source: serial.source,
  392. hash: serial.identity,
  393. }),
  394. }),
  395. });
  396. const needRebuild3 = function() {
  397. if (this.error) {
  398. this.cacheItem.invalid = true;
  399. this.cacheItem.invalidReason = 'error building';
  400. return true;
  401. }
  402. const fileHashes = this.__hardSourceFileMd5s;
  403. const cachedHashes = this.__hardSourceCachedMd5s;
  404. const resolvedLast = this.__hardSource_resolved;
  405. const missingCache = this.__hardSource_missingCache;
  406. for (const file of this.fileDependencies) {
  407. if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) {
  408. this.cacheItem.invalid = true;
  409. this.cacheItem.invalidReason = 'md5 mismatch';
  410. return true;
  411. }
  412. }
  413. for (const dir of this.contextDependencies) {
  414. if (!cachedHashes[dir] || fileHashes[dir] !== cachedHashes[dir]) {
  415. this.cacheItem.invalid = true;
  416. this.cacheItem.invalidReason = 'md5 mismatch';
  417. return true;
  418. }
  419. }
  420. let resolvedNeedRebuild = false;
  421. for (const _resolveKey in resolvedLast) {
  422. const resolveKey = JSON.parse(_resolveKey);
  423. const resolved = resolvedLast[_resolveKey];
  424. let normalId = 'normal';
  425. if (resolved.resolveOptions) {
  426. normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
  427. resolved.resolveOptions,
  428. )}`;
  429. }
  430. const resolvedMissing =
  431. missingCache[normalId] &&
  432. missingCache[normalId][
  433. JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]])
  434. ];
  435. if (!resolvedMissing || resolvedMissing.invalid) {
  436. resolved.invalid = true;
  437. resolved.invalidReason = `resolved normal invalid${
  438. resolvedMissing
  439. ? ` ${resolvedMissing.invalidReason}`
  440. : ': resolve entry not in cache'
  441. }`;
  442. resolvedNeedRebuild = true;
  443. }
  444. }
  445. return resolvedNeedRebuild;
  446. };
  447. const cacheable = module =>
  448. module.buildInfo ? module.buildInfo.cacheable : module.cacheable;
  449. class TransformNormalModulePlugin {
  450. constructor(options) {
  451. this.options = options || {};
  452. }
  453. apply(compiler) {
  454. const schema = this.options.schema;
  455. let serialNormalModule = serialNormalModule4;
  456. let needRebuild = needRebuild4;
  457. if (schema < 4) {
  458. serialNormalModule = serialNormalModule3;
  459. needRebuild = needRebuild3;
  460. }
  461. let createHash;
  462. if (schema >= 4) {
  463. createHash = require('webpack/lib/util/createHash');
  464. }
  465. let freeze;
  466. let mapFreeze;
  467. let _methods;
  468. pluginCompat.tap(
  469. compiler,
  470. '_hardSourceMethods',
  471. 'TransformNormalModulePlugin',
  472. methods => {
  473. _methods = methods;
  474. // store = methods.store;
  475. // fetch = methods.fetch;
  476. freeze = methods.freeze;
  477. // thaw = methods.thaw;
  478. mapFreeze = methods.mapFreeze;
  479. // mapThaw = methods.mapThaw;
  480. },
  481. );
  482. pluginCompat.tap(
  483. compiler,
  484. 'compilation',
  485. 'TransformNormalModulePlugin',
  486. compilation => {
  487. pluginCompat.tap(
  488. compilation,
  489. 'succeedModule',
  490. 'TransformNormalModulePlugin',
  491. module => {
  492. if (module instanceof NormalModule) {
  493. try {
  494. module._dependencyBlock = freeze(
  495. 'DependencyBlock',
  496. null,
  497. module,
  498. {
  499. module,
  500. parent: module,
  501. compilation,
  502. },
  503. );
  504. } catch (e) {
  505. logMessages.moduleFreezeError(compilation, module, e);
  506. }
  507. }
  508. },
  509. );
  510. },
  511. );
  512. pluginCompat.tap(
  513. compiler,
  514. '_hardSourceFreezeModule',
  515. 'TransformNormalModulePlugin',
  516. (frozen, module, extra) => {
  517. // Set hash if it was not set.
  518. if (
  519. schema === 4 &&
  520. module instanceof NormalModule &&
  521. module.buildTimestamp &&
  522. !module.hash
  523. ) {
  524. const outputOptions = extra.compilation.outputOptions;
  525. const hashFunction = outputOptions.hashFunction;
  526. const hashDigest = outputOptions.hashDigest;
  527. const hashDigestLength = outputOptions.hashDigestLength;
  528. if (module.buildInfo && module._initBuildHash) {
  529. module._initBuildHash(extra.compilation);
  530. }
  531. const moduleHash = createHash(hashFunction);
  532. module.updateHash(moduleHash);
  533. module.hash = moduleHash.digest(hashDigest);
  534. module.renderedHash = module.hash.substr(0, hashDigestLength);
  535. if (module._cachedSource) {
  536. module._cachedSourceHash = module.getHashDigest(
  537. extra.compilation.dependencyTemplates,
  538. );
  539. }
  540. }
  541. if (
  542. module.request &&
  543. (cacheable(module) || !module.built) &&
  544. module instanceof NormalModule &&
  545. (!frozen ||
  546. (schema >= 4 && module.hash !== frozen.build.hash) ||
  547. (schema < 4 &&
  548. module.getHashDigest(extra.compilation.dependencyTemplates) !==
  549. frozen.hash))
  550. ) {
  551. const compilation = extra.compilation;
  552. if (module.cacheItem) {
  553. module.cacheItem.invalid = false;
  554. module.cacheItem.invalidReason = null;
  555. }
  556. let serialModule = serialNormalModule;
  557. if (!module.built) {
  558. serialModule = serialNormalModule4PreBuild;
  559. }
  560. const f = serialModule.freeze(
  561. null,
  562. module,
  563. {
  564. module,
  565. compilation,
  566. },
  567. _methods,
  568. );
  569. // The saved dependencies may not be the ones derived in the hash. This is
  570. // alright, in such a case the dependencies were altered before the source
  571. // was rendered. The dependencies should be modified a second time, if
  572. // they are in the same way they'll match. If they are not modified in the
  573. // same way, then it'll correctly rerender.
  574. if (module._dependencyBlock) {
  575. f.dependencyBlock = module._dependencyBlock;
  576. }
  577. return f;
  578. }
  579. return frozen;
  580. },
  581. );
  582. pluginCompat.tap(
  583. compiler,
  584. '_hardSourceThawModule',
  585. 'TransformNormalModulePlugin thaw',
  586. (module, frozen, { compilation, normalModuleFactory }) => {
  587. if (frozen.type === 'NormalModule') {
  588. let m;
  589. if (module === null) {
  590. let serialModule = serialNormalModule;
  591. if (!frozen.build || !frozen.build.built) {
  592. serialModule = serialNormalModule4PreBuild;
  593. }
  594. m = serialModule.thaw(
  595. null,
  596. frozen,
  597. {
  598. state: { imports: {} },
  599. compilation: compilation,
  600. normalModuleFactory: normalModuleFactory,
  601. },
  602. _methods,
  603. );
  604. } else {
  605. m = serialNormalModule4PostBuild.thaw(
  606. module,
  607. frozen,
  608. {
  609. state: { imports: {} },
  610. compilation: compilation,
  611. normalModuleFactory: normalModuleFactory,
  612. },
  613. _methods,
  614. );
  615. }
  616. m.cacheItem = frozen;
  617. m.__hardSourceFileMd5s = compilation.__hardSourceFileMd5s;
  618. m.__hardSourceCachedMd5s = compilation.__hardSourceCachedMd5s;
  619. m.__hardSource_missingCache = compiler.__hardSource_missingCache;
  620. m.needRebuild = needRebuild;
  621. // Unbuild if there is no cache. The module will be rebuilt. Not
  622. // unbuilding will lead to double dependencies.
  623. if (m.built && schema === 4 && !compilation.cache) {
  624. m.unbuild();
  625. }
  626. // Side load into the cache if something for this identifier isn't already
  627. // there.
  628. else if (
  629. m.built &&
  630. compilation.cache &&
  631. !compilation.cache[`m${m.identifier()}`]
  632. ) {
  633. compilation.cache[`m${m.identifier()}`] = m;
  634. }
  635. return m;
  636. }
  637. return module;
  638. },
  639. );
  640. }
  641. }
  642. module.exports = TransformNormalModulePlugin;