diff --git a/HARDHAT_README.md b/HARDHAT_README.md
index 2420f1e7..f182a53b 100644
--- a/HARDHAT_README.md
+++ b/HARDHAT_README.md
@@ -69,11 +69,13 @@ module.exports = {
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
| istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] |
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.|
+| coverageContractsTemp | *String* | `.coverage_contracts` | Temporary folder location for instrumented contracts - Note that this directory will automatically be deleted when coverage completes. |
| onServerReady[*][14] | *Function* | | Hook run *after* server is launched, *before* the tests execute. Useful if you need to use the Oraclize bridge or have setup scripts which rely on the server's availability. [More...][23] |
+| onPreCompile[*][14] | *Function* | | Hook run *after* filesystem and compiler configuration is applied, *before* the compiler is run. Can be used with the other hooks to be able to generate coverage reports on non-standard / customized directory structures, as well as contracts with absolute import paths. [More...][23] |
| onCompileComplete[*][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]|
| onTestsComplete[*][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]|
| onIstanbulComplete[*][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up. [More...][23]|
-| configureYulOptimizer | *Boolean* | false | (Experimental) Setting to `true` should resolve "stack too deep" compiler errrors in large projects using ABIEncoderV2 |
+| configureYulOptimizer | *Boolean* | false | (Experimental) Setting to `true` should resolve "stack too deep" compiler errors in large projects using ABIEncoderV2 |
[* Advanced use][14]
diff --git a/README.md b/README.md
index df6e3519..0f6d4128 100644
--- a/README.md
+++ b/README.md
@@ -125,11 +125,13 @@ module.exports = {
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
| istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] |
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.|
+| coverageContractsTemp | *String* | `.coverage_contracts` | Temporary folder location for instrumented contracts - Note that this directory will automatically be deleted when coverage completes. |
| onServerReady[*][14] | *Function* | | Hook run *after* server is launched, *before* the tests execute. Useful if you need to use the Oraclize bridge or have setup scripts which rely on the server's availability. [More...][23] |
+| onPreCompile[*][14] | *Function* | | Hook run *after* filesystem and compiler configuration is applied, *before* the compiler is run. Can be used with the other hooks to be able to generate coverage reports on non-standard / customized directory structures, as well as contracts with absolute import paths. [More...][23] |
| onCompileComplete[*][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]|
| onTestsComplete[*][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]|
| onIstanbulComplete[*][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up. [More...][23]|
-| configureYulOptimizer | *Boolean* | false | (Experimental) Setting to `true` should resolve "stack too deep" compiler errrors in large projects using ABIEncoderV2 |
+| configureYulOptimizer | *Boolean* | false | (Experimental) Setting to `true` should resolve "stack too deep" compiler errors in large projects using ABIEncoderV2 |
[* Advanced use][14]
diff --git a/docs/advanced.md b/docs/advanced.md
index c267e7f4..f1820026 100644
--- a/docs/advanced.md
+++ b/docs/advanced.md
@@ -34,6 +34,7 @@ The stages/hooks are (in order of execution):
| Stage | Post-stage hook |
|----------------------------------------|--------------------|
+| Before compiling | onPreCompile |
| Launch server | onServerReady |
| Instrument and compile contracts | onCompileComplete |
| Run tests using instrumented artifacts | onTestsComplete |
@@ -85,6 +86,14 @@ This option allows you to avoid that but it's important to realise that the temp
folder is **automatically deleted** when coverage completes. You shouldn't use it if your preferred
build target contains information you want to preserve between test runs.
+## Setting a custom temporary contracts directory
+
+A custom disposable folder to be used for the contracts can be specified by setting the
+```
+coverageContractsTemp
+```
+property in the configuration file. If not set, this directory defaults to `.coverage_contracts`.
+
## Reducing the instrumentation footprint
If your project is very large or if you have logic that's gas sensitive, it can be useful to
diff --git a/lib/api.js b/lib/api.js
index cbc83c4e..13ad991f 100644
--- a/lib/api.js
+++ b/lib/api.js
@@ -36,6 +36,7 @@ class API {
this.onTestsComplete = config.onTestsComplete || this.defaultHook;
this.onCompileComplete = config.onCompileComplete || this.defaultHook;
this.onIstanbulComplete = config.onIstanbulComplete || this.defaultHook;
+ this.onPreCompile = config.onPreCompile || this.defaultHook;
this.server = null;
this.defaultPort = 8555;
diff --git a/plugins/resources/plugin.utils.js b/plugins/resources/plugin.utils.js
index 39c5586c..8ef73105 100644
--- a/plugins/resources/plugin.utils.js
+++ b/plugins/resources/plugin.utils.js
@@ -94,7 +94,7 @@ function toRelativePath(pathToFile, pathToParent){
function getTempLocations(config){
const contractsRoot = path.parse(config.contractsDir).dir
const cwd = config.workingDir;
- const contractsDirName = '.coverage_contracts';
+ const contractsDirName = config.coverageContractsTemp || '.coverage_contracts';
const artifactsDirName = config.temp || '.coverage_artifacts';
return {
diff --git a/plugins/truffle.plugin.js b/plugins/truffle.plugin.js
index 6888d67d..aebd9df6 100644
--- a/plugins/truffle.plugin.js
+++ b/plugins/truffle.plugin.js
@@ -93,6 +93,9 @@ async function plugin(config){
config.all = true;
config.compilers.solc.settings.optimizer.enabled = false;
+ // Run pre-compile hook;
+ await api.onPreCompile(config);
+
// Compile Instrumented Contracts
await truffle.contracts.compile(config);
await api.onCompileComplete(config);
diff --git a/test/integration/projects/test-files/.solcover.js b/test/integration/projects/test-files/.solcover.js
index 42903c81..7d813ad6 100644
--- a/test/integration/projects/test-files/.solcover.js
+++ b/test/integration/projects/test-files/.solcover.js
@@ -5,6 +5,7 @@ module.exports = {
skipFiles: ['Migrations.sol'],
silent: process.env.SILENT ? true : false,
istanbulReporter: ['json-summary', 'text'],
+ onPreCompile: fn.bind(null, 'running onPreCompile'),
onServerReady: fn.bind(null, 'running onServerReady'),
onTestsComplete: fn.bind(null, 'running onTestsComplete'),
onCompileComplete: fn.bind(null, 'running onCompileComplete'),
diff --git a/test/units/truffle/standard.js b/test/units/truffle/standard.js
index f1946ac1..909c9007 100644
--- a/test/units/truffle/standard.js
+++ b/test/units/truffle/standard.js
@@ -420,6 +420,7 @@ describe('Truffle Plugin: standard use cases', function() {
await plugin(truffleConfig);
assert(
+ mock.loggerOutput.val.includes('running onPreCompile') &&
mock.loggerOutput.val.includes('running onServerReady') &&
mock.loggerOutput.val.includes('running onTestsComplete') &&
mock.loggerOutput.val.includes('running onCompileComplete') &&