diff --git a/bin/pluginsLoader.ts b/bin/pluginsLoader.ts new file mode 100644 index 0000000..4fe35b0 --- /dev/null +++ b/bin/pluginsLoader.ts @@ -0,0 +1,73 @@ +import Debug from 'debug'; +import path from 'path'; +import { ExportPluginCtor, ExportPluginModule } from '../lib/types'; +import { Utils } from '../lib/utils'; + +const info = Debug('sol-merger:info'); +const error = Debug('sol-merger:error'); + +export class PluginLoadError extends Error { + constructor(message: string) { + super(message); + } +} + +export class PluginsLoader { + #pluginPaths: string[]; + #npmRoot: string; + + constructor(pluginPaths: string[], npmRoot: string) { + this.#pluginPaths = pluginPaths; + this.#npmRoot = npmRoot; + } + + async getPlugins(): Promise { + const result: ExportPluginCtor[] = []; + for (const pluginPath of this.#pluginPaths) { + const fullPluginPath = this.getPluginPath(pluginPath); + if (!fullPluginPath) { + continue; + } + try { + const plugin = await this.tryLoadPlugin(fullPluginPath); + result.push(plugin); + } catch (e) { + if (e instanceof Error) { + error(e.message); + } else { + error(e); + } + } + } + return result; + } + + private getPluginPath(pluginPath: string): string | null { + if (!this.#npmRoot && !Utils.isRelative(pluginPath)) { + info( + `[PluginsLoader] SKIP: Unable to load plugin '${pluginPath}' because npmRoot is not found.`, + ); + return null; + } + + if (Utils.isRelative(pluginPath)) { + return path.join(process.cwd(), pluginPath); + } + return path.join(this.#npmRoot, pluginPath); + } + + private async tryLoadPlugin(modulePath: string): Promise { + const resolvedPath = path.resolve(modulePath); + const pluginModule: Partial = await import( + resolvedPath + ).catch((e) => { + throw new PluginLoadError(`Can not import plugin ${resolvedPath}`); + }); + if (pluginModule.ExportPlugin) { + return pluginModule.ExportPlugin; + } + throw new PluginLoadError( + `Plugin ${resolvedPath} does not have exported member ExportPlugin`, + ); + } +} diff --git a/bin/sol-merger.ts b/bin/sol-merger.ts index c765aaa..e2150bc 100644 --- a/bin/sol-merger.ts +++ b/bin/sol-merger.ts @@ -1,13 +1,16 @@ #!/usr/bin/env node -import fs from 'fs-extra'; -import path from 'path'; -import glob from 'glob'; import colors from 'cli-color'; +import program from 'commander'; import Debug from 'debug'; +import fs from 'fs-extra'; +import glob from 'glob'; +import path from 'path'; import { Merger } from '../lib/merger'; +import { ExportPluginCtor } from '../lib/types'; +import { Utils } from '../lib/utils'; import { done } from '../utils/done'; -import program from 'commander'; +import { PluginsLoader } from './pluginsLoader'; const debug = Debug('sol-merger:debug'); @@ -18,6 +21,12 @@ let append = ''; program .option('-a, --append [append]', '', /^([a-zA-Z_]+)$/) .option('-c, --remove-comments', `Remove comment from exports`, false) + .option( + '-p, --export-plugin [pathToPlugin]', + `Add post processor for exports`, + collectExportPluginOption, + [], + ) .arguments(' [outputDir]') .action((_glob, _outputDir) => { inputGlob = _glob; @@ -41,6 +50,7 @@ if (outputDir) { debug('Output directory', outputDir); debug('RemoveComments?', program.removeComments); +debug('ExportPlugins?', program.exportPlugin); glob( inputGlob, @@ -59,14 +69,16 @@ async function execute(err: Error, files: string[]) { debug(files); if (files.length === 0) { - // eslint-disable-next-line console.log(colors.yellow('No files found for merge')); } + const exportPlugins = await getExportPlugins(program.exportPlugin); + const promises = files.map(async (file) => { const merger = new Merger({ delimeter: '\n\n', removeComments: program.removeComments, + exportPlugins, }); let result: string; result = await merger.processFile(file, true); @@ -88,3 +100,15 @@ async function execute(err: Error, files: string[]) { .then(() => done()) .catch(done); } + +function collectExportPluginOption(value: string, previousValue: string[]) { + return previousValue.concat([value]); +} + +async function getExportPlugins( + plugins: string[], +): Promise { + const npmRoot = await Utils.getNodeModulesPath(process.cwd()); + const loader = new PluginsLoader(plugins, npmRoot); + return loader.getPlugins(); +} diff --git a/lib/merger.ts b/lib/merger.ts index fc02166..372b309 100644 --- a/lib/merger.ts +++ b/lib/merger.ts @@ -226,17 +226,7 @@ export class Merger { } async getNodeModulesPath(file: string): Promise { - return new Promise((resolve, reject) => { - exec('npm root', { cwd: path.dirname(file) }, (err, stdout) => { - if (err) { - error( - 'Unable to find npm root directory. Make sure contract is inside npm package.', - ); - return reject(err); - } - resolve(stdout.trim()); - }); - }); + return Utils.getNodeModulesPath(file); } private isComment(str: string) { diff --git a/lib/types.ts b/lib/types.ts index 65f16ad..ccc188c 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -17,3 +17,7 @@ export interface ExportPluginProcessor { export type ExportPluginCtor< T extends ExportPluginProcessor = ExportPluginProcessor > = new () => T; + +export interface ExportPluginModule { + ExportPlugin: ExportPluginCtor +} diff --git a/lib/utils.ts b/lib/utils.ts index 75f363e..c8a96fe 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,5 +1,25 @@ +import { exec } from 'child_process'; +import Debug from 'debug'; +import path from 'path'; + +const error = Debug('sol-merger:error'); + export class Utils { static isRelative(file: string) { return file.startsWith('.'); } + + static async getNodeModulesPath(file: string): Promise { + return new Promise((resolve, reject) => { + exec('npm root', { cwd: path.dirname(file) }, (err, stdout) => { + if (err) { + error( + 'Unable to find npm root directory. Make sure contract is inside npm package.', + ); + return reject(err); + } + resolve(stdout.trim()); + }); + }); + } }