1+ const vscode = require ( 'vscode' ) ;
2+ const solc = require ( 'solc' ) ;
3+ const path = require ( 'path' ) ;
4+ const fs = require ( 'fs' ) ;
5+
6+ let decorationType ;
7+ let workspaceRoot ;
8+ let remappings = [ ] ;
9+
10+ function activate ( context ) {
11+ console . log ( 'contract-size: Activating extension' ) ;
12+ workspaceRoot = vscode . workspace . workspaceFolders ?. [ 0 ] ?. uri . fsPath || '' ;
13+ console . log ( 'contract-size: Workspace root:' , workspaceRoot ) ;
14+
15+ loadRemappings ( ) ;
16+
17+ decorationType = vscode . window . createTextEditorDecorationType ( {
18+ after : {
19+ margin : '0 0 0 1em' ,
20+ textDecoration : 'none; opacity: 0.7;'
21+ }
22+ } ) ;
23+
24+ context . subscriptions . push (
25+ vscode . workspace . onDidChangeTextDocument ( event => {
26+ if ( event . document . languageId === 'solidity' ) {
27+ updateDecorations ( event . document ) ;
28+ }
29+ } ) ,
30+ vscode . window . onDidChangeActiveTextEditor ( editor => {
31+ if ( editor ?. document . languageId === 'solidity' ) {
32+ updateDecorations ( editor . document ) ;
33+ }
34+ } )
35+ ) ;
36+
37+ if ( vscode . window . activeTextEditor ?. document . languageId === 'solidity' ) {
38+ updateDecorations ( vscode . window . activeTextEditor . document ) ;
39+ }
40+
41+ console . log ( 'contract-size: Activation complete' ) ;
42+ }
43+
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 ) ;
56+ } else {
57+ console . log ( 'contract-size: No remappings.txt found' ) ;
58+ }
59+ }
60+
61+ function updateDecorations ( document ) {
62+ const text = document . getText ( ) ;
63+ const contractSizes = compileAndGetSizes ( document . fileName , text ) ;
64+ const decorations = [ ] ;
65+
66+ const contractRegex = / c o n t r a c t \s + ( \w + ) (?: \s + i s \s + [ ^ { ] + ) ? \s * { / g;
67+ let match ;
68+ while ( ( match = contractRegex . exec ( text ) ) !== null ) {
69+ const contractName = match [ 1 ] ;
70+ const size = contractSizes [ contractName ] ;
71+ if ( size ) {
72+ const pos = document . positionAt ( match . index ) ;
73+ const range = document . lineAt ( pos . line ) . range ;
74+ decorations . push ( {
75+ range,
76+ renderOptions : {
77+ after : {
78+ contentText : ` (${ formatSize ( size ) } )` ,
79+ color : getSizeColor ( size ) ,
80+ }
81+ }
82+ } ) ;
83+ }
84+ }
85+
86+ vscode . window . activeTextEditor ?. setDecorations ( decorationType , decorations ) ;
87+ }
88+
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+ }
102+
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 { } ;
113+ }
114+ }
115+
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' ) } ;
125+ }
126+ }
127+ else {
128+ console . log ( 'contract-size: Resolved import without remappings:' , importPath ) ;
129+ return { contents : fs . readFileSync ( importPath , 'utf8' ) } ;
130+ }
131+ }
132+
133+ }
134+
135+ function formatSize ( bytes ) {
136+ return `${ ( bytes / 1024 ) . toFixed ( 2 ) / 2 } KB` ;
137+ }
138+
139+ function getSizeColor ( bytes ) {
140+ const kb = bytes / 1024 ;
141+ if ( kb > 22 ) return '#FF4136' ;
142+ if ( kb > 20 ) return '#FFDC00' ;
143+ return '#2ECC40' ;
144+ }
145+
146+ module . exports = {
147+ activate
148+ } ;
0 commit comments