diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1502ec2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist +node_modules + diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d64eabc --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "semi": true, + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "es5" +} + diff --git a/README.md b/README.md index 956d585..b0fa4bb 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,20 @@ API gateway, usage metering, and billing services for the Callora API marketplac ## Scripts -| Command | Description | -|----------------|--------------------------------| -| `npm run dev` | Run with tsx watch (no build) | -| `npm run build`| Compile TypeScript to `dist/` | -| `npm start` | Run compiled `dist/index.js` | +| Command | Description | +| ------------------ | ------------------------------------ | +| `npm run dev` | Run with tsx watch (no build) | +| `npm run build` | Compile TypeScript to `dist/` | +| `npm start` | Run compiled `dist/index.js` | +| `npm run lint` | Run ESLint on `src/` | +| `npm run lint:fix` | Auto-fix ESLint issues | +| `npm run format` | Format `src/` + README with Prettier | + +## Linting and formatting + +- `npm run lint` runs ESLint on `src/` +- `npm run lint:fix` applies ESLint auto-fixes +- `npm run format` formats files with Prettier ## Project layout diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..070350b --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,57 @@ +import tsParser from '@typescript-eslint/parser'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; +import prettierPlugin from 'eslint-plugin-prettier'; +import prettierConfig from 'eslint-config-prettier'; +import eslintJs from '@eslint/js'; + +const globals = { + console: 'readonly', + process: 'readonly', + Buffer: 'readonly', + setImmediate: 'readonly', + clearImmediate: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + describe: 'readonly', + it: 'readonly', + test: 'readonly', + expect: 'readonly', + beforeAll: 'readonly', + afterAll: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + jest: 'readonly', +}; + +export default [ + { + ignores: ['dist/**', 'node_modules/**'], + }, + eslintJs.configs.recommended, + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tsParser, + ecmaVersion: 2022, + sourceType: 'module', + globals, + }, + plugins: { + '@typescript-eslint': tsPlugin, + prettier: prettierPlugin, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + ...prettierConfig.rules, + 'prettier/prettier': [ + 'error', + { + singleQuote: true, + semi: true, + }, + ], + }, + }, +]; diff --git a/eslintrc.json b/eslintrc.json deleted file mode 100644 index b28655a..0000000 --- a/eslintrc.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "env": { - "node": true, - "es2022": true, - "jest": true - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "ignorePatterns": ["dist", "node_modules"] -} \ No newline at end of file diff --git a/jest.config.js b/jest.config.cjs similarity index 100% rename from jest.config.js rename to jest.config.cjs diff --git a/package-lock.json b/package-lock.json index 6984af8..61db13c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "express": "^4.18.2" }, "devDependencies": { + "@eslint/js": "^9.0.0", "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^20.10.0", @@ -18,7 +19,10 @@ "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", "eslint": "^10.0.2", + "eslint-config-prettier": "^10.0.0", + "eslint-plugin-prettier": "^5.2.1", "jest": "^30.2.0", + "prettier": "^3.5.0", "supertest": "^7.2.2", "ts-jest": "^29.4.6", "tsx": "^4.7.0", @@ -56,7 +60,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1163,6 +1166,19 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@eslint/js": { + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, "node_modules/@eslint/object-schema": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", @@ -2742,7 +2758,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3058,7 +3073,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3730,7 +3744,6 @@ "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -3781,6 +3794,53 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", @@ -4073,6 +4133,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4836,7 +4903,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -6138,6 +6204,35 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -6959,7 +7054,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7158,7 +7252,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 1dc3f49..42f5664 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "build": "tsc", "start": "node dist/index.js", "dev": "tsx watch src/index.ts", - "lint": "eslint . --ext .ts", + "lint": "eslint \"src/**/*.ts\"", + "lint:fix": "npm run lint -- --fix", + "format": "prettier --write \"src/**/*.ts\" \"README.md\"", "typecheck": "tsc --noEmit", "test": "jest --runInBand" }, @@ -14,6 +16,7 @@ "express": "^4.18.2" }, "devDependencies": { + "@eslint/js": "^9.0.0", "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^20.10.0", @@ -21,7 +24,10 @@ "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", "eslint": "^10.0.2", + "eslint-config-prettier": "^10.0.0", + "eslint-plugin-prettier": "^5.2.1", "jest": "^30.2.0", + "prettier": "^3.5.0", "supertest": "^7.2.2", "ts-jest": "^29.4.6", "tsx": "^4.7.0", diff --git a/src/index.test.ts b/src/index.test.ts index 73a05dc..e7b8c11 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,4 @@ import request from 'supertest'; -import express from 'express'; import app from './index'; describe('Health API', () => { @@ -8,4 +7,4 @@ describe('Health API', () => { expect(response.status).toBe(200); expect(response.body.status).toBe('ok'); }); -}); \ No newline at end of file +}); diff --git a/src/index.ts b/src/index.ts index c40217b..07ea34c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,4 +23,4 @@ if (process.env.NODE_ENV !== 'test') { }); } -export default app; \ No newline at end of file +export default app;