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.
Files changed (105) hide show
  1. package/compiler/Builder.ff +31 -39
  2. package/compiler/Compiler.ff +14 -4
  3. package/compiler/DevelopMode.ff +406 -0
  4. package/compiler/Main.ff +73 -53
  5. package/compiler/ModuleCache.ff +5 -5
  6. package/core/.firefly/include/package.json +1 -1
  7. package/core/BuildSystem.ff +82 -11
  8. package/core/NodeSystem.ff +47 -30
  9. package/core/Path.ff +7 -2
  10. package/experimental/proxy/Main.ff +60 -0
  11. package/experimental/proxy/Runner.ff +11 -0
  12. package/experimental/proxy/Tcp.ff +162 -0
  13. package/experimental/random/Superdigit.ff +18 -0
  14. package/experimental/terrain/Main.ff +40 -0
  15. package/experimental/terrain/Terrain.ff +97 -0
  16. package/experimental/terrain/Terrain2.ff +109 -0
  17. package/fireflysite/Main.ff +0 -1
  18. package/fireflysite/assets/markdown/reference/statements-and-expressions.md +1 -1
  19. package/lsp/CompletionHandler.ff +2 -2
  20. package/output/js/ff/compiler/Builder.mjs +24 -48
  21. package/output/js/ff/compiler/Builder.mjs.map +7 -11
  22. package/output/js/ff/compiler/Compiler.mjs +66 -12
  23. package/output/js/ff/compiler/Compiler.mjs.map +18 -14
  24. package/output/js/ff/compiler/Dependencies.mjs.map +2 -2
  25. package/output/js/ff/compiler/DependencyLock.mjs.map +1 -1
  26. package/output/js/ff/compiler/Deriver.mjs.map +1 -1
  27. package/output/js/ff/compiler/DevelopMode.mjs +1049 -0
  28. package/output/js/ff/compiler/DevelopMode.mjs.map +183 -0
  29. package/output/js/ff/compiler/Dictionaries.mjs.map +1 -1
  30. package/output/js/ff/compiler/Environment.mjs.map +1 -1
  31. package/output/js/ff/compiler/Inference.mjs.map +1 -1
  32. package/output/js/ff/compiler/JsEmitter.mjs.map +1 -1
  33. package/output/js/ff/compiler/JsImporter.mjs.map +1 -1
  34. package/output/js/ff/compiler/LspHook.mjs.map +1 -1
  35. package/output/js/ff/compiler/Main.mjs +256 -105
  36. package/output/js/ff/compiler/Main.mjs.map +43 -38
  37. package/output/js/ff/compiler/ModuleCache.mjs +12 -8
  38. package/output/js/ff/compiler/ModuleCache.mjs.map +4 -3
  39. package/output/js/ff/compiler/Parser.mjs.map +1 -1
  40. package/output/js/ff/compiler/Patterns.mjs.map +1 -1
  41. package/output/js/ff/compiler/Resolver.mjs.map +1 -1
  42. package/output/js/ff/compiler/SourceMap.mjs.map +1 -1
  43. package/output/js/ff/compiler/Substitution.mjs.map +1 -1
  44. package/output/js/ff/compiler/Syntax.mjs.map +1 -1
  45. package/output/js/ff/compiler/Token.mjs.map +1 -1
  46. package/output/js/ff/compiler/Tokenizer.mjs.map +1 -1
  47. package/output/js/ff/compiler/Unification.mjs.map +1 -1
  48. package/output/js/ff/compiler/Wildcards.mjs.map +1 -1
  49. package/output/js/ff/compiler/Workspace.mjs.map +1 -1
  50. package/output/js/ff/core/Any.mjs.map +1 -1
  51. package/output/js/ff/core/Array.mjs.map +1 -1
  52. package/output/js/ff/core/AssetSystem.mjs.map +1 -1
  53. package/output/js/ff/core/Atomic.mjs.map +1 -1
  54. package/output/js/ff/core/Bool.mjs.map +1 -1
  55. package/output/js/ff/core/BrowserSystem.mjs.map +1 -1
  56. package/output/js/ff/core/Buffer.mjs.map +1 -1
  57. package/output/js/ff/core/BuildSystem.mjs +116 -12
  58. package/output/js/ff/core/BuildSystem.mjs.map +35 -6
  59. package/output/js/ff/core/Channel.mjs.map +1 -1
  60. package/output/js/ff/core/Char.mjs.map +1 -1
  61. package/output/js/ff/core/Core.mjs.map +1 -1
  62. package/output/js/ff/core/Crypto.mjs.map +1 -1
  63. package/output/js/ff/core/Date.mjs.map +1 -1
  64. package/output/js/ff/core/Duration.mjs.map +1 -1
  65. package/output/js/ff/core/Equal.mjs.map +1 -1
  66. package/output/js/ff/core/Error.mjs.map +1 -1
  67. package/output/js/ff/core/FileHandle.mjs.map +1 -1
  68. package/output/js/ff/core/Float.mjs.map +1 -1
  69. package/output/js/ff/core/HttpClient.mjs.map +1 -1
  70. package/output/js/ff/core/Int.mjs.map +1 -1
  71. package/output/js/ff/core/IntMap.mjs.map +1 -1
  72. package/output/js/ff/core/Js.mjs.map +1 -1
  73. package/output/js/ff/core/JsSystem.mjs.map +1 -1
  74. package/output/js/ff/core/JsValue.mjs.map +1 -1
  75. package/output/js/ff/core/Json.mjs.map +1 -1
  76. package/output/js/ff/core/List.mjs.map +1 -1
  77. package/output/js/ff/core/Lock.mjs.map +1 -1
  78. package/output/js/ff/core/Log.mjs.map +1 -1
  79. package/output/js/ff/core/Map.mjs.map +1 -1
  80. package/output/js/ff/core/NodeSystem.mjs +54 -20
  81. package/output/js/ff/core/NodeSystem.mjs.map +14 -6
  82. package/output/js/ff/core/Nothing.mjs.map +1 -1
  83. package/output/js/ff/core/Option.mjs.map +1 -1
  84. package/output/js/ff/core/Ordering.mjs.map +1 -1
  85. package/output/js/ff/core/Pair.mjs.map +1 -1
  86. package/output/js/ff/core/Path.mjs +30 -4
  87. package/output/js/ff/core/Path.mjs.map +8 -6
  88. package/output/js/ff/core/Queue.mjs.map +1 -1
  89. package/output/js/ff/core/Random.mjs.map +1 -1
  90. package/output/js/ff/core/RbMap.mjs.map +1 -1
  91. package/output/js/ff/core/Serializable.mjs.map +1 -1
  92. package/output/js/ff/core/Set.mjs.map +1 -1
  93. package/output/js/ff/core/Show.mjs.map +1 -1
  94. package/output/js/ff/core/SourceLocation.mjs.map +1 -1
  95. package/output/js/ff/core/Stream.mjs.map +1 -1
  96. package/output/js/ff/core/String.mjs.map +1 -1
  97. package/output/js/ff/core/StringMap.mjs.map +1 -1
  98. package/output/js/ff/core/Task.mjs.map +1 -1
  99. package/output/js/ff/core/Try.mjs.map +1 -1
  100. package/output/js/ff/core/Unit.mjs.map +1 -1
  101. package/output/js/ff/core/node_modules +1 -0
  102. package/package.json +1 -1
  103. package/vscode/package.json +1 -1
  104. package/webserver/.firefly/include/package.json +1 -1
  105. package/webserver/WebServer.ff +6 -3
package/compiler/Main.ff CHANGED
@@ -11,10 +11,12 @@ import Inference
11
11
  import ModuleCache
12
12
  import LspHook
13
13
  import DependencyLock
14
+ import DevelopMode
14
15
 
15
16
  data MainCommand {
16
17
  BootstrapCommand
17
18
  RunCommand(mainPath: String, argument: List[String])
19
+ DevelopCommand(mainPath: String, argument: List[String])
18
20
  BrowserCommand(mainPath: String)
19
21
  BuildCommand(mainPath: String)
20
22
  CheckCommand(filePath: String)
@@ -27,43 +29,6 @@ main(system: NodeSystem): Unit {
27
29
 
28
30
  let fireflyPath = detectFireflyPath(system)
29
31
 
30
- function buildScript(
31
- mainPath: Path
32
- mainPackagePair: PackagePair
33
- emitTarget: EmitTarget
34
- resolvedDependencies: ResolvedDependencies
35
- ): ModuleKey {
36
- let fixedPackagePaths = if(resolvedDependencies.packagePaths.contains(PackagePair("ff", "core"))) {
37
- resolvedDependencies.packagePaths
38
- } else {
39
- resolvedDependencies.packagePaths.add(PackagePair("ff", "core"), fireflyPath.slash("core"))
40
- }
41
- let compilerModulePath = if(emitTarget != EmitBrowser && emitTarget != EmitExecutable) {
42
- fireflyPath.slash("output").slash("js").slash("ff").slash("compiler/Builder.mjs")
43
- }
44
- let targetName = emitTarget.{
45
- | EmitBuild => "build"
46
- | EmitNode => "node"
47
- | EmitBrowser => "browser"
48
- | EmitExecutable => "executable"
49
- }
50
- let folders = mainPath.parent().grab().relativeListTo(fixedPackagePaths.grab(mainPackagePair))
51
- let name = mainPath.base().removeLast(".ff").grab()
52
- let moduleKey = ModuleKey(mainPackagePair, folders, name)
53
- Builder.build(
54
- system = system
55
- emitTarget = emitTarget
56
- mainModules = [moduleKey]
57
- resolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
58
- compilerModulePath = compilerModulePath
59
- tempPath = system.path(".firefly").slash("temporary")
60
- jsOutputPath = system.path(".firefly").path("output").path(targetName)
61
- printMeasurements = False
62
- moduleCache = ModuleCache.new(0)
63
- )
64
- moduleKey
65
- }
66
-
67
32
  function runCommand(command: MainCommand) {
68
33
  | RunCommand(mainFile, arguments) =>
69
34
  let resolvedDependencies = Dependencies.process(
@@ -73,12 +38,15 @@ main(system: NodeSystem): Unit {
73
38
  )
74
39
  prepareFireflyDirectory(system.path("."))
75
40
  let mainPath = system.path(mainFile)
76
- let moduleKey = buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitNode, resolvedDependencies)
41
+ let moduleKey = buildScript(system, mainPath, resolvedDependencies.mainPackagePair, EmitNode, resolvedDependencies)
77
42
  if(!importAndRun(system, fireflyPath, "node", moduleKey, arguments)) {
78
43
  let at = Location(system.path(mainFile).absolute(), 1, 1)
79
44
  throw(CompileError(at, "This module does not contain a 'nodeMain' function"))
80
45
  }
81
46
 
47
+ | DevelopCommand(mainFile, arguments) =>
48
+ DevelopMode.run(system, fireflyPath, mainFile, arguments)
49
+
82
50
  | BrowserCommand(mainFile) =>
83
51
  let resolvedDependencies = Dependencies.process(
84
52
  system.httpClient()
@@ -88,7 +56,7 @@ main(system: NodeSystem): Unit {
88
56
  prepareFireflyDirectory(system.path("."))
89
57
  let mainPath = system.path(mainFile)
90
58
  let moduleKey =
91
- buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitBrowser, resolvedDependencies)
59
+ buildScript(system, mainPath, resolvedDependencies.mainPackagePair, EmitBrowser, resolvedDependencies)
92
60
  bundleForBrowser(system, resolvedDependencies.mainPackagePair, moduleKey)
93
61
 
94
62
  | BuildCommand(mainFile) =>
@@ -99,8 +67,8 @@ main(system: NodeSystem): Unit {
99
67
  )
100
68
  prepareFireflyDirectory(system.path("."))
101
69
  let mainPath = system.path(mainFile)
102
- let moduleKey = buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitBuild, resolvedDependencies)
103
- buildScript(mainPath, resolvedDependencies.mainPackagePair, EmitExecutable, resolvedDependencies)
70
+ let moduleKey = buildScript(system, mainPath, resolvedDependencies.mainPackagePair, EmitBuild, resolvedDependencies)
71
+ buildScript(system, mainPath, resolvedDependencies.mainPackagePair, EmitExecutable, resolvedDependencies)
104
72
  bundleForPkg(system, resolvedDependencies.mainPackagePair, mainPath.base())
105
73
  importAndRun(system, fireflyPath, "build", moduleKey, [])
106
74
 
@@ -141,7 +109,6 @@ main(system: NodeSystem): Unit {
141
109
  singleFilePackages = [].toSet()
142
110
  )
143
111
  compilerModulePath = None
144
- tempPath = workingDirectory.slash("output").slash("temporary")
145
112
  jsOutputPath = workingDirectory.slash("output").slash("js")
146
113
  printMeasurements = True
147
114
  moduleCache = ModuleCache.new(0)
@@ -184,14 +151,55 @@ usageString = """
184
151
  usage: firefly <main-file> [<main-arguments>] | <command> [<command-arguments>]
185
152
 
186
153
  These are the commands:
187
- run <main-file> [<main-arguments>] Run the main file with the provided arguments
188
- browser <main-file> Compile the main file for the browser
189
- build <main-file> Build the main file
190
- check <firefly-file> Check the firefly source file for errors
191
- symbols <out-file> <firefly-file> Print a .tsv with the symbols of a firefly source file
192
- bootstrap Bootstrap the compiler
154
+ run <main-file> [<main-arguments>] Run the main file with the provided arguments
155
+ develop <main-file> [<main-arguments>] Develop the main file with the provided arguments
156
+ browser <main-file> Compile the main file for the browser
157
+ build <main-file> Build the main file
158
+ check <firefly-file> Check the firefly source file for errors
159
+ symbols <out-file> <firefly-file> Print a .tsv with the symbols of a firefly source file
160
+ bootstrap Bootstrap the compiler
193
161
  """
194
162
 
163
+ buildScript(
164
+ system: NodeSystem
165
+ mainPath: Path
166
+ mainPackagePair: PackagePair
167
+ emitTarget: EmitTarget
168
+ resolvedDependencies: ResolvedDependencies
169
+ moduleCache: ModuleCache = ModuleCache.new(0)
170
+ printMeasurements: Bool = False
171
+ ): ModuleKey {
172
+ let fireflyPath = detectFireflyPath(system)
173
+ let fixedPackagePaths = if(resolvedDependencies.packagePaths.contains(PackagePair("ff", "core"))) {
174
+ resolvedDependencies.packagePaths
175
+ } else {
176
+ resolvedDependencies.packagePaths.add(PackagePair("ff", "core"), fireflyPath.slash("core"))
177
+ }
178
+ let compilerModulePath = if(emitTarget != EmitBrowser && emitTarget != EmitExecutable) {
179
+ fireflyPath.slash("output").slash("js").slash("ff").slash("compiler/Builder.mjs")
180
+ }
181
+ let targetName = emitTarget.{
182
+ | EmitBuild => "build"
183
+ | EmitNode => "node"
184
+ | EmitBrowser => "browser"
185
+ | EmitExecutable => "executable"
186
+ }
187
+ let folders = mainPath.parent().grab().relativeListTo(fixedPackagePaths.grab(mainPackagePair))
188
+ let name = mainPath.base().removeLast(".ff").grab()
189
+ let moduleKey = ModuleKey(mainPackagePair, folders, name)
190
+ Builder.build(
191
+ system = system
192
+ emitTarget = emitTarget
193
+ mainModules = [moduleKey]
194
+ resolvedDependencies = resolvedDependencies.ResolvedDependencies(packagePaths = fixedPackagePaths)
195
+ compilerModulePath = compilerModulePath
196
+ jsOutputPath = system.path(".firefly").path("output").path(targetName)
197
+ printMeasurements = printMeasurements
198
+ moduleCache = moduleCache
199
+ )
200
+ moduleKey
201
+ }
202
+
195
203
  parseCommandLine(arguments: List[String]): MainCommand {
196
204
  | [mainFile, ...mainArguments] {mainFile.removeLast(".ff") | Some(mainName)} =>
197
205
  RunCommand(mainFile, mainArguments)
@@ -203,6 +211,14 @@ parseCommandLine(arguments: List[String]): MainCommand {
203
211
  "You must specify a Firefly file (.ff) as first argument to run." + usageString
204
212
  ))
205
213
  }
214
+ | ["develop", ...runArguments] =>
215
+ runArguments.{
216
+ | [mainFile, ...mainArguments] {mainFile.removeLast(".ff") | Some(mainName)} =>
217
+ DevelopCommand(mainFile, mainArguments)
218
+ | _ => throw(CommandLineError(
219
+ "You must specify a Firefly file (.ff) as first argument to develop." + usageString
220
+ ))
221
+ }
206
222
  | ["browser", ...browserArguments] =>
207
223
  browserArguments.{
208
224
  | [mainFile] {mainFile.removeLast(".ff") | Some(mainName)} =>
@@ -278,11 +294,7 @@ importAndRun(
278
294
  moduleKey: ModuleKey
279
295
  arguments: List[String]
280
296
  ): Bool {
281
- let process = Js.await(Js.dynamicImport("process"))
282
- let cwd = process->cwd()
283
- let workingDirectory = if(cwd->indexOf(":") === 1) {"file:///" + cwd?} else {cwd?}
284
- let packagePath = moduleKey.packagePair.groupName("/")
285
- let runFile = workingDirectory + "/.firefly/output/" + target + "/" + packagePath + "/" + moduleKey.importName() + ".run.mjs"
297
+ let runFile = locateRunFile(system, target, moduleKey)
286
298
  let runFilePath = if(runFile.contains("://")) {system.pathFromUrl(runFile)} else {system.path(runFile)}
287
299
  if(runFilePath.exists()) {
288
300
  let main = Js.await(Js.dynamicImport(runFile))
@@ -298,6 +310,14 @@ importAndRun(
298
310
  }
299
311
  }
300
312
 
313
+ locateRunFile(system: NodeSystem, target: String, moduleKey: ModuleKey): String {
314
+ let process = Js.await(Js.dynamicImport("process"))
315
+ let cwd = process->cwd()
316
+ let workingDirectory = if(cwd->indexOf(":") === 1) {"file:///" + cwd?} else {cwd?}
317
+ let packagePath = moduleKey.packagePair.groupName("/")
318
+ workingDirectory + "/.firefly/output/" + target + "/" + packagePath + "/" + moduleKey.importName() + ".run.mjs"
319
+ }
320
+
301
321
  prepareFireflyDirectory(path: Path) {
302
322
  if(!path.slash(".firefly").slash("output").exists()) {
303
323
  if(!path.slash(".firefly").exists()) {
@@ -22,9 +22,11 @@ new(version: Int): ModuleCache {
22
22
 
23
23
  extend self: ModuleCache {
24
24
 
25
- remove(keys: List[String]) {
25
+ remove(keys: List[String], removeParsed: Bool = True) {
26
26
  if(!keys.isEmpty()):
27
- self.parsedModules = self.parsedModules.removeList(keys)
27
+ if(removeParsed) {
28
+ self.parsedModules = self.parsedModules.removeList(keys)
29
+ }
28
30
  self.resolvedModules = self.resolvedModules.removeList(keys)
29
31
  self.derivedModules = self.derivedModules.removeList(keys)
30
32
  self.inferredModules = self.inferredModules.removeList(keys)
@@ -32,13 +34,11 @@ extend self: ModuleCache {
32
34
  }
33
35
 
34
36
  invalidate(key: String) {
35
- //Log.trace("Invalidate: " + uri)
36
37
  self.parsedModules.get(key).each: | Pair(module, _) =>
37
38
  self.remove([key])
38
39
  self.parsedModules.each {| k, Pair(m, _) =>
39
40
  if(m.imports.any {i => i.moduleKey == module.moduleKey}) {
40
- //Log.trace("Invalidating due to import of invalidated module: " + m.packagePair.groupName() + "/" + m.file)
41
- self.remove([k])
41
+ self.remove([k], removeParsed = False)
42
42
  }
43
43
  }
44
44
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "dependencies": {
3
- "esbuild": "^0.21.5"
3
+ "esbuild": "^0.25.9"
4
4
  }
5
5
  }
@@ -14,14 +14,48 @@ extend self: BuildSystem {
14
14
  internalCompile(self, mainFiles.map {internalPath(self, _)}, "browser")
15
15
  let browserOutputPath = internalPath(self, ".firefly/output/browser")
16
16
  let runPaths = internalListPath(browserOutputPath).filter {_.base().endsWith(".run.mjs")}.toList()
17
- let outputPath = runPaths.grabFirst().parent().grab()
18
- internalBrowserCallEsBuild(self, runPaths.map {_.absolute()}, outputPath.absolute(), minify, sourceMaps)
19
- let bundlePaths = internalListPath(browserOutputPath).filter {p =>
20
- p.base().endsWith(".bundle.js") || p.base().endsWith(".bundle.js.map")
21
- }.toList()
22
- AssetSystem(bundlePaths.map {p =>
23
- Pair("/" + p.relativeTo(browserOutputPath).replace("\\", "/"), {p.readStream()})
24
- }.toMap())
17
+ if(Js.globalThis()->ffDevelopMode.typeof() == "undefined") {
18
+ let outputPath = runPaths.grabFirst().parent().grab()
19
+ let start = self.mainTask().elapsed()
20
+ internalBrowserCallEsBuild(self, runPaths.map {_.absolute()}, outputPath.absolute(), minify, sourceMaps)
21
+ let bundlePaths = runPaths.flatMap {p =>
22
+ let outputPath = p.parent().grab()
23
+ let bundlePath = outputPath.slash(p.base().removeLast(".mjs").grab() + ".bundle.js")
24
+ let mapPath = outputPath.slash(p.base().removeLast(".mjs").grab() + ".bundle.js.map")
25
+ [bundlePath, mapPath]
26
+ }
27
+ AssetSystem(bundlePaths.map {p =>
28
+ Pair("/" + p.relativeListTo(browserOutputPath).join("/"), {p.readStream()})
29
+ }.toMap())
30
+ } else {
31
+ mutable built = Set.new()
32
+ let lock = self.mainTask().lock()
33
+ AssetSystem(runPaths.flatMap {p =>
34
+ let outputPath = p.parent().grab()
35
+ let bundlePath = outputPath.slash(p.base().removeLast(".mjs").grab() + ".bundle.js")
36
+ let mapPath = outputPath.slash(p.base().removeLast(".mjs").grab() + ".bundle.js.map")
37
+ let bundleAssetPath = "/" + bundlePath.relativeListTo(browserOutputPath).join("/")
38
+ let mapAssetPath = "/" + mapPath.relativeListTo(browserOutputPath).join("/")
39
+ function bundle() {
40
+ lock.do {
41
+ if(!built.contains(p.absolute())) {
42
+ internalBrowserCallEsBuild(self, [p.absolute()], outputPath.absolute(), minify, sourceMaps)
43
+ built = built.add(p.absolute())
44
+ }
45
+ bundlePath.readStream()
46
+ }
47
+ }
48
+ let bundleEntry = Pair(bundleAssetPath, {
49
+ bundle()
50
+ bundlePath.readStream()
51
+ })
52
+ let mapEntry = Pair(mapAssetPath, {
53
+ bundle()
54
+ mapPath.readStream()
55
+ })
56
+ [bundleEntry, mapEntry]
57
+ }.toMap())
58
+ }
25
59
  }
26
60
 
27
61
  buildMode(): Bool {
@@ -82,16 +116,37 @@ internalNodeCallEsBuild(
82
116
  minify: Bool
83
117
  ): Unit {
84
118
  let esbuild = Js.import("esbuild")
85
- esbuild->build(Js->(
119
+ Js.await(esbuild->build(Js->(
86
120
  entryPoints = [mainJsFile]
87
121
  bundle = True
88
122
  minify = minify
89
123
  sourcemap = True
90
124
  platform = "node"
91
125
  target = "es2017"
92
- external = ["../../../node_modules/*"] // TODO
126
+ external = ["esbuild", "uws.js"]
127
+ loader = Js.object().with(".node", "copy")
93
128
  outfile = outputPath
94
- ))
129
+ )))
130
+ }
131
+
132
+ internalNodeCallEsBuildContext(
133
+ self: NodeSystem
134
+ mainJsFile: String
135
+ outputPath: String
136
+ minify: Bool
137
+ ): JsValue {
138
+ let esbuild = Js.import("esbuild")
139
+ Js.await(esbuild->context(Js->(
140
+ entryPoints = [mainJsFile]
141
+ bundle = True
142
+ minify = minify
143
+ sourcemap = True
144
+ platform = "node"
145
+ target = "es2017"
146
+ external = ["esbuild", "uws.js"]
147
+ loader = Js.object().with(".node", "copy")
148
+ outfile = outputPath
149
+ )))
95
150
  }
96
151
 
97
152
  internalListDirectory(path: Path): List[Pair[String, () => Stream[Buffer]]] {
@@ -125,12 +180,28 @@ internalPath(buildSystem: BuildSystem, absoluteOrRelative: String): Path {
125
180
  }
126
181
 
127
182
  internalCompile(buildSystem: BuildSystem, mainFiles: List[Path], target: String): Unit {
183
+ if(Js.globalThis()->ffDevelopMode.typeof() != "undefined") {
184
+ Js->process->send(Js->(
185
+ ffDevelopMode = "internalCompile"
186
+ mainFiles = mainFiles.map {_.absolute()}
187
+ target = target
188
+ ))
189
+ Js.awaitCancellablePromise {resolve, reject, cleanup =>
190
+ Js->process->on("message", Js->{message =>
191
+ if(message->ffDevelopMode === "internalCompile") {
192
+ resolve(Unit) // Handle errors?
193
+ }
194
+ })
195
+ }
196
+ } else:
128
197
  // Ported from the old FFI. It's quite fragile w.r.t. changes in code generation.
129
198
  Js.await(Js.rawIdentifier("$firefly_compiler")->"buildViaBuildSystem_$"(
130
199
  buildSystem!
131
200
  internalPath(buildSystem, buildSystem!->"fireflyPath_"?)!
132
201
  mainFiles!
133
202
  target
203
+ Js.undefined()
204
+ Js.undefined()
134
205
  Js.currentTask()!
135
206
  ))
136
207
  }
@@ -116,7 +116,9 @@ extend self: NodeSystem {
116
116
  maxBuffer: Int = 16777216
117
117
  killSignal: Int = 9
118
118
  shell: Bool = False
119
+ node: Option[(JsValue, JsValue) => Unit] = None
119
120
  ): ProcessResult {
121
+ let inheritStdio = !node.isEmpty()
120
122
  let childProcess = Js.import("node:child_process")
121
123
  let env = environment.map {e =>
122
124
  let o = Js.object()
@@ -124,43 +126,58 @@ extend self: NodeSystem {
124
126
  o
125
127
  }.else {Js->process->env}
126
128
  Js.withSignal {signal => Js.awaitCancellablePromise {resolve, reject, onSettle =>
127
- let newProcess = childProcess->spawn(command, arguments, Js->(
128
- cwd = directory.map {_.absolutePath!}.else {Js.undefined()}
129
- windowsHide = True
130
- signal = signal
131
- killSignal = killSignal
132
- env = env
133
- shell = shell
134
- ))
129
+ let newProcess = if(!node.isEmpty()) {
130
+ let p = childProcess->fork(command, arguments, Js->(
131
+ cwd = directory.map {_.absolutePath!}.else {Js.undefined()}
132
+ signal = signal
133
+ killSignal = killSignal
134
+ env = env
135
+ silent = True
136
+ stdio = if(inheritStdio) {"inherit"} else {"pipe"}
137
+ ))
138
+ p->on("message", Js->{message => node.grab()(message, p)})
139
+ p
140
+ } else {
141
+ childProcess->spawn(command, arguments, Js->(
142
+ cwd = directory.map {_.absolutePath!}.else {Js.undefined()}
143
+ windowsHide = True
144
+ signal = signal
145
+ killSignal = killSignal
146
+ env = env
147
+ shell = shell
148
+ ))
149
+ }
135
150
  mutable size = 0
136
151
  let out = Array.new()
137
152
  let err = Array.new()
138
- newProcess->stdout->on("data", Js->{data =>
139
- if(size <= maxBuffer) {
140
- size += data->byteLength?
141
- if(size > maxBuffer) {
142
- newProcess->kill(killSignal)
143
- } else {
144
- out.push(data)
153
+ if(!inheritStdio) {
154
+ newProcess->stdout->on("data", Js->{data =>
155
+ if(size <= maxBuffer) {
156
+ size += data->byteLength?
157
+ if(size > maxBuffer) {
158
+ newProcess->kill(killSignal)
159
+ } else {
160
+ out.push(data)
161
+ }
145
162
  }
146
- }
147
- Unit
148
- })
149
- newProcess->stderr->on("data", Js->{data =>
150
- if(size <= maxBuffer) {
151
- size += data->byteLength?
152
- if(size > maxBuffer) {
153
- newProcess->kill(killSignal)
154
- } else {
155
- err.push(data)
163
+ Unit
164
+ })
165
+ newProcess->stderr->on("data", Js->{data =>
166
+ if(size <= maxBuffer) {
167
+ size += data->byteLength?
168
+ if(size > maxBuffer) {
169
+ newProcess->kill(killSignal)
170
+ } else {
171
+ err.push(data)
172
+ }
156
173
  }
174
+ Unit
175
+ })
176
+ if(standardIn!->byteLength !== 0) {
177
+ newProcess->stdin->write(standardIn!)
157
178
  }
158
- Unit
159
- })
160
- if(standardIn!->byteLength !== 0) {
161
- newProcess->stdin->write(standardIn!)
179
+ newProcess->stdin->end()
162
180
  }
163
- newProcess->stdin->end()
164
181
  newProcess->on("error", Js->{error =>
165
182
  if(size > maxBuffer) {
166
183
  reject(internalProcessError("maxBuffer exceeded"))
package/core/Path.ff CHANGED
@@ -104,6 +104,11 @@ extend self: Path {
104
104
  let nodePath = Js.import("path")
105
105
  nodePath->relative(path.absolutePath, self.absolutePath)?
106
106
  }
107
+
108
+ relativeUrlTo(path: Path): String {
109
+ let relative = self.relativeListTo(path)
110
+ if(relative.first().any {_ == ".."}) {relative.join("/")} else {"./" + relative.join("/")}
111
+ }
107
112
 
108
113
  relativeListTo(path: Path): List[String] {
109
114
  let nodePath = Js.import("path")
@@ -299,7 +304,7 @@ extend self: PathEntry {
299
304
 
300
305
  }
301
306
 
302
- internalReadStream(createReadStream: () => JsValue): Stream[Buffer] {
307
+ internalReadStream(createReadStream: () => JsValue, close: Bool = True): Stream[Buffer] {
303
308
  mutable readable = None
304
309
  mutable seenError = Js.null()
305
310
  let emptyResolve = {}
@@ -357,7 +362,7 @@ internalReadStream(createReadStream: () => JsValue): Stream[Buffer] {
357
362
  }
358
363
  go()
359
364
  } {
360
- readable.each {_->destroy()}
365
+ if(close) {readable.each {_->destroy()}}
361
366
  }
362
367
  }
363
368
 
@@ -0,0 +1,60 @@
1
+ nodeMain(system: NodeSystem) {
2
+ let net = Js.import("node:net")
3
+ let targetServer = "localhost" // 127.0.0.1
4
+ let targetPort = 8080
5
+
6
+ function parseHeaders(headerData: JsValue): JsValue {
7
+ let headers = Js->()
8
+ let lines = headerData->split("\r\n")
9
+ if(lines.get(0)->endsWith(" HTTP/1.1")? || lines.get(0)->endsWith(" HTTP/1.0")?) {
10
+ lines->forEach(Js->{line =>
11
+ let index = line->indexOf(":")
12
+ if(index !== -1) {
13
+ let key = line->substring(0, index)->trim()->toLowerCase()
14
+ let value = line->substring(index + 1!)->trim()
15
+ headers.set(key, value)
16
+ }
17
+ })
18
+ }
19
+ headers
20
+ }
21
+
22
+ let proxyServer = net->createServer(Js->{clientSocket =>
23
+ mutable buffer = Js->Buffer->alloc(0)
24
+ mutable isHttpNavigateRequest = False
25
+ clientSocket->on("data", Js->{data =>
26
+ buffer = Js->Buffer->concat([buffer, data])
27
+ let headerEnd = buffer->indexOf("\r\n\r\n")
28
+ if(headerEnd !== -1 || buffer->length? >= 64 * 1024) {
29
+ let headerData = buffer->subarray(0, headerEnd)->toString()
30
+ let headers = parseHeaders(headerData)
31
+
32
+ if(headers.get("sec-fetch-user") === "?1") {
33
+ isHttpNavigateRequest = True
34
+ Js->console->log("Detected HTTP request with Sec-Fetch-User")
35
+ }
36
+
37
+ mutable targetSocket = Js.undefined()
38
+ targetSocket = net->createConnection(targetPort, targetServer, Js->{
39
+ Js->console->log("Connected to target server")
40
+ targetSocket->write(buffer)
41
+ clientSocket->pipe(targetSocket)->pipe(clientSocket)
42
+ })
43
+
44
+ clientSocket->on("error", Js->{err =>
45
+ Js->console->error("Client socket error:", err)
46
+ targetSocket->end()
47
+ })
48
+
49
+ targetSocket->on("error", Js->{err =>
50
+ Js->console->error("Target socket error:", err)
51
+ clientSocket->end()
52
+ })
53
+ }
54
+ })
55
+ })
56
+
57
+ proxyServer->listen(8081, Js->{
58
+ Js->console->log("Proxy server running on port 8081")
59
+ })
60
+ }
@@ -0,0 +1,11 @@
1
+ data Runner(
2
+ state: RunnerState
3
+ changedSinceCompilationStarted: Set[String]
4
+ )
5
+
6
+ data RunnerState {
7
+ Compiling
8
+ CompileError(output: String)
9
+ AppRunning(output: String)
10
+ AppCrashed(output: String)
11
+ }