diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 1fb53c8..bc98e9a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -10,6 +10,7 @@ - [Creating a minimal Denops plugin](./tutorial/helloworld/creating-a-minimal-denops-plugin.md) - [Adding Denops APIs](./tutorial/helloworld/adding-an-api.md) - [Calling Vim features](./tutorial/helloworld/calling-vim-features.md) + - [Managing dependencies](./tutorial/helloworld/managing-dependencies.md) - [Tutorial (Maze)](./tutorial/maze/README.md) - [Utilizing third-party library](./tutorial/maze/utilizing-third-party-library.md) - [Outputting content to buffer](./tutorial/maze/outputting-content-to-buffer.md) diff --git a/src/getting-started/README.md b/src/getting-started/README.md index 2ec1080..5983a57 100644 --- a/src/getting-started/README.md +++ b/src/getting-started/README.md @@ -22,7 +22,7 @@ $HOME Next, write the following TypeScript code in `main.ts`: ```typescript,title=denops/denops-getting-started/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; export const main: Entrypoint = (denops) => { denops.dispatcher = { @@ -33,6 +33,12 @@ export const main: Entrypoint = (denops) => { }; ``` +> [!NOTE] +> +> This example uses direct URL imports for simplicity. The recommended approach +> for managing dependencies is to use `deno.jsonc` with import maps, which +> you'll learn about in the [tutorials](../tutorial.md). + ## Activate the Plugin Add the following line to your Vim or Neovim configuration file (e.g., diff --git a/src/getting-started/explanation.md b/src/getting-started/explanation.md index 0a35d9c..63e497f 100644 --- a/src/getting-started/explanation.md +++ b/src/getting-started/explanation.md @@ -91,7 +91,7 @@ easily call. In the Getting Started, we wrote the following code in the `main.ts` file: ```typescript -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; export const main: Entrypoint = (denops) => { denops.dispatcher = { @@ -107,7 +107,7 @@ Let's break down this code step by step. ### About Imports ```typescript -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; ``` The first line imports the `Entrypoint` type from the [@denops/std] standard @@ -215,7 +215,7 @@ For example, use Vim's function instead of `denops.call` like: ```typescript -import * as fn from "jsr:@denops/std@^7.0.0/function"; +import * as fn from "jsr:@denops/std@^8.0.0/function"; // Bad (result1 is `unknown`) const result1 = await denops.call("expand", "%"); diff --git a/src/introduction.md b/src/introduction.md index 0afa18c..58e4abf 100644 --- a/src/introduction.md +++ b/src/introduction.md @@ -18,9 +18,10 @@ features: - **Unified codebase for Vim and Neovim**:
Denops provides a unified API for both Vim and Neovim. You can write a plugin that functions on both Vim and Neovim with a single codebase. -- **No worries about dependency management**:
Deno includes a built-in - dependency management system, allowing developers to write plugins with - third-party libraries without concerns about dependency management. +- **Modern dependency management**:
Deno's built-in dependency system with + import maps provides clean, maintainable dependency management. The workspace + configuration ensures each plugin's dependencies are isolated, preventing + conflicts when multiple Denops plugins are installed together. - **Simple and efficient code**:
Deno utilizes the V8 engine, significantly faster than Vim script. You can write a plugin with straightforward code, without the need for complex optimizations solely for performance. diff --git a/src/tutorial/helloworld/adding-an-api.md b/src/tutorial/helloworld/adding-an-api.md index 5dbda21..ca543f1 100644 --- a/src/tutorial/helloworld/adding-an-api.md +++ b/src/tutorial/helloworld/adding-an-api.md @@ -7,7 +7,7 @@ Open `denops/denops-helloworld/main.ts` and rewrite the content with the following code: ```typescript,title=denops/denops-helloworld/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; import { assert, is } from "jsr:@core/unknownutil@^4.3.0"; export const main: Entrypoint = (denops) => { diff --git a/src/tutorial/helloworld/calling-vim-features.md b/src/tutorial/helloworld/calling-vim-features.md index 2e2b5fe..dd6ed1a 100644 --- a/src/tutorial/helloworld/calling-vim-features.md +++ b/src/tutorial/helloworld/calling-vim-features.md @@ -5,7 +5,7 @@ the `denops` instance passed to the plugin's `main` function. You can rewrite `main.ts` as follows to register the `DenopsHello` as a Vim command: ```typescript,title=denops/denops-helloworld/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; import { assert, is } from "jsr:@core/unknownutil@^4.3.0"; export const main: Entrypoint = (denops) => { @@ -57,8 +57,11 @@ as a result. ## Next Steps -In the next step, follow the tutorial to learn how to develop a real Denops -plugin. +Learn about managing dependencies with import maps for cleaner code: + +- [Managing dependencies](./managing-dependencies.md) + +Or jump to the maze tutorial to learn more advanced concepts: - [Tutorial (Maze)](../../tutorial/maze/index.html) - [API reference](https://jsr.io/@denops/std) diff --git a/src/tutorial/helloworld/creating-a-minimal-denops-plugin.md b/src/tutorial/helloworld/creating-a-minimal-denops-plugin.md index 5963ac7..0ec7146 100644 --- a/src/tutorial/helloworld/creating-a-minimal-denops-plugin.md +++ b/src/tutorial/helloworld/creating-a-minimal-denops-plugin.md @@ -32,7 +32,7 @@ denops-helloworld Here is the content of the `denops/denops-helloworld/main.ts` file: ```typescript,title=denops/denops-helloworld/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; export const main: Entrypoint = (denops) => { console.log("Hello, Denops from TypeScript!"); diff --git a/src/tutorial/helloworld/managing-dependencies.md b/src/tutorial/helloworld/managing-dependencies.md new file mode 100644 index 0000000..5676b8a --- /dev/null +++ b/src/tutorial/helloworld/managing-dependencies.md @@ -0,0 +1,120 @@ +# Managing Dependencies with Import Maps + +In the previous examples, we used direct URL imports like +`jsr:@denops/std@^8.0.0`. While this works, the recommended approach for Denops +plugins (v8.0.0+) is to use import maps with `deno.jsonc` for cleaner and more +maintainable dependency management. + +## Why Use Import Maps? + +The main reason to use import maps is to avoid conflicts between multiple Denops +plugins. Each Denops plugin must have a unique directory name under `denops/`, +but root-level configuration files could potentially conflict: + +``` +# Multiple plugins installed: +~/.vim/pack/plugins/start/plugin-a/ +├── deno.jsonc # Could conflict +└── denops/plugin-a/ # Always unique + +~/.vim/pack/plugins/start/plugin-b/ +├── deno.jsonc # Could conflict +└── denops/plugin-b/ # Always unique +``` + +Some plugin managers have a "merge" feature that combines plugin directories, +but even without merging, placing configuration files in plugin-specific +directories (`denops/plugin-name/`) ensures no conflicts can occur regardless of +how plugins are installed or managed. + +## Setting Up Your Plugin Structure + +Update your `denops-helloworld` structure to include configuration files: + +``` +denops-helloworld/ +├── deno.jsonc # Development configuration +├── denops/ +│ └── denops-helloworld/ +│ ├── deno.jsonc # Runtime dependencies +│ └── main.ts +└── plugin/ + └── denops-helloworld.vim +``` + +### Root deno.jsonc (Development) + +Create a `deno.jsonc` in your repository root for workspace configuration: + +```json +{ + "workspace": [ + "./denops/denops-helloworld" + ] +} +``` + +This enables Deno commands like `deno fmt`, `deno lint`, and `deno test` to work +from your project root and discover your plugin's configuration. + +### Plugin deno.jsonc (Runtime) + +Create `denops/denops-helloworld/deno.jsonc` for runtime dependencies: + +```json +{ + "imports": { + "@denops/std": "jsr:@denops/std@^8.0.0", + "@core/unknownutil": "jsr:@core/unknownutil@^4.3.0" + } +} +``` + +## Updating Your Code + +With import maps configured, update your imports from: + +```typescript +import type { Entrypoint } from "jsr:@denops/std@^8.0.0"; +import { assert, is } from "jsr:@core/unknownutil@^4.3.0"; +``` + +To cleaner versions: + +```typescript +import type { Entrypoint } from "@denops/std"; +import { assert, is } from "@core/unknownutil"; +``` + +## Alternative: import_map.json + +Denops also supports `import_map.json(c)` files, but they require more verbose +configuration due to the +[Import Maps Standard](https://github.com/WICG/import-maps): + +```json +// denops/denops-helloworld/import_map.json +{ + "imports": { + "@denops/std": "jsr:@denops/std@^8.0.0", + "@denops/std/": "jsr:/@denops/std@^8.0.0/" // Required for submodules + } +} +``` + +We recommend using `deno.jsonc` as it's less verbose and integrates better with +Deno tooling. For more details about the differences, see the +[Deno documentation](https://docs.deno.com/runtime/fundamentals/modules/#differentiating-between-imports-or-importmap-in-deno.json-and---import-map-option). + +> [!IMPORTANT] +> +> Import map features require Denops v8.0.0 or later. For older versions, +> continue using direct URL imports. + +## Benefits + +1. **Cleaner imports**: No more long URLs in your code +2. **Version management**: Update dependencies in one place +3. **Better IDE support**: Auto-completion and type checking work seamlessly +4. **No conflicts**: Each plugin manages its own dependencies +5. **Development tools**: Format and lint your code from the project root diff --git a/src/tutorial/maze/adjusting-maze-size-to-fit-the-window.md b/src/tutorial/maze/adjusting-maze-size-to-fit-the-window.md index 4c62423..de208ca 100644 --- a/src/tutorial/maze/adjusting-maze-size-to-fit-the-window.md +++ b/src/tutorial/maze/adjusting-maze-size-to-fit-the-window.md @@ -7,10 +7,10 @@ to have a maze that fits the current window size. Let's modify the plugin to ensure the generated maze fits the current window size. -```typescript,title=denops/denops-helloworld/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import * as fn from "jsr:@denops/std@^7.0.0/function"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +```typescript,title=denops/denops-maze/main.ts +import type { Entrypoint } from "@denops/std"; +import * as fn from "@denops/std/function"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/creating-applicative-plugin.md b/src/tutorial/maze/creating-applicative-plugin.md index 5c331dc..e43ce62 100644 --- a/src/tutorial/maze/creating-applicative-plugin.md +++ b/src/tutorial/maze/creating-applicative-plugin.md @@ -28,17 +28,30 @@ augroup denops_maze augroup END ``` -Then, modify the `main.ts` file to accept the optional argument for a custom +Then, update your `denops/denops-maze/deno.jsonc` to include the unknownutil +dependency: + +```json,title=denops/denops-maze/deno.jsonc +{ + "imports": { + "@denops/std": "jsr:@denops/std@^8.0.0", + "@core/unknownutil": "jsr:@core/unknownutil@^4.3.0", + "maze_generator": "npm:@thewizardbear/maze_generator@^0.4.0" + } +} +``` + +Now modify the `main.ts` file to accept the optional argument for a custom opener, generate a maze that fits the current window size, configure the buffer options to make it non-file readonly buffer, etc. ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import { batch, collect } from "jsr:@denops/std@^7.0.0/batch"; -import * as fn from "jsr:@denops/std@^7.0.0/function"; -import * as op from "jsr:@denops/std@^7.0.0/option"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; -import { assert, is } from "jsr:@core/unknownutil@^4.3.0"; +import type { Entrypoint } from "@denops/std"; +import { batch, collect } from "@denops/std/batch"; +import * as fn from "@denops/std/function"; +import * as op from "@denops/std/option"; +import { Maze } from "maze_generator"; +import { assert, is } from "@core/unknownutil"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/outputting-content-to-buffer.md b/src/tutorial/maze/outputting-content-to-buffer.md index 0cc0f5c..1c147b4 100644 --- a/src/tutorial/maze/outputting-content-to-buffer.md +++ b/src/tutorial/maze/outputting-content-to-buffer.md @@ -7,8 +7,8 @@ the maze to a buffer so that users can yank the maze with daily Vim operations! Let's modify the code to make the generated maze output to a buffer. ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +import type { Entrypoint } from "@denops/std"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/properly-configured-the-buffer.md b/src/tutorial/maze/properly-configured-the-buffer.md index 018cb89..3f84e21 100644 --- a/src/tutorial/maze/properly-configured-the-buffer.md +++ b/src/tutorial/maze/properly-configured-the-buffer.md @@ -7,11 +7,11 @@ buffer after closure. Open the `main.ts` file and modify the `maze` method as follows: ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import * as buffer from "jsr:@denops/std@^7.0.0/buffer"; -import * as fn from "jsr:@denops/std@^7.0.0/function"; -import * as op from "jsr:@denops/std@^7.0.0/option"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +import type { Entrypoint } from "@denops/std"; +import * as buffer from "@denops/std/buffer"; +import * as fn from "@denops/std/function"; +import * as op from "@denops/std/option"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/properly-create-a-virtual-buffer.md b/src/tutorial/maze/properly-create-a-virtual-buffer.md index d15cb3f..c0f7a74 100644 --- a/src/tutorial/maze/properly-create-a-virtual-buffer.md +++ b/src/tutorial/maze/properly-create-a-virtual-buffer.md @@ -10,10 +10,10 @@ proper virtual buffer that concretizes the buffer content. Let's modify the `main.ts` file as follows: ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import * as buffer from "jsr:@denops/std@^7.0.0/buffer"; -import * as fn from "jsr:@denops/std@^7.0.0/function"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +import type { Entrypoint } from "@denops/std"; +import * as buffer from "@denops/std/buffer"; +import * as fn from "@denops/std/function"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/reduce-the-number-of-rpc-calls.md b/src/tutorial/maze/reduce-the-number-of-rpc-calls.md index 33bcd8f..87aa5b8 100644 --- a/src/tutorial/maze/reduce-the-number-of-rpc-calls.md +++ b/src/tutorial/maze/reduce-the-number-of-rpc-calls.md @@ -6,12 +6,12 @@ enhance performance by reducing the number of RPC calls using the `batch` module from `@denops/std`. Let's revise the `main.ts` file as follows: ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import { batch, collect } from "jsr:@denops/std@^7.0.0/batch"; -import * as buffer from "jsr:@denops/std@^7.0.0/buffer"; -import * as fn from "jsr:@denops/std@^7.0.0/function"; -import * as op from "jsr:@denops/std@^7.0.0/option"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +import type { Entrypoint } from "@denops/std"; +import { batch, collect } from "@denops/std/batch"; +import * as buffer from "@denops/std/buffer"; +import * as fn from "@denops/std/function"; +import * as op from "@denops/std/option"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = { diff --git a/src/tutorial/maze/utilizing-third-party-library.md b/src/tutorial/maze/utilizing-third-party-library.md index 11a0914..19d5a01 100644 --- a/src/tutorial/maze/utilizing-third-party-library.md +++ b/src/tutorial/maze/utilizing-third-party-library.md @@ -19,18 +19,41 @@ directory tree will look like this: ``` ~/denops-maze +├── deno.jsonc ├── denops │ └── denops-maze +│ ├── deno.jsonc │ └── main.ts └── plugin └── denops-maze.vim ``` +First, create the root `deno.jsonc` file for workspace configuration: + +```json,title=deno.jsonc +{ + "workspace": [ + "./denops/denops-maze" + ] +} +``` + +Then, create the `denops/denops-maze/deno.jsonc` file for runtime dependencies: + +```json,title=denops/denops-maze/deno.jsonc +{ + "imports": { + "@denops/std": "jsr:@denops/std@^8.0.0", + "maze_generator": "npm:@thewizardbear/maze_generator@^0.4.0" + } +} +``` + The content of the `denops/denops-maze/main.ts` file will be: ```typescript,title=denops/denops-maze/main.ts -import type { Entrypoint } from "jsr:@denops/std@^7.0.0"; -import { Maze } from "npm:@thewizardbear/maze_generator@^0.4.0"; +import type { Entrypoint } from "@denops/std"; +import { Maze } from "maze_generator"; export const main: Entrypoint = (denops) => { denops.dispatcher = {