express-generator-typescript 2.8.1 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -18
- package/bin/cli.js +91 -32
- package/lib/express-generator-typescript.js +125 -151
- package/lib/{project-files → template}/.prettierignore +2 -4
- package/lib/template/README.md +50 -0
- package/lib/template/bs-config.js +8 -0
- package/lib/{project-files → template}/config/.env.development +4 -6
- package/lib/{project-files → template}/config/.env.production +4 -5
- package/lib/{project-files → template}/config/.env.test +4 -5
- package/lib/{project-files → template}/eslint.config.ts +4 -0
- package/lib/template/gitignore +10 -0
- package/lib/{project-files → template}/package.json +31 -36
- package/lib/{project-files → template}/src/common/constants/HttpStatusCodes.ts +12 -8
- package/lib/template/src/common/constants/Paths.ts +22 -0
- package/lib/template/src/common/constants/env-inv.ts +31 -0
- package/lib/template/src/common/types/utility-types.ts +5 -0
- package/lib/template/src/common/utils/date-utils.ts +29 -0
- package/lib/template/src/common/utils/route-errors.ts +37 -0
- package/lib/template/src/main.ts +24 -0
- package/lib/template/src/models/User.model.ts +95 -0
- package/lib/template/src/models/common/types.ts +10 -0
- package/lib/template/src/public/scripts/http.js +25 -0
- package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +4229 -0
- package/lib/template/src/public/scripts/renderUsers.js +90 -0
- package/lib/template/src/public/scripts/users.js +142 -0
- package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +11798 -0
- package/lib/template/src/repos/MockOrm.ts +71 -0
- package/lib/{project-files → template}/src/repos/UserRepo.ts +21 -35
- package/lib/template/src/repos/common/database.json +28 -0
- package/lib/{project-files → template}/src/routes/UserRoutes.ts +15 -18
- package/lib/template/src/routes/apiRouter.ts +28 -0
- package/lib/template/src/routes/common/express-types.ts +9 -0
- package/lib/template/src/routes/common/parseReq.ts +23 -0
- package/lib/template/src/server.ts +78 -0
- package/lib/template/src/services/UserService.ts +66 -0
- package/lib/{project-files → template}/src/views/users.html +13 -6
- package/lib/{project-files → template}/tests/common/comparators.ts +6 -6
- package/lib/template/tests/common/supertest-types.ts +11 -0
- package/lib/template/tests/frontend.test.ts +33 -0
- package/lib/template/tests/support/agent.ts +23 -0
- package/lib/template/tests/users.test.ts +205 -0
- package/lib/{project-files → template}/tsconfig.json +1 -3
- package/lib/{project-files → template}/tsconfig.prod.json +3 -9
- package/lib/{project-files → template}/vitest.config.mts +2 -0
- package/package.json +65 -52
- package/lib/cli.js +0 -33
- package/lib/project-files/README.md +0 -39
- package/lib/project-files/gitignore +0 -4
- package/lib/project-files/src/common/constants/Paths.ts +0 -15
- package/lib/project-files/src/common/constants/env.ts +0 -28
- package/lib/project-files/src/common/types/structure-utils.ts +0 -2
- package/lib/project-files/src/common/utils/number-utils.ts +0 -10
- package/lib/project-files/src/common/utils/route-errors.ts +0 -35
- package/lib/project-files/src/common/utils/validators.ts +0 -14
- package/lib/project-files/src/main.ts +0 -24
- package/lib/project-files/src/models/User.model.ts +0 -72
- package/lib/project-files/src/models/common/types.ts +0 -4
- package/lib/project-files/src/public/stylesheets/lib/bootstrap.min.css +0 -6
- package/lib/project-files/src/repos/MockOrm.ts +0 -63
- package/lib/project-files/src/repos/common/database.json +0 -1
- package/lib/project-files/src/repos/common/database.test.json +0 -1
- package/lib/project-files/src/routes/apiRouter.ts +0 -28
- package/lib/project-files/src/routes/common/express-types.ts +0 -8
- package/lib/project-files/src/routes/common/parseReq.ts +0 -19
- package/lib/project-files/src/server.ts +0 -73
- package/lib/project-files/src/services/UserService.ts +0 -64
- package/lib/project-files/tests/common/error-utils.ts +0 -25
- package/lib/project-files/tests/common/supertest-types.ts +0 -15
- package/lib/project-files/tests/support/agent.ts +0 -23
- package/lib/project-files/tests/users.test.ts +0 -131
- /package/lib/{project-files → template}/.prettierrc.json +0 -0
- /package/lib/{project-files → template}/src/public/stylesheets/users.css +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,198 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/main/assets/express-typescript.png" width="420">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# express-generator-typescript
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
8
|
+
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
9
|
+
[](https://github.com/seanpmaxwell/express-generator-typescript/blob/main/LICENSE)
|
|
10
|
+
|
|
11
|
+
Command line tool which generates production-ready express templates with TypeScript baked in. Spin up a web server in seconds that follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices/blob/main/README.md).
|
|
12
|
+
|
|
13
|
+
<p align="center">· · ·</p>
|
|
14
|
+
|
|
15
|
+
## 🧭 Overview
|
|
16
|
+
|
|
17
|
+
`express-generator-typescript` creates a new Express application similar to the classic `express-generator` package, but the generated project is fully wired for TypeScript. You get strict typing, linting, hot reloading, production builds, testing utilities, and sane defaults that focus on APIs (no view engine or opinionated ORM). Path aliases are preconfigured through `tsconfig-paths` and `_moduleAliases`, so referencing modules stays clean even as the app grows.
|
|
18
|
+
|
|
19
|
+
<p align="center">· · ·</p>
|
|
20
|
+
|
|
21
|
+
## ✨ Features
|
|
22
|
+
|
|
23
|
+
- **TypeScript-first** – strict compiler settings, linting, and sensible tsconfig defaults ready to go.
|
|
24
|
+
- **API-centric** – no view engine or extra dependencies; ideal for SPAs, mobile backends, or services.
|
|
25
|
+
- **Productivity tooling** – includes nodemon, ts-node, hot reload scripts, Vitest, ESLint, and production builds.
|
|
26
|
+
- **Path aliases** – `@src/*` aliases configured in `tsconfig.json` (and `_moduleAliases` in `package.json` for production) so you can import modules cleanly.
|
|
27
|
+
- **Keeps dependencies lean** – no bundled ORM or UI layers; only the essentials for Express + TS development.
|
|
28
|
+
|
|
29
|
+
<p align="center">· · ·</p>
|
|
30
|
+
|
|
31
|
+
## 📦 Installation
|
|
32
|
+
|
|
33
|
+
Requires Node.js 22.12 or newer.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx express-generator-typescript
|
|
37
|
+
# or install globally
|
|
38
|
+
npm install -g express-generator-typescript
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
<p align="center">· · ·</p>
|
|
42
|
+
|
|
43
|
+
## ⚡ Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# generate a project (defaults to express-gen-ts)
|
|
47
|
+
npx express-generator-typescript my-api
|
|
48
|
+
|
|
49
|
+
cd my-api
|
|
50
|
+
|
|
51
|
+
# start developing at http://localhost:3000
|
|
52
|
+
npm run dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `--use-yarn` if you prefer Yarn over npm. If you omit the project name, the generator creates `express-gen-ts`.
|
|
56
|
+
|
|
57
|
+
<p align="center">· · ·</p>
|
|
58
|
+
|
|
59
|
+
## 🖥️ CLI Options
|
|
60
|
+
|
|
61
|
+
| Option | Description |
|
|
62
|
+
| ----------------- | --------------------------------------------------------------------------- |
|
|
63
|
+
| `project name` | Folder to create. Defaults to `express-gen-ts` if omitted. |
|
|
64
|
+
| `--use-yarn` | Installs dependencies with Yarn instead of npm. |
|
|
65
|
+
| `--force` | Writes into the target folder even if it is not empty (existing files with the same names are overwritten). |
|
|
66
|
+
| `-h`, `--help` | Shows usage. |
|
|
67
|
+
| `-v`, `--version` | Shows the generator version. |
|
|
68
|
+
|
|
69
|
+
> The generator refuses to write into a folder that already has files in it, so it can't overwrite your work by accident.
|
|
70
|
+
|
|
71
|
+
<p align="center">· · ·</p>
|
|
72
|
+
|
|
73
|
+
## 🧩 Generated Template
|
|
74
|
+
|
|
75
|
+
The generated template is a CRUD app for the `User` record to demonstrate model, services, and routing patterns in Express + TypeScript. Commands for linting, transpiling, formatting, and hot-reloading are all configured for you.
|
|
76
|
+
|
|
77
|
+
### Available `package.json` Scripts
|
|
78
|
+
|
|
79
|
+
- `npm run dev` – Run the server in dev mode with live reload and browser refresh.
|
|
80
|
+
- `npm run test` - Run tests with vitest.
|
|
81
|
+
- `npm run test -- users.test.ts` – Target a single test file.
|
|
82
|
+
- `npm run lint` – Run ESLint checks.
|
|
83
|
+
- `npm run format` - Run prettier.
|
|
84
|
+
- `npm run build` – Compile the project for production.
|
|
85
|
+
- `npm start` – Serve the built project.
|
|
86
|
+
- `npm run typecheck` – Run the TypeScript compiler without emitting files.
|
|
87
|
+
- `npm run install:clean` – Delete `node_modules` and the lockfile, then reinstall.
|
|
88
|
+
|
|
89
|
+
### Architecture
|
|
90
|
+
|
|
91
|
+
Because this is a small CRUD app, **layered** is the architectural pattern of choice. However, you should consider switching to a **domain-based** layout if you plan on scaling. There is a good tutorial [here](https://github.com/seanpmaxwell/Typescript-Best-Practices/tree/main?tab=readme-ov-file#architecture) in the _Typescript Best Practices README_ about architectural patterns with TypeScript.
|
|
92
|
+
|
|
93
|
+
Layers explained:
|
|
94
|
+
```yml
|
|
95
|
+
- src/ <-- source code
|
|
96
|
+
- common/
|
|
97
|
+
- constants/
|
|
98
|
+
- Paths.ts <-- Single source of truth for all API routes
|
|
99
|
+
- routes/ <-- extracting and validating values from express Request/Response objects
|
|
100
|
+
- services/ <-- Business logic (where everything comes together)
|
|
101
|
+
- repos/ <-- Talking to the database layer
|
|
102
|
+
- models/ <-- For describing/handling objects representing database records
|
|
103
|
+
- tests/ <-- unit-tests
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
<p align="center">· · ·</p>
|
|
107
|
+
|
|
108
|
+
## Notes for VSCode users
|
|
109
|
+
|
|
110
|
+
<details>
|
|
111
|
+
<summary>Format on save</summary>
|
|
112
|
+
|
|
113
|
+
The generated template uses `eslint`+`prettier`, so if you want features like _formatting on save_, you need to make sure to install the prettier extension for VSCode and set it as your default formatter in `.vscode/setting.json`:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
// .vscode/settings.json
|
|
117
|
+
{
|
|
118
|
+
"editor.minimap.enabled": false,
|
|
119
|
+
"editor.rulers": [80],
|
|
120
|
+
"editor.tabSize": 2,
|
|
121
|
+
|
|
122
|
+
"workbench.sideBar.location": "right",
|
|
123
|
+
"workbench.editor.empty.hint": "hidden",
|
|
124
|
+
|
|
125
|
+
// Formatting: Prettier only
|
|
126
|
+
"editor.formatOnSave": true,
|
|
127
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
128
|
+
|
|
129
|
+
// ESLint: linting only (NO formatting)
|
|
130
|
+
"eslint.format.enable": false,
|
|
131
|
+
"eslint.nodePath": "node_modules",
|
|
132
|
+
"eslint.validate": ["javascript", "typescript", "typescriptreact"],
|
|
133
|
+
|
|
134
|
+
// Run ESLint fixes (non-formatting) on save
|
|
135
|
+
"editor.codeActionsOnSave": {
|
|
136
|
+
"source.fixAll.eslint": "explicit"
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
// Language overrides (keep Prettier)
|
|
140
|
+
"[javascript]": {
|
|
141
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
142
|
+
},
|
|
143
|
+
"[typescript]": {
|
|
144
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
145
|
+
},
|
|
146
|
+
"[json]": {
|
|
147
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
// JSDoc noise reduction
|
|
151
|
+
"javascript.suggest.completeJSDocs": false,
|
|
152
|
+
"javascript.suggest.jsdoc.generateReturns": false,
|
|
153
|
+
"typescript.suggest.completeJSDocs": false,
|
|
154
|
+
"typescript.suggest.jsdoc.generateReturns": false
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
</details>
|
|
159
|
+
|
|
160
|
+
<details>
|
|
161
|
+
<summary>Debugging</summary>
|
|
162
|
+
|
|
163
|
+
If you want to debug in VSCode with breakpoints you need to start the processes through `.vscode/launch.json`:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
// .vscode/launch.json
|
|
167
|
+
{
|
|
168
|
+
"version": "0.2.0",
|
|
169
|
+
"configurations": [
|
|
170
|
+
{
|
|
171
|
+
"name": "Dev - ts-node",
|
|
172
|
+
"type": "node",
|
|
173
|
+
"request": "launch",
|
|
174
|
+
"runtimeExecutable": "npm",
|
|
175
|
+
"runtimeArgs": ["run", "dev"],
|
|
176
|
+
"skipFiles": ["<node_internals>/**"],
|
|
177
|
+
"console": "integratedTerminal"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"name": "Test - Vitest",
|
|
181
|
+
"type": "node",
|
|
182
|
+
"request": "launch",
|
|
183
|
+
"runtimeExecutable": "npm",
|
|
184
|
+
"runtimeArgs": ["run", "test"],
|
|
185
|
+
"skipFiles": ["<node_internals>/**"],
|
|
186
|
+
"console": "integratedTerminal"
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
</details>
|
|
193
|
+
|
|
194
|
+
<p align="center">· · ·</p>
|
|
195
|
+
|
|
196
|
+
## 📄 License
|
|
197
|
+
|
|
198
|
+
MIT © [seanpmaxwell1](LICENSE)
|
package/bin/cli.js
CHANGED
|
@@ -1,33 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { parseArgs } = require('util');
|
|
4
|
+
|
|
5
|
+
const expressGenTs = require('../lib/express-generator-typescript');
|
|
6
|
+
const { version, engines } = require('../package.json');
|
|
7
|
+
|
|
8
|
+
// ========================================================================= //
|
|
9
|
+
// CONSTANTS //
|
|
10
|
+
// ========================================================================= //
|
|
11
|
+
|
|
12
|
+
const DEFAULT_DEST = 'express-gen-ts';
|
|
13
|
+
const MIN_NODE = engines.node.replace(/^>=/, '');
|
|
14
|
+
|
|
15
|
+
const HELP = `Usage: express-generator-typescript [project-name] [options]
|
|
16
|
+
|
|
17
|
+
Creates a new Express + TypeScript project (default name: "${DEFAULT_DEST}").
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--use-yarn Install dependencies with yarn instead of npm
|
|
21
|
+
--force Write into the target folder even if it is not empty
|
|
22
|
+
-h, --help Show this help
|
|
23
|
+
-v, --version Show the version`;
|
|
24
|
+
|
|
25
|
+
// ========================================================================= //
|
|
26
|
+
// EXEC //
|
|
27
|
+
// ========================================================================= //
|
|
28
|
+
|
|
29
|
+
main().catch((err) => {
|
|
30
|
+
console.error('\nProject setup failed: ' + err.message);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ========================================================================= //
|
|
35
|
+
// FUNCTIONS //
|
|
36
|
+
// ========================================================================= //
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse the command-line args and hand off to `expressGenTs`.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
async function main() {
|
|
44
|
+
// ---- Parse command-line-arguments
|
|
45
|
+
const { values, positionals } = parseArgs({
|
|
46
|
+
allowPositionals: true,
|
|
47
|
+
options: {
|
|
48
|
+
'use-yarn': { type: 'boolean', default: false },
|
|
49
|
+
force: { type: 'boolean', default: false },
|
|
50
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
51
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
if (values.help) {
|
|
55
|
+
console.log(HELP);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (values.version) {
|
|
59
|
+
console.log(version);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (positionals.length > 1) {
|
|
63
|
+
throw new Error('Expected at most one project name.\n\n' + HELP);
|
|
64
|
+
}
|
|
65
|
+
assertNodeVersion();
|
|
66
|
+
|
|
67
|
+
// ---- Run "express-generator-typescript"
|
|
68
|
+
const dest = path.resolve(process.cwd(), positionals[0] ?? DEFAULT_DEST);
|
|
69
|
+
console.log('Setting up new Express/TypeScript project in ' + dest);
|
|
70
|
+
await expressGenTs(dest, {
|
|
71
|
+
useYarn: values['use-yarn'],
|
|
72
|
+
force: values.force,
|
|
73
|
+
});
|
|
74
|
+
console.log('Project setup complete!');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The generated project's tooling (vitest, eslint) requires a recent Node, so
|
|
79
|
+
* fail fast instead of producing a project that can't run.
|
|
80
|
+
*
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
function assertNodeVersion() {
|
|
84
|
+
const toParts = (v) => v.split('.').map(Number);
|
|
85
|
+
const [maj, min] = toParts(process.versions.node);
|
|
86
|
+
const [reqMaj, reqMin] = toParts(MIN_NODE);
|
|
87
|
+
if (maj < reqMaj || (maj === reqMaj && min < reqMin)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Node ${MIN_NODE} or newer is required (found ${process.versions.node}).`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,151 +1,125 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
*/
|
|
127
|
-
function downloadNodeModules(destination, useYarn) {
|
|
128
|
-
const options = { cwd: destination };
|
|
129
|
-
// Setup dependencies string
|
|
130
|
-
let depStr = DEPENDENCIES.join(' '),
|
|
131
|
-
devDepStr = DEV_DEPENDENCIES.join(' ');
|
|
132
|
-
// Setup download command
|
|
133
|
-
let downloadLibCmd,
|
|
134
|
-
downloadDepCmd;
|
|
135
|
-
if (useYarn) {
|
|
136
|
-
downloadLibCmd = 'yarn add ' + depStr;
|
|
137
|
-
downloadDepCmd = 'yarn add ' + devDepStr + ' -D';
|
|
138
|
-
} else {
|
|
139
|
-
downloadLibCmd = 'npm i -s ' + depStr;
|
|
140
|
-
downloadDepCmd = 'npm i -D ' + devDepStr;
|
|
141
|
-
}
|
|
142
|
-
// Execute command
|
|
143
|
-
childProcess.execSync(downloadLibCmd, options);
|
|
144
|
-
childProcess.execSync(downloadDepCmd, options);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/******************************************************************************
|
|
148
|
-
Export
|
|
149
|
-
******************************************************************************/
|
|
150
|
-
|
|
151
|
-
module.exports = expressGenTs;
|
|
1
|
+
const childProcess = require('child_process');
|
|
2
|
+
const fs = require('fs/promises');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// ========================================================================= //
|
|
6
|
+
// CONSTANTS //
|
|
7
|
+
// ========================================================================= //
|
|
8
|
+
|
|
9
|
+
const TEMPLATE_DIR = path.join(__dirname, 'template');
|
|
10
|
+
|
|
11
|
+
// Local build/editor artifacts that only exist when running from a checkout.
|
|
12
|
+
const EXCLUDED_NAMES = new Set([
|
|
13
|
+
'node_modules',
|
|
14
|
+
'package-lock.json',
|
|
15
|
+
'dist',
|
|
16
|
+
'tsconfig.tsbuildinfo',
|
|
17
|
+
'.vscode',
|
|
18
|
+
'database.test.json',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
// ========================================================================= //
|
|
22
|
+
// FUNCTIONS //
|
|
23
|
+
// ========================================================================= //
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Entry point: scaffold a new project at `dest` and install its dependencies.
|
|
27
|
+
* Rejects on any failure so callers can report it and exit non-zero.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} dest - Absolute path to create the project in.
|
|
30
|
+
* @param {object} [options]
|
|
31
|
+
* @param {boolean} [options.useYarn] - Install with yarn instead of npm.
|
|
32
|
+
* @param {boolean} [options.force] - Allow writing into a non-empty folder.
|
|
33
|
+
* @param {boolean} [options.skipInstall] - Don't install dependencies.
|
|
34
|
+
* @returns {Promise<void>}
|
|
35
|
+
*/
|
|
36
|
+
async function expressGenTs(dest, options = {}) {
|
|
37
|
+
const { useYarn = false, force = false, skipInstall = false } = options;
|
|
38
|
+
if (!force) {
|
|
39
|
+
await assertEmptyOrMissing(dest);
|
|
40
|
+
}
|
|
41
|
+
await copyProjectFiles(dest);
|
|
42
|
+
await setPackageName(dest);
|
|
43
|
+
await renameGitignoreFile(dest);
|
|
44
|
+
if (!skipInstall) {
|
|
45
|
+
installDependencies(dest, useYarn);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Throw if `dest` exists and has any contents, so existing work is never
|
|
51
|
+
* overwritten.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} dest
|
|
54
|
+
* @returns {Promise<void>}
|
|
55
|
+
*/
|
|
56
|
+
async function assertEmptyOrMissing(dest) {
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = await fs.readdir(dest);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err.code === 'ENOENT') return;
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
if (entries.length > 0) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`"${dest}" already exists and is not empty. Choose another name or ` +
|
|
67
|
+
'pass --force to write into it anyway.',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Copy the `template` folder into `dest`.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} dest
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
function copyProjectFiles(dest) {
|
|
79
|
+
return fs.cp(TEMPLATE_DIR, dest, {
|
|
80
|
+
recursive: true,
|
|
81
|
+
filter: (src) => !EXCLUDED_NAMES.has(path.basename(src)),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Name the new project after its folder.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} dest
|
|
89
|
+
* @returns {Promise<void>}
|
|
90
|
+
*/
|
|
91
|
+
async function setPackageName(dest) {
|
|
92
|
+
const pkgPath = path.join(dest, 'package.json');
|
|
93
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
|
|
94
|
+
pkg.name = path.basename(dest);
|
|
95
|
+
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Rename the template's `gitignore` to `.gitignore`, because npm does not
|
|
100
|
+
* allow a `.gitignore` file to be published.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} dest
|
|
103
|
+
* @returns {Promise<void>}
|
|
104
|
+
*/
|
|
105
|
+
function renameGitignoreFile(dest) {
|
|
106
|
+
return fs.rename(path.join(dest, 'gitignore'), path.join(dest, '.gitignore'));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Install the versions pinned in the template's package.json.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} dest
|
|
113
|
+
* @param {boolean} useYarn
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
116
|
+
function installDependencies(dest, useYarn) {
|
|
117
|
+
const cmd = useYarn ? 'yarn install' : 'npm install';
|
|
118
|
+
childProcess.execSync(cmd, { cwd: dest, stdio: 'inherit' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ========================================================================= //
|
|
122
|
+
// EXPORT //
|
|
123
|
+
// ========================================================================= //
|
|
124
|
+
|
|
125
|
+
module.exports = expressGenTs;
|