@platformatic/service 0.16.0 → 0.17.0
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.
- package/fixtures/hello/no-server-logger.json +2 -2
- package/fixtures/hello/platformatic.service.json +2 -2
- package/fixtures/options/platformatic.service.yml +5 -4
- package/index.js +64 -68
- package/lib/compile.js +63 -88
- package/lib/config.js +2 -11
- package/lib/load-config.js +6 -2
- package/lib/sandbox-wrapper.js +26 -0
- package/lib/schema.js +34 -48
- package/lib/start.mjs +97 -69
- package/lib/utils.js +4 -0
- package/package.json +19 -19
- package/service.mjs +2 -2
- package/test/autoload.test.js +33 -21
- package/test/cli/compile.test.mjs +12 -3
- package/test/cli/gen-schema.test.mjs +4 -2
- package/test/cli/watch.test.mjs +59 -15
- package/test/config.test.js +70 -50
- package/test/fixtures/bad-typescript-plugin/dist/tsconfig.tsbuildinfo +1 -0
- package/test/fixtures/bad-typescript-plugin/platformatic.service.json +3 -5
- package/test/fixtures/directories/platformatic.service.json +2 -2
- package/test/fixtures/not-load.service.json +2 -3
- package/test/fixtures/typescript-plugin/platformatic.service.json +3 -5
- package/test/fixtures/typescript-plugin-nocompile/platformatic.service.json +3 -6
- package/test/load-and-reload-files.test.js +20 -20
- package/test/routes.test.js +3 -13
- package/test/schema.test.js +12 -0
- package/test/tmp/typescript-plugin-clone-1/platformatic.service.json +3 -5
- package/test/tmp/typescript-plugin-clone-2/platformatic.service.json +3 -6
- package/test/tmp/typescript-plugin-clone-3/dist/tsconfig.tsbuildinfo +1 -0
- package/test/tmp/typescript-plugin-clone-3/platformatic.service.json +3 -5
- package/test/tmp/typescript-plugin-clone-4/dist/tsconfig.tsbuildinfo +1 -0
- package/test/tmp/typescript-plugin-clone-4/platformatic.service.json +3 -5
- package/test/tmp/typescript-plugin-clone-5/platformatic.service.json +3 -5
- package/test/tmp/typescript-plugin-clone-6/platformatic.service.json +3 -6
- package/test/utils.test.js +8 -1
- package/lib/autoload-wrapper.js +0 -11
package/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { start } = require('@fastify/restartable')
|
|
4
|
-
const autoload = require('@fastify/autoload')
|
|
5
4
|
const sandbox = require('fastify-sandbox')
|
|
6
5
|
const underPressure = require('@fastify/under-pressure')
|
|
7
6
|
const { schema } = require('./lib/schema')
|
|
8
7
|
const ConfigManager = require('./lib/config.js')
|
|
9
|
-
const { addLoggerToTheConfig, getJSPluginPath } = require('./lib/utils')
|
|
8
|
+
const { addLoggerToTheConfig, getJSPluginPath, isFileAccessible } = require('./lib/utils')
|
|
10
9
|
const loadConfig = require('./lib/load-config')
|
|
11
10
|
const { isKeyEnabled, deepmerge } = require('@platformatic/utils')
|
|
12
11
|
const compiler = require('./lib/compile')
|
|
13
|
-
const {
|
|
14
|
-
const {
|
|
15
|
-
const wrapperPath = join(__dirname, 'lib', '
|
|
12
|
+
const { join, dirname, resolve } = require('path')
|
|
13
|
+
const { readFile } = require('fs/promises')
|
|
14
|
+
const wrapperPath = join(__dirname, 'lib', 'sandbox-wrapper.js')
|
|
16
15
|
|
|
17
16
|
function createServerConfig (config) {
|
|
18
17
|
// convert the config file to a new structure
|
|
@@ -59,15 +58,58 @@ async function platformaticService (app, opts, toLoad = []) {
|
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
if (opts.plugins) {
|
|
62
|
+
// if we don't have a fullPath, let's assume we are in a test and we can use the current working directory
|
|
63
|
+
const configPath = app.platformatic.configManager.fullPath || process.cwd()
|
|
64
|
+
const tsConfigPath = join(dirname(configPath), 'tsconfig.json')
|
|
65
|
+
/* c8 ignore next 21 */
|
|
66
|
+
if (await isFileAccessible(tsConfigPath)) {
|
|
67
|
+
const tsConfig = JSON.parse(await readFile(tsConfigPath, 'utf8'))
|
|
68
|
+
const outDir = resolve(dirname(tsConfigPath), tsConfig.compilerOptions.outDir)
|
|
69
|
+
opts.plugins.paths = opts.plugins.paths.map((plugin) => {
|
|
70
|
+
if (typeof plugin === 'string') {
|
|
71
|
+
return getJSPluginPath(configPath, plugin, outDir)
|
|
72
|
+
} else {
|
|
73
|
+
return {
|
|
74
|
+
path: getJSPluginPath(configPath, plugin.path, outDir),
|
|
75
|
+
options: plugin.options
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
} else {
|
|
80
|
+
for (const plugin of opts.plugins.paths) {
|
|
81
|
+
const path = typeof plugin === 'string' ? plugin : plugin.path
|
|
82
|
+
if (path.endsWith('.ts')) {
|
|
83
|
+
throw new Error(`Cannot load plugin ${path}, tsconfig.json not found`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// if not defined, we default to true (which can happen only if config is set programmatically,
|
|
89
|
+
// that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
|
|
90
|
+
// all individual plugin hot reload settings will be overloaded by global hot reload
|
|
91
|
+
/* c8 ignore next 1 */
|
|
92
|
+
const hotReload = opts.plugins.hotReload !== false
|
|
93
|
+
const isWatchEnabled = app.platformatic.config.watch !== false
|
|
94
|
+
app.log.debug({ plugins: opts.plugins.path }, 'loading plugin')
|
|
95
|
+
|
|
96
|
+
if (isWatchEnabled && hotReload) {
|
|
97
|
+
await app.register(sandbox, {
|
|
98
|
+
path: wrapperPath,
|
|
99
|
+
options: { paths: opts.plugins.paths },
|
|
100
|
+
customizeGlobalThis (_globalThis) {
|
|
101
|
+
// Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
|
|
102
|
+
const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
|
|
103
|
+
const dispatcher = globalThis[globalDispatcher]
|
|
104
|
+
/* istanbul ignore else */
|
|
105
|
+
if (dispatcher) {
|
|
106
|
+
_globalThis[globalDispatcher] = dispatcher
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
} else {
|
|
111
|
+
await app.register(require(wrapperPath), { paths: opts.plugins.paths })
|
|
68
112
|
}
|
|
69
|
-
} else if (opts.plugin) {
|
|
70
|
-
await loadPlugin(app, opts, opts.plugin)
|
|
71
113
|
}
|
|
72
114
|
|
|
73
115
|
// Enable CORS
|
|
@@ -87,6 +129,7 @@ async function platformaticService (app, opts, toLoad = []) {
|
|
|
87
129
|
app.register(underPressure, {
|
|
88
130
|
exposeStatusRoute: '/status',
|
|
89
131
|
healthCheckInterval: opts.healthCheck.interval !== undefined ? opts.healthCheck.interval : 5000,
|
|
132
|
+
...opts.healthCheck,
|
|
90
133
|
healthCheck: opts.healthCheck.fn
|
|
91
134
|
})
|
|
92
135
|
}
|
|
@@ -96,61 +139,6 @@ async function platformaticService (app, opts, toLoad = []) {
|
|
|
96
139
|
}
|
|
97
140
|
}
|
|
98
141
|
|
|
99
|
-
async function loadPlugin (app, config, pluginOptions) {
|
|
100
|
-
/* c8 ignore next 5 */
|
|
101
|
-
if (pluginOptions.typescript !== undefined) {
|
|
102
|
-
const configPath = app.platformatic.configManager.fullPath
|
|
103
|
-
const pluginPath = getJSPluginPath(configPath, pluginOptions.path, pluginOptions.typescript.outDir)
|
|
104
|
-
pluginOptions = { ...pluginOptions, path: pluginPath }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
app.log.debug({ plugin: pluginOptions }, 'loading plugin')
|
|
108
|
-
|
|
109
|
-
// if not defined, we defaults to true (which can happen only if config is set programmatically,
|
|
110
|
-
// that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
|
|
111
|
-
// all individual plugin hot reload settings will be overloaded by global hot reload
|
|
112
|
-
/* c8 ignore next 35 */
|
|
113
|
-
const hotReload = config.hotReload ?? pluginOptions.hotReload !== false
|
|
114
|
-
const isWatchEnabled = config.watch !== false
|
|
115
|
-
if (isWatchEnabled && hotReload) {
|
|
116
|
-
let options = pluginOptions
|
|
117
|
-
if ((await stat(pluginOptions.path)).isDirectory()) {
|
|
118
|
-
options = {
|
|
119
|
-
path: wrapperPath,
|
|
120
|
-
options: pluginOptions
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
await app.register(sandbox, {
|
|
124
|
-
...options,
|
|
125
|
-
customizeGlobalThis (_globalThis) {
|
|
126
|
-
// Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
|
|
127
|
-
const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
|
|
128
|
-
const dispatcher = globalThis[globalDispatcher]
|
|
129
|
-
/* istanbul ignore else */
|
|
130
|
-
if (dispatcher) {
|
|
131
|
-
_globalThis[globalDispatcher] = dispatcher
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
// c8 fails in reporting the coverage of this else branch, so we ignore it
|
|
136
|
-
} else {
|
|
137
|
-
if ((await stat(pluginOptions.path)).isDirectory()) {
|
|
138
|
-
const options = {
|
|
139
|
-
...pluginOptions.options,
|
|
140
|
-
dir: pluginOptions.path
|
|
141
|
-
}
|
|
142
|
-
await app.register(autoload, options)
|
|
143
|
-
} else {
|
|
144
|
-
let plugin = await import(`file://${pluginOptions.path}`)
|
|
145
|
-
if (plugin.__esModule === true) {
|
|
146
|
-
plugin = plugin.default
|
|
147
|
-
}
|
|
148
|
-
/* c8 ignore next 4 */
|
|
149
|
-
await app.register(plugin, pluginOptions.options)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
142
|
platformaticService[Symbol.for('skip-override')] = true
|
|
155
143
|
platformaticService.schema = schema
|
|
156
144
|
|
|
@@ -254,6 +242,13 @@ async function buildServer (options, app, ConfigManagerContructor) {
|
|
|
254
242
|
return handler
|
|
255
243
|
}
|
|
256
244
|
|
|
245
|
+
// This is for @platformatic/db to use
|
|
246
|
+
/* c8 ignore next 4 */
|
|
247
|
+
async function buildStart (loadConfig, buildServer) {
|
|
248
|
+
const { buildStart } = await import('./lib/start.mjs')
|
|
249
|
+
return buildStart(loadConfig, buildServer)
|
|
250
|
+
}
|
|
251
|
+
|
|
257
252
|
module.exports.buildServer = buildServer
|
|
258
253
|
module.exports.schema = require('./lib/schema')
|
|
259
254
|
module.exports.createServerConfig = createServerConfig
|
|
@@ -262,3 +257,4 @@ module.exports.addLoggerToTheConfig = addLoggerToTheConfig
|
|
|
262
257
|
module.exports.loadConfig = loadConfig
|
|
263
258
|
module.exports.tsCompiler = compiler
|
|
264
259
|
module.exports.ConfigManager = ConfigManager
|
|
260
|
+
module.exports.buildStart = buildStart
|
package/lib/compile.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { resolve, join } = require('path')
|
|
3
|
+
const { resolve, join, dirname } = require('path')
|
|
4
4
|
const pino = require('pino')
|
|
5
5
|
const pretty = require('pino-pretty')
|
|
6
6
|
const loadConfig = require('./load-config.js')
|
|
@@ -28,123 +28,98 @@ async function getTSCExecutablePath (cwd) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async function
|
|
31
|
+
async function setup (cwd) {
|
|
32
|
+
const logger = pino(
|
|
33
|
+
pretty({
|
|
34
|
+
translateTime: 'SYS:HH:MM:ss',
|
|
35
|
+
ignore: 'hostname,pid'
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
|
|
32
39
|
const { execa } = await import('execa')
|
|
33
|
-
const cwd = process.cwd()
|
|
34
40
|
|
|
35
41
|
const tscExecutablePath = await getTSCExecutablePath(cwd)
|
|
36
42
|
/* c8 ignore next 4 */
|
|
37
43
|
if (tscExecutablePath === undefined) {
|
|
38
|
-
|
|
44
|
+
const msg = 'The tsc executable was not found.'
|
|
45
|
+
logger.error(msg)
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
const tsconfigPath = resolve(cwd, 'tsconfig.json')
|
|
42
49
|
const tsconfigExists = await isFileAccessible(tsconfigPath)
|
|
43
50
|
|
|
44
51
|
if (!tsconfigExists) {
|
|
45
|
-
|
|
52
|
+
const msg = 'The tsconfig.json file was not found.'
|
|
53
|
+
logger.error(msg)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { execa, logger, tscExecutablePath }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function compile (cwd) {
|
|
60
|
+
const { execa, logger, tscExecutablePath } = await setup(cwd)
|
|
61
|
+
/* c8 ignore next 3 */
|
|
62
|
+
if (!tscExecutablePath) {
|
|
63
|
+
return false
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
try {
|
|
49
67
|
await execa(tscExecutablePath, ['--project', 'tsconfig.json'], { cwd })
|
|
50
68
|
logger.info('Typescript compilation completed successfully.')
|
|
69
|
+
return true
|
|
51
70
|
} catch (error) {
|
|
52
|
-
|
|
71
|
+
logger.error(error.message)
|
|
72
|
+
return false
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
// This path is tested but C8 does not see it that way given it needs to work
|
|
77
|
+
// through execa.
|
|
78
|
+
/* c8 ignore next 20 */
|
|
56
79
|
async function compileWatch (cwd) {
|
|
57
|
-
const { execa } = await
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
translateTime: 'SYS:HH:MM:ss',
|
|
61
|
-
ignore: 'hostname,pid'
|
|
62
|
-
})
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
const tscExecutablePath = await getTSCExecutablePath(cwd)
|
|
66
|
-
/* c8 ignore next 4 */
|
|
67
|
-
if (tscExecutablePath === undefined) {
|
|
68
|
-
logger.error('The tsc executable was not found.')
|
|
69
|
-
process.exit(1)
|
|
80
|
+
const { execa, logger, tscExecutablePath } = await setup(cwd)
|
|
81
|
+
if (!tscExecutablePath) {
|
|
82
|
+
return false
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.exit(1)
|
|
85
|
+
try {
|
|
86
|
+
await execa(tscExecutablePath, ['--project', 'tsconfig.json', '--incremental'], { cwd })
|
|
87
|
+
logger.info('Typescript compilation completed successfully. Starting watch mode.')
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new Error('Failed to compile typescript files: ' + error)
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
process.on('SIGTERM', () => child.kill('SIGKILL'))
|
|
85
|
-
|
|
86
|
-
let tsCompilationMessages = []
|
|
87
|
-
|
|
88
|
-
child.stdout.on('data', (data) => {
|
|
89
|
-
let tsCompilerMessage = data.toString().trim()
|
|
90
|
-
if (tsCompilerMessage.startsWith('\u001bc')) {
|
|
91
|
-
tsCompilerMessage = tsCompilerMessage.slice(2)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (tsCompilerMessage === '') return
|
|
95
|
-
|
|
96
|
-
const startMessage = tsCompilerMessage.match(/.*Starting compilation in watch mode.../)
|
|
97
|
-
if (startMessage !== null) {
|
|
98
|
-
logger.info(tsCompilerMessage)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
tsCompilationMessages.push(tsCompilerMessage)
|
|
103
|
-
|
|
104
|
-
const resultMessage = tsCompilerMessage.match(/.*Found (\d+) error.*/)
|
|
105
|
-
if (resultMessage !== null) {
|
|
106
|
-
const errorsCount = parseInt(resultMessage[1])
|
|
107
|
-
const compilerOutput = tsCompilationMessages.join('\n')
|
|
108
|
-
/* c8 ignore next 6 */
|
|
109
|
-
if (errorsCount === 0) {
|
|
110
|
-
logger.info(compilerOutput)
|
|
111
|
-
if (!isCompiled) {
|
|
112
|
-
isCompiled = true
|
|
113
|
-
resolve()
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
logger.error(compilerOutput)
|
|
117
|
-
if (!isCompiled) {
|
|
118
|
-
reject(new Error('Typescript compilation failed.'))
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
tsCompilationMessages = []
|
|
122
|
-
}
|
|
123
|
-
})
|
|
92
|
+
const child = execa(tscExecutablePath, ['--project', 'tsconfig.json', '--watch', '--incremental'], { cwd })
|
|
93
|
+
child.stdout.resume()
|
|
94
|
+
child.stderr.on('data', (data) => {
|
|
95
|
+
logger.error(data.toString())
|
|
124
96
|
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function compile (_args) {
|
|
128
|
-
const logger = pino(
|
|
129
|
-
pretty({
|
|
130
|
-
translateTime: 'SYS:HH:MM:ss',
|
|
131
|
-
ignore: 'hostname,pid'
|
|
132
|
-
})
|
|
133
|
-
)
|
|
134
97
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const config = configManager.current
|
|
98
|
+
return { child }
|
|
99
|
+
}
|
|
138
100
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
101
|
+
function buildCompileCmd (_loadConfig) {
|
|
102
|
+
return async function compileCmd (_args) {
|
|
103
|
+
let fullPath = null
|
|
104
|
+
try {
|
|
105
|
+
const { configManager } = await _loadConfig({}, _args)
|
|
106
|
+
await configManager.parseAndValidate()
|
|
107
|
+
fullPath = dirname(configManager.fullPath)
|
|
108
|
+
/* c8 ignore next 4 */
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(err)
|
|
111
|
+
process.exit(1)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!await compile(fullPath)) {
|
|
115
|
+
process.exit(1)
|
|
116
|
+
}
|
|
145
117
|
}
|
|
146
118
|
}
|
|
147
119
|
|
|
120
|
+
const compileCmd = buildCompileCmd(loadConfig)
|
|
121
|
+
|
|
148
122
|
module.exports.compile = compile
|
|
149
123
|
module.exports.compileWatch = compileWatch
|
|
150
|
-
module.exports.
|
|
124
|
+
module.exports.buildCompileCmd = buildCompileCmd
|
|
125
|
+
module.exports.compileCmd = compileCmd
|
package/lib/config.js
CHANGED
|
@@ -32,17 +32,8 @@ class ServiceConfigManager extends ConfigManager {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// relative-to-absolute plugin path
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.current.plugin = this.current.plugin.map(fixPluginPath)
|
|
38
|
-
} else if (this.current.plugin) {
|
|
39
|
-
this.current.plugin = fixPluginPath(this.current.plugin)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// TODO: handle arrays
|
|
43
|
-
if (this.current.plugin?.typescript) {
|
|
44
|
-
const typescript = this.current.plugin.typescript
|
|
45
|
-
typescript.outDir = this._fixRelativePath(typescript.outDir)
|
|
35
|
+
if (this.current.plugins?.paths) {
|
|
36
|
+
this.current.plugins.paths = this.current.plugins.paths.map(fixPluginPath)
|
|
46
37
|
}
|
|
47
38
|
}
|
|
48
39
|
|
package/lib/load-config.js
CHANGED
|
@@ -18,15 +18,19 @@ const ourConfigFiles = [
|
|
|
18
18
|
async function loadConfig (minimistConfig, _args, configOpts = {}, Manager = ConfigManager, configFileNames = ourConfigFiles) {
|
|
19
19
|
const args = parseArgs(_args, deepmerge({ all: true })({
|
|
20
20
|
string: ['allow-env'],
|
|
21
|
+
boolean: ['hotReload'],
|
|
21
22
|
default: {
|
|
22
|
-
allowEnv: '' // The default is set in ConfigManager
|
|
23
|
+
allowEnv: '', // The default is set in ConfigManager
|
|
24
|
+
hotReload: true
|
|
23
25
|
},
|
|
24
26
|
alias: {
|
|
25
27
|
v: 'version',
|
|
26
28
|
c: 'config',
|
|
27
|
-
allowEnv: ['allow-env', 'E']
|
|
29
|
+
allowEnv: ['allow-env', 'E'],
|
|
30
|
+
hotReload: ['hot-reload']
|
|
28
31
|
}
|
|
29
32
|
}, minimistConfig))
|
|
33
|
+
|
|
30
34
|
try {
|
|
31
35
|
if (!args.config) {
|
|
32
36
|
args.config = await findConfigFile(process.cwd(), configFileNames)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fp = require('fastify-plugin')
|
|
4
|
+
const autoload = require('@fastify/autoload')
|
|
5
|
+
const { stat } = require('fs').promises
|
|
6
|
+
|
|
7
|
+
module.exports = fp(async function (app, opts) {
|
|
8
|
+
for (let plugin of opts.paths) {
|
|
9
|
+
if (typeof plugin === 'string') {
|
|
10
|
+
plugin = { path: plugin }
|
|
11
|
+
}
|
|
12
|
+
if ((await stat(plugin.path)).isDirectory()) {
|
|
13
|
+
app.register(autoload, {
|
|
14
|
+
dir: plugin.path,
|
|
15
|
+
options: plugin.options
|
|
16
|
+
})
|
|
17
|
+
} else {
|
|
18
|
+
let loaded = await import(`file://${plugin.path}`)
|
|
19
|
+
/* c8 ignore next 3 */
|
|
20
|
+
if (loaded.__esModule === true) {
|
|
21
|
+
loaded = loaded.default
|
|
22
|
+
}
|
|
23
|
+
await app.register(loaded, plugin.options)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
package/lib/schema.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
'use strict'
|
|
2
4
|
|
|
5
|
+
const pkg = require('../package.json')
|
|
6
|
+
const version = 'v' + pkg.version
|
|
7
|
+
|
|
3
8
|
const cors = {
|
|
4
9
|
type: 'object',
|
|
5
10
|
$comment: 'See https://github.com/fastify/fastify-cors',
|
|
@@ -90,7 +95,6 @@ const cors = {
|
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
const server = {
|
|
93
|
-
$id: 'https://schemas.platformatic.dev/service/server',
|
|
94
98
|
type: 'object',
|
|
95
99
|
properties: {
|
|
96
100
|
// TODO add support for level
|
|
@@ -125,7 +129,6 @@ const server = {
|
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
const watch = {
|
|
128
|
-
$id: 'https://schemas.platformatic.dev/service/watch',
|
|
129
132
|
type: 'object',
|
|
130
133
|
properties: {
|
|
131
134
|
type: 'object',
|
|
@@ -152,29 +155,34 @@ const watch = {
|
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
const
|
|
156
|
-
$id: '#
|
|
158
|
+
const plugins = {
|
|
159
|
+
$id: '#plugins',
|
|
157
160
|
type: 'object',
|
|
158
161
|
properties: {
|
|
159
|
-
|
|
160
|
-
type: '
|
|
162
|
+
paths: {
|
|
163
|
+
type: 'array',
|
|
164
|
+
items: {
|
|
165
|
+
anyOf: [{
|
|
166
|
+
type: 'string'
|
|
167
|
+
}, {
|
|
168
|
+
type: 'object',
|
|
169
|
+
properties: {
|
|
170
|
+
path: {
|
|
171
|
+
type: 'string'
|
|
172
|
+
},
|
|
173
|
+
options: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
additionalProperties: true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}]
|
|
179
|
+
}
|
|
161
180
|
},
|
|
162
181
|
stopTimeout: {
|
|
163
182
|
type: 'integer'
|
|
164
183
|
},
|
|
165
184
|
typescript: {
|
|
166
|
-
type: '
|
|
167
|
-
properties: {
|
|
168
|
-
outDir: {
|
|
169
|
-
type: 'string'
|
|
170
|
-
},
|
|
171
|
-
build: {
|
|
172
|
-
type: 'boolean',
|
|
173
|
-
default: true
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
additionalProperties: false,
|
|
177
|
-
required: ['outDir']
|
|
185
|
+
type: 'boolean'
|
|
178
186
|
},
|
|
179
187
|
fallback: {
|
|
180
188
|
type: 'boolean'
|
|
@@ -182,38 +190,13 @@ const plugin = {
|
|
|
182
190
|
hotReload: {
|
|
183
191
|
type: 'boolean',
|
|
184
192
|
default: true
|
|
185
|
-
},
|
|
186
|
-
options: {
|
|
187
|
-
type: 'object'
|
|
188
193
|
}
|
|
189
194
|
},
|
|
190
195
|
additionalProperties: false,
|
|
191
|
-
required: ['
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const pluginTypes = {
|
|
195
|
-
$id: 'https://schemas.platformatic.dev/service/pluginTypes',
|
|
196
|
-
$defs: {
|
|
197
|
-
plugin
|
|
198
|
-
},
|
|
199
|
-
anyOf: [{
|
|
200
|
-
type: 'array',
|
|
201
|
-
items: {
|
|
202
|
-
anyOf: [{
|
|
203
|
-
$ref: '#plugin'
|
|
204
|
-
}, {
|
|
205
|
-
type: 'string'
|
|
206
|
-
}]
|
|
207
|
-
}
|
|
208
|
-
}, {
|
|
209
|
-
$ref: '#plugin'
|
|
210
|
-
}, {
|
|
211
|
-
type: 'string'
|
|
212
|
-
}]
|
|
196
|
+
required: ['paths']
|
|
213
197
|
}
|
|
214
198
|
|
|
215
199
|
const metrics = {
|
|
216
|
-
$id: 'https://schemas.platformatic.dev/service/metrics',
|
|
217
200
|
anyOf: [
|
|
218
201
|
{ type: 'boolean' },
|
|
219
202
|
{
|
|
@@ -237,11 +220,11 @@ const metrics = {
|
|
|
237
220
|
}
|
|
238
221
|
|
|
239
222
|
const platformaticServiceSchema = {
|
|
240
|
-
$id:
|
|
223
|
+
$id: `https://platformatic.dev/schemas/${version}/service`,
|
|
241
224
|
type: 'object',
|
|
242
225
|
properties: {
|
|
243
226
|
server,
|
|
244
|
-
|
|
227
|
+
plugins,
|
|
245
228
|
metrics
|
|
246
229
|
},
|
|
247
230
|
additionalProperties: {
|
|
@@ -261,6 +244,9 @@ module.exports.schema = platformaticServiceSchema
|
|
|
261
244
|
module.exports.metrics = metrics
|
|
262
245
|
module.exports.cors = cors
|
|
263
246
|
module.exports.server = server
|
|
264
|
-
module.exports.
|
|
265
|
-
module.exports.pluginTypes = pluginTypes
|
|
247
|
+
module.exports.plugins = plugins
|
|
266
248
|
module.exports.watch = watch
|
|
249
|
+
|
|
250
|
+
if (require.main === module) {
|
|
251
|
+
console.log(JSON.stringify(platformaticServiceSchema, null, 2))
|
|
252
|
+
}
|