cacheRequire.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. const defaults = require('./defaults');
  2. const md5 = require('./md5');
  3. const safeGet = require('./safeGet');
  4. const FileStore = require('./FileStore');
  5. const FileBlobStore = require('./FileBlobStore');
  6. const mkdir = require('./mkdir');
  7. let isEnabled = false;
  8. exports = function(options = {}) {
  9. if (isEnabled) {
  10. return;
  11. }
  12. isEnabled = true;
  13. defaults(options, defOptions);
  14. const Module = require('module');
  15. const fs = require('fs');
  16. const path = require('path');
  17. const vm = require('vm');
  18. const cacheDir = options.dir || getCacheDir();
  19. if (!fs.existsSync(cacheDir)) {
  20. mkdir.sync(cacheDir);
  21. }
  22. if (options.requirePath) {
  23. const _resolveFilename = Module._resolveFilename;
  24. const requirePathMapStore = new FileStore(
  25. path.resolve(cacheDir, 'require-path.json')
  26. );
  27. Module._resolveFilename = function(request, parent, isMain, options) {
  28. let currentModuleCache = requirePathMapStore.get(parent.filename);
  29. if (!currentModuleCache) {
  30. currentModuleCache = {};
  31. requirePathMapStore.set(parent.filename, currentModuleCache);
  32. }
  33. if (currentModuleCache[request]) {
  34. return currentModuleCache[request];
  35. }
  36. const pathToLoad = _resolveFilename(
  37. request,
  38. parent,
  39. isMain,
  40. options
  41. );
  42. currentModuleCache[request] = pathToLoad;
  43. return pathToLoad;
  44. };
  45. }
  46. if (options.code) {
  47. const fileMapStore = new FileStore(
  48. path.resolve(cacheDir, 'require-file.json')
  49. );
  50. Module._extensions['.js'] = function(module, filename) {
  51. let content;
  52. if (fileMapStore.get(filename)) {
  53. content = fileMapStore.get(filename);
  54. } else {
  55. content = fs.readFileSync(filename, 'utf8');
  56. fileMapStore.set(filename, content);
  57. }
  58. return module._compile(content, filename);
  59. };
  60. }
  61. if (options.compileCache) {
  62. const compileCacheBlobStore = new FileBlobStore(
  63. path.resolve(cacheDir, 'compile-cache')
  64. );
  65. process.once('exit', () => compileCacheBlobStore.save());
  66. Module.prototype._compile = function(content, filename) {
  67. const mod = this;
  68. function require(id) {
  69. return mod.require(id);
  70. }
  71. function resolve(request, options) {
  72. return Module._resolveFilename(request, mod, false, options);
  73. }
  74. require.resolve = resolve;
  75. resolve.paths = function paths(request) {
  76. return Module._resolveLookupPaths(request, mod, true);
  77. };
  78. require.main = process.mainModule;
  79. require.extensions = Module._extensions;
  80. require.cache = Module._cache;
  81. const dirname = path.dirname(filename);
  82. const compiledWrapper = moduleCompile(filename, content);
  83. const args = [
  84. mod.exports,
  85. require,
  86. mod,
  87. filename,
  88. dirname,
  89. process,
  90. global,
  91. Buffer
  92. ];
  93. return compiledWrapper.apply(mod.exports, args);
  94. };
  95. function moduleCompile(filename, content) {
  96. const wrapper = Module.wrap(content);
  97. const buffer = compileCacheBlobStore.get(filename);
  98. const script = new vm.Script(wrapper, {
  99. filename,
  100. lineOffset: 0,
  101. displayErrors: true,
  102. cachedData: buffer,
  103. produceCachedData: true
  104. });
  105. if (script.cachedDataProduced) {
  106. compileCacheBlobStore.set(filename, script.cachedData);
  107. } else if (script.cachedDataRejected) {
  108. compileCacheBlobStore.delete(filename);
  109. }
  110. const compiledWrapper = script.runInThisContext({
  111. filename,
  112. lineOffset: 0,
  113. columnOffset: 0,
  114. displayErrors: true
  115. });
  116. return compiledWrapper;
  117. }
  118. }
  119. };
  120. const defOptions = {
  121. requirePath: true,
  122. code: false,
  123. compileCache: true
  124. };
  125. function getCacheDir() {
  126. const os = require('os');
  127. return (
  128. os.tmpdir() +
  129. '/' +
  130. md5(
  131. `${get(process, 'versions.v8')}${get(require, 'main.filename') ||
  132. get(module, 'parent.filename')}`
  133. )
  134. );
  135. }
  136. function get(obj, path) {
  137. return safeGet(obj, path) || '';
  138. }
  139. module.exports = exports;