diff --git a/.vscode/settings.json b/.vscode/settings.json
index 58410f3558..e6305e3adf 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -27,5 +27,6 @@
"build/fable-library-rust/Cargo.toml",
"build/tests/Rust/Cargo.toml"
],
- "editor.inlayHints.enabled": "offUnlessPressed"
+ "editor.inlayHints.enabled": "offUnlessPressed",
+ "dotnet.defaultSolution": "disable"
}
diff --git a/Build.fsproj b/Build.fsproj
new file mode 100644
index 0000000000..299c787170
--- /dev/null
+++ b/Build.fsproj
@@ -0,0 +1,24 @@
+
+
+ Exe
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Fable.sln b/Fable.sln
index 550a366e9b..bbe3ccedf3 100644
--- a/Fable.sln
+++ b/Fable.sln
@@ -60,6 +60,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fable.Tests.TypeScript", "t
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fable.Tests.Spaces", "tests\Js\Project With Spaces\Fable.Tests.Spaces.fsproj", "{C90E23AF-4B5B-44A7-ADCC-3BF89547395B}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Build", "Build.fsproj", "{EEBF506A-F0BC-4660-9387-65ADA5F36EAA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -154,6 +156,10 @@ Global
{C90E23AF-4B5B-44A7-ADCC-3BF89547395B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C90E23AF-4B5B-44A7-ADCC-3BF89547395B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C90E23AF-4B5B-44A7-ADCC-3BF89547395B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EEBF506A-F0BC-4660-9387-65ADA5F36EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EEBF506A-F0BC-4660-9387-65ADA5F36EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EEBF506A-F0BC-4660-9387-65ADA5F36EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EEBF506A-F0BC-4660-9387-65ADA5F36EAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build copy.fsx b/build copy.fsx
new file mode 100644
index 0000000000..349eb4f238
--- /dev/null
+++ b/build copy.fsx
@@ -0,0 +1,893 @@
+#load "src/Fable.PublishUtils/PublishUtils.fs"
+
+open System
+open System.Text.RegularExpressions
+open PublishUtils
+
+// ncave FCS fork
+let FCS_REPO = "https://github.com/ncave/fsharp"
+let FCS_REPO_LOCAL = "../fsharp_fable"
+let FCS_REPO_FABLE_BRANCH = "fable"
+let FCS_REPO_SERVICE_SLIM_BRANCH = "service_slim"
+
+let BUILD_ARGS =
+ fsi.CommandLineArgs
+ |> Array.skip 1
+ |> List.ofArray
+
+let BUILD_ARGS_LOWER =
+ BUILD_ARGS
+ |> List.map (fun x -> x.ToLower())
+
+module Util =
+ let cleanDirs dirs =
+ for dir in dirs do
+ printfn $"Clean {dir}"
+ removeDirRecursive dir
+
+ // TODO: move to PublishUtils.fs ?
+ let copyFiles sourceDir searchPattern destDir =
+ printfn $"Copy {sourceDir > searchPattern} to {destDir}"
+ for source in IO.Directory.GetFiles(sourceDir, searchPattern) do
+ let fileName = IO.Path.GetFileName(source)
+ let target = destDir > fileName
+ IO.File.Copy(source, target, true)
+
+ let resolveDir dir =
+ __SOURCE_DIRECTORY__ > dir
+
+ let updateVersionsInFableTransforms compilerVersion (libraryVersions: (string * string) list) =
+ let mutable updated = Set.empty
+
+ let replaceVersion (lang: string option) version fileContent =
+ let prefix =
+ match lang with
+ | None -> ""
+ | Some lang -> $"{lang.ToUpperInvariant()}_LIBRARY_"
+
+ Regex.Replace(
+ fileContent,
+ $@"^(\s*)let \[] {prefix}VERSION = ""(.*?)""",
+ (fun (m: Match) ->
+ match lang with
+ | Some lang when m.Groups[2].Value <> version ->
+ updated <- Set.add lang updated
+ | _ -> ()
+ m.Groups[1].Value + $"let [] {prefix}VERSION = \"{version}\""
+ ),
+ RegexOptions.Multiline)
+
+ let filePath = "src/Fable.Transforms/Global/Compiler.fs"
+ readFile filePath
+ |> replaceVersion None compilerVersion
+ |> List.foldBack (fun (lang, version) fileContent ->
+ replaceVersion (Some lang) version fileContent) libraryVersions
+ |> writeFile filePath
+
+ updated
+
+ let updatePkgVersionInFsproj projFile version =
+ readFile projFile
+ |> replaceRegex Publish.NUGET_PACKAGE_VERSION (fun m ->
+ m.Groups[1].Value + version + m.Groups[3].Value)
+ |> writeFile projFile
+
+ let runTSLint projectDir =
+ run ("npm run tslint -- --project " + projectDir)
+
+ let runTypeScript projectDir =
+ run ("npm run tsc -- --project " + projectDir)
+
+ let runTypeScriptWithArgs projectDir args =
+ run ("npm run tsc -- --project " + projectDir + " " + String.concat " " args)
+
+ let runFableWithArgs projectDir args =
+ run ("dotnet run -c Release --project src/Fable.Cli -- " + projectDir + " " + String.concat " " args)
+
+ let watchFableWithArgs projectDir args =
+ run ("dotnet watch --project src/Fable.Cli run -- " + projectDir + " --cwd ../.. " + String.concat " " args)
+
+ let runFableWithArgsInDirAs release projectDir args =
+ let cliDir = resolveDir "src/Fable.Cli"
+ let cliArgs = args |> String.concat " "
+ let cliCmd = $"""dotnet run {if release then "-c Release" else ""} --project {cliDir} -- {cliArgs}"""
+ runInDir (resolveDir projectDir) cliCmd
+
+ let runFableWithArgsInDir projectDir args =
+ runFableWithArgsInDirAs true projectDir args
+
+ let runFableWithArgsAsync projectDir args =
+ runAsync ("dotnet run -c Release --project src/Fable.Cli -- " + projectDir + " " + String.concat " " args)
+
+ let runNpx command args =
+ run ("npx " + command + " " + (String.concat " " args))
+
+ let runNpmScript script args =
+ run ("npm run " + script + " -- " + (String.concat " " args))
+
+ let runNpmScriptAsync script args =
+ runAsync ("npm run " + script + " -- " + (String.concat " " args))
+
+ let runFable projectDir =
+ runFableWithArgs projectDir []
+
+ let runMocha testDir =
+ runNpmScript "mocha" [$"{testDir} --reporter dot -t 10000"]
+
+open Util
+
+module Unused =
+ let downloadAndExtractTo (url: string) (targetDir: string) =
+ $"npx download --extract --out %s{targetDir} \"%s{url}\"" |> run
+
+ let coverage() =
+ // report converter
+ // https://github.com/danielpalme/ReportGenerator
+ // dotnet tool install dotnet-reportgenerator-globaltool --tool-path tools
+ if not (pathExists "./bin/tools/reportgenerator") && not (pathExists "./bin/tools/reportgenerator.exe") then
+ runInDir "." "dotnet tool install dotnet-reportgenerator-globaltool --tool-path bin/tools"
+ let reportGen =
+ if pathExists "./bin/tools/reportgenerator" then "bin/tools/reportgenerator"
+ else "bin\\tools\\reportgenerator.exe"
+
+ // if not (pathExists "build/fable-library") then
+ // buildLibrary()
+
+ cleanDirs ["build/tests"]
+ runFable "tests"
+
+ // JS
+ run "npx nyc mocha build/tests --require source-map-support/register --reporter dot -t 10000"
+ runInDir "." (reportGen + " \"-reports:build/coverage/nyc/lcov.info\" -reporttypes:Html \"-targetdir:build/coverage/nyc/html\" ")
+
+ // .NET
+ //runInDir "tests/Main" "dotnet build /t:Collect_Coverage"
+ cleanDirs ["build/coverage/netcoreapp2.0/out"]
+ runInDir "." (reportGen + " \"-reports:build/coverage/netcoreapp2.0/coverage.xml\" -reporttypes:Html \"-targetdir:build/coverage/netcoreapp2.0/html\" ")
+
+// TARGETS ---------------------------
+
+// let buildLibraryJsWithOptions (opts: {| watch: bool |}) =
+// let baseDir = __SOURCE_DIRECTORY__
+
+// let projectDir = baseDir > "src/fable-library"
+// let buildDir = baseDir > "build/fable-library"
+// let fableOpts = [
+// "--outDir " + buildDir
+// "--fableLib " + buildDir
+// "--exclude Fable.Core"
+// "--define FX_NO_BIGINT"
+// "--define FABLE_LIBRARY"
+// if opts.watch then "--watch"
+// ]
+
+// cleanDirs [buildDir]
+// runInDir baseDir "npm install"
+// makeDirRecursive buildDir
+
+// copyFile (projectDir > "package.json") buildDir
+
+// if opts.watch then
+// Async.Parallel [
+// runNpmScriptAsync "tsc" [
+// "--project " + projectDir
+// "--watch"
+// ]
+// runFableWithArgsAsync projectDir fableOpts
+// ] |> runAsyncWorkflow
+// else
+// runTSLint projectDir
+// runTypeScript projectDir
+// runFableWithArgs projectDir fableOpts
+// removeDirRecursive (buildDir > ".fable")
+
+// let buildLibraryJs() = buildLibraryJsWithOptions {| watch = false |}
+
+// let buildLibraryJsIfNotExists() =
+// if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library")) then
+// buildLibraryJs()
+
+let buildLibraryTs() =
+ let baseDir = __SOURCE_DIRECTORY__
+ let sourceDir = "./src/fable-library"
+ let buildDirTs = "./build/fable-library-ts"
+ let buildDirJs = "./build/fable-library"
+
+ cleanDirs [buildDirTs; buildDirJs]
+ runInDir baseDir "npm install"
+
+ runFableWithArgs sourceDir [
+ "--outDir " + buildDirTs
+ "--fableLib " + buildDirTs
+ "--lang TypeScript"
+ "--typedArrays false"
+ "--exclude Fable.Core"
+ "--define FX_NO_BIGINT"
+ "--define FABLE_LIBRARY"
+ ]
+
+ copyFiles sourceDir "*.ts" buildDirTs
+ copyFiles (sourceDir > "ts") "*.json" buildDirTs
+ copyDirRecursive (sourceDir > "lib") (buildDirTs > "lib")
+ copyFile (sourceDir > "package.json") buildDirTs
+
+ // runTSLint buildDirTs
+ runTypeScriptWithArgs buildDirTs ["--outDir " + buildDirJs]
+ copyFile (buildDirTs > "lib/big.d.ts") (buildDirJs > "lib/big.d.ts")
+ copyFile (buildDirTs > "package.json") buildDirJs
+
+ copyFile (sourceDir > "README.md") buildDirJs
+
+let buildLibraryTsIfNotExists() =
+ if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-ts")) then
+ buildLibraryTs()
+
+let buildLibraryPy() =
+ let libraryDir = "./src/fable-library-py"
+ let projectDir = libraryDir > "fable_library"
+ let buildDirPy = "./build/fable-library-py"
+
+ cleanDirs [buildDirPy]
+
+ runFableWithArgs projectDir [
+ "--outDir " + buildDirPy > "fable_library"
+ "--fableLib " + buildDirPy > "fable_library"
+ "--lang Python"
+ "--exclude Fable.Core"
+ "--define FABLE_LIBRARY"
+ ]
+
+ // Copy python related files from projectDir to buildDir
+ copyFiles libraryDir "*" buildDirPy
+ copyFiles projectDir "*.py" (buildDirPy > "fable_library")
+
+ // Fix issues with Fable .fsproj not supporting links
+ copyDirNonRecursive (buildDirPy > "fable_library/fable-library") (buildDirPy > "fable_library")
+ removeDirRecursive (buildDirPy > "fable_library/fable-library")
+
+let buildLibraryPyIfNotExists() =
+ let baseDir = __SOURCE_DIRECTORY__
+ if not (pathExists (baseDir > "build/fable-library-py")) then
+ buildLibraryPy()
+
+let buildLibraryRust() =
+ let libraryDir = "src/fable-library-rust"
+ let sourceDir = libraryDir > "src"
+ let buildDir = "build/fable-library-rust"
+ let outDir = buildDir > "src"
+ let fableLib = "."
+
+ cleanDirs [buildDir]
+
+ runFableWithArgsInDir sourceDir [
+ "--outDir " + resolveDir outDir
+ "--fableLib " + fableLib
+ "--lang Rust"
+ "--exclude Fable.Core"
+ "--define FABLE_LIBRARY"
+ "--noCache"
+ ]
+
+ copyFiles libraryDir "*.toml" buildDir
+ copyFiles sourceDir "*.rs" outDir
+ copyDirRecursive (libraryDir > "vendored") (buildDir > "vendored")
+
+ runInDir buildDir "cargo fmt"
+ runInDir buildDir "cargo fix --allow-no-vcs"
+ runInDir buildDir "cargo build"
+
+let buildLibraryRustIfNotExists() =
+ if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-rust")) then
+ buildLibraryRust()
+
+let buildLibraryDart(clean: bool) =
+ let sourceDir = resolveDir "src/fable-library-dart"
+ let buildDir = resolveDir "build/fable-library-dart"
+
+ if clean then
+ cleanDirs [buildDir]
+ makeDirRecursive buildDir
+ copyFiles sourceDir "pubspec.*" buildDir
+ copyFiles sourceDir "analysis_options.yaml" buildDir
+
+ copyFiles sourceDir "*.dart" buildDir
+
+ runFableWithArgsInDir sourceDir [
+ "--outDir " + buildDir
+ "--fableLib " + buildDir
+ "--lang Dart"
+ "--exclude Fable.Core"
+ "--define FABLE_LIBRARY"
+ if clean then "--noCache"
+ ]
+
+let buildLibraryDartIfNotExists() =
+ if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-dart")) then
+ buildLibraryDart(true)
+
+// Like testStandalone() but doesn't create bundles/packages for fable-standalone & friends
+// Mainly intended for CI
+let testStandaloneFast() =
+ runFableWithArgs "src/fable-standalone/src" [
+ "--noCache"
+ ]
+
+ runFableWithArgs "src/fable-compiler-js/src" [
+ "--exclude Fable.Core"
+ "--define LOCAL_TEST"
+ ]
+
+ let fableJs = "./src/fable-compiler-js/src/app.fs.js"
+ let testProj = "tests/Js/Main/Fable.Tests.fsproj"
+ let buildDir = "build/tests/Standalone"
+ run $"node {fableJs} {testProj} {buildDir}"
+ runMocha buildDir
+
+let buildWorker (opts: {| minify: bool; watch: bool |}) =
+ printfn "Building worker%s..." (if opts.minify then "" else " (no minification)")
+
+ let projectDir = "src/fable-standalone/src"
+ let buildDir = "build/fable-standalone"
+ let distDir = "src/fable-standalone/dist"
+
+ runFableWithArgs (projectDir + "/Worker") [
+ "--outDir " + buildDir + "/worker"
+ ]
+
+ let rollupTarget =
+ match opts.minify with
+ | true -> buildDir > "worker.js"
+ | false -> distDir > "worker.min.js"
+
+ // make standalone worker dist
+ runNpmScript "rollup" [$"""{buildDir}/worker/Worker.js -o {rollupTarget} --format iife"""]
+
+ if opts.minify then
+ // runNpx "webpack" [sprintf "--entry ./%s/worker.js --output ./%s/worker.min.js --config ./%s/../worker.config.js" buildDir distDir projectDir]
+ runNpmScript "terser" [$"{buildDir}/worker.js -o {distDir}/worker.min.js --mangle --compress"]
+
+ // Put fable-library files next to bundle
+ printfn "Copying fable-library..."
+ buildLibraryTsIfNotExists()
+ let libraryDir = "build/fable-library"
+ let libraryTarget = distDir > "fable-library"
+ copyDirRecursive libraryDir libraryTarget
+
+let buildStandalone (opts: {| minify: bool; watch: bool |}) =
+ buildLibraryTs()
+
+ printfn "Building standalone%s..." (if opts.minify then "" else " (no minification)")
+
+ let projectDir = "src/fable-standalone/src"
+ let buildDir = "build/fable-standalone"
+ let distDir = "src/fable-standalone/dist"
+
+ let rollupTarget =
+ match opts.watch, opts.minify with
+ | true, _ ->
+ match BUILD_ARGS with
+ | _::rollupTarget::_ -> rollupTarget
+ | _ -> failwith "Pass the bundle output, e.g.: npm run build watch-standalone ../repl3/public/js/repl/bundle.min.js"
+ | false, true -> buildDir > "bundle.js"
+ | false, false -> distDir > "bundle.min.js"
+
+ let rollupArgs = [
+ buildDir > "bundle/Main.js"
+ "-o " + rollupTarget
+ "--format umd"
+ "--name __FABLE_STANDALONE__"
+ ]
+
+ // cleanup
+ if not opts.watch then
+ cleanDirs [buildDir; distDir]
+ makeDirRecursive distDir
+
+ // build standalone bundle
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir > "bundle"
+ if opts.watch then
+ "--watch"
+ "--run rollup"
+ yield! rollupArgs
+ "--watch"
+ ]
+
+ // make standalone bundle dist
+ runNpmScript "rollup" rollupArgs
+ if opts.minify then
+ runNpmScript "terser" [
+ buildDir > "bundle.js"
+ "-o " + distDir > "bundle.min.js"
+ "--mangle"
+ "--compress"
+ ]
+
+ // build standalone worker
+ buildWorker opts
+
+ // print bundle size
+ fileSizeInBytes (distDir > "bundle.min.js") / 1000. |> printfn "Bundle size: %fKB"
+ fileSizeInBytes (distDir > "worker.min.js") / 1000. |> printfn "Worker size: %fKB"
+
+let buildCompilerJs(minify: bool) =
+ let projectDir = "src/fable-compiler-js/src"
+ let buildDir = "build/fable-compiler-js"
+ let distDir = "src/fable-compiler-js/dist"
+
+ if not (pathExists "build/fable-standalone") then
+ buildStandalone {|minify=minify; watch=false|}
+
+ cleanDirs [buildDir; distDir]
+ makeDirRecursive distDir
+
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ ]
+
+ let rollupTarget = if minify then distDir > "app.js" else distDir > "app.min.js"
+ run $"npx rollup {buildDir}/app.js -o {rollupTarget} --format umd --name Fable"
+ if minify then
+ run $"npx terser {distDir}/app.js -o {distDir}/app.min.js --mangle --compress"
+
+ // Copy fable-library
+ copyDirRecursive ("build/fable-library") (distDir > "fable-library")
+ // Copy fable-metadata
+ copyDirRecursive ("src/fable-metadata/lib") (distDir > "fable-metadata")
+
+let testStandalone(minify) =
+ let fableDir = "src/fable-compiler-js"
+ let buildDir = "build/tests/Standalone"
+
+ if not (pathExists "build/fable-compiler-js") then
+ buildCompilerJs(minify)
+
+ cleanDirs [buildDir]
+
+ // Link fable-compiler-js to local packages
+ runInDir fableDir "npm link ../fable-metadata"
+ runInDir fableDir "npm link ../fable-standalone"
+
+ // Test fable-compiler-js locally
+ run $"node {fableDir} tests/Js/Main/Fable.Tests.fsproj {buildDir}"
+ runMocha buildDir
+
+ // // Another local fable-compiler-js test
+ // runInDir (fableDir > "test") "node .. test_script.fsx"
+ // runInDir (fableDir > "test") "node test_script.fsx.js"
+
+ // Unlink local packages after test
+ runInDir fableDir "npm unlink ../fable-metadata && cd ../fable-metadata && npm unlink"
+ runInDir fableDir "npm unlink ../fable-standalone && cd ../fable-standalone && npm unlink"
+
+let testReact() =
+ runFableWithArgs "tests/React" ["--noCache"]
+ runInDir "tests/React" "npm i && npm test"
+
+let compileAndRunTestsWithMocha clean projectName =
+ let projectDir = "tests/Js/" + projectName
+ let buildDir = "build/tests/Js/" + projectName
+
+ if clean then
+ cleanDirs [buildDir]
+
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ ]
+
+ runMocha buildDir
+
+let testProjectConfigs() =
+ [ "tests/Integration/ProjectConfigs/DebugWithExtraDefines", "Debug"
+ "tests/Integration/ProjectConfigs/CustomConfiguration", "Test"
+ "tests/Integration/ProjectConfigs/ReleaseNoExtraDefines", String.Empty
+ "tests/Integration/ProjectConfigs/ConsoleApp", String.Empty
+ ]
+ |> List.iter (fun (projectDir, configuration) ->
+ let buildDir = "build/"+ projectDir
+
+ cleanDirs [ buildDir ]
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ if not(String.IsNullOrEmpty configuration) then
+ "--configuration " + configuration
+ ]
+
+ runMocha buildDir
+ )
+
+let testIntegration() =
+ runInDir "tests/Integration/Integration" "dotnet run -c Release"
+
+ buildLibraryTsIfNotExists()
+ runInDir "tests/Integration/Compiler" "dotnet run -c Release"
+ testProjectConfigs()
+
+let testJs() =
+ buildLibraryTsIfNotExists()
+
+ compileAndRunTestsWithMocha true "Main"
+
+ runInDir "tests/Js/Main" "dotnet run -c Release"
+
+ // Adaptive tests must go in a different project to avoid conflicts with Queue shim, see #2559
+ compileAndRunTestsWithMocha true "Adaptive"
+
+ testReact()
+
+ if envVarOrNone "CI" |> Option.isSome then
+ testStandaloneFast()
+
+let testTypeScript isWatch =
+ buildLibraryTsIfNotExists()
+
+ let projectDir = "tests/TypeScript"
+ let buildDir = "build/tests/TypeScript"
+ let buildDir2 = "build/tests/TypeScriptCompiled"
+
+ cleanDirs [buildDir; buildDir2]
+
+ copyFile (projectDir > "tsconfig.json") (buildDir > "tsconfig.json")
+
+ runFableWithArgsInDirAs (not isWatch) "." [
+ projectDir
+ "--lang ts"
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ if isWatch then
+ "--watch"
+ $"--runWatch npm run test-ts"
+ ]
+
+ runNpmScript "test-ts" []
+
+let testPython() =
+ buildLibraryPyIfNotExists() // NOTE: fable-library-py needs to be built separately.
+
+ let projectDir = "tests/Python"
+ let buildDir = "build/tests/Python"
+
+ cleanDirs [buildDir]
+ runInDir projectDir "dotnet test -c Release"
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ "--lang Python"
+ ]
+
+ runInDir buildDir "poetry run pytest -x"
+ // Testing in Windows
+ // runInDir buildDir "python -m pytest -x"
+
+type RustTestMode =
+ | NoStd
+ | Default
+ | Threaded
+
+let testRust testMode =
+ // buildLibraryRustIfNotExists()
+ buildLibraryRust()
+
+ let testAstDir = "src/Fable.Transforms/Rust/AST/Tests"
+ let projectDir = "tests/Rust"
+ let buildDir = "build/tests/Rust"
+
+ // limited cleanup to reduce IO churn, speed up rebuilds,
+ // and save the ssd (target folder can get huge)
+ cleanDirs [buildDir > "src"]
+ cleanDirs [buildDir > "tests"]
+ cleanDirs [buildDir > ".fable"]
+
+ // copy rust only tests files (these must be present when running dotnet test as import expr tests for file presence)
+ makeDirRecursive (buildDir > "tests" > "src")
+ copyFiles (projectDir > "tests/src") "*.rs" (buildDir > "tests/src")
+
+ // run .NET tests
+ runInDir testAstDir "dotnet test -c Release"
+ runInDir projectDir "dotnet test -c Release"
+
+ // build Fable Rust tests
+ runFableWithArgs projectDir [
+ "--outDir " + buildDir
+ "--exclude Fable.Core"
+ "--lang Rust"
+ "--fableLib fable-library-rust"
+ "--noCache"
+ if testMode = NoStd then "--define NO_STD_NO_EXCEPTIONS"
+ ]
+
+ // copy project file
+ copyFile (projectDir > "Cargo.toml") buildDir
+
+ // rustfmt all tests
+ runInDir buildDir "cargo fmt"
+ // runInDir buildDir "cargo fix --allow-no-vcs"
+ runInDir buildDir "cargo build"
+
+ // run Fable Rust tests
+ match testMode with
+ | Default ->
+ runInDir buildDir "cargo test"
+ | NoStd ->
+ runInDir buildDir "cargo test --features no_std"
+ | Threaded ->
+ runInDir buildDir "cargo test --features threaded"
+
+let testDart isWatch =
+ if not (pathExists "build/fable-library-dart") then
+ buildLibraryDart(true)
+
+ let buildDir = resolveDir "build/tests/Dart"
+ let sourceDir = resolveDir "tests/Dart"
+
+ cleanDirs [buildDir]
+ makeDirRecursive buildDir
+ copyFiles sourceDir "pubspec.*" buildDir
+ copyFiles sourceDir "analysis_options.yaml" buildDir
+ copyFiles sourceDir "*.dart" buildDir
+
+ runFableWithArgsInDirAs (not isWatch) sourceDir [
+ "src"
+ "--outDir " + (buildDir > "src")
+ "--exclude Fable.Core"
+ "--lang Dart"
+ "--noCache"
+ if isWatch then
+ "--watch"
+ $"--runWatch dart test {buildDir}/main.dart"
+ ]
+ runInDir buildDir "dart test main.dart"
+
+let buildLocalPackageWith pkgDir pkgCommand fsproj action =
+ let version = Publish.loadReleaseVersion "src/Fable.Cli" + "-local-build-" + DateTime.Now.ToString("yyyyMMdd-HHmm")
+ action version
+ updatePkgVersionInFsproj fsproj version
+ run $"dotnet pack {fsproj} -p:Pack=true -c Release -o {pkgDir}"
+
+ // Return install command
+ $"""dotnet {pkgCommand} --version "{version}" --add-source {fullPath pkgDir}"""
+
+let buildLocalPackage pkgDir =
+ buildLocalPackageWith pkgDir
+ "tool install fable"
+ (resolveDir "src/Fable.Cli/Fable.Cli.fsproj") (fun version ->
+ updateVersionsInFableTransforms version [] |> ignore
+ buildLibraryTs())
+
+let testRepos() =
+ let repos = [
+ "https://github.com/alfonsogarciacaro/FsToolkit.ErrorHandling:update-fable-3", "npm i && npm test"
+ "https://github.com/fable-compiler/fable-promise:master", "npm i && npm test"
+ "https://github.com/alfonsogarciacaro/Thoth.Json:nagareyama", "dotnet paket restore && npm i && dotnet fable tests -o tests/bin --run mocha tests/bin"
+ "https://github.com/alfonsogarciacaro/FSharp.Control.AsyncSeq:nagareyama", "cd tests/fable && npm i && npm test"
+ "https://github.com/alfonsogarciacaro/Fable.Extras:nagareyama", "dotnet paket restore && npm i && npm test"
+ "https://github.com/alfonsogarciacaro/Fable.Jester:nagareyama", "npm i && npm test"
+ "https://github.com/Zaid-Ajaj/Fable.SimpleJson:master", "npm i && npm run test-nagareyama"
+ ]
+
+ let testDir = tempPath() > "fable-repos"
+ printfn $"Cloning repos to: {testDir}"
+
+ cleanDirs [testDir]
+ makeDirRecursive testDir
+ let pkgInstallCmd = buildLocalPackage (testDir > "pkg")
+
+ for (repo, command) in repos do
+ let url, branch = let i = repo.LastIndexOf(":") in repo[..i-1], repo[i+1..]
+ let name = url[url.LastIndexOf("/") + 1..]
+ runInDir testDir $"git clone {url} {name}"
+ let repoDir = testDir > name
+ runInDir repoDir ("git checkout " + branch)
+ runInDir repoDir "dotnet tool uninstall fable"
+ runInDir repoDir pkgInstallCmd
+ runInDir repoDir "dotnet tool restore"
+ runInDir repoDir command
+
+let githubRelease() =
+ match envVarOrNone "GITHUB_USER", envVarOrNone "GITHUB_TOKEN" with
+ | Some user, Some token ->
+ try
+ let version, notes = Publish.loadReleaseVersionAndNotes "src/Fable.Cli"
+ let notes = notes |> Array.map (fun n -> $"""'{n.Replace("'", @"\'").Replace("`", @"\`")}'""") |> String.concat ","
+ run $"git commit -am \"Release {version}\" && git push"
+ runSilent $"""node --eval "require('ghreleases').create({{ user: '{user}', token: '{token}', }}, 'fable-compiler', 'Fable', {{ tag_name: '{version}', name: '{version}', body: [{notes}].join('\n'), }}, (err, res) => {{ if (err != null) {{ console.error(err) }} }})" """
+ printfn $"Github release %s{version} created successfully"
+ with ex ->
+ printfn $"Github release failed: %s{ex.Message}"
+ | _ -> failwith "Expecting GITHUB_USER and GITHUB_TOKEN enviromental variables"
+
+let copyFcsRepo sourceDir =
+ let targetDir = "src/fcs-fable"
+ let path1 = "fcs/fcs-fable"
+ let path2 = "src/Compiler"
+ cleanDirs [targetDir]
+ copyDirRecursive (sourceDir > path1) targetDir
+ copyDirRecursive (sourceDir > path2) (targetDir > path2)
+ removeFile (targetDir > ".gitignore")
+ let projPath = (targetDir > "fcs-fable.fsproj")
+ let projText = readFile projPath
+ let projText =
+ Regex.Replace(projText,
+ @"(\$\(MSBuildProjectDirectory\)).*?(<\/FSharpSourcesRoot>)",
+ "$1/src/Compiler$2")
+ // let projText =
+ // Regex.Replace(projText,
+ // @"artifacts\/bin\/FSharp.Core\/Release\/netstandard2.0",
+ // "lib/fcs")
+ projText |> writeFile projPath
+
+let syncFcsRepo() =
+ // FAKE is giving lots of problems with the dotnet SDK version, ignore it
+ let cheatWithDotnetSdkVersion dir f =
+ let path = dir > "build.fsx"
+ let script = readFile path
+ Regex.Replace(script, @"let dotnetExePath =[\s\S]*DotNetCli\.InstallDotNetSDK", "let dotnetExePath = \"dotnet\" //DotNetCli.InstallDotNetSDK") |> writeFile path
+ f ()
+ runInDir dir "git reset --hard"
+
+ printfn $"Expecting %s{FCS_REPO} repo to be cloned at %s{FCS_REPO_LOCAL}"
+
+ // TODO: Prompt to reset --hard changes
+ // service_slim
+ runInDir FCS_REPO_LOCAL ("git checkout " + FCS_REPO_SERVICE_SLIM_BRANCH)
+ runInDir FCS_REPO_LOCAL "git pull"
+ cheatWithDotnetSdkVersion (FCS_REPO_LOCAL > "fcs") (fun () ->
+ runBashOrCmd (FCS_REPO_LOCAL > "fcs") "build" "")
+ copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Compiler.Service.dll") "../fable/lib/fcs/"
+ copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Compiler.Service.xml") "../fable/lib/fcs/"
+ copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Core.dll") "../fable/lib/fcs/"
+ copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Core.xml") "../fable/lib/fcs/"
+
+ // fcs-fable
+ runInDir FCS_REPO_LOCAL ("git checkout " + FCS_REPO_FABLE_BRANCH)
+ runInDir FCS_REPO_LOCAL "git pull"
+ cheatWithDotnetSdkVersion (FCS_REPO_LOCAL > "fcs") (fun () ->
+ runBashOrCmd (FCS_REPO_LOCAL > "fcs") "build" "CodeGen.Fable")
+ copyFcsRepo FCS_REPO_LOCAL
+
+let packages() =
+ ["Fable.AST", doNothing
+ "Fable.Core", doNothing
+ "Fable.Cli", (fun () ->
+ // TODO: Add library versions for other languages
+ let compilerVersion = Publish.loadReleaseVersion "src/Fable.Cli"
+ let updatedLibs = updateVersionsInFableTransforms compilerVersion [
+ "js", getNpmVersion "src/fable-library"
+ ]
+ buildLibraryTs()
+ buildLibraryPy()
+ buildLibraryRust()
+ buildLibraryDart true
+ if updatedLibs.Contains("js") then
+ pushNpmWithoutReleaseNotesCheck "build/fable-library"
+ )
+ "Fable.PublishUtils", doNothing
+ "fable-metadata", doNothing
+ "fable-standalone", fun () -> buildStandalone {|minify=true; watch=false|}
+ "fable-compiler-js", fun () -> buildCompilerJs true
+ ]
+
+let publishPackages restArgs =
+ let packages =
+ match List.tryHead restArgs with
+ | Some pkg -> packages() |> List.filter (fun (name,_) -> name = pkg)
+ | None -> packages()
+ for (pkg, buildAction) in packages do
+ if Char.IsUpper pkg[0] then
+ let projFile = "src" > pkg > pkg + ".fsproj"
+ pushFableNuget projFile ["Pack", "true"] buildAction
+ else
+ pushNpm ("src" > pkg) buildAction
+
+let hasFlag flag =
+ BUILD_ARGS_LOWER |> List.contains flag
+
+match BUILD_ARGS_LOWER with
+// | "check-sourcemaps"::_ ->
+// ("src/quicktest/Quicktest.fs", "src/quicktest/bin/Quicktest.js", "src/quicktest/bin/Quicktest.js.map")
+// |||> sprintf "nodemon --watch src/quicktest/bin/Quicktest.js --exec 'source-map-visualization --sm=\"%s;%s;%s\"'"
+// |> List.singleton |> quicktest
+// | "coverage"::_ -> coverage()
+| ("test"|"test-js")::_ -> testJs()
+| "test-mocha"::_ -> compileAndRunTestsWithMocha true "Main"
+| "test-mocha-fast"::_ -> compileAndRunTestsWithMocha false "Main"
+| "test-react"::_ -> testReact()
+| "test-standalone"::_ ->
+ let minify = hasFlag "--no-minify" |> not
+ testStandalone(minify)
+| "test-standalone-fast"::_ -> testStandaloneFast()
+| "test-configs"::_ -> testProjectConfigs()
+| "test-integration"::_ -> testIntegration()
+| "test-repos"::_ -> testRepos()
+| ("test-ts"|"test-typescript")::_ -> testTypeScript(false)
+| ("watch-test-ts"|"watch-test-typescript")::_ -> testTypeScript(true)
+| "test-py"::_ -> testPython()
+| "test-rust"::_ -> testRust Default
+| "test-rust-no_std"::_ -> testRust NoStd
+| "test-rust-default"::_ -> testRust Default
+| "test-rust-threaded"::_ -> testRust Threaded
+| "test-dart"::_ -> testDart(false)
+| "watch-test-dart"::_ -> testDart(true)
+
+| "quicktest"::_ ->
+ buildLibraryTsIfNotExists()
+ watchFableWithArgs "src/quicktest" ["--watch --exclude Fable.Core --noCache --runScript"]
+| "quicktest-ts"::_ ->
+ buildLibraryTsIfNotExists()
+ let srcDir = "src/quicktest"
+ let outPath = "build/quicktest-ts/Quicktest.fs.js"
+ // Make sure outPath exists so nodemon doesn't complain
+ if not(pathExists outPath) then
+ makeDirRecursive (dirname outPath)
+ writeFile outPath ""
+ let runCmd = $"npx concurrently \"tsc -w -p {srcDir} --outDir {dirname outPath}\" \"nodemon -w {outPath} {outPath}\""
+ watchFableWithArgs srcDir ["--lang ts --watch --exclude Fable.Core --noCache --run"; runCmd]
+| ("quicktest-py"|"quicktest-python")::_ ->
+ buildLibraryPyIfNotExists()
+ watchFableWithArgs "src/quicktest-py" ["--lang py --watch --exclude Fable.Core --noCache --runScript"]
+| "quicktest-dart"::_ ->
+ buildLibraryDartIfNotExists()
+ watchFableWithArgs "src/quicktest-dart" ["--lang dart --watch --exclude Fable.Core --noCache --runScript"]
+| ("quicktest-rs"|"quicktest-rust")::_ ->
+ buildLibraryRustIfNotExists()
+ watchFableWithArgs "src/quicktest-rust" ["--lang rs -e .rs --watch --exclude Fable.Core --noCache --runScript"]
+| "run"::_ ->
+ buildLibraryTsIfNotExists()
+ // Don't take args from pattern matching because they're lowered
+ let restArgs = BUILD_ARGS |> List.skip 1 |> String.concat " "
+ run $"""dotnet run -c Release --project {resolveDir "src/Fable.Cli"} -- {restArgs}"""
+
+| "package"::_ ->
+ let pkgInstallCmd = buildLocalPackage (resolveDir "temp/pkg")
+ printfn $"\nPackage has been created, use the following command to install it:\n {pkgInstallCmd}\n"
+| "package-core"::_ ->
+ let pkgInstallCmd = buildLocalPackageWith (resolveDir "temp/pkg") "add package Fable.Core" (resolveDir "src/Fable.Core/Fable.Core.fsproj") ignore
+ printfn $"\nFable.Core package has been created, use the following command to install it:\n {pkgInstallCmd}\n"
+
+| ("fable-library"|"library")::_
+| ("fable-library-ts"|"library-ts")::_ -> buildLibraryTs()
+| ("fable-library-py"|"library-py")::_ -> buildLibraryPy()
+| ("fable-library-rust" | "library-rust")::_ -> buildLibraryRust()
+| ("fable-library-dart" | "library-dart")::_ ->
+ let clean = hasFlag "--no-clean" |> not
+ buildLibraryDart(clean)
+
+| ("fable-compiler-js"|"compiler-js")::_ ->
+ let minify = hasFlag "--no-minify" |> not
+ buildCompilerJs(minify)
+| ("fable-standalone"|"standalone")::_ ->
+ let minify = hasFlag "--no-minify" |> not
+ buildStandalone {|minify=minify; watch=false|}
+| ("fable-worker"|"worker")::_ ->
+ let minify = hasFlag "--no-minify" |> not
+ buildWorker {|minify=minify; watch=false|}
+| "watch-standalone"::_ -> buildStandalone {|minify=false; watch=true|}
+
+| "sync-fcs-repo"::_ -> syncFcsRepo()
+| "copy-fcs-repo"::_ -> copyFcsRepo FCS_REPO_LOCAL
+
+| "publish"::restArgs -> publishPackages restArgs
+| "github-release"::_ ->
+ publishPackages []
+ githubRelease ()
+
+| _ ->
+ printfn """Please pass a target name. Examples:
+
+- Use `test` to run tests:
+ dotnet fsi build.fsx test
+
+- Use `package` to build a local package:
+ dotnet fsi build.fsx package
+
+- Use `run` to compile a project with development version:
+ dotnet fsi build.fsx run ../path/to/my/project [Fable options]
+
+- Use `quicktest` to quickly test development version with src/quicktest project:
+ dotnet fsi build.fsx quicktest
+"""
+
+printfn "Build finished successfully"
diff --git a/build.fsx b/build.fsx
index 349eb4f238..281c54b1f2 100644
--- a/build.fsx
+++ b/build.fsx
@@ -1,893 +1,333 @@
-#load "src/Fable.PublishUtils/PublishUtils.fs"
-
-open System
-open System.Text.RegularExpressions
-open PublishUtils
-
-// ncave FCS fork
-let FCS_REPO = "https://github.com/ncave/fsharp"
-let FCS_REPO_LOCAL = "../fsharp_fable"
-let FCS_REPO_FABLE_BRANCH = "fable"
-let FCS_REPO_SERVICE_SLIM_BRANCH = "service_slim"
-
-let BUILD_ARGS =
- fsi.CommandLineArgs
- |> Array.skip 1
- |> List.ofArray
-
-let BUILD_ARGS_LOWER =
- BUILD_ARGS
- |> List.map (fun x -> x.ToLower())
-
-module Util =
- let cleanDirs dirs =
- for dir in dirs do
- printfn $"Clean {dir}"
- removeDirRecursive dir
-
- // TODO: move to PublishUtils.fs ?
- let copyFiles sourceDir searchPattern destDir =
- printfn $"Copy {sourceDir > searchPattern} to {destDir}"
- for source in IO.Directory.GetFiles(sourceDir, searchPattern) do
- let fileName = IO.Path.GetFileName(source)
- let target = destDir > fileName
- IO.File.Copy(source, target, true)
-
- let resolveDir dir =
- __SOURCE_DIRECTORY__ > dir
-
- let updateVersionsInFableTransforms compilerVersion (libraryVersions: (string * string) list) =
- let mutable updated = Set.empty
-
- let replaceVersion (lang: string option) version fileContent =
- let prefix =
- match lang with
- | None -> ""
- | Some lang -> $"{lang.ToUpperInvariant()}_LIBRARY_"
-
- Regex.Replace(
- fileContent,
- $@"^(\s*)let \[] {prefix}VERSION = ""(.*?)""",
- (fun (m: Match) ->
- match lang with
- | Some lang when m.Groups[2].Value <> version ->
- updated <- Set.add lang updated
- | _ -> ()
- m.Groups[1].Value + $"let [] {prefix}VERSION = \"{version}\""
- ),
- RegexOptions.Multiline)
-
- let filePath = "src/Fable.Transforms/Global/Compiler.fs"
- readFile filePath
- |> replaceVersion None compilerVersion
- |> List.foldBack (fun (lang, version) fileContent ->
- replaceVersion (Some lang) version fileContent) libraryVersions
- |> writeFile filePath
-
- updated
-
- let updatePkgVersionInFsproj projFile version =
- readFile projFile
- |> replaceRegex Publish.NUGET_PACKAGE_VERSION (fun m ->
- m.Groups[1].Value + version + m.Groups[3].Value)
- |> writeFile projFile
-
- let runTSLint projectDir =
- run ("npm run tslint -- --project " + projectDir)
-
- let runTypeScript projectDir =
- run ("npm run tsc -- --project " + projectDir)
-
- let runTypeScriptWithArgs projectDir args =
- run ("npm run tsc -- --project " + projectDir + " " + String.concat " " args)
-
- let runFableWithArgs projectDir args =
- run ("dotnet run -c Release --project src/Fable.Cli -- " + projectDir + " " + String.concat " " args)
-
- let watchFableWithArgs projectDir args =
- run ("dotnet watch --project src/Fable.Cli run -- " + projectDir + " --cwd ../.. " + String.concat " " args)
-
- let runFableWithArgsInDirAs release projectDir args =
- let cliDir = resolveDir "src/Fable.Cli"
- let cliArgs = args |> String.concat " "
- let cliCmd = $"""dotnet run {if release then "-c Release" else ""} --project {cliDir} -- {cliArgs}"""
- runInDir (resolveDir projectDir) cliCmd
-
- let runFableWithArgsInDir projectDir args =
- runFableWithArgsInDirAs true projectDir args
-
- let runFableWithArgsAsync projectDir args =
- runAsync ("dotnet run -c Release --project src/Fable.Cli -- " + projectDir + " " + String.concat " " args)
-
- let runNpx command args =
- run ("npx " + command + " " + (String.concat " " args))
-
- let runNpmScript script args =
- run ("npm run " + script + " -- " + (String.concat " " args))
-
- let runNpmScriptAsync script args =
- runAsync ("npm run " + script + " -- " + (String.concat " " args))
-
- let runFable projectDir =
- runFableWithArgs projectDir []
-
- let runMocha testDir =
- runNpmScript "mocha" [$"{testDir} --reporter dot -t 10000"]
-
-open Util
-
-module Unused =
- let downloadAndExtractTo (url: string) (targetDir: string) =
- $"npx download --extract --out %s{targetDir} \"%s{url}\"" |> run
-
- let coverage() =
- // report converter
- // https://github.com/danielpalme/ReportGenerator
- // dotnet tool install dotnet-reportgenerator-globaltool --tool-path tools
- if not (pathExists "./bin/tools/reportgenerator") && not (pathExists "./bin/tools/reportgenerator.exe") then
- runInDir "." "dotnet tool install dotnet-reportgenerator-globaltool --tool-path bin/tools"
- let reportGen =
- if pathExists "./bin/tools/reportgenerator" then "bin/tools/reportgenerator"
- else "bin\\tools\\reportgenerator.exe"
-
- // if not (pathExists "build/fable-library") then
- // buildLibrary()
-
- cleanDirs ["build/tests"]
- runFable "tests"
-
- // JS
- run "npx nyc mocha build/tests --require source-map-support/register --reporter dot -t 10000"
- runInDir "." (reportGen + " \"-reports:build/coverage/nyc/lcov.info\" -reporttypes:Html \"-targetdir:build/coverage/nyc/html\" ")
-
- // .NET
- //runInDir "tests/Main" "dotnet build /t:Collect_Coverage"
- cleanDirs ["build/coverage/netcoreapp2.0/out"]
- runInDir "." (reportGen + " \"-reports:build/coverage/netcoreapp2.0/coverage.xml\" -reporttypes:Html \"-targetdir:build/coverage/netcoreapp2.0/html\" ")
-
-// TARGETS ---------------------------
-
-// let buildLibraryJsWithOptions (opts: {| watch: bool |}) =
-// let baseDir = __SOURCE_DIRECTORY__
-
-// let projectDir = baseDir > "src/fable-library"
-// let buildDir = baseDir > "build/fable-library"
-// let fableOpts = [
-// "--outDir " + buildDir
-// "--fableLib " + buildDir
-// "--exclude Fable.Core"
-// "--define FX_NO_BIGINT"
-// "--define FABLE_LIBRARY"
-// if opts.watch then "--watch"
-// ]
-
-// cleanDirs [buildDir]
-// runInDir baseDir "npm install"
-// makeDirRecursive buildDir
-
-// copyFile (projectDir > "package.json") buildDir
-
-// if opts.watch then
-// Async.Parallel [
-// runNpmScriptAsync "tsc" [
-// "--project " + projectDir
-// "--watch"
-// ]
-// runFableWithArgsAsync projectDir fableOpts
-// ] |> runAsyncWorkflow
-// else
-// runTSLint projectDir
-// runTypeScript projectDir
-// runFableWithArgs projectDir fableOpts
-// removeDirRecursive (buildDir > ".fable")
-
-// let buildLibraryJs() = buildLibraryJsWithOptions {| watch = false |}
-
-// let buildLibraryJsIfNotExists() =
-// if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library")) then
-// buildLibraryJs()
-
-let buildLibraryTs() =
- let baseDir = __SOURCE_DIRECTORY__
- let sourceDir = "./src/fable-library"
- let buildDirTs = "./build/fable-library-ts"
- let buildDirJs = "./build/fable-library"
-
- cleanDirs [buildDirTs; buildDirJs]
- runInDir baseDir "npm install"
-
- runFableWithArgs sourceDir [
- "--outDir " + buildDirTs
- "--fableLib " + buildDirTs
- "--lang TypeScript"
- "--typedArrays false"
- "--exclude Fable.Core"
- "--define FX_NO_BIGINT"
- "--define FABLE_LIBRARY"
- ]
-
- copyFiles sourceDir "*.ts" buildDirTs
- copyFiles (sourceDir > "ts") "*.json" buildDirTs
- copyDirRecursive (sourceDir > "lib") (buildDirTs > "lib")
- copyFile (sourceDir > "package.json") buildDirTs
-
- // runTSLint buildDirTs
- runTypeScriptWithArgs buildDirTs ["--outDir " + buildDirJs]
- copyFile (buildDirTs > "lib/big.d.ts") (buildDirJs > "lib/big.d.ts")
- copyFile (buildDirTs > "package.json") buildDirJs
-
- copyFile (sourceDir > "README.md") buildDirJs
-
-let buildLibraryTsIfNotExists() =
- if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-ts")) then
- buildLibraryTs()
-
-let buildLibraryPy() =
- let libraryDir = "./src/fable-library-py"
- let projectDir = libraryDir > "fable_library"
- let buildDirPy = "./build/fable-library-py"
-
- cleanDirs [buildDirPy]
-
- runFableWithArgs projectDir [
- "--outDir " + buildDirPy > "fable_library"
- "--fableLib " + buildDirPy > "fable_library"
- "--lang Python"
- "--exclude Fable.Core"
- "--define FABLE_LIBRARY"
- ]
-
- // Copy python related files from projectDir to buildDir
- copyFiles libraryDir "*" buildDirPy
- copyFiles projectDir "*.py" (buildDirPy > "fable_library")
-
- // Fix issues with Fable .fsproj not supporting links
- copyDirNonRecursive (buildDirPy > "fable_library/fable-library") (buildDirPy > "fable_library")
- removeDirRecursive (buildDirPy > "fable_library/fable-library")
-
-let buildLibraryPyIfNotExists() =
- let baseDir = __SOURCE_DIRECTORY__
- if not (pathExists (baseDir > "build/fable-library-py")) then
- buildLibraryPy()
-
-let buildLibraryRust() =
- let libraryDir = "src/fable-library-rust"
- let sourceDir = libraryDir > "src"
- let buildDir = "build/fable-library-rust"
- let outDir = buildDir > "src"
- let fableLib = "."
-
- cleanDirs [buildDir]
-
- runFableWithArgsInDir sourceDir [
- "--outDir " + resolveDir outDir
- "--fableLib " + fableLib
- "--lang Rust"
- "--exclude Fable.Core"
- "--define FABLE_LIBRARY"
- "--noCache"
- ]
-
- copyFiles libraryDir "*.toml" buildDir
- copyFiles sourceDir "*.rs" outDir
- copyDirRecursive (libraryDir > "vendored") (buildDir > "vendored")
-
- runInDir buildDir "cargo fmt"
- runInDir buildDir "cargo fix --allow-no-vcs"
- runInDir buildDir "cargo build"
-
-let buildLibraryRustIfNotExists() =
- if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-rust")) then
- buildLibraryRust()
-
-let buildLibraryDart(clean: bool) =
- let sourceDir = resolveDir "src/fable-library-dart"
- let buildDir = resolveDir "build/fable-library-dart"
-
- if clean then
- cleanDirs [buildDir]
- makeDirRecursive buildDir
- copyFiles sourceDir "pubspec.*" buildDir
- copyFiles sourceDir "analysis_options.yaml" buildDir
-
- copyFiles sourceDir "*.dart" buildDir
-
- runFableWithArgsInDir sourceDir [
- "--outDir " + buildDir
- "--fableLib " + buildDir
- "--lang Dart"
- "--exclude Fable.Core"
- "--define FABLE_LIBRARY"
- if clean then "--noCache"
- ]
-
-let buildLibraryDartIfNotExists() =
- if not (pathExists (__SOURCE_DIRECTORY__ > "build/fable-library-dart")) then
- buildLibraryDart(true)
-
-// Like testStandalone() but doesn't create bundles/packages for fable-standalone & friends
-// Mainly intended for CI
-let testStandaloneFast() =
- runFableWithArgs "src/fable-standalone/src" [
- "--noCache"
- ]
-
- runFableWithArgs "src/fable-compiler-js/src" [
- "--exclude Fable.Core"
- "--define LOCAL_TEST"
- ]
-
- let fableJs = "./src/fable-compiler-js/src/app.fs.js"
- let testProj = "tests/Js/Main/Fable.Tests.fsproj"
- let buildDir = "build/tests/Standalone"
- run $"node {fableJs} {testProj} {buildDir}"
- runMocha buildDir
-
-let buildWorker (opts: {| minify: bool; watch: bool |}) =
- printfn "Building worker%s..." (if opts.minify then "" else " (no minification)")
-
- let projectDir = "src/fable-standalone/src"
- let buildDir = "build/fable-standalone"
- let distDir = "src/fable-standalone/dist"
-
- runFableWithArgs (projectDir + "/Worker") [
- "--outDir " + buildDir + "/worker"
- ]
-
- let rollupTarget =
- match opts.minify with
- | true -> buildDir > "worker.js"
- | false -> distDir > "worker.min.js"
-
- // make standalone worker dist
- runNpmScript "rollup" [$"""{buildDir}/worker/Worker.js -o {rollupTarget} --format iife"""]
-
- if opts.minify then
- // runNpx "webpack" [sprintf "--entry ./%s/worker.js --output ./%s/worker.min.js --config ./%s/../worker.config.js" buildDir distDir projectDir]
- runNpmScript "terser" [$"{buildDir}/worker.js -o {distDir}/worker.min.js --mangle --compress"]
-
- // Put fable-library files next to bundle
- printfn "Copying fable-library..."
- buildLibraryTsIfNotExists()
- let libraryDir = "build/fable-library"
- let libraryTarget = distDir > "fable-library"
- copyDirRecursive libraryDir libraryTarget
-
-let buildStandalone (opts: {| minify: bool; watch: bool |}) =
- buildLibraryTs()
-
- printfn "Building standalone%s..." (if opts.minify then "" else " (no minification)")
-
- let projectDir = "src/fable-standalone/src"
- let buildDir = "build/fable-standalone"
- let distDir = "src/fable-standalone/dist"
-
- let rollupTarget =
- match opts.watch, opts.minify with
- | true, _ ->
- match BUILD_ARGS with
- | _::rollupTarget::_ -> rollupTarget
- | _ -> failwith "Pass the bundle output, e.g.: npm run build watch-standalone ../repl3/public/js/repl/bundle.min.js"
- | false, true -> buildDir > "bundle.js"
- | false, false -> distDir > "bundle.min.js"
-
- let rollupArgs = [
- buildDir > "bundle/Main.js"
- "-o " + rollupTarget
- "--format umd"
- "--name __FABLE_STANDALONE__"
- ]
-
- // cleanup
- if not opts.watch then
- cleanDirs [buildDir; distDir]
- makeDirRecursive distDir
-
- // build standalone bundle
- runFableWithArgs projectDir [
- "--outDir " + buildDir > "bundle"
- if opts.watch then
- "--watch"
- "--run rollup"
- yield! rollupArgs
- "--watch"
- ]
-
- // make standalone bundle dist
- runNpmScript "rollup" rollupArgs
- if opts.minify then
- runNpmScript "terser" [
- buildDir > "bundle.js"
- "-o " + distDir > "bundle.min.js"
- "--mangle"
- "--compress"
- ]
-
- // build standalone worker
- buildWorker opts
-
- // print bundle size
- fileSizeInBytes (distDir > "bundle.min.js") / 1000. |> printfn "Bundle size: %fKB"
- fileSizeInBytes (distDir > "worker.min.js") / 1000. |> printfn "Worker size: %fKB"
-
-let buildCompilerJs(minify: bool) =
- let projectDir = "src/fable-compiler-js/src"
- let buildDir = "build/fable-compiler-js"
- let distDir = "src/fable-compiler-js/dist"
-
- if not (pathExists "build/fable-standalone") then
- buildStandalone {|minify=minify; watch=false|}
-
- cleanDirs [buildDir; distDir]
- makeDirRecursive distDir
-
- runFableWithArgs projectDir [
- "--outDir " + buildDir
- "--exclude Fable.Core"
- ]
-
- let rollupTarget = if minify then distDir > "app.js" else distDir > "app.min.js"
- run $"npx rollup {buildDir}/app.js -o {rollupTarget} --format umd --name Fable"
- if minify then
- run $"npx terser {distDir}/app.js -o {distDir}/app.min.js --mangle --compress"
-
- // Copy fable-library
- copyDirRecursive ("build/fable-library") (distDir > "fable-library")
- // Copy fable-metadata
- copyDirRecursive ("src/fable-metadata/lib") (distDir > "fable-metadata")
-
-let testStandalone(minify) =
- let fableDir = "src/fable-compiler-js"
- let buildDir = "build/tests/Standalone"
-
- if not (pathExists "build/fable-compiler-js") then
- buildCompilerJs(minify)
-
- cleanDirs [buildDir]
-
- // Link fable-compiler-js to local packages
- runInDir fableDir "npm link ../fable-metadata"
- runInDir fableDir "npm link ../fable-standalone"
-
- // Test fable-compiler-js locally
- run $"node {fableDir} tests/Js/Main/Fable.Tests.fsproj {buildDir}"
- runMocha buildDir
-
- // // Another local fable-compiler-js test
- // runInDir (fableDir > "test") "node .. test_script.fsx"
- // runInDir (fableDir > "test") "node test_script.fsx.js"
-
- // Unlink local packages after test
- runInDir fableDir "npm unlink ../fable-metadata && cd ../fable-metadata && npm unlink"
- runInDir fableDir "npm unlink ../fable-standalone && cd ../fable-standalone && npm unlink"
-
-let testReact() =
- runFableWithArgs "tests/React" ["--noCache"]
- runInDir "tests/React" "npm i && npm test"
-
-let compileAndRunTestsWithMocha clean projectName =
- let projectDir = "tests/Js/" + projectName
- let buildDir = "build/tests/Js/" + projectName
-
- if clean then
- cleanDirs [buildDir]
-
- runFableWithArgs projectDir [
- "--outDir " + buildDir
- "--exclude Fable.Core"
- ]
-
- runMocha buildDir
-
-let testProjectConfigs() =
- [ "tests/Integration/ProjectConfigs/DebugWithExtraDefines", "Debug"
- "tests/Integration/ProjectConfigs/CustomConfiguration", "Test"
- "tests/Integration/ProjectConfigs/ReleaseNoExtraDefines", String.Empty
- "tests/Integration/ProjectConfigs/ConsoleApp", String.Empty
- ]
- |> List.iter (fun (projectDir, configuration) ->
- let buildDir = "build/"+ projectDir
-
- cleanDirs [ buildDir ]
- runFableWithArgs projectDir [
- "--outDir " + buildDir
- "--exclude Fable.Core"
- if not(String.IsNullOrEmpty configuration) then
- "--configuration " + configuration
- ]
-
- runMocha buildDir
- )
-
-let testIntegration() =
- runInDir "tests/Integration/Integration" "dotnet run -c Release"
+// #r "nuget: Fun.Build, 0.5.1"
+#r "nuget: Fake.IO.FileSystem, 5.23.1"
+#r "nuget: Fake.Core.Environment, 5.23.1"
+#r "nuget: Fake.Tools.Git, 5.23.1"
+#r "nuget: Fake.Api.GitHub, 5.23.1"
+#r "nuget: BlackFox.CommandLine, 1.0.0"
+#r "nuget: FsToolkit.ErrorHandling, 4.9.0"
+#r "nuget: Fli, 1.10.1"
+
+// open Fun.Build
+// open System.IO
+// open Fake.IO
+// open BlackFox.CommandLine
+// open FsToolkit.ErrorHandling
+
+// module Commands =
+
+// let isWatch =
+// CmdArg.Create(
+// "-w",
+// "--watch",
+// "Watch for changes and recompile",
+// isOptional = true
+// )
+
+// module TypeScript =
+
+// let fableBuildDest = "build/tests/TypeScript"
+// let typeScriptBuildDest = "build/tests/TypeScriptCompiled"
+// let projectDir = "tests/TypeScript"
+
+// module Stages =
+
+// let compileAndTestInWatchMode =
+// stage "" {
+// whenCmdArg Commands.isWatch
+
+// stage "Pre-compile" {
+// // We need to first pre-compile for TypeScript and Mocha to
+// // start in watch mode
+// // Could use NPM script, but doing the pre-compilation allow
+// // to have the full build system in a single place
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw projectDir
+// >> CmdLine.appendPrefix "--outDir" fableBuildDest
+// >> CmdLine.appendPrefix "--lang" "typescript"
+// >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+
+// Cmd.fable args
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendPrefix "-p" fableBuildDest
+// >> CmdLine.appendPrefix "--outDir" typeScriptBuildDest
+
+// Cmd.tsc args
+// |> CmdLine.toString
+// )
+// }
+
+// stage "Watch, compile and test" {
+// paralle
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw projectDir
+// >> CmdLine.appendPrefix "--outDir" fableBuildDest
+// >> CmdLine.appendPrefix "--lang" "typescript"
+// >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+// >> CmdLine.appendRaw "--watch"
+
+// Cmd.fable args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendPrefix "-p" fableBuildDest
+// >> CmdLine.appendPrefix "--outDir" typeScriptBuildDest
+// >> CmdLine.appendRaw "--watch"
+
+// Cmd.tsc args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// // Mocha `--watch` doesn't support for ESM modules
+// // So we use nodemon as the watcher
+// let args =
+// CmdLine.appendPrefix "-e" "js"
+// >> CmdLine.appendRaw "--watch"
+// >> CmdLine.appendRaw typeScriptBuildDest
+// >> CmdLine.appendPrefix "--delay" "200ms"
+// >> CmdLine.appendPrefix "--exec" "mocha"
+// >> CmdLine.appendRaw $"{typeScriptBuildDest}/{fableBuildDest}"
+// >> CmdLine.appendPrefix "--reporter" "dot"
+// >> CmdLine.appendPrefix "-t" "10000"
+
+// Cmd.nodemon args
+// |> CmdLine.toString
+// )
+// }
+
+// }
+
+// let compileAndTest =
+// stage "Compile and tests" {
+// whenNot {
+// cmdArg Commands.isWatch
+// }
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw projectDir
+// >> CmdLine.appendPrefix "--outDir" fableBuildDest
+// >> CmdLine.appendPrefix "--lang" "typescript"
+// >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+
+// Cmd.fable args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendPrefix "-p" fableBuildDest
+// >> CmdLine.appendPrefix "--outDir" typeScriptBuildDest
+
+// Cmd.tsc args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw $"{typeScriptBuildDest}/{fableBuildDest}"
+// >> CmdLine.appendPrefix "--reporter" "dot"
+// >> CmdLine.appendPrefix "-t" "10000"
+
+// Cmd.mocha args
+// |> CmdLine.toString
+// )
+// }
+
+// module Stages =
+
+// module JavaScript =
+
+// let testWithMocha projectName =
+// let outDir = Path.Combine("build", "tests", "Js", projectName)
+// let projectDir = Path.Combine("tests", "Js", projectName)
+
+// stage $"Run test using mocha for project: {projectDir}" {
+
+// run (fun _ ->
+// Shell.cleanDir outDir
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw projectDir
+// >> CmdLine.appendPrefix "--outDir" outDir
+// >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+
+// Cmd.fable args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw outDir
+// >> CmdLine.appendPrefix "--reporter" "dot"
+// >> CmdLine.appendPrefix "-t" "10000"
+
+// Cmd.mocha args
+// |> CmdLine.toString
+// )
+// }
+
+
+// let testStandalone =
+// stage "Standalone" {
+// whenEnvVar "CI"
+
+// // Compile Fable standalone to JavaScript
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw "src/fable-standalone/src/"
+// >> CmdLine.appendRaw "--noCache"
+
+// Cmd.fable args
+// |> CmdLine.toString
+// )
+
+// // Compile Fable compiler to JavaScript
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw "src/fable-compiler-js/src"
+// >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+// >> CmdLine.appendPrefix "--define" "LOCAL_TEST"
+
+// Cmd.fable args
+// |> CmdLine.toString
+// )
+
+// // Compile test project using JavaScript fable compiler
+// run (fun _ ->
+// let fableJs = "./src/fable-compiler-js/src/app.fs.js"
+// let testProj = "tests/Js/Main/Fable.Tests.fsproj"
+// let buildDir = "build/tests/Standalone"
+
+// let args =
+// CmdLine.appendRaw fableJs
+// >> CmdLine.appendRaw testProj
+// >> CmdLine.appendRaw buildDir
+
+// Cmd.node args
+// |> CmdLine.toString
+// )
+
+// run (fun _ ->
+// let args =
+// CmdLine.appendRaw "build/tests/Standalone"
+// >> CmdLine.appendPrefix "--reporter" "dot"
+// >> CmdLine.appendPrefix "-t" "10000"
- buildLibraryTsIfNotExists()
- runInDir "tests/Integration/Compiler" "dotnet run -c Release"
- testProjectConfigs()
-
-let testJs() =
- buildLibraryTsIfNotExists()
-
- compileAndRunTestsWithMocha true "Main"
-
- runInDir "tests/Js/Main" "dotnet run -c Release"
-
- // Adaptive tests must go in a different project to avoid conflicts with Queue shim, see #2559
- compileAndRunTestsWithMocha true "Adaptive"
-
- testReact()
-
- if envVarOrNone "CI" |> Option.isSome then
- testStandaloneFast()
-
-let testTypeScript isWatch =
- buildLibraryTsIfNotExists()
-
- let projectDir = "tests/TypeScript"
- let buildDir = "build/tests/TypeScript"
- let buildDir2 = "build/tests/TypeScriptCompiled"
-
- cleanDirs [buildDir; buildDir2]
-
- copyFile (projectDir > "tsconfig.json") (buildDir > "tsconfig.json")
-
- runFableWithArgsInDirAs (not isWatch) "." [
- projectDir
- "--lang ts"
- "--outDir " + buildDir
- "--exclude Fable.Core"
- if isWatch then
- "--watch"
- $"--runWatch npm run test-ts"
- ]
-
- runNpmScript "test-ts" []
-
-let testPython() =
- buildLibraryPyIfNotExists() // NOTE: fable-library-py needs to be built separately.
-
- let projectDir = "tests/Python"
- let buildDir = "build/tests/Python"
-
- cleanDirs [buildDir]
- runInDir projectDir "dotnet test -c Release"
- runFableWithArgs projectDir [
- "--outDir " + buildDir
- "--exclude Fable.Core"
- "--lang Python"
- ]
-
- runInDir buildDir "poetry run pytest -x"
- // Testing in Windows
- // runInDir buildDir "python -m pytest -x"
-
-type RustTestMode =
- | NoStd
- | Default
- | Threaded
-
-let testRust testMode =
- // buildLibraryRustIfNotExists()
- buildLibraryRust()
-
- let testAstDir = "src/Fable.Transforms/Rust/AST/Tests"
- let projectDir = "tests/Rust"
- let buildDir = "build/tests/Rust"
-
- // limited cleanup to reduce IO churn, speed up rebuilds,
- // and save the ssd (target folder can get huge)
- cleanDirs [buildDir > "src"]
- cleanDirs [buildDir > "tests"]
- cleanDirs [buildDir > ".fable"]
-
- // copy rust only tests files (these must be present when running dotnet test as import expr tests for file presence)
- makeDirRecursive (buildDir > "tests" > "src")
- copyFiles (projectDir > "tests/src") "*.rs" (buildDir > "tests/src")
-
- // run .NET tests
- runInDir testAstDir "dotnet test -c Release"
- runInDir projectDir "dotnet test -c Release"
-
- // build Fable Rust tests
- runFableWithArgs projectDir [
- "--outDir " + buildDir
- "--exclude Fable.Core"
- "--lang Rust"
- "--fableLib fable-library-rust"
- "--noCache"
- if testMode = NoStd then "--define NO_STD_NO_EXCEPTIONS"
- ]
-
- // copy project file
- copyFile (projectDir > "Cargo.toml") buildDir
-
- // rustfmt all tests
- runInDir buildDir "cargo fmt"
- // runInDir buildDir "cargo fix --allow-no-vcs"
- runInDir buildDir "cargo build"
-
- // run Fable Rust tests
- match testMode with
- | Default ->
- runInDir buildDir "cargo test"
- | NoStd ->
- runInDir buildDir "cargo test --features no_std"
- | Threaded ->
- runInDir buildDir "cargo test --features threaded"
-
-let testDart isWatch =
- if not (pathExists "build/fable-library-dart") then
- buildLibraryDart(true)
-
- let buildDir = resolveDir "build/tests/Dart"
- let sourceDir = resolveDir "tests/Dart"
-
- cleanDirs [buildDir]
- makeDirRecursive buildDir
- copyFiles sourceDir "pubspec.*" buildDir
- copyFiles sourceDir "analysis_options.yaml" buildDir
- copyFiles sourceDir "*.dart" buildDir
-
- runFableWithArgsInDirAs (not isWatch) sourceDir [
- "src"
- "--outDir " + (buildDir > "src")
- "--exclude Fable.Core"
- "--lang Dart"
- "--noCache"
- if isWatch then
- "--watch"
- $"--runWatch dart test {buildDir}/main.dart"
- ]
- runInDir buildDir "dart test main.dart"
-
-let buildLocalPackageWith pkgDir pkgCommand fsproj action =
- let version = Publish.loadReleaseVersion "src/Fable.Cli" + "-local-build-" + DateTime.Now.ToString("yyyyMMdd-HHmm")
- action version
- updatePkgVersionInFsproj fsproj version
- run $"dotnet pack {fsproj} -p:Pack=true -c Release -o {pkgDir}"
-
- // Return install command
- $"""dotnet {pkgCommand} --version "{version}" --add-source {fullPath pkgDir}"""
-
-let buildLocalPackage pkgDir =
- buildLocalPackageWith pkgDir
- "tool install fable"
- (resolveDir "src/Fable.Cli/Fable.Cli.fsproj") (fun version ->
- updateVersionsInFableTransforms version [] |> ignore
- buildLibraryTs())
-
-let testRepos() =
- let repos = [
- "https://github.com/alfonsogarciacaro/FsToolkit.ErrorHandling:update-fable-3", "npm i && npm test"
- "https://github.com/fable-compiler/fable-promise:master", "npm i && npm test"
- "https://github.com/alfonsogarciacaro/Thoth.Json:nagareyama", "dotnet paket restore && npm i && dotnet fable tests -o tests/bin --run mocha tests/bin"
- "https://github.com/alfonsogarciacaro/FSharp.Control.AsyncSeq:nagareyama", "cd tests/fable && npm i && npm test"
- "https://github.com/alfonsogarciacaro/Fable.Extras:nagareyama", "dotnet paket restore && npm i && npm test"
- "https://github.com/alfonsogarciacaro/Fable.Jester:nagareyama", "npm i && npm test"
- "https://github.com/Zaid-Ajaj/Fable.SimpleJson:master", "npm i && npm run test-nagareyama"
- ]
-
- let testDir = tempPath() > "fable-repos"
- printfn $"Cloning repos to: {testDir}"
-
- cleanDirs [testDir]
- makeDirRecursive testDir
- let pkgInstallCmd = buildLocalPackage (testDir > "pkg")
-
- for (repo, command) in repos do
- let url, branch = let i = repo.LastIndexOf(":") in repo[..i-1], repo[i+1..]
- let name = url[url.LastIndexOf("/") + 1..]
- runInDir testDir $"git clone {url} {name}"
- let repoDir = testDir > name
- runInDir repoDir ("git checkout " + branch)
- runInDir repoDir "dotnet tool uninstall fable"
- runInDir repoDir pkgInstallCmd
- runInDir repoDir "dotnet tool restore"
- runInDir repoDir command
-
-let githubRelease() =
- match envVarOrNone "GITHUB_USER", envVarOrNone "GITHUB_TOKEN" with
- | Some user, Some token ->
- try
- let version, notes = Publish.loadReleaseVersionAndNotes "src/Fable.Cli"
- let notes = notes |> Array.map (fun n -> $"""'{n.Replace("'", @"\'").Replace("`", @"\`")}'""") |> String.concat ","
- run $"git commit -am \"Release {version}\" && git push"
- runSilent $"""node --eval "require('ghreleases').create({{ user: '{user}', token: '{token}', }}, 'fable-compiler', 'Fable', {{ tag_name: '{version}', name: '{version}', body: [{notes}].join('\n'), }}, (err, res) => {{ if (err != null) {{ console.error(err) }} }})" """
- printfn $"Github release %s{version} created successfully"
- with ex ->
- printfn $"Github release failed: %s{ex.Message}"
- | _ -> failwith "Expecting GITHUB_USER and GITHUB_TOKEN enviromental variables"
-
-let copyFcsRepo sourceDir =
- let targetDir = "src/fcs-fable"
- let path1 = "fcs/fcs-fable"
- let path2 = "src/Compiler"
- cleanDirs [targetDir]
- copyDirRecursive (sourceDir > path1) targetDir
- copyDirRecursive (sourceDir > path2) (targetDir > path2)
- removeFile (targetDir > ".gitignore")
- let projPath = (targetDir > "fcs-fable.fsproj")
- let projText = readFile projPath
- let projText =
- Regex.Replace(projText,
- @"(\$\(MSBuildProjectDirectory\)).*?(<\/FSharpSourcesRoot>)",
- "$1/src/Compiler$2")
- // let projText =
- // Regex.Replace(projText,
- // @"artifacts\/bin\/FSharp.Core\/Release\/netstandard2.0",
- // "lib/fcs")
- projText |> writeFile projPath
-
-let syncFcsRepo() =
- // FAKE is giving lots of problems with the dotnet SDK version, ignore it
- let cheatWithDotnetSdkVersion dir f =
- let path = dir > "build.fsx"
- let script = readFile path
- Regex.Replace(script, @"let dotnetExePath =[\s\S]*DotNetCli\.InstallDotNetSDK", "let dotnetExePath = \"dotnet\" //DotNetCli.InstallDotNetSDK") |> writeFile path
- f ()
- runInDir dir "git reset --hard"
-
- printfn $"Expecting %s{FCS_REPO} repo to be cloned at %s{FCS_REPO_LOCAL}"
-
- // TODO: Prompt to reset --hard changes
- // service_slim
- runInDir FCS_REPO_LOCAL ("git checkout " + FCS_REPO_SERVICE_SLIM_BRANCH)
- runInDir FCS_REPO_LOCAL "git pull"
- cheatWithDotnetSdkVersion (FCS_REPO_LOCAL > "fcs") (fun () ->
- runBashOrCmd (FCS_REPO_LOCAL > "fcs") "build" "")
- copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Compiler.Service.dll") "../fable/lib/fcs/"
- copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Compiler.Service.xml") "../fable/lib/fcs/"
- copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Core.dll") "../fable/lib/fcs/"
- copyFile (FCS_REPO_LOCAL > "artifacts/bin/FSharp.Compiler.Service/Release/netstandard2.0/FSharp.Core.xml") "../fable/lib/fcs/"
-
- // fcs-fable
- runInDir FCS_REPO_LOCAL ("git checkout " + FCS_REPO_FABLE_BRANCH)
- runInDir FCS_REPO_LOCAL "git pull"
- cheatWithDotnetSdkVersion (FCS_REPO_LOCAL > "fcs") (fun () ->
- runBashOrCmd (FCS_REPO_LOCAL > "fcs") "build" "CodeGen.Fable")
- copyFcsRepo FCS_REPO_LOCAL
-
-let packages() =
- ["Fable.AST", doNothing
- "Fable.Core", doNothing
- "Fable.Cli", (fun () ->
- // TODO: Add library versions for other languages
- let compilerVersion = Publish.loadReleaseVersion "src/Fable.Cli"
- let updatedLibs = updateVersionsInFableTransforms compilerVersion [
- "js", getNpmVersion "src/fable-library"
- ]
- buildLibraryTs()
- buildLibraryPy()
- buildLibraryRust()
- buildLibraryDart true
- if updatedLibs.Contains("js") then
- pushNpmWithoutReleaseNotesCheck "build/fable-library"
- )
- "Fable.PublishUtils", doNothing
- "fable-metadata", doNothing
- "fable-standalone", fun () -> buildStandalone {|minify=true; watch=false|}
- "fable-compiler-js", fun () -> buildCompilerJs true
- ]
-
-let publishPackages restArgs =
- let packages =
- match List.tryHead restArgs with
- | Some pkg -> packages() |> List.filter (fun (name,_) -> name = pkg)
- | None -> packages()
- for (pkg, buildAction) in packages do
- if Char.IsUpper pkg[0] then
- let projFile = "src" > pkg > pkg + ".fsproj"
- pushFableNuget projFile ["Pack", "true"] buildAction
- else
- pushNpm ("src" > pkg) buildAction
-
-let hasFlag flag =
- BUILD_ARGS_LOWER |> List.contains flag
-
-match BUILD_ARGS_LOWER with
-// | "check-sourcemaps"::_ ->
-// ("src/quicktest/Quicktest.fs", "src/quicktest/bin/Quicktest.js", "src/quicktest/bin/Quicktest.js.map")
-// |||> sprintf "nodemon --watch src/quicktest/bin/Quicktest.js --exec 'source-map-visualization --sm=\"%s;%s;%s\"'"
-// |> List.singleton |> quicktest
-// | "coverage"::_ -> coverage()
-| ("test"|"test-js")::_ -> testJs()
-| "test-mocha"::_ -> compileAndRunTestsWithMocha true "Main"
-| "test-mocha-fast"::_ -> compileAndRunTestsWithMocha false "Main"
-| "test-react"::_ -> testReact()
-| "test-standalone"::_ ->
- let minify = hasFlag "--no-minify" |> not
- testStandalone(minify)
-| "test-standalone-fast"::_ -> testStandaloneFast()
-| "test-configs"::_ -> testProjectConfigs()
-| "test-integration"::_ -> testIntegration()
-| "test-repos"::_ -> testRepos()
-| ("test-ts"|"test-typescript")::_ -> testTypeScript(false)
-| ("watch-test-ts"|"watch-test-typescript")::_ -> testTypeScript(true)
-| "test-py"::_ -> testPython()
-| "test-rust"::_ -> testRust Default
-| "test-rust-no_std"::_ -> testRust NoStd
-| "test-rust-default"::_ -> testRust Default
-| "test-rust-threaded"::_ -> testRust Threaded
-| "test-dart"::_ -> testDart(false)
-| "watch-test-dart"::_ -> testDart(true)
-
-| "quicktest"::_ ->
- buildLibraryTsIfNotExists()
- watchFableWithArgs "src/quicktest" ["--watch --exclude Fable.Core --noCache --runScript"]
-| "quicktest-ts"::_ ->
- buildLibraryTsIfNotExists()
- let srcDir = "src/quicktest"
- let outPath = "build/quicktest-ts/Quicktest.fs.js"
- // Make sure outPath exists so nodemon doesn't complain
- if not(pathExists outPath) then
- makeDirRecursive (dirname outPath)
- writeFile outPath ""
- let runCmd = $"npx concurrently \"tsc -w -p {srcDir} --outDir {dirname outPath}\" \"nodemon -w {outPath} {outPath}\""
- watchFableWithArgs srcDir ["--lang ts --watch --exclude Fable.Core --noCache --run"; runCmd]
-| ("quicktest-py"|"quicktest-python")::_ ->
- buildLibraryPyIfNotExists()
- watchFableWithArgs "src/quicktest-py" ["--lang py --watch --exclude Fable.Core --noCache --runScript"]
-| "quicktest-dart"::_ ->
- buildLibraryDartIfNotExists()
- watchFableWithArgs "src/quicktest-dart" ["--lang dart --watch --exclude Fable.Core --noCache --runScript"]
-| ("quicktest-rs"|"quicktest-rust")::_ ->
- buildLibraryRustIfNotExists()
- watchFableWithArgs "src/quicktest-rust" ["--lang rs -e .rs --watch --exclude Fable.Core --noCache --runScript"]
-| "run"::_ ->
- buildLibraryTsIfNotExists()
- // Don't take args from pattern matching because they're lowered
- let restArgs = BUILD_ARGS |> List.skip 1 |> String.concat " "
- run $"""dotnet run -c Release --project {resolveDir "src/Fable.Cli"} -- {restArgs}"""
-
-| "package"::_ ->
- let pkgInstallCmd = buildLocalPackage (resolveDir "temp/pkg")
- printfn $"\nPackage has been created, use the following command to install it:\n {pkgInstallCmd}\n"
-| "package-core"::_ ->
- let pkgInstallCmd = buildLocalPackageWith (resolveDir "temp/pkg") "add package Fable.Core" (resolveDir "src/Fable.Core/Fable.Core.fsproj") ignore
- printfn $"\nFable.Core package has been created, use the following command to install it:\n {pkgInstallCmd}\n"
-
-| ("fable-library"|"library")::_
-| ("fable-library-ts"|"library-ts")::_ -> buildLibraryTs()
-| ("fable-library-py"|"library-py")::_ -> buildLibraryPy()
-| ("fable-library-rust" | "library-rust")::_ -> buildLibraryRust()
-| ("fable-library-dart" | "library-dart")::_ ->
- let clean = hasFlag "--no-clean" |> not
- buildLibraryDart(clean)
-
-| ("fable-compiler-js"|"compiler-js")::_ ->
- let minify = hasFlag "--no-minify" |> not
- buildCompilerJs(minify)
-| ("fable-standalone"|"standalone")::_ ->
- let minify = hasFlag "--no-minify" |> not
- buildStandalone {|minify=minify; watch=false|}
-| ("fable-worker"|"worker")::_ ->
- let minify = hasFlag "--no-minify" |> not
- buildWorker {|minify=minify; watch=false|}
-| "watch-standalone"::_ -> buildStandalone {|minify=false; watch=true|}
-
-| "sync-fcs-repo"::_ -> syncFcsRepo()
-| "copy-fcs-repo"::_ -> copyFcsRepo FCS_REPO_LOCAL
-
-| "publish"::restArgs -> publishPackages restArgs
-| "github-release"::_ ->
- publishPackages []
- githubRelease ()
-
-| _ ->
- printfn """Please pass a target name. Examples:
-
-- Use `test` to run tests:
- dotnet fsi build.fsx test
-
-- Use `package` to build a local package:
- dotnet fsi build.fsx package
-
-- Use `run` to compile a project with development version:
- dotnet fsi build.fsx run ../path/to/my/project [Fable options]
-
-- Use `quicktest` to quickly test development version with src/quicktest project:
- dotnet fsi build.fsx quicktest
-"""
-
-printfn "Build finished successfully"
+// Cmd.mocha args
+// |> CmdLine.toString
+// )
+// }
+
+
+// pipeline "Standalone" {
+// // Make the pipeline think it is running on CI
+// // This is to activate the condition on the "Standalone" stage
+// envVars ["CI", "true"]
+
+// Stages.testStandalone
+
+// runIfOnlySpecified
+// }
+
+// type TestsPython () =
+// inherit TestsTarget(
+// "python",
+// BuildFableLibraryPython(),
+// Path.Combine("tests", "Python"),
+// Path.Combine("build", "tests", "Python")
+// )
+
+// override this.TestsAgainstTargetStage =
+// stage "Run tests against Python" {
+// workingDir this.BuildDir
+
+// run "poetry run pytest -x"
+// }
+
+// type TestsRust() =
+// inherit TestsTarget(
+// "rust",
+// BuildFableLibraryRust(),
+// Path.Combine("tests", "Rust"),
+// Path.Combine("build", "tests", "Rust")
+// )
+
+// override this.TestsAgainstTargetStage =
+// stage "Run tests against Rust" {
+// workingDir this.BuildDir
+
+// run "cargo test"
+// }
+
+// module Test =
+
+// let python = TestsPython()
+
+// pipeline "test-javascript" {
+// FableLibrary.javaScript.Stage()
+
+// // Test against .NET
+// stage "Run tests against .NET" {
+// workingDir "tests/Js/Main"
+
+// run "dotnet run -c Release"
+// }
+
+// Stages.JavaScript.testWithMocha "Main"
+// Stages.JavaScript.testWithMocha "Adaptive"
+// Stages.JavaScript.testReact
+// Stages.testStandalone
+
+// runIfOnlySpecified
+// }
+
+// pipeline "test-typescript" {
+// FableLibrary.typeScript.Stage()
+
+// // Stages below use a flag to determine which stage should
+// TypeScript.Stages.compileAndTest
+// TypeScript.Stages.compileAndTestInWatchMode
+
+// runIfOnlySpecified
+// }
+
+// pipeline "test-react" {
+// FableLibrary.javaScript.Stage()
+// Stages.JavaScript.testReact
+
+// runIfOnlySpecified
+// }
+
+// // Register the build fable-library pipelines
+// FableLibrary.rust.Pipeline()
+// FableLibrary.python.Pipeline()
+// FableLibrary.dart.Pipeline()
+// FableLibrary.typeScript.Pipeline()
+// FableLibrary.javaScript.Pipeline()
+
+// // Register the tests pipelines
+// Test.python.Pipeline()
+
+// tryPrintPipelineCommandHelp()
+
+
+
+
+open Fli
+
+cli {
+ Shell CMD
+ Command "echo Hello World!"
+}
+|> Command.execute
\ No newline at end of file
diff --git a/build2/FableLibrary/Core.fs b/build2/FableLibrary/Core.fs
new file mode 100644
index 0000000000..47778bac42
--- /dev/null
+++ b/build2/FableLibrary/Core.fs
@@ -0,0 +1,112 @@
+namespace Build.FableLibrary
+
+open Fun.Build
+open BlackFox.CommandLine
+open Fake.IO
+open System.IO
+open Build.Utils
+open Build.Utils
+
+///
+/// Building fable-library is similar enough for all the targets
+/// that we can use this class to standardise the process.
+///
+type BuildFableLibrary
+ (
+ language : string,
+ libraryDir : string,
+ sourceDir : string,
+ buildDir : string,
+ outDir : string,
+ ?fableLibArg : string
+ ) =
+
+ // It seems like the different target have a different way of supporting
+ // --fableLib argument.
+ // For example,
+ // If we provide `--fableLib < out dir path>`, then the Dart target
+ // generates import './Option.dart' as option;
+ // Bt if we provides `--fableLib .`, then the Dart target
+ // generates import '../../src/fable-library-dart/Option.dart' as option;
+ // Python seems to ignore completely the --fableLib argument.
+ // Investigation are required to make all the targets behave the same way.
+ // For now, I am providing a way to override the --fableLib argument in the
+ // build system but it should be removed once the issue is fixed.
+ let fableLibArg = defaultArg fableLibArg "."
+
+ member val LibraryDir = libraryDir
+ member val SourceDir = sourceDir
+ member val BuildDir = buildDir
+ member val OutDir = outDir
+
+ // Allow language to be orverriden from "do constructor"
+ // Useful for JavaScript target which is a specialisation of the TypeScript
+ member val Language = language with get, set
+
+ abstract member FableArgsBuilder : (CmdLine -> CmdLine)
+ default _.FableArgsBuilder = id
+
+ abstract member PostFableBuildStage : Internal.StageContext
+ default _.PostFableBuildStage =
+ stage "Post fable build" {
+ echo "Nothing to do"
+ }
+
+ abstract member CopyStageRunner : Internal.StageContext -> unit
+ default _.CopyStageRunner _ =
+ ()
+
+ member this.Pipeline () =
+ pipeline $"fable-library-{this.Language}" {
+ this.Stage()
+
+ runIfOnlySpecified
+ }
+
+ member this.Stage () : Internal.StageContext =
+
+ stage $"Build fable-library-{this.Language}" {
+ whenCmd {
+ longName "--fast"
+ description $"Skip building fable-library-{this.Language} if the build directory already exists"
+ optional
+ }
+
+ run (fun ctx ->
+ let isFast = ctx.TryGetCmdArg "--fast" |> ValueOption.isSome
+ if isFast && Directory.Exists buildDir then
+ ctx.SoftCancelStage()
+ )
+
+ run (fun _ ->
+ // Make sure to work with a clean build directory
+ if Directory.Exists buildDir then
+ Directory.Delete(buildDir, true)
+ )
+
+ stage "Build library with Fable" {
+ run (fun _ ->
+ let args =
+ CmdLine.appendRaw sourceDir
+ >> CmdLine.appendPrefix "--outDir" outDir
+ >> CmdLine.appendPrefix "--fableLib" fableLibArg
+ >> CmdLine.appendPrefix "--lang" language
+ >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+ >> CmdLine.appendPrefix "--define" "FABLE_LIBRARY"
+ >> CmdLine.appendRaw "--noCache"
+ // Target implementation can require additional arguments
+ >> this.FableArgsBuilder
+
+ // Compile F# files of Fable library to TypeScript
+ Cmd.fable args
+ |> CmdLine.toString
+ )
+ }
+
+ stage "Copy" {
+ run this.CopyStageRunner
+ }
+
+ this.PostFableBuildStage
+
+ }
\ No newline at end of file
diff --git a/build2/FableLibrary/Dart.fs b/build2/FableLibrary/Dart.fs
new file mode 100644
index 0000000000..208b64058e
--- /dev/null
+++ b/build2/FableLibrary/Dart.fs
@@ -0,0 +1,26 @@
+namespace Build.FableLibrary
+
+open System.IO
+open Fun.Build
+open Fake.IO
+
+type BuildFableLibraryDart() =
+ inherit BuildFableLibrary(
+ "dart",
+ Path.Combine("src", "fable-library-dart"),
+ Path.Combine("src", "fable-library-dart"),
+ Path.Combine("build", "fable-library-dart"),
+ Path.Combine("build", "fable-library-dart"),
+ Path.Combine("." , "build", "fable-library-dart")
+ )
+
+ override this.CopyStageRunner _ =
+ Directory.GetFiles(this.SourceDir, "pubspec.*")
+ |> Shell.copyFiles this.BuildDir
+
+ Shell.copyFile
+ this.BuildDir
+ (Path.Combine(this.LibraryDir, "analysis_options.yaml"))
+
+ Directory.GetFiles(this.SourceDir, "*.dart")
+ |> Shell.copyFiles this.OutDir
diff --git a/build2/FableLibrary/JavaScript.fs b/build2/FableLibrary/JavaScript.fs
new file mode 100644
index 0000000000..670cb3894e
--- /dev/null
+++ b/build2/FableLibrary/JavaScript.fs
@@ -0,0 +1,56 @@
+namespace Build.FableLibrary
+
+open System.IO
+open Fun.Build
+open Fake.IO
+open BlackFox.CommandLine
+open Build.Utils
+
+type BuildFableLibraryJavaScript() =
+ // JavaScript is a specialisation of the TypeScript target
+ inherit BuildFableLibraryTypeScript()
+
+ let jsOutDir = Path.Combine("build", "fable-library")
+ do
+ base.Language <- "javascript"
+
+ override this.PostFableBuildStage =
+ // Alias to make it clear which directory is referred to
+ let tsBuildDir = this.BuildDir
+
+ stage "Post Build" {
+
+ // Make sure to work with a clean build directory
+ // We need to delete the directy here because JavaScript is
+ // a bit special compared to other targets.
+ // JavaScript things happens after the Fable.Library to TypeScript compilation
+ run (fun _ ->
+ if Directory.Exists jsOutDir then
+ Directory.Delete(jsOutDir, true)
+ )
+
+ run (fun _ ->
+ // Compile the library to JavaScript using the TypeScript compiler
+ let args =
+ CmdLine.appendPrefix "--project" tsBuildDir
+ >> CmdLine.appendPrefix "--outDir" jsOutDir
+
+ Cmd.tsc args
+ |> CmdLine.toString
+ )
+
+ run (fun _ ->
+ // Copy lib/big.d.ts to the JavaScript build directory
+ // // Copy lib/big.d.ts to the JavaScript build directory
+ let bigDts = Path.Combine(tsBuildDir, "lib", "big.d.ts")
+ Shell.copyFile bigDts jsOutDir
+
+ Shell.copyFile
+ jsOutDir
+ (Path.Combine(tsBuildDir, "package.json"))
+
+ Shell.copyFile
+ jsOutDir
+ (Path.Combine(this.SourceDir, "README.md"))
+ )
+ }
diff --git a/build2/FableLibrary/Python.fs b/build2/FableLibrary/Python.fs
new file mode 100644
index 0000000000..3fb4d80c50
--- /dev/null
+++ b/build2/FableLibrary/Python.fs
@@ -0,0 +1,35 @@
+namespace Build.FableLibrary
+
+open System.IO
+open Fun.Build
+open Fake.IO
+open Build.Utils
+
+type BuildFableLibraryPython() =
+ inherit BuildFableLibrary(
+ "python",
+ Path.Combine("src", "fable-library-py"),
+ Path.Combine("src", "fable-library-py", "fable_library"),
+ Path.Combine("build", "fable-library-py"),
+ Path.Combine("build", "fable-library-py", "fable_library")
+ )
+
+ override this.CopyStageRunner _ =
+ // Copy all *.rs files to the build directory
+ Directory.GetFiles(this.LibraryDir, "*")
+ |> Shell.copyFiles this.BuildDir
+
+ Directory.GetFiles(this.SourceDir, "*.py")
+ |> Shell.copyFiles this.OutDir
+
+ override this.PostFableBuildStage =
+ stage "Post fable build" {
+ run (fun _ ->
+ // Fix issues with Fable .fsproj not supporting links
+ let linkedFileFolder = Path.Combine(this.BuildDir, "fable_library", "fable-library")
+ Directory.GetFiles(linkedFileFolder, "*")
+ |> Shell.copyFiles this.OutDir
+
+ Shell.deleteDir (this.BuildDir > "fable_library/fable-library")
+ )
+ }
diff --git a/build2/FableLibrary/Rust.fs b/build2/FableLibrary/Rust.fs
new file mode 100644
index 0000000000..6fdf071ab8
--- /dev/null
+++ b/build2/FableLibrary/Rust.fs
@@ -0,0 +1,36 @@
+namespace Build.FableLibrary
+
+open System.IO
+open Fun.Build
+open Fake.IO
+
+type BuildFableLibraryRust() =
+ inherit BuildFableLibrary(
+ "rust",
+ Path.Combine("src", "fable-library-rust"),
+ Path.Combine("src", "fable-library-rust", "src"),
+ Path.Combine("build", "fable-library-rust"),
+ Path.Combine("build", "fable-library-rust", "src")
+ )
+
+ override this.PostFableBuildStage =
+ stage "Post fable build" {
+ workingDir this.BuildDir
+ run "cargo fmt"
+ run "cargo fix --allow-no-vcs"
+ run "cargo build"
+ }
+
+ override this.CopyStageRunner _ =
+ // Copy all *.rs files to the build directory
+ Directory.GetFiles(this.SourceDir, "*.rs")
+ |> Shell.copyFiles this.OutDir
+
+ Shell.copyFile
+ this.BuildDir
+ (Path.Combine(this.LibraryDir, "Cargo.toml"))
+
+ Shell.copyDir
+ (Path.Combine(this.BuildDir, "vendored"))
+ (Path.Combine(this.LibraryDir, "vendored"))
+ FileFilter.allFiles
diff --git a/build2/FableLibrary/TypeScript.fs b/build2/FableLibrary/TypeScript.fs
new file mode 100644
index 0000000000..8223b3e2d5
--- /dev/null
+++ b/build2/FableLibrary/TypeScript.fs
@@ -0,0 +1,44 @@
+namespace Build.FableLibrary
+
+open System.IO
+open Fun.Build
+open Fake.IO
+open BlackFox.CommandLine
+
+type BuildFableLibraryTypeScript() =
+ inherit BuildFableLibrary(
+ "typescript",
+ Path.Combine("src", "fable-library"),
+ Path.Combine("src", "fable-library"),
+ Path.Combine("build", "fable-library-ts"),
+ Path.Combine("build", "fable-library-ts"),
+ Path.Combine(".", "build", "fable-library-ts")
+ )
+
+ // TODO add pre stage runner
+
+ override _.FableArgsBuilder =
+ CmdLine.appendPrefix "--typedArrays" "false"
+ >> CmdLine.appendPrefix "--define" "FX_NO_BIGINT"
+
+ override this.CopyStageRunner _ =
+ // Copy all *.ts files to the build directory from source directory
+ Directory.GetFiles(this.SourceDir, "*.ts")
+ |> Shell.copyFiles this.OutDir
+
+ // Copy the tsconfig.json file to the build directory
+ let typeScriptConfig = Path.Combine(this.SourceDir, "ts", "tsconfig.json")
+ Shell.copyFile this.OutDir typeScriptConfig
+
+ // Copy the lib folder to the build directory
+ let libSourceFolder = Path.Combine(this.SourceDir, "lib")
+ let libDestinationFolder = Path.Combine(this.OutDir, "lib")
+ Shell.copyDir libDestinationFolder libSourceFolder FileFilter.allFiles
+
+ // Copy the package.json file to the build directory
+ let packageJson = Path.Combine(this.SourceDir, "package.json")
+ Shell.copyFile this.OutDir packageJson
+
+ // Copy the README.md file to the build directory
+ let readme = Path.Combine(this.SourceDir, "README.md")
+ Shell.copyFile this.OutDir readme
diff --git a/build2/Fun.Build.Extensions.fs b/build2/Fun.Build.Extensions.fs
new file mode 100644
index 0000000000..95edc1b927
--- /dev/null
+++ b/build2/Fun.Build.Extensions.fs
@@ -0,0 +1,20 @@
+module Fun.Build
+
+open Fun.Build
+open Fun.Build.Internal
+open Fun.Build.BuiltinCmdsInternal
+open BlackFox.CommandLine
+
+type StageBuilder with
+
+ /// Add a step to run command. This will not encrypt any sensitive information when print to console.
+ []
+ member inline _.run([] build: BuildStage, command: CmdLine) =
+ BuildStage(fun ctx ->
+ build.Invoke(ctx).AddCommandStep(fun _ ->
+ async {
+ let command = command |> CmdLine.toString
+ return command
+ }
+ )
+ )
diff --git a/build2/Main.fs b/build2/Main.fs
new file mode 100644
index 0000000000..3b6e6424e8
--- /dev/null
+++ b/build2/Main.fs
@@ -0,0 +1,21 @@
+module Build.Main
+
+open Fun.Build
+open Build.FableLibrary
+
+// Register fable-library pipelines
+BuildFableLibraryJavaScript().Pipeline()
+BuildFableLibraryRust().Pipeline()
+BuildFableLibraryDart().Pipeline()
+BuildFableLibraryPython().Pipeline()
+BuildFableLibraryTypeScript().Pipeline()
+BuildFableLibraryJavaScript().Pipeline()
+
+// Register main tests
+JavaScript.Tests.registerPipelines()
+Python.Tests.registerPipelines()
+Rust.Tests.registerPipelines()
+
+// Register quick tests
+
+tryPrintPipelineCommandHelp()
diff --git a/build2/Tests/JavaScript.fs b/build2/Tests/JavaScript.fs
new file mode 100644
index 0000000000..d24f44a198
--- /dev/null
+++ b/build2/Tests/JavaScript.fs
@@ -0,0 +1,233 @@
+module Build.JavaScript.Tests
+
+open Fun.Build
+open Fun.Build.Internal
+open Build.FableLibrary
+open System.IO
+open BlackFox.CommandLine
+open Build.Utils
+
+module private Commands =
+
+ let watch =
+ CmdArg.Create(
+ "-w",
+ "--watch",
+ "Watch for changes and recompile",
+ isOptional = true
+ )
+
+ let isWatch (ctx : StageContext) =
+ ctx.TryGetCmdArg watch
+ |> Option.isSome
+
+module private Stages =
+
+ let testReact (allowWatchMode : bool) =
+ stage "Test react" {
+ workingDir "tests/React"
+
+ stage "Restore" {
+ run "npm install"
+ }
+
+ stage "Processes" {
+ paralle (fun ctx ->
+ allowWatchMode && Commands.isWatch ctx
+ )
+
+ run (fun ctx ->
+ let isWatch = Commands.isWatch ctx
+ let args = CmdLine.appendRaw "--noCache"
+
+ Cmd.fable (args, isWatch)
+ |> CmdLine.toString
+ )
+
+ run (fun ctx ->
+ let isWatch = Commands.isWatch ctx
+
+ Cmd.jest (
+ CmdLine.appendIf isWatch "--watch"
+ )
+ |> CmdLine.toString
+ )
+ }
+ }
+
+ /// Create a mocha tests stage
+ /// Name of the main folder for the tests
+ /// Does the stage supports watch mode
+ ///
+ let createMochaTestStage (folderName : string) (isWatchable : bool) =
+ let sourceDir = Path.Resolve("tests", "Js", folderName)
+ let destinationDir = Path.Resolve("build", "tests", "JavaScript", folderName)
+
+ stage $"Js/{folderName}" {
+ run (fun _ ->
+ if Directory.Exists destinationDir then
+ Directory.Delete(destinationDir, true)
+
+ // Ensure the directory exists, so nodemon can watch it
+ Directory.CreateDirectory(destinationDir) |> ignore
+ )
+
+ // We need a sub-stage to make sure the clean up is done
+ // Before starting long running processes
+ stage "Processes" {
+ paralle (fun ctx ->
+ isWatchable && Commands.isWatch ctx
+ )
+
+ run (fun ctx ->
+ let isWatch =
+ Commands.isWatch ctx
+
+ let args =
+ CmdLine.appendRaw sourceDir
+ >> CmdLine.appendPrefix "--outDir" destinationDir
+ >> CmdLine.appendPrefix "--lang" "javascript"
+ >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+ >> CmdLine.appendRaw "--noCache"
+ >> CmdLine.appendIf isWatch "--watch"
+ // This improve the logs readability in watch mode
+ // by forcing Fable to not write on the same line its progress
+ >> CmdLine.appendIf isWatch "--verbose"
+
+ Cmd.fable(args, isWatch)
+ |> CmdLine.toString
+ )
+
+ run (fun ctx ->
+ let isWatch =
+ Commands.isWatch ctx
+
+ let mochaCommand =
+ Cmd.mocha (
+ CmdLine.appendRaw destinationDir
+ >> CmdLine.appendPrefix "--reporter" "dot"
+ >> CmdLine.appendPrefix "-t" "10000"
+ )
+
+ // Mocha doesn't support watch ESM files
+ // So we use nodemon as the watcher instead
+ let nodemonCommand =
+ CmdLine.concat [
+ Cmd.nodemon (
+ CmdLine.appendPrefix "-e" "js"
+ >> CmdLine.appendPrefix "--watch" destinationDir
+ // Avoid polluting the logs when Fable generates a lot of files
+ >> CmdLine.appendPrefix "--delay" "1s"
+ >> CmdLine.appendRaw "--exec"
+ )
+ mochaCommand
+ ]
+
+ if isWatch then
+ nodemonCommand
+ |> CmdLine.toString
+ else
+ mochaCommand
+ |> CmdLine.toString
+ )
+ }
+ }
+
+ let testMain =
+ createMochaTestStage "Main" true
+
+ let testAdaptive =
+ createMochaTestStage "Adaptive" false
+
+ let testStandalone =
+ let fableJs = Path.Resolve("src", "fable-compiler-js", "src", "app.fs.js")
+ let testProj = Path.Resolve("tests", "Js", "Main", "Fable.Tests.fsproj")
+ let buildDir = Path.Resolve("build", "tests", "JavaScript", "Standalone")
+
+ stage "Standalone" {
+ whenEnvVar "CI"
+
+ run (
+ Cmd.fable (
+ CmdLine.appendRaw "src/fable-standalone/src"
+ >> CmdLine.appendRaw "--noCache"
+ )
+ |> CmdLine.toString
+ )
+
+ run (
+ Cmd.fable (
+ CmdLine.appendRaw "src/fable-compiler-js/src"
+ >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+ >> CmdLine.appendPrefix "--define" "LOCAL_TEST"
+ >> CmdLine.appendRaw "--noCache"
+ )
+ |> CmdLine.toString
+ )
+
+ run (
+ Cmd.node (
+ CmdLine.appendRaw fableJs
+ >> CmdLine.appendRaw testProj
+ >> CmdLine.appendRaw buildDir
+ )
+ |> CmdLine.toString
+ )
+
+ run (
+ Cmd.mocha (
+ CmdLine.appendRaw buildDir
+ >> CmdLine.appendPrefix "--reporter" "dot"
+ >> CmdLine.appendPrefix "-t" "10000"
+ )
+ |> CmdLine.toString
+ )
+ }
+
+let registerPipelines () =
+ pipeline "test-javascript" {
+ BuildFableLibraryJavaScript().Stage()
+ whenCmdArg Commands.watch
+
+ let mainTestsDir = Path.Combine("tests", "Js", "Main")
+
+ stage "Main tests" {
+ paralle Commands.isWatch
+
+ stage "Run tests against .NET" {
+ workingDir mainTestsDir
+
+ run (fun ctx ->
+ let isWatch = Commands.isWatch ctx
+
+ if isWatch then
+ "dotnet watch run -c Release"
+ else
+ "dotnet run -c Release"
+ )
+ }
+
+ // Note: We only support watch mode for the Main tests
+ // otherwise the logs is way too verbose and processes
+ // erase each other making it impossible to read
+ Stages.testMain
+ }
+
+ // The stages below will be run when not in watch mode
+ // Because watch mode will be captured above and
+ // the user will press Ctrl+C to stop it stopping the pipeline too
+ Stages.testReact false
+ Stages.testAdaptive
+ Stages.testStandalone
+
+ runIfOnlySpecified
+ }
+
+ pipeline "test-javascript-react" {
+ BuildFableLibraryJavaScript().Stage()
+ whenCmdArg Commands.watch
+
+ Stages.testReact true
+
+ runIfOnlySpecified
+ }
\ No newline at end of file
diff --git a/build2/Tests/Python.fs b/build2/Tests/Python.fs
new file mode 100644
index 0000000000..80c4b61bea
--- /dev/null
+++ b/build2/Tests/Python.fs
@@ -0,0 +1,89 @@
+module Build.Python.Tests
+
+open Fun.Build
+open Fun.Build.Internal
+open Build.FableLibrary
+open System.IO
+open Build.Utils
+open BlackFox.CommandLine
+
+module private Commands =
+
+ let watch =
+ CmdArg.Create(
+ "-w",
+ "--watch",
+ "Watch for changes and recompile",
+ isOptional = true
+ )
+
+ let isWatch (ctx : StageContext) =
+ ctx.TryGetCmdArg watch
+ |> Option.isSome
+
+let private buildDir = Path.Resolve("build", "tests", "Python")
+let private sourceDir = Path.Resolve("tests", "Python")
+
+let registerPipelines () =
+
+ pipeline $"test-python" {
+ BuildFableLibraryPython().Stage()
+
+ stage "Clean" {
+ run (fun _ ->
+ if Directory.Exists buildDir then
+ Directory.Delete(buildDir, true)
+ Directory.CreateDirectory(buildDir) |> ignore
+ )
+ }
+
+ stage "Build" {
+ run (fun _ ->
+ let args =
+ CmdLine.appendRaw sourceDir
+ >> CmdLine.appendPrefix "--outDir" buildDir
+ >> CmdLine.appendPrefix "--lang" "python"
+ >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+ >> CmdLine.appendRaw "--noCache"
+
+ Cmd.fable args
+ |> CmdLine.toString
+ )
+ }
+
+ stage "Run tests against .NET" {
+ workingDir sourceDir
+
+ run "dotnet test -c Release"
+ }
+
+ stage "Run tests against Python" {
+ workingDir buildDir
+ whenCmdArg Commands.watch
+
+ run (fun ctx ->
+ let isWatch = Commands.isWatch ctx
+
+ let poetryCommand =
+ "poetry run pytest -x"
+
+ if isWatch then
+ Cmd.nodemon (
+ CmdLine.appendPrefix "-e" "py"
+ >> CmdLine.appendPrefix "-w" buildDir
+ // Avoid polluting the logs when Fable generates a lot of files
+ >> CmdLine.appendPrefix "--delay" "1s"
+ >> CmdLine.appendRaw "--exec"
+ // We need to wrap the command in quotes
+ >> CmdLine.appendRaw "\""
+ >> CmdLine.appendRaw poetryCommand
+ >> CmdLine.appendRaw "\""
+ )
+ |> CmdLine.toString
+ else
+ poetryCommand
+ )
+ }
+
+ runIfOnlySpecified
+ }
\ No newline at end of file
diff --git a/build2/Tests/Rust.fs b/build2/Tests/Rust.fs
new file mode 100644
index 0000000000..9bfe30a34c
--- /dev/null
+++ b/build2/Tests/Rust.fs
@@ -0,0 +1,136 @@
+module Build.Rust.Tests
+
+open Fun.Build
+open Fun.Build.Internal
+open Build.FableLibrary
+open System.IO
+open Build.Utils
+open BlackFox.CommandLine
+open Fake.IO
+open Fake.IO.Globbing
+open Fake.IO.Globbing.Operators
+
+module private Commands =
+
+ let watch =
+ CmdArg.Create(
+ "-w",
+ "--watch",
+ "Watch for changes and recompile",
+ isOptional = true
+ )
+
+ let noStd =
+ CmdArg.Create(
+ "-n",
+ "--no-std",
+ "Do not use the Rust standard library",
+ isOptional = true
+ )
+
+ let threaded =
+ CmdArg.Create(
+ "-t",
+ "--threaded",
+ "Use the Rust threaded runtime",
+ isOptional = true
+ )
+
+ let isWatch (ctx : StageContext) =
+ ctx.TryGetCmdArg watch
+ |> Option.isSome
+
+module Stages =
+
+ let testAST =
+ let projectDir = Path.Resolve("src", "Fable.Transforms", "Rust", "AST", "Tests")
+
+ stage "Test AST" {
+ workingDir projectDir
+
+ run "dotnet test -c Release"
+ }
+
+ let mainTests =
+ let testsDestinationDir = Path.Resolve("build", "tests", "Rust")
+ let testsProjectDir = Path.Resolve("tests", "Rust")
+
+ stage "Main tests" {
+
+ stage "Clean up" {
+ run (fun _ ->
+ // limited cleanup to reduce IO churn, speed up rebuilds,
+ // and save the ssd (target folder can get huge)
+ let cleanUp dirName =
+ if Directory.Exists (testsDestinationDir > dirName) then
+ Directory.Delete(testsDestinationDir > dirName, true)
+ Directory.CreateDirectory(testsDestinationDir > dirName) |> ignore
+
+ cleanUp "src"
+ cleanUp "tests"
+ cleanUp ".fable"
+ )
+ }
+
+ stage "Copy required files" {
+ // copy rust only tests files (these must be present when running dotnet test as import expr tests for file presence)
+ run (fun _ ->
+ Directory.CreateDirectory(testsDestinationDir > "tests" > "src") |> ignore
+ Shell.copyFile testsDestinationDir (testsProjectDir > "cargo.toml")
+
+ !! (testsProjectDir > "tests" > "src" > "*.rs")
+ |> Seq.iter (fun file ->
+ let destionation = testsDestinationDir > "tests" > "src"
+ Shell.copyFile destionation file
+ )
+ )
+ }
+
+ stage "Main tests" {
+ stage ".NET" {
+ workingDir testsProjectDir
+
+ run "dotnet test -c Release"
+ }
+
+ run (
+ Cmd.fable (
+ CmdLine.appendRaw testsProjectDir
+ >> CmdLine.appendPrefix "--outDir" testsDestinationDir
+ >> CmdLine.appendPrefix "--exclude" "Fable.Core"
+ >> CmdLine.appendPrefix "--lang" "rust"
+ >> CmdLine.appendPrefix "--fableLib" "fable-library-rust"
+ >> CmdLine.appendRaw "--noCache"
+ // >> CmdLine.appendIf (Commands.isWatch) "--watch"
+ // >> CmdLine.appendIf (Commands.noStd) "--noStd"
+ // >> CmdLine.appendIf (Commands.threaded) "--threaded"
+ )
+ |> CmdLine.toString
+ )
+
+ stage "Rust" {
+ workingDir testsDestinationDir
+
+ // run "cargo fmt"
+ // run "cargo build"
+ run "cargo test"
+ }
+
+
+ }
+
+ }
+
+let registerPipelines () =
+
+ pipeline $"test-rust" {
+ BuildFableLibraryRust().Stage()
+
+ whenCmdArg Commands.noStd
+ whenCmdArg Commands.threaded
+
+ Stages.testAST
+ Stages.mainTests
+
+ runIfOnlySpecified
+ }
\ No newline at end of file
diff --git a/build2/Utils.fs b/build2/Utils.fs
new file mode 100644
index 0000000000..a96841401a
--- /dev/null
+++ b/build2/Utils.fs
@@ -0,0 +1,114 @@
+namespace Build.Utils
+
+open BlackFox.CommandLine
+open System
+open System.IO
+
+[]
+module Operators =
+
+ let (>) (p1: string) (p2: string): string =
+ Path.Combine(p1, p2)
+
+type Path =
+
+ ///
+ /// Resolve a path relative to the repository root
+ ///
+ static member Resolve ([] segments : string array): string =
+ let paths =
+ Array.concat [
+ [| __SOURCE_DIRECTORY__; ".." |]
+ segments
+ ]
+
+ Path.Combine(paths)
+
+type Cmd =
+
+ /// Build a command line to invoke the local Fable build
+ ///
+ ///
+ /// If true then dotnet watch will be use
+ /// If false then dotnet run will be use
+ ///
+ ///
+ /// Returns the command line with the arguments to invoke Fable
+ ///
+ static member fable (?argsBuilder : CmdLine -> CmdLine, ?watchMode : bool) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+ // Use absolute path so we can invoke the command from anywhere
+ let localFableDir = __SOURCE_DIRECTORY__ > ".." > "src" > "Fable.Cli"
+ let watchMode = defaultArg watchMode false
+
+ if watchMode then
+ CmdLine.empty
+ |> CmdLine.appendRaw "dotnet"
+ |> CmdLine.appendRaw "watch"
+ |> CmdLine.appendPrefix "--project" localFableDir
+ |> CmdLine.appendRaw "run"
+ // Without the release mode, Fable stack overflow when compiling the tests
+ |> CmdLine.appendPrefix "-c" "Release"
+ |> CmdLine.appendRaw "--"
+ |> argsBuilder
+ else
+ CmdLine.empty
+ |> CmdLine.appendRaw "dotnet"
+ |> CmdLine.appendRaw "run"
+ |> CmdLine.appendPrefix "-c" "Release"
+ |> CmdLine.appendPrefix "--project" localFableDir
+ |> CmdLine.appendRaw "--"
+ |> argsBuilder
+
+ static member node (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "node"
+ |> argsBuilder
+
+ static member tsc (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "npx"
+ |> CmdLine.appendRaw "tsc"
+ |> argsBuilder
+
+ static member mocha (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "npx"
+ |> CmdLine.appendRaw "mocha"
+ |> argsBuilder
+
+ static member nodemon (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "npx"
+ |> CmdLine.appendRaw "nodemon"
+ |> argsBuilder
+
+ static member dotnet (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "dotnet"
+ |> argsBuilder
+
+ static member jest (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "npx"
+ |> CmdLine.appendRaw "jest"
+ |> argsBuilder
+
+ static member npx (?argsBuilder : CmdLine -> CmdLine) : CmdLine =
+ let argsBuilder = defaultArg argsBuilder id
+
+ CmdLine.empty
+ |> CmdLine.appendRaw "npx"
+ |> argsBuilder
\ No newline at end of file
diff --git a/tests/Js/Main/TailCallTests.fs b/tests/Js/Main/TailCallTests.fs
index 825425d44c..be25f1e726 100644
--- a/tests/Js/Main/TailCallTests.fs
+++ b/tests/Js/Main/TailCallTests.fs
@@ -128,6 +128,10 @@ type Element =
let tests =
testList "TailCalls" [
+ // The tests belows only past in release mode
+ // Remove the compiler directive when
+ // https://github.com/fable-compiler/Fable/issues/3522 is fixed
+ #if RELEASE
testCase "Tailcall works in tail position" <| fun () ->
Issue3301.simple 100000 1 |> equal 100001
@@ -136,6 +140,7 @@ let tests =
testCase "Tailcall works with tuple deconstruction" <| fun () ->
Issue3301.tupleDeconstruction 100000 1 |> equal 100001
+ #endif
testCase "Recursive functions can be tailcall optimized" <| fun () ->
factorial1 1 10 |> equal 3628800