Skip to content

Commit fd6f478

Browse files
authored
feat!: add, improve and remove config presets (#94)
* feat!: add, improve and remove config presets * fix: add back node 14 and 16 to make tests pass * fix: add back `base.json` as an alias
1 parent a7417dd commit fd6f478

14 files changed

+282
-112
lines changed

README.md

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Are meant to be used with javascript code, not typescript code, hence they're ha
1414
npm install --save-dev @voxpelli/tsconfig
1515
```
1616

17-
Then in your `tsconfig.json`, it [`extends`](https://www.typescriptlang.org/tsconfig#extends) the chosen base config:
17+
Then add an [`extends`](https://www.typescriptlang.org/tsconfig#extends) to your `tsconfig.json` like this:
1818

1919
```json
2020
{
@@ -32,20 +32,22 @@ Then in your `tsconfig.json`, it [`extends`](https://www.typescriptlang.org/tsco
3232

3333
### Generic ones
3434

35-
* [`base`](base.json) – where most of the configuration is set
36-
* [`legacy`](legacy.json) – like `base` but for older TypeScript versions – version 4.5 and onward
37-
* [`recommended`](recommended.json) – like `base` but adds a [`target`](https://www.typescriptlang.org/tsconfig#target) set to `ES2015`
35+
* [`base-node-bare`](base-node-bare.json) – where most of the configuration is set (Node.js focused)
36+
* [`base-node-jsdoc`](base-node-jsdoc.json) – adds JSDoc related config to `base-node-bare`
3837

3938
### Node specific ones
4039

41-
These extends `base` with the correct [`lib`](https://www.typescriptlang.org/tsconfig#lib) and [`target`](https://www.typescriptlang.org/tsconfig#target) for the node.js version.
40+
These extends `base-node-jsdoc` with the correct [`lib`](https://www.typescriptlang.org/tsconfig#lib), [`module`](https://www.typescriptlang.org/tsconfig#module), [`moduleResolution`](https://www.typescriptlang.org/tsconfig#moduleResolution) and [`target`](https://www.typescriptlang.org/tsconfig#target) for each Node.js version.
4241

4342
Inspired by [tsconfig/bases](https://github.com/tsconfig/bases).
4443

45-
* [`node20`](node20.json)
44+
* [`node14`](node14.json) _deprecated_
45+
* [`node16`](node16.json) _deprecated_
4646
* [`node18`](node18.json)
47-
* [`node16`](node16.json)
48-
* [`node14`](node14.json)
47+
* [`node20`](node20.json)
48+
* [`node22`](node22.json)
49+
* [`node24`](node24.json)
50+
* [`nodenext`](nodenext.json) (currently an alias for `base-node-jsdoc`)
4951

5052
## Can I use this in my own project?
5153

@@ -55,6 +57,119 @@ Just as with [voxpelli/eslint-config](https://github.com/voxpelli/eslint-config)
5557

5658
Give me a ping if you use it, would be a delight to know you like it 🙂
5759

60+
## Generate types
61+
62+
When publishing a module, no matter if we use JSDoc or TS syntax we need to publish type declarations.
63+
64+
Here's how to generate type declarations when using JSDoc,
65+
66+
### 1. Declaration specific config file
67+
68+
Add a new declaration specific tsconfig (eg. `declaration.tsconfig.json`) that extends your base tsconfig. Something like:
69+
70+
```json
71+
{
72+
"extends": "./tsconfig",
73+
"files": [],
74+
"exclude": [
75+
"test/**/*.js"
76+
],
77+
"compilerOptions": {
78+
"declaration": true,
79+
"declarationMap": true,
80+
"noEmit": false,
81+
"emitDeclarationOnly": true
82+
}
83+
}
84+
```
85+
86+
The above excludes all top level files and all files in `test/` from having types generated. If one wants eg. `index.js` to have auto-generated types, then one needs to either remove `"files": [],` to use the inherited value or explicitly add it (`"files": ["index.js"],`).
87+
88+
If one uses eg. [`@deprecated`](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#deprecated) and wants to retain JSDoc comments in ones type declarations, then one should set [`"removeComments": false`](https://www.typescriptlang.org/tsconfig/#removeComments) in the `compilerOptions`. By default `@voxpelli/tsconfig` sets `"removeComments": true` to keep generated types lean and DRY.
89+
90+
### 2. Add scripts
91+
92+
We should add scripts that uses the config file. These are examples add them to `"scripts"` in `package.json` and uses [`npm-run-all2`](https://github.com/bcomnes/npm-run-all2) to give clean separation and enable parallel execution.
93+
94+
#### Build script
95+
96+
```json
97+
"build:0": "run-s clean",
98+
"build:1-declaration": "tsc -p declaration.tsconfig.json",
99+
"build": "run-s build:*",
100+
```
101+
102+
When we run `build` we sequentially run all `build:*` using [`run-s`](https://github.com/bcomnes/npm-run-all2/blob/e9ca500b9a5f2d4550f4a72020afc1cd8d68b281/docs/run-s.md).
103+
104+
1. First we run `clean` to remove any pre-existing generated type declarations (as else they will be used as the source)
105+
2. Then we run `tsc` which generates the new type declarations thanks to it using the declaration specific tsconfig
106+
107+
#### Clean script
108+
109+
```json
110+
"clean:declarations-top": "rm -f $(find . -maxdepth 1 -type f -name '*.d.ts*' ! -name 'index.d.ts')",
111+
"clean:declarations-lib": "rm -f $(find lib -type f -name '*.d.ts*' ! -name '*-types.d.ts')",
112+
"clean": "run-p clean:*",
113+
```
114+
115+
When we run `clean` we run all `clean:*` in parallel using [`run-p`](https://github.com/bcomnes/npm-run-all2/blob/e9ca500b9a5f2d4550f4a72020afc1cd8d68b281/docs/run-p.md).
116+
117+
Both clean commands use `rm -f` to delete a list of files found through `find`. The `-f` flag is needed since `find` may return an empty list, which without `-f` causes `rm` to fail.
118+
119+
The `find` command returns all matching type declaration files. It uses three flags:
120+
121+
* `-maxdepth 1'` is used when running in `.` to avoid recursing into `node_modules` (as we of course do _not_ want to remove any type declarations in there)
122+
* `-name '*.d.ts*'` limits matching file names to `.d.ts` and `.d.ts.map` files. (If you generate `.mts` or `.cts` as well, then change this to `*.d.*ts*`)
123+
* `-type f` ensures that only files are returned
124+
125+
The two clean scripts are:
126+
127+
* `clean:declarations-top` cleans all top level (`.`) type declarations except for `index.d.ts` (as that's often hand coded instead). One can remove the `! -name 'index.d.ts'` or add additional `! -name` sections to tweak what is ignored.
128+
* `clean:declarations-lib` recursively cleans all type declarations in `lib` except for those ending with `-types.d.ts` (as our naming convention is that all such files are hand coded). One can add additional `! -name` sections to ignore further files.
129+
130+
#### Tweak type validation scripts
131+
132+
Assuming that we have something like the following that checks our types (if you're not using [`type-coverage`](https://github.com/plantain-00/type-coverage) you should start!):
133+
134+
```json
135+
"check:tsc": "tsc",
136+
"check:type-coverage": "type-coverage --detail --strict --at-least 99 --ignore-files 'test/*'",
137+
```
138+
139+
Then we should make sure that we run `clean` before we run our checks as else `tsc` will use the type declarations rather than the JSDoc types when validating.
140+
141+
So we should do something like the following, it first runs `clean`, then runs `check:*` in parallel.
142+
143+
```json
144+
"check": "run-s clean && run-p check:*",
145+
```
146+
147+
#### Ensure we generate before publish
148+
149+
Lastly we should make sure that we generate the files before publish, something we can do by eg. adding a [`prepublishOnly`](https://docs.npmjs.com/cli/v8/using-npm/scripts#life-cycle-scripts) life cyle script:
150+
151+
```json
152+
"prepublishOnly": "run-s build",
153+
```
154+
155+
### 3. Ignore files in `.gitignore`
156+
157+
And something like this in your `.gitignore`:
158+
159+
```gitignore
160+
# Generated types
161+
*.d.ts
162+
*.d.ts.map
163+
!/lib/**/*-types.d.ts
164+
!/index.d.ts
165+
```
166+
167+
The ignores here (`!/lib/**/*-types.d.ts`, `!/index.d.ts`) matches the ignores we added in our [`clean:*`](#2-add-scripts) scripts
168+
169+
### Reference example
170+
171+
See my [`voxpelli/node-module-template`](https://github.com/voxpelli/node-module-template) repository for a fully functioning example of my current (and hopefully up to date) reference of this pattern.
172+
58173
## Alternatives
59174

60175
* [sindresorhus/tsconfig](https://github.com/sindresorhus/tsconfig)

base-node-bare.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
"display": "Base Node.js Bare",
4+
5+
"compilerOptions": {
6+
"strict": true,
7+
8+
"skipLibCheck": false, // See https://github.com/voxpelli/tsconfig/issues/1
9+
"esModuleInterop": true, // Will be true anyhow because module is nodenext
10+
"allowSyntheticDefaultImports": true, // Will be true anyhow because esModuleInterop is true
11+
12+
/* Node.js Module Setup */
13+
"module": "nodenext", // Supports require() of ESM, see: https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-node18-nodenext
14+
"moduleResolution": "nodenext", // Default when module is nodenext
15+
16+
"lib": ["esnext"], // Avoids DOM libraries etc being included
17+
"target": "esnext", // Default when module is nodenext
18+
19+
"types": ["node"], // Avoids automatic inclusion of all visible ”@types” packages to be included
20+
21+
/* Clean up generated declarations */
22+
"removeComments": true,
23+
"stripInternal": true,
24+
25+
/* New checks being tried out */
26+
"exactOptionalPropertyTypes": true,
27+
"noFallthroughCasesInSwitch": true,
28+
"noImplicitOverride": true,
29+
"noPropertyAccessFromIndexSignature": true,
30+
"noUncheckedIndexedAccess": true,
31+
"noUncheckedSideEffectImports": true,
32+
33+
/* Additional checks */
34+
"forceConsistentCasingInFileNames": true,
35+
"noImplicitReturns": false, // Deactivated as I believe implicit "return undefined" at end of function is okay + explicit clashes with ESLint "no-useless-return"
36+
"noUnusedLocals": true,
37+
"noUnusedParameters": true
38+
39+
/* To make strict checking somewhat less strict during a transition stage, add one or more of: */
40+
/*
41+
"noImplicitThis": false,
42+
"noImplicitAny": false,
43+
"strictNullChecks": false,
44+
*/
45+
}
46+
}

base-node-jsdoc.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
"display": "Base Node.js JSDoc",
4+
5+
"extends": "./base-node-bare.json",
6+
7+
"compilerOptions": {
8+
"allowJs": true,
9+
"checkJs": true,
10+
"maxNodeModuleJsDepth": 0, // Fix when used with jsconfig.json, see https://github.com/voxpelli/types-in-js/discussions/25
11+
"noEmit": true,
12+
"resolveJsonModule": true
13+
}
14+
}

base.json

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,6 @@
11
{
22
"$schema": "https://json.schemastore.org/tsconfig",
3-
"display": "Base",
3+
"display": "Alias for compatibility",
44

5-
"compilerOptions": {
6-
"module": "node16",
7-
"moduleResolution": "node16",
8-
9-
"strict": true,
10-
11-
"esModuleInterop": true, // Will be true anyhow because module is node16
12-
"allowSyntheticDefaultImports": true, // Will be true anyhow because esModuleInterop is true
13-
"skipLibCheck": false, // See https://github.com/voxpelli/tsconfig/issues/1
14-
"types": ["node"], // Don't implicitly pull in declarations from `@types` packages
15-
16-
/* Clean up generated declarations */
17-
"removeComments": true,
18-
"stripInternal": true,
19-
20-
/* Make it a JS-targeted config */
21-
"allowJs": true,
22-
"checkJs": true,
23-
"noEmit": true,
24-
"resolveJsonModule": true,
25-
26-
/* New checks being tried out */
27-
"exactOptionalPropertyTypes": true,
28-
"noFallthroughCasesInSwitch": true,
29-
"noImplicitOverride": true,
30-
"noPropertyAccessFromIndexSignature": true,
31-
"noUncheckedIndexedAccess": true,
32-
"noUncheckedSideEffectImports": true,
33-
34-
/* Additional checks */
35-
"forceConsistentCasingInFileNames": true,
36-
"noImplicitReturns": false, // Deactivated as I believe implicit "return undefined" at end of function is okay + explicit clashes with ESLint "no-useless-return"
37-
"noUnusedLocals": true,
38-
"noUnusedParameters": true
39-
40-
/* To make strict checking somewhat less strict during a transition stage, add one or more of: */
41-
/*
42-
"noImplicitThis": false,
43-
"noImplicitAny": false,
44-
"strictNullChecks": false,
45-
*/
46-
}
5+
"extends": "./base-node-jsdoc.json",
476
}

legacy.json

Lines changed: 0 additions & 40 deletions
This file was deleted.

node14.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
"$schema": "https://json.schemastore.org/tsconfig",
33
"display": "Node 14",
44

5-
"extends": "./base.json",
5+
"extends": "./base-node-jsdoc.json",
66

77
"compilerOptions": {
88
"lib": ["es2020"],
9-
"target": "es2020"
9+
"target": "es2020",
10+
11+
/* Overriding base config that's nodenext */
12+
"module": "node16",
13+
"moduleResolution": "node16" // Default when module is node16
1014
}
1115
}

node16.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
"$schema": "https://json.schemastore.org/tsconfig",
33
"display": "Node 16",
44

5-
"extends": "./base.json",
5+
"extends": "./base-node-jsdoc.json",
66

77
"compilerOptions": {
88
"lib": ["es2021"],
9-
"target": "es2021"
9+
"target": "es2021",
10+
11+
/* Overriding base config that's nodenext */
12+
"module": "node16",
13+
"moduleResolution": "node16" // Default when module is node16
1014
}
1115
}

node18.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
"$schema": "https://json.schemastore.org/tsconfig",
33
"display": "Node 18",
44

5-
"extends": "./base.json",
5+
"extends": "./base-node-jsdoc.json",
66

77
"compilerOptions": {
88
"lib": ["es2022"],
9-
"target": "es2022"
9+
"target": "es2022",
10+
11+
/* Overriding base config that's nodenext */
12+
"module": "node18",
13+
"moduleResolution": "node16" // Default when module is node18
1014
}
1115
}

node20.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@
22
"$schema": "https://json.schemastore.org/tsconfig",
33
"display": "Node 20",
44

5-
"extends": "./base.json",
5+
"extends": "./base-node-jsdoc.json",
66

77
"compilerOptions": {
88
"lib": ["es2023"],
99
"target": "es2022",
10+
11+
/*
12+
* Setting a strict module resolution
13+
*
14+
* Currently "nodenext" is an alias for "node16", but that might change, so lets pin it
15+
*
16+
* Docs: https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution
17+
* Prior art: https://github.com/tsconfig/bases/blob/40be80edaa16e4e18d6cc01cc841f2c231a6907d/bases/node20.json#L14
18+
*/
19+
"module": "nodenext",
20+
"moduleResolution": "node16"
1021
}
1122
}

0 commit comments

Comments
 (0)