javascript - 如何将 Node js native 模块注入(inject) puppeteer 页面

标签 javascript node.js google-chrome puppeteer

我有一个使用 NWjs 的应用程序,在我的应用程序页面中,它使用了很多很多 nodejs 原生模块(如 fs/http/etc)

然后我想用puppeteer来测试我的页面,所以我们需要注入(inject)nodejs原生模块来运行页面

我尝试在下面的代码中运行,但它无法将 nodejs native 模块注入(inject)页面

const fs = require("fs");
const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
    const page = await browser.newPage();
    page.on('console', msg => console.log(msg.text()));
    await page.exposeFunction("require", function (name) {
        console.log("require module name:"+name);
        return require(name) // or return fs , result is same
    })
    await page.evaluate(async () => {

        const fs = await window.require("fs");
        console.log(fs,typeof fs.readFile);//fs.readFile is undefined

    });
    await browser.close();
});

最佳答案

您的代码没问题。问题是 puppeteer 只能与页面上下文交换可序列化的数据。也就是说,可通过 JSON.stringify 传输的对象。

函数和其他复杂的 Web Api 不可转移。这就是您在页面上下文中看到 JSHandle@object 的原因。它是一个对象,包含来自 fs 模块的所有 module.exports 可序列化值。

你可以做一个简单的测试看看。在与您的代码相同的文件夹中设置另一个包含简单模块的文件,并尝试在您的代码中要求它。示例:

// file mod1.js
module.exports = {
  number: 1,
  string: 'test',
  object: { complex: 'object' },
  function: function() {}, // this will not be transfered
  fs: require('fs')
};

现在你运行你的代码调用这个模块:

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  page.on('console', msg => console.log(msg.text()));
  await page.exposeFunction("require", function (name) {
    console.log("required module name: " + name);
    return require(name); // or return fs , result is same
  });

  await page.evaluate(async () => {
    const module = await window.require("./mod1");
    // changed to JSON.stringify for you to see module's content
    console.log('module:', JSON.stringify(module, null, 2));
  });
  await browser.close();
});

不幸的是,关于 page.exposeFunction 方法的这一点,文档并不清楚。

编辑:我想出了一个可能的解决方案来要求一个本地模块。我只测试了 fs.unlikSync、fs.writeFileSync 和 fs.readFileSync 方法,但它们都有效。 :) 这是代码:

const puppeteer = require('puppeteer');

// expose every methods of the moduleName to the Page, under window[moduleName] object.
async function exposeModuleMethods(page, moduleName) {
  const module = require(moduleName);
  const methodsNames = Object.getOwnPropertyNames(module);
  for (const methodName of methodsNames) {
    await page.exposeFunction('__' + moduleName + '_' + methodName, module[methodName]);
    await page.evaluate((moduleName, methodName) => {
      window[moduleName] = window[moduleName] || {};
      window[moduleName][methodName] = window['__' + moduleName + '_' + methodName]; // alias
    }, moduleName, methodName);
  }
}

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  page.on('console', msg => console.log(msg.text()));

  // expose `require` on Page. When it is used on Page, the function `exposeModuleMethods`
  // expose the individual module's methods on window[moduleName].
  await page.exposeFunction('require', async function (moduleName) {
    await exposeModuleMethods(page, moduleName);
  });

  // make the Page require "fs" native module
  await page.evaluate(async (moduleName) => await require(moduleName), 'fs');

  // make the Page test the "fs" module
  await page.evaluate(async (moduleName) => {
    // save a file on the current directory named "mod2.js"
    await window[moduleName]['writeFileSync']('./mod2.js', 'any text');
    // read the file "mod2.js"
    const mod2 = await window[moduleName]['readFileSync']('./mod2.js', { encoding: 'utf8' });
    // log it's content
    console.log(JSON.stringify(mod2, null, 2));
    // delete the file
    await window[moduleName]['unlinkSync']('./mod2.js');
  }, 'fs');

  await browser.close();
})();

关于javascript - 如何将 Node js native 模块注入(inject) puppeteer 页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57863547/

相关文章:

javascript - react 代码中的@internal JavaScript 文档标记,这是 jsdoc、闭包还是其他?

javascript - 保存文件时阻止 javascript blob 编辑数据

javascript - NPM 搜索远程包

javascript - 获取图像 Angular 或 Node 的 Base64 的最佳方法

javascript - 未使用异步调用最后一个回调

jQuery 的 'keypress' 不适用于 Chrome 中的某些键。如何解决?

javascript - 如何使用 Lodash/JS 递归过滤嵌套对象?

javascript - 找不到 Chrome webkitNotification : api missing

javascript - 如何使用 API 关闭 Google Chrome 窗口中的所有选项卡

javascript - 使用 Flask 解决跨域资源共享