UNPKG

@profitsniper/email

Version:

While developing with Typescript and Node.js is awesome, **setting up a new project is painful**. This minimal and modern starter repo is here to help you get started with Node.js and Typecript without the pain.

193 lines (177 loc) 5 kB
// @ts-check /** * Specifying all tasks (build, test, bundle, etc..) inside code to be able to * more easily guarantuee a similar interface across operating systems (linux, * macos, windows). */ import path from 'node:path' import nodemon from 'nodemon' import shell from 'shelljs' /** * Builds project. Typescript -> Javascript * @param {Array<string>} [additionalArguments] */ export const build = (additionalArguments = []) => { clean() console.log('Building project...') const pathToTsc = ['node_modules', 'typescript', 'bin', 'tsc'].join(path.sep) const { code } = shell.exec( `node ${pathToTsc} ${additionalArguments.join(' ')}` ) handleNonZeroReturnCode(code) } /** * Tests the project. */ export const test = () => { build() // runs tests based on the test runner execution model // src: https://nodejs.org/api/test.html#test-runner-execution-model" const { code } = shell.exec(`node --test --test-reporter spec`) handleNonZeroReturnCode(code) } /** * Bundles the code into a single file. */ export const bundle = () => { // Builds without emitting to ensure that the code is still valid typescript // before bundling. build(['--noEmit']) const pathToEsbuild = ['node_modules', 'esbuild', 'bin', 'esbuild'].join( path.sep ) const { code } = shell.exec( `node ${pathToEsbuild} src/index.ts --outdir=bundle --bundle --platform=node --target=node18.16.0` ) handleNonZeroReturnCode(code) } /** * Runs your code. Only works if it was built before. */ export const start = () => { const pathToCode = ['dist', 'src', 'index.js'].join(path.sep) const { code } = shell.exec(`node ${pathToCode}`) handleNonZeroReturnCode(code) } /** * Runs your javscript code every time it changes. Use this while developing to * reduce the time taken to get feedback. * Typically invoked by running "yarn dev". */ export const watchNode = () => { const pathToCode = ['dist', 'src', 'index.js'].join(path.sep) nodemon({ script: pathToCode, ext: 'js json' }) nodemon .on('start', function() { console.log('App has started') }) .on('quit', function() { console.log('App has quit') process.exit() }) .on('restart', function(files) { console.log('App restarted due to: ', files) }) } /** * Compiles your typescript code to javascript every time it changes. Use this * while developing to reduce the time taken to get feedback. * Typically invoked by running "yarn dev". */ export const watchTs = () => { const pathToTsc = ['node_modules', 'typescript', 'bin', 'tsc'].join(path.sep) const { code } = shell.exec(`node ${pathToTsc} -w`) handleNonZeroReturnCode(code) } /** * Cleans up all built/bundled files by removing the bundle and dist * directories. */ export const clean = () => { shell.rm('-rf', ['dist', 'build']) } /** * Formats the whole codebase (ignores files specified in .prettierignore). * @param {Array<string>} [additionalArguments] */ export const format = (additionalArguments) => { const pathToPrettier = [ 'node_modules', 'prettier', 'bin', 'prettier.cjs' ].join(path.sep) const { code } = shell.exec( `node ${pathToPrettier} . ${additionalArguments}` ) if (code !== 0) { console.error(`task failed with code ${code}`) console.error( 'Run `yarn format` to format your codebase. Or set up the pre-commit hook to avoid these kind of issues.' ) process.exit(code) } } /** * Lints the whole codebase (ignores files specified in .eslintignore). * @param {Array<string>} [additionalArguments] */ export const lint = (additionalArguments) => { const pathToEslint = ['node_modules', 'eslint', 'bin', 'eslint.js'].join( path.sep ) const { code } = shell.exec(`node ${pathToEslint} . ${additionalArguments}`) if (code !== 0) { console.error(`task failed with code ${code}`) console.error( 'Run `yarn lint` to lint your codebase. It fixes automatically fixable problems for you.' + 'The rest of the problems have to be figured out and cleared by yourself.' ) process.exit(code) } } /** * @param {number} code **/ const handleNonZeroReturnCode = (code) => { if (code !== 0) { console.error(`task failed with code ${code}`) process.exit(code) } } const main = () => { const [_, __, taskName, ...additionalArguments] = process.argv if (taskName === 'build') { return build() } if (taskName === 'bundle') { return bundle() } if (taskName === 'clean') { return clean() } if (taskName === 'format') { return format(additionalArguments) } if (taskName === 'lint') { return lint(additionalArguments) } if (taskName === 'start') { return start() } if (taskName === 'test') { return test() } if (taskName === 'watch-node') { return watchNode() } if (taskName === 'watch-ts') { return watchTs() } console.error(`Unknown task: ${taskName}`) process.exit(-1) } main()