Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dandv/typescript-modern-project
TypeScript+ESLint+Jest project template to import modules without extension, and modules with default exports that use node built-ins
https://github.com/dandv/typescript-modern-project
jest node nodejs typescript
Last synced: 3 months ago
JSON representation
TypeScript+ESLint+Jest project template to import modules without extension, and modules with default exports that use node built-ins
- Host: GitHub
- URL: https://github.com/dandv/typescript-modern-project
- Owner: dandv
- License: mit
- Created: 2020-02-08T06:51:09.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-06T02:43:32.000Z (about 2 years ago)
- Last Synced: 2024-10-15T03:46:04.194Z (4 months ago)
- Topics: jest, node, nodejs, typescript
- Language: TypeScript
- Homepage: https://dev.to/dandv/typescript-settings-for-modern-projects-4596
- Size: 383 KB
- Stars: 44
- Watchers: 4
- Forks: 5
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Modern TypeScript project template
Minimalistic example of configuring TypeScript and Node to:
* emit modern ES modules code
* import modules that use Node built-ins
* import modules that don't have named exports (e.g. [`apollo-server`](https://github.com/apollographql/apollo-server/issues/1356#issuecomment-565277759), [`node-influx`](https://github.com/node-influx/node-influx/issues/298))
* import your own modules without specifying an extension
* lint with ESLint, with TypeScript support
* test the TypeScript code instantly without having to build first
* run the resulting JavaScript code, with support for the optional chaining operator `?.`Bonus: continuous integration script for GitHub Actions. It automatically runs tests on every pushed commit.
There is no need for Babel.
# Emit ES modules code
In [`tsconfig.json`](tsconfig.json), set this in `compilerOptions`:
```json
"target": "esnext",
"module": "esnext", // Output `import`/`export` ES modules
```# Import modules that use Node built-ins (`http`, `url` etc.)
* run `npm install --save-dev @types/node`
* in `tsconfig.json` under `compilerOptions`, set
* `"moduleResolution": "node"`, so `tsc` can find modules [when targeting ES6+](https://github.com/Microsoft/TypeScript/issues/8189)
* `"types": ["node"]` to avoid errors related to Node built-in modules# Import modules that don't have named exports
Normally we could write in TypeScript
import { InfluxDB } from 'influx';
but when generating ES modules code, that statement will be passed through as is, and will cause Node to fail with
> SyntaxError: The requested module 'influx' does not provide an export named 'InfluxDB'
because [`node-influx` doesn't provide named exports](https://github.com/node-influx/node-influx/issues/298) (and neither does an even more popular module, [`apollo-server`](https://github.com/apollographql/apollo-server/issues/1356#issuecomment-565277759)).
One alternative would be to generate old ugly commonjs modules code by,
* removing the `"type": "module"` line from `package.json`, and
* changing the module line to `"module": "CommonJS"` in `tsconfig.json` (`allowSyntheticDefaultImports` also becomes unnecessary)What we'll do is import the entire module:
```js
import Influx from 'influx';
const influx = new Influx.InfluxDB();
```However, this will generate `Error TS1192: Module '...' has no default export.` To prevent that, set `"allowSyntheticDefaultImports": true` in `tsconfig.json`.
# Import your own modules without specifying an extension
When transpiling, [TypeScript won't generate an extension for you](https://github.com/microsoft/TypeScript/issues/16577). Run Node with the [`node --experimental-specifier-resolution=node` parameter](https://nodejs.org/api/cli.html#cli_experimental_specifier_resolution_mode):
node --experimental-specifier-resolution=node run.js
Otherwise, [node mandates that you specify the extension](https://nodejs.org/api/esm.html#esm_mandatory_file_extensions) in the `import` statement.# Optional chaining
To support optional chaining, add the `--harmony` flag to the node command line.
# Run the resulting JavaScript code
Add `"type": "module"` to `package.json`, because [TypeScript can't generate files with the .mjs extension](https://github.com/microsoft/TypeScript/issues/18442#issuecomment-581738714).
# ESLint
To be able to run `eslint`, we must create an `.eslintrc.cjs` file, rather than a `.js` one (due to `"type": "module"` in `package.json`). Then, install the required dependencies:
npm i -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
Here's the [diff to add ESLint support](https://github.com/dandv/typescript-modern-project/commit/f816fe6e8d83ce554bd3066ac6638fb4406e917f).
# Testing with Jest
The cleanest way to fully support Jest with TypeScript, ES Modules and ESLint, is to use `ts-jest`, which has [a number of advantages over using Babel](https://github.com/kulshekhar/ts-jest#ts-jest). What we need to do:
* `npm install --save-dev jest @types/jest eslint-plugin-jest` - for ES Lint support
* add `"jest"` to the `types` array in `tsconfig.json`
* add the `'jest'` plugin to `.eslintrc.cjs` and also add `'jest/globals': true` to its `env` key
* use the [`jest.config.cjs`](jest.config.cjs) generated by ts-jest `config:init` (just renamed .js -> .cjs).Normally, to run Jest from `package.json`, we'd add a `"test": "jest"` line. That won't be sufficient, because we need to pass the `--harmony` flag to node (for optional chaining support).
To pass parameters to Node when running Jest, we'll add the following `test` line:"test": "node --harmony node_modules/.bin/jest"
The only caveat here is that Jest seems to prefer generated `.js` files over their `.ts` originals, so we'll exclude them via [`jest.config.cjs`](jest.config.cjs):
```
testRegex: '.*.test.ts', // test filenames matching this regex
moduleFileExtensions: ['ts', 'js'], // modules are only in .ts files, but 'js' *must* be specified too
```# Source maps
If your script generates an error, you'll see the line numbers from the generated `.js` files, which is not helpful. We want to see the original paths and line numbers from the `.ts` files. To do that, we'll add `sourceMap: true` to `tsconfig.json`, install [`source-map-support`](https://www.npmjs.com/package/source-map-support) and run node with the `-r source-map-support/register` parameter. Note that Jest already takes care of source mapping so you'll see the `.ts` line numbers without having to do anything extra.
Here's [the diff to add source map support](https://github.com/dandv/typescript-modern-project/commit/4e31278833f2ce07f474d9c6348bb4509082ee97).## CI testing
Using [GitHub Actions](https://github.com/features/actions), we can configure automatic testing via `.yml` files under [.github/workflows](.github/workflows).
## TODO
The `tsconfig.json` settings generate `.js` built files, and `.js.map` source map files, next to the original `.ts` file. While some IDEs conveniently hide these files, it may be desirable to output them in a separate directory, typically `dist`.
This can be done using the `rootDir`/`outDir` settings in `tsconfig.json`, but that sort of setup comes with an [annoying limitation](https://github.com/microsoft/TypeScript/issues/9858) of Typescript that [forbids importing files outside the `rootDir`](https://stackoverflow.com/questions/52121725/maintain-src-folder-structure-when-building-to-dist-folder-with-typescript-3). That can be a [problem with monorepos](https://github.com/microsoft/TypeScript/issues/17611).