From 9bad30fc1dcff70aa3ecb5a9bf6ea1af5fa96679 Mon Sep 17 00:00:00 2001 From: Eric Clemmons Date: Sun, 20 Jan 2019 18:04:02 -0800 Subject: [PATCH 1/3] Add typescript-example --- packages/typescript-example/index.ts | 30 ++++++ packages/typescript-example/package.json | 13 +++ routes/index.js | 3 + routes/typescript/index.ts | 1 + yarn.lock | 119 ++++++++++++++++++++++- 5 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 packages/typescript-example/index.ts create mode 100644 packages/typescript-example/package.json create mode 100644 routes/typescript/index.ts diff --git a/packages/typescript-example/index.ts b/packages/typescript-example/index.ts new file mode 100644 index 0000000..d971a03 --- /dev/null +++ b/packages/typescript-example/index.ts @@ -0,0 +1,30 @@ +import { Request, Response } from "express" + +let hits = 0 + +export default (req: Request, res: Response) => { + hits++ + + res.send(` + + + +
+ +
+
+

+ 👋 Howdy from TypeScript +

+ +

+ ${hits} ${hits ? "hits" : "hit"} +

+
+ + +
+ `) +} diff --git a/packages/typescript-example/package.json b/packages/typescript-example/package.json new file mode 100644 index 0000000..aec52e1 --- /dev/null +++ b/packages/typescript-example/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "name": "typescript-example", + "version": "0.0.0", + "devDependencies": { + "ts-node": "^7.0.1", + "tslint": "^5.12.1", + "typescript": "^3.2.4" + }, + "dependencies": { + "express": "^4.16.4" + } +} diff --git a/routes/index.js b/routes/index.js index b8384ca..18493a2 100644 --- a/routes/index.js +++ b/routes/index.js @@ -46,6 +46,9 @@ module.exports = (req, res) => {
  • Server-Sent Events (SSE)
  • +
  • + TypeScript +
  • diff --git a/routes/typescript/index.ts b/routes/typescript/index.ts new file mode 100644 index 0000000..8032653 --- /dev/null +++ b/routes/typescript/index.ts @@ -0,0 +1 @@ +module.exports = require("typescript-example") diff --git a/yarn.lock b/yarn.lock index 2f200a8..b249c25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1294,6 +1294,13 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -1452,6 +1459,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + babel-core@7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -1673,7 +1689,7 @@ browserslist@^4.1.0: electron-to-chromium "^1.3.92" node-releases "^1.1.1" -buffer-from@^1.0.0: +buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -1692,7 +1708,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0: +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -1903,6 +1919,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^2.12.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -2261,6 +2282,11 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" +diff@^3.1.0, diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -2406,6 +2432,11 @@ eslint-scope@^4.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -2795,7 +2826,7 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3: +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -3447,6 +3478,19 @@ js-levenshtein@^1.1.3: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.7.0: + version "3.12.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" + integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -3735,6 +3779,11 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5187,6 +5236,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.6: + version "0.5.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" + integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@~0.5.6: version "0.5.9" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" @@ -5248,6 +5305,11 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sshpk@^1.7.0: version "1.16.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de" @@ -5599,11 +5661,50 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -tslib@^1.9.0: +ts-node@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" + integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" + +tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslint@^5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" + integrity sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -5634,6 +5735,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d" + integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== + uglify-es@^3.3.4: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -6014,6 +6120,11 @@ yallist@^3.0.0, yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= + zen-observable-ts@^0.8.13: version "0.8.13" resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.13.tgz#ae1fd77c84ef95510188b1f8bca579d7a5448fc2" From 544e4cdc78fa6cbc3f60f6eb948fba9b582a1eab Mon Sep 17 00:00:00 2001 From: Eric Clemmons Date: Sun, 20 Jan 2019 18:04:16 -0800 Subject: [PATCH 2/3] Routes support .ts --- packages/polydev/src/middleware/router/createRouterFromFiles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/polydev/src/middleware/router/createRouterFromFiles.js b/packages/polydev/src/middleware/router/createRouterFromFiles.js index 0610873..887c799 100644 --- a/packages/polydev/src/middleware/router/createRouterFromFiles.js +++ b/packages/polydev/src/middleware/router/createRouterFromFiles.js @@ -4,7 +4,7 @@ const path = require("path") const handle = require("./handle") // Match index[.*|get|post].js -const REGEXP_INDEX = /^index(?:\.(\*|get|post))?\.js$/ +const REGEXP_INDEX = /^index(?:\.(\*|get|post))?\.(?:j|t)s$/ const REGEXP_PARAM = /\[([a-zA-Z0-9-]+)\]/g const REGEXP_PARAM_REPLACE = ":$1" const REGEXP_TRAILING_SLASH = /\/+$/ From aebd7ff3ec38e2a0497838111743a483f10bfdbe Mon Sep 17 00:00:00 2001 From: Eric Clemmons Date: Sun, 20 Jan 2019 18:12:01 -0800 Subject: [PATCH 3/3] Add docs for TypeScript --- README.md | 146 ++++++++++++++++++++++++++++++++++++- packages/polydev/README.md | 12 ++- 2 files changed, 156 insertions(+), 2 deletions(-) mode change 120000 => 100644 README.md diff --git a/README.md b/README.md deleted file mode 120000 index 1717ef8..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -packages/polydev/README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ace5280 --- /dev/null +++ b/README.md @@ -0,0 +1,145 @@ +![polydev](/logo.png) + +> Faster, route-centric development for [Node.js][node] apps with built-in +> [Hot Module Replacement][hmr]. +> +> ![Demo GIF](/polydev.gif) + +## Rationale + +As your project grows, **working on a large or monolithic [Node.js][node] app gets slower**: + +- Working on _part_ of the app means running the _entire_ app. +- The `require` tree grows so large it can take several seconds to start the server. +- Restarting the server on every change impedes development. +- Middleware for projects like [Next.js][next] & [Storybook][storybook] are expensive + to restart with each change. +- Tools like [concurrently][concurrently], [nodemon][nodemon], & [piping][piping] still + run the entire app. +- You shouldn't waste time in the terminal hitting Ctrl-C and restarting. + +## Features + +- Fast startup. +- [Hot Module Replacement][hmr] built-in. +- Run only the parts of your app that's requested. +- Supports [yarn workspaces][workspaces]. +- Pretty `404` screens with the option to create the missing route. +- Pretty `500` screens, so you spend less time in the terminal. +- Iterative adoption, so it's easy to get started. + +## Quick Started + +1. Install + + ```shell + yarn add polydev --dev + ``` + +2. Run `polydev` + + ```shell + yarn run polydev --open + ``` + +For customizing the `node` runtime, you can use `NODE_OPTIONS`. + +For example, [TypeScript][typescript] can be enabled via [ts-node][ts-node]: + +```shell +NODE_OPTIONS="--require ts-node/register" polydev +``` + +## Defining `routes` + +The `routes` folder is similar to Old-Time™ HTML & PHP, where +**folders mirror the URL structure**, followed by an `index.js` file: + +- `routes/` + + - `page/[id]/index.js` + + _Has access to `req.params.id` for [/page/123](http://localhost:3000/page/123)._ + + - `contact-us/` + + - `index.get.js` + - `index.post.js` + + - `posts/index.*.js` + + _Responds to both `GET` & `POST` for [/posts/\*](http://localhost:3000/posts)._ + + - `index.js` + + _Responds to both `GET` & `POST` for [/](http://localhost:3000/)._ + +### Route Handlers + +Route handlers can be any of the following: + +1. Functional middleware: + + ```js + module.exports = (req, res) => { + res.send("Howdy there!") + } + ``` + +2. Express apps: + + ```js + const express = require("express") + + module.exports = express().get("/", (req, res) => { + res.send(`Howdy from ${req.path}!`) + }) + ``` + +3. A [yarn workspace][workspaces] package: + + ```js + module.exports = require("my-package-name") + ``` + +4. A `package.json` path: + + ```js + module.exports = require.resolve("my-app/package.json") + ``` + + These are considered stand-alone apps that will be ran via `yarn dev` or `yarn start` (whichever exists) for development only. + + This is good for when you want to have a separate API server open on `process.env.PORT` that's not part of your application. + +## Roadmap + +- [ ] [Reload browser on double-save](/../../issues/1) +- [ ] [Loading screen, for slow routes](/../../issues/2) +- [ ] [Option to install missing modules](/../../issues/3) +- [ ] [ESM support](/../../issues/4) +- [x] [TypeScript support](/../../issues/5) +- [ ] [Better errors for broken routes/modules](/../../issues/6) +- [ ] [Storybook example](/../../issues/7) +- [ ] [View All][issues] + +## Contributing + +> See [CONTRIBUTING.md](/CONTRIBUTING.md). + +## Author + +- [Eric Clemmons][twitter] + +[concurrently]: https://github.com/kimmobrunfeldt/concurrently +[hmr]: https://github.com/sidorares/hot-module-replacement +[issues]: https://github.com/ericclemmons/polydev/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc +[next]: https://github.com/zeit/next.js/ +[node]: https://nodejs.org/ +[nodemon]: https://github.com/remy/nodemon +[piping]: https://www.npmjs.com/package/piping +[storybook]: https://github.com/storybooks/storybook +[ts-node]: https://github.com/TypeStrong/ts-node +[typescript]: https://www.typescriptlang.org/ +[twitter]: https://twitter.com/ericclemmons +[workspaces]: https://yarnpkg.com/en/docs/workspaces diff --git a/packages/polydev/README.md b/packages/polydev/README.md index 3c5cae4..ace5280 100644 --- a/packages/polydev/README.md +++ b/packages/polydev/README.md @@ -42,6 +42,14 @@ As your project grows, **working on a large or monolithic [Node.js][node] app ge yarn run polydev --open ``` +For customizing the `node` runtime, you can use `NODE_OPTIONS`. + +For example, [TypeScript][typescript] can be enabled via [ts-node][ts-node]: + +```shell +NODE_OPTIONS="--require ts-node/register" polydev +``` + ## Defining `routes` The `routes` folder is similar to Old-Time™ HTML & PHP, where @@ -110,7 +118,7 @@ Route handlers can be any of the following: - [ ] [Loading screen, for slow routes](/../../issues/2) - [ ] [Option to install missing modules](/../../issues/3) - [ ] [ESM support](/../../issues/4) -- [ ] [TypeScript support](/../../issues/5) +- [x] [TypeScript support](/../../issues/5) - [ ] [Better errors for broken routes/modules](/../../issues/6) - [ ] [Storybook example](/../../issues/7) - [ ] [View All][issues] @@ -131,5 +139,7 @@ Route handlers can be any of the following: [nodemon]: https://github.com/remy/nodemon [piping]: https://www.npmjs.com/package/piping [storybook]: https://github.com/storybooks/storybook +[ts-node]: https://github.com/TypeStrong/ts-node +[typescript]: https://www.typescriptlang.org/ [twitter]: https://twitter.com/ericclemmons [workspaces]: https://yarnpkg.com/en/docs/workspaces