From 9e595d4391ecc3d0313154a59ec605669947b9b0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 Mar 2024 13:13:45 +0100 Subject: [PATCH] Apply exlucde filter to imported paths --- src/core.test.ts | 24 +++++++++--- src/core.test.ts.md | 85 ++++++++++++++++++++++++++++++++++++++++++ src/core.test.ts.snap | Bin 2425 -> 2464 bytes src/core.ts | 6 ++- src/plugin.ts | 29 +++----------- 5 files changed, 115 insertions(+), 29 deletions(-) diff --git a/src/core.test.ts b/src/core.test.ts index b9f8202..8599313 100644 --- a/src/core.test.ts +++ b/src/core.test.ts @@ -15,13 +15,14 @@ test('snapshot', async t => { const rootRelativeSourcesPath = path.relative(hre.config.paths.root, hre.config.paths.sources); const sourcesPathPrefix = path.normalize(rootRelativeSourcesPath + '/'); const include = (sourceName: string) => sourceName.startsWith(sourcesPathPrefix); + const exclude = (sourceName: string) => false; const [bip] = await hre.artifacts.getBuildInfoPaths(); const bi: BuildInfo = JSON.parse(await fs.readFile(bip!, 'utf8')); const config = { paths: hre.config.paths, exposed: { ...baseConfig, initializers: false } }; - const exposed = getExposed(bi.output, include, config); + const exposed = getExposed(bi.output, include, exclude, config); const exposedFiles = [...exposed.values()].sort((a, b) => a.absolutePath.localeCompare(b.absolutePath)) - for (const rf of exposedFiles) { + for (const rf of exposedFiles) { t.snapshot(rf.content.rawContent); } }); @@ -30,7 +31,7 @@ test('snapshot initializers', async t => { const [bip] = await hre.artifacts.getBuildInfoPaths(); const bi: BuildInfo = JSON.parse(await fs.readFile(bip!, 'utf8')); const config = { paths: hre.config.paths, exposed: { ...baseConfig, initializers: true } }; - const exposed = getExposed(bi.output, sourceName => sourceName === 'contracts/Initializers.sol', config); + const exposed = getExposed(bi.output, sourceName => sourceName === 'contracts/Initializers.sol', () => false, config); const exposedFiles = [...exposed.values()].sort((a, b) => a.absolutePath.localeCompare(b.absolutePath)) for (const rf of exposedFiles) { t.snapshot(rf.content.rawContent); @@ -41,9 +42,22 @@ test('snapshot imports', async t => { const [bip] = await hre.artifacts.getBuildInfoPaths(); const bi: BuildInfo = JSON.parse(await fs.readFile(bip!, 'utf8')); const config = { paths: hre.config.paths, exposed: { ...baseConfig, initializers: false, imports: true } }; - const exposed = getExposed(bi.output, sourceName => sourceName === 'contracts/Imported.sol', config); + const exposed = getExposed(bi.output, sourceName => sourceName === 'contracts/Imported.sol', () => false, config); const exposedFiles = [...exposed.values()].sort((a, b) => a.absolutePath.localeCompare(b.absolutePath)) - for (const rf of exposedFiles) { + for (const rf of exposedFiles) { + const absolutePath = path.relative(process.cwd(), rf.absolutePath); + const { rawContent } = rf.content; + t.snapshot({ absolutePath, rawContent }); + } +}); + +test('snapshot exluded imports', async t => { + const [bip] = await hre.artifacts.getBuildInfoPaths(); + const bi: BuildInfo = JSON.parse(await fs.readFile(bip!, 'utf8')); + const config = { paths: hre.config.paths, exposed: { ...baseConfig, initializers: false, imports: true } }; + const exposed = getExposed(bi.output, sourceName => sourceName === 'contracts/Imported.sol', sourceName => sourceName =='contracts/Imported2.sol', config); + const exposedFiles = [...exposed.values()].sort((a, b) => a.absolutePath.localeCompare(b.absolutePath)) + for (const rf of exposedFiles) { const absolutePath = path.relative(process.cwd(), rf.absolutePath); const { rawContent } = rf.content; t.snapshot({ absolutePath, rawContent }); diff --git a/src/core.test.ts.md b/src/core.test.ts.md index 6784d97..05c8722 100644 --- a/src/core.test.ts.md +++ b/src/core.test.ts.md @@ -1104,3 +1104,88 @@ Generated by [AVA](https://avajs.dev). }␊ `, } + +## snapshot exluded imports + +> Snapshot 1 + + { + absolutePath: 'contracts-exposed/$_/@openzeppelin/contracts/proxy/Clones.sol', + rawContent: `// SPDX-License-Identifier: UNLICENSED␊ + ␊ + pragma solidity >=0.6.0;␊ + ␊ + import "@openzeppelin/contracts/proxy/Clones.sol";␊ + ␊ + contract $Clones {␊ + bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";␊ + ␊ + event return$clone(address instance);␊ + ␊ + event return$cloneDeterministic(address instance);␊ + ␊ + constructor() payable {␊ + }␊ + ␊ + function $clone(address implementation) external payable returns (address instance) {␊ + (instance) = Clones.clone(implementation);␊ + emit return$clone(instance);␊ + }␊ + ␊ + function $cloneDeterministic(address implementation,bytes32 salt) external payable returns (address instance) {␊ + (instance) = Clones.cloneDeterministic(implementation,salt);␊ + emit return$cloneDeterministic(instance);␊ + }␊ + ␊ + function $predictDeterministicAddress(address implementation,bytes32 salt,address deployer) external pure returns (address predicted) {␊ + (predicted) = Clones.predictDeterministicAddress(implementation,salt,deployer);␊ + }␊ + ␊ + function $predictDeterministicAddress(address implementation,bytes32 salt) external view returns (address predicted) {␊ + (predicted) = Clones.predictDeterministicAddress(implementation,salt);␊ + }␊ + ␊ + receive() external payable {}␊ + }␊ + `, + } + +> Snapshot 2 + + { + absolutePath: 'contracts-exposed/Imported.sol', + rawContent: `// SPDX-License-Identifier: UNLICENSED␊ + ␊ + pragma solidity >=0.6.0;␊ + ␊ + import "../contracts/Imported.sol";␊ + import "../contracts/Imported2.sol";␊ + import "@openzeppelin/contracts/proxy/Clones.sol";␊ + ␊ + contract $NotImported is NotImported {␊ + bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";␊ + ␊ + constructor() payable {␊ + }␊ + ␊ + receive() external payable {}␊ + }␊ + ␊ + contract $Imported is Imported {␊ + bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";␊ + ␊ + constructor() payable {␊ + }␊ + ␊ + function $_testNotImported(NotImported ni) external {␊ + super._testNotImported(ni);␊ + }␊ + ␊ + function $_testNotImported2(NotImported2 ni) external {␊ + super._testNotImported2(ni);␊ + }␊ + ␊ + receive() external payable {}␊ + }␊ + `, + } diff --git a/src/core.test.ts.snap b/src/core.test.ts.snap index 62e03a23d55114641f1386e8e172d2c346e88598..bfdc334b740159b725b6caf6a25025ee53fd89d9 100644 GIT binary patch literal 2464 zcmV;R319X>RzVj-_w+ zcN$L{JI`u0q?-nz(!)k$TQM*tK%v(5k*#P$6{-!(&>lXk)%-WohW|!FmJ~eySd%14 z>W?Y3nzCdL`x;Ut#lRK?m`c6gV9HyG348)=5 zpmY0^p+XH(-;_)+27L`OQlC5!0u_ixu)Zlln?iztHi7$8`&466TfZNYT6KL!Qfs@7 zfz(oA%)y()u*cgi&A`x#YvaVAK0-$!{GZFJ6OMbH^@a0&F~kZ* z29`E@6k3!MWt|r*M%=(W1kR5&G$CokdSbNsEFTe>ry@^BMvl?R=@@J0A;)O1&Db&I zR120PCM>U#4a>X{FOKsiGqAh=4H?*f?#{}<@Bk7-0ajq##9jdcG2L|(m2BK=+gL?*Z9v}<3C>tfTCab#u&A-K?3aJJn1ThyFR4(p%4#-p)1u(LH^eBll4n*9saJ+o6878`_~h z{Axx!)Hl$gc@6>RcTo7kFdUb1e$B+CbYPkYUk2*j|5oa3N(eJ$Y>avxG32{2B#Pl6qCxl_R3_4iK44N1_77w<&c^~n(d`iP>*2F%}N_P@V-Xr3|R6g zIJ4e+`XQCEmIkaruan44Vv5%7Fa_#&1yH}sLVf!D?2YecQszmr3q9olGM{1t#Lck* zQm$R)+EuPyNe+1k$jz)>E~0XkELX{Lm0Yb=@)-3Oy*$DZ#FQC^fBy`hA^0)qI|AAe zZWdW1+b3_1UY{Hs_s$Mpi=C4VJz@4dM*RklL!h@;r-#Sw)^&(XFr=BrG3sAq7Io-M z(4^5hvQwtA{B&G2H$HRZr(bZ|@Xas5ePT>_rpa@`S~xZWe9r8T?yB2WNn|i(>G+ z_`#0=DU>>M`U#en5g>9V%J(0{Nkj*M7B_oP@~AyAQNC{Fk68j38cG)GC6T+ar&M%lbnDod2b#EWaCfdF)V43 zyH7Ggfd<5!Mo_w_l35kl#uWVeI$8+7zFDU68#l@%Hm6GPC!kDbHx8J_smK}ZETfHh zN|f;}*6gz%rDF@YkHT@diq56q1M9U1{lXtMvZRF81%yzEwbpL!6JKxbH7q{D`x$&W zAZ#~o2F5cM_i+XG#9oOT$9uS835@6x(laM{TxV77P<1)GFpShp{$Az*T!7d?C9BB$ojQwRaQ ztfep3?P7AM9$h({SMilDOBL*!L__~mL=xhvFDD-;`UAURd+*<&8@B(vSJr?oc?~F6 z)x)VaG63f%yJo1Y7G0idQLel!ORZVOYh_*O?K8yr)s+@EM6WTU0<^43EnVlfR;6;e zhAh{~s=O-eSC^-LwYXZn#*AXXvOsaw4RMjxuUQsZ8sF_RKDNgSr(ezlRE~R)fd;_wQG#zphl*D%E?HFRE+5 zsjgS5cOF!K1^Sku4JmvD=)iB>5BB%I&2De>w(}OeMkv$>_~_ivf|#5&X$1Fa|mqm6&mXKtfcud<|Maqc=XJFb5IJF2$5VMmlGV*d$Oz z3JrBS@OaKM^erDog{EeVo$4h)%Gd{F1fd#|1lJmfB8{~KnQR3t%x0*<#ck#zEA+vG z^}i?0*FWCSeEns7zUIGdkUb%nUt(X9baVM7_C=kive?W1Z4i7xoBan1We`>dVPz0j e24Q6oRt8~Z5LO0ZWe_&2AngBM@c|!#YXAT{nxj$x literal 2425 zcmV-<35NDTRzVgSH9{jLs0Wzm{Gd9kyEsN1cPGtE-j`&UGL;riN66N76UD zTaCw!ttYFiNViNIOZOX%O~o{@4HWEb9t;(2phC4lD%>YT{vBz}zat?>3a)=zmB^k01)>W`Zo?slwgI#;-TT$|tITREcSBaItxU*jZCV&u zEtN)&5PfS}(71$_1vO-F^p!3oj0q2xQ2NQ+>bM`pxuboB-Bsx36XgzaxpP-Ois?gSUw2_Mtga}fgz_N zSdKtgUL*|5Jcwtf`J4gl_J2bF`_Ju30nF$_8xh+oFb=Wjz$QYrT?hG>6L-%58CZVw zSQ@VTGV8;9F7wH=+!N3h@=MGt^4Kg>K&@|R~1m1QRKiVQ-tYoLy94m+9wOVGc{E+Fx3>}U@=YgFfi4_*_dj4c{#pR zL`EXw6kmTKy)_`cy)7|SL;*KB`KpG%@Vjx^YF^VoJ=>uet z1y>QqTs2U-8`-DM+P{&`+7COcmugc++!H%%U*xPjjJawlB5oYKS`=Qb8wszLXa7`F z`ZO6#I9umn$5U6}pI`8wQ1GA31^9=hz&{jP_ayABVX93T@lfon6Ops>Fy^WaOFQe7 zI_vaCI_oq?Pj#BrQ=Jy-sp8asBq+W1u=IMW`41v5NA9Bn*8+lztx&(*4y{n{e>tHQ z>Pxbe&a(+Py^X@}Ow$c1_iG{{r31@C#(Ch*-EXAMx&RDzXcEpr#!jx~y^!biB zDm#)6b<-Xtgry?@sy?3V*ymd%;$461R(vOlgW-0WPJ~nwGL%EuAJ{M-Ls`hMS&m^- z$nasgCO;G~ycDx*6EK}aJAvJ0+HNcfj?6Z@1zSAhx>vBvXVEC5G)Wz7yCEPH=Fs~L zwz)+595bYnU=mC16ck9AWee<(_yy8QWxAn=2L9Ira3n9RgGOz zHFi~FS3Dp|a9q#WMHyA2WHm}wqvT?Zl82}_>){bzkkvB-hX4GTaZJDx)V%=O0IruA zBilzW4_+MY9d?iRUWkK}H9evCB<5-GJOqAw{%W7B`mZ8n%pnaLgz^f#s4Hj8CQZ)4 zFr_QYFNd<55I))i7DYhlh=ZkaB2kno&;1-N6C=)pEc>psgCG||1Jr&^V;8ga1_N+iDeUs@~{P{{LmVB$f zD?M#!jm^)x*F#eQRB12gTre_Km*) zcB#~vTTd{wkXv#l%eU{uNko@{n0GUODsd;7c{4SaNC;9gYu=jof;a#W=$( zkHoAW=QB$V{U2wU=c|yY4XJ0EntQo@6{%l&Qr-XCDYAD;%FZzCj!=5R5O1?d<_jjC zMSHY(B7q-Dbs%@Oppyb$ba-W(RK+oM`0N7Onh8`Qn9yaiDV{ax zOp;Bkgf;YmhLNQ~X24KmHUdMEU)!ZJIdN-o{w|tHPGFY2Hjfjs_9yBXwzMeR#~Go( z17b}hEGesGRR!26ps(+vnds}oGELt&D3jDIl;BTbnXGP{FilfYFbuOm8;O*t;v1~k zS3fFeD9}Di$MGuLm;MPjFFfk+{AQD`G@*0`Q>f_Tj6M=Y&pz?zt-XTT&+z^LZw?6K zhGt+qu+Yl|SQC3k+&JCC1#GzHwURC5p8ihgEK*xvbf=n323?o?+$OTm76XF$1A zJ)CPJ4RAl%XNIcNqVscFlsjIQ<<_jjYt^~Zn+L?{ohvO5M6WR81ZZ_CwF;e^dMcI6 zHDrZO*2$~t{ObIiUoG!euQ1~wOOuT&o3_ckkX^s(rFlTVAT&AvDXM*H)Hl zx9-(G20gNBA7J<#;J#nD8?5hr%xP_sk+E4vI+h+=D{b2wAouaER diff --git a/src/core.ts b/src/core.ts index 8d2c676..980626a 100644 --- a/src/core.ts +++ b/src/core.ts @@ -31,6 +31,7 @@ export const getExposedPath = (config: Config) => path.join(config.paths.root, c export function getExposed( solcOutput: SolcOutput, include: (sourceName: string) => boolean, + exclude: (sourceName: string) => boolean, config: Config, ): Map { const rootRelativeSourcesPath = path.relative(config.paths.root, config.paths.sources); @@ -41,7 +42,7 @@ export function getExposed( const imports: Record> = {}; for (const { ast } of Object.values(solcOutput.sources)) { - if (!include(ast.absolutePath)) { + if (!include(ast.absolutePath) || exclude(ast.absolutePath)) { continue; } @@ -74,6 +75,9 @@ export function getExposed( } for (const [absoluteImportedPath, contracts] of Object.entries(imports)) { + if (exclude(absoluteImportedPath)) { + continue; + } const filter: ContractFilter = node => contracts.has(node); const ast = solcOutput.sources[absoluteImportedPath]?.ast; assert(ast !== undefined); diff --git a/src/plugin.ts b/src/plugin.ts index 1dab9e6..840cf07 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -86,12 +86,15 @@ task(TASK_COMPILE_SOLIDITY_COMPILE_JOB, async (args, hre, superC }); async function getExposedJob(hre: HardhatRuntimeEnvironment, compilationJob: CompilationJob, output: CompilerOutput): Promise { + const path = await import('path'); + const { isMatch } = await import('micromatch'); const { getExposed } = await import('./core'); - const inputFiles = Object.fromEntries(compilationJob.getResolvedFiles().map(rf => [rf.sourceName, rf.absolutePath])); + const sourcesDir = path.relative(hre.config.paths.root, hre.config.paths.sources); - const include = await getMatcher(hre.config); - const exposed = getExposed(output, include, hre.config); + const include = (sourceName: string) => sourceName.startsWith(sourcesDir) && hre.config.exposed.include.some(p => isMatch(path.relative(sourcesDir, sourceName), p)); + const exclude = (sourceName: string) => hre.config.exposed.exclude.some(p => isMatch(path.relative(sourcesDir, sourceName), p)); + const exposed = getExposed(output, include, exclude, hre.config); const cj: CompilationJob = { getResolvedFiles: () => [...exposed.values()], @@ -121,23 +124,3 @@ async function cleanExposed(hre: HardhatRuntimeEnvironment) { const exposedPath = getExposedPath(hre.config); await fs.rm(exposedPath, { recursive: true, force: true }); } - -async function getMatcher(config: HardhatConfig) { - const { isMatch } = await import('micromatch'); - const path = await import('path'); - - const sourcesDir = path.relative(config.paths.root, config.paths.sources); - const includePatterns = config.exposed.include; - const excludePatterns = config.exposed.exclude; - - return function (sourceName: string) { - if (!sourceName.startsWith(sourcesDir)) { - return false; - } - sourceName = path.relative(sourcesDir, sourceName); - return ( - includePatterns.some(p => isMatch(sourceName, p)) && - !excludePatterns.some(p => isMatch(sourceName, p)) - ); - }; -}