diff --git a/eslint.config.mjs b/eslint.config.mjs index 5669e32..484ba42 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,8 +1,10 @@ // @ts-check import withNuxt from "./.nuxt/eslint.config.mjs"; import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import nodePlugin from "eslint-plugin-n"; export default withNuxt(eslintPluginPrettierRecommended, { + plugins: { node: nodePlugin }, files: ["**/*.{ts,js,vue}"], rules: { // Vue rules @@ -13,5 +15,8 @@ export default withNuxt(eslintPluginPrettierRecommended, { "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-require-imports": "off", "@typescript-eslint/no-unused-vars": "warn", + + // Node rules + "node/no-process-env": "error", }, }); diff --git a/nuxt.config.ts b/nuxt.config.ts index 8394956..4b8c3f6 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,6 +1,7 @@ import { readFileSync } from "fs"; import { resolve } from "path"; import tailwindcss from "@tailwindcss/vite"; +import "./shared/utils/env"; const packageJsonContent = JSON.parse(readFileSync(resolve(__dirname, "package.json"), "utf-8")); diff --git a/package-lock.json b/package-lock.json index 666c2f4..6257557 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "shadcn-nuxt": "^2.4.3", "tailwind-merge": "^3.4.0", "vue": "^3.5.22", - "vue-router": "^4.6.3" + "vue-router": "^4.6.3", + "zod": "^4.2.1" }, "devDependencies": { "@nuxt/eslint": "^1.9.0", @@ -31,6 +32,7 @@ "@vueuse/nuxt": "^14.1.0", "eslint": "^9.38.0", "eslint-config-prettier": "^10.1.8", + "eslint-plugin-n": "^17.23.1", "eslint-plugin-prettier": "^5.5.4", "happy-dom": "^20.0.8", "husky": "^9.1.7", @@ -7261,6 +7263,22 @@ } } }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, "node_modules/eslint-config-flat-gitignore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-config-flat-gitignore/-/eslint-config-flat-gitignore-2.1.0.tgz", @@ -7344,6 +7362,28 @@ "eslint": "*" } }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, "node_modules/eslint-plugin-import-lite": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import-lite/-/eslint-plugin-import-lite-0.3.1.tgz", @@ -7429,6 +7469,56 @@ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, + "node_modules/eslint-plugin-n": { + "version": "17.23.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.1.tgz", + "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.0", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "globrex": "^0.1.2", + "ignore": "^5.3.2", + "semver": "^7.6.3", + "ts-declaration-location": "^1.0.6" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-n/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", @@ -8322,6 +8412,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -13940,6 +14037,29 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -15394,6 +15514,15 @@ "engines": { "node": ">= 14" } + }, + "node_modules/zod": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", + "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 9e55cb7..83554b8 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "shadcn-nuxt": "^2.4.3", "tailwind-merge": "^3.4.0", "vue": "^3.5.22", - "vue-router": "^4.6.3" + "vue-router": "^4.6.3", + "zod": "^4.2.1" }, "devDependencies": { "@nuxt/eslint": "^1.9.0", @@ -41,6 +42,7 @@ "@vueuse/nuxt": "^14.1.0", "eslint": "^9.38.0", "eslint-config-prettier": "^10.1.8", + "eslint-plugin-n": "^17.23.1", "eslint-plugin-prettier": "^5.5.4", "happy-dom": "^20.0.8", "husky": "^9.1.7", diff --git a/shared/utils/env.ts b/shared/utils/env.ts new file mode 100644 index 0000000..4fce706 --- /dev/null +++ b/shared/utils/env.ts @@ -0,0 +1,10 @@ +import { z } from "zod"; + +const EnvSchema = z.object({ + NODE_ENV: z.string(), +}); + +export type EnvSchema = z.infer; + +// eslint-disable-next-line node/no-process-env +export default EnvSchema.parse(process.env);