11const vscode = require ( 'vscode' ) ;
2- const solc = require ( 'solc' ) ;
32const path = require ( 'path' ) ;
43const fs = require ( 'fs' ) ;
54
65let decorationType ;
76let workspaceRoot ;
8- let remappings = [ ] ;
7+ let foundryOutDir = 'out' ;
8+ let hardhatOutDir = null ;
99
1010function activate ( context ) {
11- console . log ( 'contract-size: Activating extension' ) ;
11+ console . info ( 'contract-size: Activating extension' ) ;
1212 workspaceRoot = vscode . workspace . workspaceFolders ?. [ 0 ] ?. uri . fsPath || '' ;
13- console . log ( 'contract-size: Workspace root:' , workspaceRoot ) ;
13+ console . info ( 'contract-size: Workspace root:' , workspaceRoot ) ;
1414
15- loadRemappings ( ) ;
15+ loadConfig ( ) ;
1616
1717 decorationType = vscode . window . createTextEditorDecorationType ( {
1818 after : {
@@ -38,29 +38,51 @@ function activate(context) {
3838 updateDecorations ( vscode . window . activeTextEditor . document ) ;
3939 }
4040
41- console . log ( 'contract-size: Activation complete' ) ;
41+ console . info ( 'contract-size: Activation complete' ) ;
4242}
4343
44- function loadRemappings ( ) {
45- const remappingsPath = path . join ( workspaceRoot , 'remappings.txt' ) ;
46- console . log ( 'contract-size: Looking for remappings at:' , remappingsPath ) ;
47- if ( fs . existsSync ( remappingsPath ) ) {
48- const content = fs . readFileSync ( remappingsPath , 'utf8' ) ;
49- remappings = content . split ( '\n' )
50- . filter ( line => line . trim ( ) !== '' )
51- . map ( line => {
52- const [ from , to ] = line . split ( '=' ) ;
53- return { from : from . trim ( ) , to : to . trim ( ) } ;
54- } ) ;
55- console . log ( 'contract-size: Loaded remappings:' , remappings ) ;
44+ function loadConfig ( ) {
45+ const foundryConfigPath = path . join ( workspaceRoot , 'foundry.toml' ) ;
46+
47+ if ( fs . existsSync ( foundryConfigPath ) ) {
48+ console . info ( 'contract-size: Foundry configuration detected' ) ;
49+ const configContent = fs . readFileSync ( foundryConfigPath , 'utf8' ) ;
50+ const outDirMatch = configContent . match ( / o u t \s * = \s * [ ' " ] ( .+ ) [ ' " ] / ) ;
51+ if ( outDirMatch ) {
52+ foundryOutDir = outDirMatch [ 1 ] ;
53+ }
54+ console . info ( `contract-size: Using Foundry out directory: ${ foundryOutDir } ` ) ;
55+ }
56+
57+ // Search for Hardhat artifacts directory
58+ hardhatOutDir = findArtifactsDir ( workspaceRoot ) ;
59+ if ( hardhatOutDir ) {
60+ console . info ( `contract-size: Hardhat artifacts directory found: ${ hardhatOutDir } ` ) ;
5661 } else {
57- console . log ( 'contract-size: No remappings.txt found' ) ;
62+ console . info ( 'contract-size: Hardhat artifacts directory not found' ) ;
63+ }
64+ }
65+
66+ function findArtifactsDir ( dir ) {
67+ const files = fs . readdirSync ( dir ) ;
68+ for ( const file of files ) {
69+ const filePath = path . join ( dir , file ) ;
70+ const stat = fs . statSync ( filePath ) ;
71+ if ( stat . isDirectory ( ) ) {
72+ if ( file === 'artifacts' ) {
73+ return filePath ;
74+ } else {
75+ const result = findArtifactsDir ( filePath ) ;
76+ if ( result ) return result ;
77+ }
78+ }
5879 }
80+ return null ;
5981}
6082
6183function updateDecorations ( document ) {
6284 const text = document . getText ( ) ;
63- const contractSizes = compileAndGetSizes ( document . fileName , text ) ;
85+ const contractSizes = getContractSizes ( document . fileName ) ;
6486 const decorations = [ ] ;
6587
6688 const contractRegex = / c o n t r a c t \s + ( \w + ) (?: \s + i s \s + [ ^ { ] + ) ? \s * { / g;
@@ -86,54 +108,43 @@ function updateDecorations(document) {
86108 vscode . window . activeTextEditor ?. setDecorations ( decorationType , decorations ) ;
87109}
88110
89- function compileAndGetSizes ( fileName , source ) {
90- const input = {
91- language : 'Solidity' ,
92- sources : { [ fileName ] : { content : source } } ,
93- settings : { outputSelection : { '*' : { '*' : [ 'evm.bytecode' ] } } }
94- } ;
95-
96- try {
97- const output = JSON . parse ( solc . compile ( JSON . stringify ( input ) , { import : findImports } ) ) ;
98- if ( output . errors ?. some ( error => error . severity === 'error' ) ) {
99- console . error ( 'contract-size: Compilation errors:' , output . errors ) ;
100- return { } ;
101- }
111+ function getContractSizes ( fileName ) {
112+ const sizes = { } ;
113+ const contractName = path . basename ( fileName , '.sol' ) ;
102114
103- const sizes = { } ;
104- for ( const [ , contracts ] of Object . entries ( output . contracts ) ) {
105- for ( const [ name , contract ] of Object . entries ( contracts ) ) {
106- sizes [ name ] = contract . evm . bytecode . object . length / 2 ;
107- }
108- }
109- return sizes ;
110- } catch ( error ) {
111- console . error ( 'contract-size: Compilation failed:' , error ) ;
112- return { } ;
115+ // Paths for both Foundry and Hardhat
116+ const foundryJsonPath = path . join ( workspaceRoot , foundryOutDir , `${ path . basename ( fileName ) } ` , `${ contractName } .json` ) ;
117+ let hardhatJsonPath = null ;
118+ if ( hardhatOutDir ) {
119+ const relativePath = path . relative ( path . join ( workspaceRoot , 'contracts' ) , fileName ) ;
120+ hardhatJsonPath = path . join ( hardhatOutDir , relativePath , `${ contractName } .json` ) ;
113121 }
114- }
115122
116- function findImports ( importPath ) {
117- console . log ( 'contract-size: Resolving import:' , importPath ) ;
118- for ( const remap of remappings ) {
119- if ( importPath . startsWith ( remap . from ) ) {
120- const remappedPath = importPath . replace ( remap . from , remap . to ) ;
121- const fullPath = path . resolve ( workspaceRoot , remappedPath ) ;
122- if ( fs . existsSync ( fullPath ) ) {
123- console . log ( 'contract-size: Resolved import via remappings:' , fullPath ) ;
124- return { contents : fs . readFileSync ( fullPath , 'utf8' ) } ;
123+ const jsonPaths = [ foundryJsonPath , hardhatJsonPath ] . filter ( Boolean ) ;
124+
125+ for ( const jsonPath of jsonPaths ) {
126+ try {
127+ if ( fs . existsSync ( jsonPath ) ) {
128+ const jsonContent = JSON . parse ( fs . readFileSync ( jsonPath , 'utf8' ) ) ;
129+ const bytecode = jsonPath === hardhatJsonPath ? jsonContent . deployedBytecode : jsonContent . deployedBytecode . object ;
130+ sizes [ contractName ] = ( bytecode . slice ( 2 ) . length ) / 2 ;
131+ console . info ( `contract-size: Size calculated for ${ contractName } from ${ jsonPath } ` ) ;
132+ break ; // Stop after finding the first valid file
125133 }
134+ } catch ( error ) {
135+ console . error ( `contract-size: Error reading or parsing JSON for ${ contractName } at ${ jsonPath } :` , error ) ;
126136 }
127- else {
128- console . log ( 'contract-size: Resolved import without remappings:' , importPath ) ;
129- return { contents : fs . readFileSync ( importPath , 'utf8' ) } ;
130- }
131137 }
132-
138+
139+ if ( ! sizes [ contractName ] ) {
140+ console . info ( `contract-size: JSON file not found for ${ contractName } in any expected location` ) ;
141+ }
142+
143+ return sizes ;
133144}
134145
135146function formatSize ( bytes ) {
136- return `${ ( bytes / 1024 ) . toFixed ( 2 ) / 2 } KB` ;
147+ return `${ ( bytes / 1024 ) . toFixed ( 2 ) } KB` ;
137148}
138149
139150function getSizeColor ( bytes ) {
0 commit comments