UNPKG

twreporter-react

Version:

React-Redux site for The Reporter Foundation in Taiwan

482 lines (383 loc) 14.7 kB
// Hacking too much time // Based on Node.js Module class sources: // https://github.com/nodejs/node/blob/master/lib/module.js 'use strict'; var _Set = require('babel-runtime/core-js/set')['default']; var _getIterator = require('babel-runtime/core-js/get-iterator')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; exports.__esModule = true; var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _path2 = require('path'); var _path3 = _interopRequireDefault(_path2); var _module2 = require('module'); var _module3 = _interopRequireDefault(_module2); var _toolsLog = require('./tools/log'); var _toolsLog2 = _interopRequireDefault(_toolsLog); var _helpers = require('./helpers'); var _toolsSerializeJavascript = require('./tools/serialize-javascript'); var _toolsSerializeJavascript2 = _interopRequireDefault(_toolsSerializeJavascript); var require_hacker = { preceding_path_resolvers: [], path_resolvers: [], global_hook_resolved_modules: {}, global_hooks_enabled: true, occupied_file_extensions: new _Set(), // logging log: new _toolsLog2['default']('require-hook', { debug: false }), // this.options.debug // installs a require() path resolver // // resolve - a function which takes two parameters: // // the path to be resolved // the module in which the require() call was originated // // must return either a new path to the require()d module // or it can return nothing to fall back to the original require()d module path // // returns an object with an .unmount() method // resolver: function resolver(resolve) { validate.resolve(resolve); var resolver = function resolver(path, module) { // resolve the path for this require() call var resolved_path = resolve(path, module); // if no path was resolved - do nothing if (!_helpers.exists(resolved_path)) { return; } // return the path to be require()d return resolved_path; }; require_hacker.preceding_path_resolvers.push(resolver); var result = { unmount: function unmount() { // javascript arrays still have no .remove() method in the XXI-st century require_hacker.preceding_path_resolvers = require_hacker.preceding_path_resolvers.filter(function (x) { return x !== resolver; }); } }; return result; }, // installs a global require() hook for all paths // // (if these paths are certain to exist in the filesystem // and if you need only a specific file extension // then use the .hook(extension, resolve) method instead) // // id - a meaningful textual identifier // // resolve - a function which takes two parameters: // // the path to be resolved // the module in which the require() call was originated // // must return either a javascript CommonJS module source code // (i.e. "module.exports = ...", etc) // or it can return nothing to fall back to the original Node.js loader // // returns an object with an .unmount() method // // options: // // precede_node_loader: // // true - this require() hook will intercept all require() calls // before they go into the original Node.js loader // // false - this require() hook will only intercept those require() calls // which failed to be resolved by the original Node.js loader // // default value: true // global_hook: function global_hook(id, resolve) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; validate.global_hook(id, resolve); var resolver = function resolver(path, module) { // get CommonJS module source code for this require() call var source = resolve(path, module); // if no CommonJS module source code returned - skip this require() hook if (!_helpers.exists(source)) { return; } // CommonJS module source code returned, // so put it into a hash for a corresponding key var resolved_path = path + '.' + id; // flush require() cache delete require.cache[resolved_path]; // put the CommonJS module source code into the hash require_hacker.global_hook_resolved_modules[resolved_path] = source; // return the path to be require()d // in order to get the CommonJS module source code return resolved_path; }; if (options.precede_node_loader === false) { require_hacker.path_resolvers.push(resolver); } else { require_hacker.preceding_path_resolvers.push(resolver); } var hook = this.hook(id, function (path) { var source = require_hacker.global_hook_resolved_modules[path]; delete require_hacker.global_hook_resolved_modules[path]; return source; }); var result = { unmount: function unmount() { // javascript arrays still have no .remove() method in the XXI-st century require_hacker.preceding_path_resolvers = require_hacker.preceding_path_resolvers.filter(function (x) { return x !== resolver; }); require_hacker.path_resolvers = require_hacker.path_resolvers.filter(function (x) { return x !== resolver; }); hook.unmount(); } }; return result; }, // installs a require() hook for the extension // // extension - a file extension to hook into require()s of // (examples: 'css', 'jpg', 'js') // // resolve - a function that takes two parameters: // // the path requested in the require() call // the module in which the require() call was originated // // must return either a javascript CommonJS module source code // (i.e. "module.exports = ...", etc) // or it can return nothing to fall back to the original Node.js loader // hook: function hook(extension, resolve) { var _this = this; this.log.debug('Hooking into *.' + extension + ' files loading'); // validation validate.extension(extension); validate.resolve(resolve); // occupy file extension this.occupied_file_extensions.add(extension); // dotted extension var dot_extension = '.' + extension; // keep original extension loader var original_loader = _module3['default']._extensions[dot_extension]; // display a warning in case of extension loader override if (original_loader) { // output a debug message in case of extension loader override, // not a warning, so that it doesn't scare people this.log.debug('-----------------------------------------------'); this.log.debug('Overriding an already existing require() hook '); this.log.debug('for file extension ' + dot_extension); this.log.debug('-----------------------------------------------'); } // the list of cached modules var cached_modules = new _Set(); // Node.js inner API check /* istanbul ignore if */ if (!_module3['default']._extensions) { throw new Error('Incompatilbe Node.js version detected: "Module._extensions" array is missing. File an issue on GitHub.'); } // set new loader for this extension _module3['default']._extensions[dot_extension] = function (module, filename) { _this.log.debug('require() hook fired for ' + filename); // var source = fs.readFileSync(filename, 'utf8') var source = resolve(filename, module); if (!_helpers.exists(source)) { _this.log.debug('Fallback to original loader'); // this message would appear if there was no loader // for the extension of the filename if (_path3['default'].extname(filename) !== dot_extension) { _this.log.info('Trying to load "' + _path3['default'].basename(filename) + '" as a "*' + dot_extension + '"'); } // load the file with the original loader return (original_loader || _module3['default']._extensions['.js'])(module, filename); } // add this file path to the list of cached modules cached_modules.add(filename); // Node.js inner API check /* istanbul ignore if */ if (!module._compile) { throw new Error('Incompatilbe Node.js version detected: "Module.prototype._compile" function is missing. File an issue on GitHub.'); } // compile javascript module from its source // https://github.com/nodejs/node/blob/master/lib/module.js#L379 module._compile(source, filename); }; var result = { // uninstall the hook unmount: function unmount() { // clear require() cache for this file extension for (var _iterator = cached_modules, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } var _path = _ref; delete require.cache[_path]; } // mount the original loader for this file extension _module3['default']._extensions[dot_extension] = original_loader; // free file extension _this.occupied_file_extensions['delete'](extension); } }; return result; }, // returns a CommonJS modules source. to_javascript_module_source: function to_javascript_module_source(anything) { // if the asset source wasn't found - return an empty CommonJS module if (!_helpers.exists(anything)) { return 'module.exports = undefined'; } // if it's already a common js module source if (typeof anything === 'string' && is_a_module_declaration(anything)) { return anything; } // generate javascript module source code based on the `source` variable return 'module.exports = ' + _toolsSerializeJavascript2['default'](anything); }, // resolves a requireable `path` to a real filesystem path relative to the `module` // (resolves `npm link`, etc) resolve: function resolve(path_to_resolve, module) { // Module._resolveFilename existence check is perfomed outside of this method try { require_hacker.global_hooks_enabled = false; return original_resolveFilename(path_to_resolve, module); } finally { require_hacker.global_hooks_enabled = true; } } }; // validation var validate = { extension: function extension(_extension) { // if (typeof extension !== 'string') // { // throw new Error(`Expected string extension. Got ${extension}`) // } if (_path3['default'].extname('test.' + _extension) !== '.' + _extension) { throw new Error('Invalid file extension "' + _extension + '"'); } // check if the file extension is already occupied if (require_hacker.occupied_file_extensions.has(_extension)) { throw new Error('File extension "' + _extension + '" is already occupied by require-hacker'); } }, resolve: function resolve(_resolve) { if (typeof _resolve !== 'function') { throw new Error('Resolve should be a function. Got "' + _resolve + '"'); } }, global_hook: function global_hook(id, resolver) { if (!id) { throw new Error('You must specify global hook id'); } if (_path3['default'].extname('test.' + id) !== '.' + id) { throw new Error('Invalid global hook id "' + id + '". Expected a valid file extension.'); } // check if the file extension is already occupied if (require_hacker.occupied_file_extensions.has(id)) { throw new Error('File extension "' + id + '" is already occupied by require-hacker'); } validate.resolve(resolver); } }; // Node.js inner API check /* istanbul ignore if */ if (!_module3['default']._resolveFilename) { throw new Error('Incompatilbe Node.js version detected: "Module._resolveFilename" function is missing. File an issue on GitHub.'); } // Node.js inner API check /* istanbul ignore if */ if (!_module3['default']._findPath) { throw new Error('Incompatilbe Node.js version detected: "Module._findPath" function is missing. File an issue on GitHub.'); } // the module in which the require() call originated var require_caller = undefined; // instrument Module._resolveFilename // https://github.com/nodejs/node/blob/master/lib/module.js#L322 // // `arguments` would conflict with Babel, therefore `...parameters` // // const native_module = require('native_module') var original_resolveFilename = _module3['default']._resolveFilename; _module3['default']._resolveFilename = function () { for (var _len = arguments.length, parameters = Array(_len), _key = 0; _key < _len; _key++) { parameters[_key] = arguments[_key]; } var request = parameters[0]; var parent = parameters[1]; // take note of the require() caller require_caller = parent; return original_resolveFilename.apply(this, parameters); }; // instrument Module._findPath // https://github.com/nodejs/node/blob/master/lib/module.js#L335-L341 // // `arguments` would conflict with Babel, therefore `...parameters` // var original_findPath = _module3['default']._findPath; _module3['default']._findPath = function () { for (var _len2 = arguments.length, parameters = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { parameters[_key2] = arguments[_key2]; } var request = parameters[0]; // const paths = parameters[1] // preceeding resolvers if (require_hacker.global_hooks_enabled) { for (var _iterator2 = require_hacker.preceding_path_resolvers, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) { var _ref2; if (_isArray2) { if (_i2 >= _iterator2.length) break; _ref2 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) break; _ref2 = _i2.value; } var resolver = _ref2; var resolved_path = resolver(request, require_caller); if (_helpers.exists(resolved_path)) { return resolved_path; } } } // original Node.js loader var filename = original_findPath.apply(undefined, parameters); if (filename !== false) { return filename; } // rest resolvers if (require_hacker.global_hooks_enabled) { for (var _iterator3 = require_hacker.path_resolvers, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _getIterator(_iterator3);;) { var _ref3; if (_isArray3) { if (_i3 >= _iterator3.length) break; _ref3 = _iterator3[_i3++]; } else { _i3 = _iterator3.next(); if (_i3.done) break; _ref3 = _i3.value; } var resolver = _ref3; var resolved = resolver.resolve(request, require_caller); if (_helpers.exists(resolved)) { return resolved; } } } return false; }; // detect if it is a CommonJS module declaration function is_a_module_declaration(text) { return text.indexOf('module.exports = ') === 0 || /\s+module\.exports = .+/.test(text); } exports['default'] = require_hacker; module.exports = exports['default']; //# sourceMappingURL=require hacker.js.map