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.
- package/bin/Release.ff +3 -3
- package/bin/firefly.mjs +1 -1
- package/compiler/Builder.ff +11 -5
- package/compiler/Compiler.ff +14 -3
- package/compiler/JsEmitter.ff +6 -11
- package/compiler/Main.ff +10 -15
- package/compiler/ModuleCache.ff +2 -1
- package/core/Buffer.ff +26 -4
- package/core/BuildSystem.ff +49 -46
- package/core/JsValue.ff +5 -0
- package/experimental/date/Date.ff +604 -0
- package/experimental/date/Main.ff +51 -0
- package/experimental/random/AsciiBuffer.ff +15 -0
- package/experimental/rhymeapp/Main.ff +2 -3
- package/experimental/site/Main.ff +2 -2
- package/experimental/site2/Main.ff +2 -2
- package/firefly.sh +1 -1
- package/fireflysite/CommunityOverview.ff +3 -3
- package/fireflysite/CountingButtonDemo.ff +3 -3
- package/fireflysite/ExamplesOverview.ff +3 -12
- package/fireflysite/FrontPage.ff +3 -3
- package/fireflysite/GettingStarted.ff +3 -3
- package/fireflysite/Guide.ff +7 -187
- package/fireflysite/Html.ff +66 -0
- package/fireflysite/Main.ff +13 -77
- package/fireflysite/MatchingPasswordsDemo.ff +3 -3
- package/fireflysite/Menu.ff +59 -0
- package/fireflysite/PackagesOverview.ff +3 -3
- package/fireflysite/PostgresqlDemo.ff +3 -3
- package/fireflysite/RouteFront.ff +30 -0
- package/fireflysite/RouteNonMarkdown.ff +48 -0
- package/fireflysite/RouteReference.ff +40 -0
- package/fireflysite/Router.ff +33 -0
- package/fireflysite/Website.ff +133 -0
- package/fireflysite/assets/markdown/reference/{FunctionsAndMethods.md → functions-and-methods.md} +1 -1
- package/fireflysite/assets/markdown/reference/{StatementsAndExpressions.md → statements-and-expressions.md} +0 -1
- package/fireflysite/assets/markdown/reference/{StructuredConcurrency.md → structured-concurrency.md} +0 -1
- package/fireflysite/assets/markdown/reference.md +3 -0
- package/graph/.firefly/package.ff +1 -0
- package/graph/Graph.ff +79 -0
- package/lsp/CompletionHandler.ff +2 -2
- package/lux/CssTest.ff +2 -2
- package/lux/Main.ff +2 -2
- package/lux/Main2.ff +2 -2
- package/output/js/ff/compiler/Builder.mjs +44 -12
- package/output/js/ff/compiler/Compiler.mjs +28 -10
- package/output/js/ff/compiler/Dependencies.mjs +0 -2
- package/output/js/ff/compiler/DependencyLock.mjs +0 -2
- package/output/js/ff/compiler/Deriver.mjs +0 -2
- package/output/js/ff/compiler/Dictionaries.mjs +0 -2
- package/output/js/ff/compiler/Environment.mjs +0 -2
- package/output/js/ff/compiler/Inference.mjs +0 -2
- package/output/js/ff/compiler/JsEmitter.mjs +12 -30
- package/output/js/ff/compiler/JsImporter.mjs +0 -2
- package/output/js/ff/compiler/LspHook.mjs +0 -2
- package/output/js/ff/compiler/Main.mjs +24 -47
- package/output/js/ff/compiler/Main.run.mjs +25 -0
- package/output/js/ff/compiler/ModuleCache.mjs +4 -6
- package/output/js/ff/compiler/Parser.mjs +0 -2
- package/output/js/ff/compiler/Patterns.mjs +0 -2
- package/output/js/ff/compiler/Resolver.mjs +0 -2
- package/output/js/ff/compiler/Substitution.mjs +0 -2
- package/output/js/ff/compiler/Syntax.mjs +0 -2
- package/output/js/ff/compiler/Token.mjs +0 -2
- package/output/js/ff/compiler/Tokenizer.mjs +0 -2
- package/output/js/ff/compiler/Unification.mjs +0 -2
- package/output/js/ff/compiler/Wildcards.mjs +0 -2
- package/output/js/ff/compiler/Workspace.mjs +0 -2
- package/output/js/ff/core/Any.mjs +0 -2
- package/output/js/ff/core/Array.mjs +0 -2
- package/output/js/ff/core/AssetSystem.mjs +0 -2
- package/output/js/ff/core/Atomic.mjs +0 -2
- package/output/js/ff/core/Bool.mjs +0 -2
- package/output/js/ff/core/BrowserSystem.mjs +0 -2
- package/output/js/ff/core/Buffer.mjs +50 -10
- package/output/js/ff/core/BuildSystem.mjs +92 -72
- package/output/js/ff/core/Channel.mjs +0 -2
- package/output/js/ff/core/Char.mjs +0 -2
- package/output/js/ff/core/Core.mjs +0 -2
- package/output/js/ff/core/Crypto.mjs +0 -2
- package/output/js/ff/core/Date.mjs +0 -2
- package/output/js/ff/core/Duration.mjs +0 -2
- package/output/js/ff/core/Equal.mjs +0 -2
- package/output/js/ff/core/Error.mjs +0 -2
- package/output/js/ff/core/FileHandle.mjs +0 -2
- package/output/js/ff/core/Float.mjs +0 -2
- package/output/js/ff/core/HttpClient.mjs +0 -2
- package/output/js/ff/core/Int.mjs +0 -2
- package/output/js/ff/core/IntMap.mjs +0 -2
- package/output/js/ff/core/Js.mjs +0 -2
- package/output/js/ff/core/JsSystem.mjs +0 -2
- package/output/js/ff/core/JsValue.mjs +8 -2
- package/output/js/ff/core/Json.mjs +0 -2
- package/output/js/ff/core/List.mjs +0 -2
- package/output/js/ff/core/Lock.mjs +0 -2
- package/output/js/ff/core/Log.mjs +0 -2
- package/output/js/ff/core/Map.mjs +0 -2
- package/output/js/ff/core/NodeSystem.mjs +0 -2
- package/output/js/ff/core/Nothing.mjs +0 -2
- package/output/js/ff/core/Option.mjs +0 -2
- package/output/js/ff/core/Ordering.mjs +0 -2
- package/output/js/ff/core/Pair.mjs +0 -2
- package/output/js/ff/core/Path.mjs +0 -2
- package/output/js/ff/core/Queue.mjs +0 -2
- package/output/js/ff/core/Random.mjs +0 -2
- package/output/js/ff/core/RbMap.mjs +0 -2
- package/output/js/ff/core/Serializable.mjs +0 -2
- package/output/js/ff/core/Set.mjs +0 -2
- package/output/js/ff/core/Show.mjs +0 -2
- package/output/js/ff/core/SourceLocation.mjs +0 -2
- package/output/js/ff/core/Stream.mjs +0 -2
- package/output/js/ff/core/String.mjs +0 -2
- package/output/js/ff/core/StringMap.mjs +0 -2
- package/output/js/ff/core/Task.mjs +0 -2
- package/output/js/ff/core/Try.mjs +0 -2
- package/output/js/ff/core/Unit.mjs +0 -2
- package/package.json +1 -1
- package/vscode/client/src/extension.ts +1 -1
- package/vscode/package.json +1 -1
- package/vscode/snippets.json +2 -2
- package/webserver/WebRoute.ff +51 -14
- package/fireflysite/ReferenceAll.ff +0 -18
- /package/fireflysite/assets/markdown/reference/{BaseTypes.md → base-types.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{EmittedJavascript.md → emitted-javascript.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{Exceptions.md → exceptions.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{JavascriptInterop.md → javascript-interop.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{ModulesAndPackages.md → modules-and-packages.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{PatternMatching.md → pattern-matching.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{TraitsAndInstances.md → traits-and-instances.md} +0 -0
- /package/fireflysite/assets/markdown/reference/{UserDefinedTypes.md → user-defined-types.md} +0 -0
- /package/fireflysite/assets/markdown/scratch/{ControlFlow.md → control-flow.md} +0 -0
- /package/fireflysite/assets/markdown/{reference/OldStructuredConcurrency.md → scratch/old-structured-concurrency.md} +0 -0
package/firefly.sh
CHANGED
|
@@ -2,8 +2,8 @@ import Guide
|
|
|
2
2
|
import CountingButtonDemo
|
|
3
3
|
import MatchingPasswordsDemo
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
}
|
package/fireflysite/FrontPage.ff
CHANGED
|
@@ -2,8 +2,8 @@ import Guide
|
|
|
2
2
|
import CountingButtonDemo
|
|
3
3
|
import MatchingPasswordsDemo
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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
|
}
|
package/fireflysite/Guide.ff
CHANGED
|
@@ -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,
|
|
252
|
-
|
|
253
|
-
lux.
|
|
254
|
-
|
|
255
|
-
lux.
|
|
256
|
-
|
|
257
|
-
|
|
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
|
+
}
|
package/fireflysite/Main.ff
CHANGED
|
@@ -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 =
|
|
33
|
-
|
|
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
|
-
| _ {
|
|
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
|
-
|
|
98
|
-
let
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
}
|