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+ }
0 commit comments