#+TITLE: JavaScript layer #+TAGS: general|js|layer|multi-paradigm|programming [[file:img/javascript.png]] * Table of Contents :TOC_5_gh:noexport: - [[#description][Description]] - [[#features][Features:]] - [[#install][Install]] - [[#web-beautify][web-beautify]] - [[#prettier][prettier]] - [[#import-js][import-js]] - [[#choosing-a-backend][Choosing a backend]] - [[#choosing-a-formatter][Choosing a formatter]] - [[#format-buffer-before-saving][Format buffer before saving]] - [[#backends][Backends]] - [[#tern][Tern]] - [[#language-server-protocol][Language Server Protocol]] - [[#typescript][TypeScript]] - [[#flow][Flow]] - [[#debugger-dap-integration][Debugger (dap integration)]] - [[#configuration][Configuration]] - [[#indentation][Indentation]] - [[#repl][REPL]] - [[#browser-based-repl][Browser based REPL]] - [[#server-based-repl][Server based REPL]] - [[#node][Node]] - [[#node-modules][Node Modules]] - [[#node-externs][Node Externs]] - [[#flowtypescript][Flow/Typescript]] - [[#key-bindings][Key bindings]] - [[#js2-mode][js2-mode]] - [[#folding-js2-mode][Folding (js2-mode)]] - [[#importing-import-js][Importing (import-js)]] - [[#refactoring-js2-refactor][Refactoring (js2-refactor)]] - [[#documentation-js-doc][Documentation (js-doc)]] - [[#browser-based-repl-skewer-mode][Browser based REPL (skewer-mode)]] - [[#server-based-repl-nodejs-repl][Server based REPL (nodejs-repl)]] - [[#debugger-dap-mode][debugger (dap mode)]] * Description This layer adds support for the JavaScript language using [[https://github.com/mooz/js2-mode][js2-mode]]. ** Features: - Multiple backends support: Tern and LSP - Smart code folding - Refactoring: done using [[https://github.com/magnars/js2-refactor.el][js2-refactor]]. - Auto-completion and documentation - Browser based REPL available via [[https://github.com/skeeto/skewer-mode][skewer-mode]] and [[https://github.com/pandeiro/livid-mode][livid-mode]] - Server based REPL with [[https://github.com/abicky/nodejs-repl.el][nodejs-repl]] - Formatting with [[https://github.com/yasuyk/web-beautify][web-beautify]] or [[https://github.com/prettier/prettier][prettier]] - Interactive debugger using [[https://github.com/emacs-lsp/dap-mode][dap-mode]] - Display Flow & Typescript type information * Install To use this configuration layer, add it to your =~/.spacemacs=. You will need to add =javascript= to the existing =dotspacemacs-configuration-layers= list in this file. To enable the importing helper, install the =ImportJS=: #+BEGIN_SRC sh $ npm install -g import-js #+END_SRC To activate error checking using flycheck, install one of the [[http://www.flycheck.org/en/latest/languages.html#javascript][available linters]] such as =ESLint= or =JSHint=: #+BEGIN_SRC sh $ npm install -g eslint # or $ npm install -g jshint #+END_SRC If you install these in non-standard locations, then add the following to your =dotspacemacs/user-init= function: #+BEGIN_SRC elisp (add-to-list 'exec-path "/path/to/node/bins" t) #+END_SRC ** web-beautify See [[file:../../+tools/web-beautify/README.org][web-beautify layer]] documentation. ** prettier See [[file:../../+tools/prettier/README.org][prettier layer]] documentation. ** import-js See [[file:../../+tools/import-js/README.org][import-js layer]] documentation. To enable it, set the layer variable =javascript-import-tool=: #+BEGIN_SRC elisp (javascript :variables javascript-import-tool 'import-js) #+END_SRC ** Choosing a backend To choose a default backend set the layer variable =javascript-backend=: #+BEGIN_SRC elisp (javascript :variables javascript-backend 'tern) #+END_SRC Alternatively the =lsp= backend will be automatically chosen if the layer =lsp= is used and you did not specify any value for =javascript-backend=. Backend can be chosen on a per project basis using directory local variables (files named =.dir-locals.el= at the root of a project), an example to use the =lsp= backend: #+BEGIN_SRC elisp ;;; Directory Local Variables ;;; For more information see (info "(emacs) Directory Variables") ((js2-mode (javascript-backend . lsp))) #+END_SRC When =lsp= is set as the backend, but you don't want to use lsp as the linter, set the variable =javascript-lsp-linter= to =nil=. #+BEGIN_SRC elisp (javascript :variables javascript-backend 'lsp javascript-lsp-linter nil) #+END_SRC ** Choosing a formatter To choose a formatter, set the layer variable =javascript-fmt-tool=: #+BEGIN_SRC elisp (javascript :variables javascript-fmt-tool 'web-beautify) #+END_SRC The formatter can be chosen on a per project basis using directory local variables (files named =.dir-locals.el= at the root of a project). For example to use the =prettier= formatter: #+BEGIN_SRC elisp ;;; Directory Local Variables ;;; For more information see (info "(emacs) Directory Variables") ((js2-mode (javascript-fmt-tool . prettier))) #+END_SRC *Note:* you can easily add a directory local variable with ~SPC f v d~. You can choose formatting tool: #+BEGIN_SRC elisp (setq-default dotspacemacs-configuration-layers '( (javascript :variables javascript-fmt-tool 'prettier))) #+END_SRC Default is =’web-beautify=. ** Format buffer before saving #+BEGIN_SRC elisp (javascript :variables javascript-fmt-on-save t) #+END_SRC * Backends ** Tern See [[file:../../+tools/tern/README.org][tern layer]] documentation. ** Language Server Protocol To use an LSP server with JavaScript, add it as a =javascript-backend= to your =~/.spacemacs=. #+BEGIN_SRC emacs-lisp (setq-default dotspacemacs-configuration-layers '((javascript :variables javascript-backend 'lsp))) #+END_SRC *** TypeScript You have to install =typescript-language-server= (recommended) or =javascript-typescript-langserver= language server packages via #+BEGIN_SRC sh npm i -g typescript typescript-language-server #+END_SRC or #+BEGIN_SRC sh npm i -g typescript javascript-typescript-langserver #+END_SRC *** Flow You have to install =flow-bin=. #+BEGIN_SRC sh npm i -g flow-bin #+END_SRC Or, if you'd rather use a locally-installed =flow-bin= from your project's node_modules directory, see the [[#node-modules][node-add-modules-path setting]]. *** Debugger (dap integration) To install the debug adapter you may run =M-x dap-firefox-setup= or =M-x dap-chrome-setup= if you are using Linux or download it manually from [[https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-firefox-debug][Firefox Debug Adapter]] or [[https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome][Chrome Debug Adapter]] and adjust =dap-firefox-debug-path= or =dap-chrome-debug-path=. For usage instructions refer to [[https://github.com/emacs-lsp/dap-mode][dap-mode]] readme. * Configuration ** Indentation To change how js2-mode indents code, set the variable =js2-basic-offset=, as such: #+BEGIN_SRC emacs-lisp (setq-default js2-basic-offset 2) #+END_SRC or when adding the =javascript= configuration layer: #+BEGIN_SRC emacs-lisp (javascript :variables js2-basic-offset 2) #+END_SRC Similarly, to change how js-mode indents JSON files, set the variable =js-indent-level=, as such: #+BEGIN_SRC emacs-lisp (setq-default js-indent-level 2) #+END_SRC or when adding the =javascript= configuration layer: #+BEGIN_SRC emacs-lisp (javascript :variables js-indent-level 2) #+END_SRC ** REPL *** Browser based REPL To use the browser based REPL set the =javascript-repl= variable as shown below: #+BEGIN_SRC elisp (setq-default dotspacemacs-configuration-layers '((javascript :variables javascript-repl `skewer))) #+END_SRC In addition you need a running httpd server and a page loaded with skewer. If a blank page serves your needs, just use the run-skewer command in your javascript buffer. If you want to inject it in your own page, follow [[https://github.com/skeeto/skewer-mode#skewering-with-cors][these instructions]] (install the Greasemonkey script and then click the triangle in the top-right corner - if it turns green, you're good to go). *** Server based REPL To use the server based REPL set the =javascript-repl= variable as shown below: #+BEGIN_SRC elisp (setq-default dotspacemacs-configuration-layers '((javascript :variables javascript-repl `nodejs))) #+END_SRC In addition install nodejs and make sure that =node= is in the path. ** Node *** Node Modules If you would like =node_modules/.bin= to be automatically added to the buffer local =exec_path=, e.g. to support project local eslint installations, set the =node-add-modules-path= variable in the =javascript= config section. Note that doing this [[https://stackoverflow.com/questions/9679932#comment33532258_9683472][introduces a security risk]]: #+BEGIN_SRC elisp (setq-default dotspacemacs-configuration-layers '((javascript :variables node-add-modules-path t))) #+END_SRC *** Node Externs If you want =js2-mode= to presume =node= variables are defined by the host system (for completion purposes /i.a./) set the =js2-include-node-exters= variable to =t= in the =javascript= config section: #+BEGIN_SRC elisp (setq-default dotspacemacs-configuration-layers '((javascript :variables js2-include-node-externs t))) #+END_SRC ** Flow/Typescript Enable the [[Language Server Protocol][lsp]] backend to view type information, jump to declarations, and more. js2-mode is sometimes confused by the type syntax, so you may wish to disable their parse warnings: #+BEGIN_SRC emacs-lisp (javascript :variables javascript-backend 'lsp js2-mode-show-strict-warnings nil js2-mode-show-parse-errors nil) #+END_SRC * Key bindings ** js2-mode | Key binding | Description | |-------------+--------------------------------------| | ~SPC m w~ | toggle js2-mode warnings and errors | | ~%~ | jump between blockswith [[https://github.com/redguardtoo/evil-matchit][evil-matchit]] | ** Folding (js2-mode) | Key binding | Description | |-------------+--------------------------| | ~SPC m z c~ | hide element | | ~SPC m z o~ | show element | | ~SPC m z r~ | show all element | | ~SPC m z e~ | toggle hide/show element | | ~SPC m z F~ | toggle hide functions | | ~SPC m z C~ | toggle hide comments | ** Importing (import-js) | Key binding | Description | |-------------+---------------------------------------------------------------------| | ~SPC m i i~ | Import the module for the variable under the cursor | | ~SPC m i f~ | Import any missing modules and remove any modules that are not used | | ~SPC m g i~ | Go to the module of the variable under cursor | ** Refactoring (js2-refactor) Bindings should match the plain emacs assignments. | Key binding | Description | |---------------+----------------------------------------------------------------------------------------------------------------| | ~SPC m k~ | deletes to the end of the line, but does not cross semantic boundaries | | ~SPC m r 3 i~ | converts ternary operator to if-statement | | ~SPC m r a g~ | creates a =/* global */= annotation if it is missing, and adds var to point to it | | ~SPC m r a o~ | replaces arguments to a function call with an object literal of named arguments | | ~SPC m r b a~ | moves the last child out of current function, if-statement, for-loop or while-loop | | ~SPC m r c a~ | converts a multiline array to one line | | ~SPC m r c o~ | converts a multiline object literal to one line | | ~SPC m r c u~ | converts a multiline function to one line (expecting semicolons as statement delimiters) | | ~SPC m r e a~ | converts a one line array to multiline | | ~SPC m r e f~ | extracts the marked expressions into a new named function | | ~SPC m r e m~ | extracts the marked expressions out into a new method in an object literal | | ~SPC m r e o~ | converts a one line object literal to multiline | | ~SPC m r e u~ | converts a one line function to multiline (expecting semicolons as statement delimiters) | | ~SPC m r e v~ | takes a marked expression and replaces it with a var | | ~SPC m r i g~ | creates a shortcut for a marked global by injecting it in the wrapping immediately invoked function expression | | ~SPC m r i p~ | changes the marked expression to a parameter in a local function | | ~SPC m r i v~ | replaces all instances of a variable with its initial value | | ~SPC m r l p~ | changes a parameter to a local var in a local function | | ~SPC m r l t~ | adds a console.log statement for what is at point (or region) | | ~SPC m r r v~ | renames the variable on point and all occurrences in its lexical scope | | ~SPC m r s l~ | moves the next statement into current function, if-statement, for-loop, while-loop | | ~SPC m r s s~ | splits a =String= | | ~SPC m r s v~ | splits a =var= with multiple vars declared into several =var= statements | | ~SPC m r t f~ | toggle between function declaration and function expression | | ~SPC m r u w~ | replaces the parent statement with the selected region | | ~SPC m r v t~ | changes local =var a= to be =this.a= instead | | ~SPC m r w i~ | wraps the entire buffer in an immediately invoked function expression | | ~SPC m r w l~ | wraps the region in a for-loop | | ~SPC m x m j~ | move line down, while keeping commas correctly placed | | ~SPC m x m k~ | move line up, while keeping commas correctly placed | *** Documentation (js-doc) You can check more [[https://github.com/mooz/js-doc/][here]] | Key binding | Description | |---------------+---------------------------------------| | ~SPC m r d b~ | insert JSDoc comment for current file | | ~SPC m r d f~ | insert JSDoc comment for function | | ~SPC m r d t~ | insert tag to comment | | ~SPC m r d h~ | show list of available jsdoc tags | ** Browser based REPL (skewer-mode) | Key binding | Description | |-------------+------------------------------------------------------------------| | ~SPC m e e~ | evaluates the last expression | | ~SPC m e E~ | evaluates and inserts the result of the last expression at point | | Key binding | Description | |-------------+------------------------------------------------------------------------------------| | ~SPC m T l~ | Toggle live evaluation of whole buffer in REPL on buffer changes | | ~SPC m s b~ | send current buffer contents to the skewer REPL | | ~SPC m s B~ | send current buffer contents to the skewer REPL and switch to it in insert state | | ~SPC m s f~ | send current function at point to the skewer REPL | | ~SPC m s F~ | send current function at point to the skewer REPL and switch to it in insert state | | ~SPC m s i~ | starts/switch to the skewer REPL | | ~SPC m s r~ | send current region to the skewer REPL | | ~SPC m s R~ | send current region to the skewer REPL and switch to it in insert state | | ~SPC m s s~ | switch to REPL | ** Server based REPL (nodejs-repl) | Key binding | Description | |-------------+--------------------------------------------------------------------| | ~SPC m s i~ | Switch to NodeJS REPL if one has been started, otherwise start one | | ~SPC m s e~ | Send last expression to NodeJS REPL | | ~SPC m s E~ | Send last expression to NodeJS REPL and switch to REPL | | ~SPC m s b~ | Send current buffer to NodeJS REPL | | ~SPC m s B~ | Send current buffer to NodeJS REPL and switch to REPL | | ~SPC m s l~ | Send current line to NodeJS REPL | | ~SPC m s L~ | Send current line to NodeJS REPL and switch to REPL | | ~SPC m s r~ | Send active region to NodeJS REPL | | ~SPC m s R~ | Send active region to NodeJS REPL and switch to REPL | | ~SPC m s s~ | switch to REPL | ** debugger (dap mode) Using the =dap= layer you'll get access to all the DAP key bindings, see the complete list of key bindings on the [[https://github.com/syl20bnr/spacemacs/tree/develop/layers/%2Btools/dap#key-bindings][dap layer description]].