12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 |
- 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;
- };
|