syndicate-js/packages/ts-plugin/README.md

240 lines
8.0 KiB
Markdown

# TypeScript plugin for Syndicate
Rewrites Syndicate DSL syntax into plain TypeScript as a `tsserver`
plugin, allowing your IDE to work directly with Syndicate constructs
and not requiring a separate preprocessing step.
Sadly, `tsc` doesn't pay attention to plugins (not as of April 2021,
anyway). If you're using `tsc` as part of your build, you will need to
use `@syndicate-lang/tsc`'s `syndicate-tsc` command instead.
## Installing the plugin
`package.json`:
{
"devDependencies": {
"@syndicate-lang/ts-plugin": "file:../..",
...
},
...
}
then `yarn install`.
## Enabling the plugin in the TypeScript compiler
`tsconfig.json`:
{
"compilerOptions": {
"plugins": [
{ "name": "@syndicate-lang/ts-plugin" }
],
...
},
...
}
## Getting it to work with specific editors
### Emacs with Tide
It should Just Work, if the `node_modules` next to `tsconfig.json` has
a `typescript/` subdirectory.
If no such `typescript/` subdirectory exists in `node_modules`, then
because
[tsserver loads plugins from relative to tsserver.js rather than tsconfig.json][tsserver-plugin-loading-problem],
you will have to tell `tide-mode` about where your project's
`tsserver` lives.
One easy way to do that is to create a symlink in your `node_modules`:
```shell
ln -s /FULL/PATH/TO/YOUR/PROJECT/node_modules/typescript ./node_modules/
```
Another way to do it is to put a `.dir-locals.el` file in your project
root, containing
```elisp
((tide-mode
. ((tide-tsserver-executable
. "/FULL/PATH/TO/YOUR/PROJECT/node_modules/typescript/bin/tsserver"))))
```
I don't know of any way of automatically resolving a relative path
specification with respect to the directory containing
`.dir-locals.el` without using `eval`, but if you're happy to do so,
you can use the following:
```elisp
((typescript-mode
. ((eval . (setq tide-tsserver-executable
(concat
(let ((d (dir-locals-find-file ".")))
(if (stringp d) d (car d)))
"node_modules/typescript/lib/tsserver.js"))))))
```
If you use the `.dir-locals.el` methods, you may need to run
`tide-restart-server` once after opening the first TypeScript file in
your project (and then close and re-open that TypeScript file).
### Emacs with LSP
#### Tell lsp-mode to automatically find and use your project's tsserver
Since lsp-mode version 8.0.1, the first thing to try is to set
`lsp-clients-typescript-prefer-use-project-ts-server` to `t`. From the lsp-mode documentation:
> `lsp-clients-typescript-prefer-use-project-ts-server`
>
> *Type:* `boolean`
>
> *Default:* `nil`
>
> When set, prefers using the tsserver.js from your project. This can allow loading plugins configured in your tsconfig.json.
When `lsp-clients-typescript-prefer-use-project-ts-server` is set, but your emacs hasn't
downloaded and installed the `ts-ls` lsp-mode backend yet, lsp-mode will prompt you to download
and install `ts-ls`. After that process has completed, it should then look for and load your
project's tsserver.
If `lsp-clients-typescript-prefer-use-project-ts-server` is not available (e.g. your lsp-mode
is older than 8.0.1), or if, for some reason, it doesn't work, read on.
#### Explicitly configuring your project's tsserver with lsp-mode
LSP will, by default (for versions older than 8.0.1 or when
`lsp-clients-typescript-prefer-use-project-ts-server` is `nil`), use its own `tsserver`, no
matter what is in the local `node_modules`. Because of the [issue with tsserver's approach to
plugin loading][tsserver-plugin-loading-problem], this means that by default it will not load
the Syndicate plugin.
Overriding the `tsserver` location is similar to the way it's done for
Tide, but instead of a variable value change, a function has to be
called. One good way to do it is to use `.dir-locals.el`, as above:
```elisp
((typescript-mode
. ((eval . (progn
(require 'lsp-javascript)
(lsp-dependency
'typescript
`(:system ,(concat
(let ((d (dir-locals-find-file ".")))
(if (stringp d) d (car d)))
"node_modules/typescript/lib/tsserver.js"))))))))
```
And, of course, you can make changes for both Tide and LSP at once, if
you like:
```elisp
((typescript-mode
. ((eval
. (progn
;; For TIDE:
(setq tide-tsserver-executable
(concat
(let ((d (dir-locals-find-file ".")))
(if (stringp d) d (car d)))
"node_modules/typescript/lib/tsserver.js"))
;; For LSP:
(require 'lsp-javascript)
(lsp-dependency 'typescript
`(:system ,(concat
(let ((d (dir-locals-find-file ".")))
(if (stringp d) d (car d)))
"node_modules/typescript/lib/tsserver.js"))))
))))
```
Finally, I've had some trouble with LSP when I've not had
`typescript-language-server`, which is different to `tsserver` (?),
installed. Then, I've had to install the `typescript-language-server`
npm package, as well as `typescript`, and to take an approach similar
to this to point LSP at it:
```elisp
((typescript-mode
. ((eval
. (progn
(require 'lsp-javascript)
(let ((node-modules (concat
(let ((d (dir-locals-find-file ".")))
(if (stringp d) d (car d)))
"node_modules/")))
(lsp-dependency 'typescript-language-server
`(:system ,(concat node-modules
"typescript-language-server/lib/cli.mjs")))
(lsp-dependency 'typescript
`(:system ,(concat node-modules
"typescript/lib/tsserver.js")))))
))))
```
Older versions of `typescript-language-server` use `cli.js` instead of
`cli.mjs` above.
### Visual Studio Code
After `yarn install`, if you have a `node_modules/typescript`
directory, then the following will work. (Otherwise, there may not be
an option to select "Use Workspace Version", and you may need to
symlink a `typescript` directory into `node_modules` as described for
Emacs Tide above.)
Open VS Code, and select the version of TypeScript contained therein
by following instructions
[here](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions).
Specifically, when selecting a TypeScript version,
[choose "Use Workspace Version"](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-the-workspace-version-of-typescript).
For me, the net effect of this is to create a `.vscode/settings.json`
file containing:
{
"typescript.tsdk": "node_modules/typescript/lib"
}
## Debugging
### Emacs with Tide
You can get verbose logs from Tide's tsserver by setting the Emacs
variable `tide-tsserver-process-environment`:
```elisp
(setq tide-tsserver-process-environment '("TSS_LOG=-file /tmp/tss.log"))
```
You can also enable "verbose" output, if that's useful:
```elisp
(setq tide-tsserver-process-environment '("TSS_LOG=-level verbose -file /tmp/tss.log"))
```
Finally, you can set these options in a `.dir-locals.el` file, too:
```elisp
((typescript-mode
. ((tide-tsserver-process-environment . ("TSS_LOG=-level verbose -file /tmp/tss.log")))))
```
### Emacs with LSP
Older versions of `lsp-mode` by default helpfully put logs in a `.log/` directory in your
project root. Newer versions seem not to have that logging enabled by default; for these
versions, set (customize) the variable `lsp-clients-typescript-server-args` to include the
strings `"--tsserver-log-verbosity" "verbose"`. For example,
```elisp
(setq lsp-clients-typescript-server-args '("--stdio" "--tsserver-log-verbosity" "verbose"))
```
[tsserver-plugin-loading-problem]: https://github.com/microsoft/TypeScript/issues/42688