Skip to content

Instantly share code, notes, and snippets.

@en9inerd
Last active November 11, 2023 05:17
Show Gist options
  • Save en9inerd/2b7b45fee3910ca11df10e0c3b3fd702 to your computer and use it in GitHub Desktop.
Save en9inerd/2b7b45fee3910ca11df10e0c3b3fd702 to your computer and use it in GitHub Desktop.
Dynamic module discovery using json5 and glob
import { access, readFile } from 'fs/promises';
import { join, resolve, dirname } from 'path';
import { TSConfig } from './types';
import { sync as globSync } from 'glob';
import { parse as parseJSON } from 'json5';
import { DiscoveryError } from './exceptions';
async function getRootAppDir(): Promise<string> {
const appDir = process.env.PWD;
if (appDir) {
return appDir;
}
let dir = __dirname;
if (dir.includes('node_modules')) {
dir = dir.split('node_modules')[0];
}
let dirFound = false;
while (true) {
try {
await access(join(dir, 'package.json'));
dirFound = true;
break;
} catch (error) {
const parentDir = dirname(dir);
if (parentDir === dir) {
break;
}
dir = parentDir;
}
}
if (dirFound) {
return dir;
}
throw new DiscoveryError('Cannot find root app directory');
}
async function getTSConfig(): Promise<TSConfig | void> {
const tsConfigPath = join(await getRootAppDir(), 'tsconfig.json');
try {
const tsConfig = await readFile(tsConfigPath, 'utf8');
return parseJSON(tsConfig);
} catch (err: unknown) {
if ((<{ code: string }>err).code === 'ENOENT') {
return;
}
throw err;
}
}
async function discover<T>(pattern: string, instantiate = false, validator?: CallableFunction): Promise<T[]> {
const outDir = resolve(
await getRootAppDir(),
(await getTSConfig())?.compilerOptions?.outDir || ''
);
const files = globSync(pattern, {
cwd: outDir,
absolute: true,
nodir: true,
ignore: '**/node_modules/**'
});
const instancesOrClasses: T[] = files.map((filePath) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const module = require(filePath);
if (Object.keys(module).length !== 1) {
throw new DiscoveryError(`Command module '${filePath}' must have only one named export or export default`);
}
return instantiate ? new module[Object.keys(module)[0]]() : module[Object.keys(module)[0]];
});
if (validator) validator(instancesOrClasses);
return instancesOrClasses;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment