Skip to content

Commit ccb2c16

Browse files
author
ci-bot
committed
proxy pattern
1 parent 1f855b9 commit ccb2c16

File tree

3 files changed

+158
-71
lines changed

3 files changed

+158
-71
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
'use strict'
2+
3+
import { Plugin } from '@remixproject/engine'
4+
import { Compiler } from './compiler'
5+
import { DependencyResolver } from './dependency-resolver'
6+
import { CompilerState, Source } from './types'
7+
8+
/**
9+
* SmartCompiler - A wrapper around the standard Compiler that automatically
10+
* handles dependency resolution before compilation.
11+
*
12+
* This class exposes the exact same interface as Compiler but adds intelligent
13+
* pre-compilation dependency resolution using DependencyResolver.
14+
*
15+
* Usage:
16+
* const smartCompiler = new SmartCompiler(pluginApi)
17+
* smartCompiler.compile(sources, target) // Same interface as Compiler!
18+
*
19+
* What it does:
20+
* 1. Uses DependencyResolver to build complete source bundle with npm aliases
21+
* 2. Saves resolution index for "Go to Definition" functionality
22+
* 3. Passes pre-built sources to the underlying Compiler
23+
* 4. Transparently forwards all other method calls to the underlying Compiler
24+
*/
25+
export class SmartCompiler {
26+
private compiler: Compiler
27+
private pluginApi: Plugin
28+
29+
constructor(
30+
pluginApi: Plugin,
31+
importCallback?: (url: string, cb: (err: Error | null, result?: any) => void) => void,
32+
importResolverFactory?: (target: string) => any
33+
) {
34+
this.pluginApi = pluginApi
35+
36+
// Create the underlying compiler
37+
// Note: We can pass null for importResolverFactory since we handle imports differently
38+
this.compiler = new Compiler(importCallback, null)
39+
40+
console.log(`[SmartCompiler] 🧠 Created smart compiler wrapper`)
41+
42+
// Create a proxy that transparently forwards all method calls to the underlying compiler
43+
// except for the ones we explicitly override (compile)
44+
return new Proxy(this, {
45+
get(target, prop, receiver) {
46+
// If the property exists on SmartCompiler, use it
47+
if (prop in target) {
48+
return Reflect.get(target, prop, receiver)
49+
}
50+
51+
// Otherwise, forward to the underlying compiler
52+
const compilerValue = Reflect.get(target.compiler, prop)
53+
54+
// If it's a method, bind it to the compiler instance
55+
if (typeof compilerValue === 'function') {
56+
return compilerValue.bind(target.compiler)
57+
}
58+
59+
// If it's a property, return it directly
60+
return compilerValue
61+
}
62+
})
63+
}
64+
65+
/**
66+
* Smart compile method - performs dependency resolution first, then compiles
67+
*/
68+
public compile(sources: Source, target: string): void {
69+
console.log(`[SmartCompiler] 🚀 Starting smart compilation for: ${target}`)
70+
71+
// Perform dependency resolution asynchronously, then compile
72+
this.performSmartCompilation(sources, target).catch(error => {
73+
console.log(`[SmartCompiler] ❌ Smart compilation failed:`, error)
74+
75+
// Fallback: try to compile with original sources if dependency resolution fails
76+
console.log(`[SmartCompiler] 🔄 Falling back to direct compilation...`)
77+
this.compiler.compile(sources, target)
78+
})
79+
}
80+
81+
/**
82+
* Internal async method to handle dependency resolution and compilation
83+
*/
84+
private async performSmartCompilation(sources: Source, target: string): Promise<void> {
85+
try {
86+
// Step 1: Build dependency tree BEFORE compilation
87+
console.log(`[SmartCompiler] 🌳 Building dependency tree...`)
88+
const depResolver = new DependencyResolver(this.pluginApi, target)
89+
90+
// Build complete source bundle with context-aware resolution
91+
const sourceBundle = await depResolver.buildDependencyTree(target)
92+
93+
console.log(`[SmartCompiler] ✅ Dependency tree built successfully`)
94+
console.log(`[SmartCompiler] 📦 Source bundle contains ${sourceBundle.size} files`)
95+
96+
// Step 2: Save resolution index for "Go to Definition" functionality
97+
await depResolver.saveResolutionIndex()
98+
99+
// Step 3: Get import graph for debugging/logging
100+
const importGraph = depResolver.getImportGraph()
101+
if (importGraph.size > 0) {
102+
console.log(`[SmartCompiler] 📊 Import graph:`)
103+
importGraph.forEach((imports, file) => {
104+
console.log(`[SmartCompiler] ${file}`)
105+
imports.forEach(imp => console.log(`[SmartCompiler] → ${imp}`))
106+
})
107+
}
108+
109+
// Step 4: Convert to compiler input format
110+
const resolvedSources = depResolver.toCompilerInput()
111+
112+
// Step 5: Add the entry file if it's not already in the bundle (e.g., local file)
113+
if (!resolvedSources[target] && sources[target]) {
114+
resolvedSources[target] = sources[target]
115+
}
116+
117+
console.log(`[SmartCompiler] 🔨 Passing ${Object.keys(resolvedSources).length} files to underlying compiler`)
118+
119+
// Log all files that will be compiled
120+
Object.keys(resolvedSources).forEach((filePath, index) => {
121+
console.log(`[SmartCompiler] ${index + 1}. ${filePath}`)
122+
})
123+
124+
// Step 6: Delegate to the underlying compiler with pre-built sources
125+
console.log(`[SmartCompiler] ⚡ Starting compilation with resolved sources...`)
126+
this.compiler.compile(resolvedSources, target)
127+
128+
} catch (error) {
129+
// Re-throw to be caught by the outer catch block
130+
throw error
131+
}
132+
}
133+
134+
// Note: All other methods (set, onCompilerLoaded, loadVersion, visitContracts, etc.)
135+
// are automatically forwarded to the underlying compiler via the Proxy
136+
}

libs/remix-solidity/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { Compiler } from './compiler/compiler'
2+
export { SmartCompiler } from './compiler/smart-compiler'
23
export { ImportResolver } from './compiler/import-resolver'
34
export { DependencyResolver } from './compiler/dependency-resolver'
45
export { IImportResolver } from './compiler/import-resolver-interface'

libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts

Lines changed: 21 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ICompilerApi } from '@remix-project/remix-lib'
2-
import { getValidLanguage, Compiler, ImportResolver, DependencyResolver } from '@remix-project/remix-solidity'
2+
import { getValidLanguage, SmartCompiler } from '@remix-project/remix-solidity'
33
import { EventEmitter } from 'events'
44
import { configFileContent } from '../compilerConfiguration'
55

@@ -29,12 +29,15 @@ export class CompileTabLogic {
2929
this.event = new EventEmitter()
3030

3131

32-
// Create compiler with dependency resolution error callback
33-
this.compiler = new Compiler((url, cb) => {
34-
console.error(`[CompileTabLogic] ❌ File missing - could not resolve: ${url}`)
35-
console.error(`[CompileTabLogic] 🔍 This indicates a bug in our dependency resolution system`)
36-
cb(`File not found: ${url} - Missing from pre-built dependency tree`)
37-
})
32+
// Create smart compiler with automatic dependency resolution
33+
this.compiler = new SmartCompiler(
34+
this.api as any,
35+
(url, cb) => {
36+
console.error(`[CompileTabLogic] ❌ File missing - could not resolve: ${url}`)
37+
console.error(`[CompileTabLogic] 🔍 This indicates a bug in our dependency resolution system`)
38+
cb(new Error(`File not found: ${url} - Missing from pre-built dependency tree`))
39+
}
40+
)
3841

3942
this.evmVersions = ['default', 'prague', 'cancun', 'shanghai', 'paris', 'london', 'berlin', 'istanbul', 'petersburg', 'constantinople', 'byzantium', 'spuriousDragon', 'tangerineWhistle', 'homestead']
4043
}
@@ -142,7 +145,7 @@ export class CompileTabLogic {
142145
if (!target) throw new Error('No target provided for compilation')
143146

144147
try {
145-
console.log(`[CompileTabLogic] 🎯 Starting compilation for: ${target}`)
148+
console.log(`[CompileTabLogic] 🎯 Starting smart compilation for: ${target}`)
146149

147150
// Read the entry file
148151
const content = await this.api.readFile(target)
@@ -152,70 +155,17 @@ export class CompileTabLogic {
152155
await this.setCompilerMappings()
153156
await this.setCompilerConfigContent()
154157

155-
// Build dependency tree BEFORE compilation
156-
console.log(`[CompileTabLogic] 🌳 Building dependency tree...`)
157-
const depResolver = new DependencyResolver(this.api as any, target)
158+
// SmartCompiler automatically handles dependency resolution and compilation
159+
console.log(`[CompileTabLogic] 🧠 Using SmartCompiler with automatic dependency resolution`)
158160

159-
try {
160-
// Build complete source bundle with context-aware resolution
161-
const sourceBundle = await depResolver.buildDependencyTree(target)
162-
163-
console.log(`[CompileTabLogic] ✅ Dependency tree built successfully`)
164-
console.log(`[CompileTabLogic] 📦 Source bundle contains ${sourceBundle.size} files`)
165-
166-
// Save resolution index for "Go to Definition" functionality
167-
await depResolver.saveResolutionIndex()
168-
169-
// Get import graph for debugging/logging
170-
const importGraph = depResolver.getImportGraph()
171-
if (importGraph.size > 0) {
172-
console.log(`[CompileTabLogic] 📊 Import graph:`)
173-
importGraph.forEach((imports, file) => {
174-
console.log(`[CompileTabLogic] ${file}`)
175-
imports.forEach(imp => console.log(`[CompileTabLogic] → ${imp}`))
176-
})
177-
}
178-
179-
// Convert to compiler input format
180-
const sources = depResolver.toCompilerInput()
181-
182-
// Add the entry file if it's not already in the bundle (e.g., local file)
183-
if (!sources[target]) {
184-
sources[target] = { content }
185-
}
186-
187-
console.log(`[CompileTabLogic] 🔨 Passing ${Object.keys(sources).length} files to simple new compiler`)
188-
189-
// Log all files that will be compiled
190-
Object.keys(sources).forEach((filePath, index) => {
191-
console.log(`[CompileTabLogic] ${index + 1}. ${filePath}`)
192-
})
193-
194-
console.log(`[CompileTabLogic] �️ Starting compilation with this.compiler...`, sources)
195-
// Enable compilation with simple stopping callback
196-
setTimeout(() => {
197-
this.compiler.compile(sources, target)
198-
}, 100)
199-
200-
console.log(`[CompileTabLogic] ⏳ Compilation triggered for: ${target}`)
201-
202-
return true
203-
} catch (depError) {
204-
console.error(`[CompileTabLogic] ❌ Failed to build dependency tree:`, depError)
205-
206-
// Fall back to old approach if dependency resolution fails
207-
console.log(`[CompileTabLogic] ⚠️ Falling back to legacy approach - but COMPILATION DISABLED`)
208-
const sources = { [target]: { content } }
209-
210-
console.log(`[CompileTabLogic] ⏸️ Would compile with legacy approach: ${target}`)
211-
212-
// Skip actual compilation for now
213-
// setTimeout(() => {
214-
// this.compiler.compile(sources, target)
215-
// }, 100)
216-
217-
return true
218-
}
161+
const sources = { [target]: { content } }
162+
163+
console.log(`[CompileTabLogic] � Starting smart compilation...`)
164+
this.compiler.compile(sources, target)
165+
166+
console.log(`[CompileTabLogic] ⏳ Smart compilation triggered for: ${target}`)
167+
168+
return true
219169
} catch (error) {
220170
console.error(`[CompileTabLogic] ❌ Compilation failed:`, error)
221171
throw error

0 commit comments

Comments
 (0)