commit aaa607a40c3eb400d374bf2bdcbcaae50c496b5d Author: Alexander Ungar Date: Tue Mar 14 23:38:19 2023 +0100 Add initial app with use case diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..24bb26d --- /dev/null +++ b/.eslintrc @@ -0,0 +1,26 @@ +{ + "env": { + "es2021": true, + "node": true + }, + "extends": [ + "standard-with-typescript", + "prettier" + ], + "plugins": [ + "prettier" + ], + "overrides": [ + ], + "ignorePatterns": [ + "generated/**/*" + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": "tsconfig.json" + }, + "rules": { + "prettier/prettier": "error" + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34afe59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +lib-cov +*.seed +*.log +*.dat +*.out +*.pid +*.gz +*.swp + +pids +logs +results +tmp + +# Coverage reports +coverage + +# API keys and secrets +.env + +# Dependency directory +node_modules + +# Editors +.idea +*.iml + +# OS metadata +.DS_Store +Thumbs.db + +# Ignore built ts files +dist + +# ignore yarn.lock +package-lock.json + +# Generated files +generated \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f51ffe --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +## About + +This project was created with [express-generator-typescript](https://github.com/seanpmaxwell/express-generator-typescript). + + +## Available Scripts + +### `npm run dev` + +Run the server in development mode. + +### `npm test` + +Run all unit-tests with hot-reloading. + +### `npm test -- --testFile="name of test file" (i.e. --testFile=Users).` + +Run a single unit-test. + +### `npm run test:no-reloading` + +Run all unit-tests without hot-reloading. + +### `npm run lint` + +Check for linting errors. + +### `npm run build` + +Build the project for production. + +### `npm start` + +Run the production build (Must be built first). + +### `npm start -- --environment="name of environment file" (default is production).` + +Run production build with a different environment file. + + +## Additional Notes + +- If `npm run dev` gives you issues with bcrypt on MacOS you may need to run: `npm rebuild bcrypt --build-from-source`. diff --git a/env/development.env b/env/development.env new file mode 100644 index 0000000..ea731f8 --- /dev/null +++ b/env/development.env @@ -0,0 +1,3 @@ +## Server ## +PORT=3000 +NODE_ENV=development \ No newline at end of file diff --git a/env/production.env b/env/production.env new file mode 100644 index 0000000..29f2069 --- /dev/null +++ b/env/production.env @@ -0,0 +1,3 @@ +## Server ## +PORT=3000 +NODE_ENV=production \ No newline at end of file diff --git a/env/test.env b/env/test.env new file mode 100644 index 0000000..63ab6ed --- /dev/null +++ b/env/test.env @@ -0,0 +1,3 @@ +## Server ## +PORT=3000 +NODE_ENV=test \ No newline at end of file diff --git a/local-development/docker-compose.yml b/local-development/docker-compose.yml new file mode 100644 index 0000000..eb6d5e4 --- /dev/null +++ b/local-development/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + mongo: + restart: always + image: mongo:6.0.4 + expose: + - 27017 + volumes: + - mongo_volume:/data/db + +volumes: + mongo_volume: \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..ea7a94b --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "my-finance-pal", + "version": "1.0.0", + "description": "A slick application for manging personal finances", + "license": "MIT", + "author": "Alexander Ungar", + "type": "module", + "main": "dist/src/index.js", + "scripts": { + "build": "tsc", + "lint": "eslint --ext .ts src/", + "dev": "nodemon src/server.ts", + "test": "nodemon --config ./spec/nodemon.json", + "generate-api-types": "openapi-typescript api/my-finance-pal.yml --output generated/api.ts" + }, + "nodemonConfig": { + "execMap": { + "ts": "ts-node --esm" + } + }, + "dependencies": { + "body-parser": "^1.20.2", + "dotenv": "^16.0.3", + "envalid": "^7.3.1", + "express": "^4.18.2", + "express-async-errors": "^3.1.1", + "express-winston": "^4.2.0", + "helmet": "^6.0.1", + "http-status-codes": "^2.2.0", + "openapi-typescript": "^6.2.0", + "ts-command-line-args": "^2.4.2", + "uuid": "^9.0.0", + "winston": "^3.8.2" + }, + "devDependencies": { + "@types/uuid": "^9.0.1", + "@types/eslint": "^8.21.1", + "@types/express": "^4.17.17", + "@types/morgan": "^1.9.4", + "@types/node": "^18.15.0", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.7.0", + "eslint-config-standard-with-typescript": "^34.0.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.1.1", + "nodemon": "^2.0.21", + "prettier": "^2.8.4", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.5" + } +} diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..9cda85c --- /dev/null +++ b/src/app.ts @@ -0,0 +1,28 @@ +import express from "express"; +import helmet from "helmet"; +import environment from "./config/environment.js"; +import expressLogger from "./logging/express-logger.js"; +import ApiRouter from "./routes/ApiRouter.js"; +import BudgetUseCases from "./usecase/budget/BudgetUseCases.js"; +import BudgetMongoRepository from "./repository/budget/BudgetMongoRepository.js"; +import { errorHandler } from "./middleware/error-handler.js"; + +const app = express(); +const API_ROOT = "/api"; + +app.use(expressLogger); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +if (environment.isProd) { + app.use(helmet()); +} + +const budgetRepository = new BudgetMongoRepository(); +const budgetUseCases = new BudgetUseCases(budgetRepository); +app.use(API_ROOT, ApiRouter(budgetUseCases)); + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +app.use(errorHandler); + +export default app; diff --git a/src/config/environment.ts b/src/config/environment.ts new file mode 100644 index 0000000..3f725db --- /dev/null +++ b/src/config/environment.ts @@ -0,0 +1,9 @@ +import { cleanEnv, port, str } from "envalid"; +import * as process from "process"; + +const env = cleanEnv(process.env, { + PORT: port(), + LOG_LEVEL: str({ default: "info" }), +}); + +export default env; diff --git a/src/logging/express-logger.ts b/src/logging/express-logger.ts new file mode 100644 index 0000000..41aa806 --- /dev/null +++ b/src/logging/express-logger.ts @@ -0,0 +1,14 @@ +import expressWinston from "express-winston"; +import winston from "winston"; + +const expressLogger = expressWinston.logger({ + transports: [new winston.transports.Console()], + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ), + meta: true, + expressFormat: true, +}); + +export default expressLogger; diff --git a/src/logging/logger.ts b/src/logging/logger.ts new file mode 100644 index 0000000..a7a50b6 --- /dev/null +++ b/src/logging/logger.ts @@ -0,0 +1,21 @@ +import winston from "winston"; +import environment from "../config/environment.js"; + +const logger = winston.createLogger({ + level: environment.LOG_LEVEL, + format: winston.format.json(), + defaultMeta: { service: "my-finance-pal" }, +}); + +if (!environment.isProd) { + logger.add( + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize({ all: true }), + winston.format.simple() + ), + }) + ); +} + +export default logger; diff --git a/src/middleware/error-handler.ts b/src/middleware/error-handler.ts new file mode 100644 index 0000000..cc317ec --- /dev/null +++ b/src/middleware/error-handler.ts @@ -0,0 +1,107 @@ +import type * as Http from "http"; +import logger from "../logging/logger.js"; +import * as util from "util"; +import { NextFunction, type Request, type Response } from "express"; + +let httpServerRef: Http.Server; + +export class AppError extends Error { + constructor( + public name: string, + public message: string, + public HTTPStatus: number = 500, + public isTrusted = true, + public cause?: unknown + ) { + super(message); + } +} + +// Listen to the global process-level error events +export const listenToErrorEvents = (httpServer: Http.Server): void => { + httpServerRef = httpServer; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + process.on("uncaughtException", (error) => { + handleError(error); + }); + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + process.on("unhandledRejection", (reason) => { + handleError(reason); + }); + + process.on("SIGTERM", () => { + logger.error( + "App received SIGTERM event, try to gracefully close the server" + ); + terminateHttpServerAndExit(); + }); + + process.on("SIGINT", () => { + logger.error( + "App received SIGINT event, try to gracefully close the server" + ); + terminateHttpServerAndExit(); + }); +}; + +const handleError = (errorToHandle: unknown): void => { + try { + const appError: AppError = normalizeError(errorToHandle); + logger.error(appError.message, appError); + // A common best practice is to crash when an unknown error (non-trusted) is being thrown + if (!appError.isTrusted) { + terminateHttpServerAndExit(); + } + } catch (handlingError: unknown) { + // Not using the logger here because it might have failed + process.stdout.write( + "The error handler failed, here are the handler failure and then the origin error that it tried to handle" + ); + process.stdout.write(JSON.stringify(handlingError)); + process.stdout.write(JSON.stringify(errorToHandle)); + } +}; + +const terminateHttpServerAndExit = (): void => { + // maybe implement more complex logic here (like using 'http-terminator' library) + httpServerRef?.close(); + process.exit(); +}; + +// The input might won't be 'AppError' or even 'Error' instance, the output of this function will be - AppError. +const normalizeError = (errorToHandle: unknown): AppError => { + if (errorToHandle instanceof AppError) { + return errorToHandle; + } + if (errorToHandle instanceof Error) { + const appError = new AppError(errorToHandle.name, errorToHandle.message); + appError.stack = errorToHandle.stack; + return appError; + } + // meaning it could be any type, + const inputType = typeof errorToHandle; + return new AppError( + "general-error", + `Error Handler received a none error instance with type - ${inputType}, value - ${util.inspect( + errorToHandle + )}` + ); +}; + +export const errorHandler = ( + err: Error, + _: Request, + res: Response, + next: NextFunction +): void => { + if (typeof err === "object") { + const error = err as AppError; + if (error.isTrusted === undefined || error.isTrusted === null) { + error.isTrusted = true; // Error during a specific request is usually not fatal and should not lead to process exit + } + } + // ✅ Best Practice: Pass all error to a centralized error handler so they get treated equally + handleError(err); + res.status((err as AppError).HTTPStatus ?? 500).end(); +}; diff --git a/src/models/Budget.ts b/src/models/Budget.ts new file mode 100644 index 0000000..b1e307f --- /dev/null +++ b/src/models/Budget.ts @@ -0,0 +1,22 @@ +import { v4 as uuidv4 } from "uuid"; +import { type Transaction } from "./Transaction.js"; + +export class BudgetId { + uuid: string; + + constructor() { + this.uuid = uuidv4(); + } +} + +export interface NewBudget { + name: string; + amount: number; + startDate?: Date; + endDate?: Date; +} + +export interface Budget extends NewBudget { + id: BudgetId; + transactions: Transaction[]; +} diff --git a/src/models/Transaction.ts b/src/models/Transaction.ts new file mode 100644 index 0000000..7a7a297 --- /dev/null +++ b/src/models/Transaction.ts @@ -0,0 +1,16 @@ +import { v4 as uuidv4 } from "uuid"; + +export class TransactionId { + uuid: string; + + constructor() { + this.uuid = uuidv4(); + } +} + +export interface Transaction { + id: TransactionId; + description: string; + amount: number; + date: Date; +} diff --git a/src/pre-start.ts b/src/pre-start.ts new file mode 100644 index 0000000..628f361 --- /dev/null +++ b/src/pre-start.ts @@ -0,0 +1,34 @@ +/** + * Pre-start is where we want to place things that must run BEFORE the express + * server is started. This is useful for environment variables, command-line + * arguments, and cron-jobs. + */ + +// NOTE: DO NOT IMPORT ANY SOURCE CODE HERE +import dotenv from "dotenv"; +import { parse } from "ts-command-line-args"; + +// **** Types **** // + +interface Args { + env: string; +} + +// **** Setup **** // + +// Command line arguments +const args = parse({ + env: { + type: String, + defaultValue: "development", + alias: "e", + }, +}); + +// Set the env file +const dotenvConfig = dotenv.config({ + path: new URL(`../env/${args.env}.env`, import.meta.url).pathname, +}); +if (dotenvConfig.error != null) { + throw dotenvConfig.error; +} diff --git a/src/repository/budget/BudgetMongoRepository.ts b/src/repository/budget/BudgetMongoRepository.ts new file mode 100644 index 0000000..d215511 --- /dev/null +++ b/src/repository/budget/BudgetMongoRepository.ts @@ -0,0 +1,12 @@ +import type BudgetRepository from "./BudgetRepository.js"; +import insertBudget from "./lib/insert-budget.js"; + +class BudgetMongoRepository implements BudgetRepository { + insertBudget: BudgetRepository["insertBudget"]; + + constructor() { + this.insertBudget = insertBudget(); + } +} + +export default BudgetMongoRepository; diff --git a/src/repository/budget/BudgetRepository.ts b/src/repository/budget/BudgetRepository.ts new file mode 100644 index 0000000..0bd8fed --- /dev/null +++ b/src/repository/budget/BudgetRepository.ts @@ -0,0 +1,7 @@ +import { type Budget } from "../../models/Budget.js"; + +interface BudgetRepository { + insertBudget: (budget: Budget) => Promise; +} + +export default BudgetRepository; diff --git a/src/repository/budget/lib/insert-budget.ts b/src/repository/budget/lib/insert-budget.ts new file mode 100644 index 0000000..f142f0c --- /dev/null +++ b/src/repository/budget/lib/insert-budget.ts @@ -0,0 +1,9 @@ +import { type Budget } from "../../../models/Budget.js"; + +const insertBudget = + () => + async (budget: Budget): Promise => { + return budget; + }; + +export default insertBudget; diff --git a/src/routes/ApiRouter.ts b/src/routes/ApiRouter.ts new file mode 100644 index 0000000..d5eab60 --- /dev/null +++ b/src/routes/ApiRouter.ts @@ -0,0 +1,16 @@ +import { Router } from "express"; +import type BudgetUseCases from "../usecase/budget/BudgetUseCases.js"; +import BudgetRouter from "./budget/BudgetRouter.js"; + +const paths = { + BUDGETS: "/budgets", +}; + +const ApiRouter = (budgetUseCases: BudgetUseCases): Router => { + const router = Router(); + router.use(paths.BUDGETS, BudgetRouter(budgetUseCases)); + + return router; +}; + +export default ApiRouter; diff --git a/src/routes/budget/BudgetRouter.ts b/src/routes/budget/BudgetRouter.ts new file mode 100644 index 0000000..99c97fc --- /dev/null +++ b/src/routes/budget/BudgetRouter.ts @@ -0,0 +1,17 @@ +import { Router } from "express"; +import type BudgetUseCases from "../../usecase/budget/BudgetUseCases.js"; +import createBudget from "./create-budget.js"; + +const paths = { + CREATE_BUDGET: "/", +}; + +const BudgetRouter = (budgetUseCases: BudgetUseCases): Router => { + const router = Router(); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + router.post(paths.CREATE_BUDGET, createBudget(budgetUseCases)); + + return router; +}; + +export default BudgetRouter; diff --git a/src/routes/budget/create-budget.ts b/src/routes/budget/create-budget.ts new file mode 100644 index 0000000..446e35c --- /dev/null +++ b/src/routes/budget/create-budget.ts @@ -0,0 +1,20 @@ +import { type NextFunction, type Request, type Response } from "express"; +import type BudgetUseCases from "../../usecase/budget/BudgetUseCases.js"; +import { NewBudgetConverter, type NewBudgetDto } from "./dto/budget.js"; +import { StatusCodes } from "http-status-codes"; + +const createBudget = + (budgetUseCases: BudgetUseCases) => + async (req: Request, res: Response, next: NextFunction): Promise => { + try { + const dto: NewBudgetDto = req.body; + const newBudget = NewBudgetConverter.toDomain(dto); + const createdBudget = await budgetUseCases.createBudget(newBudget); + + res.status(StatusCodes.CREATED).json(createdBudget); + } catch (error) { + next(error); + } + }; + +export default createBudget; diff --git a/src/routes/budget/dto/budget.ts b/src/routes/budget/dto/budget.ts new file mode 100644 index 0000000..f16a85b --- /dev/null +++ b/src/routes/budget/dto/budget.ts @@ -0,0 +1,29 @@ +import { type components } from "../../../../generated/api.js"; +import { Budget, type NewBudget } from "../../../models/Budget.js"; +import { toDate } from "../../../util/date.js"; +import { TransactionConverter } from "./transaction.js"; + +export type BudgetDto = components["schemas"]["Budget"]; +export type NewBudgetDto = components["schemas"]["NewBudget"]; + +export const NewBudgetConverter = { + toDomain: (dto: NewBudgetDto): NewBudget => ({ + name: dto.name, + amount: dto.amount, + startDate: toDate(dto.startDate), + endDate: toDate(dto.endDate), + }), +}; + +export const BudgetConverter = { + toDto: (domain: Budget): BudgetDto => ({ + id: domain.id.uuid, + amount: domain.amount, + name: domain.name, + startDate: domain.startDate?.toISOString(), + endDate: domain.endDate?.toISOString(), + transactions: domain.transactions.map((transaction) => + TransactionConverter.toDto(transaction) + ), + }), +}; diff --git a/src/routes/budget/dto/transaction.ts b/src/routes/budget/dto/transaction.ts new file mode 100644 index 0000000..0a5cc3b --- /dev/null +++ b/src/routes/budget/dto/transaction.ts @@ -0,0 +1,13 @@ +import { type components } from "../../../../generated/api.js"; +import { type Transaction } from "../../../models/Transaction.js"; + +export type TransactionDto = components["schemas"]["Transaction"]; + +export const TransactionConverter = { + toDto: (domain: Transaction): TransactionDto => ({ + id: domain.id.uuid, + amount: domain.amount, + description: domain.description, + date: domain.date.toISOString(), + }), +}; diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..1c20053 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,18 @@ +import "./pre-start.js"; +import * as http from "http"; +import app from "./app.js"; +import { listenToErrorEvents } from "./middleware/error-handler.js"; +import logger from "./logging/logger.js"; +import environment from "./config/environment.js"; + +const onListening = (server: http.Server) => (): void => { + const addr = server.address(); + const bind = + typeof addr === "string" ? `pipe ${addr}` : `port ${addr?.port ?? ""}`; + logger.info(`Listening on ${bind}`); +}; + +const server = http.createServer(app); +listenToErrorEvents(server); +server.on("listening", onListening(server)); +server.listen(environment.PORT); diff --git a/src/usecase/budget/BudgetUseCases.ts b/src/usecase/budget/BudgetUseCases.ts new file mode 100644 index 0000000..0e7b82e --- /dev/null +++ b/src/usecase/budget/BudgetUseCases.ts @@ -0,0 +1,13 @@ +import createBudget from "./lib/create-budget.js"; +import { type Budget, type NewBudget } from "../../models/Budget.js"; +import type BudgetRepository from "../../repository/budget/BudgetRepository.js"; + +class BudgetUseCases { + createBudget: (budget: NewBudget) => Promise; + + constructor(budgetRepository: BudgetRepository) { + this.createBudget = createBudget(budgetRepository); + } +} + +export default BudgetUseCases; diff --git a/src/usecase/budget/lib/create-budget.ts b/src/usecase/budget/lib/create-budget.ts new file mode 100644 index 0000000..10f3831 --- /dev/null +++ b/src/usecase/budget/lib/create-budget.ts @@ -0,0 +1,19 @@ +import type BudgetRepository from "../../../repository/budget/BudgetRepository.js"; +import { + type Budget, + BudgetId, + type NewBudget, +} from "../../../models/Budget.js"; + +const createBudget = + (repo: BudgetRepository) => + async (newBudget: NewBudget): Promise => { + const budget: Budget = { + id: new BudgetId(), + transactions: [], + ...newBudget, + }; + return await repo.insertBudget(budget); + }; + +export default createBudget; diff --git a/src/util/date.ts b/src/util/date.ts new file mode 100644 index 0000000..69cf57b --- /dev/null +++ b/src/util/date.ts @@ -0,0 +1,5 @@ +export const toDate = (isoString?: string): Date | undefined => { + if (isoString !== null && isoString !== undefined) { + return new Date(isoString); + } +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bf3775f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,84 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ESNext", + /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "NodeNext", + /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "dist", + /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, + /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "NodeNext", + /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "baseUrl": "./", + /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, + /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, + /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, + /* Disallow inconsistently-cased references to the same file. */ + "useUnknownInCatchVariables": false + }, + "include": [ + "src/**/*.ts", + "generated/**/*.ts", + "env/*.env" + ], +} \ No newline at end of file diff --git a/tsconfig.prod.json b/tsconfig.prod.json new file mode 100644 index 0000000..5ab3bdc --- /dev/null +++ b/tsconfig.prod.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "sourceMap": false, + "removeComments": true + }, + "exclude": [ + "spec", + ] +}