When developing TypeScript projects that use window.Shiny
, we recommend installing the Shiny TypeScript definitions to your package. To install the latest stable definitions, call
yarn add https://github.com/rstudio/shiny\#v1.7.0
, matching the GitHub tag to your current the Shiny CRAN release (ex: v1.7.0
). If you are asked to select a version of @types/jquery
, please select the closest matching version.
This will provide a global type definition of Shiny
, let your IDE know that window.Shiny
is of type Shiny
, and declare a globally available variable Shiny
within your project. You should not need to import anything. Similar to jQuery
, it should Just WorkTM.
When loading your compiled file, it should be loaded after shiny.js
is loaded. If you are using an htmlDependency()
to add your code to the page, your script will automatically be loaded after has been loaded.
All files will be described as if the working directory is the root folder of rstudio/shiny
, not relative to this README.md
file.
Shiny's TypeScript build tools use Node.js, along with yarn v2 to manage the JavaScript packages.
Installation of Node.js differs across platforms, see the official Node.js website for instructions on downloading and installing. We presume that you have Node.js installed on your machine before continuing.
Install yarn using the official instructions.
You can test that Node.js and yarn are installed properly by running the following commands:
node --version
yarn --version
Once both are installed, run the following in the root repo directory to install the packages :
# Sitting in `rstudio/shiny` repo
yarn install
This repo uses Yarn v2. If you have the latest yarn installed from brew (v1.x), Yarn will pick up on the fact that this repo is a Yarn v2 (berry
) repo.
For compatibility with esbuild
, the ./node_modules
is still maintained.
To avoid finding and determining which is the best node
version to use, use a package called n
to help facilitate installing the latest stable version of node.
npx
is a command that allows you to run a package and command even if the package is not installed. It is distributed with the latest releases of node.
# Update to the latest stable node version
npx n stable
# View installed versions
npx n ls
If in the future you want to upgrade or add a package, run:
yarn add --dev [packagename]
This will automatically add the package to the dependencies in package.json
, and it will also update the yarn.lock
to reflect that change. If someone other than yourself does this, simply run yarn
to update your local packages to match the new package.json
.
Periodically, it's good to upgrade the packages to a recent version. There's two ways of doing this, depending on your intention:
Use yarn up
to upgrade all dependencies to their latest version based on the version range specified in the package.json file (the yarn.lock
file will be recreated as well. Yarn packages use semantic versioning, i.e. each version is writen with a maximum of 3 dot-separated numbers such that: major.minor.patch
. For example in the version 3.1.4
, 3 is the major version number, 1 is the minor version number and 4 is the patch version number. Here are the most used operators (these appear before the version number):
~
is for upgrades that keep the minor version the same (assuming that was specified);
^
is for upgrades that keep the major version the same (more or less -- more specifically, it allow changes that do not modify the first non-zero digit in the version, either the 3 in 3.1.4 or the 4 in 0.4.2.). This is the default operator added to the package.json when you run yarn add [package-name]
.
Use yarn up [package]
to upgrade a single named package to the version specified by the latest tag (potentially upgrading the package across major versions).
To see all outdated packages, run yarn outdated
The documentation by TypeScript is a solid resource to know each and every bell and whistle. Most features have examples and convey the thoughts well.
TypeScript Deep Dive is an online bookdown
-like approach to TypeScript by "Microsoft MVP for TypeScript", Basarat Ali Syed. In his book, he goes through many examples of what you "should do", not necessarily "what is possible" like the TypeScript docs.
Using the style guid from TypeScript Deep Dive / StyleGuide, we extend it to have the usage be more familiar to R developers and preexisting Shiny development. The goal is to produce consistent code that can be injested quickly.
null
vs. undefined
x === null
unless you truly mean it.if (x) {}
type
vs interface
Use
type
when you might need a union or intersection:type Foo = number | { someProperty: number }
Use
interface
when you want extends or implements:interface FooBar extends Foo { bar: string;}
Otherwise use whatever makes you happy that day.
PascalCase
Shiny
eslint
) StyleGuidecamelCase
const hello = "world
PascalCase
class InputBinding {}
PascalCase
type BindingBase = {name: string}
interface ShinyEventMessage extends JQuery.Event {}
PascalCase
When you can't use double quotes, try using back ticks (`).
Type[]
Foo[]
(vs Array<Foo>
){[key: string]: valueType}
const x: {[key: string]: number} = {a: 4}
const x: {known: string, [key: string]: number} = {known: "yes", a: 4}
camelCase
- Enforced by eslint
The JavaScript community likes to build many small, effective packages that do minimal work. The unfortunate side effect is needing a config file for everything.
All config files are located in the root folder to avoid opening two separate VS Code projects.
.browserslistrc
browserslist
and core-js
to determine which polyfills should be incorporated..eslintrc.yml
eslint
and prettier
to determine how the TypeScript files should be formatted and which lint failures should cause warnings, errors, or be ignored..madgerc
type
only imports are ignored as they are not included in the final bundle..prettierrc.yml
prettier
to know how to adjust code when a file is saved in VSCode or within eslint
's linting process.yarnrc.yml
yarn
to use yarn
v2, install ./node_modules
folder for esbuild
, and any CLI plugins.babel.config.json
babel
transpilation of TypeScript -> JavaScript -> polyfilled JavaScript."useBuiltIns": "usage"
- core-js
polyfills are only added as they are used."corejs": "3.9"
- This number should match the installed core-js
number."ignore":["node_modules/core-js"]
- The core-js
library is directly ignored to avoid being processed by babel
.jest.config.js
jest
testingpackage.json
yarn
via yarn run SCRIPTNAME
.yarn run watch
- Watch srcts/src
for changes and rebuild the JavaScript files.yarn run build
- Build shiny.js
and shiny.min.js
in inst/www/shared
. Both files will have a corresponding sourcemapyarn run lint
- Fix all TypeScript lints using eslint
and prettier
yarn run test
- Run all TypeScript teststsconfig.json
-target: ES5
- Compile to es5, so babel has an easier job.preserveConstEnums: false
- Do no preserve enum values into the final code. (If true, produces bloat / unused code)isolatedModules: true
& esModuleInterop: true
- Requested by esbuild
. This allows for esbuild
to safely compile the files in parallelesbuild is a build tool that (for Shiny's purposes) compiles the TypeScript into a single JavaScript file.
To run all build tasks, run:
yarn build
It's also useful to have esbuild
watch for updated files and immediately re-build shiny.js
as necessary during development. This is done with:
yarn watch
Both JavaScript files will produce a sourcemap (**.js.map
) that the browser will understand. This will help you debug Shiny's JavaScript code within the browser and point back to the original TypeScript files.
./extras/globalShiny.ts
contains global declarations to define window.Shiny
, a globally available Shiny
variable, and a globally available Shiny
type. This file is in a parallel folder to ./src
to avoid Shiny
from being globally accessable within the source code. However, this file is the default type definition when the Type definitions are installed by external developers.
On push to the main
branch or push to a Pull Request to the main
branch, a GitHub Action will be run to make sure the bundled JavaScript code is up to date. If the source code does not compile to the exact same file, it will be committed an pushed back to the outdated branch. (This makes it so the full build tools are not necessary for small tweaks and comments. 🎉)
@types/jquery
As of v3.5.5, @types/jquery
produces a globally available constant of $
and jQuery
. This is problematic as TypeScript is there to enforce that all variables are accounted for. Declaring that these two variables exist globally removes the requirement to import $
(or jQuery
). This is bad for Shiny as the $
would not be enforced to the "within package" $
.
To overcome this, a patch is used to remove the globally defined $
(and Symbol
) variable declarations. Yarn v2 has a patch
protocol that allows a local patch to be applied to the publically available @types/jquery
package upon installation.
If in the future where the variables are not globally declared anymore, the patch may be removed and @types/jquery
can be imported directly.
A global module plugin is used by esbuild
to swap out a real jquery
module to just return window.jQuery
. This allows for tests and core code to behave the same way.
core-js
To update the version of core-js
:
yarn outdated core-js
. (If there's no output, then you have the latest version.)yarn add --dev core-js --exact
.Shiny already has a handful of html dependencies that should NOT be bundled within shiny.js
. To update the dependencies below, see the directions in tools/README.md
.
jquery
/ @types/jquery
bootstrap
/ @types/bootstrap
* Bootstrap is not being updated anymore. Only bootstrap 3.4 will be utilized within shiny.js. To use the latest bootstrap, see rstudio/bslib
bootstrap-datepicker
/ @types/bootstrap-datepicker
ion-rangeslider
/ @types/ion-rangeslider
* selectize
/ @types/selectize
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.