firefly-compiler 0.5.50 → 0.5.52

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 (132) hide show
  1. package/bin/Release.ff +3 -3
  2. package/bin/firefly.mjs +1 -1
  3. package/compiler/Builder.ff +11 -5
  4. package/compiler/Compiler.ff +14 -3
  5. package/compiler/JsEmitter.ff +6 -11
  6. package/compiler/Main.ff +10 -15
  7. package/compiler/ModuleCache.ff +2 -1
  8. package/core/Buffer.ff +26 -4
  9. package/core/BuildSystem.ff +49 -46
  10. package/core/JsValue.ff +5 -0
  11. package/experimental/date/Date.ff +604 -0
  12. package/experimental/date/Main.ff +51 -0
  13. package/experimental/random/AsciiBuffer.ff +15 -0
  14. package/experimental/rhymeapp/Main.ff +2 -3
  15. package/experimental/site/Main.ff +2 -2
  16. package/experimental/site2/Main.ff +2 -2
  17. package/firefly.sh +1 -1
  18. package/fireflysite/CommunityOverview.ff +3 -3
  19. package/fireflysite/CountingButtonDemo.ff +3 -3
  20. package/fireflysite/ExamplesOverview.ff +3 -12
  21. package/fireflysite/FrontPage.ff +3 -3
  22. package/fireflysite/GettingStarted.ff +3 -3
  23. package/fireflysite/Guide.ff +7 -187
  24. package/fireflysite/Html.ff +66 -0
  25. package/fireflysite/Main.ff +13 -77
  26. package/fireflysite/MatchingPasswordsDemo.ff +3 -3
  27. package/fireflysite/Menu.ff +59 -0
  28. package/fireflysite/PackagesOverview.ff +3 -3
  29. package/fireflysite/PostgresqlDemo.ff +3 -3
  30. package/fireflysite/RouteFront.ff +30 -0
  31. package/fireflysite/RouteNonMarkdown.ff +48 -0
  32. package/fireflysite/RouteReference.ff +40 -0
  33. package/fireflysite/Router.ff +33 -0
  34. package/fireflysite/Website.ff +133 -0
  35. package/fireflysite/assets/markdown/reference/{FunctionsAndMethods.md → functions-and-methods.md} +1 -1
  36. package/fireflysite/assets/markdown/reference/{StatementsAndExpressions.md → statements-and-expressions.md} +0 -1
  37. package/fireflysite/assets/markdown/reference/{StructuredConcurrency.md → structured-concurrency.md} +0 -1
  38. package/fireflysite/assets/markdown/reference.md +3 -0
  39. package/graph/.firefly/package.ff +1 -0
  40. package/graph/Graph.ff +79 -0
  41. package/lsp/CompletionHandler.ff +2 -2
  42. package/lux/CssTest.ff +2 -2
  43. package/lux/Main.ff +2 -2
  44. package/lux/Main2.ff +2 -2
  45. package/output/js/ff/compiler/Builder.mjs +44 -12
  46. package/output/js/ff/compiler/Compiler.mjs +28 -10
  47. package/output/js/ff/compiler/Dependencies.mjs +0 -2
  48. package/output/js/ff/compiler/DependencyLock.mjs +0 -2
  49. package/output/js/ff/compiler/Deriver.mjs +0 -2
  50. package/output/js/ff/compiler/Dictionaries.mjs +0 -2
  51. package/output/js/ff/compiler/Environment.mjs +0 -2
  52. package/output/js/ff/compiler/Inference.mjs +0 -2
  53. package/output/js/ff/compiler/JsEmitter.mjs +12 -30
  54. package/output/js/ff/compiler/JsImporter.mjs +0 -2
  55. package/output/js/ff/compiler/LspHook.mjs +0 -2
  56. package/output/js/ff/compiler/Main.mjs +24 -47
  57. package/output/js/ff/compiler/Main.run.mjs +25 -0
  58. package/output/js/ff/compiler/ModuleCache.mjs +4 -6
  59. package/output/js/ff/compiler/Parser.mjs +0 -2
  60. package/output/js/ff/compiler/Patterns.mjs +0 -2
  61. package/output/js/ff/compiler/Resolver.mjs +0 -2
  62. package/output/js/ff/compiler/Substitution.mjs +0 -2
  63. package/output/js/ff/compiler/Syntax.mjs +0 -2
  64. package/output/js/ff/compiler/Token.mjs +0 -2
  65. package/output/js/ff/compiler/Tokenizer.mjs +0 -2
  66. package/output/js/ff/compiler/Unification.mjs +0 -2
  67. package/output/js/ff/compiler/Wildcards.mjs +0 -2
  68. package/output/js/ff/compiler/Workspace.mjs +0 -2
  69. package/output/js/ff/core/Any.mjs +0 -2
  70. package/output/js/ff/core/Array.mjs +0 -2
  71. package/output/js/ff/core/AssetSystem.mjs +0 -2
  72. package/output/js/ff/core/Atomic.mjs +0 -2
  73. package/output/js/ff/core/Bool.mjs +0 -2
  74. package/output/js/ff/core/BrowserSystem.mjs +0 -2
  75. package/output/js/ff/core/Buffer.mjs +50 -10
  76. package/output/js/ff/core/BuildSystem.mjs +92 -72
  77. package/output/js/ff/core/Channel.mjs +0 -2
  78. package/output/js/ff/core/Char.mjs +0 -2
  79. package/output/js/ff/core/Core.mjs +0 -2
  80. package/output/js/ff/core/Crypto.mjs +0 -2
  81. package/output/js/ff/core/Date.mjs +0 -2
  82. package/output/js/ff/core/Duration.mjs +0 -2
  83. package/output/js/ff/core/Equal.mjs +0 -2
  84. package/output/js/ff/core/Error.mjs +0 -2
  85. package/output/js/ff/core/FileHandle.mjs +0 -2
  86. package/output/js/ff/core/Float.mjs +0 -2
  87. package/output/js/ff/core/HttpClient.mjs +0 -2
  88. package/output/js/ff/core/Int.mjs +0 -2
  89. package/output/js/ff/core/IntMap.mjs +0 -2
  90. package/output/js/ff/core/Js.mjs +0 -2
  91. package/output/js/ff/core/JsSystem.mjs +0 -2
  92. package/output/js/ff/core/JsValue.mjs +8 -2
  93. package/output/js/ff/core/Json.mjs +0 -2
  94. package/output/js/ff/core/List.mjs +0 -2
  95. package/output/js/ff/core/Lock.mjs +0 -2
  96. package/output/js/ff/core/Log.mjs +0 -2
  97. package/output/js/ff/core/Map.mjs +0 -2
  98. package/output/js/ff/core/NodeSystem.mjs +0 -2
  99. package/output/js/ff/core/Nothing.mjs +0 -2
  100. package/output/js/ff/core/Option.mjs +0 -2
  101. package/output/js/ff/core/Ordering.mjs +0 -2
  102. package/output/js/ff/core/Pair.mjs +0 -2
  103. package/output/js/ff/core/Path.mjs +0 -2
  104. package/output/js/ff/core/Queue.mjs +0 -2
  105. package/output/js/ff/core/Random.mjs +0 -2
  106. package/output/js/ff/core/RbMap.mjs +0 -2
  107. package/output/js/ff/core/Serializable.mjs +0 -2
  108. package/output/js/ff/core/Set.mjs +0 -2
  109. package/output/js/ff/core/Show.mjs +0 -2
  110. package/output/js/ff/core/SourceLocation.mjs +0 -2
  111. package/output/js/ff/core/Stream.mjs +0 -2
  112. package/output/js/ff/core/String.mjs +0 -2
  113. package/output/js/ff/core/StringMap.mjs +0 -2
  114. package/output/js/ff/core/Task.mjs +0 -2
  115. package/output/js/ff/core/Try.mjs +0 -2
  116. package/output/js/ff/core/Unit.mjs +0 -2
  117. package/package.json +1 -1
  118. package/vscode/client/src/extension.ts +1 -1
  119. package/vscode/package.json +1 -1
  120. package/vscode/snippets.json +2 -2
  121. package/webserver/WebRoute.ff +51 -14
  122. package/fireflysite/ReferenceAll.ff +0 -18
  123. /package/fireflysite/assets/markdown/reference/{BaseTypes.md → base-types.md} +0 -0
  124. /package/fireflysite/assets/markdown/reference/{EmittedJavascript.md → emitted-javascript.md} +0 -0
  125. /package/fireflysite/assets/markdown/reference/{Exceptions.md → exceptions.md} +0 -0
  126. /package/fireflysite/assets/markdown/reference/{JavascriptInterop.md → javascript-interop.md} +0 -0
  127. /package/fireflysite/assets/markdown/reference/{ModulesAndPackages.md → modules-and-packages.md} +0 -0
  128. /package/fireflysite/assets/markdown/reference/{PatternMatching.md → pattern-matching.md} +0 -0
  129. /package/fireflysite/assets/markdown/reference/{TraitsAndInstances.md → traits-and-instances.md} +0 -0
  130. /package/fireflysite/assets/markdown/reference/{UserDefinedTypes.md → user-defined-types.md} +0 -0
  131. /package/fireflysite/assets/markdown/scratch/{ControlFlow.md → control-flow.md} +0 -0
  132. /package/fireflysite/assets/markdown/{reference/OldStructuredConcurrency.md → scratch/old-structured-concurrency.md} +0 -0
package/firefly.sh CHANGED
@@ -2,4 +2,4 @@
2
2
  set -e
3
3
  firefly_link=`readlink -f "$0"`
4
4
  firefly=`dirname "$firefly_link"`
5
- node --harmony-temporal "$firefly/output/js/ff/compiler/Main.mjs" $@
5
+ node --harmony-temporal "$firefly/output/js/ff/compiler/Main.run.mjs" $@
@@ -2,8 +2,8 @@ import Guide
2
2
  import CountingButtonDemo
3
3
  import MatchingPasswordsDemo
4
4
 
5
- new(): Document {
6
- ReadyDocument([
5
+ sections(): List[Section] {
6
+ [
7
7
  Section("Community", [
8
8
  Paragraph([
9
9
  Text("The developers of Firefly hang out in")
@@ -16,5 +16,5 @@ new(): Document {
16
16
  Text("If you're interested in Firefly, that's the best way to be part of the community for now.")
17
17
  ])
18
18
  ])
19
- ])
19
+ ]
20
20
  }
@@ -24,8 +24,8 @@ render(lux: Lux): Unit {
24
24
  }
25
25
  }
26
26
 
27
- newDocument(): Document {
28
- ReadyDocument([
27
+ sections(): List[Section] {
28
+ [
29
29
  Section(name, [
30
30
  Paragraph([Text("A button that counts how many times it's been clicked.")])
31
31
  ])
@@ -54,5 +54,5 @@ newDocument(): Document {
54
54
  }
55
55
  """, firefly = True)
56
56
  ])
57
- ])
57
+ ]
58
58
  }
@@ -3,15 +3,6 @@ import CountingButtonDemo
3
3
  import MatchingPasswordsDemo
4
4
  import PostgresqlDemo
5
5
 
6
- mock(): List[Document] {
7
- [
8
- new()
9
- CountingButtonDemo.newDocument()
10
- MatchingPasswordsDemo.newDocument()
11
- PostgresqlDemo.newDocument()
12
- ]
13
- }
14
-
15
6
  demos(): List[Demo] {
16
7
  [
17
8
  CountingButtonDemo.new()
@@ -19,8 +10,8 @@ demos(): List[Demo] {
19
10
  ]
20
11
  }
21
12
 
22
- new(): Document {
23
- ReadyDocument([
13
+ sections(): List[Section] {
14
+ [
24
15
  Section("Examples", [
25
16
  Paragraph([
26
17
  Text("Here you'll find examples that demonstrate what you can do with Firefly.")
@@ -36,5 +27,5 @@ new(): Document {
36
27
  Text("Check the list here for examples and demos.")
37
28
  ])
38
29
  ])
39
- ])
30
+ ]
40
31
  }
@@ -2,8 +2,8 @@ import Guide
2
2
  import CountingButtonDemo
3
3
  import MatchingPasswordsDemo
4
4
 
5
- new(): Document {
6
- ReadyDocument([
5
+ sections(): List[Section] {
6
+ [
7
7
  Section("The full-stack programming language", [
8
8
  Paragraph([
9
9
  Text("Firefly code runs in the browser and on the server, or even at build time. ")
@@ -340,5 +340,5 @@ new(): Document {
340
340
  ]
341
341
  ])
342
342
  ])
343
- ])
343
+ ]
344
344
  }
@@ -1,7 +1,7 @@
1
1
  import Guide
2
2
 
3
- new(): Document {
4
- ReadyDocument([
3
+ sections(): List[Section] {
4
+ [
5
5
  Section("Getting started", [
6
6
  Paragraph([
7
7
  Text("Firefly comes with a compiler, a build system and a package manager.")
@@ -41,5 +41,5 @@ new(): Document {
41
41
  Text("If you'd like to make an extension for a different editor, the language server is available, as well as the source code for the VSCode extension.")
42
42
  ])
43
43
  ])
44
- ])
44
+ ]
45
45
  }
@@ -6,20 +6,6 @@ import Token from ff:compiler
6
6
  import Styles
7
7
  import DocumentParser
8
8
 
9
- data Guide(
10
- prefix: String
11
- documents: List[Document]
12
- )
13
-
14
- data Document {
15
- ReadyDocument(
16
- sections: List[Section]
17
- )
18
- UnfetchedDocument(
19
- header: String
20
- )
21
- }
22
-
23
9
 
24
10
  data Section(heading: String) {
25
11
  Section(
@@ -49,83 +35,11 @@ data Inline {
49
35
  Anchor(heading: String, title: Option[String] = None)
50
36
  }
51
37
 
52
- extend self: Guide {
53
- heading(): String {
54
- self.documents.first().map {_.heading()}.else {""}
55
- }
56
- title(): String {
57
- if(self.heading() == "Firefly") {self.heading()} else {"Firefly " + self.heading()}
58
- }
59
- }
60
-
61
- extend self: Document {
62
- heading(): String {
63
- self.{
64
- | ReadyDocument(sections) =>
65
- sections.first().map {_.heading}.else {""}
66
- | UnfetchedDocument(header) => header
67
- }
68
- }
69
- title(guide: Guide): String {
70
- if(guide.documents.first().any {_.heading() == self.heading()}) {
71
- guide.title()
72
- } else {
73
- self.heading() + " · " + guide.title()
74
- }
75
- }
76
- path(guide: Guide): String {
77
- if(guide.documents.first().any {_.heading() == self.heading()}) {
78
- guide.prefix
79
- } else {
80
- guide.prefix + kebabCase(self.heading())
81
- }
82
- }
83
- }
84
-
85
38
  capability Demo(
86
39
  name: String
87
40
  render: Lux => Unit
88
41
  )
89
42
 
90
- data GuideDocument(
91
- guide: Guide
92
- document: Document
93
- )
94
-
95
- render(lux: Lux, httpClient: HttpClient, prefix: String, kebab: String, guides: List[Guide], demos: List[Demo]) {
96
- let guide = guides.find {_.prefix == prefix}.else {guides.grabFirst()}
97
- let document = guide.documents.find {d =>
98
- kebabCase(d.heading()) == kebab
99
- }.else {guide.documents.grabFirst()}
100
- let guideDocuments = guides.flatMap {guide => guide.documents.map {document =>
101
- GuideDocument(guide, document)
102
- }}
103
- let nextDocument = guideDocuments.dropWhile {n =>
104
- n.guide.prefix != guide.prefix || n.document.heading() != document.heading()
105
- }.dropFirst().first()
106
- lux.add("div") {
107
- lux.useState(False): menu, setMenu =>
108
- lux.cssClass(Styles.pageCss)
109
- lux.add("div") {
110
- lux.cssClass(Styles.guideCss)
111
- lux.add("main") {
112
- lux.cssClass(Styles.guideMainCss)
113
- renderDocument(lux, httpClient, prefix, document, demos, nextDocument)
114
- }
115
- renderTopbar(lux, menu, setMenu)
116
- lux.add("div") {
117
- lux.cssClass(Styles.guideSidebarBackdropCss)
118
- if(menu) {lux.cssClass(Styles.guideSidebarBackdropOpenCss)}
119
- lux.onClick {event =>
120
- event.preventDefault()
121
- setMenu(False)
122
- }
123
- }
124
- renderSidebar(lux, guides, guide, document.heading(), menu)
125
- }
126
- }
127
- }
128
-
129
43
  renderTopbar(lux: Lux, menu: Bool, setMenu: Bool => Unit) {
130
44
  lux.add("div") {
131
45
  lux.cssClass(Styles.guideTopbarCss)
@@ -150,113 +64,19 @@ renderTopbar(lux: Lux, menu: Bool, setMenu: Bool => Unit) {
150
64
  }
151
65
  }
152
66
 
153
- renderSidebar(lux: Lux, guides: List[Guide], selectedGuide: Guide, selectedHeading: String, menu: Bool) {
154
- lux.add("nav") {
155
- lux.cssClass(Styles.guideSidebarCss)
156
- if(menu) {lux.cssClass(Styles.guideSidebarOpenCss)}
157
- lux.add("a") {
158
- lux.set("href", "/")
159
- lux.cssClass(Styles.guideSidebarLogoCss)
160
- lux.add("img") {
161
- lux.set("src", "/assets/image/firefly-logo-yellow.webp")
162
- }
163
- lux.div {
164
- lux.text("Firefly")
165
- }
166
- }
167
- lux.add("form") {
168
- lux.set("role", "search")
169
- lux.add("input") {
170
- lux.set("aria-label", "Search")
171
- lux.cssClass(Styles.searchInputCss)
172
- lux.set("placeholder", "Search...")
173
- }
174
- }
175
- lux.add("ul") {
176
- lux.cssClass(Styles.guideSidebarUl1Css)
177
- guides.dropFirst().each {guide =>
178
- lux.add("li") {
179
- lux.cssClass(Styles.guideSidebarLi1Css)
180
- lux.add("a") {
181
- lux.cssClass(Styles.whiteLinkCss)
182
- let heading = guide.documents.grabFirst().heading()
183
- if(selectedGuide.prefix == guide.prefix && heading == selectedHeading) {
184
- lux.set("aria-current", "page")
185
- }
186
- lux.set("href", guide.prefix)
187
- lux.text(heading)
188
- }
189
- if(guide.documents.size() > 1):
190
- lux.add("ul") {
191
- lux.cssClass(Styles.guideSidebarUl2Css)
192
- guide.documents.dropFirst().each {document =>
193
- lux.add("li") {
194
- lux.cssClass(Styles.guideSidebarLi2Css)
195
- lux.add("a") {
196
- lux.cssClass(Styles.whiteLinkCss)
197
- let heading = document.heading()
198
- if(selectedGuide.prefix == guide.prefix && heading == selectedHeading) {
199
- lux.set("aria-current", "page")
200
- }
201
- lux.set("href", guide.prefix + kebabCase(heading))
202
- lux.text(heading)
203
- }
204
- }
205
- }
206
- }
207
- }
208
- }
209
- }
210
- }
211
- }
212
-
213
- renderDocument(
214
- lux: Lux
215
- http: HttpClient
216
- prefix: String
217
- document: Document
218
- demos: List[Demo]
219
- nextDocument: Option[GuideDocument]
220
- ) {
221
- lux.add("article") {
222
- lux.cssClass(Styles.guideDocumentCss)
223
- document.{
224
- | ReadyDocument(sections) =>
225
- renderSections(lux, sections, demos)
226
- renderNext(lux, nextDocument)
227
- | UnfetchedDocument(header) =>
228
- lux.div {
229
- lux.useSuspense {lux.div {lux.text("Loading document...")}}: lux =>
230
- let firstName = document.heading().split(' ').map {w =>
231
- let word = w.trim().lower()
232
- word.slice(0, 1).upper() + word.dropFirst()
233
- }.join()
234
- let asset = "/assets/markdown" + prefix + firstName + ".md"
235
- let markdown = http.get(asset, []) {_.readText()}
236
- let parser = DocumentParser(asset, markdown.split('\n'), 0)
237
- let sections = parser.parseDocument()
238
- renderSections(lux, sections, demos)
239
- renderNext(lux, nextDocument)
240
- }
241
- }
242
- }
243
- }
244
-
245
67
  renderSections(lux: Lux, sections: List[Section], demos: List[Demo]) {
246
68
  sections.pairs().each {| Pair(index, section) =>
247
69
  renderSection(lux, index == 0, section, demos)
248
70
  }
249
71
  }
250
72
 
251
- renderNext(lux: Lux, nextDocument: Option[GuideDocument]) {
252
- nextDocument.each {next =>
253
- lux.div {
254
- lux.cssClass(Styles.guideNextButtonCss)
255
- lux.add("a") {
256
- lux.cssClass(Styles.guideButtonCss)
257
- lux.set("href", next.document.path(next.guide))
258
- lux.text("Next: " + next.document.heading())
259
- }
73
+ renderNext(lux: Lux, path: String, title: String) {
74
+ lux.div {
75
+ lux.cssClass(Styles.guideNextButtonCss)
76
+ lux.add("a") {
77
+ lux.cssClass(Styles.guideButtonCss)
78
+ lux.set("href", path)
79
+ lux.text("Next: " + title)
260
80
  }
261
81
  }
262
82
  }
@@ -0,0 +1,66 @@
1
+ import WebServer from ff:webserver
2
+ import WebRoute from ff:webserver
3
+ import Lux from ff:lux
4
+ import Menu
5
+
6
+ serveHtml(
7
+ moduleName: String
8
+ title: String
9
+ pageData: Buffer
10
+ contentHtml: String
11
+ styleTags: String
12
+ request: WebRequest[WebResponse]
13
+ ): Unit {
14
+ request.writeHeader("Content-Type", "text/html; charset=UTF-8")
15
+ request.writeText("<!doctype html>")
16
+ request.writeText("<html lang='en' style='background-color: #ffffff; color: #333333; width: 100%; height: 100%; color-scheme: light;'>")
17
+ request.writeText("<head>")
18
+ request.writeText("<title>" + title + "</title>")
19
+ request.writeText("<meta name='viewport' content='width=device-width, initial-scale=1.0'>")
20
+ request.writeText("<meta name='theme-color' content='#ecc45e'>")
21
+ request.writeText("<script type='module' src='/js/ff/fireflysite/" + moduleName + ".run.mjs'></script>")
22
+ request.writeText("<script type='text/plain' id='data'>" + pageData.toLetter16() + "</script>")
23
+ request.writeText("<link rel='preload' href='/assets/font/firefly-mono.woff2' as='font' crossorigin='anonymous'>")
24
+ request.writeText("<link rel='preload' href='/assets/font/firefly-sans.woff2' as='font' crossorigin='anonymous'>")
25
+ request.writeText("<link rel='preload' href='/assets/image/firefly-logo-yellow.webp' as='image'>")
26
+ request.writeText("<style>@font-face { font-family: 'Firefly Mono'; font-display: fallback; src: url('/assets/font/firefly-mono.woff2'); }</style>")
27
+ request.writeText("<style>@font-face { font-family: 'Firefly Sans'; font-display: fallback; src: url('/assets/font/firefly-sans.woff2'); }</style>")
28
+ request.writeText(styleTags)
29
+ request.writeText("</head>")
30
+ request.writeText("<body style='margin: 0; padding: 0; width: 100%; height: 100%; touch-action: manipulation;'>")
31
+ request.writeText("<div id='main'>" + contentHtml + "</div>")
32
+ request.writeText("</body>")
33
+ request.writeText("</html>")
34
+ }
35
+
36
+ serve404(
37
+ request: WebRequest[WebResponse]
38
+ ): Unit {
39
+ request.writeText("404 Not Found")
40
+ request.writeStatus("404 Not Found")
41
+ }
42
+
43
+ title(menuEntry: MenuItem): String {
44
+ "Firefly · " + menuEntry.name
45
+ }
46
+
47
+ renderAndServe[T: Serializable](
48
+ system: NodeSystem
49
+ moduleName: String
50
+ pageData: T
51
+ title: String
52
+ request: WebRequest[WebResponse]
53
+ render: Lux => Unit
54
+ ) {
55
+ let serialized = Serializable.serialize(pageData)
56
+ let htmlAndCss = Lux.renderToString(system, render)
57
+ serveHtml(moduleName, title, serialized, htmlAndCss.first, htmlAndCss.second, request)
58
+ }
59
+
60
+ renderToMain[T: Serializable](system: BrowserSystem, render: (Lux, T) => Unit) {
61
+ let letter16 = system.js()->document->getElementById("data")->textContent.grabString()
62
+ let chat = Serializable.deserialize(Buffer.fromLetter16(letter16))
63
+ Lux.renderById(system, "main") {lux =>
64
+ render(lux, chat)
65
+ }
66
+ }
@@ -1,18 +1,22 @@
1
1
  import WebServer from ff:webserver
2
+ import WebRoute from ff:webserver
2
3
  import Lux from ff:lux
3
4
  import Css from ff:lux
4
5
  import Guide
5
6
  import GettingStarted
6
- import ReferenceAll
7
7
  import ExamplesOverview
8
8
  import PackagesOverview
9
9
  import CommunityOverview
10
10
  import FrontPage
11
11
  import Styles
12
+ import Router
12
13
 
13
14
  nodeMain(system: NodeSystem): Unit {
14
15
  let host = system.arguments().grab(0)
15
16
  let port = system.arguments().grab(1).grabInt()
17
+ let context = RouteContext(system)
18
+ let handlers = WebRouteHandler(Array.new())
19
+ Router.handlers(handlers)
16
20
  let cacheSalt = system.crypto().randomBuffer(16)
17
21
  system.writeLine("Listening on " + host + ":" + port)
18
22
  WebServer.new(system, host, port).listen {request =>
@@ -29,11 +33,8 @@ nodeMain(system: NodeSystem): Unit {
29
33
  request.writeHeader("Location", "https://www.firefly-lang.org" + path + parameters)
30
34
  request.writeStatus("302 Found")
31
35
  | ["js", ...] =>
32
- let asset = if(path == "/js/ff/fireflysite/Main.mjs" && system.assets().exists("/js/Main.bundle.js")) {
33
- "/js/Main.bundle.js"
34
- } else {
35
- path
36
- }
36
+ let asset = path.removeLast(".run.mjs").map {_ + ".run.bundle.js"}
37
+ .filter {system.assets().exists(_)}.else {path}
37
38
  serveAsset(system, cacheSalt, request, "text/javascript; charset=UTF-8", asset)
38
39
  | ["favicon.ico"] =>
39
40
  serveAsset(system, cacheSalt, request, "image/png", "/assets/image/firefly-logo-notext.png")
@@ -42,7 +43,7 @@ nodeMain(system: NodeSystem): Unit {
42
43
  | ["assets", "font", "firefly-sans.woff2"] =>
43
44
  serveAsset(system, cacheSalt, request, "font/woff2", "/assets/font/gf-nunito-sans-latin.woff2")
44
45
  | ["assets", ...rest] => serveAssets(system, cacheSalt, request, rest)
45
- | _ {serveGuide(system, request)} =>
46
+ | _ {handlers.handle(request, context)} =>
46
47
  | _ =>
47
48
  request.writeText("404 Not Found")
48
49
  request.writeStatus("404 Not Found")
@@ -50,54 +51,11 @@ nodeMain(system: NodeSystem): Unit {
50
51
  }
51
52
  }
52
53
 
53
- guides = [
54
- Guide("/front/", [FrontPage.new()])
55
- Guide("/getting-started/", [GettingStarted.new()])
56
- Guide("/examples/", ExamplesOverview.mock())
57
- Guide("/reference/", ReferenceAll.mock())
58
- Guide("/packages/", [PackagesOverview.new()])
59
- Guide("/community/", [CommunityOverview.new()])
60
- ]
61
-
62
- serveGuide(system: NodeSystem, request: WebRequest[WebResponse]): Bool {
63
- let demos = ExamplesOverview.demos()
64
- mutable served = False
65
- guides.each {guide =>
66
- if(isGuidePath(request.readPath(), guide.prefix)):
67
- let kebab = request.readPath().dropFirst(guide.prefix.size())
68
- let htmlAndCss = Lux.renderToString(system) {lux =>
69
- Guide.render(lux, system.httpClient(), guide.prefix, kebab, guides, demos)
70
- }
71
- let title = guide.documents.find {d =>
72
- Guide.kebabCase(d.heading()) == kebab
73
- }.map {_.title(guide)}.else {guide.title()}
74
- serveGuideHtml(title, htmlAndCss.first, htmlAndCss.second, request)
75
- served = True
76
- }
77
- served
78
- }
79
-
80
- browserMain(system: BrowserSystem): Unit {
81
- let demos = ExamplesOverview.demos()
82
- guides.collect {guide =>
83
- if(isGuidePath(system.urlPath(), guide.prefix)):
84
- Lux.renderById(system, "main") {lux =>
85
- let kebab = system.urlPath().dropFirst(guide.prefix.size())
86
- Guide.render(lux, system.httpClient(), guide.prefix, kebab, guides, demos)
87
- }
88
- }
89
- }
90
-
91
- isGuidePath(path: String, prefix: String): Bool {
92
- //Log.debug(path + " vs. " + prefix)
93
- path.startsWith(prefix) || (prefix == "/front/" && (path == "/" || path == "/index.html")) // Why is index.html requested
94
- }
95
-
96
54
  buildMain(system: BuildSystem) {
97
- let browser = system.compileForBrowser("Main.ff") // .bundle()
98
- let assets = AssetSystem.create()
99
- .addAssets("/js/", browser.assets())
100
- .addAssets("/assets/", system.packageAssets().assets("/assets/"))
55
+ mutable assets = AssetSystem.create()
56
+ let browserAssets = system.bundleForBrowser(Router.modulesWithBrowserMain().map {_ + ".ff"})
57
+ assets = assets.addAssets("/js/", browserAssets)
58
+ assets = assets.addAssets("/assets/", system.packageAssets().assets("/assets/"))
101
59
  system.setAssets(assets)
102
60
  }
103
61
 
@@ -127,7 +85,7 @@ guessContentType(path: String): Option[String] {
127
85
  serveAsset(system: NodeSystem, salt: Buffer, request: WebRequest[WebResponse], contentType: String, asset: String): Unit {
128
86
  let etag = "\"" + system.crypto().hmacSha256(salt, asset.toBuffer()).toHex() + "\""
129
87
  request.writeHeader("Content-Type", contentType)
130
- if(request.readHeader("host") == Some("localhost:8080")) {
88
+ if(request.readHeader("host") == Some("localhost:8080") && False) {
131
89
  request.writeHeader("Cache-Control", "no-cache")
132
90
  } else {
133
91
  request.writeHeader("Cache-Control", "public, max-age=60, must-revalidate")
@@ -139,25 +97,3 @@ serveAsset(system: NodeSystem, salt: Buffer, request: WebRequest[WebResponse], c
139
97
  request.writeStream(system.assets().readStream(asset))
140
98
  }
141
99
  }
142
-
143
- serveGuideHtml(title: String, contentHtml: String, styleTags: String, request: WebRequest[WebResponse]): Unit {
144
- request.writeHeader("Content-Type", "text/html; charset=UTF-8")
145
- request.writeText("<!doctype html>")
146
- request.writeText("<html lang='en' style='background-color: #ffffff; color: #333333; width: 100%; height: 100%; color-scheme: light;'>")
147
- request.writeText("<head>")
148
- request.writeText("<title>" + title + "</title>")
149
- request.writeText("<meta name='viewport' content='width=device-width, initial-scale=1.0'>")
150
- request.writeText("<meta name='theme-color' content='#ecc45e'>")
151
- request.writeText("<script type='module' src='/js/ff/fireflysite/Main.mjs'></script>")
152
- request.writeText("<link rel='preload' href='/assets/font/firefly-mono.woff2' as='font' crossorigin='anonymous'>")
153
- request.writeText("<link rel='preload' href='/assets/font/firefly-sans.woff2' as='font' crossorigin='anonymous'>")
154
- request.writeText("<link rel='preload' href='/assets/image/firefly-logo-yellow.webp' as='image'>")
155
- request.writeText("<style>@font-face { font-family: 'Firefly Mono'; font-display: fallback; src: url('/assets/font/firefly-mono.woff2'); }</style>")
156
- request.writeText("<style>@font-face { font-family: 'Firefly Sans'; font-display: fallback; src: url('/assets/font/firefly-sans.woff2'); }</style>")
157
- request.writeText(styleTags)
158
- request.writeText("</head>")
159
- request.writeText("<body style='margin: 0; padding: 0; width: 100%; height: 100%; touch-action: manipulation;'>")
160
- request.writeText("<div id='main'>" + contentHtml + "</div>")
161
- request.writeText("</body>")
162
- request.writeText("</html>")
163
- }
@@ -39,8 +39,8 @@ render(lux: Lux): Unit {
39
39
  }
40
40
  }
41
41
 
42
- newDocument(): Document {
43
- ReadyDocument([
42
+ sections(): List[Section] {
43
+ [
44
44
  Section(name, [
45
45
  Paragraph([Text("Two passwords fields and a check that they match.")])
46
46
  ])
@@ -78,5 +78,5 @@ newDocument(): Document {
78
78
  }
79
79
  """, firefly = True)
80
80
  ])
81
- ])
81
+ ]
82
82
  }
@@ -0,0 +1,59 @@
1
+ data Menu(
2
+ path: String
3
+ name: String
4
+ menu: List[Menu]
5
+ )
6
+
7
+ data MenuItem(
8
+ path: List[String]
9
+ name: String
10
+ next: Option[MenuItem]
11
+ )
12
+
13
+ menu: List[Menu] = [
14
+ Menu("getting-started", "Getting started", [])
15
+ Menu("examples", "Examples", [
16
+ Menu("counting-button", "Counting button", [])
17
+ Menu("matching-passwords", "Matching passwords", [])
18
+ Menu("connecting-to-postgresql", "Connecting to PostgreSQL", [])
19
+ ])
20
+ Menu("reference", "Reference", [
21
+ Menu("modules-and-packages", "Modules and packages", [])
22
+ Menu("base-types", "Base types", [])
23
+ Menu("user-defined-types", "User defined types", [])
24
+ Menu("statements-and-expressions", "Statements and expressions", [])
25
+ Menu("functions-and-methods", "Functions and methods", [])
26
+ Menu("pattern-matching", "Pattern matching", [])
27
+ Menu("traits-and-instances", "Traits and instances", [])
28
+ Menu("exceptions", "Exceptions", [])
29
+ Menu("structured-concurrency", "Structured concurrency", [])
30
+ Menu("javascript-interop", "JavaScript interop", [])
31
+
32
+ ])
33
+ Menu("packages", "Packages", [])
34
+ Menu("community", "Community", [])
35
+ ]
36
+
37
+ menuItems = flattenMenus(menu)
38
+
39
+ findItem(path: List[String]): Option[MenuItem] {
40
+ menuItems.find {_.path == path}
41
+ }
42
+
43
+ flattenMenus(menus: List[Menu]): List[MenuItem] {
44
+ let result = Array.new()
45
+ function go(menus: List[Menu], prefix: List[String]) {
46
+ menus.each {menu =>
47
+ let currentPath = [...prefix, menu.path]
48
+ let item = MenuItem(currentPath, menu.name, None)
49
+ result.last().map {previous =>
50
+ result.set(result.size() - 1, previous.MenuItem(next = Some(item)))
51
+ }
52
+ result.push(item)
53
+ go(menu.menu, currentPath)
54
+ }
55
+ }
56
+ go(menus, [])
57
+ result.toList()
58
+ }
59
+
@@ -2,8 +2,8 @@ import Guide
2
2
  import CountingButtonDemo
3
3
  import MatchingPasswordsDemo
4
4
 
5
- new(): Document {
6
- ReadyDocument([
5
+ sections(): List[Section] {
6
+ [
7
7
  Section("Packages", [
8
8
  Paragraph([
9
9
  Text("Here you'll eventually find the package index.")
@@ -45,5 +45,5 @@ new(): Document {
45
45
  Text("A FFI package for JavaScript.")
46
46
  ])
47
47
  ])
48
- ])
48
+ ]
49
49
  }
@@ -4,8 +4,8 @@ import LuxEvent from ff:lux
4
4
 
5
5
  name = "Connecting to PostgreSQL"
6
6
 
7
- newDocument(): Document {
8
- ReadyDocument([
7
+ sections(): List[Section] {
8
+ [
9
9
  Section(name, [
10
10
  Paragraph([Text("Connect to a PostgreSQL database and run a query.")])
11
11
  Paragraph([Text("Add a dependency on "), Code("ff:postgresql"), Text(":")])
@@ -30,5 +30,5 @@ newDocument(): Document {
30
30
  """, firefly = True)
31
31
  Paragraph([Text("Note that"), Code(".map", firefly = True), Text("here runs for each row of the result.")])
32
32
  ])
33
- ])
33
+ ]
34
34
  }