@uscreen.de/create-fastify-app 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.eslintcache +1 -1
  2. package/.github/dependabot.yml +11 -0
  3. package/.github/workflows/main.yml +51 -0
  4. package/.gitignore +2 -0
  5. package/README.md +22 -0
  6. package/bin/cli.js +27 -68
  7. package/package.json +15 -5
  8. package/skeleton/.gitignore +2 -1
  9. package/skeleton/.nvmrc +1 -1
  10. package/skeleton/Makefile +1 -19
  11. package/skeleton/README.md +0 -4
  12. package/skeleton/app/config.js +3 -7
  13. package/skeleton/app/server.js +13 -26
  14. package/skeleton/package.json +12 -21
  15. package/skeleton/test/helper.js +2 -6
  16. package/test/_arena/new-app/.editorconfig +13 -0
  17. package/test/_arena/new-app/.env.example +4 -0
  18. package/test/_arena/new-app/.envrc +4 -0
  19. package/test/_arena/new-app/.eslintrc +3 -0
  20. package/test/_arena/new-app/.gitignore +7 -0
  21. package/test/_arena/new-app/.gitlab-ci.yml.example +25 -0
  22. package/test/_arena/new-app/.husky/_/.gitignore +1 -0
  23. package/test/_arena/new-app/.nvmrc +1 -0
  24. package/test/_arena/new-app/.taprc +7 -0
  25. package/test/_arena/new-app/Makefile +11 -0
  26. package/test/_arena/new-app/README.md +38 -0
  27. package/test/_arena/new-app/app/app.js +17 -0
  28. package/test/_arena/new-app/app/config.js +34 -0
  29. package/test/_arena/new-app/app/plugins/noop.js +14 -0
  30. package/test/_arena/new-app/app/schemas.js +34 -0
  31. package/test/_arena/new-app/app/server.js +37 -0
  32. package/test/_arena/new-app/app/services/noop.js +22 -0
  33. package/test/_arena/new-app/package.json +40 -0
  34. package/test/_arena/new-app/test/app/noop.test.js +54 -0
  35. package/test/_arena/new-app/test/helper.js +29 -0
  36. package/test/cli.test.js +109 -0
  37. package/test/setup.js +33 -0
  38. package/skeleton/app/modules/common-esm.js +0 -17
  39. package/skeleton/pm2-dev.config.cjs +0 -16
  40. package/skeleton/pm2.config.cjs +0 -29
package/.eslintcache CHANGED
@@ -1 +1 @@
1
- [{"/Users/martin/repos/uscreen/npm/create-fastify-app/bin/cli.js":"1"},{"size":4769,"mtime":1669380908819,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","suppressedMessages":"6","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1s60doj","/Users/martin/repos/uscreen/npm/create-fastify-app/bin/cli.js",[],[]]
1
+ [{"/Users/martin/repos/uscreen/npm/create-fastify-app/bin/cli.js":"1","/Users/martin/repos/uscreen/npm/create-fastify-app/test/cli.test.js":"2","/Users/martin/repos/uscreen/npm/create-fastify-app/test/setup.js":"3"},{"size":3522,"mtime":1675168988666,"results":"4","hashOfConfig":"5"},{"size":2397,"mtime":1675169193473,"results":"6","hashOfConfig":"5"},{"size":717,"mtime":1675081403094,"results":"7","hashOfConfig":"5"},{"filePath":"8","messages":"9","suppressedMessages":"10","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1ohqtje",{"filePath":"11","messages":"12","suppressedMessages":"13","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"14","messages":"15","suppressedMessages":"16","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/Users/martin/repos/uscreen/npm/create-fastify-app/bin/cli.js",[],[],"/Users/martin/repos/uscreen/npm/create-fastify-app/test/cli.test.js",[],[],"/Users/martin/repos/uscreen/npm/create-fastify-app/test/setup.js",[],[]]
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "npm"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,51 @@
1
+ # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3
+
4
+ name: Test CI
5
+
6
+ on:
7
+ push:
8
+ branches: ["master"]
9
+ pull_request:
10
+ branches: ["master"]
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ node-version: [14, 16, 18, 19]
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v3
21
+
22
+ - name: Use Node.js ${{ matrix.node-version }}
23
+ uses: actions/setup-node@v3
24
+ with:
25
+ node-version: ${{ matrix.node-version }}
26
+ cache: "npm"
27
+
28
+ - name: Install packages
29
+ run: npm install
30
+
31
+ - name: Run tests
32
+ run: npm run test:ci
33
+
34
+ - name: Coveralls
35
+ uses: coverallsapp/github-action@master
36
+ with:
37
+ github-token: ${{ secrets.GITHUB_TOKEN }}
38
+
39
+ automerge:
40
+ if: >
41
+ github.event_name == 'pull_request' &&
42
+ github.event.pull_request.user.login == 'dependabot[bot]'
43
+ needs: test
44
+ runs-on: ubuntu-latest
45
+ permissions:
46
+ pull-requests: write
47
+ contents: write
48
+ steps:
49
+ - uses: fastify/github-action-merge-dependabot@v3
50
+ with:
51
+ github-token: ${{ secrets.GITHUB_TOKEN }}
package/.gitignore CHANGED
@@ -2,5 +2,7 @@
2
2
  .DS_Store
3
3
  yarn-error.log
4
4
  /.nyc_output
5
+ /coverage
5
6
  /skeleton/yarn.lock
7
+ /test/_arena
6
8
  /.eslintcache
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # create-fastify-app
2
2
 
3
+ [![Test CI](https://github.com/uscreen/create-fastify-app/actions/workflows/main.yml/badge.svg)](https://github.com/uscreen/create-fastify-app/actions/workflows/main.yml)
4
+ [![Test Coverage](https://coveralls.io/repos/github/uscreen/create-fastify-app/badge.svg?branch=master)](https://coveralls.io/github/uscreen/create-fastify-app?branch=master)
5
+ [![Known Vulnerabilities](https://snyk.io/test/github/uscreen/create-fastify-app/badge.svg?targetFile=package.json)](https://snyk.io/test/github/uscreen/create-fastify-app?targetFile=package.json)
6
+ [![NPM Version](https://badge.fury.io/js/@uscreen.de%2Fcreate-fastify-app.svg)](https://badge.fury.io/js/@uscreen.de%2Fcreate-fastify-app)
7
+
3
8
  > cli to quickly bootstrap a new fastify-app
4
9
 
5
10
  This package provides the cli skript to create a new `@uscreen.de/fastify-app` from scratch. No need to install any other prerequisites than node and yarn. Features include:
@@ -68,6 +73,23 @@ $ make logs
68
73
 
69
74
  ## Changelog
70
75
 
76
+ ### 1.1.0
77
+
78
+ #### Changed
79
+
80
+ - migrated yorkie to husky
81
+ - cleanup app.js and server.js
82
+
83
+ #### Added
84
+
85
+ - uses @uscreen.de/common-esm for esm migration
86
+ - uses @uscreen.de/fastify-app options for app creation
87
+ - uses nodemon for dev mode
88
+
89
+ #### Removed
90
+
91
+ - dropped pm2 package and config
92
+
71
93
  ### 1.0.0
72
94
 
73
95
  #### Changed
package/bin/cli.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  import path from 'path'
4
4
  import { fileURLToPath } from 'url'
5
+ import { createRequire } from 'module'
6
+
5
7
  import { program } from 'commander'
6
8
  import { readPackageUpSync } from 'read-pkg-up'
7
9
  import { writePackage } from 'write-pkg'
@@ -11,6 +13,8 @@ import { spawn } from 'child_process'
11
13
 
12
14
  const __filename = fileURLToPath(import.meta.url)
13
15
  const __dirname = path.dirname(__filename)
16
+ const require = createRequire(import.meta.url)
17
+ const { version } = require('../package.json')
14
18
 
15
19
  let root
16
20
  let skeleton
@@ -25,6 +29,7 @@ const initializeGitRepository = (path) =>
25
29
  git.stderr.on('data', (data) => process.stderr.write(data))
26
30
  git.on('close', (code) => {
27
31
  if (code === 0) return resolve(code)
32
+ /* c8 ignore next */
28
33
  reject(code)
29
34
  })
30
35
  })
@@ -32,57 +37,35 @@ const initializeGitRepository = (path) =>
32
37
  /**
33
38
  * init new yarn project
34
39
  */
35
- const initializeYarn = (path) =>
40
+ const initializeYarn = (path, { yes } = {}) =>
36
41
  new Promise((resolve, reject) => {
37
- const yarn = spawn('yarn', ['init'], {
42
+ const params = ['init']
43
+ if (yes) params.push('-y')
44
+
45
+ const yarn = spawn('yarn', params, {
38
46
  cwd: path,
39
47
  stdio: [0, 1, 2]
40
48
  })
41
49
  yarn.on('close', (code) => {
42
50
  if (code === 0) return resolve(code)
51
+ /* c8 ignore next */
43
52
  reject(code)
44
53
  })
45
54
  })
46
55
 
47
56
  /**
48
- * install extra dev packages from skeleleton
49
- */
50
- const installDevPackages = (appPath, skelPath) => {
51
- const skelPack = readPackageUpSync({ cwd: skelPath })
52
- const devDependencies = Object.entries(
53
- skelPack.packageJson.devDependencies
54
- ).map(([key, val]) => `${key}@${val}`)
55
-
56
- return new Promise((resolve, reject) => {
57
- const yarn = spawn('yarn', ['add', ...devDependencies, '-D'], {
58
- cwd: appPath
59
- })
60
- yarn.stdout.on('data', (data) => process.stdout.write(data))
61
- yarn.stderr.on('data', (data) => process.stderr.write(data))
62
- yarn.on('close', (code) => {
63
- if (code === 0) return resolve(code)
64
- reject(code)
65
- })
66
- })
67
- }
68
-
69
- /**
70
- * install extra prod packages from skeleleton
57
+ * install packages
71
58
  */
72
- const installPackages = (appPath, skelPath) => {
73
- const skelPack = readPackageUpSync({ cwd: skelPath })
74
- const dependencies = Object.entries(skelPack.packageJson.dependencies).map(
75
- ([key, val]) => `${key}@${val}`
76
- )
77
-
59
+ const installPackages = (appPath) => {
78
60
  return new Promise((resolve, reject) => {
79
- const yarn = spawn('yarn', ['add', ...dependencies], {
61
+ const yarn = spawn('yarn', ['install'], {
80
62
  cwd: appPath
81
63
  })
82
64
  yarn.stdout.on('data', (data) => process.stdout.write(data))
83
65
  yarn.stderr.on('data', (data) => process.stderr.write(data))
84
66
  yarn.on('close', (code) => {
85
67
  if (code === 0) return resolve(code)
68
+ /* c8 ignore next */
86
69
  reject(code)
87
70
  })
88
71
  })
@@ -98,25 +81,13 @@ const addPackageConfig = (path, skelPath) => {
98
81
  delete pack.packageJson.readme
99
82
 
100
83
  pack.packageJson.main = skelPack.packageJson.main
101
- pack.packageJson.type = 'module'
102
-
103
- pack.packageJson.scripts = Object.assign(pack.packageJson.scripts || {}, {
104
- start: 'pm2 start pm2-dev.config.cjs',
105
- stop: 'pm2 delete pm2-dev.config.cjs',
106
- logs: `pm2 logs ${pack.packageJson.name} --raw | pino-pretty -t`,
107
- lint: "eslint '**/*.js' --fix",
108
- test: 'tap test/**/*.test.js',
109
- 'test:cov': 'tap --coverage-report=html test/**/*.test.js',
110
- 'test:ci': 'tap --coverage-report=text-summary test/**/*.test.js',
111
- deploy: 'pm2 deploy pm2.config.cjs',
112
- postdeploy: 'pm2 reload pm2.config.cjs'
113
- })
114
- pack.packageJson.gitHooks = {
115
- 'pre-commit': 'lint-staged'
116
- }
117
- pack.packageJson['lint-staged'] = {
118
- '*.{js}': ['eslint --fix', 'git add']
119
- }
84
+ pack.packageJson.type = skelPack.packageJson.type
85
+ pack.packageJson.scripts = skelPack.packageJson.scripts
86
+ pack.packageJson['lint-staged'] = skelPack.packageJson['lint-staged']
87
+
88
+ pack.packageJson.dependencies = skelPack.packageJson.dependencies
89
+ pack.packageJson.devDependencies = skelPack.packageJson.devDependencies
90
+
120
91
  return writePackage(pack.path, pack.packageJson)
121
92
  }
122
93
 
@@ -144,14 +115,10 @@ const copyEnv = (appPath, skelPath) => {
144
115
  * define the command
145
116
  */
146
117
  program
147
- .version('0.1.0')
148
- .arguments('<name> [opt]')
118
+ .version(version)
119
+ .arguments('<name>')
120
+ .option('-y --yes')
149
121
  .action(async (name, opt) => {
150
- if (typeof name === 'undefined') {
151
- console.error('please specify your new apps name...')
152
- process.exit(1)
153
- }
154
-
155
122
  /**
156
123
  * the root directory of new project
157
124
  */
@@ -166,10 +133,9 @@ program
166
133
  * setup new app
167
134
  */
168
135
  await initializeGitRepository(root)
169
- await initializeYarn(root)
170
- await installDevPackages(root, skeleton)
171
- await installPackages(root, skeleton)
136
+ await initializeYarn(root, opt)
172
137
  await addPackageConfig(root, skeleton)
138
+ await installPackages(root)
173
139
  await copySkeleton(root, skeleton)
174
140
  await copyEnv(root, skeleton)
175
141
  })
@@ -178,10 +144,3 @@ program
178
144
  * read args
179
145
  */
180
146
  program.parse(process.argv)
181
-
182
- /**
183
- * output help as default
184
- */
185
- if (!process.argv.slice(2).length) {
186
- program.help()
187
- }
package/package.json CHANGED
@@ -1,29 +1,39 @@
1
1
  {
2
2
  "name": "@uscreen.de/create-fastify-app",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "cli to create a new @uscreen.de/fastify-app",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
- "repository": "git@gitlab.uscreen.net:uscreen/npm/create-fastify-app.git",
7
+ "homepage": "https://github.com/uscreen/create-fastify-app",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/uscreen/create-fastify-app.git"
11
+ },
8
12
  "author": "Marcus Spiegel <spiegel@uscreen.de>",
9
13
  "license": "MIT",
10
14
  "bin": {
11
15
  "create-fastify-app": "./bin/cli.js"
12
16
  },
13
17
  "scripts": {
18
+ "test": "c8 tap",
19
+ "test:cov": "c8 --reporter=html --reporter=text tap",
20
+ "test:ci": "c8 --reporter=lcovonly tap",
14
21
  "lint": "eslint --fix",
15
22
  "prepare": "husky install"
16
23
  },
17
24
  "dependencies": {
18
- "commander": "^9.4.1",
19
- "fs-extra": "^10.0.1",
25
+ "commander": "^10.0.0",
26
+ "fs-extra": "^11.1.0",
20
27
  "read-pkg-up": "^9.1.0",
21
28
  "write-pkg": "^5.0.0"
22
29
  },
23
30
  "devDependencies": {
24
31
  "@uscreen.de/eslint-config-prettystandard-node": "^0.2.10",
32
+ "c8": "^7.12.0",
25
33
  "husky": ">=8.0.2",
26
- "lint-staged": ">=13.0.3"
34
+ "lint-staged": ">=13.0.3",
35
+ "strip-ansi": "^7.0.1",
36
+ "tap": "^16.3.4"
27
37
  },
28
38
  "lint-staged": {
29
39
  "*.js": "eslint --cache --fix"
@@ -3,4 +3,5 @@ node_modules
3
3
  /.nyc_output
4
4
  /coverage
5
5
  .DS_Store
6
- yarn-error.log
6
+ yarn-error.log
7
+ /.eslintcache
package/skeleton/.nvmrc CHANGED
@@ -1 +1 @@
1
- 16
1
+ 18
package/skeleton/Makefile CHANGED
@@ -1,12 +1,6 @@
1
1
  start:
2
2
  yarn
3
- yarn start
4
-
5
- stop:
6
- yarn stop
7
-
8
- logs:
9
- yarn logs
3
+ yarn dev
10
4
 
11
5
  test:
12
6
  yarn test
@@ -14,16 +8,4 @@ test:
14
8
  test.coverage:
15
9
  yarn test:cov
16
10
 
17
- deploy:
18
- yarn deploy stage
19
-
20
- deploy.setup:
21
- yarn deploy stage setup
22
-
23
- live.deploy:
24
- yarn deploy live
25
-
26
- live.deploy.setup:
27
- yarn deploy live setup
28
-
29
11
  .PHONY: test
@@ -6,11 +6,7 @@
6
6
  ## Usage
7
7
 
8
8
  * `make start`: start app on local dev
9
- * `make logs`: watch logs
10
- * `make stop`: stop app
11
9
  * `make test`: test app
12
- * `make deploy`: deploy app to stage
13
- * `make live.deploy`: deploy app to live
14
10
 
15
11
  ## Configure
16
12
 
@@ -1,8 +1,7 @@
1
- import path from 'path'
2
1
  import envSchema from 'env-schema'
3
- import { dirname } from './modules/common-esm.js'
2
+ import CommonESM from '@uscreen.de/common-esm'
4
3
 
5
- const __dirname = dirname(import.meta.url)
4
+ const { join } = new CommonESM(import.meta.url)
6
5
 
7
6
  const schema = {
8
7
  type: 'object',
@@ -20,10 +19,7 @@ const config = envSchema({
20
19
  dotenv: true
21
20
  })
22
21
 
23
- config.autoloads = [
24
- path.join(__dirname, 'plugins'),
25
- path.join(__dirname, 'services')
26
- ]
22
+ config.autoloads = [join('plugins'), join('services')]
27
23
 
28
24
  config.swagger = {
29
25
  routePrefix: `${config.prefix}/docs`,
@@ -1,29 +1,9 @@
1
1
  import fastify from 'fastify'
2
- import hyperid from 'hyperid'
2
+ import { options } from '@uscreen.de/fastify-app'
3
3
  import config from './config.js'
4
4
  import app from './app.js'
5
- import { json } from './modules/common-esm.js'
6
5
 
7
- const { name, version } = json(import.meta.url, '../package.json')
8
-
9
- const instance = hyperid({ urlSafe: true })
10
-
11
- const server = fastify({
12
- genReqId() {
13
- return instance()
14
- },
15
-
16
- logger: config.logEnabled
17
- ? {
18
- level: config.logLevel,
19
- name: `${name} (v${version}) ${process.env.NODE_APP_INSTANCE}`,
20
- redact: {
21
- paths: ['pattern'], // limit hemeras verbosity
22
- remove: true
23
- }
24
- }
25
- : false
26
- })
6
+ const server = fastify(options(config))
27
7
 
28
8
  server.register(app, config)
29
9
 
@@ -32,17 +12,24 @@ server.register(app, config)
32
12
  */
33
13
  server.ready((err) => {
34
14
  if (err) throw err
35
- server.log.debug('server ready, routes are set:\n' + server.printRoutes())
15
+ server.log.debug(
16
+ 'server ready, routes are set:\n' +
17
+ server.printRoutes({ commonPrefix: false })
18
+ )
36
19
  })
37
20
 
38
21
  /**
39
22
  * graceful shutdown (closing handles, etc.)
40
23
  */
41
- process.on('SIGINT', async () => {
42
- server.log.info(`server shutting down.`)
24
+ const shutdown = async () => {
25
+ server.log.info(
26
+ `application shutting down. (${server.app.name} ${server.app.version})`
27
+ )
43
28
  await server.close()
44
29
  process.exit()
45
- })
30
+ }
31
+ process.on('SIGINT', shutdown)
32
+ process.on('SIGTERM', shutdown)
46
33
 
47
34
  /**
48
35
  * start http server
@@ -4,41 +4,32 @@
4
4
  "main": "app/server.js",
5
5
  "type": "module",
6
6
  "dependencies": {
7
+ "@uscreen.de/common-esm": "^1.0.0",
7
8
  "@uscreen.de/fastify-app": "^1.0.0-0",
8
- "desm": "^1.3.0",
9
9
  "env-schema": "^5.1.1",
10
10
  "fastify": "^4.10.2",
11
11
  "fastify-plugin": "^4.3.0",
12
12
  "fluent-json-schema": "^4.0.0",
13
- "hyperid": "^3.0.1"
13
+ "pino-pretty": "^9.1.1"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@uscreen.de/eslint-config-prettystandard-node": "^0.2.8",
17
17
  "c8": "^7.12.0",
18
- "lint-staged": "^13.0.3",
19
- "pino-pretty": "^9.1.1",
20
- "pm2": "^5.2.2",
21
- "prettier": "^2.7.1",
22
- "tap": "^16.3.2",
23
- "yorkie": "^2.0.0"
18
+ "husky": ">=6",
19
+ "lint-staged": ">=10",
20
+ "nodemon": "^2.0.20",
21
+ "prettier": "^2.8.0",
22
+ "tap": "^16.3.2"
24
23
  },
25
24
  "scripts": {
26
- "start": "pm2 start pm2-dev.config.cjs",
27
- "stop": "pm2 delete pm2-dev.config.cjs",
28
- "logs": "pm2 logs new-fastify-app --raw | pino-pretty -t",
29
- "lint": "eslint '**/*.js' --fix",
25
+ "dev": "nodemon",
26
+ "lint": "eslint --fix",
30
27
  "test": "c8 tap",
31
28
  "test:cov": "c8 --reporter=html --reporter=text tap",
32
- "test:ci": "c8 --reporter=text-summary tap",
33
- "deploy": "pm2 deploy pm2.config.cjs",
34
- "postdeploy": "pm2 reload pm2.config.cjs"
35
- },
36
- "gitHooks": {
37
- "pre-commit": "lint-staged"
29
+ "test:ci": "tap",
30
+ "prepare": "husky install"
38
31
  },
39
32
  "lint-staged": {
40
- "*.{js}": [
41
- "eslint --fix"
42
- ]
33
+ "*.js": "eslint --cache --fix"
43
34
  }
44
35
  }
@@ -8,13 +8,9 @@ import fp from 'fastify-plugin'
8
8
  import App from '../app/app.js'
9
9
  import Config from '../app/config.js'
10
10
 
11
- // overwrite some config option(s) on tests
12
- Config.hemeralogLevel = 'fatal'
13
- Config.hemeraNS = `test-${process.env.TAP_CHILD_ID}`
14
-
15
11
  // automatically build and tear down our instance
16
- export const build = async (t, featureSwitches = {}, ConfigOverwrite = {}) => {
17
- return new Promise((resolve, reject) => {
12
+ export const build = async (t, ConfigOverwrite = {}) => {
13
+ return new Promise((resolve) => {
18
14
  const app = Fastify()
19
15
 
20
16
  // setup to register YOUR app
@@ -0,0 +1,13 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [{Makefile,**.mk}]
12
+ # Use tabs for indentation (Makefiles require tabs)
13
+ indent_style = tab
@@ -0,0 +1,4 @@
1
+ # @see ./app/config.js for defaults and options
2
+
3
+ logLevel = debug
4
+
@@ -0,0 +1,4 @@
1
+ # @see https://direnv.net/ and https://github.com/direnv/direnv/wiki/Node for docs
2
+ set -e
3
+ use node
4
+ layout node
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "@uscreen.de/eslint-config-prettystandard-node"
3
+ }
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ /.env
3
+ /.nyc_output
4
+ /coverage
5
+ .DS_Store
6
+ yarn-error.log
7
+ /.eslintcache
@@ -0,0 +1,25 @@
1
+ # image: node:lts-alpine
2
+
3
+ # cache:
4
+ # paths:
5
+ # - node_modules/
6
+ # - .yarn
7
+
8
+ # stages:
9
+ # - test
10
+ # - audit
11
+
12
+ # test:
13
+ # stage: test
14
+ # script:
15
+ # - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
16
+ # - yarn install --pure-lockfile --cache-folder .yarn
17
+ # - yarn test:ci
18
+ # coverage: '/^Statements\s*:\s*([^%]+)/'
19
+
20
+ # audit:
21
+ # stage: audit
22
+ # only:
23
+ # - schedules
24
+ # script:
25
+ # - yarn audit
@@ -0,0 +1 @@
1
+ 18
@@ -0,0 +1,7 @@
1
+ {
2
+ "timeout": 10,
3
+ "reporter": "spec",
4
+ "check-coverage": false,
5
+ "no-coverage": true,
6
+ "test-regex": "\\w+\\.test\\.js$"
7
+ }
@@ -0,0 +1,11 @@
1
+ start:
2
+ yarn
3
+ yarn dev
4
+
5
+ test:
6
+ yarn test
7
+
8
+ test.coverage:
9
+ yarn test:cov
10
+
11
+ .PHONY: test
@@ -0,0 +1,38 @@
1
+ # ${app-name}
2
+
3
+ > __TODO:__
4
+ > add a name & short description of this apps purpose
5
+
6
+ ## Usage
7
+
8
+ * `make start`: start app on local dev
9
+ * `make test`: test app
10
+
11
+ ## Configure
12
+
13
+ Configuration is read by dotenv from `app/.env` file and validated _[optional modified]_ by `app/config.js`. Please add extra defaults if needed to `config.js`. Default values should __always__ refer to local dev setups.
14
+
15
+ > __Note:__
16
+ > the `.env` file should __never__ get pushed to repository. So adding secrets and credentials to `.env` can be considered a secure option bound to specific environments.
17
+
18
+ ## Api
19
+
20
+ After starting your server you will find swagger documenation at [http://127.0.0.1:3000/api/docs/](http://127.0.0.1:3000/api/docs/) to explore web api requests interactively.
21
+
22
+ ---
23
+
24
+ ## Roadmap
25
+
26
+ > TBD
27
+
28
+ ## Changelog
29
+
30
+ ### v0.0.0
31
+
32
+ - initially bootstrapped by `yarn create @uscreen.de/fastify-app`
33
+
34
+ ---
35
+
36
+ ## Credits
37
+
38
+ application [boilerplate](https://www.npmjs.com/package/@uscreen.de/fastify-app) provided by [u|screen](https://uscreen.de)
@@ -0,0 +1,17 @@
1
+ import fastifyApp from '@uscreen.de/fastify-app'
2
+ import fp from 'fastify-plugin'
3
+ import schemas from './schemas.js'
4
+
5
+ export default fp((fastify, opts, next) => {
6
+ /**
7
+ * add schemas
8
+ */
9
+ fastify.register(schemas)
10
+
11
+ /**
12
+ * register app
13
+ */
14
+ fastify.register(fastifyApp, opts)
15
+
16
+ next()
17
+ })
@@ -0,0 +1,34 @@
1
+ import envSchema from 'env-schema'
2
+ import CommonESM from '@uscreen.de/common-esm'
3
+
4
+ const { join } = new CommonESM(import.meta.url)
5
+
6
+ const schema = {
7
+ type: 'object',
8
+ properties: {
9
+ httpPort: { default: 3000 },
10
+ httpBind: { default: '127.0.0.1' },
11
+ prefix: { default: '/api' },
12
+ logEnabled: { default: true },
13
+ logLevel: { default: 'info' }
14
+ }
15
+ }
16
+
17
+ const config = envSchema({
18
+ schema: schema,
19
+ dotenv: true
20
+ })
21
+
22
+ config.autoloads = [join('plugins'), join('services')]
23
+
24
+ config.swagger = {
25
+ routePrefix: `${config.prefix}/docs`,
26
+ exposeRoute: true,
27
+ addModels: true
28
+ }
29
+
30
+ config.health = {
31
+ exposeStatusRoute: `${config.prefix}/health`
32
+ }
33
+
34
+ export default config
@@ -0,0 +1,14 @@
1
+ import fp from 'fastify-plugin'
2
+
3
+ export default fp(
4
+ (fastify, opts, next) => {
5
+ fastify.decorate('noop', () => {
6
+ return 'Hello Universe'
7
+ })
8
+
9
+ next()
10
+ },
11
+ {
12
+ name: 'noop'
13
+ }
14
+ )
@@ -0,0 +1,34 @@
1
+ import fp from 'fastify-plugin'
2
+ import S from 'fluent-json-schema'
3
+
4
+ /**
5
+ * Usage of the Globaly Shared Schema feature
6
+ */
7
+
8
+ export default fp((fastify, opts, next) => {
9
+ const addSchema = (schema) => {
10
+ fastify.addSchema(schema)
11
+ return schema
12
+ }
13
+
14
+ /**
15
+ * add generic schemas
16
+ */
17
+ const noop = addSchema(S.object().id('noop').prop('noop'))
18
+
19
+ const plugin = addSchema(S.object().id('plugin').prop('plugin'))
20
+
21
+ /**
22
+ * combine and extend schemas
23
+ */
24
+ addSchema(
25
+ S.object()
26
+ .id('noopNplugin')
27
+ .required(['noop'])
28
+ // .prop('property') // --> strip it
29
+ .extend(noop)
30
+ .extend(plugin)
31
+ )
32
+
33
+ next()
34
+ })
@@ -0,0 +1,37 @@
1
+ import fastify from 'fastify'
2
+ import { options } from '@uscreen.de/fastify-app'
3
+ import config from './config.js'
4
+ import app from './app.js'
5
+
6
+ const server = fastify(options(config))
7
+
8
+ server.register(app, config)
9
+
10
+ /**
11
+ * post-treatment
12
+ */
13
+ server.ready((err) => {
14
+ if (err) throw err
15
+ server.log.debug(
16
+ 'server ready, routes are set:\n' +
17
+ server.printRoutes({ commonPrefix: false })
18
+ )
19
+ })
20
+
21
+ /**
22
+ * graceful shutdown (closing handles, etc.)
23
+ */
24
+ const shutdown = async () => {
25
+ server.log.info(
26
+ `application shutting down. (${server.app.name} ${server.app.version})`
27
+ )
28
+ await server.close()
29
+ process.exit()
30
+ }
31
+ process.on('SIGINT', shutdown)
32
+ process.on('SIGTERM', shutdown)
33
+
34
+ /**
35
+ * start http server
36
+ */
37
+ server.listen({ port: config.httpPort, host: config.httpBind })
@@ -0,0 +1,22 @@
1
+ export default (fastify, opts, next) => {
2
+ fastify.get(
3
+ '/noop',
4
+ {
5
+ schema: {
6
+ response: {
7
+ 200: { $ref: 'noopNplugin#' } // fastest and json-schema spec compatiple way, or use 'fastify.getSchema()'
8
+ // 200: fastify.getSchema('noopNplugin')
9
+ }
10
+ }
11
+ },
12
+ async (req, res) => {
13
+ return {
14
+ noop: 'Hello world',
15
+ plugin: fastify.noop(),
16
+ property: 'should be stripped from response'
17
+ }
18
+ }
19
+ )
20
+
21
+ next()
22
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "new-app",
3
+ "version": "1.0.0",
4
+ "main": "app/server.js",
5
+ "author": {
6
+ "name": "Martin Herting",
7
+ "email": "herting@uscreen.de"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "scripts": {
12
+ "dev": "nodemon",
13
+ "lint": "eslint --fix",
14
+ "test": "c8 tap",
15
+ "test:cov": "c8 --reporter=html --reporter=text tap",
16
+ "test:ci": "tap",
17
+ "prepare": "husky install"
18
+ },
19
+ "lint-staged": {
20
+ "*.js": "eslint --cache --fix"
21
+ },
22
+ "dependencies": {
23
+ "@uscreen.de/common-esm": "^1.0.0",
24
+ "@uscreen.de/fastify-app": "^1.0.0-0",
25
+ "env-schema": "^5.1.1",
26
+ "fastify": "^4.10.2",
27
+ "fastify-plugin": "^4.3.0",
28
+ "fluent-json-schema": "^4.0.0",
29
+ "pino-pretty": "^9.1.1"
30
+ },
31
+ "devDependencies": {
32
+ "@uscreen.de/eslint-config-prettystandard-node": "^0.2.8",
33
+ "c8": "^7.12.0",
34
+ "husky": ">=6",
35
+ "lint-staged": ">=10",
36
+ "nodemon": "^2.0.20",
37
+ "prettier": "^2.8.0",
38
+ "tap": "^16.3.2"
39
+ }
40
+ }
@@ -0,0 +1,54 @@
1
+ import tap from 'tap'
2
+ import { build } from '../helper.js'
3
+
4
+ tap.test('Test Setup', (t) => {
5
+ t.equal(true, true, 'Tests and assertions should work')
6
+ t.end()
7
+ })
8
+
9
+ tap.test('Healthcheck', async (t) => {
10
+ const fastify = await build(t)
11
+ const { prefix } = fastify.config
12
+
13
+ t.test('a valid GET Request', (t) => {
14
+ fastify.inject(
15
+ {
16
+ method: 'GET',
17
+ url: `${prefix}/health`
18
+ },
19
+ (e, response) => {
20
+ t.error(e)
21
+ t.same(response.statusCode, 200, 'response ok')
22
+ t.same(JSON.parse(response.body), { status: 'ok' }, 'payload ok')
23
+ t.end()
24
+ }
25
+ )
26
+ })
27
+ })
28
+
29
+ tap.test('Noop Service', async (t) => {
30
+ const fastify = await build(t)
31
+ const { prefix } = fastify.config
32
+
33
+ t.test('a valid GET Request', (t) => {
34
+ fastify.inject(
35
+ {
36
+ method: 'GET',
37
+ url: `${prefix}/noop`
38
+ },
39
+ (e, response) => {
40
+ t.error(e)
41
+ t.same(response.statusCode, 200, 'response ok')
42
+ t.same(
43
+ JSON.parse(response.body),
44
+ {
45
+ noop: 'Hello world',
46
+ plugin: 'Hello Universe'
47
+ },
48
+ 'payload ok'
49
+ )
50
+ t.end()
51
+ }
52
+ )
53
+ })
54
+ })
@@ -0,0 +1,29 @@
1
+ // This file contains code that we reuse
2
+ // between our tests.
3
+
4
+ import Fastify from 'fastify'
5
+ import fp from 'fastify-plugin'
6
+
7
+ // setup to import YOUR app
8
+ import App from '../app/app.js'
9
+ import Config from '../app/config.js'
10
+
11
+ // automatically build and tear down our instance
12
+ export const build = async (t, ConfigOverwrite = {}) => {
13
+ return new Promise((resolve) => {
14
+ const app = Fastify()
15
+
16
+ // setup to register YOUR app
17
+ app.register(fp(App), { ...Config, ...ConfigOverwrite })
18
+
19
+ // tear down our app after we are done
20
+ t.teardown(app.close.bind(app))
21
+
22
+ app.ready((err) => {
23
+ if (err) throw err
24
+ resolve(app)
25
+ })
26
+ })
27
+ }
28
+
29
+ export const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
@@ -0,0 +1,109 @@
1
+ import tap from 'tap'
2
+ import fs from 'fs-extra'
3
+ import path from 'path'
4
+ import { createRequire } from 'module'
5
+ import stripAnsi from 'strip-ansi'
6
+ import { before, teardown, cli, cwd } from './setup.js'
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const { version } = require('../package.json')
10
+
11
+ tap.before(before)
12
+ tap.teardown(teardown)
13
+
14
+ tap.test('$ cli', async (t) => {
15
+ const result = await cli([])
16
+ t.equal(
17
+ true,
18
+ result.stderr.startsWith("error: missing required argument 'name'"),
19
+ 'Should print error message'
20
+ )
21
+
22
+ t.end()
23
+ })
24
+
25
+ tap.test('$ cli -V', async (t) => {
26
+ const result = await cli(['--version'])
27
+ t.equal(
28
+ true,
29
+ result.stdout.startsWith(version),
30
+ 'Should show correct version'
31
+ )
32
+
33
+ t.end()
34
+ })
35
+
36
+ tap.test('$ cli new-app -y', async (t) => {
37
+ const result = await cli(['new-app', '-y'])
38
+
39
+ /**
40
+ * check output
41
+ */
42
+ t.equal(0, result.code, 'Should succeed')
43
+
44
+ t.test('Check output', (t) => {
45
+ const expectedOut = [
46
+ `Initialized empty Git repository in ${path.resolve(
47
+ cwd,
48
+ 'new-app',
49
+ '.git'
50
+ )}`,
51
+ 'success Saved package.json',
52
+ '[1/4] Resolving packages...',
53
+ '[2/4] Fetching packages...',
54
+ '[3/4] Linking dependencies...',
55
+ '[4/4] Building fresh packages...',
56
+ 'success Saved lockfile.',
57
+ 'husky - Git hooks installed'
58
+ ]
59
+
60
+ const stdout = stripAnsi(result.stdout)
61
+
62
+ for (const e of expectedOut) {
63
+ t.equal(true, stdout.includes(e), `"${e.substring(0, 36)}"`)
64
+ }
65
+
66
+ t.end()
67
+ })
68
+
69
+ /**
70
+ * check files
71
+ */
72
+ t.test('Check files', (t) => {
73
+ const appPath = path.resolve(cwd, 'new-app')
74
+
75
+ t.equal(
76
+ true,
77
+ fs.existsSync(path.resolve(appPath, 'package.json')),
78
+ 'package.json was created'
79
+ )
80
+ t.equal(
81
+ true,
82
+ fs.existsSync(path.resolve(appPath, 'README.md')),
83
+ 'skeleton was copied'
84
+ )
85
+ t.equal(
86
+ true,
87
+ fs.existsSync(path.resolve(appPath, 'yarn.lock')),
88
+ 'Packages were installed'
89
+ )
90
+
91
+ const pack = JSON.parse(
92
+ fs.readFileSync(path.resolve(appPath, 'package.json'), {
93
+ encoding: 'utf-8'
94
+ })
95
+ )
96
+ t.equal(
97
+ true,
98
+ pack.name !== 'new-fastify-app',
99
+ 'package.json was not just copied from skeleton'
100
+ )
101
+ t.equal(
102
+ true,
103
+ pack.main === 'app/server.js',
104
+ 'package.json was correctly enriched with data'
105
+ )
106
+
107
+ t.end()
108
+ })
109
+ })
package/test/setup.js ADDED
@@ -0,0 +1,33 @@
1
+ import fs from 'fs-extra'
2
+ import path from 'path'
3
+ import { exec } from 'child_process'
4
+ import { fileURLToPath } from 'url'
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
+
8
+ export const cwd = path.resolve(__dirname, './_arena/')
9
+
10
+ export const cli = (args) => {
11
+ return new Promise((resolve) => {
12
+ exec(
13
+ `node ${path.resolve(__dirname, '../bin/cli.js')} ${args.join(' ')}`,
14
+ { cwd },
15
+ (error, stdout, stderr) => {
16
+ resolve({
17
+ code: error && error.code ? error.code : 0,
18
+ error,
19
+ stdout,
20
+ stderr
21
+ })
22
+ }
23
+ )
24
+ })
25
+ }
26
+
27
+ export const before = () => {
28
+ fs.ensureDirSync(cwd)
29
+ }
30
+
31
+ export const teardown = () => {
32
+ fs.removeSync(cwd)
33
+ }
@@ -1,17 +0,0 @@
1
- import { readFileSync } from 'fs'
2
- import desm, { dirname, filename, join } from 'desm'
3
-
4
- /**
5
- * like const { name } = require('./package.json')
6
- */
7
- const json = (metaUrl, file) => JSON.parse(readFileSync(join(metaUrl, file)))
8
-
9
- /**
10
- * like if (!module.parent)
11
- */
12
- const isMain = (metaUrl) =>
13
- filename(metaUrl) === process.argv[1] ||
14
- filename(metaUrl) === process.env.pm_exec_path
15
-
16
- export { dirname, filename, join, json, isMain }
17
- export default desm
@@ -1,16 +0,0 @@
1
- const { name, main } = require('./package.json')
2
-
3
- module.exports = {
4
- apps: [
5
- {
6
- name,
7
- script: main,
8
- merge_logs: true,
9
- watch: ['app'],
10
- ignore_watch: ['.git', 'app/*.pid'],
11
- env: {
12
- NODE_ENV: 'development'
13
- }
14
- }
15
- ]
16
- }
@@ -1,29 +0,0 @@
1
- const { name, main, repository } = require('./package.json')
2
-
3
- module.exports = {
4
- apps: [
5
- {
6
- name,
7
- script: main,
8
- merge_logs: true,
9
- ignore_watch: ['.git', 'app/*.pid'],
10
- env: {
11
- NODE_ENV: 'production'
12
- }
13
- }
14
- ],
15
- deploy: {
16
- stage: {
17
- user: 'user',
18
- host: 'server-stage.example.com',
19
- ref: 'origin/master',
20
- repo: repository.url || repository,
21
- path: `/home/user/${name}`,
22
- 'pre-setup': 'yarn add pm2 pino-pretty;',
23
- 'post-setup':
24
- 'cp ./.env.example ../shared/.env; ln -s ../shared/.env ./.env',
25
- 'post-deploy':
26
- 'yarn install --production; ~/node_modules/.bin/pm2 reload pm2.config.cjs'
27
- }
28
- }
29
- }