Skip to content

Commit a38135e

Browse files
lambdalisueclaude
andcommitted
docs: Update to recommend workspace + deno.jsonc for dependency management
This commit updates the Denops documentation to reflect the new recommended approach for managing dependencies using workspace configuration and import maps. Key changes: - Explain workspace + deno.jsonc pattern as the recommended approach - Clarify separation between development config (root) and runtime deps (plugin) - Update all examples to use deno.jsonc instead of direct URL imports - Add import map support to all tutorials (Hello World and Maze) - Document support for import_map.json(c) as an alternative - Explain why plugin-specific config prevents conflicts in merged runtime paths - Add Denops v8.0.0+ requirement for import map features - Apply consistent formatting with deno fmt The documentation now properly guides users to: 1. Use root deno.jsonc for development tools (lint, fmt, test) 2. Use plugin-specific deno.jsonc for runtime dependencies 3. Understand that both deno.json and import_map.json formats are supported 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 13390fa commit a38135e

13 files changed

+434
-51
lines changed

src/getting-started/README.md

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,61 @@ Let's start by creating a simple plugin to learn how to develop Denops plugins.
88

99
## Create a Plugin
1010

11-
Create a directory named `denops-getting-started` in your home directory and a
12-
file named `main.ts` within it, under `denops/denops-getting-started/`:
11+
Create a directory named `denops-getting-started` in your home directory with
12+
the following structure:
1313

1414
```
1515
$HOME
1616
└── denops-getting-started
17+
├── deno.jsonc
1718
└── denops
1819
└── denops-getting-started
20+
├── deno.jsonc
1921
└── main.ts
2022
```
2123

24+
First, create a root `deno.jsonc` file for development configuration:
25+
26+
```json,title=deno.jsonc
27+
{
28+
"workspace": [
29+
"./denops/denops-getting-started"
30+
]
31+
}
32+
```
33+
34+
> [!TIP]
35+
>
36+
> The root `deno.jsonc` is for development purposes. It enables commands like
37+
> `deno fmt`, `deno lint`, and `deno test` to work from your project root. You
38+
> can add development-specific settings here without affecting runtime.
39+
40+
Then, create a `deno.jsonc` file inside `denops/denops-getting-started/` for
41+
runtime dependencies:
42+
43+
```json,title=denops/denops-getting-started/deno.jsonc
44+
{
45+
"imports": {
46+
"@denops/std": "jsr:@denops/std@^8.0.0"
47+
}
48+
}
49+
```
50+
51+
> [!NOTE]
52+
>
53+
> Import map support (both `deno.json` imports and `import_map.json`) requires
54+
> Denops v8.0.0 or later. Denops supports multiple configuration formats:
55+
>
56+
> - `deno.json` and `deno.jsonc` (recommended)
57+
> - `import_map.json` and `import_map.jsonc` (also supported)
58+
>
59+
> We use `deno.jsonc` in examples as it allows comments and requires less
60+
> verbose configuration than import maps.
61+
2262
Next, write the following TypeScript code in `main.ts`:
2363

2464
```typescript,title=denops/denops-getting-started/main.ts
25-
import type { Entrypoint } from "jsr:@denops/std@^7.0.0";
65+
import type { Entrypoint } from "@denops/std";
2666

2767
export const main: Entrypoint = (denops) => {
2868
denops.dispatcher = {
@@ -33,6 +73,18 @@ export const main: Entrypoint = (denops) => {
3373
};
3474
```
3575

76+
> [!IMPORTANT]
77+
>
78+
> We separate development configuration from runtime dependencies:
79+
>
80+
> - **Root `deno.jsonc`**: Contains workspace configuration for development
81+
> tools
82+
> - **Plugin `deno.jsonc`**: Contains import maps for runtime dependencies
83+
>
84+
> This separation is crucial because the root `deno.jsonc` is only used during
85+
> development, while the plugin-specific `deno.jsonc` is what Denops uses at
86+
> runtime to resolve imports.
87+
3688
## Activate the Plugin
3789

3890
Add the following line to your Vim or Neovim configuration file (e.g.,

src/getting-started/explanation.md

Lines changed: 239 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,22 @@ Let's break down this code step by step.
106106

107107
### About Imports
108108

109+
In the Getting Started example, we used direct URL imports:
110+
109111
```typescript
110112
import type { Entrypoint } from "jsr:@denops/std@^7.0.0";
111113
```
112114

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`.
118125

119126
Note that we use `import type` syntax, which is part of TypeScript's
120127
[Type-Only Imports and Export](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html).
@@ -215,7 +222,7 @@ For example, use
215222
Vim's function instead of `denops.call` like:
216223

217224
```typescript
218-
import * as fn from "jsr:@denops/std@^7.0.0/function";
225+
import * as fn from "@denops/std/function";
219226

220227
// Bad (result1 is `unknown`)
221228
const result1 = await denops.call("expand", "%");
@@ -227,6 +234,232 @@ const result2 = await fn.expand(denops, "%");
227234
If developers use `function` module instead, they can benefit from features like
228235
auto-completion and type checking provided by LSP (Language Server Protocol).
229236

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+
230463
## Next Steps
231464

232465
In the next step, follow the tutorial to learn how to develop a minimum Denops

src/introduction.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ features:
1818
- **Unified codebase for Vim and Neovim**:<br>Denops provides a unified API for
1919
both Vim and Neovim. You can write a plugin that functions on both Vim and
2020
Neovim with a single codebase.
21-
- **No worries about dependency management**:<br>Deno includes a built-in
22-
dependency management system, allowing developers to write plugins with
23-
third-party libraries without concerns about dependency management.
21+
- **Modern dependency management**:<br>Deno's built-in dependency system with
22+
import maps provides clean, maintainable dependency management. The workspace
23+
configuration ensures each plugin's dependencies are isolated, preventing
24+
conflicts when multiple Denops plugins are installed together.
2425
- **Simple and efficient code**:<br>Deno utilizes the V8 engine, significantly
2526
faster than Vim script. You can write a plugin with straightforward code,
2627
without the need for complex optimizations solely for performance.

src/tutorial/helloworld/adding-an-api.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
In the previous section, we created a minimal Denops plugin. In this section, we
44
will enhance the plugin by adding an API.
55

6-
Open `denops/denops-helloworld/main.ts` and rewrite the content with the
6+
First, update your `denops/denops-helloworld/deno.jsonc` to include the
7+
unknownutil dependency:
8+
9+
```json,title=denops/denops-helloworld/deno.jsonc
10+
{
11+
"imports": {
12+
"@denops/std": "jsr:@denops/std@^8.0.0",
13+
"@core/unknownutil": "jsr:@core/unknownutil@^4.3.0"
14+
}
15+
}
16+
```
17+
18+
Then open `denops/denops-helloworld/main.ts` and rewrite the content with the
719
following code:
820

921
```typescript,title=denops/denops-helloworld/main.ts
10-
import type { Entrypoint } from "jsr:@denops/std@^7.0.0";
11-
import { assert, is } from "jsr:@core/unknownutil@^4.3.0";
22+
import type { Entrypoint } from "@denops/std";
23+
import { assert, is } from "@core/unknownutil";
1224

1325
export const main: Entrypoint = (denops) => {
1426
denops.dispatcher = {

0 commit comments

Comments
 (0)