# 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