前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >something about Node.js

something about Node.js

作者头像
theanarkh
发布2021-12-02 12:31:33
2370
发布2021-12-02 12:31:33
举报
文章被收录于专栏:原创分享原创分享

1 如何监听 Node.js 的所有函数

这是一次危险的探索,但是或许某些场景下可以用到。主要想做的事情是劫持所有的 Node.js 函数,在函数执行前后,插入钩子做些事情。但是由于场景很多而且负责,劫持的风险非常高,如果你使用以下代码有问题,可以提个 issue。以下代码可以通过预加载方式加载或者在你的代码执行前加载。

module-wrap.js

代码语言:javascript
复制
const { Module } = require('module');

function before(...args) {
    console.log(`before call function args: ${args}`);}
function after(...args) {
    console.log(`after call function result: ${args}`)}const originRequire = Module.prototype.require;// hack to make console init 
console.log('');

function newRequire(...args){
    let exports = originRequire.call(this, ...args);
    function patch(originFunc, key = originFunc.name) {
        function dummy(...args) {
            // you can do something before the function will be executed
            before([key, ...args]);
            let result;
            // if the function call by new, we call by new too
            if (new.target) {
                result = new originFunc(...args);
                // make the constructor point to new.target instead of originFunc because new.target maybe be a subclass of originFunc
                result.constructor = new.target;
            } else {
                result = originFunc.call(this, ...args);
            }
            const params = [key];
            if (result) {
                params.push(result);
            }
            // you can do something after the function have executed
            after(params);
            return result;
        }
        // we need merge the fields which is writable of originFunc into dummy
        for (const [key, descriptionInfo] of Object.entries(Object.getOwnPropertyDescriptors(originFunc))) {
            if (descriptionInfo.writable) {
                Object.defineProperty(dummy, key, descriptionInfo);
            }
        }
        // change the function name to the name of originFunc
        Object.defineProperty(dummy, 'name', { configurable: true, value: originFunc.name });
        Object.defineProperty(dummy, 'name', { configurable: false });
        // the prototype of dummy need point to originFunc.prototype
        dummy.prototype = originFunc.prototype;
        return dummy;
    }

    // wrapper all functions in export, but now we don not handle the exports recursively
    if (Object.prototype.toString.call(exports) === '[object Object]') {
        for (const [key, value] of Object.entries(exports)) {
            if (typeof value === 'function') {
                exports[key] = patch(value, key);
            }
        }
    } else if (Object.prototype.toString.call(exports) === '[object Function]') {
        exports = patch(exports);
    }
    return exports;}

Module.prototype.require = newRequire;

测试例子。server.js

代码语言:javascript
复制
const http = require('http');
http.createServer((req, res) => {
    res.end('ok');}).listen(8888);

执行 node -r ./module-wraper.js server.js 将会看到输出

代码语言:javascript
复制
before call function args: createServer,(req, res) => {
    res.end('ok');}
after call function result: createServer,[object Object]

你可以在钩子里做你想做的事情。

2 如何实现直接执行 ts 代码

ts-node 相信很多同学都使用过,它可以直接执行 ts 模块。下面的代码同样可以做到。

代码语言:javascript
复制
const { Module } = require('module');const fs = require('fs');const path = require('path');const ts = require('typescript');const { compileFunction } = process.binding('contextify');
Module._extensions['.ts'] = function(module, filename) {
    const content = fs.readFileSync(filename, 'utf8');
    const { outputText } = ts.transpileModule(content, { compilerOptions: { module: ts.ModuleKind.CommonJS }});
    const result = compileFunction(
        outputText,
        filename,
        0,
        0,
        undefined,
        false,
        undefined,
        [],
        [
          'exports',
          'require',
          'module',
          '__filename',
          '__dirname',
        ]
    );
    result.function.call(this, module.exports, (...args) => module.require(...args), module, filename, path.dirname(filename));};

原理很简单,主要是给 Node.js 增加一个 ts 模块的 加载器,在加载器里通过 typescript 包编译 ts 成 js,然后再调用 V8 的 compileFunction 执行 js。

3 如何写一个 js loader

Node.js 的某些框架的实现模块是在启动前会加载所有的模块成一个树状的结果,下面代码是实现这个 loader 的逻辑。

代码语言:javascript
复制
const fs = require('fs');const { relative } = require('path');

function load() {
    return new Promise((resolve, reject) => {
        const root = process.cwd() + '/a';
        const fileTree = {};
        const REGEXP = /\.(js|json|node)$/;
        const filters = ['node_modules', '__tests__'];
        let request = 0;
        let done = false;
        function _load(currentPath) {
            request++;
            fs.readdir(currentPath, (error, dirOrFiles) => {
                request--;
                if (error) {
                    console.error(error);
            if (!done) {
                done = true;
            reject(error);
            }
                } else if (dirOrFiles.length) {
                    const absolutePaths = dirOrFiles.filter( (file) => !filters.includes(file) ).map((file) => `${currentPath}/${file}`);
                    for (let i = 0; i < absolutePaths.length; i++) {
                        const absolutePath = absolutePaths[i];
                        request++;
                        fs.stat(absolutePath, (error, stat) => {
                            request--;
                            if (error) {
                                console.error(error);
                                if (!done) {
                  done = true;
                  reject(error);
                    }
                            } else {
                                if (stat.isDirectory()) {
                                    _load(absolutePath);
                                } else {
                                    try {
                                        if (REGEXP.test(absolutePath)) {
                                            const absolutePathWhithoutExt = absolutePath.replace(REGEXP, '');
                                            const relativePathWhithoutExt = relative(root, absolutePathWhithoutExt);
                                            const paths = relativePathWhithoutExt.split('/');
                                            let currentNode = fileTree;
                                            for (let j = 0; j < paths.length - 1; j++) {
                                                const path = paths[j];
                                                if (typeof currentNode[path] === 'object' && currentNode[path] !== null) {
                                                    currentNode = currentNode[path];
                                                } else {
                                                    currentNode = currentNode[path] = {};
                                                }
                                            }
                                            currentNode[paths[paths.length - 1]] = require(absolutePath);
                                        }
                                    } catch(e) {
                                        console.error(e);
                    if (!done) {
                      done = true;
                      reject(e);
                     }
                                    }
                                }
                            }
                            if (!request && !done) {
                done = true;
                                resolve(fileTree);
                            }
                        });
                    }
                }
                if (!request && !done) {
                    resolve(fileTree);
                }
            });
        }
        _load(root);
    });}load().then(console.log).catch(console.error);

利用异步读取的方式提高速度。

github 地址:

1. https://github.com/theanarkh/Node.js-Function-Wrapper

2. https://github.com/theanarkh/tiny-ts-node

3. https://github.com/theanarkh/Node.js-Loader

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 如何监听 Node.js 的所有函数
  • 2 如何实现直接执行 ts 代码
  • 3 如何写一个 js loader
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档