Eclipse Theia 入门(一):在 Docker 开发环境中启动 Theia 开发本文是通过为 Theia 设置本地基于 docker 的开发环境来帮助新手开始 Eclipse Theia 开发的系列文章的一部分。 所以就在这里。经过 10 多年的 Eclipse 开发后,我想亲自动手开发一些全新的东西。我很长时间以来一直关注Orion、Che和Theia的发展,但只是在远处;我只是没有时间实际尝试,更不用说参与这些项目了。 我使用过Gitpod几次,但只是在我没有本地开发环境并且不想启动和运行时快速查看 GitHub 存储库。 然而,我注意到我的客户对基于 Web 的环境越来越感兴趣,因此,我最终想尝试使用 Eclipse Theia。 但是怎么做?我发现这篇文章 提供了几个以不同方式运行 Eclipse Theia 的选项,但是这些选项都没有真正适合我。我想在我的浏览器中运行一个 Theia 实例,所以Theia Blueprint 不是一个选项。我希望能够离线工作,因此无法使用 Gitpod。而且我不想在我的机器上安装 nodejs。但我们的目标仍然是让一个基本的 Theia 产品启动并运行,它应该准备好开始破解和定制它,然后开始创建我的第一个 Theia 扩展。 我已经开始并再次中止了几次尝试创建一个我可以在其中执行此操作的 Docker 容器。但到目前为止,我还没有设法创造一个顺畅的环境。 然后,最近,我发现了Docker Dev Environments Preview。这是 Docker Desktop 的一个实验性功能(目前似乎没有可用于此功能的 CLI,而且 Docker Desktop 仅适用于 Windows 和 MacOS)。它将 Git 存储库克隆、Docker 容器设置和 VS Code 集成结合在一起。这导致了一个简单的过程,您只需单击几下即可在本地启动和运行环境。它似乎是可以在本地运行的 Gitpod 的一个小兄弟。 因此,我已按照撰写 Theia 应用程序的说明进行操作, 并将 FROM node:12 RUN apt-get update \ && apt-get install -y g++ gcc make python2.7 pkg-config libx11-dev libxkbfile-dev libsecret-1-dev # install code generator for theia extensions RUN npm install -g yo generator-theia-extension USER node EXPOSE 3000 ENTRYPOINT docker-entrypoint.sh 为了表明 Docker Dev Environments 应该使用它 version: "3.7" services: theia: build: context: ../docker-container stdin_open: true tty: true volumes: - /home/stefan/theia-boilerplate/:/theia:cached 从这三个文件中,我创建了 GitHub 存储库https://github.com/xpomul/theia-boilerplate,现在可以将其用作进一步 Theia 实验的模板。 入门现在很简单,只需执行以下步骤: 1. Fork GitHub 上的存储库(或使用我的来试用) 2. 打开 Docker 桌面,转到“开发环境”并单击“创建开发环境”。粘贴 GitHub URL 并继续。一段时间后,您的 Theia 开发环境现在应该已启动并运行。 3. 单击“在 VS Code 中打开”。您的工作区现在应该包含存储库文件。从现在开始,可以按照https:///docs/composing_applications的说明进行操作: 4. 打开一个新终端并输入yarn以下载所有 Theia 依赖项。(这可能需要相当长的时间……) 5. 该过程完成后,输入yarn theia build, 以构建 Theia。(同样,这可能需要一些时间;不要因有关filenamify模块的警告而气馁) 6. 最后进入yarn theia start --plugins=local-dir:plugins,Theia 会出现在http://localhost:3000(VS Code 会宣布这一点并提供打开浏览器) 我们准备好了。从这里开始,我们有一个正在运行的 Theia,可以通过在 VS Code 中添加和更改文件以及在 VS Code 终端中管理 Theia 来修改和自定义它。例如,下一步,我们可以跳转到Authoring a Theia Extension。 Eclipse Theia 入门(二):在 VS Code 远程容器中开始 Theia 开发 本文是通过为 Theia 设置本地基于 docker 的开发环境来帮助新手开始 Eclipse Theia 开发的系列文章的一部分。 在写完上一篇文章 Eclipse Theia 入门(1):在 Docker 开发环境中启动 Theia 开发之后,我在另一台没有安装Docker 桌面但使用docker-machine 的机器上尝试了相同的设置在boot2docker VM中管理其容器。 我的目标是在这台机器上实现类似的简单启动(以及在没有Docker Desktop的 Linux 机器上)。通过深入挖掘Docker 开发环境功能的内部工作原理,我了解到大部分功能也可以通过远程容器扩展直接在VS Code中使用。 与Docker 开发环境功能类似,您可以将文件夹添加.devcontainer到包含名为devcontainer.json. 然后,任何人都可以直接克隆这个存储库并使用VS Code在 docker 容器中开始对其进行处理。这是我的devcontainer.json文件的样子: { "name": "theia-dev", "context": "../docker-container", "dockerFile": "../docker-container/Dockerfile", "forwardPorts": [3000], "postCreateCommand": "yarn && yarn theia build", "remoteUser": "node" } 此处引用的内容与我们在上一篇文章Dockerfile中用于Docker 开发环境的内容相同。 要开始使用, 在 VS Code 中安装远程容器扩展后,请按照以下步骤操作: 1. 在命令面板 (F3) 中,运行“Remote-Containers: Clone Repository in Container Volume”。 2. 输入存储库的 URL(或克隆我的存储库github.com/xpomul/theia-boilerplate.git)并按照导航选择您的 GitHub 存储库 3. 现在等待。将构建容器,将存储库克隆到一个卷中,甚至自动执行yarnand命令(如 中所指定)。yarn theia builddevcontainer.json 4. 一切完成后,您可以关闭终端,打开一个新终端,然后运行yarn theia start --plugins=local-dir:plugins,您的Eclipse Theia实例将出现在 http://localhost:3000 同样,从这里,您可以按照通常的 Theia 教程并开始试验。 Eclipse Theia 入门(三):Hello World 扩展与前端调试本文是通过为 Theia 设置本地基于 docker 的开发环境来帮助新手开始 Eclipse Theia 开发的系列文章的一部分。 设置好 Theia 开发环境(使用Docker 开发环境 或 VS Code 远程容器)后,下一步就是实际编写一些代码。 本文很好地描述了在yeoman代码生成器 的帮助下创建第一个扩展的过程。这个代码生成器实际上负责创建扩展代码和自定义 theia 产品。所以我们甚至不必使用 所以让我们忽略现有文件,在我们的Docker工作区中打开一个终端 并创建一个新文件夹。然后我们切换到新文件夹并调用代码生成器: $ mkdir hello-world 在随后的对话框中,我们选择Hello World扩展并接受建议的名称。 现在,首先是代码生成器,然后 在现在运行的Theia 应用程序中,请注意我们 在Edit Menu中有一个新菜单项Say Hello 。选择此项目时,右下角会显示一条消息Hello World。 让我们尝试修改消息。但首先,为了让我们的代码更改被拾取,我们需要告诉TypeScript编译器观察代码。观看意味着,对.ts 文件中代码的任何更改都会被直接拾取并编译为JavaScript。对于习惯于 Eclipse IDE 的开发人员, 这相当于激活 Project > Build Automatically。打开一个新终端, 并且——因为我们只会对hello-world扩展进行更改——更改到我们的hello-world扩展的文件夹并 注意: 如果 现在在VS Code 编辑器中找到并打开hello-world-contribution.ts 文件,并将第 19 行的消息更改为. 保存文件,您将短暂地看到控制台在接收更改并报告“发现 0 错误”时闪烁。 最后,如果 Theia 页面仍然打开,我们需要在浏览器中重新加载它。原因是产生消息的代码是从TypeScript编译成JavaScript 的,但由于它是前端代码,它实际上已经被加载并在我们的 Web 浏览器中执行。 调试执行和更改代码是一回事,但迟早,我们会想要使用调试器检查运行时发生的情况。因此,让我们在更改消息的行处创建一个断点并尝试命中它。 请注意,如上所述,这行代码是前端代码的一部分,因此我们需要将调试器附加到前端,即浏览器。因此,我们现在可以使用所有 Web 浏览器中的内置开发工具。但是浏览器不知道TypeScript和TypeScript 编译器;它只知道它加载和执行的JavaScript 。因此,我们将无法命中VS Code 中设置的断点。 相反,我们需要找出JavaScript中的哪一行与TypeScript中的行相关,并在浏览器的开发工具中在这一行设置断点。在实践中,这似乎很麻烦且容易出错。 更正: 实际上,在组装要下载到浏览器的资产时,webpack 包含了 typescript 源和源映射。这意味着在浏览器开发工具中,应该有一个可见的 webpack:// 节点,其中包含 .ts 文件;并且实际上可以在那里设置断点并使用浏览器开发工具直接在浏览器中调试Theia。因此,如果您熟悉浏览器开发工具,或者在尝试将 VS Code 附加到浏览器时遇到问题,如下所述,这是调试前端的不错选择。 幸运的是,VS Code还具有前端调试功能,能够附加到内置于 Google Chrome或Microsoft Edge浏览器中的调试服务器。设置非常简单:切换到Activity Bar中的Run and Debug视图,然后单击Create launch.json File链接。然后选择Chrome环境并调整文件以包含 { "version": "0.2.0", "configurations": [ { "type": "pwa-chrome", "request": "launch", "name": "Launch Chrome against localhost", "url": "http://localhost:3000", "webRoot": "${workspaceFolder}/hello-world/hello-world", "runtimeExecutable": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" } ] } 请注意,需要设置url属性,使其指向运行 Theia 的 localhost 端口。webRoot属性需要指向扩展的代码所在的工作空间中的实际路径。这用于将断点位置连接到浏览器的调试服务器。如果此属性设置为错误的位置,您将 在调试器运行时看到它被标记为未绑定。 另请注意, runtimeExecutable可能不是必需的,但对我来说它是必需的——否则启动浏览器可执行文件将失败而不会给出特定的错误消息。 现在,我们要做的就是确保Theia(即 这就是今天的内容。由于hello-world 扩展不包含任何后端代码,我们将把后端调试推迟到以后的文章中。 Eclipse Theia入门(四):创建后端服务并调试后端本文是通过为 Theia 设置本地基于 docker 的开发环境来帮助新手开始 Eclipse Theia 开发的系列文章的一部分。 在 上一篇文章中,我们创建了我们的第一个 Eclipse Theia 扩展,更改了一些代码,并使用调试器在前端设置和命中断点。 作为下一步,我想演示如何调试后端。所以我认为最好的方法是创建一个后端服务来提供“Hello World”消息,而不是在前端硬编码。 如Theia 文档中所述,Theia 扩展可以为前端和后端添加逻辑。为了促进两个组件之间的通信,可以使用任何协议;后端可以注册并打开自己的端点,前端可以访问它。但是,当然,Theia 已经提供了一个可以很容易使用的JSON-RPC API。 下面的所有代码都可以在我的 GitHub 存储库的hello-world 分支中找到。 让我们从指定一个可以提供我们的“Hello World”消息的服务开始。因为它需要后端和前端都知道,我们把它放在 export namespace HelloWorldConstants { export const SERVICE_PATH = '/services/hello-world'; } export const HelloWorld = Symbol("HelloWorld") export interface HelloWorld { getHelloString(): Promise<string>; } 这段代码为我们非常简单的服务定义了一个接口,为它定义了一个符号(依赖注入框架需要它),以及我们想要发布/使用服务的服务路径的常量。请注意,该服务返回Promise<string>而不是纯字符串。由于我们正在处理远程服务,因此使用 Promise 可以使代码表现得更好,因为我们可以在收到结果时异步使用结果,如下所示。 后端的服务实现 @injectable() export class HelloWorldImpl implements HelloWorld { getHelloString(): Promise<string> { return Promise.resolve("Hello from the backend!"); } } 我们需要使用@injectable()来注释类,因为我们希望稍后将它用作后端模块中的注入绑定。 现在,我们有了服务及其实现,让我们从前端的 CommandContribution中使用它(我只显示更改后的类HelloWorldCommandContribution): @injectable() export class HelloWorldCommandContribution implements CommandContribution { constructor( @inject(MessageService) private readonly messageService: MessageService, @inject(HelloWorld) private readonly helloWorldService: HelloWorld ) { } registerCommands(registry: CommandRegistry): void { registry.registerCommand(HelloWorldCommand, { execute: async () => { this.helloWorldService.getHelloString().then(helloString => this.messageService.info(helloString)) } }); } } 请注意,我们为HelloWorld服务添加了注入,在执行逻辑中,我们通过then() 函数将Promise与回调逻辑链接起来。因此,我们异步请求helloString,一旦收到(并且Promise被解析),我们就会调用messageService来显示它。 下一步是告诉前端的依赖注入框架如何在 HelloWorldCommandContribution 中提供我们想要注入和使用的HelloWorld服务。为此,我们将现有的扩展 如下: export default new ContainerModule(bind => { // add your contribution bindings here bind(CommandContribution).to(HelloWorldCommandContribution); bind(MenuContribution).to(HelloWorldMenuContribution); bind(HelloWorld).toDynamicValue(ctx => { const connection = ctx.container.get(WebSocketConnectionProvider); return connection.createProxy<HelloWorld>(HelloWorldConstants.SERVICE_PATH); }).inSingletonScope(); }); 我们在这里所做的是创建一个由WebSocketConnectionProvider支持的HelloWorld 接口的代理实现,该接口 反过来被指示通过 SERVICE_PATH 路径处理请求。代理上的每个方法调用都编码在JSON-RPC 请求中,并通过给定的SERVICE_PATH发送到后端。 在后端 export default new ContainerModule(bind => { bind(HelloWorld).to(HelloWorldImpl).inSingletonScope(); bind(ConnectionHandler).toDynamicValue(ctx => new JsonRpcConnectionHandler<HelloWorld>(HelloWorldConstants.SERVICE_PATH, (_client: any) => ctx.container.get<HelloWorld>(HelloWorld)) ).inSingletonScope() }) 首先,我们将HelloWorld服务绑定到它的实际实现HelloWorldImpl。对于我们的用例,这并不是严格要求的,但只要HelloWorldImpl想要访问通过注入提供的其他服务,这将是有意义的。 接下来,我们创建与上面的WebSocketConnectionProvider 对应的JsonRpcConnectionHandler。就像在前端一样,我们将 WebSocketConnectionProvider 绑定到 SERVICE_PATH。然后将所有传入的方法调用请求转发到 HelloWorldServiceImpl实例。(请注意,还有一个_client参数。我们在这里不使用它,但我们可以使用它来实现一个双向协议,其中客户端可以被服务器回调)。 现在剩下要做的就是在文件中注册后端的ContainerModule配置 "theiaExtensions": [ { "frontend": "lib/browser/hello-world-frontend-module", "backend": "lib/node/hello-world-backend-module" } ] 我们都准备好了。当我们先启动后端,然后在浏览器中启动前端并单击Say Hello菜单项时,将出现新消息:来自后端的 Hello! 调试后端调试比上一篇文章中描述的前端调试更容易,因为我们不需要处理浏览器调试引擎。VS Code调试器可以原生附加到后端进程,并且 yeoman代码生成器已经在 launch.json中为我们创建了启动配置。因此,我们可以 在HelloWorldImpl类的getHelloString()方法中放置一个断点, 启动 Launch Backend 配置,当我们单击前端的菜单项时,我们看到断点被命中。 除了普通断点,VS Code还支持条件断点、带有命中计数的断点和日志点。后者在试图找出何时调用代码的哪一部分时非常有用。使用日志点,您可以注入任意日志语句,该语句在调试器不暂停执行的情况下执行。 为了尝试一下,让我们 在onRequest 现在,我们可以在前端进行操作,并且可以看到对后端有问题的请求: 注意:在撰写本文时,VS Code JavaScript调试器中存在一个错误,该错误会阻止日志点与Theia一起正常工作。如此处所述,在每晚安装vscode-js-debug时已修复此错误,并有望在下一个VS Code版本中很快得到解决。 |
|