Skip to content

Commit

Permalink
feat(sol-merger): add support for additional roots for resolving cont…
Browse files Browse the repository at this point in the history
…racts

This will allow to pass CLI argument or the parameter through the code
to pass additional roots while resolving the contracts, similar to the
`node_modules`.

Example of usage in CLI:

```sh
sol-merger --additional-root "./test/contracts/imports" "test/contracts/ImportWithAdditionalRoot.sol" compiled
```

Example of usage in code:

```ts
const merger = new Merger({
  delimeter: '\n\n',
  additionalRoots: ['./test/contracts/imports'],
});
```
  • Loading branch information
RyuuGan committed Apr 14, 2023
1 parent 5cca21c commit a4447fa
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from node:16-stretch
FROM node:16-stretch

WORKDIR /sol-merger
COPY test-cli.sh test-docker-entrypoint.sh package.json package-lock.json tsconfig.json tsconfig.app.json tslint.json ./
Expand Down
16 changes: 12 additions & 4 deletions bin/sol-merger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ program
.option(
'-p, --export-plugin [pathToPlugin]',
`Add post processor for exports`,
collectExportPluginOption,
collectArrayOptions,
[],
)
.option(
'-r, --additional-root [pathToRoot]',
`Add additional root to search contracts in`,
collectArrayOptions,
[],
)
.arguments('<glob> [outputDir]')
Expand All @@ -49,8 +55,9 @@ if (outputDir) {
}

debug('Output directory', outputDir);
debug('RemoveComments?', program.removeComments);
debug('ExportPlugins?', program.exportPlugin);
debug('Remove comments?', program.removeComments);
debug('Export plugins', program.exportPlugin);
debug('Additional roots', program.additionalRoot);

glob(
inputGlob,
Expand Down Expand Up @@ -79,6 +86,7 @@ async function execute(err: Error, files: string[]) {
delimeter: '\n\n',
removeComments: program.removeComments,
exportPlugins,
additionalRoots: program.additionalRoot,
});
let result: string;
result = await merger.processFile(file, true);
Expand All @@ -101,7 +109,7 @@ async function execute(err: Error, files: string[]) {
.catch(done);
}

function collectExportPluginOption(value: string, previousValue: string[]) {
function collectArrayOptions(value: string, previousValue: string[]) {
return previousValue.concat([value]);
}

Expand Down
22 changes: 20 additions & 2 deletions lib/merger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { exec } from 'child_process';
import Debug from 'debug';
import path from 'path';
import { promises as fs, constants } from 'fs';
import { ExportsAnalyzerResult } from './exportsAnalyzer';
import { FileAnalyzer, FileAnalyzerResult } from './fileAnalyzer';
import { ImportsRegistry } from './importRegistry';
Expand All @@ -20,6 +20,8 @@ export class Merger {
private importRegistry: ImportsRegistry;
#pluginsRegistry: ExportPluginsRegistry;
nodeModulesRoot = '';
#additionalRoots: string[];
#fileRoots: string[];

constructor(private options: SolMergerOptions = {}) {
if ('removeComments' in options) {
Expand All @@ -32,6 +34,7 @@ export class Merger {

const exportPlugins = options.exportPlugins || [];
this.#pluginsRegistry = new ExportPluginsRegistry(exportPlugins);
this.#additionalRoots = options.additionalRoots ?? [];
}

getPragmaRegex() {
Expand Down Expand Up @@ -60,6 +63,7 @@ export class Merger {
async init(file: string) {
this.importRegistry = new ImportsRegistry();
this.nodeModulesRoot = await this.getNodeModulesPath(file);
this.#fileRoots = [this.nodeModulesRoot, ...this.#additionalRoots];
}

async processFile(
Expand Down Expand Up @@ -127,7 +131,7 @@ export class Merger {
for (const i of analyzedFile.imports) {
let filePath = Utils.isRelative(i.file)
? path.join(path.dirname(analyzedFile.filename), i.file)
: path.join(this.nodeModulesRoot, i.file);
: await this.getRootPath(i.file);
filePath = path.normalize(filePath);

const contents = await this.processFile(filePath, false, i);
Expand All @@ -139,6 +143,19 @@ export class Merger {
return result;
}

private async getRootPath(nonRelativePath: string): Promise<string> {
for (const root of this.#fileRoots) {
const filePath = path.join(root, nonRelativePath);
try {
await fs.access(filePath, constants.R_OK);
return filePath;
} catch {
debug('File %s does is not readable in root directory %s', nonRelativePath, root);
}
}
return path.join(this.nodeModulesRoot, nonRelativePath);
}

async processExports(
analyzedFile: FileAnalyzerResult,
parentImport?: ImportsAnalyzerResult,
Expand Down Expand Up @@ -239,4 +256,5 @@ export interface SolMergerOptions {
removeComments?: boolean;
commentsDelimeter?: string;
exportPlugins?: ExportPluginCtor[];
additionalRoots?: string[];
}
17 changes: 13 additions & 4 deletions test-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ WITHOUT_COMMENTS=(
SKIPPED=(
compiled/EmptyFile.sol
compiled/LocalImportsWithSPDX.sol
compiled/ImportWithAdditionalRoot.sol
)

PROCESSED_GLOB="test/contracts/!(ImportWithAdditionalRoot).sol"

compareFile() {
local file="$1";
echo "Comparing file: $file"
Expand All @@ -30,7 +33,7 @@ compareFile() {
# Compare files with comments

rm -rf compiled
sol-merger "test/contracts/*.sol" compiled
sol-merger "$PROCESSED_GLOB" compiled

echo "Default compilation (with comments)"

Expand All @@ -49,19 +52,25 @@ done
# Remove SPDX Plugin
echo "Compilation with plugins"
rm -rf compiled
sol-merger --export-plugin ./dist/lib/plugins/SPDXLicenseRemovePlugin.js "test/contracts/*.sol" compiled
sol-merger --export-plugin ./dist/lib/plugins/SPDXLicenseRemovePlugin.js "$PROCESSED_GLOB" compiled

compareFile compiled/LocalImportsWithSPDX.sol

rm -rf compiled
sol-merger --export-plugin SPDXLicenseRemovePlugin "test/contracts/*.sol" compiled
sol-merger --export-plugin SPDXLicenseRemovePlugin "$PROCESSED_GLOB" compiled

compareFile compiled/LocalImportsWithSPDX.sol

echo "Compilation with --additional-root option"
rm -rf compiled
sol-merger --additional-root "./test/contracts/imports" "test/contracts/ImportWithAdditionalRoot.sol" compiled

compareFile compiled/ImportWithAdditionalRoot.sol

# Remove Comments
echo "Compilation with --remove-comments option"
rm -rf compiled
sol-merger --remove-comments "test/contracts/*.sol" compiled
sol-merger --remove-comments "$PROCESSED_GLOB" compiled

for file in "${WITHOUT_COMMENTS[@]}"
do
Expand Down
33 changes: 33 additions & 0 deletions test/compiled/ImportWithAdditionalRoot.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pragma solidity ^0.4.11;
pragma experimental ABIEncoderV2;


contract Ownable {
address public owner;

function Ownable() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}

}

contract MyOwned is Ownable {
// Super important comment here
string public constant name = "My Owned";

/**
* Super important description here
*/
function MyOwned() {}
}
14 changes: 14 additions & 0 deletions test/contracts/ImportWithAdditionalRoot.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma solidity ^0.4.11;
pragma experimental ABIEncoderV2;

import "ownable.sol";

contract MyOwned is Ownable {
// Super important comment here
string public constant name = "My Owned";

/**
* Super important description here
*/
function MyOwned() {}
}
15 changes: 15 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,19 @@ describe('Solidity Merger', () => {
it('should compile file with user defined operators (0.8.19 support)', async () => {
await testFile('UserDefinedOperators');
});

it('should compile file with additional roots defined', async () => {
await testFile('ImportWithAdditionalRoot', {
additionalRoots: ['./test/contracts/imports'],
});
});

it('should not compile file without additional roots defined', async () => {
try {
await testFile('ImportWithAdditionalRoot', {});
assert.equal(true, false, 'Should never happen');
} catch (e) {
assert.isOk(e);
}
});
});

0 comments on commit a4447fa

Please sign in to comment.