56 lines
2.3 KiB
JavaScript
56 lines
2.3 KiB
JavaScript
const fs = require('fs');
|
|
const glob = require('glob');
|
|
const match = require('micromatch');
|
|
const path = require('path');
|
|
const { findAll } = require('solidity-ast/utils');
|
|
|
|
module.exports = function (
|
|
pattern = 'contracts/**/*.sol',
|
|
skipPatterns = ['contracts/mocks/**/*.sol'],
|
|
artifacts = [],
|
|
) {
|
|
// Use available hardhat artifacts. They reliably identify pragmas and the contracts, libraries and interfaces
|
|
// definitions with minimal IO operations.
|
|
const metadata = Object.fromEntries(
|
|
artifacts.flatMap(artifact => {
|
|
const { output: solcOutput } = require(path.resolve(__dirname, '..', artifact));
|
|
return Object.keys(solcOutput.contracts)
|
|
.filter(source => match.all(source, pattern) && !match.any(source, skipPatterns))
|
|
.map(source => [
|
|
source,
|
|
{
|
|
pragma: Array.from(findAll('PragmaDirective', solcOutput.sources[source].ast))
|
|
.find(({ literals }) => literals.at(0) == 'solidity')
|
|
.literals.slice(1)
|
|
.join(''),
|
|
sources: Array.from(findAll('ImportDirective', solcOutput.sources[source].ast)).map(
|
|
({ absolutePath }) => absolutePath,
|
|
),
|
|
interface: Array.from(findAll('ContractDefinition', solcOutput.sources[source].ast)).every(
|
|
({ contractKind }) => contractKind === 'interface',
|
|
),
|
|
},
|
|
]);
|
|
}),
|
|
);
|
|
|
|
// Artifacts are missing files that only include imports. We have a few of these in contracts/interfaces
|
|
// We add the missing metadata entries using the foundry artifacts
|
|
glob
|
|
.sync(pattern)
|
|
.filter(file => !match.any(file, skipPatterns) && !Object.hasOwn(metadata, file))
|
|
.forEach(file => {
|
|
const entries = glob.sync(`out/${path.basename(file)}/*`);
|
|
metadata[file] = {
|
|
pragma: fs.readFileSync(file, 'utf-8').match(/pragma solidity (?<pragma>[<>=^]*[0-9]+\.[0-9]+\.[0-9]+);/)
|
|
?.groups.pragma,
|
|
sources: entries
|
|
.flatMap(entry => Object.keys(JSON.parse(fs.readFileSync(entry)).metadata.sources))
|
|
.filter(source => source !== file && match.all(source, pattern) && !match.any(source, skipPatterns)),
|
|
interface: entries.every(entry => path.basename(entry).match(/^I[A-Z]/)),
|
|
};
|
|
});
|
|
|
|
return metadata;
|
|
};
|