@xnoxs/flux-lang 3.1.1
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/CHANGELOG.md +103 -0
- package/README.md +1089 -0
- package/bin/flux.js +1397 -0
- package/dist/flux.cjs.js +6664 -0
- package/dist/flux.esm.js +6674 -0
- package/dist/flux.min.js +263 -0
- package/index.d.ts +202 -0
- package/index.js +26 -0
- package/package.json +77 -0
- package/scripts/build.js +76 -0
- package/src/bundler.js +216 -0
- package/src/checker.js +322 -0
- package/src/codegen.js +785 -0
- package/src/css-preprocessor.js +399 -0
- package/src/formatter.js +140 -0
- package/src/jsx.js +480 -0
- package/src/lexer.js +518 -0
- package/src/linter.js +758 -0
- package/src/mangler.js +280 -0
- package/src/parser.js +1671 -0
- package/src/self/bundler.flux +167 -0
- package/src/self/bundler.js +187 -0
- package/src/self/checker.flux +249 -0
- package/src/self/checker.js +338 -0
- package/src/self/codegen.flux +555 -0
- package/src/self/codegen.js +784 -0
- package/src/self/css-preprocessor.flux +373 -0
- package/src/self/css-preprocessor.js +387 -0
- package/src/self/formatter.flux +93 -0
- package/src/self/formatter.js +114 -0
- package/src/self/jsx.flux +430 -0
- package/src/self/jsx.js +396 -0
- package/src/self/lexer.flux +529 -0
- package/src/self/lexer.js +709 -0
- package/src/self/lexer.stage2.js +700 -0
- package/src/self/linter.flux +515 -0
- package/src/self/linter.js +804 -0
- package/src/self/mangler.flux +253 -0
- package/src/self/mangler.js +348 -0
- package/src/self/parser.flux +1146 -0
- package/src/self/parser.js +1571 -0
- package/src/self/sourcemap.flux +66 -0
- package/src/self/sourcemap.js +72 -0
- package/src/self/stdlib.flux +356 -0
- package/src/self/stdlib.js +396 -0
- package/src/self/test-runner.flux +201 -0
- package/src/self/test-runner.js +132 -0
- package/src/self/transpiler.flux +123 -0
- package/src/self/transpiler.js +83 -0
- package/src/self/type-checker.flux +821 -0
- package/src/self/type-checker.js +1106 -0
- package/src/sourcemap.js +82 -0
- package/src/stdlib.js +436 -0
- package/src/test-runner.js +239 -0
- package/src/transpiler.js +172 -0
- package/src/type-checker.js +1206 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Flux Self-Hosted JSX Preprocessor
|
|
3
|
+
// src/self/jsx.flux — written in Flux, compiled by stage-0
|
|
4
|
+
//
|
|
5
|
+
// Transforms <jsx> syntax into _fluxH() calls before lexing.
|
|
6
|
+
// <div class="x">text</div> → _fluxH("div",{"class":"x"},"text")
|
|
7
|
+
// <br /> → _fluxH("br",null)
|
|
8
|
+
// <>...</> → _fluxH("",null,...)
|
|
9
|
+
// ============================================================
|
|
10
|
+
|
|
11
|
+
val JSX_STYLE_VALUE_PROPS = {
|
|
12
|
+
bg: v -> "background:" + v,
|
|
13
|
+
fg: v -> "color:" + v,
|
|
14
|
+
color: v -> "color:" + v,
|
|
15
|
+
p: v -> "padding:" + v,
|
|
16
|
+
px: v -> "paddingLeft:" + v + ",paddingRight:" + v,
|
|
17
|
+
py: v -> "paddingTop:" + v + ",paddingBottom:" + v,
|
|
18
|
+
pt: v -> "paddingTop:" + v,
|
|
19
|
+
pb: v -> "paddingBottom:" + v,
|
|
20
|
+
pl: v -> "paddingLeft:" + v,
|
|
21
|
+
pr: v -> "paddingRight:" + v,
|
|
22
|
+
m: v -> "margin:" + v,
|
|
23
|
+
mx: v -> "marginLeft:" + v + ",marginRight:" + v,
|
|
24
|
+
my: v -> "marginTop:" + v + ",marginBottom:" + v,
|
|
25
|
+
mt: v -> "marginTop:" + v,
|
|
26
|
+
mb: v -> "marginBottom:" + v,
|
|
27
|
+
ml: v -> "marginLeft:" + v,
|
|
28
|
+
mr: v -> "marginRight:" + v,
|
|
29
|
+
radius: v -> "borderRadius:" + v,
|
|
30
|
+
w: v -> "width:" + v,
|
|
31
|
+
h: v -> "height:" + v,
|
|
32
|
+
"min-w": v -> "minWidth:" + v,
|
|
33
|
+
"max-w": v -> "maxWidth:" + v,
|
|
34
|
+
"min-h": v -> "minHeight:" + v,
|
|
35
|
+
"max-h": v -> "maxHeight:" + v,
|
|
36
|
+
gap: v -> "gap:" + v,
|
|
37
|
+
"col-gap": v -> "columnGap:" + v,
|
|
38
|
+
"row-gap": v -> "rowGap:" + v,
|
|
39
|
+
text: v -> "fontSize:" + v,
|
|
40
|
+
font: v -> "fontFamily:" + v,
|
|
41
|
+
weight: v -> "fontWeight:" + v,
|
|
42
|
+
tracking: v -> "letterSpacing:" + v,
|
|
43
|
+
leading: v -> "lineHeight:" + v,
|
|
44
|
+
shadow: v -> "boxShadow:" + v,
|
|
45
|
+
opacity: v -> "opacity:" + v,
|
|
46
|
+
border: v -> "border:" + v,
|
|
47
|
+
outline: v -> "outline:" + v,
|
|
48
|
+
transition: v -> "transition:" + v,
|
|
49
|
+
cursor: v -> "cursor:" + v,
|
|
50
|
+
overflow: v -> "overflow:" + v,
|
|
51
|
+
z: v -> "zIndex:" + v,
|
|
52
|
+
transform: v -> "transform:" + v,
|
|
53
|
+
direction: v -> "flexDirection:" + v,
|
|
54
|
+
align: v -> "alignItems:" + v,
|
|
55
|
+
justify: v -> "justifyContent:" + v,
|
|
56
|
+
"align-self": v -> "alignSelf:" + v,
|
|
57
|
+
"place-items":v -> "placeItems:" + v,
|
|
58
|
+
grow: v -> "flexGrow:" + v,
|
|
59
|
+
shrink: v -> "flexShrink:" + v,
|
|
60
|
+
basis: v -> "flexBasis:" + v,
|
|
61
|
+
cols: v -> "gridTemplateColumns:" + v,
|
|
62
|
+
rows: v -> "gridTemplateRows:" + v,
|
|
63
|
+
inset: v -> "inset:" + v,
|
|
64
|
+
top: v -> "top:" + v,
|
|
65
|
+
right: v -> "right:" + v,
|
|
66
|
+
bottom: v -> "bottom:" + v,
|
|
67
|
+
left: v -> "left:" + v,
|
|
68
|
+
"object-fit": v -> "objectFit:" + v,
|
|
69
|
+
"line-height":v -> "lineHeight:" + v,
|
|
70
|
+
"text-align": v -> "textAlign:" + v,
|
|
71
|
+
decoration: v -> "textDecoration:" + v,
|
|
72
|
+
clip: v -> "clipPath:" + v,
|
|
73
|
+
filter: v -> "filter:" + v,
|
|
74
|
+
backdrop: v -> "backdropFilter:" + v,
|
|
75
|
+
animation: v -> "animation:" + v,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
val JSX_STYLE_BOOL_PROPS = {
|
|
79
|
+
flex: 'display:"flex"',
|
|
80
|
+
grid: 'display:"grid"',
|
|
81
|
+
block: 'display:"block"',
|
|
82
|
+
"inline-flex": 'display:"inline-flex"',
|
|
83
|
+
"inline-block": 'display:"inline-block"',
|
|
84
|
+
bold: "fontWeight:700",
|
|
85
|
+
italic: 'fontStyle:"italic"',
|
|
86
|
+
underline: 'textDecoration:"underline"',
|
|
87
|
+
pointer: 'cursor:"pointer"',
|
|
88
|
+
hidden: 'display:"none"',
|
|
89
|
+
relative: 'position:"relative"',
|
|
90
|
+
absolute: 'position:"absolute"',
|
|
91
|
+
fixed: 'position:"fixed"',
|
|
92
|
+
sticky: 'position:"sticky"',
|
|
93
|
+
"flex-col": 'flexDirection:"column"',
|
|
94
|
+
"flex-row": 'flexDirection:"row"',
|
|
95
|
+
"flex-wrap": 'flexWrap:"wrap"',
|
|
96
|
+
"flex-1": "flex:1",
|
|
97
|
+
"w-full": 'width:"100%"',
|
|
98
|
+
"h-full": 'height:"100%"',
|
|
99
|
+
center: 'textAlign:"center"',
|
|
100
|
+
truncate: 'overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"',
|
|
101
|
+
"select-none": 'userSelect:"none"',
|
|
102
|
+
"no-wrap": 'whiteSpace:"nowrap"',
|
|
103
|
+
"no-list": 'listStyle:"none"',
|
|
104
|
+
"no-outline": 'outline:"none"',
|
|
105
|
+
"no-border": 'border:"none"',
|
|
106
|
+
"box-border": 'boxSizing:"border-box"',
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export val FLUX_H_BROWSER = `
|
|
110
|
+
function _fluxH(tag,props,...children){
|
|
111
|
+
if(tag===""){const f=document.createDocumentFragment();children.flat(Infinity).forEach(c=>{if(c==null)return;f.appendChild(c instanceof Node?c:document.createTextNode(String(c)));});return f;}
|
|
112
|
+
const el=document.createElement(tag);
|
|
113
|
+
if(props){for(const[k,v]of Object.entries(props)){if(k==="class"||k==="className"){el.className=v;}else if(k==="style"&&typeof v==="object"){Object.assign(el.style,v);}else if(k.startsWith("on")&&typeof v==="function"){el.addEventListener(k.slice(2).toLowerCase(),v);}else if(typeof v==="boolean"){if(v)el.setAttribute(k,"");}else{el.setAttribute(k,String(v));}}}
|
|
114
|
+
children.flat(Infinity).forEach(c=>{if(c==null)return;el.appendChild(c instanceof Node?c:document.createTextNode(String(c)));});
|
|
115
|
+
return el;
|
|
116
|
+
}`
|
|
117
|
+
|
|
118
|
+
export val FLUX_H_SERVER = `
|
|
119
|
+
function _fluxH(tag,props,...children){
|
|
120
|
+
const VOID=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);
|
|
121
|
+
if(tag==="")return children.flat(Infinity).map(c=>c==null?"":String(c)).join("");
|
|
122
|
+
let attrs="";
|
|
123
|
+
if(props){for(const[k,v]of Object.entries(props)){if(k==="class"||k==="className")attrs+=` + '` class="${v}"`' + `;else if(k==="style"&&typeof v==="object")attrs+=` + '` style="${Object.entries(v).map(([p,val])=>p.replace(/[A-Z]/g,m=>"-"+m.toLowerCase())+":"+val).join(";")}"`' + `;else if(typeof v!=="function"&&typeof v!=="boolean")attrs+=` + '` ${k}="${String(v).replace(/"/g,""")}"`' + `;else if(v===true)attrs+=` + '` ${k}`' + `;}}
|
|
124
|
+
const inner=children.flat(Infinity).map(c=>c==null?"":String(c)).join("");
|
|
125
|
+
if(VOID.has(tag))return` + '`<${tag}${attrs}>`' + `;
|
|
126
|
+
return` + '`<${tag}${attrs}>${inner}</${tag}>`' + `;
|
|
127
|
+
}`
|
|
128
|
+
|
|
129
|
+
export val FLUX_CSS_BROWSER = `
|
|
130
|
+
function _fluxCSS(css){const s=document.createElement("style");s.textContent=css;document.head.appendChild(s);return css;}`
|
|
131
|
+
|
|
132
|
+
export val FLUX_CSS_SERVER = `
|
|
133
|
+
function _fluxCSS(css){return css;}`
|
|
134
|
+
|
|
135
|
+
export class JsxPreprocessor:
|
|
136
|
+
src: any
|
|
137
|
+
pos: int
|
|
138
|
+
out: any
|
|
139
|
+
hasJsx: bool
|
|
140
|
+
|
|
141
|
+
fn transform():
|
|
142
|
+
while self.pos < self.src.length:
|
|
143
|
+
self.scanTop()
|
|
144
|
+
self.hasJsx = self.out != self.src
|
|
145
|
+
return self.out
|
|
146
|
+
|
|
147
|
+
fn scanTop():
|
|
148
|
+
val c = self.src[self.pos]
|
|
149
|
+
|
|
150
|
+
// Line comment
|
|
151
|
+
if c == "/" and self.src[self.pos + 1] == "/":
|
|
152
|
+
val end_ = self.src.indexOf("\n", self.pos)
|
|
153
|
+
if end_ == -1:
|
|
154
|
+
self.out = self.out + self.src.slice(self.pos)
|
|
155
|
+
self.pos = self.src.length
|
|
156
|
+
else:
|
|
157
|
+
self.out = self.out + self.src.slice(self.pos, end_ + 1)
|
|
158
|
+
self.pos = end_ + 1
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
// Block comment
|
|
162
|
+
if c == "/" and self.src[self.pos + 1] == "*":
|
|
163
|
+
val end_ = self.src.indexOf("*/", self.pos + 2)
|
|
164
|
+
if end_ == -1:
|
|
165
|
+
self.out = self.out + self.src.slice(self.pos)
|
|
166
|
+
self.pos = self.src.length
|
|
167
|
+
else:
|
|
168
|
+
self.out = self.out + self.src.slice(self.pos, end_ + 2)
|
|
169
|
+
self.pos = end_ + 2
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
if c == '"':
|
|
173
|
+
self.passString('"')
|
|
174
|
+
return
|
|
175
|
+
if c == "'":
|
|
176
|
+
self.passString("'")
|
|
177
|
+
return
|
|
178
|
+
if c == "`":
|
|
179
|
+
self.passTemplateLit()
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
// Potential JSX open
|
|
183
|
+
if c == "<" and self.isJsxStart():
|
|
184
|
+
val jsxCode = self.parseJsxElement()
|
|
185
|
+
self.out = self.out + jsxCode
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
self.out = self.out + c
|
|
189
|
+
self.pos = self.pos + 1
|
|
190
|
+
|
|
191
|
+
fn isJsxStart():
|
|
192
|
+
val next = self.src[self.pos + 1] ?? ""
|
|
193
|
+
if next != ">" and not (next >= "a" and next <= "z") and not (next >= "A" and next <= "Z"):
|
|
194
|
+
return false
|
|
195
|
+
var i = self.pos - 1
|
|
196
|
+
while i >= 0 and (self.src[i] == " " or self.src[i] == "\t"):
|
|
197
|
+
i = i - 1
|
|
198
|
+
if i < 0: return true
|
|
199
|
+
val prev = self.src[i]
|
|
200
|
+
if '=([{,:>\n?'.includes(prev): return true
|
|
201
|
+
if /[a-z]/.test(prev):
|
|
202
|
+
var j = i
|
|
203
|
+
while j >= 0 and /[a-z]/.test(self.src[j]):
|
|
204
|
+
j = j - 1
|
|
205
|
+
val word = self.src.slice(j + 1, i + 1)
|
|
206
|
+
val exprKw = new Set(["return", "not", "and", "or", "val", "var", "await", "yield", "else", "in", "throw"])
|
|
207
|
+
if exprKw.has(word): return true
|
|
208
|
+
return false
|
|
209
|
+
|
|
210
|
+
fn passString(quote):
|
|
211
|
+
self.out = self.out + quote
|
|
212
|
+
self.pos = self.pos + 1
|
|
213
|
+
while self.pos < self.src.length:
|
|
214
|
+
val c = self.src[self.pos]
|
|
215
|
+
if c == "\\":
|
|
216
|
+
self.out = self.out + c + (self.src[self.pos + 1] ?? "")
|
|
217
|
+
self.pos = self.pos + 2
|
|
218
|
+
continue
|
|
219
|
+
if c == quote:
|
|
220
|
+
self.out = self.out + c
|
|
221
|
+
self.pos = self.pos + 1
|
|
222
|
+
return
|
|
223
|
+
self.out = self.out + c
|
|
224
|
+
self.pos = self.pos + 1
|
|
225
|
+
|
|
226
|
+
fn passTemplateLit():
|
|
227
|
+
self.out = self.out + "`"
|
|
228
|
+
self.pos = self.pos + 1
|
|
229
|
+
while self.pos < self.src.length:
|
|
230
|
+
val c = self.src[self.pos]
|
|
231
|
+
if c == "\\":
|
|
232
|
+
self.out = self.out + c + (self.src[self.pos + 1] ?? "")
|
|
233
|
+
self.pos = self.pos + 2
|
|
234
|
+
continue
|
|
235
|
+
if c == "`":
|
|
236
|
+
self.out = self.out + c
|
|
237
|
+
self.pos = self.pos + 1
|
|
238
|
+
return
|
|
239
|
+
self.out = self.out + c
|
|
240
|
+
self.pos = self.pos + 1
|
|
241
|
+
|
|
242
|
+
fn parseJsxElement():
|
|
243
|
+
self.pos = self.pos + 1 // consume '<'
|
|
244
|
+
|
|
245
|
+
// Fragment: <>...</>
|
|
246
|
+
if self.src[self.pos] == ">":
|
|
247
|
+
self.pos = self.pos + 1
|
|
248
|
+
val children = self.parseJsxChildren("")
|
|
249
|
+
self.expectClose("")
|
|
250
|
+
val childStr = children.length > 0 ? "," + children.join(",") : ""
|
|
251
|
+
return '_fluxH("",null' + childStr + ")"
|
|
252
|
+
|
|
253
|
+
val tag = self.readTagName()
|
|
254
|
+
val attrs = self.parseJsxAttrs()
|
|
255
|
+
|
|
256
|
+
// Self-closing
|
|
257
|
+
if self.src[self.pos] == "/" and self.src[self.pos + 1] == ">":
|
|
258
|
+
self.pos = self.pos + 2
|
|
259
|
+
return '_fluxH("' + tag + '",' + attrs + ")"
|
|
260
|
+
|
|
261
|
+
// Opening >
|
|
262
|
+
if self.src[self.pos] == ">":
|
|
263
|
+
self.pos = self.pos + 1
|
|
264
|
+
val children = self.parseJsxChildren(tag)
|
|
265
|
+
self.expectClose(tag)
|
|
266
|
+
val childStr = children.length > 0 ? "," + children.join(",") : ""
|
|
267
|
+
return '_fluxH("' + tag + '",' + attrs + childStr + ")"
|
|
268
|
+
|
|
269
|
+
return "<" + tag
|
|
270
|
+
|
|
271
|
+
fn readTagName():
|
|
272
|
+
var name = ""
|
|
273
|
+
while self.pos < self.src.length:
|
|
274
|
+
val c = self.src[self.pos]
|
|
275
|
+
if /[a-zA-Z0-9\-_\.]/.test(c):
|
|
276
|
+
name = name + c
|
|
277
|
+
self.pos = self.pos + 1
|
|
278
|
+
else:
|
|
279
|
+
break
|
|
280
|
+
return name
|
|
281
|
+
|
|
282
|
+
fn parseJsxAttrs():
|
|
283
|
+
self.skipWs()
|
|
284
|
+
val pairs = []
|
|
285
|
+
val styleParts = []
|
|
286
|
+
var explicitStyle = null
|
|
287
|
+
|
|
288
|
+
while self.pos < self.src.length:
|
|
289
|
+
val c = self.src[self.pos]
|
|
290
|
+
if c == ">" or (c == "/" and self.src[self.pos + 1] == ">"): break
|
|
291
|
+
self.skipWs()
|
|
292
|
+
if not /[a-zA-Z_\-]/.test(self.src[self.pos] ?? ""): break
|
|
293
|
+
|
|
294
|
+
var attrName = ""
|
|
295
|
+
while self.pos < self.src.length and /[a-zA-Z0-9\-_:]/.test(self.src[self.pos]):
|
|
296
|
+
attrName = attrName + self.src[self.pos]
|
|
297
|
+
self.pos = self.pos + 1
|
|
298
|
+
self.skipWs()
|
|
299
|
+
|
|
300
|
+
// Boolean attribute
|
|
301
|
+
if self.src[self.pos] != "=":
|
|
302
|
+
val boolStyle = JSX_STYLE_BOOL_PROPS[attrName]
|
|
303
|
+
if boolStyle != undefined:
|
|
304
|
+
styleParts.push(boolStyle)
|
|
305
|
+
else:
|
|
306
|
+
pairs.push('"' + attrName + '":true')
|
|
307
|
+
continue
|
|
308
|
+
|
|
309
|
+
self.pos = self.pos + 1 // consume '='
|
|
310
|
+
self.skipWs()
|
|
311
|
+
|
|
312
|
+
val ch = self.src[self.pos]
|
|
313
|
+
var rawVal = ""
|
|
314
|
+
var exprVal_ = ""
|
|
315
|
+
|
|
316
|
+
if ch == '"' or ch == "'":
|
|
317
|
+
val q = ch
|
|
318
|
+
self.pos = self.pos + 1
|
|
319
|
+
var v = ""
|
|
320
|
+
while self.pos < self.src.length and self.src[self.pos] != q:
|
|
321
|
+
if self.src[self.pos] == "\\":
|
|
322
|
+
v = v + self.src[self.pos] + self.src[self.pos + 1]
|
|
323
|
+
self.pos = self.pos + 2
|
|
324
|
+
else:
|
|
325
|
+
v = v + self.src[self.pos]
|
|
326
|
+
self.pos = self.pos + 1
|
|
327
|
+
self.pos = self.pos + 1 // close quote
|
|
328
|
+
rawVal = v
|
|
329
|
+
else if ch == '{':
|
|
330
|
+
exprVal_ = self.readBraced()
|
|
331
|
+
else:
|
|
332
|
+
val boolStyle = JSX_STYLE_BOOL_PROPS[attrName]
|
|
333
|
+
if boolStyle != undefined:
|
|
334
|
+
styleParts.push(boolStyle)
|
|
335
|
+
else:
|
|
336
|
+
pairs.push('"' + attrName + '":true')
|
|
337
|
+
self.skipWs()
|
|
338
|
+
continue
|
|
339
|
+
|
|
340
|
+
if rawVal != null and JSX_STYLE_VALUE_PROPS[attrName]:
|
|
341
|
+
val expanded = JSX_STYLE_VALUE_PROPS[attrName](JSON.stringify(rawVal))
|
|
342
|
+
styleParts.push(expanded)
|
|
343
|
+
else if exprVal_ != null and attrName == "style":
|
|
344
|
+
explicitStyle = exprVal_
|
|
345
|
+
else:
|
|
346
|
+
if rawVal != null:
|
|
347
|
+
pairs.push('"' + attrName + '":' + JSON.stringify(rawVal))
|
|
348
|
+
else:
|
|
349
|
+
pairs.push('"' + attrName + '":' + exprVal_)
|
|
350
|
+
|
|
351
|
+
self.skipWs()
|
|
352
|
+
|
|
353
|
+
// Build style entry
|
|
354
|
+
if styleParts.length > 0 or explicitStyle != null:
|
|
355
|
+
val utilObj = styleParts.length > 0 ? '{' + styleParts.join(',') + '}' : null
|
|
356
|
+
if explicitStyle != null and utilObj != null:
|
|
357
|
+
pairs.push('"style":Object.assign(' + explicitStyle + ',' + utilObj + ')')
|
|
358
|
+
else if explicitStyle != null:
|
|
359
|
+
pairs.push('"style":' + explicitStyle)
|
|
360
|
+
else:
|
|
361
|
+
pairs.push('"style":{' + styleParts.join(',') + '}')
|
|
362
|
+
|
|
363
|
+
return pairs.length > 0 ? '{' + pairs.join(',') + '}' : 'null'
|
|
364
|
+
|
|
365
|
+
fn readBraced():
|
|
366
|
+
self.pos = self.pos + 1 // consume '{'
|
|
367
|
+
var depth = 1
|
|
368
|
+
var content = ""
|
|
369
|
+
while self.pos < self.src.length and depth > 0:
|
|
370
|
+
val c = self.src[self.pos]
|
|
371
|
+
if c == '{': depth = depth + 1
|
|
372
|
+
if c == '}':
|
|
373
|
+
depth = depth - 1
|
|
374
|
+
if depth == 0:
|
|
375
|
+
self.pos = self.pos + 1
|
|
376
|
+
break
|
|
377
|
+
content = content + c
|
|
378
|
+
self.pos = self.pos + 1
|
|
379
|
+
return content.trim()
|
|
380
|
+
|
|
381
|
+
fn parseJsxChildren(tag):
|
|
382
|
+
val children = []
|
|
383
|
+
while self.pos < self.src.length:
|
|
384
|
+
// End tag
|
|
385
|
+
if self.src[self.pos] == "<" and self.src[self.pos + 1] == "/": break
|
|
386
|
+
|
|
387
|
+
// Nested element
|
|
388
|
+
if self.src[self.pos] == "<":
|
|
389
|
+
val next = self.src[self.pos + 1] ?? ""
|
|
390
|
+
if (next >= "a" and next <= "z") or (next >= "A" and next <= "Z") or next == ">":
|
|
391
|
+
children.push(self.parseJsxElement())
|
|
392
|
+
continue
|
|
393
|
+
|
|
394
|
+
// Expression {expr}
|
|
395
|
+
if self.src[self.pos] == '{':
|
|
396
|
+
val expr = self.readBraced()
|
|
397
|
+
if expr.trim(): children.push(expr)
|
|
398
|
+
continue
|
|
399
|
+
|
|
400
|
+
// Text node
|
|
401
|
+
var text = ""
|
|
402
|
+
while self.pos < self.src.length:
|
|
403
|
+
val c = self.src[self.pos]
|
|
404
|
+
if c == "<" or c == '{': break
|
|
405
|
+
text = text + c
|
|
406
|
+
self.pos = self.pos + 1
|
|
407
|
+
val trimmed = text.replace(/\s+/g, " ").trim()
|
|
408
|
+
if trimmed: children.push(JSON.stringify(trimmed))
|
|
409
|
+
|
|
410
|
+
return children
|
|
411
|
+
|
|
412
|
+
fn expectClose(tag):
|
|
413
|
+
if self.src[self.pos] == "<" and self.src[self.pos + 1] == "/":
|
|
414
|
+
self.pos = self.pos + 2
|
|
415
|
+
while self.pos < self.src.length and self.src[self.pos] != ">":
|
|
416
|
+
self.pos = self.pos + 1
|
|
417
|
+
if self.src[self.pos] == ">": self.pos = self.pos + 1
|
|
418
|
+
|
|
419
|
+
fn skipWs():
|
|
420
|
+
while self.pos < self.src.length and (self.src[self.pos] == " " or self.src[self.pos] == "\t"):
|
|
421
|
+
self.pos = self.pos + 1
|
|
422
|
+
|
|
423
|
+
export fn transformJsx(src, opts):
|
|
424
|
+
val target = (opts ?? {}).target ?? "browser"
|
|
425
|
+
val proc = new JsxPreprocessor(src, 0, "", false)
|
|
426
|
+
val source = proc.transform()
|
|
427
|
+
val hasJsx = proc.hasJsx or src.includes("_fluxH") or src.includes("_fluxCSS")
|
|
428
|
+
val hHelper = target == "browser" ? FLUX_H_BROWSER : FLUX_H_SERVER
|
|
429
|
+
val cssHelper = target == "browser" ? FLUX_CSS_BROWSER : FLUX_CSS_SERVER
|
|
430
|
+
return { source, hasJsx, runtimeHelpers: hHelper + cssHelper }
|