Still need tsx to do type checking
This is especially hairy when making a typescript library that is distributed non-compiled (without dist/) and is supposed to run in both browser and Node.
`allowImportingTsExtensions: true` (https://www.typescriptlang.org/tsconfig/#allowImportingTsExt..., useful if you're running `tsc` in noEmit mode as a linter)
`rewriteRelativeImportExtensions: true` (https://www.typescriptlang.org/tsconfig/#rewriteRelativeImpo..., useful if you're using `tsc` to compile TS files to JS.
This allows you to use fully-specified imports in TypeScript files, which works basically everywhere — NodeJS, TypeScript, bundlers, etc.
The exceptions are browsers (obviously, only normal JS syntax there), and packages inside `node_modules`, which NodeJS will not do any type stripping for. So if you're writing a library, you'll probably still need to distribute the compiled sources, rather than distributing the raw TypeScript files alone. Or you use the JSDoc syntax for TypeScript, which can do everything that .ts files can do, but is more verbose and idiosyncratic.
This way I could just use node --watch instead of tsx or nodemon.
// ---- dev-ts-resolve.js
export async function resolve(specifier, context, nextResolve) {
try {
return await nextResolve(specifier, context);
} catch (err) {
const isRelative = specifier.startsWith('./')
|| specifier.startsWith('../')
|| specifier.startsWith('/')
|| specifier.startsWith('file:');
if (err?.code === 'ERR_MODULE_NOT_FOUND' && isRelative && specifier.endsWith('.js')) {
return nextResolve(`${specifier.slice(0, -3)}.ts`, context);
}
throw err;
}
}
// ---- dev-loader.js
import { register } from 'node:module';
register('./dev-ts-resolve.js', import.meta.url);
// ----
usage: node --import ./dev-loader.js --watch-path=./src