@cockernutx/language-plugin-brace 0.1.0 → 0.1.3

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.
Files changed (2) hide show
  1. package/README.md +27 -101
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -6,33 +6,6 @@ Without it, Volar hands the brace source to the HTML parser: template expression
6
6
  *checked*, so `@if (…)` / `@for (…)` conditions are seen as plain text and the whole block
7
7
  produces spurious errors and no hover.
8
8
 
9
- ## Build before you use it
10
-
11
- ```sh
12
- deno task build:plugin # from the repo root
13
- ```
14
-
15
- **Volar loads a language plugin by resolving it and calling `require()` on the result**, in
16
- `vue-tsc` and in the editor's language server — both Node processes. Serving
17
- `src/index.ts` works only under Deno, which can `require()` TypeScript directly. In Node it
18
- is a `SyntaxError`; Volar catches it and carries on **without the plugin**, so every brace
19
- template loses hover, completions and diagnostics, and the only trace is a warning in the
20
- Vue Language Server output channel:
21
-
22
- ```
23
- [Vue] Resolve plugin path failed: @cockernutx/language-plugin-brace SyntaxError: …
24
- ```
25
-
26
- This is the failure mode this package was written in, and it is worth knowing because it
27
- looks like "the plugin does nothing" rather than "the plugin did not load". `index.cjs`
28
- therefore loads `dist/index.js`, and `deno task test` rebuilds it; a test fails if `dist` is
29
- missing or older than the source it came from.
30
-
31
- **How to tell it loaded:** open a brace template and hover a variable in a `@try` block. With
32
- the plugin loaded you get a type; without it, `any` (or nothing) — and Volar will be reporting
33
- errors on lines of brace syntax that are perfectly valid. **Reload the window** after building:
34
- the language server only loads plugins at startup.
35
-
36
9
  ## Setup
37
10
 
38
11
  ```jsonc
@@ -44,85 +17,38 @@ the language server only loads plugins at startup.
44
17
  }
45
18
  ```
46
19
 
47
- ## How it works
48
-
49
- Volar reaches a custom template language through three hooks. The plugin implements all
50
- three:
51
-
52
- 1. **`getEmbeddedCodes` / `resolveEmbeddedCode`** — publish the raw brace block as an
53
- embedded document with `lang: 'brace'`. Volar's built-in `vue-sfc-template` plugin only
54
- does this for `lang === 'html'`, so without these hooks a brace template has no virtual
55
- file at all: no highlighting and no in-template features.
56
- 2. **`compileSFCTemplate`** — compiles the generated template with language-core's
57
- `compileTemplate` and supplies the AST that template type checking works from.
58
- 3. **Offset remapping** — every location in that AST is rewritten back into brace-source
59
- coordinates.
60
-
61
- Step 3 is why `compileBrace` returns a map. Because the transform emits one line per input
62
- line, a generated line belongs to exactly one source line, and within it every copied
63
- fragment is recorded as a `{ gen, src, length }` segment. A generated position inside one of
64
- those segments maps to the exact source character; anything else is synthesised markup
65
- (`<template v-if="…">`) and falls back to the end of the nearest copied run, or to the end of
66
- the source line when it has none.
67
-
68
- Per-fragment mapping matters more than it sounds. One `@for` line becomes
69
- `<template v-for="item in items" :key="item.id">`, so `item`, `items` and `item.id` all sit
70
- at different offsets from their generated counterparts; a single shift per line gets only
71
- the first of them right, and the rest — the ones outside an `@if` line, which is most of a
72
- template — point at the wrong token, which is what makes hover and go-to-definition fail.
73
-
74
- The embedded content pushed in step 1 is the *raw brace source*, which is the same
75
- coordinate space the remapped AST uses. Keeping those two in step is the whole trick, and
76
- it is why the two hooks cannot be implemented independently.
77
-
78
- A malformed block is reported through `onError` rather than thrown, so a bad template
79
- produces a diagnostic instead of a dead language server.
80
-
81
- ### Why the AST must stay in brace coordinates
82
-
83
- Volar does not only read offsets out of this AST — it also slices text:
20
+ ## Build before you use it
84
21
 
85
- - `options.template` (the brace source) is sliced using AST offsets, so offsets have to be
86
- brace coordinates for expressions, props and children to map anywhere sensible.
87
- - `parseVForNode` slices `node.loc.source` — the *generated* text — using offsets relative to
88
- the node. That is the one place where the two coordinate spaces collide, and it is what used
89
- to make `@for` with `index` / `key` report two TypeScript syntax errors; the alignment pass
90
- described under Caveats is what reconciles them. Everything else is consistent because both
91
- the offsets and the document they describe end up in brace coordinates.
22
+ ```sh
23
+ deno task build:plugin # from the repo root
24
+ ```
92
25
 
93
- ## Highlighting
26
+ Volar loads the plugin by calling `require()` on it in a Node process (`vue-tsc` and the
27
+ editor's language server), so the entry must be built JavaScript — `src/index.ts` resolves only
28
+ under Deno. When the load fails Volar swallows the `SyntaxError` and runs **without the plugin**:
29
+ brace templates lose hover, completions and diagnostics, and the only trace is this line in the
30
+ Vue Language Server output channel:
94
31
 
95
- The `lang: 'brace'` embedded document needs a grammar to be useful, which VS Code can only
96
- get from an extension. `packages/vscode-vue-brace` registers that language id and a TextMate
97
- grammar — install it as described in its README. Without it, brace templates render as
98
- plain text.
32
+ ```
33
+ [Vue] Resolve plugin path failed: @cockernutx/language-plugin-brace SyntaxError: …
34
+ ```
99
35
 
100
- ## Entry point
36
+ `index.cjs` loads `dist/index.js`, `deno task test` rebuilds it, and a test fails if `dist` is
37
+ missing or older than the source it came from.
101
38
 
102
- Volar resolves plugin names with `require()` and then calls the resolved module *itself* as
103
- the factory (no `.default` unwrapping), so the entry has to be a callable CommonJS module
104
- that **Node** can load. `index.cjs` re-exports the default from `dist/index.js`, which
105
- `deno task build:plugin` produces. Pointing it at `src/index.ts` instead is the trap
106
- described at the top of this file: Deno loads it happily and the editor never does.
39
+ **Check it loaded:** hover a variable inside `@try` — a type means loaded, `any` (or nothing)
40
+ means not. In that case Volar also reports errors on valid brace syntax.
107
41
 
108
- ## Caveats
42
+ **Reload the window** after building: the language server only loads plugins at startup.
109
43
 
110
- - The plugin API version is pinned to `2.2`. language-core's `validVersions` is
111
- `[2, 2.1, 2.2]`; anything else makes it drop the plugin with only a console warning.
112
- - **`@for` with `index` or `key` needs the element aligned to the author's column.** Volar
113
- rebuilds the
114
- `v-for` binding by slicing the generated `<template v-for="(item, i) in …">` text with
115
- offsets *relative to the element*, so those deltas have to come out as the generated ones.
116
- `collectVForPatterns` reads the binding names with generated deltas *before* the remap;
117
- `alignVForPatterns` then anchors the element on the `@for` column the author wrote and
118
- rebuilds `loc.source` as a padded pattern occupying the author's own columns. Before that,
119
- the slice produced garbage: the errors above, plus garbage binding names whose semantic
120
- tokens landed across the template. (An earlier attempt moved the element's start onto the
121
- generated geometry instead. That repairs the slice and corrupts every other consumer of that
122
- offset, which reads it as a *source* position — do not reintroduce it.)
123
- - **`vue-tsc` needs a Node runtime.** It cannot run under Deno — Volar registers `.vue`
124
- support by rewriting `tsc.js` through an `fs.readFileSync` hook that Deno bypasses — so a
125
- Node binary has to be present to type-check templates. That is how the loader bug above was
126
- found: `node …/vue-tsc.js --noEmit` reported `Resolve plugin path failed`, and
127
- `--listFiles` showed no `.vue` files in the program at all.
44
+ ## Notes
128
45
 
46
+ - Highlighting is separate: install [`vscode-vue-brace`](../vscode-vue-brace) for the `brace`
47
+ language id and the grammar. The plugin only supplies language features.
48
+ - The plugin API version is pinned to `2.2`, one of language-core's `validVersions`
49
+ (`[2, 2.1, 2.2]`); anything else drops the plugin with a console warning only.
50
+ - **`@for` with `index` / `key`, and the `@catch` bindings, depend on offset alignment that is
51
+ load-bearing.** Read [`docs/internals/volar-and-editor-notes.md`](../../docs/internals/volar-and-editor-notes.md)
52
+ before changing it.
53
+ - **`vue-tsc` needs a Node runtime** and cannot run under Deno: Volar registers `.vue` support
54
+ through an `fs.readFileSync` hook that Deno bypasses.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cockernutx/language-plugin-brace",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Volar / Vue language tools plugin for lang=\"brace\" SFC templates",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "homepage": "https://github.com/strawberyy-coconut/vue-brace#readme",
28
28
  "bugs": "https://github.com/strawberyy-coconut/vue-brace/issues",
29
29
  "dependencies": {
30
- "@cockernutx/brace-template": "^0.1.0",
30
+ "@cockernutx/brace-template": "^0.1.3",
31
31
  "@vue/language-core": "^3.3.11"
32
32
  },
33
33
  "peerDependencies": {