From 96fe2715453e1b101d636eff7ea2a9805571b51d Mon Sep 17 00:00:00 2001 From: KaKa <23028015+climba03003@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:10:01 +0800 Subject: [PATCH] feat: config (#10) * feat: add config --- .github/workflows/ci-config.yml | 115 +++++++++++++++++++++++ packages/config/.eslintrc | 24 +++++ packages/config/.npmignore | 132 +++++++++++++++++++++++++++ packages/config/README.md | 36 ++++++++ packages/config/lib/index.ts | 28 ++++++ packages/config/lib/mjs/package.json | 3 + packages/config/package.json | 60 ++++++++++++ packages/config/test/index.test.ts | 47 ++++++++++ packages/config/tsconfig.cjs.json | 7 ++ packages/config/tsconfig.json | 25 +++++ packages/config/tsconfig.mjs.json | 15 +++ pnpm-lock.yaml | 76 +++++++++++++-- 12 files changed, 562 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/ci-config.yml create mode 100644 packages/config/.eslintrc create mode 100644 packages/config/.npmignore create mode 100644 packages/config/README.md create mode 100644 packages/config/lib/index.ts create mode 100644 packages/config/lib/mjs/package.json create mode 100644 packages/config/package.json create mode 100644 packages/config/test/index.test.ts create mode 100644 packages/config/tsconfig.cjs.json create mode 100644 packages/config/tsconfig.json create mode 100644 packages/config/tsconfig.mjs.json diff --git a/.github/workflows/ci-config.yml b/.github/workflows/ci-config.yml new file mode 100644 index 00000000..d10792ce --- /dev/null +++ b/.github/workflows/ci-config.yml @@ -0,0 +1,115 @@ +name: Continuous Integration - Config + +on: + push: + paths: + - ".github/workflows/ci-config.yml" + - "packages/config/**" + pull_request: + paths: + - ".github/workflows/ci-config.yml" + - "packages/config/**" + +jobs: + linter: + name: Lint Code + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check out repo + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Lint code + run: pnpm --filter "./packages/config" run lint + + test: + name: Test + runs-on: ${{ matrix.os }} + permissions: + contents: read + strategy: + matrix: + node-version: [18, 20] + os: [macos-latest, ubuntu-latest, windows-latest] + steps: + - name: Check out repo + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-${{ matrix.node-version }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.node-version }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Run tests + run: pnpm --filter "./packages/config" run test + + automerge: + name: Automerge Dependabot PRs + if: > + github.event_name == 'pull_request' && + github.event.pull_request.user.login == 'dependabot[bot]' + needs: test + permissions: + pull-requests: write + contents: write + runs-on: ubuntu-latest + steps: + - uses: fastify/github-action-merge-dependabot@v3 + with: + exclude: ${{ inputs.auto-merge-exclude }} + github-token: ${{ secrets.GITHUB_TOKEN }} + target: major diff --git a/packages/config/.eslintrc b/packages/config/.eslintrc new file mode 100644 index 00000000..ea67ea7d --- /dev/null +++ b/packages/config/.eslintrc @@ -0,0 +1,24 @@ +{ + "extends": "standard-with-typescript", + "parserOptions": { + "project": "./tsconfig.json" + }, + "rules": { + // conflict between standard and standard-typescript + "no-void": ["error", { "allowAsStatement": true }] + }, + "overrides": [ + { + "files": ["**/*.test.ts"], + "rules": { + "@typescript-eslint/no-floating-promises": "off" + } + }, + { + "files": ["scripts/*.mjs"], + "rules": { + "@typescript-eslint/explicit-function-return-type": "off" + } + } + ] +} diff --git a/packages/config/.npmignore b/packages/config/.npmignore new file mode 100644 index 00000000..5cd421af --- /dev/null +++ b/packages/config/.npmignore @@ -0,0 +1,132 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Project Source Files +.yarn +test/ +bin/ +.vscode/ +.github/ + +.eslint* +.git* +.prettier* +jest* + +lib/**/*.ts +!lib/**/*.d.ts +scripts/ \ No newline at end of file diff --git a/packages/config/README.md b/packages/config/README.md new file mode 100644 index 00000000..a13e3719 --- /dev/null +++ b/packages/config/README.md @@ -0,0 +1,36 @@ +# @kakang/fastify-config + +[![Continuous Integration](https://github.com/kaka-repo/fastify-plugins/actions/workflows/ci-config.yml/badge.svg)](https://github.com/kaka-repo/fastify-plugins/actions/workflows/ci-config.yml) +[![NPM version](https://img.shields.io/npm/v/@kakang/fastify-config.svg?style=flat)](https://www.npmjs.com/package/@kakang/fastify-config) + +This plugin use `env-schema` to read the .env file and provides +the value through variables and fastify plugin. + +## Install + +```bash +npm install @kakang/fastify-config --save + +yarn add @kakang/fastify-config +``` + +## Usage + +```ts +import { build } from '@kakang/fastify-config' + +const { env, plugin } = build({ + schema: { + type: 'object', + properties: { + FOO: { type: 'string' } + } + }, + data: process.env + dotenv: true +}) + +fastify.register(plugin) + +fastify.config // here is the env value +``` diff --git a/packages/config/lib/index.ts b/packages/config/lib/index.ts new file mode 100644 index 00000000..7e430e2c --- /dev/null +++ b/packages/config/lib/index.ts @@ -0,0 +1,28 @@ +import envSchema, { type EnvSchemaData, type EnvSchemaOpt } from 'env-schema' +import { type FastifyPluginCallback } from 'fastify' +import FastifyPlugin from 'fastify-plugin' + +export function build (options: EnvSchemaOpt): { env: T, plugin: FastifyPluginCallback } { + const env = envSchema(options) + + const plugin: FastifyPluginCallback = function (fastify, _, done) { + fastify.decorate('config', env as unknown) + done() + } + FastifyPlugin(plugin, { + fastify: '4.x', + name: '@kakang/fastify-config', + decorators: { + fastify: [], + request: [], + reply: [] + }, + dependencies: [], + encapsulate: false + }) + + return { + env, + plugin + } +} diff --git a/packages/config/lib/mjs/package.json b/packages/config/lib/mjs/package.json new file mode 100644 index 00000000..3dbc1ca5 --- /dev/null +++ b/packages/config/lib/mjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/config/package.json b/packages/config/package.json new file mode 100644 index 00000000..50a68ca0 --- /dev/null +++ b/packages/config/package.json @@ -0,0 +1,60 @@ +{ + "name": "@kakang/fastify-config", + "version": "0.0.2", + "description": "Fastify plugin for env config", + "keywords": [ + "fastify", + "kakang" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "exports": { + ".": { + "import": "./lib/mjs/index.js", + "require": "./lib/index.js" + } + }, + "scripts": { + "clean": "node ../../scripts/build.mjs --clean", + "lint": "eslint --ext .ts lib test", + "lint:fix": "npm run lint -- --fix", + "build": "node ../../scripts/build.mjs --build=\"all\"", + "build:cjs": "node ../../scripts/build.mjs --build='cjs'", + "build:mjs": "node ../../scripts/build.mjs --build='mjs'", + "unit": "node --require ts-node/register ../../scripts/test.mjs", + "test": "npm run lint && npm run unit", + "coverage": "c8 node --require ts-node/register ./test/run.ts", + "prepublishOnly": "npm run build", + "postpublish": "npm run clean" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "repository": { + "type": "git", + "url": "https://github.com/kaka-repo/fastify-plugins.git" + }, + "author": "KaKa ", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.10.7", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", + "c8": "^9.0.0", + "eslint": "^8.56.0", + "eslint-config-standard-with-typescript": "^43.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.1", + "eslint-plugin-promise": "^6.1.1", + "fastify": "^4.25.2", + "rimraf": "^5.0.5", + "ts-node": "^10.9.2", + "tsc-alias": "^1.8.8", + "typescript": "^5.3.3" + }, + "dependencies": { + "env-schema": "^5.2.1", + "fastify-plugin": "^4.5.1" + } +} diff --git a/packages/config/test/index.test.ts b/packages/config/test/index.test.ts new file mode 100644 index 00000000..e1da4871 --- /dev/null +++ b/packages/config/test/index.test.ts @@ -0,0 +1,47 @@ +import Fastify from 'fastify' +import assert from 'node:assert/strict' +import { rm, writeFile } from 'node:fs/promises' +import { resolve } from 'node:path' +import { test } from 'node:test' +import { build } from '../lib' + +test('envfile', async function (t) { + const tmpEnv = resolve('.tmp.env') + + t.before(async function () { + await writeFile(tmpEnv, 'FOO=BAR\nNOT=EXIST') + }) + + t.after(async function () { + await rm(tmpEnv, { force: true }) + }) + + await t.test('', async function () { + const { env, plugin } = build({ + schema: { + type: 'object', + properties: { + FOO: { type: 'string' } + } + }, + data: process.env, + dotenv: { path: tmpEnv } + }) + + assert.ok(env) + assert.equal(env.FOO, 'BAR') + assert.equal(typeof env.NOT, 'undefined') + assert.equal(typeof plugin, 'function') + + const fastify = Fastify() + fastify.register(plugin) + await fastify.ready() + + // @ts-expect-error we need to types by client + assert.ok(fastify.config) + // @ts-expect-error we need to types by client + assert.equal(fastify.config.FOO, 'BAR') + // @ts-expect-error we need to types by client + assert.equal(typeof fastify.config.NOT, 'undefined') + }) +}) diff --git a/packages/config/tsconfig.cjs.json b/packages/config/tsconfig.cjs.json new file mode 100644 index 00000000..97d519dc --- /dev/null +++ b/packages/config/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["lib/**/*"] +} diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json new file mode 100644 index 00000000..e2f72419 --- /dev/null +++ b/packages/config/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "CommonJS", + "target": "ES2018", + "moduleResolution": "Node", + + "resolveJsonModule": true, + + "removeComments": true, + "preserveConstEnums": true, + + "sourceMap": true, + + "declaration": true, + + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + + "skipLibCheck": true, + "esModuleInterop": true + }, + "exclude": ["node_modules"] +} diff --git a/packages/config/tsconfig.mjs.json b/packages/config/tsconfig.mjs.json new file mode 100644 index 00000000..3b2981ae --- /dev/null +++ b/packages/config/tsconfig.mjs.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "outDir": "lib/mjs" + }, + "include": ["lib/**/*"], + "tsc-alias": { + "resolveFullPaths": true, + "fileExtensions": { + "inputGlob": "{js,jsx,mjs}", + "outputCheck": ["js", "json", "jsx", "mjs"] + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa0be46f..3c85c37d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,58 @@ importers: .: {} + packages/config: + dependencies: + env-schema: + specifier: ^5.2.1 + version: 5.2.1 + fastify-plugin: + specifier: ^4.5.1 + version: 4.5.1 + devDependencies: + '@types/node': + specifier: ^20.10.7 + version: 20.10.8 + '@typescript-eslint/eslint-plugin': + specifier: ^6.17.0 + version: 6.17.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.17.0 + version: 6.17.0(eslint@8.56.0)(typescript@5.3.3) + c8: + specifier: ^9.0.0 + version: 9.0.0 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + eslint-config-standard-with-typescript: + specifier: ^43.0.0 + version: 43.0.0(@typescript-eslint/eslint-plugin@6.17.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.1)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: + specifier: ^2.29.1 + version: 2.29.1(@typescript-eslint/parser@6.17.0)(eslint@8.56.0) + eslint-plugin-n: + specifier: ^16.6.1 + version: 16.6.1(eslint@8.56.0) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.1.1(eslint@8.56.0) + fastify: + specifier: ^4.25.2 + version: 4.25.2 + rimraf: + specifier: ^5.0.5 + version: 5.0.5 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.10.8)(typescript@5.3.3) + tsc-alias: + specifier: ^1.8.8 + version: 1.8.8 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + packages/cronjob: dependencies: cron-parser: @@ -739,7 +791,6 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - dev: true /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -1127,6 +1178,16 @@ packages: esutils: 2.0.3 dev: true + /dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dev: false + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: false + /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true @@ -1139,6 +1200,14 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /env-schema@5.2.1: + resolution: {integrity: sha512-gWMNrQ3dVHAZcCx7epiFwgXcyfBh4heD/6+OK3bEbke3uL+KqwYA9nUOwzJyRZh1cJOFcwdPuY1n0GKSFlSWAg==} + dependencies: + ajv: 8.12.0 + dotenv: 16.3.1 + dotenv-expand: 10.0.0 + dev: false + /es-abstract@1.22.3: resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} engines: {node: '>= 0.4'} @@ -1578,7 +1647,6 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} @@ -2152,7 +2220,6 @@ packages: /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2513,7 +2580,6 @@ packages: /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - dev: true /queue-lit@1.5.2: resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} @@ -2568,7 +2634,6 @@ packages: /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: true /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -3012,7 +3077,6 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 - dev: true /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}