* feat: Replace client-side markdown rendering with server-side in showcase mode * `devtools::document()` (GitHub Actions) * chore: callout rendering differences may happen
Using Shiny TypeScript Definitions
When developing TypeScript projects that use window.Shiny, we recommend installing the Shiny TypeScript definitions to your package. To install the latest stable definitions, run:
npm install https://github.com/rstudio/shiny\#v1.11.0 --save-dev
, matching the GitHub tag to your current the Shiny CRAN release (ex: v1.11.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 window.Shiny. In your code, you can access the Shiny object via window.Shiny or just Shiny. However, note that if you are using TypeScript, it will be OK with window.Shiny but it will flag uses of Shiny (without the window. prefix), because TypeScript won't know that it's a global variable. We consider it better practice to use window.Shiny instead of Shiny, but if you want TypeScript to know that Shiny is available as a global variable, you can add the following to a TypeScript file in your code base.
declare global {
const Shiny: ShinyClass;
}
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.
TypeScript build tools (Shiny Developers)
All files will be described as if the working directory is the root folder of rstudio/shiny, not relative to this README.md file.
First-time setup
Shiny's TypeScript build tools use Node.js and npm.
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 npm using the official instructions.
You can test that Node.js and npm are installed properly by running the following commands:
node --version
npm --version
Once both are installed, run the following in the root repo directory to install the packages :
# Sitting in `rstudio/shiny` repo
npm install
Reproducible builds
To ensure that the same versions of packages are used between users, install the package lock dependencies by running:
npm ci
This command will install the exact versions of packages specified in the package-lock.json file, ensuring that all developers are using the same versions of dependencies.
Updating Node
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.
npxis 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
Adding packages
If in the future you want to upgrade or add a package, run:
npm install --save-dev [packagename]
This will automatically add the package to the dependencies in package.json, and it will also update the package-lock.json to reflect that change. If someone other than yourself does this, simply run npm ci to update your local packages to match the new package.json.
Upgrading packages
Periodically, it's good to upgrade the packages to a recent version. There's two ways of doing this, depending on your intention:
-
Use
npm outdatedto see outdated dependencies based on the version range specified in thepackage.jsonfile. Npm 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 version3.1.4, 3 is the major version number, 1 is the minor version number and 4 is the patch version number. Please use anxto denote where packages can automatically bump their version. For example,3.1.xwill accept any patch version for3.1. Or3.xto accept any v3 minor version. -
Use
npm update [package]to upgrade a single named package to the version specified by the latest tag (potentially upgrading the package across major versions).
TypeScript
Learn about TypeScript
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.
TypeScript StyleGuide
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.
StyleGuide
nullvs.undefined- Do not use
x === nullunless you truly mean it. - Safer to use truthy or falsey checks instead. Ex:
if (x) {}
- Do not use
typevsinterface-
Use
typewhen you might need a union or intersection:type Foo = number | { someProperty: number } -
Use
interfacewhen you want extends or implements:interface FooBar extends Foo { bar: string;} -
Otherwise use whatever makes you happy that day.
-
- Namespace
PascalCase- Ex:
Shiny
Enforced (by eslint) StyleGuide
- Variable
camelCase- Ex:
const hello = "world
- Class
PascalCase- Ex:
class InputBinding {}
- Type, Interface definitions:
PascalCase- Ex:
type BindingBase = {name: string} - Ex:
interface ShinyEventMessage extends JQuery.Event {}
- Enum
PascalCase- (Currently unused)
- Single vs. Double Quotes
- While the JS community has decided on single quotes, R has decided on double quotes.
-
When you can't use double quotes, try using back ticks (`).
- Annotate Arrays as
Type[]- Ex:
Foo[](vsArray<Foo>)
- Ex:
- Annotate Records as
{[key: string]: valueType}- Ex:
const x: {[key: string]: number} = {a: 4} - Ex: Extend the unknown key definition with static keys:
const x: {known: string, [key: string]: number} = {known: "yes", a: 4}
- Ex:
- File Names
camelCase- Enforced byeslint
Config files
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.
.eslintrc.yml- Used with
eslintandprettierto determine how the TypeScript files should be formatted and which lint failures should cause warnings, errors, or be ignored.
- Used with
.prettierrc.yml- Used by
prettierto know how to adjust code when a file is saved in VSCode or withineslint's linting process.
- Used by
package.json- Contains useful scripts that can be run by
npmvianpm run SCRIPTNAME. - The scripts described below are inteded for developer use. All other scripts are means to an end.
- Contains useful scripts that can be run by
tsconfig.json-- TypeScript config file
- Notable options set:
target: ES2021- Compile to es2021.preserveConstEnums: false- Do no preserve enum values into the final code. (If true, produces bloat / unused code)isolatedModules: true&esModuleInterop: true- Requested byesbuild. This allows foresbuildto safely compile the files in parallel
Bundle TypeScript
esbuild is a build tool that (for Shiny's purposes) compiles the TypeScript into a single JavaScript file.
To run all build tasks, run:
npm run 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:
npm run 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.
Exported types
./extras/globalShiny.ts contains global declarations to define window.Shiny, a globally available Shiny variable, and a globally available ShinyClass 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.
GitHub Actions
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. 🎉)
Updating dependencies
External libraries
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/jquerybootstrap/@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 is not being updated anymore. Only bootstrap 3.4 will be utilized within shiny.js. To use the latest bootstrap, see
bootstrap-datepicker/@types/bootstrap-datepickerion-rangeslider/@types/ion-rangesliderselectize/@types/selectizestrftime