firefly-compiler 0.5.79 → 0.5.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/Builder.ff +31 -39
- package/compiler/Compiler.ff +14 -4
- package/compiler/DevelopMode.ff +406 -0
- package/compiler/Main.ff +73 -53
- package/compiler/ModuleCache.ff +5 -5
- package/core/.firefly/include/package.json +1 -1
- package/core/BuildSystem.ff +82 -11
- package/core/NodeSystem.ff +47 -30
- package/core/Path.ff +7 -2
- package/experimental/proxy/Main.ff +60 -0
- package/experimental/proxy/Runner.ff +11 -0
- package/experimental/proxy/Tcp.ff +162 -0
- package/experimental/random/Superdigit.ff +18 -0
- package/experimental/terrain/Main.ff +40 -0
- package/experimental/terrain/Terrain.ff +97 -0
- package/experimental/terrain/Terrain2.ff +109 -0
- package/fireflysite/Main.ff +0 -1
- package/fireflysite/assets/markdown/reference/statements-and-expressions.md +1 -1
- package/lsp/CompletionHandler.ff +2 -2
- package/output/js/ff/compiler/Builder.mjs +24 -48
- package/output/js/ff/compiler/Builder.mjs.map +7 -11
- package/output/js/ff/compiler/Compiler.mjs +66 -12
- package/output/js/ff/compiler/Compiler.mjs.map +18 -14
- package/output/js/ff/compiler/Dependencies.mjs.map +2 -2
- package/output/js/ff/compiler/DependencyLock.mjs.map +1 -1
- package/output/js/ff/compiler/Deriver.mjs.map +1 -1
- package/output/js/ff/compiler/DevelopMode.mjs +1049 -0
- package/output/js/ff/compiler/DevelopMode.mjs.map +183 -0
- package/output/js/ff/compiler/Dictionaries.mjs.map +1 -1
- package/output/js/ff/compiler/Environment.mjs.map +1 -1
- package/output/js/ff/compiler/Inference.mjs.map +1 -1
- package/output/js/ff/compiler/JsEmitter.mjs.map +1 -1
- package/output/js/ff/compiler/JsImporter.mjs.map +1 -1
- package/output/js/ff/compiler/LspHook.mjs.map +1 -1
- package/output/js/ff/compiler/Main.mjs +256 -105
- package/output/js/ff/compiler/Main.mjs.map +43 -38
- package/output/js/ff/compiler/ModuleCache.mjs +12 -8
- package/output/js/ff/compiler/ModuleCache.mjs.map +4 -3
- package/output/js/ff/compiler/Parser.mjs.map +1 -1
- package/output/js/ff/compiler/Patterns.mjs.map +1 -1
- package/output/js/ff/compiler/Resolver.mjs.map +1 -1
- package/output/js/ff/compiler/SourceMap.mjs.map +1 -1
- package/output/js/ff/compiler/Substitution.mjs.map +1 -1
- package/output/js/ff/compiler/Syntax.mjs.map +1 -1
- package/output/js/ff/compiler/Token.mjs.map +1 -1
- package/output/js/ff/compiler/Tokenizer.mjs.map +1 -1
- package/output/js/ff/compiler/Unification.mjs.map +1 -1
- package/output/js/ff/compiler/Wildcards.mjs.map +1 -1
- package/output/js/ff/compiler/Workspace.mjs.map +1 -1
- package/output/js/ff/core/Any.mjs.map +1 -1
- package/output/js/ff/core/Array.mjs.map +1 -1
- package/output/js/ff/core/AssetSystem.mjs.map +1 -1
- package/output/js/ff/core/Atomic.mjs.map +1 -1
- package/output/js/ff/core/Bool.mjs.map +1 -1
- package/output/js/ff/core/BrowserSystem.mjs.map +1 -1
- package/output/js/ff/core/Buffer.mjs.map +1 -1
- package/output/js/ff/core/BuildSystem.mjs +116 -12
- package/output/js/ff/core/BuildSystem.mjs.map +35 -6
- package/output/js/ff/core/Channel.mjs.map +1 -1
- package/output/js/ff/core/Char.mjs.map +1 -1
- package/output/js/ff/core/Core.mjs.map +1 -1
- package/output/js/ff/core/Crypto.mjs.map +1 -1
- package/output/js/ff/core/Date.mjs.map +1 -1
- package/output/js/ff/core/Duration.mjs.map +1 -1
- package/output/js/ff/core/Equal.mjs.map +1 -1
- package/output/js/ff/core/Error.mjs.map +1 -1
- package/output/js/ff/core/FileHandle.mjs.map +1 -1
- package/output/js/ff/core/Float.mjs.map +1 -1
- package/output/js/ff/core/HttpClient.mjs.map +1 -1
- package/output/js/ff/core/Int.mjs.map +1 -1
- package/output/js/ff/core/IntMap.mjs.map +1 -1
- package/output/js/ff/core/Js.mjs.map +1 -1
- package/output/js/ff/core/JsSystem.mjs.map +1 -1
- package/output/js/ff/core/JsValue.mjs.map +1 -1
- package/output/js/ff/core/Json.mjs.map +1 -1
- package/output/js/ff/core/List.mjs.map +1 -1
- package/output/js/ff/core/Lock.mjs.map +1 -1
- package/output/js/ff/core/Log.mjs.map +1 -1
- package/output/js/ff/core/Map.mjs.map +1 -1
- package/output/js/ff/core/NodeSystem.mjs +54 -20
- package/output/js/ff/core/NodeSystem.mjs.map +14 -6
- package/output/js/ff/core/Nothing.mjs.map +1 -1
- package/output/js/ff/core/Option.mjs.map +1 -1
- package/output/js/ff/core/Ordering.mjs.map +1 -1
- package/output/js/ff/core/Pair.mjs.map +1 -1
- package/output/js/ff/core/Path.mjs +30 -4
- package/output/js/ff/core/Path.mjs.map +8 -6
- package/output/js/ff/core/Queue.mjs.map +1 -1
- package/output/js/ff/core/Random.mjs.map +1 -1
- package/output/js/ff/core/RbMap.mjs.map +1 -1
- package/output/js/ff/core/Serializable.mjs.map +1 -1
- package/output/js/ff/core/Set.mjs.map +1 -1
- package/output/js/ff/core/Show.mjs.map +1 -1
- package/output/js/ff/core/SourceLocation.mjs.map +1 -1
- package/output/js/ff/core/Stream.mjs.map +1 -1
- package/output/js/ff/core/String.mjs.map +1 -1
- package/output/js/ff/core/StringMap.mjs.map +1 -1
- package/output/js/ff/core/Task.mjs.map +1 -1
- package/output/js/ff/core/Try.mjs.map +1 -1
- package/output/js/ff/core/Unit.mjs.map +1 -1
- package/output/js/ff/core/node_modules +1 -0
- package/package.json +1 -1
- package/vscode/package.json +1 -1
- package/webserver/.firefly/include/package.json +1 -1
- package/webserver/WebServer.ff +6 -3
package/compiler/Builder.ff
CHANGED
|
@@ -16,54 +16,40 @@ build(
|
|
|
16
16
|
mainModules: List[ModuleKey]
|
|
17
17
|
resolvedDependencies: ResolvedDependencies
|
|
18
18
|
compilerModulePath: Option[Path]
|
|
19
|
-
tempPath: Path
|
|
20
19
|
jsOutputPath: Path
|
|
21
20
|
printMeasurements: Bool
|
|
22
21
|
moduleCache: ModuleCache
|
|
23
22
|
): Unit {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
mainModules.each {moduleKey => compiler.emit(moduleKey, isMainModule = True)}
|
|
43
|
-
if(printMeasurements) {compiler.printMeasurements()}
|
|
44
|
-
resolvedDependencies.packagePaths.each {packagePair, packagePath =>
|
|
45
|
-
resolvedDependencies.packages.get(packagePair).each {packageInfo =>
|
|
46
|
-
processNodeModules(system, jsPathFile, packagePath, packageInfo)
|
|
47
|
-
if(emitTarget != EmitBrowser) {
|
|
48
|
-
processIncludes(jsPathFile, packagePath, packageInfo)
|
|
49
|
-
}
|
|
23
|
+
jsOutputPath.createDirectory(createParentDirectories = True)
|
|
24
|
+
let compiler = Compiler.new(
|
|
25
|
+
emitTarget
|
|
26
|
+
system.mainTask()
|
|
27
|
+
compilerModulePath
|
|
28
|
+
jsOutputPath
|
|
29
|
+
resolvedDependencies
|
|
30
|
+
Map.new()
|
|
31
|
+
moduleCache
|
|
32
|
+
lspHook = LspHook.disabled()
|
|
33
|
+
)
|
|
34
|
+
mainModules.each {moduleKey => compiler.emit(moduleKey, isMainModule = True)}
|
|
35
|
+
if(printMeasurements) {compiler.printMeasurementsPerPhase()}
|
|
36
|
+
resolvedDependencies.packagePaths.each {packagePair, packagePath =>
|
|
37
|
+
resolvedDependencies.packages.get(packagePair).each {packageInfo =>
|
|
38
|
+
processNodeModules(system, jsOutputPath, packagePath, packageInfo)
|
|
39
|
+
if(emitTarget != EmitBrowser) {
|
|
40
|
+
processIncludes(jsOutputPath, packagePath, packageInfo)
|
|
50
41
|
}
|
|
51
42
|
}
|
|
52
|
-
True
|
|
53
43
|
}
|
|
54
|
-
|
|
55
|
-
if(success) {
|
|
56
|
-
if(jsOutputPath.exists()) {jsOutputPath.delete()}
|
|
57
|
-
jsPathFile.renameTo(jsOutputPath)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
processIncludes(jsPathFile: Path, packagePath: Path, info: PackageInfo) {
|
|
63
47
|
info.includes.each {include =>
|
|
64
48
|
let fromPath = packagePath.slash(".firefly").slash("include").slash(include.path)
|
|
65
49
|
let toPath = jsPathFile.slash(info.package.packagePair.groupName("/")).slash(include.path)
|
|
66
|
-
toPath.
|
|
50
|
+
if(!toPath.exists()) {
|
|
51
|
+
toPath.createSymlinkTo(fromPath, junction = True)
|
|
52
|
+
}
|
|
67
53
|
}
|
|
68
54
|
}
|
|
69
55
|
|
|
@@ -86,7 +72,14 @@ processNodeModules(system: NodeSystem, jsPathFile: Path, packagePath: Path, info
|
|
|
86
72
|
}
|
|
87
73
|
}
|
|
88
74
|
|
|
89
|
-
buildViaBuildSystem(
|
|
75
|
+
buildViaBuildSystem(
|
|
76
|
+
system: NodeSystem
|
|
77
|
+
fireflyPath: Path
|
|
78
|
+
mainFiles: List[Path]
|
|
79
|
+
target: String
|
|
80
|
+
moduleCache: ModuleCache = ModuleCache.new(0)
|
|
81
|
+
printMeasurements: Bool = False
|
|
82
|
+
) {
|
|
90
83
|
let resolvedDependencies = Dependencies.process(
|
|
91
84
|
system.httpClient()
|
|
92
85
|
DependencyLock.new(system.mainTask())
|
|
@@ -115,10 +108,9 @@ buildViaBuildSystem(system: NodeSystem, fireflyPath: Path, mainFiles: List[Path]
|
|
|
115
108
|
mainModules = mainModuleKeys
|
|
116
109
|
resolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
|
|
117
110
|
compilerModulePath = None
|
|
118
|
-
tempPath = system.path(".firefly/temporary")
|
|
119
111
|
jsOutputPath = system.path(".firefly/output").slash(target)
|
|
120
|
-
printMeasurements =
|
|
121
|
-
moduleCache =
|
|
112
|
+
printMeasurements = printMeasurements
|
|
113
|
+
moduleCache = moduleCache
|
|
122
114
|
)
|
|
123
115
|
}
|
|
124
116
|
|
package/compiler/Compiler.ff
CHANGED
|
@@ -132,6 +132,16 @@ extend self: Compiler {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
printMeasurementsPerPhase(): Unit {
|
|
136
|
+
self.phaseDurations.toList().map {
|
|
137
|
+
| Pair(name, d) {name.split(' ') | [phase, module]} => Pair(phase, d)
|
|
138
|
+
| Pair(name, d) => Pair("Unknown", d)
|
|
139
|
+
}.group().each {phase, durations =>
|
|
140
|
+
let d = Duration(durations.map {_.seconds}.foldLeft(0.0, {_ + _}))
|
|
141
|
+
Log.debug(phase + " (" + durations.size() + "):\t" + d.show())
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
135
145
|
parse(moduleKey: ModuleKey, importedAt: Option[Location]): Module {
|
|
136
146
|
self.cache.cacheParsedModule(self.packagePaths, moduleKey): path =>
|
|
137
147
|
self.measure("Parse", moduleKey):
|
|
@@ -214,21 +224,21 @@ extend self: Compiler {
|
|
|
214
224
|
self.infer(i.moduleKey)
|
|
215
225
|
}
|
|
216
226
|
|
|
227
|
+
let packagePath = self.jsOutputPath.slash(moduleKey.packagePair.group).slash(moduleKey.packagePair.name)
|
|
228
|
+
let jsPath = moduleKey.folders.foldLeft(packagePath) {p, f => p.slash(f)}
|
|
217
229
|
let allModules = [module, ...otherModules]
|
|
218
230
|
let emitter = JsEmitter.new(
|
|
219
231
|
otherModules = allModules
|
|
220
232
|
emitTarget = self.emitTarget
|
|
221
233
|
isMainModule = isMainModule
|
|
222
|
-
compilerModuleFileUrl = self.compilerModulePath.map {_.
|
|
234
|
+
compilerModuleFileUrl = self.compilerModulePath.map {_.relativeUrlTo(jsPath)}
|
|
223
235
|
moduleKey = moduleKey
|
|
224
236
|
)
|
|
225
237
|
emitter.emitModule(module)
|
|
226
|
-
let packagePath = self.jsOutputPath.slash(moduleKey.packagePair.group).slash(moduleKey.packagePair.name)
|
|
227
|
-
let jsPath = moduleKey.folders.foldLeft(packagePath) {p, f => p.slash(f)}
|
|
228
238
|
let jsFile = jsPath.slash(moduleKey.name + ".mjs")
|
|
229
239
|
let sourceMapFile = jsPath.slash(moduleKey.name + ".mjs.map")
|
|
230
240
|
let source = Some(path.readText())
|
|
231
|
-
let jsAndSourceMap = emitter.makeOutputAndSourceMap(path.
|
|
241
|
+
let jsAndSourceMap = emitter.makeOutputAndSourceMap(path.relativeUrlTo(jsPath), source)
|
|
232
242
|
jsPath.createDirectory(createParentDirectories = True)
|
|
233
243
|
jsFile.writeText(jsAndSourceMap.first + "\n\n//# sourceMappingURL=" + sourceMapFile.base())
|
|
234
244
|
sourceMapFile.writeText(jsAndSourceMap.second.write(Some(" ")))
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import Main
|
|
2
|
+
import ModuleCache
|
|
3
|
+
import Dependencies
|
|
4
|
+
import DependencyLock
|
|
5
|
+
import Syntax
|
|
6
|
+
import JsEmitter
|
|
7
|
+
import Builder from ff:compiler
|
|
8
|
+
|
|
9
|
+
capability Runner(
|
|
10
|
+
lock: Lock
|
|
11
|
+
lockCondition: LockCondition
|
|
12
|
+
mutable iteration: Int
|
|
13
|
+
mutable state: RunnerState
|
|
14
|
+
mutable changedSinceCompilationStarted: Set[String]
|
|
15
|
+
mutable recompile: Bool
|
|
16
|
+
mutable appRunning: Bool
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
data RunnerState {
|
|
20
|
+
CompilingState
|
|
21
|
+
CompileErrorState(at: Option[Location], output: String)
|
|
22
|
+
ApplicationRunningState
|
|
23
|
+
ApplicationCrashedState(output: String)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class EsbuildContext(mutable jsValue: Option[JsValue])
|
|
27
|
+
|
|
28
|
+
run(system: NodeSystem, fireflyPath: Path, mainFile: String, arguments: List[String]) {
|
|
29
|
+
let esbuildContext = EsbuildContext(None)
|
|
30
|
+
let lock = system.mainTask().lock()
|
|
31
|
+
let lockCondition = lock.condition()
|
|
32
|
+
let runner = Runner(lock, lockCondition, 1, CompilingState, Set.new(), False, False)
|
|
33
|
+
startProxy(system, runner, 8081, 8080)
|
|
34
|
+
startChangeListener(system, runner, system.path("."))
|
|
35
|
+
let moduleCache = ModuleCache.new(0)
|
|
36
|
+
while {True} {
|
|
37
|
+
let moduleKey = build(system, runner, mainFile, moduleCache)
|
|
38
|
+
let task = moduleKey.{
|
|
39
|
+
| None =>
|
|
40
|
+
system.mainTask().spawn {_ => }
|
|
41
|
+
| Some(key) =>
|
|
42
|
+
runner.lock.do {
|
|
43
|
+
runner.state = ApplicationRunningState
|
|
44
|
+
}
|
|
45
|
+
startApp(system, runner, fireflyPath, esbuildContext, moduleCache, key, mainFile, arguments)
|
|
46
|
+
}
|
|
47
|
+
runner.lock.do {
|
|
48
|
+
if(runner.appRunning) {
|
|
49
|
+
while {runner.appRunning} {
|
|
50
|
+
runner.lockCondition.sleep()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
while {!runner.recompile} {
|
|
54
|
+
runner.lockCondition.sleep()
|
|
55
|
+
}
|
|
56
|
+
task.abort()
|
|
57
|
+
runner.changedSinceCompilationStarted.each {key =>
|
|
58
|
+
moduleCache.invalidate(key)
|
|
59
|
+
}
|
|
60
|
+
runner.state = CompilingState
|
|
61
|
+
runner.recompile = False
|
|
62
|
+
runner.changedSinceCompilationStarted = Set.new()
|
|
63
|
+
runner.iteration += 1
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
build(system: NodeSystem, runner: Runner, mainFile: String, moduleCache: ModuleCache): Option[ModuleKey] {
|
|
69
|
+
try {
|
|
70
|
+
Main.prepareFireflyDirectory(system.path("."))
|
|
71
|
+
let resolvedDependencies = Dependencies.process(
|
|
72
|
+
system.httpClient()
|
|
73
|
+
DependencyLock.new(system.mainTask())
|
|
74
|
+
system.path(mainFile)
|
|
75
|
+
)
|
|
76
|
+
let mainPath = system.path(mainFile)
|
|
77
|
+
Some(Main.buildScript(
|
|
78
|
+
system
|
|
79
|
+
mainPath
|
|
80
|
+
resolvedDependencies.mainPackagePair
|
|
81
|
+
EmitNode
|
|
82
|
+
resolvedDependencies
|
|
83
|
+
moduleCache
|
|
84
|
+
printMeasurements = False
|
|
85
|
+
))
|
|
86
|
+
} tryCatch {| CompileError(at, message), error =>
|
|
87
|
+
Log.debug(message)
|
|
88
|
+
Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
|
|
89
|
+
runner.lock.do {
|
|
90
|
+
runner.state = CompileErrorState(Some(at), message)
|
|
91
|
+
}
|
|
92
|
+
None
|
|
93
|
+
} tryCatch {| CompileErrors(compileErrors), error =>
|
|
94
|
+
compileErrors.each {| CompileError(at, message) =>
|
|
95
|
+
Log.debug(message)
|
|
96
|
+
Log.debug(" at " + at.file.replace("./", "") + ":" + at.line + ":" + at.column)
|
|
97
|
+
runner.lock.do {
|
|
98
|
+
runner.state = CompileErrorState(Some(at), message)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
None
|
|
102
|
+
} catchAny {error =>
|
|
103
|
+
Log.debug(error.message())
|
|
104
|
+
runner.lock.do {
|
|
105
|
+
runner.state = CompileErrorState(None, error.message())
|
|
106
|
+
}
|
|
107
|
+
None
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
startApp(
|
|
112
|
+
system: NodeSystem
|
|
113
|
+
runner: Runner
|
|
114
|
+
fireflyPath: Path
|
|
115
|
+
esbuildContext: EsbuildContext
|
|
116
|
+
moduleCache: ModuleCache
|
|
117
|
+
moduleKey: ModuleKey
|
|
118
|
+
mainFile: String
|
|
119
|
+
arguments: List[String]
|
|
120
|
+
): Task {
|
|
121
|
+
let taskIteration = runner.iteration
|
|
122
|
+
system.mainTask().spawn {task =>
|
|
123
|
+
try {
|
|
124
|
+
let runFile = Main.locateRunFile(system, "node", moduleKey)
|
|
125
|
+
let runFilePath = if(runFile.contains("://")) {system.pathFromUrl(runFile)} else {system.path(runFile)}
|
|
126
|
+
let startPath = runFilePath.parent().grab().slash(runFilePath.base() + ".start.mjs")
|
|
127
|
+
startPath.writeText(
|
|
128
|
+
"import * as run from " + Json.string("./" + runFilePath.base()).write() + "\n" +
|
|
129
|
+
"globalThis.ffDevelopMode = true\n" +
|
|
130
|
+
// The following should be awaited, but esbuild doesn't support top level await - still seems to work though
|
|
131
|
+
"run.$run$(" + Json.string(fireflyPath.absolute()).write() + ", " + Json.toJson(arguments).write() + ")"
|
|
132
|
+
)
|
|
133
|
+
let esBuildPath = runFilePath.parent().grab().slash(runFilePath.base() + ".minified.js")
|
|
134
|
+
let context = esbuildContext.jsValue.else {
|
|
135
|
+
let jsValue = BuildSystem.internalNodeCallEsBuildContext(system, startPath.absolute(), esBuildPath.absolute(), minify = True)
|
|
136
|
+
esbuildContext.jsValue = Some(jsValue)
|
|
137
|
+
jsValue
|
|
138
|
+
}
|
|
139
|
+
Js.await(context->rebuild())
|
|
140
|
+
let relativeStartFile = esBuildPath.relativeTo(system.path("."))
|
|
141
|
+
let result = system.execute(relativeStartFile, arguments, node = Some({message, forkedProcess =>
|
|
142
|
+
if(message->ffDevelopMode === "internalCompile") {
|
|
143
|
+
let mainFiles: List[String] = message->mainFiles?
|
|
144
|
+
let mainPaths = mainFiles.map {system.path(_)}
|
|
145
|
+
let target: String = message->target?
|
|
146
|
+
runner.lock.do {
|
|
147
|
+
if(taskIteration == runner.iteration):
|
|
148
|
+
try {
|
|
149
|
+
Builder.buildViaBuildSystem(system, fireflyPath, mainPaths, target, moduleCache, printMeasurements = False)
|
|
150
|
+
forkedProcess->send(Js->(ffDevelopMode = "internalCompile"))
|
|
151
|
+
Unit
|
|
152
|
+
} tryCatch {| CompileError(at, message), error =>
|
|
153
|
+
runner.state = CompileErrorState(Some(at), message)
|
|
154
|
+
} tryCatch {| CompileErrors(compileErrors), error =>
|
|
155
|
+
compileErrors.each {| CompileError(at, message) =>
|
|
156
|
+
runner.state = CompileErrorState(Some(at), message)
|
|
157
|
+
}
|
|
158
|
+
} catchAny {error =>
|
|
159
|
+
runner.state = CompileErrorState(None, error.message())
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}))
|
|
164
|
+
let standardOut = result.standardOut.toString()
|
|
165
|
+
let standardError = result.standardError.toString()
|
|
166
|
+
runner.lock.do {
|
|
167
|
+
runner.appRunning = False
|
|
168
|
+
Log.debug("Exited with code: " + result.exitCode + "\n\n" + standardOut + "\n\n" + standardError)
|
|
169
|
+
runner.state.{
|
|
170
|
+
| ApplicationRunningState {taskIteration == runner.iteration} =>
|
|
171
|
+
runner.state = ApplicationCrashedState(
|
|
172
|
+
"Exited with code: " + result.exitCode + "\n\n" + standardOut + "\n\n" + standardError
|
|
173
|
+
)
|
|
174
|
+
| _ =>
|
|
175
|
+
}
|
|
176
|
+
runner.lockCondition.wakeAll()
|
|
177
|
+
}
|
|
178
|
+
} catchAny {error =>
|
|
179
|
+
runner.lock.do {
|
|
180
|
+
runner.appRunning = False
|
|
181
|
+
runner.lockCondition.wakeAll()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
startChangeListener(
|
|
188
|
+
system: NodeSystem
|
|
189
|
+
runner: Runner
|
|
190
|
+
path: Path
|
|
191
|
+
) {
|
|
192
|
+
let fs = Js.import("node:fs")
|
|
193
|
+
fs->watch(path.absolute(), Js->(recursive = True), Js->{eventType, fileName =>
|
|
194
|
+
if(!fileName.isNullOrUndefined()):
|
|
195
|
+
let file: String = fileName?
|
|
196
|
+
if(file.endsWith(".ff") || file.endsWith(".firefly-workspace")) {
|
|
197
|
+
let key = system.path(file).absolute()
|
|
198
|
+
runner.lock.do {
|
|
199
|
+
// Probably we should also listen for other files, e.g. resources
|
|
200
|
+
runner.changedSinceCompilationStarted = runner.changedSinceCompilationStarted.add(key)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
startProxy(
|
|
207
|
+
system: NodeSystem
|
|
208
|
+
runner: Runner
|
|
209
|
+
proxyPort: Int
|
|
210
|
+
targetPort: Int
|
|
211
|
+
) {
|
|
212
|
+
let net = Js.import("node:net")
|
|
213
|
+
let targetServer = "localhost" // 127.0.0.1
|
|
214
|
+
let proxyServer = net->createServer(Js->(pauseOnConnect = True), Js->{clientSocket =>
|
|
215
|
+
mutable targetSocket = Js.undefined()
|
|
216
|
+
mutable connected = False
|
|
217
|
+
clientSocket->on("error", Js->{err =>
|
|
218
|
+
if(!targetSocket.isUndefined()) {targetSocket->end()}
|
|
219
|
+
Log.debugDynamic(err)
|
|
220
|
+
})
|
|
221
|
+
function serveWaiterHtml() {
|
|
222
|
+
if(runner.changedSinceCompilationStarted.size() != 0) {
|
|
223
|
+
runner.recompile = True
|
|
224
|
+
system.mainTask().spawn {task =>
|
|
225
|
+
runner.lock.do {
|
|
226
|
+
runner.lockCondition.wakeAll()
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function escapeHtml(html: String): String {
|
|
231
|
+
html.replace("&", "&")
|
|
232
|
+
.replace("'", "'")
|
|
233
|
+
.replace("\"", """)
|
|
234
|
+
.replace("<", "<")
|
|
235
|
+
.replace(">", ">")
|
|
236
|
+
}
|
|
237
|
+
let status = runner.state.{
|
|
238
|
+
| _ {runner.recompile} => "Restarting..."
|
|
239
|
+
| ApplicationCrashedState(output) => "Application crashed!<br>" + escapeHtml(output)
|
|
240
|
+
| ApplicationRunningState => "Starting application..."
|
|
241
|
+
| CompileErrorState(Some(at), output) =>
|
|
242
|
+
let location = escapeHtml(at.file + ":" + at.line + ":" + at.column)
|
|
243
|
+
let relativeFile = system.path(at.file).relativeTo(system.path("."))
|
|
244
|
+
let relativeLocation = escapeHtml(relativeFile + ":" + at.line + ":" + at.column)
|
|
245
|
+
let link = "<a href='vscode://file/" + escapeHtml(location) + "'>" + relativeLocation + "</a>"
|
|
246
|
+
escapeHtml(output) + "<br><br>at " + link
|
|
247
|
+
| CompileErrorState(None, output) => "Compiler crashed!<br>" + escapeHtml(output)
|
|
248
|
+
| CompilingState => "Compiling..."
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let waiterBuffer = waiterHtml.replace("[STATUS]", status).toBuffer()
|
|
252
|
+
clientSocket->write("HTTP/1.1 200 OK\r\n")
|
|
253
|
+
clientSocket->write("Content-Type: text/html\r\n")
|
|
254
|
+
clientSocket->write("Content-Length: " + waiterBuffer.size() + "\r\n")
|
|
255
|
+
clientSocket->write("x-firefly-develop-mode: true\r\n")
|
|
256
|
+
clientSocket->write("Connection: close\r\n")
|
|
257
|
+
clientSocket->write("\r\n")
|
|
258
|
+
clientSocket->write(
|
|
259
|
+
Js->Buffer->from(waiterBuffer!->buffer, waiterBuffer!->byteOffset, waiterBuffer!->byteLength)
|
|
260
|
+
)
|
|
261
|
+
clientSocket->end()
|
|
262
|
+
}
|
|
263
|
+
targetSocket = net->createConnection(targetPort, targetServer, Js->{
|
|
264
|
+
connected = True
|
|
265
|
+
let direct = runner.state.{
|
|
266
|
+
| ApplicationRunningState => !runner.recompile && runner.changedSinceCompilationStarted.size() == 0
|
|
267
|
+
| _ => False
|
|
268
|
+
}
|
|
269
|
+
if(direct) {
|
|
270
|
+
clientSocket->pipe(targetSocket)->pipe(clientSocket)
|
|
271
|
+
clientSocket->resume()?
|
|
272
|
+
} else {
|
|
273
|
+
serveWaiterHtml()
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
targetSocket->on("error", Js->{err =>
|
|
277
|
+
if(connected) {
|
|
278
|
+
clientSocket->end()?
|
|
279
|
+
} else {
|
|
280
|
+
serveWaiterHtml()
|
|
281
|
+
}
|
|
282
|
+
})?
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
proxyServer->listen(proxyPort, Js->{
|
|
286
|
+
//print(system, "Proxy server running on port " + proxyPort)
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
waiterHtml = """<!DOCTYPE html>
|
|
291
|
+
<html lang="en">
|
|
292
|
+
<head>
|
|
293
|
+
<meta charset="UTF-8">
|
|
294
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
295
|
+
<title>Firefly develop mode</title>
|
|
296
|
+
<style>
|
|
297
|
+
body {
|
|
298
|
+
background-color: #121212;
|
|
299
|
+
color: #e0e0e0;
|
|
300
|
+
font-family: 'Courier New', monospace;
|
|
301
|
+
display: flex;
|
|
302
|
+
flex-direction: column;
|
|
303
|
+
justify-content: center;
|
|
304
|
+
align-items: center;
|
|
305
|
+
height: 100vh;
|
|
306
|
+
margin: 0;
|
|
307
|
+
background-image: radial-gradient(circle at center, #1a1a2e, #121212);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
h1 {
|
|
311
|
+
color: #00f2ff;
|
|
312
|
+
text-shadow: 0 0 10px #00f2ff;
|
|
313
|
+
font-size: 3rem;
|
|
314
|
+
margin-bottom: 20px;
|
|
315
|
+
animation: glow 2s ease-in-out infinite alternate;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
p {
|
|
319
|
+
background-color: #1a1a2e;
|
|
320
|
+
color: #00f2ff;
|
|
321
|
+
padding: 15px 30px;
|
|
322
|
+
border-radius: 25px;
|
|
323
|
+
font-size: 1.2rem;
|
|
324
|
+
border: 2px solid #00f2ff;
|
|
325
|
+
box-shadow: 0 0 15px #00f2ff;
|
|
326
|
+
animation: pulse 1.5s infinite;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
a {
|
|
330
|
+
color: #00f2ff;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@media only screen and (max-width: 600px) {
|
|
334
|
+
h1 {
|
|
335
|
+
font-size: 1.7rem;
|
|
336
|
+
}
|
|
337
|
+
p {
|
|
338
|
+
padding: 10px 20px;
|
|
339
|
+
border-radius: 50px;
|
|
340
|
+
font-size: 1.1rem;
|
|
341
|
+
max-width: 100%;
|
|
342
|
+
box-sizing: border-box;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@keyframes glow {
|
|
347
|
+
from {
|
|
348
|
+
text-shadow: 0 0 10px #00f2ff;
|
|
349
|
+
}
|
|
350
|
+
to {
|
|
351
|
+
text-shadow: 0 0 20px #00f2ff, 0 0 30px #00f2ff;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@keyframes pulse {
|
|
356
|
+
0% {
|
|
357
|
+
box-shadow: 0 0 0 0 rgba(0, 242, 255, 0.7);
|
|
358
|
+
}
|
|
359
|
+
70% {
|
|
360
|
+
box-shadow: 0 0 0 10px rgba(0, 242, 255, 0);
|
|
361
|
+
}
|
|
362
|
+
100% {
|
|
363
|
+
box-shadow: 0 0 0 0 rgba(0, 242, 255, 0);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
</style>
|
|
367
|
+
<script>
|
|
368
|
+
let start = Date.now()
|
|
369
|
+
let appStarted = null
|
|
370
|
+
let delay = 10
|
|
371
|
+
let poll = async () => {
|
|
372
|
+
//delay *= 1.1
|
|
373
|
+
try {
|
|
374
|
+
let response = await fetch(".", {cache: 'no-store'})
|
|
375
|
+
if(!response.headers.has('x-firefly-develop-mode')) {
|
|
376
|
+
let now = Date.now()
|
|
377
|
+
let compiling = appStarted - start
|
|
378
|
+
let appStarting = now - appStarted
|
|
379
|
+
//window.alert("Reloading after: " + (now - start) + " ms. Compiling: " + compiling + " ms. Starting application: " + appStarting + " ms.")
|
|
380
|
+
window.location.reload(true)
|
|
381
|
+
return
|
|
382
|
+
} else {
|
|
383
|
+
let html = await response.text()
|
|
384
|
+
if(appStarted == null && html.includes("Starting application...")) appStarted = Date.now()
|
|
385
|
+
let parser = new DOMParser()
|
|
386
|
+
let d = parser.parseFromString(html, 'text/html')
|
|
387
|
+
let bodyHtml = d.body.innerHTML
|
|
388
|
+
if(document.body.innerHTML !== bodyHtml) {
|
|
389
|
+
document.body.innerHTML = bodyHtml
|
|
390
|
+
}
|
|
391
|
+
setTimeout(poll, delay)
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error("Polling error:", error)
|
|
395
|
+
setTimeout(poll, delay)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
setTimeout(poll, delay)
|
|
399
|
+
</script>
|
|
400
|
+
</head>
|
|
401
|
+
<body>
|
|
402
|
+
<h1>Firefly develop mode</h1>
|
|
403
|
+
<p>[STATUS]</p>
|
|
404
|
+
</body>
|
|
405
|
+
</html>
|
|
406
|
+
"""
|