diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index 90176879e3..1db0869649 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -277,26 +277,6 @@ type FsWatcher(delayMs: int) = |> Observable.throttle delayMs |> Observable.map caseInsensitiveSet -// TODO: Check the path is actually normalized? -type File(normalizedFullPath: string) = - let mutable sourceHash = None - member _.NormalizedFullPath = normalizedFullPath - member _.ReadSource() = - match sourceHash with - | Some h -> h, lazy File.readAllTextNonBlocking normalizedFullPath - | _ -> - let source = File.readAllTextNonBlocking normalizedFullPath - let h = hash source - sourceHash <- Some h - h, lazy source - - static member MakeSourceReader (files: File[]) = - let fileDic = - files - |> Seq.map (fun f -> f.NormalizedFullPath, f) |> dict - let sourceReader f = fileDic[f].ReadSource() - files |> Array.map (fun file -> file.NormalizedFullPath), sourceReader - type ProjectCracked(cliArgs: CliArgs, crackerResponse: CrackerResponse, sourceFiles: File array) = member _.CliArgs = cliArgs @@ -311,7 +291,7 @@ type ProjectCracked(cliArgs: CliArgs, crackerResponse: CrackerResponse, sourceFi member _.FableLibDir = crackerResponse.FableLibDir member _.FableModulesDir = crackerResponse.FableModulesDir - member _.MakeCompiler(currentFile, project, ?triggeredByDependency) = + member _.MakeCompiler(checker: InteractiveChecker, currentFile, project, ?triggeredByDependency) = let opts = match triggeredByDependency with | Some t -> { cliArgs.CompilerOptions with TriggeredByDependency = t } @@ -320,7 +300,7 @@ type ProjectCracked(cliArgs: CliArgs, crackerResponse: CrackerResponse, sourceFi let fableLibDir = Path.getRelativePath currentFile crackerResponse.FableLibDir let watchDependencies = if cliArgs.IsWatch then Some(HashSet()) else None - CompilerImpl(currentFile, project, opts, fableLibDir, crackerResponse.OutputType, + CompilerImpl(checker, currentFile, project, opts, fableLibDir, crackerResponse.OutputType, ?outDir=cliArgs.OutDir, ?watchDependencies=watchDependencies) member _.MapSourceFiles(f) = @@ -399,7 +379,7 @@ type FableCompilerState = { ReplyChannel = replyChannel } -and FableCompiler(projCracked: ProjectCracked, fableProj: Project, checker: InteractiveChecker) = +and FableCompiler(checker: InteractiveChecker, projCracked: ProjectCracked, fableProj: Project) = let agent = MailboxProcessor.Start(fun agent -> let startInThreadPool toMsg work = @@ -415,6 +395,7 @@ and FableCompiler(projCracked: ProjectCracked, fableProj: Project, checker: Inte let fableProj = state.FableProj startInThreadPool FableFileCompiled (fun () -> async { let com = projCracked.MakeCompiler( + checker, fileName, fableProj, triggeredByDependency = state.TriggeredByDependency(fileName) @@ -559,7 +540,7 @@ and FableCompiler(projCracked: ProjectCracked, fableProj: Project, checker: Inte ?precompiledInfo = (projCracked.PrecompiledInfo |> Option.map (fun i -> i :> _)), getPlugin = loadType projCracked.CliArgs ) - return FableCompiler(projCracked, fableProj, checker) + return FableCompiler(checker, projCracked, fableProj) } member _.CompileToFile(outFile: string) = diff --git a/src/Fable.Compiler.Service/Library.fs b/src/Fable.Compiler.Service/Library.fs index 9df9ea349b..3cc23bdb51 100644 --- a/src/Fable.Compiler.Service/Library.fs +++ b/src/Fable.Compiler.Service/Library.fs @@ -97,16 +97,13 @@ type BabelWriter // let sourcePath = defaultArg file sourcePath |> Path.getRelativeFileOrDirPath false targetPath false // mapGenerator.Force().AddMapping(generated, original, source=sourcePath, ?name=displayName) -// TODO: real bad code, refactor -let mutable checker = Unchecked.defaultof - let mkCompilerForFile + (checker: InteractiveChecker) (cliArgs: CliArgs) (crackerResponse: CrackerResponse) (currentFile: string) : Async = async { - checker <- InteractiveChecker.Create(crackerResponse.ProjectOptions) let! assemblies = checker.GetImportedAssemblies() let sourceReader fileName = @@ -144,18 +141,15 @@ let mkCompilerForFile let fableLibDir = Path.getRelativePath currentFile crackerResponse.FableLibDir - let watchDependencies = None - // if cliArgs.IsWatch then Some(HashSet()) else None - return CompilerImpl( + checker, currentFile, fableProj, opts, fableLibDir, crackerResponse.OutputType, - ?outDir = cliArgs.OutDir, - ?watchDependencies = watchDependencies + ?outDir = cliArgs.OutDir ) } @@ -187,7 +181,7 @@ let compileFile (com: Compiler) (pathResolver: PathResolver) (outPath: string) = 1, lazy source - let! dependentFiles = checker.GetDependentFiles(com.CurrentFile, com.SourceFiles, sourceReader) + let! dependentFiles = com.GetDependentFiles() return output, dependentFiles } diff --git a/src/Fable.Compiler.Service/Library.fsi b/src/Fable.Compiler.Service/Library.fsi index b14ffb154a..fafc2768bd 100644 --- a/src/Fable.Compiler.Service/Library.fsi +++ b/src/Fable.Compiler.Service/Library.fsi @@ -1,8 +1,19 @@ module Fable.Compiler.Service.Compiler +open FSharp.Compiler.SourceCodeServices open Fable open Fable.Compiler.Service.Util open Fable.Compiler.Service.ProjectCracker -val mkCompilerForFile: cliArgs: CliArgs -> crackerResponse: CrackerResponse -> currentFile: string -> Async -val compileFile: com: Compiler -> pathResolver: PathResolver -> outPath: string -> Async +val mkCompilerForFile: + checker: InteractiveChecker -> + cliArgs: CliArgs -> + crackerResponse: CrackerResponse -> + currentFile: string -> + Async + +val compileFile: + com: Compiler -> + pathResolver: PathResolver -> + outPath: string -> + Async diff --git a/src/Fable.Transforms/Dart/Fable2Dart.fs b/src/Fable.Transforms/Dart/Fable2Dart.fs index b398fb46f3..52f6e86606 100644 --- a/src/Fable.Transforms/Dart/Fable2Dart.fs +++ b/src/Fable.Transforms/Dart/Fable2Dart.fs @@ -2284,6 +2284,7 @@ module Compiler = member _.AddWatchDependency(fileName) = com.AddWatchDependency(fileName) member _.AddLog(msg, severity, ?range, ?fileName:string, ?tag: string) = com.AddLog(msg, severity, ?range=range, ?fileName=fileName, ?tag=tag) + member _.GetDependentFiles () = com.GetDependentFiles() let makeCompiler com = DartCompiler(com) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index b6a2ca3aab..6184d90940 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -1934,6 +1934,7 @@ type FableCompiler(com: Compiler) = member _.AddWatchDependency(fileName) = com.AddWatchDependency(fileName) member _.AddLog(msg, severity, ?range, ?fileName:string, ?tag: string) = com.AddLog(msg, severity, ?range=range, ?fileName=fileName, ?tag=tag) + member _.GetDependentFiles () = com.GetDependentFiles() let rec attachClassMembers (com: FableCompiler) = function diff --git a/src/Fable.Transforms/Fable.Transforms.fsproj b/src/Fable.Transforms/Fable.Transforms.fsproj index 173effc88e..e0067eb681 100644 --- a/src/Fable.Transforms/Fable.Transforms.fsproj +++ b/src/Fable.Transforms/Fable.Transforms.fsproj @@ -6,6 +6,7 @@ $(OtherFlags) --nowarn:3536 + diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 08b0712785..209ad94ab0 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -3164,6 +3164,7 @@ module Compiler = member _.AddWatchDependency(fileName) = com.AddWatchDependency(fileName) member _.AddLog(msg, severity, ?range, ?fileName:string, ?tag: string) = com.AddLog(msg, severity, ?range=range, ?fileName=fileName, ?tag=tag) + member _.GetDependentFiles () = com.GetDependentFiles() let makeCompiler com = BabelCompiler(com) diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 371f68845c..36cee8eb49 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -751,7 +751,7 @@ let rec transformDeclaration transformations (com: Compiler) file decl = AttachedMembers = attachedMembers } |> ClassDeclaration -let transformFile (com: Compiler) (file: File) = +let transformFile (com: Compiler) (file: Fable.AST.Fable.File) = let transformations = getTransformations com let newDecls = List.map (transformDeclaration transformations com file) file.Declarations - File(newDecls, usedRootNames=file.UsedNamesInRootScope) + Fable.AST.Fable.File(newDecls, usedRootNames=file.UsedNamesInRootScope) diff --git a/src/Fable.Transforms/File.fs b/src/Fable.Transforms/File.fs new file mode 100644 index 0000000000..7063de9b2b --- /dev/null +++ b/src/Fable.Transforms/File.fs @@ -0,0 +1,41 @@ +namespace Fable.Transforms + +open System.IO + +// TODO: Check the path is actually normalized? +type File(normalizedFullPath: string) = + let mutable sourceHash = None + + let readAllTextNonBlocking (path: string) = + if File.Exists(path) then + use fileStream = + new FileStream( + path, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite + ) + + use textReader = new StreamReader(fileStream) + textReader.ReadToEnd() + else + // Log.always("File does not exist: " + path) + "" + + member _.NormalizedFullPath = normalizedFullPath + + member _.ReadSource() = + match sourceHash with + | Some h -> h, lazy readAllTextNonBlocking normalizedFullPath + | _ -> + let source = readAllTextNonBlocking normalizedFullPath + let h = hash source + sourceHash <- Some h + h, lazy source + + static member MakeSourceReader(files: File array) = + let fileDic = + files |> Seq.map (fun f -> f.NormalizedFullPath, f) |> dict + + let sourceReader f = fileDic[f].ReadSource() + files |> Array.map (fun file -> file.NormalizedFullPath), sourceReader diff --git a/src/Fable.Transforms/Global/Compiler.fs b/src/Fable.Transforms/Global/Compiler.fs index 91965bd45f..091e0c9e89 100644 --- a/src/Fable.Transforms/Global/Compiler.fs +++ b/src/Fable.Transforms/Global/Compiler.fs @@ -71,6 +71,10 @@ type Compiler = abstract AddLog: msg:string * severity: Severity * ?range: SourceLocation * ?fileName:string * ?tag: string -> unit + /// Invokes InteractiveChecker.GetDependentFiles + /// This will find dependent file via the untyped tree graph. + abstract GetDependentFiles: unit -> Async + type InlineExprLazy(f: Compiler -> InlineExpr) = let mutable value: InlineExpr voption = ValueNone member this.Calculate(com: Compiler) = diff --git a/src/Fable.Transforms/Php/Fable2Php.fs b/src/Fable.Transforms/Php/Fable2Php.fs index f031c08020..315976b65a 100644 --- a/src/Fable.Transforms/Php/Fable2Php.fs +++ b/src/Fable.Transforms/Php/Fable2Php.fs @@ -1652,6 +1652,8 @@ type PhpCompiler(com: Fable.Compiler) = member this.FindLableLevel(label) = List.findIndex(function Some v when v = label -> true | _ -> false) breakable + member _.GetDependentFiles () = com.GetDependentFiles() + module Compiler = let transformFile com (file: Fable.File) = diff --git a/src/Fable.Transforms/Python/Fable2Python.fs b/src/Fable.Transforms/Python/Fable2Python.fs index 0bdf708696..28f2cde882 100644 --- a/src/Fable.Transforms/Python/Fable2Python.fs +++ b/src/Fable.Transforms/Python/Fable2Python.fs @@ -4182,6 +4182,7 @@ module Compiler = member _.AddLog(msg, severity, ?range, ?fileName: string, ?tag: string) = com.AddLog(msg, severity, ?range = range, ?fileName = fileName, ?tag = tag) + member _.GetDependentFiles () = com.GetDependentFiles() let makeCompiler com = PythonCompiler(com) diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index acf32ed08a..7d3830bbcc 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -4315,6 +4315,8 @@ module Compiler = member _.AddLog(msg, severity, ?range, ?fileName:string, ?tag: string) = com.AddLog(msg, severity, ?range=range, ?fileName=fileName, ?tag=tag) + member _.GetDependentFiles () = com.GetDependentFiles() + let makeCompiler com = RustCompiler(com) let transformFile (com: Fable.Compiler) (file: Fable.File) = diff --git a/src/Fable.Transforms/State.fs b/src/Fable.Transforms/State.fs index 6bf1d64b6f..a5606d0bda 100644 --- a/src/Fable.Transforms/State.fs +++ b/src/Fable.Transforms/State.fs @@ -1,5 +1,6 @@ module Fable.Transforms.State +open FSharp.Compiler.SourceCodeServices open Fable open Fable.AST open System.Collections.Concurrent @@ -237,9 +238,20 @@ type Log = /// Type with utilities for compiling F# files to JS. /// Not thread-safe, an instance must be created per file -type CompilerImpl(currentFile, project: Project, options, fableLibDir: string, ?outType: OutputType, ?outDir: string, - ?watchDependencies: HashSet, ?logs: ResizeArray, ?isPrecompilingInlineFunction: bool) = - +type CompilerImpl + ( + checker: InteractiveChecker, + currentFile, + project: Project, + options, + fableLibDir: string, + ?outType: OutputType, + ?outDir: string, + ?watchDependencies: HashSet, + ?logs: ResizeArray, + ?isPrecompilingInlineFunction: bool + ) + = let mutable counter = -1 let outType = defaultArg outType OutputType.Exe let logs = Option.defaultWith ResizeArray logs @@ -272,7 +284,7 @@ type CompilerImpl(currentFile, project: Project, options, fableLibDir: string, ? Path.Combine(Path.GetDirectoryName(currentFile), fableLibraryDir) else fableLibraryDir |> Path.getRelativeFileOrDirPath false file true - CompilerImpl(file, project, options, fableLibraryDir, outType, ?outDir=outDir, + CompilerImpl(checker, file, project, options, fableLibraryDir, outType, ?outDir=outDir, ?watchDependencies=watchDependencies, logs=logs, isPrecompilingInlineFunction=true) member _.GetImplementationFile(fileName) = @@ -324,3 +336,7 @@ type CompilerImpl(currentFile, project: Project, options, fableLibDir: string, ? member _.AddLog(msg, severity, ?range, ?fileName:string, ?tag: string) = Log.Make(severity, msg, ?range=range, ?fileName=fileName, ?tag=tag) |> logs.Add + + member _.GetDependentFiles () = + let sourceReader = File.MakeSourceReader (Array.map File project.SourceFiles) |> snd + checker.GetDependentFiles (currentFile, project.SourceFiles, sourceReader) diff --git a/tests/FCSTest/Program.fs b/tests/FCSTest/Program.fs index 05576eddfe..0e5600aee7 100644 --- a/tests/FCSTest/Program.fs +++ b/tests/FCSTest/Program.fs @@ -2,6 +2,7 @@ open System.Diagnostics open System.IO open System.Text.Json +open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.CodeAnalysis open Fable.Compiler.Service.Compiler open Fable @@ -170,8 +171,11 @@ let cliArgs: CliArgs = { } } +let checker = InteractiveChecker.Create(crackerResponse.ProjectOptions) + let compilerForFile = mkCompilerForFile + checker cliArgs crackerResponse (Path.normalizeFullPath @"C:\Users\nojaf\Projects\MyFableApp\Lib.fs")