UNPKG

@czhlin/vite-plugin-vscode

Version:

Use vue/react to develop 'vscode extension webview', supporting esm/cjs

461 lines (353 loc) 15.9 kB
# @czhlin/vite-plugin-vscode [![npm](https://img.shields.io/npm/v/@czhlin/vite-plugin-vscode)](https://www.npmjs.com/package/@czhlin/vite-plugin-vscode) ![node-current (scoped)](https://img.shields.io/node/v/@czhlin/vite-plugin-vscode) ![NPM](https://img.shields.io/npm/l/@czhlin/vite-plugin-vscode) [![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue)](https://www.jsdocs.io/package/@czhlin/vite-plugin-vscode) [English](./README.md) | **中文** > 用 `vue`/`react` 来开发 [vscode extension webview](https://code.visualstudio.com/api/references/vscode-api#WebviewPanel) ,支持 `esm` 和 `cjs`。 在开发模式时,给 `vscode 扩展代码``web 页面代码`中注入 [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) 相同的代码,用来支持 `HMR`;生产构建时,将最终生成的`index.html` 代码注入到 `vscode 扩展代码` 中,减少工作量。 ## 特性 - 使用 [tsup](https://github.com/egoist/tsup) 快速构建 `扩展代码` - 配置简单,专注业务 - 支持 `esm``cjs` - 支持 ESM 扩展(vscode `v1.100.0+`) - 支持 webview `HMR` - 支持 [@types/vscode-webview](https://www.npmjs.com/package/@types/vscode-webview) 的 `acquireVsCodeApi` - 支持[多页面应用](https://cn.vitejs.dev/guide/build.html#multi-page-app) - 支持 `vue``react` 等其他 `vite` 支持的[框架](https://cn.vitejs.dev/guide/#trying-vite-online) ### ESM 扩展 NodeJS 扩展现在(`v1.100.0+`)支持使用 JavaScript 模块 (ESM) 的扩展。它只需要在扩展的 `package.json` 文件中添加 `"type": "module"` 条目即可。这样,JavaScript 代码就可以使用 `import``export` 语句,包括特殊的模块 `import('vscode')` ## 安装 ```bash # pnpm pnpm add @czhlin/vite-plugin-vscode -D # yarn yarn add @czhlin/vite-plugin-vscode -D # npm npm i @czhlin/vite-plugin-vscode -D ``` ## 使用说明 ### 推荐约定 设置 `recommended` 参数会修改一些预置配置,详细查看 [PluginOptions](#pluginoptions) 和 `recommended` 参数说明。 #### 目录结构 - 默认情况下,`recommended:true` 会根据如下目录结构作为约定 ``` |--extension // extension code | |--index.ts |--src // front-end code | |--App.vue | |--main.ts |--index.html ``` - 零配置,默认 dist 输出目录 ``` |--dist | |--extension | | |--index.js | | |--index.js.map | |--webview | | |--index.html ``` - 如果你想修改 `extension` 源码目录为 `src`,可以设置 `{ extension: { entry: 'src/index.ts' } }` ``` |--src // extension code | |--index.ts |--webview // front-end code | |--App.vue | |--main.ts |--index.html ``` ### extension 代码片段,更多配置看示例 ```ts const panel = window.createWebviewPanel('showHelloWorld', 'Hello World', ViewColumn.One, { enableScripts: true, localResourceRoots: [Uri.joinPath(extensionUri, 'dist/webview')], }); // vite 开发模式和生产模式注入不同的webview代码,减少开发工作 panel.webview.html = __getWebviewHtml__({ // vite 开发模式 serverUrl: process.env.VITE_DEV_SERVER_URL, // vite 生产模式 webview, context, inputName: 'index', injectCode: `<script>window.__FLAG1__=666;window.__FLAG2__=888;</script>`, }); ``` - `package.json` ```json { "main": "dist/extension/index.js" } ``` ### vue - `vite.config.ts` ```ts import vscode from '@czhlin/vite-plugin-vscode'; import vue from '@vitejs/plugin-vue'; import { defineConfig } from 'vite'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement: (tag: string) => tag.startsWith('vscode-'), }, }, }), vscode(), // 修改扩展源码入口路径,同时修改`index.html`入口文件路径 // vscode({ extension: { entry: 'src/index.ts' } }), ], }); ``` ### react - `vite.config.ts` ```ts import vscode from '@czhlin/vite-plugin-vscode'; import react from '@vitejs/plugin-react-swc'; import { defineConfig } from 'vite'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), vscode()], }); ``` ### **getWebviewHtml** 可查看 [vue-import](./examples/vue-import) 示例 - `vite.config.ts` ```ts import path from 'node:path'; import vscode from '@czhlin/vite-plugin-vscode'; export default defineConfig({ plugins: [vscode()], build: { rollupOptions: { // https://cn.vitejs.dev/guide/build.html#multi-page-app input: [path.resolve(__dirname, 'index.html'), path.resolve(__dirname, 'index2.html')], // 也可自定义名称 // input:{ // 'index': path.resolve(__dirname, 'index.html'), // 'index2': path.resolve(__dirname, 'index2.html'), // } }, }, }); ``` - 页面一 ```ts __getWebviewHtml__({ // vite 开发模式 serverUrl: process.env.VITE_DEV_SERVER_URL, // vite 生产模式 webview, context, }); ``` - 页面二 ```ts __getWebviewHtml__({ // vite 开发模式 serverUrl: `${process.env.VITE_DEV_SERVER_URL}/index2.html`, // vite 生产模式 webview, context, inputName: 'index2', }); ``` - 单个页面通过不同参数来实现不同功能 ```ts __getWebviewHtml__({ // vite 开发模式 serverUrl: `${process.env.VITE_DEV_SERVER_URL}?id=666`, // vite 生产模式 webview, context, injectCode: `<script>window.__id__=666;</script>`, }); ``` **getWebviewHtml** 说明 ```ts interface WebviewHtmlOptions { /** * `[vite serve]` vite开发服务器的url, 请用 `process.env.VITE_DEV_SERVER_URL` */ serverUrl?: string; /** * `[vite build]` 扩展的 Webview 实例 */ webview: Webview; /** * `[vite build]` 扩展的 ExtensionContext 实例 */ context: ExtensionContext; /** * `[vite build]` vite build.rollupOptions.input 设置的名称. 默认 `index`. */ inputName?: string; /** * `[vite build]` 向 head 元素的结束前注入代码 <head>--inject-- */ injectCode?: string; } /** * 获取webview的html */ function __getWebviewHtml__(options?: WebviewHtmlOptions): string; ``` ### 警告 使用 [@types/vscode-webview](https://www.npmjs.com/package/@types/vscode-webview) 的 `acquireVsCodeApi().getState()` 方法时,要使用 `await` 调用。由于 `acquireVsCodeApi` 是插件对该方法的模拟实现,故与原方法出现不一致性,非常抱歉。如果有其他方案,请分享,非常感谢。 ```ts const value = await acquireVsCodeApi().getState(); ``` ## 文档 - [unpkg.com](https://www.unpkg.com/) 提供的 [index.d.ts](https://www.unpkg.com/browse/@czhlin/vite-plugin-vscode/dist/index.d.ts). ## 参数 ### PluginOptions | 参数名 | 类型 | 默认值 | 说明 | | ----------- | -------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | recommended | `boolean` | `true` | 这个选项是为了提供推荐的默认参数和行为 | | extension | [ExtensionOptions](#ExtensionOptions) | | vscode extension 可选配置 | | webview | `boolean` \| `string` \| [WebviewOption](#WebviewOption) | `__getWebviewHtml__` | 注入 html 代码 | | devtools | `boolean` | `true` | 注入 script 代码用于 [react-devtools](https://github.com/facebook/react/tree/main/packages/react-devtools) 或 [vue-devtools](https://devtools.vuejs.org/guide/standalone) 调试 | #### Notice `recommended` 选项用于设置默认配置和行为,几乎可以达到零配置使用,默认为 `true` 。如果你要自定义配置,请设置它为`false`。以下默认的前提条件是使用推荐的 [项目结构](#目录结构)。 - 输出目录根据 `vite``build.outDir` 参数, 将 `extension``src` 分别输出到 `dist/extension``dist/webview` - 其他待实现的行为 #### Webview 在 vscode 扩展代码和 web 客户端代码中注入 [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview),使 `webview` 在开发阶段能够支持 `HMR`。 - vite serve - extension: 在调用 `__getWebviewHtml__` 方法的文件顶部注入 `import __getWebviewHtml__ from '@tomjs/vscode-extension-webview';` - web: 在 index.html 中添加 `<script>` 标签,注入 `@tomjs/vscode-extension-webview/client` 代码 - vite build - extension: 在调用 `__getWebviewHtml__` 方法的文件顶部注入 `import __getWebviewHtml__ from '@czhlin/vite-plugin-vscode-inject';` 如果为字符串,则设置注入方法名,默认为 `__getWebviewHtml__`。 #### devtools 开发阶段,支持 `react``vue` 的独立开发工具应用,默认开启。 - `react`: 注入 `<script src="http://localhost:8097"></script>`,支持 [react-devtools](https://github.com/facebook/react/tree/main/packages/react-devtools) - `vue`: 注入 `<script src="http://localhost:8098"></script>`,支持 [vue-devtools](https://devtools.vuejs.org/guide/standalone) ### ExtensionOptions 继承自 [tsup](https://tsup.egoist.dev/) 的 [Options](https://paka.dev/npm/tsup),添加了一些默认值,方便使用。 | 参数名 | 类型 | 默认值 | 说明 | | --------- | ------------------------------------------------------------------- | --------------------- | ------------------------ | | entry | `string` | `extension/index.ts` | 入口文件 | | outDir | `string` | `dist-extension/main` | 输出文件夹 | | onSuccess | `() => Promise<void \| undefined \| (() => void \| Promise<void>)>` | `undefined` | 构建成功后运行的回调函数 | ### WebviewOption | 参数名 | 类型 | 默认值 | 说明 | | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | name | `string` | `__getWebviewHtml__` | 注入的方法名 | | csp | `string` | `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src {{cspSource}} 'unsafe-inline'; script-src 'nonce-{{nonce}}' 'unsafe-eval';">` | webview 的 `CSP` | - `{{cspSource}}`: [webview.cspSource](https://code.visualstudio.com/api/references/vscode-api#Webview) - `{{nonce}}`: uuid ### 补充说明 - `extension` 未配置相关参数时的默认值 | 参数 | 开发模式默认值 | 生产模式默认值 | | --------- | -------------- | -------------- | | sourcemap | `true` | `false` | | minify | `false` | `true` | ## 环境变量 `vscode extension` 使用 - `development` 模式 | 变量 | 描述 | | --------------------- | ------------------- | | `VITE_DEV_SERVER_URL` | vite开发服务器的url | - `production` 模式 | 变量 | 描述 | | ------------------- | ------------------------- | | `VITE_WEBVIEW_DIST` | vite webview 页面输出路径 | ## Debug ### 扩展调试 通过 `vscode` 运行 `Debug Extension` 调试,调试工具参考 [官方文档](https://code.visualstudio.com/docs/editor/debugging) `launch.json` 配置如下: ```json { "version": "0.2.0", "configurations": [ { "name": "Debug Extension", "type": "extensionHost", "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "outFiles": ["${workspaceFolder}/dist/extension/*.js"], "preLaunchTask": "npm: dev" }, { "name": "Preview Extension", "type": "extensionHost", "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "outFiles": ["${workspaceFolder}/dist/extension/*.js"], "preLaunchTask": "npm: build" } ] } ``` `tasks.json` 配置如下: ```json { "version": "2.0.0", "tasks": [ { "type": "npm", "script": "dev", "problemMatcher": { "owner": "typescript", "fileLocation": "relative", "pattern": { "regexp": "^([a-zA-Z]\\:/?([\\w\\-]/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", "file": 1, "line": 3, "column": 4, "code": 5, "message": 6 }, "background": { "activeOnStart": true, "beginsPattern": "^.*extension build start*$", "endsPattern": "^.*extension (build|rebuild) success.*$" } }, "isBackground": true, "presentation": { "reveal": "never" }, "group": { "kind": "build", "isDefault": true } }, { "type": "npm", "script": "build", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [] } ] } ``` ### 网页调试 可以使用 [react-devtools](https://github.com/facebook/react/tree/main/packages/react-devtools) 和 [vue-devtools](https://devtools.vuejs.org/guide/standalone) 的独立应用调试 `webview` ## 示例 先执行以下命令安装依赖,并生成库文件: ```bash pnpm install pnpm build ``` 打开 [examples](./examples) 目录,有 `vue``react` 示例。 - [react](./examples/react):简单的 react 示例。 - [vue](./examples/vue):简单的 vue 示例。 - [vue-esm](./examples/vue-esm):简单的 vue(ESM 扩展)示例。 - [vue-import](./examples/vue-import):动态 import() 和多页面示例。 ## 关联 - [@tomjs/vscode](https://npmjs.com/package/@tomjs/vscode): 一些实用工具,用于简化 [vscode 扩展](https://marketplace.visualstudio.com/VSCode) 的开发。 - [@tomjs/vscode-dev](https://npmjs.com/package/@tomjs/vscode-dev): 一些开发工具,用于简化 [vscode 扩展](https://marketplace.visualstudio.com/VSCode) 的开发。 - [@tomjs/vscode-webview](https://npmjs.com/package/@tomjs/vscode-webview): 优化 `webview` 页面与 [vscode 扩展](https://marketplace.visualstudio.com/VSCode) 的 `postMessage` 问题 ## 重要说明 ### v4.0.0 **破坏性更新:** - 开发和生产的 `__getWebviewHtml__` 方法合并为同一个,参考 [getWebviewHtml](#getwebviewhtml) ### v3.0.0 **破坏性更新:** - 模拟的 `acquireVsCodeApi` 与 [@types/vscode-webview](https://www.npmjs.com/package/@types/vscode-webview) 的 `acquireVsCodeApi` 保持一致,改用 `sessionStorage.getItem` 和 `sessionStorage.setItem` 来实现 `getState` 和 `setState`。