Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md

This file was deleted.

145 changes: 145 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <kbd>Ctrl-C</kbd> 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&trade; 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
12 changes: 11 additions & 1 deletion packages/polydev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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&trade; HTML & PHP, where
Expand Down Expand Up @@ -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]
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 = /\/+$/
Expand Down
30 changes: 30 additions & 0 deletions packages/typescript-example/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Request, Response } from "express"

let hits = 0

export default (req: Request, res: Response) => {
hits++

res.send(`
<link href="https://fonts.googleapis.com/css?family=Quicksand:300,500" rel="stylesheet">
<link href="/_polydev/styles.css" rel="stylesheet">

<div id="splash"></div>

<section>
<main>
<h1>
👋 Howdy from <kbd>TypeScript</kbd>
</h1>

<p>
<kbd>${hits}</kbd> ${hits ? "hits" : "hit"}
</p>
</main>

<footer>
<a href="/">&laquo; Back</a>
</footer>
</section>
`)
}
13 changes: 13 additions & 0 deletions packages/typescript-example/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
3 changes: 3 additions & 0 deletions routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ module.exports = (req, res) => {
<li>
<a href="/sse">Server-Sent Events (SSE)</a>
</li>
<li>
<a href="/typescript">TypeScript</a>
</li>
</ul>
</main>
</section>
Expand Down
1 change: 1 addition & 0 deletions routes/typescript/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("typescript-example")
Loading