新增调试信息

This commit is contained in:
2025-10-27 22:15:25 +08:00
parent ae62457d8c
commit 04642cb2f0
5479 changed files with 683397 additions and 3450 deletions
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Google Inc.
Copyright (c) 2012-2013 Johannes Ewald
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+174
View File
@@ -0,0 +1,174 @@
# Requizzle
![Build status](https://github.com/hegemonic/requizzle/actions/workflows/build.yaml/badge.svg?branch=main)
Swizzle a little something into your Node.js modules.
## What's Requizzle?
Requizzle provides a drop-in replacement for Node.js's `require()` function.
This replacement enables you to change a module's source code when Node.js loads
the module.
You can use Requizzle in your test cases, or in production code if you like to
live dangerously.
## How can I change a module with Requizzle?
There are several different ways:
### Look for modules in new places
With Requizzle, you can add directories to the module lookup path, which forces
Node.js to search those directories for modules. This can be useful if:
+ You're tired of writing code like `require('../../../../../lib/foo')`.
+ You want to expose your app's modules to external plugins.
### Add code before or after the module's source code
Tamper with modules to your heart's delight by adding arbitrary code before or
after the module's own source code.
### Mess with child modules
When you use Requizzle to require a module, you can force each child module's
`require` method to inherit your changes to the parent module. (By default, only
the parent module is changed.)
## Will Requizzle break my dependencies?
Probably not. It's true that Requizzle gives you plenty of new and exciting ways
to tamper with, and possibly break, your module dependencies. But Requizzle also
tries not to break anything on its own. In particular:
+ **Requizzle preserves strict-mode declarations**. If a module starts with a
strict-mode declaration, Requizzle keeps it in place. Your changes will appear
after the strict-mode declaration.
+ **Requizzle leaves native modules alone**. If you use Requizzle to load one of
Node.js's built-in modules, such as `fs` or `path`, Requizzle won't mess with
it.
## Usage
The Requizzle module exports a single function, which returns a drop-in
replacement for `require()`.
When you call the function, you must pass in an `options` object, which can
include any of these properties:
+ `extras`: A pair of functions that return text to insert before or after the
module's source code. Each function accepts two parameters: `targetPath`, the
path to the required module, and `parentModule`, the `Module` object for the
module's parent. Each function must return a string.
+ `extras.before`: A function that returns text to insert before the
module's source code.
+ `extras.after`: A function that returns text to insert after the module's
source code.
+ `infect`: Determines whether child modules are infected with the same changes
as the parent module. Set to `true` to force child modules to inherit your
changes. Defaults to `false`.
+ `requirePaths`: Additional paths to search for required modules. For example,
if `requirePaths` is set to `['/usr/lib/junk/modules']`, and you save a
JavaScript module at `/usr/lib/junk/modules/mymodule.js`, you can require the
module as `mymodule`.
You can provide an array of paths, which will be searched before the default
module paths, or an object with the following properties:
+ `requirePaths.before`: An array of paths to search before the default
module paths.
+ `requirePaths.after`: An array of paths to search after the default module
paths. Use this property if you want the module to use its own local
dependencies when possible, then fall back to the additional paths if
necessary.
By default, the require path is not changed.
## Examples
```js
const requizzle = require('requizzle');
// Say hello and goodbye to each module.
const logRequire = requizzle({
extras: {
before: function(targetPath, parentModule) {
return 'console.log("Hello %s!", ' + targetPath + ');\n';
},
after: function(targetPath, parentModule) {
return 'console.log("Goodbye %s!", ' + targetPath + ');\n';
}
}
});
// Prints "Hello /path/to/mymodule.js!" and "Goodbye /path/to/mymodule.js!"
const myModule = logRequire('mymodule');
// Look for modules in the current module's `lib` directory, and force child
// modules to do the same.
const path = require('path');
const extraPathRequire = requizzle({
infect: true,
requirePaths: [path.join(__dirname, 'lib')]
});
// If `foo` needs to require a module in `./lib`, it can use `require('bar')`
// instead of `require('./lib/bar')`.
const foo = extraPathRequire('./foo');
```
## Troubleshooting
Here are some problems you might run into when you use Requizzle, along with
solutions to each problem. If you run into any problems that aren't addressed
here, please file a new issue!
### Requizzle slowed down my code! A lot!
Requizzle adds minimal overhead to the module-loading process. However, your
code will run _much_ slower than usual if you do both of the following:
+ Use Requizzle's `infect` option.
+ Require modules that have a lot of `require()` calls within the scope of
individual functions.
If Requizzle seems to slow down your app, look for module calls that are within
function scope, then move them to each module's top-level scope.
### Requizzle made my module do something weird!
Do you have any
[circular dependencies](https://nodejs.org/api/modules.html#modules_cycles) in
the modules that aren't working? Circular dependencies can cause unusual
behavior with Requizzle, just as they can without Requizzle. Try breaking the
circular dependency.
### Requizzle violates the [Law of Demeter](https://wikipedia.org/wiki/Law_of_Demeter)! It's an unnatural abomination!
Fair enough.
## Changelog
+ 0.2.4 (November 2022): Fixed a compatibility issue with
[core modules](https://nodejs.org/docs/latest-v18.x/api/modules.html#core-modules)
that are loaded with the `node:` prefix, as in `require('node:fs')`.
+ 0.2.3 (July 2019): Updated dependencies.
+ 0.2.2 (May 2019): Fixed a compability issue with Node.js 12.
+ 0.2.1 (December 2014): The `requirePaths` option no longer inserts an extra
line break into the source file.
+ 0.2.0 (June 2014): The `requirePaths` option can now contain `before` and
`after` properties. Paths in the `before` property will be searched first; paths
in the `after` property will be searched last.
+ 0.1.1 (June 2014): If the `requirePaths` option is used, the module loader now
searches the extra paths first rather than last.
+ 0.1.0 (June 2014): Initial release.
## Acknowledgements
Requizzle is very loosely adapted from Johannes Ewald's
[rewire](https://github.com/jhnns/rewire) module, which is designed to modify a
module's behavior for unit testing. If Requizzle doesn't meet your needs, please
take a look at rewire!
## License
[MIT license](https://github.com/hegemonic/requizzle/blob/main/LICENSE).
+28
View File
@@ -0,0 +1,28 @@
/*
Copyright 2014 Google LLC
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
const _ = require('lodash');
const Requizzle = require('./lib/requizzle');
module.exports = function requizzle(options) {
let instance;
if (!options || typeof options !== 'object') {
throw new TypeError("Requizzle's options parameter must be a non-null object.");
}
options = _.clone(options);
options.parent = module.parent;
return (filepath) => {
instance = instance || new Requizzle(options);
return instance.requizzle(filepath);
};
};
module.exports.Requizzle = Requizzle;
// force Node.js to reload this module each time it's required, so module.parent is always correct
delete require.cache[__filename];
+162
View File
@@ -0,0 +1,162 @@
/*
Copyright 2014 Google LLC
Copyright 2012-2013 Johannes Ewald
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
const _ = require('lodash');
const fs = require('fs');
const Module = require('module');
const originalWrapper = Module.wrapper.slice(0);
const requizzleWrappers = {
extras: require('./wrappers/extras'),
requirePaths: require('./wrappers/requirepaths'),
strict: require('./wrappers/strict'),
};
function wrap(wrappers, script) {
return wrappers[0] + script + wrappers[1];
}
function replaceWrapper(wrapperObj) {
const joiner = '\n';
const before = wrapperObj.before.join(joiner);
const after = wrapperObj.after.join(joiner);
const wrappers = [originalWrapper[0] + before, after + originalWrapper[1]];
Module.wrap = wrap.bind(null, wrappers);
}
function restoreWrapper() {
Module.wrap = wrap.bind(null, originalWrapper);
}
function createModule(targetPath, parentModule, moduleCache) {
moduleCache[targetPath] = moduleCache[targetPath] || new Module(targetPath, parentModule);
return moduleCache[targetPath];
}
/**
* Wrapper for `require()` to prevent the target module's dependencies from being swizzled.
*
* @param {!Module} targetModule - The module that is being swizzled.
* @param {!function} nodeRequire - The original `require()` method for the target module.
* @param {!string} filepath - The value passed to `require()`.
* @return {!Module} The requested module dependency.
*/
function requireProxy(targetModule, nodeRequire, filepath) {
restoreWrapper();
targetModule.require = nodeRequire;
return nodeRequire.call(targetModule, filepath);
}
/**
* Wrapper for `require()` to swizzle the target module's dependencies, using the same settings as
* the target module.
*
* @param {!Module} targetModule - The module that is being swizzled.
* @param {!Object} opts - The Requizzle options object.
* @param {!string} filepath - The value passed to `require()`.
* @return {!Module} The requested module dependency.
*/
function infectProxy(targetModule, cache, opts, filepath) {
let moduleExports;
// loaded here to avoid circular dependencies
const Requizzle = require('./requizzle');
let requizzle;
opts = _.clone(opts);
opts.parent = targetModule;
requizzle = new Requizzle(opts, cache);
moduleExports = requizzle.requizzle(filepath);
return moduleExports;
}
exports.load = function load(targetPath, parentModule, wrapper, cache, options) {
let nodeRequire;
let targetModule;
// Handle circular requires, and avoid reloading modules unnecessarily
if (cache.module[targetPath]) {
return cache.module[targetPath];
}
targetModule = createModule(targetPath, parentModule, cache.module);
nodeRequire = targetModule.require;
if (options.infect) {
targetModule.require = (filepath) => infectProxy(targetModule, cache, options, filepath);
} else {
targetModule.require = (filepath) => requireProxy(targetModule, nodeRequire, filepath);
}
// update the wrapper before we load the target module
replaceWrapper(wrapper);
targetModule.load(targetModule.id);
// make sure the wrapper is restored even if the target module doesn't load any dependencies
restoreWrapper();
return targetModule;
};
/**
* Check whether the entire module includes a `'use strict'` declaration.
*
* @param {string} src - The source file to check.
* @return {boolean} Set to `true` if the module includes a `use strict` declaration.
*/
function detectStrictMode(src) {
return /^\s*(?:["']use strict["'])[ \t]*(?:[\r\n]|;)/g.test(src);
}
function loadSource(targetPath, sourceCache) {
if (sourceCache[targetPath] === undefined) {
sourceCache[targetPath] = fs.readFileSync(targetPath, 'utf8');
}
return sourceCache[targetPath];
}
exports.createWrapper = function createWrapper(targetPath, parentModule, cache, options) {
let src;
const wrapperObject = {
before: [],
after: [],
};
function add(wrapperFunctions, opts) {
const params = [targetPath, parentModule, opts];
['before', 'after'].forEach((item) => {
const result = wrapperFunctions[item].apply(null, params);
if (result) {
wrapperObject[item].push(result);
}
});
}
// Preserve the module's `use strict` declaration if present
src = loadSource(targetPath, cache.source);
if (detectStrictMode(src) === true) {
add(requizzleWrappers.strict);
}
if (options.requirePaths) {
add(requizzleWrappers.requirePaths, options.requirePaths);
}
if (options.extras) {
add(requizzleWrappers.extras, options.extras);
}
return wrapperObject;
};
+102
View File
@@ -0,0 +1,102 @@
/*
Copyright 2014 Google LLC
Copyright 2012-2013 Johannes Ewald
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
/** @module lib/requizzle */
const loader = require('./loader');
const Module = require('module');
const NATIVE_MODULE_PREFIX = 'node:';
/**
* Function that returns text to swizzle into the module.
*
* @typedef module:lib/requizzle~wrapperFunction
* @type {function}
* @param {string} targetPath - The path to the target module.
* @param {string} parentModulePath - The path to the module that is requiring the target module.
* @return {string} The text to insert before or after the module's source code.
*/
/**
* Options for the wrappers that will be swizzled into the target module.
*
* @typedef module:lib/requizzle~options
* @type {Object}
* @property {Object=} options.extras - Functions that generate text to swizzle into the target
* module.
* @property {module:lib/requizzle~wrapperFunction} options.extras.after - Function that returns
* text to insert after the module's source code.
* @property {module:lib/requizzle~wrapperFunction} options.extras.before - Function that returns
* text to insert before the module's source code.
* @property {(Array.<string>|string)} options.requirePaths - Additional paths to search when
* resolving module paths in the target module.
*/
function isNativeModule(targetPath, parentModule) {
let lookupPaths;
let isNative = false;
if (targetPath.startsWith(NATIVE_MODULE_PREFIX)) {
isNative = true;
} else {
lookupPaths = Module._resolveLookupPaths(targetPath, parentModule, true);
/* istanbul ignore next */
isNative =
lookupPaths === null ||
(lookupPaths.length === 2 && lookupPaths[1].length === 0 && lookupPaths[0] === targetPath);
}
return isNative;
}
/**
* Create a `Requizzle` instance. If you provide options, Requizzle will default to those options
* when you call {@link Requizzle#requizzle}.
*
* @class
* @param {!module:lib/requizzle~options} options - Options for the wrappers that will be swizzled
* into the target module.
* @param {Object=} cache - For internal use.
*/
class Requizzle {
constructor(options, cache) {
this._options = options;
this._cache = cache || {
module: {},
source: {},
};
}
/**
* Load the module, swizzling in the requested changes.
*
* @param {!string} targetPath - The path to the module that will be loaded.
* @return {Module} The swizzled module.
*/
requizzle(targetPath) {
const options = this._options;
const parentModule = options.parent;
let targetModule;
let wrapper;
// Don't interfere with native modules
if (isNativeModule(targetPath, parentModule)) {
return require(targetPath);
}
// Resolve the filename relative to the parent module
targetPath = Module._resolveFilename(targetPath, parentModule);
wrapper = loader.createWrapper(targetPath, parentModule, this._cache, this._options);
targetModule = loader.load(targetPath, parentModule, wrapper, this._cache, this._options);
return targetModule.exports;
}
}
module.exports = Requizzle;
+21
View File
@@ -0,0 +1,21 @@
/*
Copyright 2014 Google LLC
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
function callFunction(targetPath, parentModule, func) {
if (!func) {
return '';
}
return func(targetPath, parentModule);
}
exports.before = function before(targetPath, parentModule, options) {
return callFunction(targetPath, parentModule, options.before);
};
exports.after = function after(targetPath, parentModule, options) {
return callFunction(targetPath, parentModule, options.after);
};
+48
View File
@@ -0,0 +1,48 @@
/*
Copyright 2014 Google LLC
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
const path = require('path');
function resolvePaths({ filepath }, paths) {
if (!paths) {
return [];
}
return paths.slice(0).map((p) => path.resolve(filepath, p));
}
function requirePaths(parentModule, opts) {
const result = {
before: [],
after: [],
};
if (!parentModule) {
return result;
}
if (Array.isArray(opts)) {
result.before = resolvePaths(parentModule, opts);
} else {
result.before = resolvePaths(parentModule, opts.before);
result.after = resolvePaths(parentModule, opts.after);
}
return result;
}
exports.before = function before(targetPath, parentModule, opts) {
const resolvedPaths = requirePaths(parentModule, opts);
return (
`module.paths = ${JSON.stringify(resolvedPaths.before)}.concat(module.paths)` +
`.concat(${JSON.stringify(resolvedPaths.after)}); `
);
};
exports.after = function after() {
return '';
};
+13
View File
@@ -0,0 +1,13 @@
/*
Copyright 2014 Google LLC
Use of this source code is governed by the MIT License, available in this package's LICENSE file
or at http://opensource.org/licenses/MIT.
*/
exports.before = function before() {
return '"use strict";';
};
exports.after = function after() {
return '';
};
+40
View File
@@ -0,0 +1,40 @@
{
"name": "requizzle",
"version": "0.2.4",
"description": "Swizzle a little something into your require() calls.",
"main": "index.js",
"scripts": {
"test": "gulp test"
},
"repository": {
"type": "git",
"url": "git://github.com/hegemonic/requizzle.git"
},
"keywords": [
"module",
"modules",
"require",
"inject",
"dependency",
"swizzle"
],
"author": "Jeff Williams <jeffrey.l.williams@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/hegemonic/requizzle/issues"
},
"homepage": "https://github.com/hegemonic/requizzle",
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"@jsdoc/eslint-config": "^1.1.9",
"@jsdoc/prettier-config": "^0.1.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"expectations": "^1.0.0",
"gulp": "^4.0.2",
"gulp-eslint-new": "^1.7.0",
"gulp-mocha": "^8.0.0"
}
}