@tomjs/vite-plugin-vscode 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,17 +4,18 @@
4
4
 
5
5
  **English** | [中文](./README.zh_CN.md)
6
6
 
7
- > The [vite](https://cn.vitejs.dev/) plugin for [vscode extension](https://code.visualstudio.com/api), supports `esm` and `cjs`.
7
+ > Use `vue`/`react` to develop [vscode extension webview](https://code.visualstudio.com/api/references/vscode-api#WebviewPanel), supporting `esm` and `cjs`.
8
8
 
9
- Inject [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) into vscode extension code and web client code, so that webview can support HMR during the development stage.
9
+ In development mode, inject the code of [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) into `vscode extension code` and `web page code`, use To support `HMR`; during production build, the final generated `index.html` code is injected into `vscode extension code` to reduce the workload.
10
10
 
11
11
  ## Features
12
12
 
13
- - Fast build `extension` with [tsup](https://github.com/egoist/tsup)
14
- - Little configuration, focus on business
13
+ - Use [tsup](https://github.com/egoist/tsup) to quickly build `extension code`
14
+ - Simple configuration, focus on business
15
15
  - Support `esm` and `cjs`
16
16
  - Support webview `HMR`
17
- - Support `vue` and `react` and other [frameworks](https://vitejs.dev/guide/#trying-vite-online) supported by `vite`
17
+ - Support [Multi-Page App](https://vitejs.dev/guide/build.html#multi-page-app)
18
+ - Supports `vue` and `react` and other [frameworks](https://cn.vitejs.dev/guide/#trying-vite-online) supported by `vite`
18
19
 
19
20
  ## Install
20
21
 
@@ -75,39 +76,11 @@ const panel = window.createWebviewPanel('showHelloWorld', 'Hello World', ViewCol
75
76
  enableScripts: true,
76
77
  localResourceRoots: [Uri.joinPath(extensionUri, 'dist/webview')],
77
78
  });
78
- ```
79
-
80
- ```ts
81
- private _getWebviewContent(webview: Webview, extensionUri: Uri) {
82
- // The CSS file from the Vue build output
83
- const stylesUri = getUri(webview, extensionUri, ['dist', 'webview', 'assets', 'index.css']);
84
- // The JS file from the Vue build output
85
- const scriptUri = getUri(webview, extensionUri, ['dist', 'webview', 'assets', 'index.js']);
86
-
87
- const nonce = uuid();
88
-
89
- if (process.env.VITE_DEV_SERVER_URL) {
90
- return __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL);
91
- }
92
79
 
93
- // Tip: Install the es6-string-html VS Code extension to enable code highlighting below
94
- return /*html*/ `
95
- <!doctype html>
96
- <html lang="en">
97
- <head>
98
- <meta charset="UTF-8" />
99
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
100
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
101
- <script type="module" crossorigin nonce="${nonce}" src="${scriptUri}"></script>
102
- <link rel="stylesheet" crossorigin href="${stylesUri}">
103
- <title>Hello World</title>
104
- </head>
105
- <body>
106
- <div id="app"></div>
107
- </body>
108
- </html>
109
- `;
110
- }
80
+ // Vite development mode and production mode inject different webview codes to reduce development work
81
+ panel.webview.html = process.env.VITE_DEV_SERVER_URL
82
+ ? __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL)
83
+ : __getWebviewHtml__(webview, context);
111
84
  ```
112
85
 
113
86
  - `package.json`
@@ -157,6 +130,70 @@ export default defineConfig({
157
130
  });
158
131
  ```
159
132
 
133
+ ### Multi-page application
134
+
135
+ See [vue-import](./examples/vue-import) example
136
+
137
+ - `vite.config.ts`
138
+
139
+ ```ts
140
+ import path from 'node:path';
141
+ import vscode from '@tomjs/vite-plugin-vscode';
142
+
143
+ export default defineConfig({
144
+ build: {
145
+ plugins: [vscode()]
146
+ rollupOptions: {
147
+ // https://cn.vitejs.dev/guide/build.html#multi-page-app
148
+ input: [path.resolve(__dirname, 'index.html'), path.resolve(__dirname, 'index2.html')],
149
+ // You can also customize the name
150
+ // input:{
151
+ // 'index': path.resolve(__dirname, 'index.html'),
152
+ // 'index2': path.resolve(__dirname, 'index2.html'),
153
+ // }
154
+ },
155
+ },
156
+ });
157
+ ```
158
+
159
+ - page one
160
+
161
+ ```ts
162
+ process.env.VITE_DEV_SERVER_URL
163
+ ? __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL)
164
+ : __getWebviewHtml__(webview, context);
165
+ ```
166
+
167
+ - page two
168
+
169
+ ```ts
170
+ process.env.VITE_DEV_SERVER_URL
171
+ ? __getWebviewHtml__(`${process.env.VITE_DEV_SERVER_URL}/index2.html`)
172
+ : __getWebviewHtml__(webview, context, 'index2');
173
+ ```
174
+
175
+ **getWebviewHtml** Description
176
+
177
+ ```ts
178
+ /**
179
+ * `[vite serve]` Gets the html of webview in development mode.
180
+ * @param options serverUrl: The url of the vite dev server.
181
+ */
182
+ function __getWebviewHtml__(options?: string | { serverUrl: string }): string;
183
+
184
+ /**
185
+ * `[vite serve]` Gets the html of webview in production mode.
186
+ * @param webview The WebviewPanel instance of the extension.
187
+ * @param context The ExtensionContext instance of the extension.
188
+ * @param inputName vite build.rollupOptions.input name. Default is `index`.
189
+ */
190
+ function __getWebviewHtml__(
191
+ webview: Webview,
192
+ context: ExtensionContext,
193
+ inputName?: string,
194
+ ): string;
195
+ ```
196
+
160
197
  ## Documentation
161
198
 
162
199
  - [API Documentation](https://paka.dev/npm/@tomjs/vite-plugin-vscode) provided by [paka.dev](https://paka.dev).
@@ -206,15 +243,15 @@ Based on [Options](https://paka.dev/npm/tsup) of [tsup](https://tsup.egoist.dev/
206
243
 
207
244
  - `development` mode
208
245
 
209
- | Variable | Description |
210
- | --------------------- | ------------------------------- |
211
- | `VITE_DEV_SERVER_URL` | The url of the vite dev server. |
246
+ | Variable | Description |
247
+ | --------------------- | ------------------------------ |
248
+ | `VITE_DEV_SERVER_URL` | The url of the vite dev server |
212
249
 
213
250
  - `production` mode
214
251
 
215
- | Variable | Description |
216
- | --- | --- |
217
- | `VITE_DIST_FILES` | All js files in the dist directory, excluding index.js. It's to be a json string. |
252
+ | Variable | Description |
253
+ | ------------------- | ----------------------------- |
254
+ | `VITE_WEBVIEW_DIST` | vite webview page output path |
218
255
 
219
256
  ## Debug
220
257
 
@@ -232,7 +269,15 @@ Run `Debug Extension` through `vscode` to debug. For debugging tools, refer to [
232
269
  "request": "launch",
233
270
  "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
234
271
  "outFiles": ["${workspaceFolder}/dist/extension/*.js"],
235
- "preLaunchTask": "${defaultBuildTask}"
272
+ "preLaunchTask": "npm: dev"
273
+ },
274
+ {
275
+ "name": "Preview Extension",
276
+ "type": "extensionHost",
277
+ "request": "launch",
278
+ "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
279
+ "outFiles": ["${workspaceFolder}/dist/extension/*.js"],
280
+ "preLaunchTask": "npm: build"
236
281
  }
237
282
  ]
238
283
  }
@@ -272,6 +317,15 @@ Run `Debug Extension` through `vscode` to debug. For debugging tools, refer to [
272
317
  "kind": "build",
273
318
  "isDefault": true
274
319
  }
320
+ },
321
+ {
322
+ "type": "npm",
323
+ "script": "build",
324
+ "group": {
325
+ "kind": "build",
326
+ "isDefault": true
327
+ },
328
+ "problemMatcher": []
275
329
  }
276
330
  ]
277
331
  }
@@ -288,6 +342,6 @@ pnpm build
288
342
 
289
343
  Open the [examples](./examples) directory, there are `vue` and `react` examples.
290
344
 
291
- - [react](./examples/react): simple react example.
292
- - [vue](./examples/vue): simple vue example.
293
- - [vue-import](./examples/vue-import): dynamic import() example.
345
+ - [react](./examples/react): Simple react example.
346
+ - [vue](./examples/vue): Simple vue example.
347
+ - [vue-import](./examples/vue-import): Dynamic import() and multi-page examples.
package/README.zh_CN.md CHANGED
@@ -4,17 +4,18 @@
4
4
 
5
5
  [English](./README.md) | **中文**
6
6
 
7
- > [vscode extension](https://code.visualstudio.com/api) 的 [vite](https://cn.vitejs.dev/) 插件,支持 `esm` 和 `cjs`。
7
+ > 用 `vue`/`react` 来开发 [vscode extension webview](https://code.visualstudio.com/api/references/vscode-api#WebviewPanel) ,支持 `esm` 和 `cjs`。
8
8
 
9
- vscode 扩展代码和 web 客户端代码中注入 [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview),可以让 webview 在开发阶段支持 `HMR`
9
+ 在开发模式时,给 `vscode 扩展代码` 和 `web 页面代码`中注入 [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) 的代码,用来支持 `HMR`;生产构建时,将最终生成的`index.html` 代码注入到 `vscode 扩展代码` 中,减少工作量。
10
10
 
11
11
  ## 特性
12
12
 
13
- - 使用 [tsup](https://github.com/egoist/tsup) 快速构建 `main` 和 `preload`
13
+ - 使用 [tsup](https://github.com/egoist/tsup) 快速构建 `扩展代码`
14
14
  - 配置简单,专注业务
15
15
  - 支持 `esm`和 `cjs`
16
16
  - 支持 webview `HMR`
17
- - 支持 `vue` 和 `react` 等其他 `vite` 支持的[框架](https://cn.vitejs.dev/guide/#trying-vite-online)
17
+ - 支持[多页面应用](https://cn.vitejs.dev/guide/build.html#multi-page-app)
18
+ - 支持 `vue` 、`react` 等其他 `vite` 支持的[框架](https://cn.vitejs.dev/guide/#trying-vite-online)
18
19
 
19
20
  ## 安装
20
21
 
@@ -75,39 +76,15 @@ const panel = window.createWebviewPanel('showHelloWorld', 'Hello World', ViewCol
75
76
  enableScripts: true,
76
77
  localResourceRoots: [Uri.joinPath(extensionUri, 'dist/webview')],
77
78
  });
78
- ```
79
-
80
- ```ts
81
- private _getWebviewContent(webview: Webview, extensionUri: Uri) {
82
- // The CSS file from the Vue build output
83
- const stylesUri = getUri(webview, extensionUri, ['dist/webview/assets/index.css']);
84
- // The JS file from the Vue build output
85
- const scriptUri = getUri(webview, extensionUri, ['dist/webview/assets/index.js']);
86
79
 
87
- const nonce = uuid();
88
-
89
- if (process.env.VITE_DEV_SERVER_URL) {
90
- return __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL);
91
- }
80
+ // vite 开发模式和生产模式注入不同的webview代码,减少开发工作
81
+ function getHtml(webview: Webview, context: ExtensionContext) {
82
+ process.env.VITE_DEV_SERVER_URL
83
+ ? __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL)
84
+ : __getWebviewHtml__(webview, context);
85
+ }
92
86
 
93
- // Tip: Install the es6-string-html VS Code extension to enable code highlighting below
94
- return /*html*/ `
95
- <!doctype html>
96
- <html lang="en">
97
- <head>
98
- <meta charset="UTF-8" />
99
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
100
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
101
- <script type="module" crossorigin nonce="${nonce}" src="${scriptUri}"></script>
102
- <link rel="stylesheet" crossorigin href="${stylesUri}">
103
- <title>Hello World</title>
104
- </head>
105
- <body>
106
- <div id="app"></div>
107
- </body>
108
- </html>
109
- `;
110
- }
87
+ panel.webview.html = getHtml(webview, context);
111
88
  ```
112
89
 
113
90
  - `package.json`
@@ -157,6 +134,70 @@ export default defineConfig({
157
134
  });
158
135
  ```
159
136
 
137
+ ### **getWebviewHtml**
138
+
139
+ 可查看 [vue-import](./examples/vue-import) 示例
140
+
141
+ - `vite.config.ts`
142
+
143
+ ```ts
144
+ import path from 'node:path';
145
+ import vscode from '@tomjs/vite-plugin-vscode';
146
+
147
+ export default defineConfig({
148
+ build: {
149
+ plugins: [vscode()]
150
+ rollupOptions: {
151
+ // https://cn.vitejs.dev/guide/build.html#multi-page-app
152
+ input: [path.resolve(__dirname, 'index.html'), path.resolve(__dirname, 'index2.html')],
153
+ // 也可自定义名称
154
+ // input:{
155
+ // 'index': path.resolve(__dirname, 'index.html'),
156
+ // 'index2': path.resolve(__dirname, 'index2.html'),
157
+ // }
158
+ },
159
+ },
160
+ });
161
+ ```
162
+
163
+ - 页面一
164
+
165
+ ```ts
166
+ process.env.VITE_DEV_SERVER_URL
167
+ ? __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL)
168
+ : __getWebviewHtml__(webview, context);
169
+ ```
170
+
171
+ - 页面二
172
+
173
+ ```ts
174
+ process.env.VITE_DEV_SERVER_URL
175
+ ? __getWebviewHtml__(`${process.env.VITE_DEV_SERVER_URL}/index2.html`)
176
+ : __getWebviewHtml__(webview, context, 'index2');
177
+ ```
178
+
179
+ **getWebviewHtml** 说明
180
+
181
+ ```ts
182
+ /**
183
+ * `[vite serve]` 在开发模式获取webview的html
184
+ * @param options serverUrl: vite开发服务器的url
185
+ */
186
+ function __getWebviewHtml__(options?: string | { serverUrl: string }): string;
187
+
188
+ /**
189
+ * `[vite serve]` 在生产模式获取webview的html
190
+ * @param webview 扩展的 Webview 实例
191
+ * @param context 扩展的 ExtensionContext 实例
192
+ * @param inputName vite build.rollupOptions.input 设置的名称. 默认 `index`.
193
+ */
194
+ function __getWebviewHtml__(
195
+ webview: Webview,
196
+ context: ExtensionContext,
197
+ inputName?: string,
198
+ ): string;
199
+ ```
200
+
160
201
  ## 文档
161
202
 
162
203
  - [paka.dev](https://paka.dev) 提供的 [API文档](https://paka.dev/npm/@tomjs/vite-plugin-vscode).
@@ -205,15 +246,15 @@ export default defineConfig({
205
246
 
206
247
  - `development` 模式
207
248
 
208
- | 变量 | 描述 |
209
- | --------------------- | --------------------- |
210
- | `VITE_DEV_SERVER_URL` | vite开发服务器的url |
249
+ | 变量 | 描述 |
250
+ | --------------------- | ------------------- |
251
+ | `VITE_DEV_SERVER_URL` | vite开发服务器的url |
211
252
 
212
253
  - `production` 模式
213
254
 
214
- | 变量 | 描述 |
215
- | ----------------- | --------------------------------------------------------------- |
216
- | `VITE_DIST_FILES` | dist目录下的所有js文件,不包括index.js。 它是一个 json 字符串。 |
255
+ | 变量 | 描述 |
256
+ | ------------------- | ------------------------- |
257
+ | `VITE_WEBVIEW_DIST` | vite webview 页面输出路径 |
217
258
 
218
259
  ## Debug
219
260
 
@@ -231,7 +272,15 @@ export default defineConfig({
231
272
  "request": "launch",
232
273
  "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
233
274
  "outFiles": ["${workspaceFolder}/dist/extension/*.js"],
234
- "preLaunchTask": "${defaultBuildTask}"
275
+ "preLaunchTask": "npm: dev"
276
+ },
277
+ {
278
+ "name": "Preview Extension",
279
+ "type": "extensionHost",
280
+ "request": "launch",
281
+ "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
282
+ "outFiles": ["${workspaceFolder}/dist/extension/*.js"],
283
+ "preLaunchTask": "npm: build"
235
284
  }
236
285
  ]
237
286
  }
@@ -271,6 +320,15 @@ export default defineConfig({
271
320
  "kind": "build",
272
321
  "isDefault": true
273
322
  }
323
+ },
324
+ {
325
+ "type": "npm",
326
+ "script": "build",
327
+ "group": {
328
+ "kind": "build",
329
+ "isDefault": true
330
+ },
331
+ "problemMatcher": []
274
332
  }
275
333
  ]
276
334
  }
@@ -289,4 +347,4 @@ pnpm build
289
347
 
290
348
  - [react](./examples/react):简单的 react 示例。
291
349
  - [vue](./examples/vue):简单的 vue 示例。
292
- - [vue-import](./examples/vue-import):动态 import() 示例。
350
+ - [vue-import](./examples/vue-import):动态 import() 和多页面示例。
package/dist/index.d.mts CHANGED
@@ -4,7 +4,7 @@ import { Options } from 'tsup';
4
4
  /**
5
5
  * vscode extension options. See [tsup](https://tsup.egoist.dev/) and [API Doc](https://paka.dev/npm/tsup) for more information.
6
6
  */
7
- interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' | 'watch' | 'onSuccess'> {
7
+ interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' | 'watch' | 'onSuccess' | 'skipNodeModulesBundle'> {
8
8
  /**
9
9
  * The extension entry file.
10
10
  * @default "extension/index.ts"
@@ -19,6 +19,10 @@ interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' |
19
19
  * The bundle format. Currently only supports cjs.
20
20
  */
21
21
  format?: 'cjs';
22
+ /**
23
+ * Skip dependencies and peerDependencies bundle. Default is false.
24
+ */
25
+ skipNodeModulesBundle?: boolean;
22
26
  /**
23
27
  * A function that will be executed after the build succeeds.
24
28
  */
@@ -39,19 +43,23 @@ interface PluginOptions {
39
43
  /**
40
44
  * Inject [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) into vscode extension code and web client code, so that webview can support HMR during the development stage.
41
45
  *
42
- * * extension: Inject `import getDevWebviewHtml from '@tomjs/vscode-extension-webview';` above the file that calls the `getDevWebviewHtml` method
43
- * * web: Add `<script>` tag to index.html and inject `@tomjs/vscode-extension-webview/client` code
46
+ * * vite serve
47
+ * * extension: Inject `import __getWebviewHtml__ from '@tomjs/vscode-extension-webview';` above the file that calls the `__getWebviewHtml__` method
48
+ * * web: Add `<script>` tag to index.html and inject `@tomjs/vscode-extension-webview/client` code
49
+ * * vite build
50
+ * * extension: Inject `import __getWebviewHtml__ from '@tomjs/vite-plugin-vscode-inject';` above the file that calls the `__getWebviewHtml__` method
44
51
  *
45
- * If is string, will set inject method name. Default is 'getDevWebviewHtml'.
52
+ * If is string, will set inject method name. Default is '__getWebviewHtml__'.
46
53
  *
47
54
  * @example
48
55
  * extension file
49
56
  * ```ts
50
- * if(process.env.VITE_DEV_SERVER_URL){
51
- * webview.html = getDevWebviewHtml(process.env.VITE_DEV_SERVER_URL)
52
- * } else {
53
- * webview.html = `<html></html>`
54
- * }
57
+ *function setupHtml(webview: Webview, context: ExtensionContext) {
58
+ * if (process.env.VITE_DEV_SERVER_URL) {
59
+ * return __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL);
60
+ * }
61
+ * return __getWebviewHtml__(webview, context);
62
+ *}
55
63
  * ```
56
64
  * webview client
57
65
  * ```html
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { Options } from 'tsup';
4
4
  /**
5
5
  * vscode extension options. See [tsup](https://tsup.egoist.dev/) and [API Doc](https://paka.dev/npm/tsup) for more information.
6
6
  */
7
- interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' | 'watch' | 'onSuccess'> {
7
+ interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' | 'watch' | 'onSuccess' | 'skipNodeModulesBundle'> {
8
8
  /**
9
9
  * The extension entry file.
10
10
  * @default "extension/index.ts"
@@ -19,6 +19,10 @@ interface ExtensionOptions extends Omit<Options, 'entry' | 'format' | 'outDir' |
19
19
  * The bundle format. Currently only supports cjs.
20
20
  */
21
21
  format?: 'cjs';
22
+ /**
23
+ * Skip dependencies and peerDependencies bundle. Default is false.
24
+ */
25
+ skipNodeModulesBundle?: boolean;
22
26
  /**
23
27
  * A function that will be executed after the build succeeds.
24
28
  */
@@ -39,19 +43,23 @@ interface PluginOptions {
39
43
  /**
40
44
  * Inject [@tomjs/vscode-extension-webview](https://github.com/tomjs/vscode-extension-webview) into vscode extension code and web client code, so that webview can support HMR during the development stage.
41
45
  *
42
- * * extension: Inject `import getDevWebviewHtml from '@tomjs/vscode-extension-webview';` above the file that calls the `getDevWebviewHtml` method
43
- * * web: Add `<script>` tag to index.html and inject `@tomjs/vscode-extension-webview/client` code
46
+ * * vite serve
47
+ * * extension: Inject `import __getWebviewHtml__ from '@tomjs/vscode-extension-webview';` above the file that calls the `__getWebviewHtml__` method
48
+ * * web: Add `<script>` tag to index.html and inject `@tomjs/vscode-extension-webview/client` code
49
+ * * vite build
50
+ * * extension: Inject `import __getWebviewHtml__ from '@tomjs/vite-plugin-vscode-inject';` above the file that calls the `__getWebviewHtml__` method
44
51
  *
45
- * If is string, will set inject method name. Default is 'getDevWebviewHtml'.
52
+ * If is string, will set inject method name. Default is '__getWebviewHtml__'.
46
53
  *
47
54
  * @example
48
55
  * extension file
49
56
  * ```ts
50
- * if(process.env.VITE_DEV_SERVER_URL){
51
- * webview.html = getDevWebviewHtml(process.env.VITE_DEV_SERVER_URL)
52
- * } else {
53
- * webview.html = `<html></html>`
54
- * }
57
+ *function setupHtml(webview: Webview, context: ExtensionContext) {
58
+ * if (process.env.VITE_DEV_SERVER_URL) {
59
+ * return __getWebviewHtml__(process.env.VITE_DEV_SERVER_URL);
60
+ * }
61
+ * return __getWebviewHtml__(webview, context);
62
+ *}
55
63
  * ```
56
64
  * webview client
57
65
  * ```html
package/dist/index.js CHANGED
@@ -4,10 +4,12 @@ var _path = require('path'); var _path2 = _interopRequireDefault(_path);
4
4
  var _process = require('process');
5
5
  var _lodashclonedeep = require('lodash.clonedeep'); var _lodashclonedeep2 = _interopRequireDefault(_lodashclonedeep);
6
6
  var _lodashmerge = require('lodash.merge'); var _lodashmerge2 = _interopRequireDefault(_lodashmerge);
7
+ var _nodehtmlparser = require('node-html-parser');
7
8
  var _tsup = require('tsup');
8
9
 
9
10
  // src/constants.ts
10
11
  var PLUGIN_NAME = "@tomjs:vscode";
12
+ var PACKAGE_NAME = "@tomjs/vite-plugin-vscode";
11
13
  var WEBVIEW_PACKAGE_NAME = "@tomjs/vscode-extension-webview";
12
14
  var WEBVIEW_METHOD_NAME = "__getWebviewHtml__";
13
15
 
@@ -61,6 +63,12 @@ function readJson(path2) {
61
63
  return JSON.parse(_fs2.default.readFileSync(path2, "utf8"));
62
64
  }
63
65
  }
66
+ function emptyPath(dest) {
67
+ if (_fs2.default.existsSync(dest)) {
68
+ _fs2.default.rmSync(dest, { recursive: true });
69
+ }
70
+ _fs2.default.mkdirSync(dest, { recursive: true });
71
+ }
64
72
  function resolveHostname(hostname) {
65
73
  const loopbackHosts = /* @__PURE__ */ new Set([
66
74
  "localhost",
@@ -101,7 +109,7 @@ function getPkg() {
101
109
  return pkg;
102
110
  }
103
111
  function preMergeOptions(options) {
104
- getPkg();
112
+ const pkg = getPkg();
105
113
  const opts = _lodashmerge2.default.call(void 0,
106
114
  {
107
115
  webview: true,
@@ -115,11 +123,12 @@ function preMergeOptions(options) {
115
123
  shims: true,
116
124
  clean: true,
117
125
  dts: false,
118
- treeshake: !!isDev,
126
+ treeshake: isDev ? false : "smallest",
119
127
  outExtension() {
120
128
  return { js: ".js" };
121
129
  },
122
- external: ["vscode"]
130
+ external: ["vscode"],
131
+ skipNodeModulesBundle: isDev
123
132
  }
124
133
  },
125
134
  _lodashclonedeep2.default.call(void 0, options)
@@ -139,6 +148,11 @@ function preMergeOptions(options) {
139
148
  opt.external = ["vscode", WEBVIEW_PACKAGE_NAME].concat(
140
149
  _nullishCoalesce(opt.external, () => ( []))
141
150
  );
151
+ if (!opt.skipNodeModulesBundle) {
152
+ opt.noExternal = Object.keys(pkg.dependencies || {}).concat(
153
+ Object.keys(pkg.peerDependencies || {})
154
+ );
155
+ }
142
156
  opts.extension = opt;
143
157
  if (opts.webview === true) {
144
158
  opts.webview = WEBVIEW_METHOD_NAME;
@@ -146,12 +160,69 @@ function preMergeOptions(options) {
146
160
  opts.webview = _nullishCoalesce(opts.webview, () => ( WEBVIEW_METHOD_NAME));
147
161
  return opts;
148
162
  }
149
- function readAllFiles(dir) {
150
- return _fs2.default.readdirSync(dir).reduce((files, file) => {
151
- const name = _path2.default.join(dir, file);
152
- const isDir = _fs2.default.statSync(name).isDirectory();
153
- return isDir ? [...files, ...readAllFiles(name)] : [...files, name];
154
- }, []);
163
+ var prodCachePkgName = `${PACKAGE_NAME}-inject`;
164
+ function genProdWebviewCode(cache) {
165
+ const prodCacheFolder = _path2.default.join(_process.cwd.call(void 0, ), "node_modules", prodCachePkgName);
166
+ emptyPath(prodCacheFolder);
167
+ const destFile = _path2.default.join(prodCacheFolder, "index.ts");
168
+ function handleHtmlCode(html) {
169
+ const root = _nodehtmlparser.parse.call(void 0, html);
170
+ const head = root.querySelector("head");
171
+ if (!head) {
172
+ root == null ? void 0 : root.insertAdjacentHTML("beforeend", "<head></head>");
173
+ }
174
+ head.insertAdjacentHTML(
175
+ "afterbegin",
176
+ `
177
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src {{cspSource}} 'unsafe-inline'; script-src 'nonce-{{nonce}}' 'unsafe-eval';">`
178
+ );
179
+ const tags = {
180
+ script: "src",
181
+ link: "href"
182
+ };
183
+ Object.keys(tags).forEach((tag) => {
184
+ const elements = root.querySelectorAll(tag);
185
+ elements.forEach((element) => {
186
+ const attr = element.getAttribute(tags[tag]);
187
+ if (attr) {
188
+ element.setAttribute(tags[tag], `{{baseUri}}${attr}`);
189
+ }
190
+ element.setAttribute("nonce", "{{nonce}}");
191
+ });
192
+ });
193
+ return root.toString();
194
+ }
195
+ const cacheCode = (
196
+ /* js */
197
+ `const htmlCode = {
198
+ ${Object.keys(cache).map((s) => `${s}: \`${handleHtmlCode(cache[s])}\`,`).join("\n")}
199
+ };`
200
+ );
201
+ const code = (
202
+ /* js */
203
+ `import { ExtensionContext, Uri, Webview } from 'vscode';
204
+
205
+ ${cacheCode}
206
+
207
+ function uuid() {
208
+ let text = '';
209
+ const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
210
+ for (let i = 0; i < 32; i++) {
211
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
212
+ }
213
+ return text;
214
+ }
215
+
216
+ export default function getWebviewHtml(webview: Webview, context: ExtensionContext, inputName?:string){
217
+ const nonce = uuid();
218
+ const baseUri = webview.asWebviewUri(Uri.joinPath(context.extensionUri, (process.env.VITE_WEBVIEW_DIST || 'dist')));
219
+ const html = htmlCode[inputName || 'index'] || '';
220
+ return html.replaceAll('{{cspSource}}',webview.cspSource).replaceAll('{{nonce}}', nonce).replaceAll('{{baseUri}}', baseUri);
221
+ }
222
+ `
223
+ );
224
+ _fs2.default.writeFileSync(destFile, code, { encoding: "utf8" });
225
+ return destFile.replaceAll("\\", "/");
155
226
  }
156
227
  function useVSCodePlugin(options) {
157
228
  const opts = preMergeOptions(options);
@@ -202,6 +273,7 @@ function useVSCodePlugin(options) {
202
273
  }
203
274
  }
204
275
  let buildConfig;
276
+ const prodHtmlCache = {};
205
277
  return [
206
278
  {
207
279
  name: "@tomjs:vscode",
@@ -269,26 +341,63 @@ function useVSCodePlugin(options) {
269
341
  {
270
342
  name: "@tomjs:vscode",
271
343
  apply: "build",
344
+ enforce: "post",
272
345
  config(config) {
273
346
  return handleConfig(config);
274
347
  },
275
348
  configResolved(config) {
276
349
  buildConfig = config;
277
350
  },
351
+ transformIndexHtml(html, ctx) {
352
+ var _a;
353
+ if (!opts.webview) {
354
+ return html;
355
+ }
356
+ prodHtmlCache[(_a = ctx.chunk) == null ? void 0 : _a.name] = html;
357
+ return html;
358
+ },
278
359
  closeBundle() {
279
- const { outDir } = buildConfig.build;
280
- const cwd2 = process.cwd();
281
- const allFiles = readAllFiles(outDir).filter((file) => file.endsWith(".js") && !file.endsWith("index.js")).map((s) => s.replace(cwd2, "").replaceAll("\\", "/").substring(1));
360
+ let webviewPath;
361
+ if (opts.webview) {
362
+ webviewPath = genProdWebviewCode(prodHtmlCache);
363
+ }
364
+ let outDir = buildConfig.build.outDir.replace(_process.cwd.call(void 0, ), "").replaceAll("\\", "/");
365
+ if (outDir.startsWith("/")) {
366
+ outDir = outDir.substring(1);
367
+ }
368
+ const env = {
369
+ NODE_ENV: buildConfig.mode || "production",
370
+ VITE_WEBVIEW_DIST: outDir
371
+ };
372
+ logger.info("extension build start");
282
373
  const { onSuccess: _onSuccess, ...tsupOptions } = opts.extension || {};
283
374
  _tsup.build.call(void 0,
284
375
  _lodashmerge2.default.call(void 0, tsupOptions, {
285
- env: {
286
- VITE_DIST_FILES: JSON.stringify(allFiles)
287
- },
376
+ env,
377
+ silent: true,
378
+ esbuildPlugins: !opts.webview ? [] : [
379
+ {
380
+ name: "@tomjs:vscode:inject",
381
+ setup(build) {
382
+ build.onLoad({ filter: /\.ts$/ }, async (args) => {
383
+ const file = _fs2.default.readFileSync(args.path, "utf-8");
384
+ if (file.includes(`${opts.webview}(`)) {
385
+ return {
386
+ contents: `import ${opts.webview} from \`${webviewPath}\`;
387
+ ` + file,
388
+ loader: "ts"
389
+ };
390
+ }
391
+ return {};
392
+ });
393
+ }
394
+ }
395
+ ],
288
396
  async onSuccess() {
289
397
  if (typeof _onSuccess === "function") {
290
398
  await _onSuccess();
291
399
  }
400
+ logger.info("extension build success");
292
401
  }
293
402
  })
294
403
  );
package/dist/index.mjs CHANGED
@@ -4,10 +4,12 @@ import path from "path";
4
4
  import { cwd } from "process";
5
5
  import cloneDeep from "lodash.clonedeep";
6
6
  import merge from "lodash.merge";
7
+ import { parse as htmlParser } from "node-html-parser";
7
8
  import { build as tsupBuild } from "tsup";
8
9
 
9
10
  // src/constants.ts
10
11
  var PLUGIN_NAME = "@tomjs:vscode";
12
+ var PACKAGE_NAME = "@tomjs/vite-plugin-vscode";
11
13
  var WEBVIEW_PACKAGE_NAME = "@tomjs/vscode-extension-webview";
12
14
  var WEBVIEW_METHOD_NAME = "__getWebviewHtml__";
13
15
 
@@ -60,6 +62,12 @@ function readJson(path2) {
60
62
  return JSON.parse(fs.readFileSync(path2, "utf8"));
61
63
  }
62
64
  }
65
+ function emptyPath(dest) {
66
+ if (fs.existsSync(dest)) {
67
+ fs.rmSync(dest, { recursive: true });
68
+ }
69
+ fs.mkdirSync(dest, { recursive: true });
70
+ }
63
71
  function resolveHostname(hostname) {
64
72
  const loopbackHosts = /* @__PURE__ */ new Set([
65
73
  "localhost",
@@ -100,7 +108,7 @@ function getPkg() {
100
108
  return pkg;
101
109
  }
102
110
  function preMergeOptions(options) {
103
- getPkg();
111
+ const pkg = getPkg();
104
112
  const opts = merge(
105
113
  {
106
114
  webview: true,
@@ -114,11 +122,12 @@ function preMergeOptions(options) {
114
122
  shims: true,
115
123
  clean: true,
116
124
  dts: false,
117
- treeshake: !!isDev,
125
+ treeshake: isDev ? false : "smallest",
118
126
  outExtension() {
119
127
  return { js: ".js" };
120
128
  },
121
- external: ["vscode"]
129
+ external: ["vscode"],
130
+ skipNodeModulesBundle: isDev
122
131
  }
123
132
  },
124
133
  cloneDeep(options)
@@ -138,6 +147,11 @@ function preMergeOptions(options) {
138
147
  opt.external = ["vscode", WEBVIEW_PACKAGE_NAME].concat(
139
148
  opt.external ?? []
140
149
  );
150
+ if (!opt.skipNodeModulesBundle) {
151
+ opt.noExternal = Object.keys(pkg.dependencies || {}).concat(
152
+ Object.keys(pkg.peerDependencies || {})
153
+ );
154
+ }
141
155
  opts.extension = opt;
142
156
  if (opts.webview === true) {
143
157
  opts.webview = WEBVIEW_METHOD_NAME;
@@ -145,12 +159,69 @@ function preMergeOptions(options) {
145
159
  opts.webview = opts.webview ?? WEBVIEW_METHOD_NAME;
146
160
  return opts;
147
161
  }
148
- function readAllFiles(dir) {
149
- return fs2.readdirSync(dir).reduce((files, file) => {
150
- const name = path.join(dir, file);
151
- const isDir = fs2.statSync(name).isDirectory();
152
- return isDir ? [...files, ...readAllFiles(name)] : [...files, name];
153
- }, []);
162
+ var prodCachePkgName = `${PACKAGE_NAME}-inject`;
163
+ function genProdWebviewCode(cache) {
164
+ const prodCacheFolder = path.join(cwd(), "node_modules", prodCachePkgName);
165
+ emptyPath(prodCacheFolder);
166
+ const destFile = path.join(prodCacheFolder, "index.ts");
167
+ function handleHtmlCode(html) {
168
+ const root = htmlParser(html);
169
+ const head = root.querySelector("head");
170
+ if (!head) {
171
+ root == null ? void 0 : root.insertAdjacentHTML("beforeend", "<head></head>");
172
+ }
173
+ head.insertAdjacentHTML(
174
+ "afterbegin",
175
+ `
176
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src {{cspSource}} 'unsafe-inline'; script-src 'nonce-{{nonce}}' 'unsafe-eval';">`
177
+ );
178
+ const tags = {
179
+ script: "src",
180
+ link: "href"
181
+ };
182
+ Object.keys(tags).forEach((tag) => {
183
+ const elements = root.querySelectorAll(tag);
184
+ elements.forEach((element) => {
185
+ const attr = element.getAttribute(tags[tag]);
186
+ if (attr) {
187
+ element.setAttribute(tags[tag], `{{baseUri}}${attr}`);
188
+ }
189
+ element.setAttribute("nonce", "{{nonce}}");
190
+ });
191
+ });
192
+ return root.toString();
193
+ }
194
+ const cacheCode = (
195
+ /* js */
196
+ `const htmlCode = {
197
+ ${Object.keys(cache).map((s) => `${s}: \`${handleHtmlCode(cache[s])}\`,`).join("\n")}
198
+ };`
199
+ );
200
+ const code = (
201
+ /* js */
202
+ `import { ExtensionContext, Uri, Webview } from 'vscode';
203
+
204
+ ${cacheCode}
205
+
206
+ function uuid() {
207
+ let text = '';
208
+ const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
209
+ for (let i = 0; i < 32; i++) {
210
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
211
+ }
212
+ return text;
213
+ }
214
+
215
+ export default function getWebviewHtml(webview: Webview, context: ExtensionContext, inputName?:string){
216
+ const nonce = uuid();
217
+ const baseUri = webview.asWebviewUri(Uri.joinPath(context.extensionUri, (process.env.VITE_WEBVIEW_DIST || 'dist')));
218
+ const html = htmlCode[inputName || 'index'] || '';
219
+ return html.replaceAll('{{cspSource}}',webview.cspSource).replaceAll('{{nonce}}', nonce).replaceAll('{{baseUri}}', baseUri);
220
+ }
221
+ `
222
+ );
223
+ fs2.writeFileSync(destFile, code, { encoding: "utf8" });
224
+ return destFile.replaceAll("\\", "/");
154
225
  }
155
226
  function useVSCodePlugin(options) {
156
227
  const opts = preMergeOptions(options);
@@ -201,6 +272,7 @@ function useVSCodePlugin(options) {
201
272
  }
202
273
  }
203
274
  let buildConfig;
275
+ const prodHtmlCache = {};
204
276
  return [
205
277
  {
206
278
  name: "@tomjs:vscode",
@@ -268,26 +340,63 @@ function useVSCodePlugin(options) {
268
340
  {
269
341
  name: "@tomjs:vscode",
270
342
  apply: "build",
343
+ enforce: "post",
271
344
  config(config) {
272
345
  return handleConfig(config);
273
346
  },
274
347
  configResolved(config) {
275
348
  buildConfig = config;
276
349
  },
350
+ transformIndexHtml(html, ctx) {
351
+ var _a;
352
+ if (!opts.webview) {
353
+ return html;
354
+ }
355
+ prodHtmlCache[(_a = ctx.chunk) == null ? void 0 : _a.name] = html;
356
+ return html;
357
+ },
277
358
  closeBundle() {
278
- const { outDir } = buildConfig.build;
279
- const cwd2 = process.cwd();
280
- const allFiles = readAllFiles(outDir).filter((file) => file.endsWith(".js") && !file.endsWith("index.js")).map((s) => s.replace(cwd2, "").replaceAll("\\", "/").substring(1));
359
+ let webviewPath;
360
+ if (opts.webview) {
361
+ webviewPath = genProdWebviewCode(prodHtmlCache);
362
+ }
363
+ let outDir = buildConfig.build.outDir.replace(cwd(), "").replaceAll("\\", "/");
364
+ if (outDir.startsWith("/")) {
365
+ outDir = outDir.substring(1);
366
+ }
367
+ const env = {
368
+ NODE_ENV: buildConfig.mode || "production",
369
+ VITE_WEBVIEW_DIST: outDir
370
+ };
371
+ logger.info("extension build start");
281
372
  const { onSuccess: _onSuccess, ...tsupOptions } = opts.extension || {};
282
373
  tsupBuild(
283
374
  merge(tsupOptions, {
284
- env: {
285
- VITE_DIST_FILES: JSON.stringify(allFiles)
286
- },
375
+ env,
376
+ silent: true,
377
+ esbuildPlugins: !opts.webview ? [] : [
378
+ {
379
+ name: "@tomjs:vscode:inject",
380
+ setup(build) {
381
+ build.onLoad({ filter: /\.ts$/ }, async (args) => {
382
+ const file = fs2.readFileSync(args.path, "utf-8");
383
+ if (file.includes(`${opts.webview}(`)) {
384
+ return {
385
+ contents: `import ${opts.webview} from \`${webviewPath}\`;
386
+ ` + file,
387
+ loader: "ts"
388
+ };
389
+ }
390
+ return {};
391
+ });
392
+ }
393
+ }
394
+ ],
287
395
  async onSuccess() {
288
396
  if (typeof _onSuccess === "function") {
289
397
  await _onSuccess();
290
398
  }
399
+ logger.info("extension build success");
291
400
  }
292
401
  })
293
402
  );
package/env.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- export {}; // Make this a module
2
-
1
+ import { ExtensionContext, Webview } from 'vscode';
2
+ // Make this a module
3
+ export {};
3
4
  declare global {
4
5
  /**
5
6
  * fix code hint
@@ -19,13 +20,25 @@ declare global {
19
20
  /**
20
21
  * `[vite build]` All js files in the dist directory, excluding index.js. It's to be a json string.
21
22
  */
22
- VITE_DIST_FILES?: string;
23
+ VITE_WEBVIEW_DIST?: string;
23
24
  }
24
25
  }
25
26
 
26
27
  /**
27
- * `[vite serve]` Get the html of the development webview.
28
+ * `[vite serve]` Gets the html of webview in development mode.
28
29
  * @param options serverUrl: The url of the vite dev server.
29
30
  */
30
31
  function __getWebviewHtml__(options?: string | { serverUrl: string }): string;
32
+
33
+ /**
34
+ * `[vite serve]` Gets the html of webview in production mode.
35
+ * @param webview The Webview instance of the extension.
36
+ * @param context The ExtensionContext instance of the extension.
37
+ * @param inputName vite build.rollupOptions.input name. Default is `index`.
38
+ */
39
+ function __getWebviewHtml__(
40
+ webview: Webview,
41
+ context: ExtensionContext,
42
+ inputName?: string,
43
+ ): string;
31
44
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tomjs/vite-plugin-vscode",
3
- "version": "1.4.0",
4
- "description": "The vite plugin for vscode extension, supports esm and cjs.",
3
+ "version": "2.0.0",
4
+ "description": "Use vue/react to develop 'vscode extension webview', supporting esm/cjs",
5
5
  "keywords": [
6
6
  "vite",
7
7
  "plugin",
@@ -47,6 +47,7 @@
47
47
  "kolorist": "^1.8.0",
48
48
  "lodash.clonedeep": "^4.5.0",
49
49
  "lodash.merge": "^4.6.2",
50
+ "node-html-parser": "^6.1.12",
50
51
  "tsup": "7.2.0"
51
52
  },
52
53
  "devDependencies": {