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.
Files changed (72) hide show
  1. package/README.md +198 -18
  2. package/bin/cli.js +91 -32
  3. package/lib/express-generator-typescript.js +125 -151
  4. package/lib/{project-files → template}/.prettierignore +2 -4
  5. package/lib/template/README.md +50 -0
  6. package/lib/template/bs-config.js +8 -0
  7. package/lib/{project-files → template}/config/.env.development +4 -6
  8. package/lib/{project-files → template}/config/.env.production +4 -5
  9. package/lib/{project-files → template}/config/.env.test +4 -5
  10. package/lib/{project-files → template}/eslint.config.ts +4 -0
  11. package/lib/template/gitignore +10 -0
  12. package/lib/{project-files → template}/package.json +31 -36
  13. package/lib/{project-files → template}/src/common/constants/HttpStatusCodes.ts +12 -8
  14. package/lib/template/src/common/constants/Paths.ts +22 -0
  15. package/lib/template/src/common/constants/env-inv.ts +31 -0
  16. package/lib/template/src/common/types/utility-types.ts +5 -0
  17. package/lib/template/src/common/utils/date-utils.ts +29 -0
  18. package/lib/template/src/common/utils/route-errors.ts +37 -0
  19. package/lib/template/src/main.ts +24 -0
  20. package/lib/template/src/models/User.model.ts +95 -0
  21. package/lib/template/src/models/common/types.ts +10 -0
  22. package/lib/template/src/public/scripts/http.js +25 -0
  23. package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +4229 -0
  24. package/lib/template/src/public/scripts/renderUsers.js +90 -0
  25. package/lib/template/src/public/scripts/users.js +142 -0
  26. package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +11798 -0
  27. package/lib/template/src/repos/MockOrm.ts +71 -0
  28. package/lib/{project-files → template}/src/repos/UserRepo.ts +21 -35
  29. package/lib/template/src/repos/common/database.json +28 -0
  30. package/lib/{project-files → template}/src/routes/UserRoutes.ts +15 -18
  31. package/lib/template/src/routes/apiRouter.ts +28 -0
  32. package/lib/template/src/routes/common/express-types.ts +9 -0
  33. package/lib/template/src/routes/common/parseReq.ts +23 -0
  34. package/lib/template/src/server.ts +78 -0
  35. package/lib/template/src/services/UserService.ts +66 -0
  36. package/lib/{project-files → template}/src/views/users.html +13 -6
  37. package/lib/{project-files → template}/tests/common/comparators.ts +6 -6
  38. package/lib/template/tests/common/supertest-types.ts +11 -0
  39. package/lib/template/tests/frontend.test.ts +33 -0
  40. package/lib/template/tests/support/agent.ts +23 -0
  41. package/lib/template/tests/users.test.ts +205 -0
  42. package/lib/{project-files → template}/tsconfig.json +1 -3
  43. package/lib/{project-files → template}/tsconfig.prod.json +3 -9
  44. package/lib/{project-files → template}/vitest.config.mts +2 -0
  45. package/package.json +65 -52
  46. package/lib/cli.js +0 -33
  47. package/lib/project-files/README.md +0 -39
  48. package/lib/project-files/gitignore +0 -4
  49. package/lib/project-files/src/common/constants/Paths.ts +0 -15
  50. package/lib/project-files/src/common/constants/env.ts +0 -28
  51. package/lib/project-files/src/common/types/structure-utils.ts +0 -2
  52. package/lib/project-files/src/common/utils/number-utils.ts +0 -10
  53. package/lib/project-files/src/common/utils/route-errors.ts +0 -35
  54. package/lib/project-files/src/common/utils/validators.ts +0 -14
  55. package/lib/project-files/src/main.ts +0 -24
  56. package/lib/project-files/src/models/User.model.ts +0 -72
  57. package/lib/project-files/src/models/common/types.ts +0 -4
  58. package/lib/project-files/src/public/stylesheets/lib/bootstrap.min.css +0 -6
  59. package/lib/project-files/src/repos/MockOrm.ts +0 -63
  60. package/lib/project-files/src/repos/common/database.json +0 -1
  61. package/lib/project-files/src/repos/common/database.test.json +0 -1
  62. package/lib/project-files/src/routes/apiRouter.ts +0 -28
  63. package/lib/project-files/src/routes/common/express-types.ts +0 -8
  64. package/lib/project-files/src/routes/common/parseReq.ts +0 -19
  65. package/lib/project-files/src/server.ts +0 -73
  66. package/lib/project-files/src/services/UserService.ts +0 -64
  67. package/lib/project-files/tests/common/error-utils.ts +0 -25
  68. package/lib/project-files/tests/common/supertest-types.ts +0 -15
  69. package/lib/project-files/tests/support/agent.ts +0 -23
  70. package/lib/project-files/tests/users.test.ts +0 -131
  71. /package/lib/{project-files → template}/.prettierrc.json +0 -0
  72. /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/master/express-typescript.png" width="420">
3
- </p>
4
-
5
- <p align="center">
6
- <a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/v/express-generator-typescript.svg?style=for-the-badge&logo=npm" alt="NPM Version"></a>
7
- <a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/dm/express-generator-typescript.svg?style=for-the-badge" alt="NPM Downloads"></a>
8
- <a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/l/express-generator-typescript.svg?style=for-the-badge" alt="License"></a>
9
- </p>
10
-
11
- # express-generator-typescript
12
-
13
- 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).
14
-
15
-
16
- ## Documenation
17
-
18
- Please refer to the official <a href="https://github.com/seanpmaxwell/express-generator-typescript">github repo</a> for the most up-to-date documentation.
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
+ [![npm version](https://img.shields.io/npm/v/express-generator-typescript?logo=npm&label=npm)](https://www.npmjs.com/package/express-generator-typescript)
8
+ [![npm downloads](https://img.shields.io/npm/dm/express-generator-typescript?color=orange)](https://www.npmjs.com/package/express-generator-typescript)
9
+ [![License](https://img.shields.io/npm/l/express-generator-typescript)](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 path = require('path'),
4
- expressGenTs = require('../lib/express-generator-typescript');
5
-
6
-
7
- /******************************************************************************
8
- Run
9
- ******************************************************************************/
10
-
11
- // Init
12
- console.log('Setting up new Express/TypeScript project...');
13
- const args = process.argv.slice(2);
14
-
15
- // Setup use yarn
16
- let useYarn = false;
17
- const useYarnIdx = args.indexOf('--use-yarn');
18
- if (useYarnIdx > -1) {
19
- useYarn = true;
20
- args.splice(useYarnIdx, 1);
21
- }
22
-
23
- // Setup destination
24
- let destination = 'express-gen-ts';
25
- if (args.length > 0) {
26
- destination = args[0];
27
- }
28
- destination = path.join(process.cwd(), destination);
29
-
30
- // Creating new project finished
31
- expressGenTs(destination, useYarn).then(() => {
32
- console.log('Project setup complete!');
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 path = require('path'),
2
- editJsonFile = require('edit-json-file'),
3
- childProcess = require('child_process'),
4
- ncp = require('ncp').ncp,
5
- fs = require('fs');
6
-
7
- /******************************************************************************
8
- Constants
9
- ******************************************************************************/
10
-
11
- const PROJECT_FOLDER_PATH = './project-files';
12
-
13
- const DEPENDENCIES = [
14
- 'cookie-parser',
15
- 'cross-env',
16
- 'dayjs',
17
- 'dotenv',
18
- 'express',
19
- 'helmet',
20
- 'jet-env',
21
- 'jet-logger',
22
- 'jet-paths',
23
- 'jet-validators',
24
- 'jsonfile',
25
- 'module-alias',
26
- 'morgan',
27
- 'tspo',
28
- ];
29
-
30
- const DEV_DEPENDENCIES = [
31
- '@eslint/js',
32
- '@swc/core',
33
- '@trivago/prettier-plugin-sort-imports',
34
- '@types/cookie-parser',
35
- '@types/find',
36
- '@types/fs-extra',
37
- '@types/jsonfile',
38
- '@types/module-alias',
39
- '@types/morgan',
40
- '@types/node',
41
- '@types/supertest',
42
- 'browser-sync',
43
- 'concurrently',
44
- 'delay-cli',
45
- 'eslint',
46
- 'eslint-config-prettier',
47
- 'eslint-plugin-n',
48
- 'find',
49
- 'fs-extra',
50
- 'jiti',
51
- 'nodemon',
52
- 'prettier',
53
- 'shx',
54
- 'supertest',
55
- 'ts-node',
56
- 'tsconfig-paths',
57
- 'typescript',
58
- 'typescript-eslint',
59
- 'vitest',
60
- ];
61
-
62
- // "ncp" options
63
- const ncpOpts = {
64
- filter: (fileName) => {
65
- return !(fileName === 'package-lock.json' || fileName === 'node_modules');
66
- },
67
- };
68
-
69
- /******************************************************************************
70
- Functions
71
- ******************************************************************************/
72
-
73
- /**
74
- * Entry point
75
- */
76
- async function expressGenTs(destination, useYarn) {
77
- try {
78
- await copyProjectFiles(destination);
79
- updatePackageJson(destination);
80
- await renameGitigoreFile(destination);
81
- downloadNodeModules(destination, useYarn);
82
- } catch (err) {
83
- console.error(err);
84
- }
85
- }
86
-
87
- /**
88
- * Copy project files
89
- */
90
- function copyProjectFiles(destination) {
91
- const source = path.join(__dirname, PROJECT_FOLDER_PATH);
92
- return /** @type {Promise<void>} */(new Promise((res, rej) => {
93
- return ncp(source, destination, ncpOpts, (err) => {
94
- return (!!err ? rej(err) : res());
95
- });
96
- }));
97
- }
98
-
99
- /**
100
- * Set update the package.json file.
101
- */
102
- function updatePackageJson(destination) {
103
- let file = editJsonFile(destination + '/package.json', {
104
- autosave: true
105
- });
106
- file.set('name', path.basename(destination));
107
- file.set('dependencies', {});
108
- file.set('devDependencies', {});
109
- }
110
-
111
- /**
112
- * Because npm does not allow .gitignore to be published.
113
- */
114
- function renameGitigoreFile(destination) {
115
- return /** @type {Promise<void>} */(new Promise((res, rej) =>
116
- fs.rename(
117
- (destination + '/gitignore'),
118
- (destination + '/.gitignore'),
119
- (err => !!err ? rej(err) : res()),
120
- )
121
- ));
122
- }
123
-
124
- /**
125
- * Download the dependencies.
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;
@@ -3,10 +3,8 @@
3
3
 
4
4
  # Allow these folders
5
5
  !src/
6
- !test/
7
- !script/
6
+ !tests/
8
7
 
9
8
  # Allow all files inside them
10
9
  !src/**
11
- !test/**
12
- !scripts/**
10
+ !tests/**