基于新的浏览器的木偶替换器,以及我们如何建造它

2021-03-12 07:49:27

毫无疑问,使用任何浏览器自动化库时,最有用的工具之一是可视调试器。我们' ve写了很多关于它。由于无论是专门为开发人员建立的,我们始终努力提供最佳体验,并通过更新的自动化图书馆对最先进的技术提供满意的。简单地说:无头:虚假的isn' t足以调试木偶,也不是任何当前生活" repl"在那里。

今天,我们很高兴宣布我们完全重新编写调试器现在活着🎉!它包括我们在调试器中想要的所有功能,以及更多。

新的Repl Web-App我们'在内部使用这个调试器进行了很长一段时间,并发现它非常有用。简短的功能列表包括:

相当多的规划,研发进入了这一编辑器。由于我们发现它是这样一个有用的工具,我们也认为它' D&#39是有趣的,越过其一些核心基本面以及我们如何建立它。让我暂停并在迄今为止之前说出来:如果你今天做一件事,那就试试了调试器。如果你做两件事,请阅读下面我们如何建造它!

毫无疑问,最多"有用"任何调试器或repl的方面是编辑体验。自从我们'重大粉丝的VS-Code,我们决定使用强大的梦幻般的摩纳哥编辑库。通过您最喜欢的库旋转和运行摩洛尼亚可以有点麻烦,因此我们现在备份键入为仅两个的软件包:节点和木偶。鉴于这两个包都有打字已经,它只是将它们加载到编辑器中的问题。一旦您拥有编辑器对象设置,就这样做是相当简单的,所以我们的第一步!

//为类型/ javascript supportself.monacoenvironment = {getworkerurl :( _moduleid:任何,标签:字符串)=>; {if(label ===' typescript' ||标签===' javascript'){return' ./ ts.worker.bundle.js&#39 ;;返回' ./ editor.worker.bundle.js&#39 ;; }}; //实例化编辑器const编辑器= monaco.editor.create(document.getelementById('代码'),{value:initialcode,language:' typectift&#39 ;,主题:' VS-Dark',fontsize:14,WordWrap:'',scrollbeyondlastline:false,sucemainlaylayout:true,minimap:{已启用:false}});

一旦设置,摩纳哥有一个用于将键入注入其的API以及类型标注编译器的配置。设置新的TS项目相对简单:

//指定这是一个类似节点的neamentionmonaco.languages.typescript.typescriptdefaults.setCompilerOptions({allowNontsextensions.Sture,Target:Monaco.languages.Typescript.scripttarget.es2020,moduleresolution:monaco.languages.typescript.moduleresolutionkind.nodejs,moduly.nodejs :monaco.languages.typescript.modulekind.commonjs,});

现在一切都是设置它' s只是一个将我们类型加载到编辑器中的问题。这部分原来有点棘手,因为摩纳哥想要类型作为普通字符串属性。谢天谢地,我们'重新使用webpack捆绑项目,所以"加载"这些类型与要求它们为原始文件一样简单:

//首先将类型加载为普通字符串const puppeteertypes =要求(' !!原始装载机!木偶核心/ lib / type.d.ts'); //...后,将木偶仪器类型添加到editormonaco.languages.typescript.typescriptdefaults.addextralib(puppeteertypes.default,' node_modules/@types/pppeteer/index.d.ts' ;,);

出于我们的编辑的目的,我们实际上可以进行一些操作,以便它们出口全球范围内的类型(并且不需要' T.对于那些想要查看整个项目的人来说,可以在此处找到完整的来源。

毫无疑问,第二个最有帮助的事情实际上看到了浏览器达到了⁠的东西 - 尤其是在环境中运行的环境。我们在当地机器上工作的情况很好,但是云中的问题,因此浏览器实际上在云中执行的浏览器对我们来说很重要,以便完全透明度。要做到这一点,我们达到了Chrome' s devtools协议,直接返回浏览器的截止视频帧。这意味着您的浏览器实际上正在运行木偶代码并查看会话,这意味着没有其他网络啤酒花。

const浏览器= await poppeteer.connect({browserwsendpoint}).catch((错误)=> {console.error(错误);抛出错误;}); const page = await browser.newpage(); //使用页面' s客户端开始跳斯卡constract =(页面页面页面)._客户端作为cdpsession; await client.send(' page.startscreencast' {格式:' jpeg'质量:100}); //连接屏幕CastCast ... Client.on('页面.CEreencyFrame',()=> console.log(" todo"));

现在,我们' vere get我们的浏览器和页面样板设置,它只是一个接线的问题,即使我们可以在本地浏览器上绘制它。这样做的是'然而,复杂的是,需要一些设置。对于这种特殊情况,我们' LL实际上是绘制在<帆布&gt上;浏览器内的元素。

const $ canvas = document.querySelector('#39;#39;)const ctx = $ canvas.getcontext(' 2d'); const img = new image(); onscreencastframe(({data})=> {img.onload =()=> ctx.drawimage(img,0,0,$ canvas.width,$ canvas.height); img .src ='数据:图像/ png; base64,' +数据;});

一旦两个都设置了,您可以在浏览器中执行以下操作(假设您有一个< canvas id =" screencast">在您的页面中):

const $ canvas = document.querySelector('#39;#39;)const ctx = $ canvas.getcontext(' 2d'); const img = new image(); const浏览器= await poppeteer.connect({browserwsendpoint}).catch((错误)=> {console.error(错误);抛出错误;}); const page = await browser.newpage(); const client =(页面页面)._客户作为cdpsession; await client.send(' page.startscreencast' {format:' jpeg'质量:100}); client.on('页面。页面。,onscreencastframe page.screencastframe',onscreencastframe ); onscreencastframe(({data})=> {img.onload =()=> ctx.drawimage(img,0,0,$ canvas.width,$ canvas.height); img.src =&#39 ;数据:图像/ png; base64,' +数据;});

这真的只是开始:我们不谈论如何处理键盘和鼠标事件,但如果您想看到完整的代码随意这样做。

最后一点工作正在处理下载:您的代码可能运行的事情会产生某种像PDF或PNG文件的静态资产。为此,我们必须在项目中获得一个更多的图书馆来帮助我们:文件类型。这个模块在其核心,接受了某种二进制斑点并告诉我们它是什么。对于我们而言,这主要有助于编辑从PDF和PNG图像中辨别,但也可能扩展到其他格式。

一旦页面' s代码已经ran,我们会查看脚本返回的内容,并对应该发生的事情进行受过教育的猜测。由于这一切都在网络工作中都跑了,因此我们可以使用一些沙拳来评估代码。请记住:这一切都在用户'在网络中的浏览器中遇到了它的浏览器,所以当它大多数时间都是一个坏主意,我们在这里造成了例外。

const browser = await puppeteer.connect({browserwsendpoint}); const page = await browser.newpage(); //代码以封装的函数编写,因此我们将其与//正确的参数调用它,以便它运行。 eval(代码)({页面}).then(async(res:domence)=> {//需要在这里处理res,因为它可能是函数返回的任何东西...}).catch((e:错误) => {//将错误注入页面,以便它们显示在云浏览器中显示' s devtools ...... neat!页面& page.evaluate((err)=> console.error(错误),e.tostring());});

为了处理下载,我们有一个非常简单的功能,检查它是什么:

从&#39导入文件类型;文件类型/浏览器&#39 ;; async函数(响应?:string | uint8Array):< {type:string,payload:dom,} | null> {//没有任何返回的触发器如果(!响应){return null; } // pdf / png是浏览器中的uInt8Arrays //如果没有确定类型,如果(响应instanceof uint8Array){const类型=(等待fileType.flbuffer(响应)|| {mime:未定义})。哑剧; if(!type){返回null; }返回{类型,有效载荷:响应}; } //在这里,我们通过简单的检查来检查Page.Content响应(HTML)它以&#34开头;<"字符if(respaction ===' string'){return {type:response.startswith('')? ' text / html' :'文本/ plain',有效载荷:响应,}} //检查它' sa json blob,我们应该下载(typeof response ==='对象') {return {type:'应用程序/ json',payload:json.stringify(响应,null,''),}; }返回{type:'文本/ plain',有效载荷:响应,};}

最后,一旦我们有返回的文件类型,我们可以触发浏览器下载它:

从&#39导入文件类型;文件类型/浏览器&#39 ;;从&#39导入蛹;木偶&#39 ;; const browser = await puppeteer.connect({browserwsendpoint}); const page = await browser.newpage();异步函数(响应?:string | uint8array):< {type:string,payload:dom} | null> {if(!响应){返回null; }如果(responsionsof uint8Array){const类型=(等待filetype.flbbudeer(响应)|| {mime:未定义})。mime; if(!type){返回null; }返回{类型,有效载荷:响应}; }如果(resefof response ===' string'){return {type:response.startswith(''')? ' text / html' :'文本/ plain',payload:响应,}}如果(typeof response ==='对象'){return {type:'应用程序/ json',有效载荷:json.stringify(响应,null,#39;'),}; } return {type:'文本/ plain',有效载荷:响应,}; const downloadfile =(下载)=> {const filename ='我的下载&#39 ;; const blob = new blob([download.payload],{type:download.type}); const link = document.createelement(' a'); link.href = window.url.createobjecturl(blob); link.dowload = filename; return.Click();}; eval(代码)({页面}).then(async(res:domence)=> {const payload = makedownload(res); if(powload){downloadfile(payload);}}).catch((E:错误)=> {页面&& page.evaluate((err)=> console.error(err),e.tostring());});

虽然这只是我们用于构建现场调试器的一些技术的亮点,但是在那里进入了该项目的更多技术。我们在我们的选项卡实现时触摸了,我们如何将木质仪器加载到浏览器中,甚至我们如何保存代码!你肯定应该看看回购并在荣耀中看到整个项目。最后,您肯定应该查看我们的调试器,并在您无头工作负载中正常工作时使用它。