@@ -106,15 +106,22 @@ Let's break down this code step by step.
106
106
107
107
### About Imports
108
108
109
+ In the Getting Started example, we used direct URL imports:
110
+
109
111
``` typescript
110
112
import type { Entrypoint } from " jsr:@denops/std@^7.0.0" ;
111
113
```
112
114
113
- The first line imports the ` Entrypoint ` type from the [ @denops/std ] standard
114
- library. You can find detailed information about the library by checking the
115
- URL:
` https://jsr.io/@denops/[email protected] ` (replace
` jsr: ` to
` https://jsr.io/ ` ).
116
- We fixed the version in the import URL, so it's recommended to check for details
117
- and update to the latest version URL.
115
+ However, with the recommended ` deno.jsonc ` configuration approach, this becomes:
116
+
117
+ ``` typescript
118
+ import type { Entrypoint } from " @denops/std" ;
119
+ ```
120
+
121
+ The import is resolved through the import map defined in ` deno.jsonc ` . The
122
+ [ @denops/std ] standard library provides essential types and utilities for Denops
123
+ plugin development. You can find detailed information about the library at
124
+ ` https://jsr.io/@denops/std ` .
118
125
119
126
Note that we use ` import type ` syntax, which is part of TypeScript's
120
127
[ Type-Only Imports and Export] ( https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html ) .
@@ -215,7 +222,7 @@ For example, use
215
222
Vim's function instead of ` denops.call ` like:
216
223
217
224
``` typescript
218
- import * as fn from " jsr: @denops/std@^7.0.0 /function" ;
225
+ import * as fn from " @denops/std/function" ;
219
226
220
227
// Bad (result1 is `unknown`)
221
228
const result1 = await denops .call (" expand" , " %" );
@@ -227,6 +234,232 @@ const result2 = await fn.expand(denops, "%");
227
234
If developers use ` function ` module instead, they can benefit from features like
228
235
auto-completion and type checking provided by LSP (Language Server Protocol).
229
236
237
+ ## Managing Dependencies with Workspace and Import Maps
238
+
239
+ > [ !IMPORTANT]
240
+ >
241
+ > The workspace configuration and import map features described below require
242
+ > Denops v8.0.0 or later. For older versions, use direct URL imports in your
243
+ > TypeScript files.
244
+
245
+ While the examples above use direct URL imports, the recommended approach for
246
+ Denops plugins is to separate development configuration from runtime
247
+ dependencies:
248
+
249
+ - ** Development configuration** : The root ` deno.jsonc ` with workspace settings
250
+ enables development tools (` deno lint ` , ` deno fmt ` , etc.) to work from the
251
+ project root
252
+ - ** Runtime dependencies** : The plugin-specific ` deno.jsonc ` contains only the
253
+ import map needed at runtime
254
+ - ** No conflicts** : Since each Denops plugin has a unique directory under
255
+ ` denops/ ` , their configuration files never conflict when installed
256
+ - ** Clean separation** : Development tools configuration stays in the repository
257
+ root, while runtime dependencies are isolated per plugin
258
+ - ** Flexible format** : Both ` deno.json ` and ` deno.jsonc ` are supported, but
259
+ ` deno.jsonc ` is recommended for its comment support
260
+
261
+ ### Separation of Development and Runtime Configuration
262
+
263
+ The workspace pattern serves two distinct purposes:
264
+
265
+ 1 . ** Development Time** : The root ` deno.jsonc ` enables development commands to
266
+ work properly:
267
+ ``` json
268
+ {
269
+ "workspace" : [
270
+ " ./denops/your-plugin-name"
271
+ ],
272
+ "lint" : {
273
+ "rules" : {
274
+ "tags" : [" recommended" ]
275
+ }
276
+ },
277
+ "fmt" : {
278
+ "indentWidth" : 2
279
+ }
280
+ }
281
+ ```
282
+
283
+ 2 . ** Runtime** : The plugin's ` deno.jsonc ` contains only what's needed for
284
+ execution:
285
+ ``` json
286
+ {
287
+ "imports" : {
288
+ "@denops/std" : " jsr:@denops/std@^8.0.0"
289
+ }
290
+ }
291
+ ```
292
+
293
+ ### Why This Separation Matters
294
+
295
+ When Vim/Neovim loads plugins, it merges all plugin directories into the runtime
296
+ path. If runtime dependencies were in the root configuration file, they would
297
+ conflict:
298
+
299
+ ```
300
+ # After installation, plugins are merged:
301
+ ~/.vim/pack/plugins/start/
302
+ ├── plugin-a/
303
+ │ ├── deno.jsonc # Development config (not used at runtime)
304
+ │ └── denops/
305
+ │ └── plugin-a/
306
+ │ └── deno.jsonc # Runtime dependencies (unique path)
307
+ └── plugin-b/
308
+ ├── deno.jsonc # Development config (not used at runtime)
309
+ └── denops/
310
+ └── plugin-b/
311
+ └── deno.jsonc # Runtime dependencies (unique path)
312
+ ```
313
+
314
+ ### Setting up Your Plugin
315
+
316
+ 1 . Create a ` deno.jsonc ` in your repository root for development:
317
+
318
+ ``` json
319
+ {
320
+ "workspace" : [
321
+ " ./denops/your-plugin-name"
322
+ ],
323
+ // Development-specific configuration
324
+ "lint" : {
325
+ "rules" : {
326
+ "tags" : [" recommended" ]
327
+ }
328
+ },
329
+ "fmt" : {
330
+ "indentWidth" : 2 ,
331
+ "lineWidth" : 80
332
+ },
333
+ "test" : {
334
+ "include" : [" denops/**/*_test.ts" ]
335
+ }
336
+ }
337
+ ```
338
+
339
+ 2 . Create a ` deno.jsonc ` inside your plugin directory for runtime dependencies:
340
+
341
+ ``` json
342
+ // denops/your-plugin-name/deno.jsonc
343
+ {
344
+ "imports" : {
345
+ "@denops/std" : " jsr:@denops/std@^8.0.0" ,
346
+ "@denops/core" : " jsr:@denops/core@^8.0.0" ,
347
+ "@core/unknownutil" : " jsr:@core/unknownutil@^4.3.0"
348
+ }
349
+ }
350
+ ```
351
+
352
+ This separation ensures:
353
+
354
+ - Development tools like ` deno fmt ` and ` deno lint ` work from your project root
355
+ - Runtime dependencies are properly resolved when the plugin is installed
356
+ - No conflicts occur between multiple installed Denops plugins
357
+
358
+ ### Alternative: Using import_map.json(c)
359
+
360
+ While the examples above use ` deno.jsonc ` , Denops also supports
361
+ ` import_map.json(c) ` files in the plugin directory:
362
+
363
+ ```
364
+ denops/your-plugin-name/
365
+ ├── import_map.jsonc # Alternative to deno.jsonc
366
+ └── main.ts
367
+ ```
368
+
369
+ However, there are important differences:
370
+
371
+ 1 . ** Import Maps Standard** (import_map.json) requires both entries for
372
+ submodules:
373
+ ``` json
374
+ {
375
+ "imports" : {
376
+ "@denops/std" : " jsr:@denops/std@^8.0.0" ,
377
+ "@denops/std/" : " jsr:@denops/std@^8.0.0/" // Required for submodules
378
+ }
379
+ }
380
+ ```
381
+
382
+ 2 . ** deno.json Format** (recommended) needs only one entry:
383
+ ``` json
384
+ {
385
+ "imports" : {
386
+ "@denops/std" : " jsr:@denops/std@^8.0.0" // Handles both cases
387
+ }
388
+ }
389
+ ```
390
+
391
+ > [ !IMPORTANT]
392
+ >
393
+ > We recommend using ` deno.jsonc ` over ` import_map.jsonc ` because:
394
+ >
395
+ > - It requires less verbose configuration
396
+ > - It's the standard Deno configuration format
397
+ > - It supports additional configuration options beyond imports
398
+ > - Tools like ` deno add ` and ` deno remove ` work seamlessly with it
399
+
400
+ With this configuration, you can update your imports from:
401
+
402
+ ``` typescript
403
+ import type { Entrypoint } from " jsr:@denops/std@^7.0.0" ;
404
+ import { assert , is } from " jsr:@core/unknownutil@^4.3.0" ;
405
+ ```
406
+
407
+ To cleaner import statements:
408
+
409
+ ``` typescript
410
+ import type { Entrypoint } from " @denops/std" ;
411
+ import { assert , is } from " @core/unknownutil" ;
412
+ ```
413
+
414
+ ### Using npm packages
415
+
416
+ Import maps also simplify npm package usage. Instead of:
417
+
418
+ ``` typescript
419
+ import { Maze } from " npm:@thewizardbear/maze_generator@^0.4.0" ;
420
+ ```
421
+
422
+ Add it to your import map:
423
+
424
+ ``` json
425
+ {
426
+ "imports" : {
427
+ "@denops/std" : " jsr:@denops/std@^8.0.0" ,
428
+ "maze_generator" : " npm:@thewizardbear/maze_generator@^0.4.0"
429
+ }
430
+ }
431
+ ```
432
+
433
+ Then import it as:
434
+
435
+ ``` typescript
436
+ import { Maze } from " maze_generator" ;
437
+ ```
438
+
439
+ ### Import Map Structure
440
+
441
+ The ` deno.json ` file in your Denops plugin directory should contain all the
442
+ dependencies your plugin needs. This keeps dependency management isolated to
443
+ each plugin while avoiding conflicts in the merged runtime directory.
444
+
445
+ For example, if your plugin uses additional npm packages:
446
+
447
+ ``` json
448
+ {
449
+ "imports" : {
450
+ "@denops/std" : " jsr:@denops/std@^8.0.0" ,
451
+ "@denops/std/" : " jsr:@denops/std@^8.0.0/" ,
452
+ "@core/unknownutil" : " jsr:@core/unknownutil@^4.3.0" ,
453
+ "your-npm-lib" : " npm:some-package@^1.0.0"
454
+ }
455
+ }
456
+ ```
457
+
458
+ > [ !NOTE]
459
+ >
460
+ > Including both ` @denops/std ` and ` @denops/std/ ` allows you to import both the
461
+ > main module and submodules (e.g., ` @denops/std/buffer ` ).
462
+
230
463
## Next Steps
231
464
232
465
In the next step, follow the tutorial to learn how to develop a minimum Denops
0 commit comments