Skip to content

Commit

Permalink
feat: add support for preloading ES module with --import flag (#713)
Browse files Browse the repository at this point in the history
* feat: add support for preloading ES module with --import flag

use --import or -i to preload ES module

* docs: add docs for --import flag

* fix: fix minor typos

* docs: add the --import option to the start command usage file

---------

Co-authored-by: Weixin Wu <[email protected]>
  • Loading branch information
weixinwu and Weixin Wu authored Mar 19, 2024
1 parent e85dd16 commit b82b5ba
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 22 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ You can pass the following options via CLI arguments. You can also use `--config
| Address to listen on | `-a` | `--address` | `FASTIFY_ADDRESS` |
| Socket to listen on | `-s` | `--socket` | `FASTIFY_SOCKET` |
| Module to preload | `-r` | `--require` | `FASTIFY_REQUIRE` |
| ES Module to preload | `-i` | `--import` | `FASTIFY_IMPORT` |
| Log level (default to fatal) | `-l` | `--log-level` | `FASTIFY_LOG_LEVEL` |
| Path to logging configuration module to use | `-L` | `--logging-module` | `FASTIFY_LOGGING_MODULE` |
| Start Fastify app in debug mode with nodejs inspector | `-d` | `--debug` | `FASTIFY_DEBUG` |
Expand Down
4 changes: 3 additions & 1 deletion args.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = function parseArgs (args) {
'populate--': true
},
number: ['port', 'inspect-port', 'body-limit', 'plugin-timeout', 'close-grace-delay'],
string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'config', 'method'],
string: ['log-level', 'address', 'socket', 'prefix', 'ignore-watch', 'logging-module', 'debug-host', 'lang', 'require', 'import', 'config', 'method'],
boolean: ['pretty-logs', 'options', 'watch', 'verbose-watch', 'debug', 'standardlint', 'common-prefix', 'include-hooks'],
envPrefix: 'FASTIFY_',
alias: {
Expand All @@ -41,6 +41,7 @@ module.exports = function parseArgs (args) {
watch: ['w'],
prefix: ['x'],
require: ['r'],
import: ['i'],
debug: ['d'],
'debug-port': ['I'],
'log-level': ['l'],
Expand Down Expand Up @@ -86,6 +87,7 @@ module.exports = function parseArgs (args) {
address: parsedArgs.address,
socket: parsedArgs.socket,
require: parsedArgs.require,
import: parsedArgs.import,
prefix: parsedArgs.prefix,
loggingModule: parsedArgs.loggingModule,
lang: parsedArgs.lang,
Expand Down
5 changes: 3 additions & 2 deletions examples/plugin-with-preloaded.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* global GLOBAL_MODULE_1 */
/* global GLOBAL_MODULE_1, GLOBAL_MODULE_3 */
'use strict'
const t = require('tap')

module.exports = async function (fastify, options) {
fastify.get('/', async function (req, reply) {
return { hasPreloaded: GLOBAL_MODULE_1 }
return { hasPreloaded: GLOBAL_MODULE_1 && GLOBAL_MODULE_3 }
})
fastify.addHook('onReady', function () {
t.ok(GLOBAL_MODULE_1)
t.ok(GLOBAL_MODULE_3)
})
}
4 changes: 4 additions & 0 deletions help/start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ OPTS
[env: FASTIFY_REQUIRE]
Module to preload

-i, --import
[env: FASTIFY_IMPORT]
ES Module to preload

-L, --logging-module
[env: FASTIFY_LOGGING_MODULE]
Path to logging configuration module to use
Expand Down
59 changes: 43 additions & 16 deletions start.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const parseArgs = require('./args')
const {
exit,
requireModule,
requireESModule,
requireFastifyForModule,
requireServerPluginFromPath,
showHelpForCommand,
Expand Down Expand Up @@ -57,26 +58,52 @@ function stop (message) {
exit(message)
}

function preloadCJSModules (opts) {
if (typeof opts.require === 'string') {
opts.require = [opts.require]
}
try {
opts.require.forEach(module => {
if (module) {
/* This check ensures we ignore `-r ""`, trailing `-r`, or
* other silly things the user might (inadvertently) be doing.
*/
requireModule(module)
}
})
} catch (e) {
module.exports.stop(e)
}
}

async function preloadESModules (opts) {
if (typeof opts.import === 'string') {
opts.import = [opts.import]
}
opts.import.forEach(async (m) => {
if (m) {
/* This check ensures we ignore `-i ""`, trailing `-i`, or
* other silly things the user might (inadvertently) be doing.
*/
try {
await requireESModule(m)
} catch (e) {
module.exports.stop(e)
}
}
})
}

async function runFastify (args, additionalOptions, serverOptions) {
const opts = parseArgs(args)
if (opts.require) {
if (typeof opts.require === 'string') {
opts.require = [opts.require]
}

try {
opts.require.forEach(module => {
if (module) {
/* This check ensures we ignore `-r ""`, trailing `-r`, or
* other silly things the user might (inadvertently) be doing.
*/
requireModule(module)
}
})
} catch (e) {
module.exports.stop(e)
}
if (opts.require) {
preloadCJSModules(opts)
}
if (opts.import) {
await preloadESModules(opts)
}

opts.port = opts.port || process.env.PORT || 3000

loadModules(opts)
Expand Down
14 changes: 13 additions & 1 deletion test/args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ test('should parse args correctly', t => {
'--address', 'fastify.dev:9999',
'--socket', 'fastify.dev.socket:9999',
'--require', './require-module.js',
'--import', './import-module.js',
'--log-level', 'info',
'--pretty-logs', 'true',
'--watch', 'true',
Expand Down Expand Up @@ -42,6 +43,7 @@ test('should parse args correctly', t => {
address: 'fastify.dev:9999',
socket: 'fastify.dev.socket:9999',
require: './require-module.js',
import: './import-module.js',
logLevel: 'info',
prefix: 'FASTIFY_',
pluginTimeout: 500,
Expand All @@ -67,6 +69,7 @@ test('should parse args with = assignment correctly', t => {
'--address=fastify.dev:9999',
'--socket=fastify.dev.socket:9999',
'--require', './require-module.js',
'--import', './import-module.js',
'--log-level=info',
'--pretty-logs=true',
'--watch=true',
Expand Down Expand Up @@ -97,6 +100,7 @@ test('should parse args with = assignment correctly', t => {
address: 'fastify.dev:9999',
socket: 'fastify.dev.socket:9999',
require: './require-module.js',
import: './import-module.js',
logLevel: 'info',
prefix: 'FASTIFY_',
pluginTimeout: 500,
Expand All @@ -121,6 +125,7 @@ test('should parse env vars correctly', t => {
process.env.FASTIFY_ADDRESS = 'fastify.dev:9999'
process.env.FASTIFY_SOCKET = 'fastify.dev.socket:9999'
process.env.FASTIFY_REQUIRE = './require-module.js'
process.env.FASTIFY_IMPORT = './import-module.js'
process.env.FASTIFY_LOG_LEVEL = 'info'
process.env.FASTIFY_PRETTY_LOGS = 'true'
process.env.FASTIFY_WATCH = 'true'
Expand All @@ -141,6 +146,7 @@ test('should parse env vars correctly', t => {
delete process.env.FASTIFY_ADDRESS
delete process.env.FASTIFY_SOCKET
delete process.env.FASTIFY_REQUIRE
delete process.env.FASTIFY_IMPORT
delete process.env.FASTIFY_LOG_LEVEL
delete process.env.FASTIFY_PRETTY_LOGS
delete process.env.FASTIFY_WATCH
Expand Down Expand Up @@ -173,6 +179,7 @@ test('should parse env vars correctly', t => {
prefix: 'FASTIFY_',
socket: 'fastify.dev.socket:9999',
require: './require-module.js',
import: './import-module.js',
pluginTimeout: 500,
closeGraceDelay: 30000,
pluginOptions: {},
Expand All @@ -188,7 +195,7 @@ test('should parse env vars correctly', t => {
})

test('should respect default values', t => {
t.plan(13)
t.plan(14)

const argv = [
'app.js'
Expand All @@ -209,6 +216,7 @@ test('should respect default values', t => {
t.equal(parsedArgs.debugPort, 9320)
t.equal(parsedArgs.loggingModule, undefined)
t.equal(parsedArgs.require, undefined)
t.equal(parsedArgs.import, undefined)
})

test('should parse custom plugin options', t => {
Expand All @@ -219,6 +227,7 @@ test('should parse custom plugin options', t => {
'--address', 'fastify.dev:9999',
'--socket', 'fastify.dev.socket:9999',
'--require', './require-module.js',
'--import', './import-module.js',
'--log-level', 'info',
'--pretty-logs', 'true',
'--watch', 'true',
Expand Down Expand Up @@ -256,6 +265,7 @@ test('should parse custom plugin options', t => {
address: 'fastify.dev:9999',
socket: 'fastify.dev.socket:9999',
require: './require-module.js',
import: './import-module.js',
logLevel: 'info',
prefix: 'FASTIFY_',
pluginTimeout: 500,
Expand Down Expand Up @@ -307,6 +317,7 @@ test('should parse config file correctly and prefer config values over default o
address: 'fastify.dev:9999',
socket: undefined,
require: undefined,
import: undefined,
prefix: 'FASTIFY_',
loggingModule: undefined,
lang: 'js',
Expand Down Expand Up @@ -349,6 +360,7 @@ test('should prefer command line args over config file options', t => {
address: 'fastify.dev:9999',
socket: undefined,
require: undefined,
import: undefined,
prefix: 'FASTIFY_',
loggingModule: undefined,
lang: 'js',
Expand Down
3 changes: 3 additions & 0 deletions test/data/custom-import.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* global GLOBAL_MODULE_3 */
globalThis.GLOBAL_MODULE_3 = true // eslint-disable-line
console.log('this is module to be preloaded that sets GLOBAL_MODULE_3=', GLOBAL_MODULE_3)
3 changes: 3 additions & 0 deletions test/data/custom-import2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* global GLOBAL_MODULE_4 */
globalThis.GLOBAL_MODULE_4 = true // eslint-disable-line
console.log('this is module to be preloaded that sets GLOBAL_MODULE_4=', GLOBAL_MODULE_4)
51 changes: 50 additions & 1 deletion test/start.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global GLOBAL_MODULE_1, GLOBAL_MODULE_2 */
/* global GLOBAL_MODULE_1, GLOBAL_MODULE_2, GLOBAL_MODULE_3, GLOBAL_MODULE_4 */
'use strict'

const util = require('node:util')
Expand Down Expand Up @@ -774,6 +774,17 @@ test('should support preloading custom module', async t => {
t.pass('server closed')
})

test('should support preloading custom ES module', async t => {
t.plan(2)

const argv = ['-i', './test/data/custom-import.mjs', './examples/plugin.js']
const fastify = await start.start(argv)
t.ok(globalThis.GLOBAL_MODULE_3)

await fastify.close()
t.pass('server closed')
})

test('should support preloading multiple custom modules', async t => {
t.plan(3)

Expand All @@ -786,6 +797,17 @@ test('should support preloading multiple custom modules', async t => {
t.pass('server closed')
})

test('should support preloading multiple custom ES modules', async t => {
t.plan(3)

const argv = ['-i', './test/data/custom-import.mjs', '-i', './test/data/custom-import2.mjs', './examples/plugin.js']
const fastify = await start.start(argv)
t.ok(GLOBAL_MODULE_3)
t.ok(GLOBAL_MODULE_4)
await fastify.close()
t.pass('server closed')
})

test('preloading custom module with empty and trailing require flags should not throw', async t => {
t.plan(2)

Expand All @@ -797,6 +819,17 @@ test('preloading custom module with empty and trailing require flags should not
t.pass('server closed')
})

test('preloading custom ES module with empty and trailing import flags should not throw', async t => {
t.plan(2)

const argv = ['-i', './test/data/custom-import.mjs', '-i', '', './examples/plugin.js', '-i']
const fastify = await start.start(argv)
t.ok(GLOBAL_MODULE_3)

await fastify.close()
t.pass('server closed')
})

test('preloading custom module that is not found should throw', async t => {
t.plan(2)

Expand All @@ -813,6 +846,22 @@ test('preloading custom module that is not found should throw', async t => {
t.pass('server closed')
})

test('preloading custom ES module that is not found should throw', async t => {
t.plan(2)

const oldStop = start.stop
t.teardown(() => { start.stop = oldStop })
start.stop = function (err) {
t.ok(/Cannot find module/.test(err.message), err.message)
}

const argv = ['-i', './test/data/import-missing.mjs', './examples/plugin.js']
const fastify = await start.start(argv)

await fastify.close()
t.pass('server closed')
})

test('preloading custom module should be done before starting server', async t => {
t.plan(4)

Expand Down
19 changes: 18 additions & 1 deletion util.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ function requireModule (moduleName) {
}
}

async function requireESModule (moduleName) {
if (fs.existsSync(moduleName)) {
const moduleFilePath = path.resolve(moduleName)
return import(url.pathToFileURL(moduleFilePath))
} else {
return import(moduleName)
}
}

function requireFastifyForModule (modulePath) {
try {
const basedir = path.resolve(process.cwd(), modulePath)
Expand Down Expand Up @@ -104,4 +113,12 @@ function isKubernetes () {
fs.existsSync('/run/secrets/kubernetes.io/serviceaccount/token')
}

module.exports = { isKubernetes, exit, requireModule, requireFastifyForModule, showHelpForCommand, requireServerPluginFromPath }
module.exports = {
isKubernetes,
exit,
requireModule,
requireESModule,
requireFastifyForModule,
showHelpForCommand,
requireServerPluginFromPath
}

0 comments on commit b82b5ba

Please sign in to comment.