Skip to content

Instantly share code, notes, and snippets.

@beardedtim
Last active November 25, 2020 18:46
Show Gist options
  • Save beardedtim/5693321f152945daf24ae288fa9a2301 to your computer and use it in GitHub Desktop.
Save beardedtim/5693321f152945daf24ae288fa9a2301 to your computer and use it in GitHub Desktop.
Import a directory of es modules
import glob from 'glob'
import Case from 'case'
import R from 'ramda'
interface ImportConfig {
case: 'snake' | 'kebab' | 'camel' | 'pascal' | 'constant' | 'header'
ext: 'js' | 'ts'
without?: string[]
}
const get_modules_paths = (
file_path: string,
config: ImportConfig
): Promise<string[]> =>
new Promise((res, rej) => {
glob(`${file_path}/*.${config.ext || 'js'}`, (err, matches) => {
if (err) {
rej(err)
} else {
res(matches)
}
})
})
const default_config: ImportConfig = {
case: 'camel',
ext: 'js',
without: [],
}
const import_modules = <T>(
paths: string[],
config: ImportConfig = default_config
): Promise<{ [x: string]: T }> => {
const valid_config = R.mergeDeepLeft(config, default_config)
return Promise.all(
/**
* Import all of the modules using the dynamic
* import
*
* https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports
*
* https://nodejs.org/api/esm.html#esm_import_expressions
*/
paths.map((file_path) => import(file_path))
)
.then(
/**
* Then we map over the imported modules and get either
* the export default expression if there is one or
* all of the export const expressions
*/
(modules) => modules.map((mod) => mod.default || mod)
)
.then(
/**
* Finally we namespace the modules according to the passed
* in configuration
*/
(modules) =>
modules.reduce((a, c, i) => {
const file_path = paths[i]
// Here be fuckery
const module_name = file_path.split('/').pop()?.slice(0, -3) as string
const case_key = valid_config.case as keyof typeof Case
const case_handler = Case[case_key] as (str: string) => string
const key = case_handler(module_name)
if (!R.includes(key, valid_config.without as string[])) {
return {
...a,
[key]: c,
}
}
return a
}, {})
)
}
/**
* Returns a namespaced object of modules at the
* directory given as `path`.
*
* @param {string} path The directory to import
* @param {Object} config The configuration to set
* @param { 'snake' | 'kebab'| 'camel' | 'pascal' | 'constant' | 'header'} config.case The case to namespace the object as. Defaults to camel
*/
const import_dir = async <T>(path: string, config: ImportConfig) => {
const module_paths = await get_modules_paths(path, config)
return import_modules<T>(module_paths, config)
}
export default import_dir
import importDir from './import-dir'
interface Model<T> {}
const models = importDir<Model<any>>(path.resolve(__dirname), {
case: 'camel',
ext: 'ts',
without: ['base'],
}).then(async (mods) => {
for (const [mod_name, mod] of Object.entries(mods)) {
await (mod as Model<any>).init()
}
return mods
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment