Automate code generation for config schema (#16)

Use a fork of https://github.com/lstrojny/json-schema-to-zod to generate
the boundary check for the config automatically.
This commit is contained in:
Lars Strojny 2022-11-10 13:06:16 +01:00 committed by GitHub
parent 1088a78079
commit 52efa69bf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 71 additions and 34 deletions

View file

@ -0,0 +1,29 @@
#!/usr/bin/env node
const { parseSchema } = require('json-schema-to-zod-with-defaults')
const { schema } = require('../config.schema.json')
const { format } = require('prettier')
const { join, basename } = require('path')
const prettierConfig = require('../prettier.config')
const { writeFileSync } = require('fs')
const file = join(__dirname, '../src/generated/config_boundary.ts')
console.log(`Starting code generation for ${file}`)
const zodSchema = parseSchema(schema, true)
const code = format(
`
// Auto-generated by "${join(basename(__dirname), basename(__filename))}", dont manually edit
import { z } from 'zod'
export const ConfigBoundary = ${zodSchema}
`,
{ filepath: 'codegen.ts', ...prettierConfig },
)
writeFileSync(file, code)
console.log(`Finished code generation for ${file}`)

View file

@ -7,6 +7,9 @@ const { join, basename } = require('path')
const uuidToServiceMap = {} const uuidToServiceMap = {}
const serviceToUuidMap = {} const serviceToUuidMap = {}
const file = join(__dirname, '../src/generated/services.ts')
console.log(`Starting code generation for ${file}`)
for (const [name, service] of Object.entries(hap.Service)) { for (const [name, service] of Object.entries(hap.Service)) {
if (typeof service !== 'function' || typeof service.UUID !== 'string') { if (typeof service !== 'function' || typeof service.UUID !== 'string') {
@ -28,4 +31,6 @@ export const Services: Record<string,string> = ${JSON.stringify(serviceToUuidMap
{ filepath: 'codegen.ts', ...prettierConfig }, { filepath: 'codegen.ts', ...prettierConfig },
) )
writeFileSync(join(__dirname, '../src/generated/services.ts'), code) writeFileSync(file, code)
console.log(`Finished code generation for ${file}`)

18
package-lock.json generated
View file

@ -27,7 +27,7 @@
"homebridge": "^1.3.5", "homebridge": "^1.3.5",
"homebridge-cmdswitch2": "^0.2.10", "homebridge-cmdswitch2": "^0.2.10",
"jest": "^29.3.0", "jest": "^29.3.0",
"json-schema-to-zod": "^0.2.0", "json-schema-to-zod-with-defaults": "^0.2.1",
"nodemon": "^2.0.13", "nodemon": "^2.0.13",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
@ -5328,10 +5328,10 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/json-schema-to-zod": { "node_modules/json-schema-to-zod-with-defaults": {
"version": "0.2.0", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/json-schema-to-zod/-/json-schema-to-zod-0.2.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-to-zod-with-defaults/-/json-schema-to-zod-with-defaults-0.2.1.tgz",
"integrity": "sha512-UmbLC6VnWBt6dxA6s42Ku2l0vIhJMrYpPxB2QCWUx9gMXtW4TpT5p/BMXUGg7zmhwLElOmlsx9bONlkNvlukbQ==", "integrity": "sha512-5T4GUM4Koo7COTrOknBwsIgBrhp4FqM5BAhofWewLiBt3Q+ViWy8xNIeB6d3ZGHnEwKGVIbMaRaJyuwpM9x4SA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
@ -11742,10 +11742,10 @@
"@apidevtools/json-schema-ref-parser": "9.0.9" "@apidevtools/json-schema-ref-parser": "9.0.9"
} }
}, },
"json-schema-to-zod": { "json-schema-to-zod-with-defaults": {
"version": "0.2.0", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/json-schema-to-zod/-/json-schema-to-zod-0.2.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-to-zod-with-defaults/-/json-schema-to-zod-with-defaults-0.2.1.tgz",
"integrity": "sha512-UmbLC6VnWBt6dxA6s42Ku2l0vIhJMrYpPxB2QCWUx9gMXtW4TpT5p/BMXUGg7zmhwLElOmlsx9bONlkNvlukbQ==", "integrity": "sha512-5T4GUM4Koo7COTrOknBwsIgBrhp4FqM5BAhofWewLiBt3Q+ViWy8xNIeB6d3ZGHnEwKGVIbMaRaJyuwpM9x4SA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",

View file

@ -21,7 +21,7 @@
"test": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; npm run code-generation && `npm bin`/jest `ifNotCi --watchAll`", "test": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; npm run code-generation && `npm bin`/jest `ifNotCi --watchAll`",
"link": "npm install --no-save file:///$PWD/", "link": "npm install --no-save file:///$PWD/",
"build": "rimraf ./dist && npm run code-generation && tsc", "build": "rimraf ./dist && npm run code-generation && tsc",
"code-generation": "./code-generation/hap-gen.js", "code-generation": "./code-generation/hap-gen.js && ./code-generation/config-scheme-gen.js",
"prepublishOnly": "npm run code-generation && npm run lint && npm run build", "prepublishOnly": "npm run code-generation && npm run lint && npm run build",
"release": "release() { test \"$1\" && test `git rev-parse --abbrev-ref HEAD` == \"develop\" && npm run code-generation && git pull origin develop --rebase && npm version $1 && npm publish && git push origin develop && git push origin --tags; }; release" "release": "release() { test \"$1\" && test `git rev-parse --abbrev-ref HEAD` == \"develop\" && npm run code-generation && git pull origin develop --rebase && npm version $1 && npm publish && git push origin develop && git push origin --tags; }; release"
}, },
@ -45,7 +45,7 @@
"homebridge": "^1.3.5", "homebridge": "^1.3.5",
"homebridge-cmdswitch2": "^0.2.10", "homebridge-cmdswitch2": "^0.2.10",
"jest": "^29.3.0", "jest": "^29.3.0",
"json-schema-to-zod": "^0.2.0", "json-schema-to-zod-with-defaults": "^0.2.1",
"nodemon": "^2.0.13", "nodemon": "^2.0.13",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",

View file

@ -1,19 +1,5 @@
import { z } from 'zod' import { z } from 'zod'
import { ConfigBoundary as ConfigBoundaryWithoutPlatform } from '../generated/config_boundary'
export const ConfigBoundary = z.object({ export const ConfigBoundary = z.intersection(ConfigBoundaryWithoutPlatform, z.object({ platform: z.string() }))
pin: z.string().regex(new RegExp('^\\d{3}-\\d{2}-\\d{3}$')).describe('Homebridge PIN for service authentication'), export type Config = z.infer<typeof ConfigBoundary>
debug: z.boolean().default(false),
prefix: z.string().default('homebridge'),
port: z.number().int().describe('TCP port for the prometheus probe server to listen to').default(36123),
refresh_interval: z.number().int().describe('Discover new services every <interval> seconds').default(60),
request_timeout: z
.number()
.int()
.describe('Request timeout when interacting with homebridge instances')
.default(10),
discovery_timeout: z
.number()
.int()
.describe('Discovery timeout after which the current discovery is considered failed')
.default(20),
})

View file

@ -1,7 +1,3 @@
import z from 'zod'
import { ConfigBoundary as BaseConfigBoundary } from './config'
export * from './checker' export * from './checker'
export * from './hap' export * from './hap'
export const ConfigBoundary = z.intersection(BaseConfigBoundary, z.object({ platform: z.string() })) export * from './config'
export type Config = z.infer<typeof ConfigBoundary>

View file

@ -0,0 +1,21 @@
// Auto-generated by "code-generation/config-scheme-gen.js", dont manually edit
import { z } from 'zod'
export const ConfigBoundary = z.object({
pin: z.string().regex(new RegExp('^\\d{3}-\\d{2}-\\d{3}$')).describe('Homebridge PIN for service authentication'),
debug: z.boolean().default(false),
prefix: z.string().default('homebridge'),
port: z.number().int().describe('TCP port for the prometheus probe server to listen to').default(36123),
refresh_interval: z.number().int().describe('Discover new services every <interval> seconds').default(60),
request_timeout: z
.number()
.int()
.describe('Request timeout when interacting with homebridge instances')
.default(10),
discovery_timeout: z
.number()
.int()
.describe('Discovery timeout after which the current discovery is considered failed')
.default(20),
})