Skip to content

Commit 567ffb0

Browse files
authored
Merge pull request #324 from crytic/node-module-resolution
Node module resolution
2 parents 9d52a5f + bc7c024 commit 567ffb0

File tree

9 files changed

+148
-27
lines changed

9 files changed

+148
-27
lines changed

.gitignore

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
*.nix
2+
*.pyc
13
.idea/
2-
crytic_compile.egg-info/
34
__pycache__/
4-
*.pyc
5+
artifacts
56
build/
6-
dist/
7+
cache
8+
crytic_compile.egg-info/
9+
dist/
10+
node_modules
11+
package-lock.json
12+
result

crytic_compile/utils/naming.py

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,48 @@ def combine_filename_name(filename: str, name: str) -> str:
5959
return filename + ":" + name
6060

6161

62+
def _verify_filename_existence(filename: Path, cwd: Path) -> Path:
63+
"""
64+
Check if the filename exist. If it does not, try multiple heuristics to find the right filename:
65+
- Look for contracts/FILENAME
66+
- Look for node_modules/FILENAME
67+
- Look for node_modules/FILENAME in all the parents directories
68+
69+
70+
Args:
71+
filename (Path): filename to check
72+
cwd (Path): directory
73+
74+
Raises:
75+
InvalidCompilation: if the filename is not found
76+
77+
Returns:
78+
Path: the filename
79+
"""
80+
81+
if filename.exists():
82+
return filename
83+
84+
if cwd.joinpath(Path("contracts"), filename).exists():
85+
filename = cwd.joinpath("contracts", filename)
86+
elif cwd.joinpath(filename).exists():
87+
filename = cwd.joinpath(filename)
88+
# how node.js loads dependencies from node_modules:
89+
# https://nodejs.org/api/modules.html#loading-from-node_modules-folders
90+
elif cwd.joinpath(Path("node_modules"), filename).exists():
91+
filename = cwd.joinpath("node_modules", filename)
92+
else:
93+
for parent in cwd.parents:
94+
if parent.joinpath(Path("node_modules"), filename).exists():
95+
filename = parent.joinpath(Path("node_modules"), filename)
96+
break
97+
98+
if not filename.exists():
99+
raise InvalidCompilation(f"Unknown file: {filename}")
100+
101+
return filename
102+
103+
62104
# pylint: disable=too-many-branches
63105
def convert_filename(
64106
used_filename: Union[str, Path],
@@ -75,9 +117,6 @@ def convert_filename(
75117
crytic_compile (CryticCompile): Associated CryticCompile object
76118
working_dir (Optional[Union[str, Path]], optional): Working directory. Defaults to None.
77119
78-
Raises:
79-
InvalidCompilation: [description]
80-
81120
Returns:
82121
Filename: Filename converted
83122
"""
@@ -91,9 +130,9 @@ def convert_filename(
91130
else:
92131
filename = Path(filename_txt)
93132

133+
# cwd points to the directory to be used
94134
if working_dir is None:
95135
cwd = Path.cwd()
96-
working_dir = cwd
97136
else:
98137
working_dir = Path(working_dir)
99138
if working_dir.is_absolute():
@@ -106,27 +145,21 @@ def convert_filename(
106145
filename = filename.relative_to(Path(crytic_compile.package_name))
107146
except ValueError:
108147
pass
109-
if not filename.exists():
110-
if cwd.joinpath(Path("node_modules"), filename).exists():
111-
filename = cwd.joinpath("node_modules", filename)
112-
elif cwd.joinpath(Path("contracts"), filename).exists():
113-
filename = cwd.joinpath("contracts", filename)
114-
elif working_dir.joinpath(filename).exists():
115-
filename = working_dir.joinpath(filename)
116-
else:
117-
raise InvalidCompilation(f"Unknown file: {filename}")
118-
elif not filename.is_absolute():
148+
149+
filename = _verify_filename_existence(filename, cwd)
150+
151+
if not filename.is_absolute():
119152
filename = cwd.joinpath(filename)
120153

121154
absolute = filename
122155
relative = Path(os.path.relpath(filename, Path.cwd()))
123156

124157
# Build the short path
125158
try:
126-
if working_dir.is_absolute():
127-
short = absolute.relative_to(working_dir)
159+
if cwd.is_absolute():
160+
short = absolute.relative_to(cwd)
128161
else:
129-
short = relative.relative_to(working_dir)
162+
short = relative.relative_to(cwd)
130163
except ValueError:
131164
short = relative
132165
except RuntimeError:

scripts/ci_test_hardhat.sh

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#!/usr/bin/env bash
22

3-
### Test hardhat integration
3+
echo "Testing hardhat integration of $(realpath "$(which crytic-compile)")"
44

55
cd tests/hardhat || exit 255
66

77
npm install
88

9-
crytic-compile .
10-
if [ $? -ne 0 ]
11-
then
12-
echo "hardhat test failed"
13-
exit 255
9+
if ! crytic-compile .
10+
then echo "Monorepo test failed" && exit 255
11+
else echo "Monorepo test passed" && exit 0
1412
fi

scripts/ci_test_monorepo.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
3+
echo "Testing monorepo integration of $(realpath "$(which crytic-compile)")"
4+
5+
cd tests/monorepo || exit 255
6+
7+
npm install
8+
9+
echo "Testing from the root of a monorepo"
10+
if ! crytic-compile ./contracts
11+
then echo "Monorepo test failed" && exit 255
12+
fi
13+
14+
cd contracts || exit 255
15+
16+
echo "Testing from within a subdir of a monorepo"
17+
if ! crytic-compile .
18+
then echo "Monorepo test failed" && exit 255
19+
fi
20+
21+
echo "Monorepo test passed" && exit 0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
description="Util to facilitate smart contracts compilation.",
1212
url="https://github.com/crytic/crytic-compile",
1313
author="Trail of Bits",
14-
version="0.2.4",
14+
version="0.2.5",
1515
packages=find_packages(),
1616
python_requires=">=3.8",
1717
install_requires=["pycryptodome>=3.4.6"],
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//SPDX-License-Identifier: Unlicense
2+
pragma solidity ^0.7.0;
3+
4+
import "hardhat/console.sol";
5+
6+
7+
contract Greeter {
8+
string greeting;
9+
10+
constructor(string memory _greeting) {
11+
console.log("Deploying a Greeter with greeting:", _greeting);
12+
greeting = _greeting;
13+
}
14+
15+
function greet() public view returns (string memory) {
16+
return greeting;
17+
}
18+
19+
function setGreeting(string memory _greeting) public {
20+
console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
21+
greeting = _greeting;
22+
}
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require("@nomiclabs/hardhat-waffle");
2+
3+
// This is a sample Hardhat task. To learn how to create your own go to
4+
// https://hardhat.org/guides/create-task.html
5+
task("accounts", "Prints the list of accounts", async () => {
6+
const accounts = await ethers.getSigners();
7+
8+
for (const account of accounts) {
9+
console.log(account.address);
10+
}
11+
});
12+
13+
// You need to export an object to set up your config
14+
// Go to https://hardhat.org/config/ to learn more
15+
16+
/**
17+
* @type import('hardhat/config').HardhatUserConfig
18+
*/
19+
module.exports = {
20+
solidity: "0.7.3",
21+
};
22+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "hardhat-project",
3+
"devDependencies": {
4+
"@nomiclabs/hardhat-ethers": "^2.0.0",
5+
"@nomiclabs/hardhat-waffle": "^2.0.0",
6+
"chai": "^4.2.0",
7+
"ethereum-waffle": "^3.2.0",
8+
"ethers": "^5.0.19",
9+
"hardhat": "^2.0.2"
10+
}
11+
}

tests/monorepo/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "monorepo",
3+
"workspaces": [
4+
"contracts"
5+
],
6+
"private": true
7+
}

0 commit comments

Comments
 (0)