TSdx

Zero-config CLI for TypeScript package development

README

tsdx
Blazing Fast Blazing Fast Blazing Fast Discord


Despite all the recent hype, setting up a new TypeScript (x React) library can be tough. Between Rollup, Jest,tsconfig, Yarn resolutions, ESLint, and getting VSCode to play nicely....there is just a whole lot of stuff to do (and things to screw up). TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages with ease--so you can focus on your awesome new library and not waste another afternoon on the configuration.





  - [npm start or yarn start](#npm-start-or-yarn-start)
  - [npm run build or yarn build](#npm-run-build-or-yarn-build)
  - [npm test or yarn test](#npm-test-or-yarn-test)
  - [npm run lint or yarn lint](#npm-run-lint-or-yarn-lint)
  - [prepare script](#prepare-script)
    - [Advanced babel-plugin-dev-expressions](#advanced-babel-plugin-dev-expressions)
      - [__DEV__](#__dev__)
      - [invariant](#invariant)
      - [warning](#warning)
  - Rollup
  - Babel
  - Jest
  - ESLint
  - [patch-package](#patch-package)
  - [tsdx watch](#tsdx-watch)
  - [tsdx build](#tsdx-build)
  - [tsdx test](#tsdx-test)
  - [tsdx lint](#tsdx-lint)



Features


TSDX comes with the "battery-pack included" and is part of a complete TypeScript breakfast:

- Bundles your code with Rollup and outputs multiple module formats (CJS & ESM by default, and also UMD if you want) plus development and production builds
- Comes with treeshaking, ready-to-rock lodash optimizations, and minification/compression
- Live reload / watch-mode
- Works with React
- Human readable error messages (and in VSCode-friendly format)
- Bundle size snapshots
- Opt-in to extract invariant error codes
- Jest test runner setup with sensible defaults via tsdx test
- ESLint with Prettier setup with sensible defaults via tsdx lint
- Zero-config, single dependency
- Escape hatches for customization via .babelrc.js, jest.config.js, .eslintrc.js, and tsdx.config.js

Quick Start


  1. ``` sh
  2. npx tsdx create mylib
  3. cd mylib
  4. yarn start
  5. ```

That's it. You don't need to worry about setting up TypeScript or Rollup or Jest or other plumbing. Just start editing src/index.ts and go!

Below is a list of commands you will probably find useful:

npm start or yarn start


Runs the project in development/watch mode. Your project will be rebuilt upon changes. TSDX has a special logger for your convenience. Error messages are pretty printed and formatted for compatibility VS Code's Problems tab.


Your library will be rebuilt if you make edits.

npm run build or yarn build


Bundles the package to the dist folder.
The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).


npm test or yarn test


Runs your tests using Jest.

npm run lint or yarn lint


Runs Eslint with Prettier on .ts and .tsx files.
If you want to customize eslint you can add an eslint block to your package.json, or you can run yarn lint --write-file and edit the generated .eslintrc.js file.

prepare script


Bundles and packages to the dist folder.
Runs automatically when you run either npm publish or yarn publish. The prepare script will run the equivalent of npm run build or yarn build. It will also be run if your module is installed as a git dependency (ie: "mymodule": "github:myuser/mymodule#some-branch") so it can be depended on without checking the transpiled code into git.

Optimizations


Aside from just bundling your module into different formats, TSDX comes with some optimizations for your convenience. They yield objectively better code and smaller bundle sizes.

After TSDX compiles your code with TypeScript, it processes your code with 3 Babel plugins:

- [babel-plugin-annotate-pure-calls](https://github.com/Andarist/babel-plugin-annotate-pure-calls): Injects for #__PURE annotations to enable treeshaking
- [babel-plugin-dev-expressions](https://github.com/4Catalyzer/babel-plugin-dev-expression): A mirror of Facebook's dev-expression Babel plugin. It reduces or eliminates development checks from production code
- [babel-plugin-rename-import](https://github.com/laat/babel-plugin-transform-rename-import): Used to rewrite any lodash imports

Development-only Expressions + Treeshaking


babel-plugin-annotate-pure-calls + babel-plugin-dev-expressions work together to fully eliminate dead code (aka treeshake) development checks from your production code. Let's look at an example to see how it works.

Imagine our source code is just this:

  1. ```tsx
  2. // ./src/index.ts
  3. export const sum = (a: number, b: number) => {
  4.   if (process.env.NODE_ENV !== 'production') {
  5.     console.log('Helpful dev-only error message');
  6.   }
  7.   return a + b;
  8. };
  9. ```

tsdx build will output an ES module file and 3 CommonJS files (dev, prod, and an entry file). If you want to specify a UMD build, you can do that as well. For brevity, let's examine the CommonJS output (comments added for emphasis):

  1. ``` js
  2. // Entry File
  3. // ./dist/index.js
  4. 'use strict';

  5. // This determines which build to use based on the `NODE_ENV` of your end user.
  6. if (process.env.NODE_ENV === 'production') {
  7.   module.exports = require('./mylib.cjs.production.js');
  8. } else {
  9.   module.exports = require('./mylib.development.cjs');
  10. }
  11. ```

  1. ``` js
  2. // CommonJS Development Build
  3. // ./dist/mylib.development.cjs
  4. 'use strict';

  5. const sum = (a, b) => {
  6.   {
  7.     console.log('Helpful dev-only error message');
  8.   }

  9.   return a + b;
  10. };

  11. exports.sum = sum;
  12. //# sourceMappingURL=mylib.development.cjs.map
  13. ```

  1. ``` js
  2. // CommonJS Production Build
  3. // ./dist/mylib.cjs.production.js
  4. 'use strict';
  5. exports.sum = (s, t) => s + t;
  6. //# sourceMappingURL=test-react-tsdx.cjs.production.js.map
  7. ```

AS you can see, TSDX stripped out the development check from the production code. This allows you to safely add development-only behavior (like more useful error messages) without any production bundle size impact.

For ESM build, it's up to end-user to build environment specific build with NODE_ENV replace (done by Webpack 4 automatically).

Rollup Treeshaking


TSDX's rollup config removes getters and setters on objects so that property access has no side effects. Don't do it.

Advanced babel-plugin-dev-expressions


TSDX will use babel-plugin-dev-expressions to make the following replacements _before_ treeshaking.

__DEV__

Replaces

  1. ```ts
  2. if (__DEV__) {
  3.   console.log('foo');
  4. }
  5. ```

with

  1. ``` js
  2. if (process.env.NODE_ENV !== 'production') {
  3.   console.log('foo');
  4. }
  5. ```

IMPORTANT: To use __DEV__ in TypeScript, you need to add declare var __DEV__: boolean somewhere in your project's type path (e.g. ./types/index.d.ts).

  1. ```ts
  2. // ./types/index.d.ts
  3. declare var __DEV__: boolean;
  4. ```

Note: The dev-expression transform does not run when NODE_ENV is test. As such, if you use __DEV__, you will need to define it as a global constant in your test environment.


invariant

Replaces

  1. ``` js
  2. invariant(condition, 'error message here');
  3. ```

with

  1. ``` js
  2. if (!condition) {
  3.   if ('production' !== process.env.NODE_ENV) {
  4.     invariant(false, 'error message here');
  5.   } else {
  6.     invariant(false);
  7.   }
  8. }
  9. ```

Note: TSDX doesn't supply an invariant function for you, you need to import one yourself. We recommend https://github.com/alexreardon/tiny-invariant.

To extract and minify invariant error codes in production into a static codes.json file, specify the --extractErrors flag in command line. For more details see Error extraction docs.

warning

Replaces

  1. ``` js
  2. warning(condition, 'dev warning here');
  3. ```

with

  1. ``` js
  2. if ('production' !== process.env.NODE_ENV) {
  3.   warning(condition, 'dev warning here');
  4. }
  5. ```

Note: TSDX doesn't supply a warning function for you, you need to import one yourself. We recommend https://github.com/alexreardon/tiny-warning.

Using lodash


If you want to use a lodash function in your package, TSDX will help you do it the _right_ way so that your library does not get fat shamed on Twitter. However, before you continue, seriously consider rolling whatever function you are about to use on your own. Anyways, here is how to do it right.

First, install lodash and lodash-es as _dependencies_

  1. ``` sh
  2. yarn add lodash lodash-es
  3. ```

Now install @types/lodash to your development dependencies.

  1. ``` sh
  2. yarn add @types/lodash --dev
  3. ```

Import your lodash method however you want, TSDX will optimize it like so.

  1. ```tsx
  2. // ./src/index.ts
  3. import kebabCase from 'lodash/kebabCase';

  4. export const KebabLogger = (msg: string) => {
  5.   console.log(kebabCase(msg));
  6. };
  7. ```

For brevity let's look at the ES module output.


  1. ``` js
  2. import o from"lodash-es/kebabCase";const e=e=>{console.log(o(e))};export{e as KebabLogger};
  3. //# sourceMappingURL=test-react-tsdx.esm.production.js.map
  4. ```

TSDX will rewrite your import kebabCase from 'lodash/kebabCase' to import o from 'lodash-es/kebabCase'. This allows your library to be treeshakable to end consumers while allowing to you to use @types/lodash for free.

Note: TSDX will also transform destructured imports. For example, import { kebabCase } from 'lodash' would have also been transformed to `import o from "lodash-es/kebabCase".


Error extraction


After running --extractErrors, you will have a ./errors/codes.json file with all your extracted invariant error codes. This process scans your production code and swaps out your invariant error message strings for a corresponding error code (just like React!). This extraction only works if your error checking/warning is done by a function called invariant.

Note: We don't provide this function for you, it is up to you how you want it to behave. For example, you can use either tiny-invariant or tiny-warning, but you must then import the module as a variable called invariant and it should have the same type signature.

⚠️Don't forget: you will need to host the decoder somewhere. Once you have a URL, look at ./errors/ErrorProd.js and replace the reactjs.org URL with yours.

Known issue: our transformErrorMessages babel plugin currently doesn't have sourcemap support, so you will see "Sourcemap is likely to be incorrect" warnings. We would love your help on this.


_TODO: Simple guide to host error codes to be completed_

Customization


Rollup


❗⚠️❗ Warning: <br>

These modifications will override the default behavior and configuration of TSDX. As such they can invalidate internal guarantees and assumptions. These types of changes can break internal behavior and can be very fragile against updates. Use with discretion!


TSDX uses Rollup under the hood. The defaults are solid for most packages (Formik uses the defaults!). However, if you do wish to alter the rollup configuration, you can do so by creating a file called tsdx.config.js at the root of your project like so:

  1. ``` js
  2. // Not transpiled with TypeScript or Babel, so use plain Es6/Node.js!
  3. module.exports = {
  4.   // This function will run for each entry/format/env combination
  5.   rollup(config, options) {
  6.     return config; // always return a config.
  7.   },
  8. };
  9. ```

The options object contains the following:

  1. ```tsx
  2. export interface TsdxOptions {
  3.   // path to file
  4.   input: string;
  5.   // Name of package
  6.   name: string;
  7.   // JS target
  8.   target: 'node' | 'browser';
  9.   // Module format
  10.   format: 'cjs' | 'umd' | 'esm' | 'system';
  11.   // Environment
  12.   env: 'development' | 'production';
  13.   // Path to tsconfig file
  14.   tsconfig?: string;
  15.   // Is error extraction running?
  16.   extractErrors?: boolean;
  17.   // Is minifying?
  18.   minify?: boolean;
  19.   // Is this the very first rollup config (and thus should one-off metadata be extracted)?
  20.   writeMeta?: boolean;
  21.   // Only transpile, do not type check (makes compilation faster)
  22.   transpileOnly?: boolean;
  23. }
  24. ```

Example: Adding Postcss


  1. ``` js
  2. const postcss = require('rollup-plugin-postcss');
  3. const autoprefixer = require('autoprefixer');
  4. const cssnano = require('cssnano');

  5. module.exports = {
  6.   rollup(config, options) {
  7.     config.plugins.push(
  8.       postcss({
  9.         plugins: [
  10.           autoprefixer(),
  11.           cssnano({
  12.             preset: 'default',
  13.           }),
  14.         ],
  15.         inject: false,
  16.         // only write out CSS for the first bundle (avoids pointless extra files):
  17.         extract: !!options.writeMeta,
  18.       })
  19.     );
  20.     return config;
  21.   },
  22. };
  23. ```

Babel


You can add your own .babelrc to the root of your project and TSDX will merge it with its own Babel transforms (which are mostly for optimization), putting any new presets and plugins at the end of its list.

Jest


You can add your own jest.config.js to the root of your project and TSDX will shallow merge it with its own Jest config.

ESLint


You can add your own .eslintrc.js to the root of your project and TSDX will deep merge it with its own ESLint config.

patch-package


If you still need more customizations, we recommend using [patch-package](https://github.com/ds300/patch-package) so you don't need to fork.
Keep in mind that these types of changes may be quite fragile against version updates.

Inspiration


TSDX was originally ripped out of Formik's build tooling.
TSDX has several similarities to @developit/microbundle, but that is because Formik's Rollup configuration and Microbundle's internals had converged around similar plugins.

Comparison with Microbundle


Some key differences include:

- TSDX includes out-of-the-box test running via Jest
- TSDX includes out-of-the-box linting and formatting via ESLint and Prettier
- TSDX includes a bootstrap command with a few package templates
- TSDX allows for some lightweight customization
- TSDX is TypeScript focused, but also supports plain JavaScript
- TSDX outputs distinct development and production builds (like React does) for CJS and UMD builds. This means you can include rich error messages and other dev-friendly goodies without sacrificing final bundle size.

API Reference


tsdx watch


  1. ``` sh
  2. Description
  3.   Rebuilds on any change

  4. Usage
  5.   $ tsdx watch [options]

  6. Options
  7.   -i, --entry           Entry module
  8.   --target              Specify your target environment  (default web)
  9.   --name                Specify name exposed in UMD builds
  10.   --format              Specify module format(s)  (default cjs,esm)
  11.   --tsconfig            Specify your custom tsconfig path (default <root-folder>/tsconfig.json)
  12.   --verbose             Keep outdated console output in watch mode instead of clearing the screen
  13.   --onFirstSuccess      Run a command on the first successful build
  14.   --onSuccess           Run a command on a successful build
  15.   --onFailure           Run a command on a failed build
  16.   --noClean             Don't clean the dist folder
  17.   --transpileOnly       Skip type checking
  18.   -h, --help            Displays this message

  19. Examples
  20.   $ tsdx watch --entry src/foo.tsx
  21.   $ tsdx watch --target node
  22.   $ tsdx watch --name Foo
  23.   $ tsdx watch --format cjs,esm,umd
  24.   $ tsdx watch --tsconfig ./tsconfig.foo.json
  25.   $ tsdx watch --noClean
  26.   $ tsdx watch --onFirstSuccess "echo The first successful build!"
  27.   $ tsdx watch --onSuccess "echo Successful build!"
  28.   $ tsdx watch --onFailure "echo The build failed!"
  29.   $ tsdx watch --transpileOnly
  30. ```

tsdx build


  1. ``` sh
  2. Description
  3.   Build your project once and exit

  4. Usage
  5.   $ tsdx build [options]

  6. Options
  7.   -i, --entry           Entry module
  8.   --target              Specify your target environment  (default web)
  9.   --name                Specify name exposed in UMD builds
  10.   --format              Specify module format(s)  (default cjs,esm)
  11.   --extractErrors       Opt-in to extracting invariant error codes
  12.   --tsconfig            Specify your custom tsconfig path (default <root-folder>/tsconfig.json)
  13.   --transpileOnly       Skip type checking
  14.   -h, --help            Displays this message

  15. Examples
  16.   $ tsdx build --entry src/foo.tsx
  17.   $ tsdx build --target node
  18.   $ tsdx build --name Foo
  19.   $ tsdx build --format cjs,esm,umd
  20.   $ tsdx build --extractErrors
  21.   $ tsdx build --tsconfig ./tsconfig.foo.json
  22.   $ tsdx build --transpileOnly
  23. ```

tsdx test


This runs Jest, forwarding all CLI flags to it. See https://jestjs.io for options. For example, if you would like to run in watch mode, you can runtsdx test --watch. So you could set up your package.json scripts like:

  1. ``` json
  2. {
  3.   "scripts": {
  4.     "test": "tsdx test",
  5.     "test:watch": "tsdx test --watch",
  6.     "test:coverage": "tsdx test --coverage"
  7.   }
  8. }
  9. ```

tsdx lint


  1. ``` sh
  2. Description
  3.   Run eslint with Prettier

  4. Usage
  5.   $ tsdx lint [options]

  6. Options
  7.   --fix               Fixes fixable errors and warnings
  8.   --ignore-pattern    Ignore a pattern
  9.   --max-warnings      Exits with non-zero error code if number of warnings exceed this number  (default Infinity)
  10.   --write-file        Write the config file locally
  11.   --report-file       Write JSON report to file locally
  12.   -h, --help          Displays this message

  13. Examples
  14.   $ tsdx lint src
  15.   $ tsdx lint src --fix
  16.   $ tsdx lint src test --ignore-pattern test/foo.ts
  17.   $ tsdx lint src test --max-warnings 10
  18.   $ tsdx lint src --write-file
  19.   $ tsdx lint src --report-file report.json
  20. ```

Contributing


Please see the Contributing Guidelines.

Author



License



Contributors ✨


Thanks goes to these wonderful people (emoji key):





Jared Palmer

📖 🎨 👀 🔧 ⚠️ 🚧 💻

swyx

🐛 💻 📖 🎨 🤔 🚇 🚧 👀

Jason Etcovitch

🐛 ⚠️

Sam Kvale

💻 ⚠️ 🐛 📖 👀 🤔 💬

Lucas Polito

💻 📖 💬

Steven Kalt

💻

Harry Hedger

🤔 📖 💻 💬

Arthur Denner

🐛 💻 💬

Carl

🤔 📖 💻 ⚠️ 💬

Loïc Mahieu

💻 ⚠️

Sebastian Sebald

📖 💻 ⚠️

Karl Horky

📖 🤔

James George

📖

Anton Gilgur

🚧 📖 💻 🐛 💡 🤔 💬 👀 ⚠️

Kyle Holmberg

💻 💡 ⚠️ 👀 💬

Sigurd Spieckermann

🐛 💻

Kristofer Giltvedt Selbekk

💻

Tomáš Ehrlich

🐛 💻

Kyle Johnson

🐛 💻

Etienne Dldc

🐛 💻 ⚠️

Florian Knop

🐛

Gonzalo D'Elia

💻

Alec Larson

💻 👀 🤔 💬

Justin Grant

🐛 🤔 💬

Jirat Ki.

💻 ⚠️ 🐛

Nate Moore

💻 🤔

Haz

📖

Basti Buck

💻 🐛

Pablo Saez

💻 🐛

Jake Gavin

🐛 💻

Grant Forrest

💻 ⚠️ 🐛

Sébastien Lorber

💻

Kirils Ladovs

📖

Enes Tüfekçi

💻 📖

Bogdan Chadkin

👀 💬 🤔

Daniel K.

💻 📖 ⚠️ 🤔 🐛

Quentin Sommer

📖

Hyan Mandian

💻 ⚠️

Sung M. Kim

🐛 💻

John Johnson

💻 📖

Jun Tomioka

💻 ⚠️

Leonardo Dino

💻 🐛

Honza Břečka

💻 🐛

Ward Loos

💻 🤔

Brian Bugh

💻 🐛

Cody Carse

📖

Josh Biddick

💻

Jose Albizures

💻 ⚠️ 🐛

Rahel Lüthy

📖

Michael Edelman

💻 🤔

Charlike Mike Reagent

👀 💻 🤔

Frederik Wessberg

💬

Elad Ossadon

💻 ⚠️ 🐛

Kevin Kipp

💻

Matija Folnovic

💻 📖

Andrew

💻

Ryan Castner

💻 ⚠️ 🤔

Yordis Prieto

💻

NCPhillips

📖

Arnaud Barré

💻 📖

Peter W

📖

Joe Flateau

💻 📖

H.John Choi

📖

Jon Stevens

📖 🤔 🐛

greenkeeper[bot]

🚇 💻

allcontributors[bot]

🚇 📖

dependabot[bot]

🚇 🛡️ 💻

GitHub

🚇

Eugene Samonenko

⚠️ 💡 💬 🤔

Joseph Wang

🐛

Kotaro Sugawara

🐛 💻

Semesse

💻

Bojan Mihelac

💻

Dan Dascalescu

📖

Yuriy Burychka

💻

Jesse Hoyos

💻

Mike Deverell

💻

Nick Hehr

💻 📖 💡

Bnaya Peretz

🐛 💻

Andres Alvarez

💻 📖 💡

Yaroslav K.

📖

Dragoș Străinu

🤔

George Varghese M.

💻 📖 ⚠️

Reinis Ivanovs

🤔 💬

Orta Therox

💬 📖

Martijn Saly

🐛

Alex Johansson

📖

hb-seb

💻

seungdols

🐛

Béré Cyriac

🐛

Dmitriy Serdtsev

🐛

Vladislav Moiseev

💻

Felix Mosheev

🐛 📖

Ludovico Fischer

💻

Altrim Beqiri

🐛 💻 ⚠️

Tane Morgan

🐛 💻





This project follows the all-contributors specification. Contributions of any kind welcome!