@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.
Files changed (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,373 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted CSS Preprocessor
3
+ // src/self/css-preprocessor.flux — written in Flux, compiled by stage-0
4
+ //
5
+ // Note: single-quoted strings used for any literal { or } characters
6
+ // because Flux double-quoted strings treat {expr} as interpolation.
7
+ // ============================================================
8
+
9
+ export val CSS_PROP_MAP = {
10
+ bg: 'background', fg: 'color', p: 'padding',
11
+ px: 'padding-inline', py: 'padding-block',
12
+ pt: 'padding-top', pb: 'padding-bottom', pl: 'padding-left', pr: 'padding-right',
13
+ m: 'margin', mx: 'margin-inline', my: 'margin-block',
14
+ mt: 'margin-top', mb: 'margin-bottom', ml: 'margin-left', mr: 'margin-right',
15
+ radius: 'border-radius',
16
+ w: 'width', h: 'height',
17
+ 'min-w': 'min-width', 'max-w': 'max-width', 'min-h': 'min-height', 'max-h': 'max-height',
18
+ gap: 'gap', 'col-gap': 'column-gap', 'row-gap': 'row-gap',
19
+ text: 'font-size', font: 'font-family', weight: 'font-weight',
20
+ tracking: 'letter-spacing', leading: 'line-height',
21
+ shadow: 'box-shadow', opacity: 'opacity',
22
+ border: 'border', outline: 'outline',
23
+ transition: 'transition', cursor: 'cursor', overflow: 'overflow',
24
+ 'overflow-x': 'overflow-x', 'overflow-y': 'overflow-y',
25
+ z: 'z-index', transform: 'transform', content: 'content',
26
+ resize: 'resize', appearance: 'appearance',
27
+ 'object-fit': 'object-fit', 'accent-color': 'accent-color',
28
+ direction: 'flex-direction', wrap: 'flex-wrap',
29
+ align: 'align-items', justify: 'justify-content',
30
+ 'align-self': 'align-self', 'justify-self': 'justify-self',
31
+ grow: 'flex-grow', shrink: 'flex-shrink', basis: 'flex-basis',
32
+ order: 'order', cols: 'grid-template-columns', rows: 'grid-template-rows',
33
+ 'col-span': 'grid-column', 'row-span': 'grid-row',
34
+ 'place-items': 'place-items', 'place-content': 'place-content',
35
+ 'list-style': 'list-style', 'text-align': 'text-align',
36
+ decoration: 'text-decoration', 'text-transform': 'text-transform',
37
+ 'white-space': 'white-space', 'word-break': 'word-break',
38
+ 'user-select': 'user-select', 'pointer-events': 'pointer-events',
39
+ 'vertical-align': 'vertical-align', backdrop: 'backdrop-filter',
40
+ filter: 'filter', clip: 'clip-path', animation: 'animation',
41
+ position: 'position', top: 'top', right: 'right', bottom: 'bottom',
42
+ left: 'left', inset: 'inset', color: 'color', background: 'background',
43
+ }
44
+
45
+ export val CSS_BOOL_MAP = {
46
+ flex: 'display: flex', grid: 'display: grid',
47
+ block: 'display: block', inline: 'display: inline',
48
+ 'inline-flex': 'display: inline-flex', 'inline-block': 'display: inline-block',
49
+ 'inline-grid': 'display: inline-grid',
50
+ bold: 'font-weight: 700', italic: 'font-style: italic',
51
+ relative: 'position: relative', absolute: 'position: absolute',
52
+ fixed: 'position: fixed', sticky: 'position: sticky',
53
+ hidden: 'display: none', pointer: 'cursor: pointer',
54
+ underline: 'text-decoration: underline',
55
+ 'line-through': 'text-decoration: line-through',
56
+ capitalize: 'text-transform: capitalize',
57
+ uppercase: 'text-transform: uppercase',
58
+ lowercase: 'text-transform: lowercase',
59
+ truncate: 'overflow: hidden; text-overflow: ellipsis; white-space: nowrap',
60
+ 'select-none': 'user-select: none',
61
+ 'no-wrap': 'white-space: nowrap',
62
+ 'no-list': 'list-style: none',
63
+ 'no-outline': 'outline: none',
64
+ 'no-border': 'border: none',
65
+ 'no-bg': 'background: transparent',
66
+ 'no-padding': 'padding: 0',
67
+ 'no-margin': 'margin: 0',
68
+ 'no-resize': 'resize: none',
69
+ center: 'text-align: center',
70
+ 'items-center': 'align-items: center',
71
+ 'justify-center': 'justify-content: center',
72
+ 'place-center': 'place-items: center',
73
+ 'flex-col': 'flex-direction: column',
74
+ 'flex-row': 'flex-direction: row',
75
+ 'flex-wrap': 'flex-wrap: wrap',
76
+ 'flex-1': 'flex: 1',
77
+ 'w-full': 'width: 100%',
78
+ 'h-full': 'height: 100%',
79
+ 'w-screen': 'width: 100vw',
80
+ 'h-screen': 'height: 100vh',
81
+ 'box-border': 'box-sizing: border-box',
82
+ }
83
+
84
+ fn expandProp(name):
85
+ val t = name.trim()
86
+ return CSS_PROP_MAP[t] ?? t
87
+
88
+ fn stripQuotes(v_):
89
+ val v = v_.trim()
90
+ if v.length >= 2:
91
+ val q0 = v[0]
92
+ val ql = v[v.length - 1]
93
+ if (q0 == '"' and ql == '"') or (q0 == "'" and ql == "'"):
94
+ return v.slice(1, -1)
95
+ return v
96
+
97
+ fn resolveSelector(sel, parent):
98
+ val s = sel.trim()
99
+ if not parent: return s
100
+ if s.startsWith('@'): return s
101
+ if parent.startsWith('@keyframes') or parent.startsWith('@font-face'): return s
102
+ if s.includes('&'): return s.replace(/&/g, parent)
103
+ return parent + ' ' + s
104
+
105
+ // ── CSS Block Parser ──────────────────────────────────────────────────────────
106
+ class CssBlockParser:
107
+ content: any
108
+ indentLevel: int
109
+ rules: any[]
110
+ atRuleSegs: any[]
111
+ selStack: any[]
112
+
113
+ fn curSel():
114
+ if self.selStack.length == 0: return null
115
+ return self.selStack[self.selStack.length - 1]
116
+
117
+ fn findOrCreateRule(sel):
118
+ for entry in self.rules:
119
+ if entry.sel == sel: return entry
120
+ val entry = { sel, decls: [] }
121
+ self.rules.push(entry)
122
+ return entry
123
+
124
+ fn addDecl(sel, d):
125
+ val entry = self.findOrCreateRule(sel)
126
+ entry.decls.push(d)
127
+
128
+ fn addDeclarationLine(line, sel):
129
+ if line.includes(';'):
130
+ val parts = line.split(';')
131
+ for part in parts:
132
+ val t = part.trim()
133
+ if t: self.addDeclarationLine(t, sel)
134
+ return
135
+ val boolDecl = CSS_BOOL_MAP[line]
136
+ if boolDecl:
137
+ val bdParts = boolDecl.split(';')
138
+ for bd in bdParts:
139
+ val t = bd.trim()
140
+ if t: self.addDecl(sel, t)
141
+ return
142
+ val ci = line.indexOf(':')
143
+ if ci != -1:
144
+ val prop = expandProp(line.slice(0, ci))
145
+ val v = stripQuotes(line.slice(ci + 1))
146
+ if v != '': self.addDecl(sel, prop + ': ' + v)
147
+
148
+ fn collectBlock(lines, startI):
149
+ var depth = 1
150
+ var inner = ''
151
+ var j = startI
152
+ while j < lines.length and depth > 0:
153
+ val ln = lines[j]
154
+ j = j + 1
155
+ for ch in ln:
156
+ if ch == '{': depth = depth + 1
157
+ else if ch == '}': depth = depth - 1
158
+ if depth > 0:
159
+ inner = inner + ln + "\n"
160
+ else:
161
+ val ci = ln.lastIndexOf('}')
162
+ if ci > 0: inner = inner + ln.slice(0, ci) + "\n"
163
+ return { inner, nextI: j }
164
+
165
+ fn parse():
166
+ val lines = self.content.split("\n")
167
+ var i = 0
168
+ while i < lines.length:
169
+ val raw = lines[i]
170
+ i = i + 1
171
+ val line = raw.trim()
172
+ if not line or line.startsWith('//'): continue
173
+
174
+ val ob = line.indexOf('{')
175
+ val cb = line.lastIndexOf('}')
176
+
177
+ // @keyframes / @font-face — collect raw block
178
+ if line.startsWith('@keyframes') or line.startsWith('@font-face'):
179
+ val atSel = ob != -1 ? line.slice(0, ob).trim() : line.trim()
180
+ var atBody = ''
181
+ if ob != -1 and cb != -1 and cb > ob:
182
+ atBody = line.slice(ob + 1, cb)
183
+ else if ob != -1:
184
+ val res = self.collectBlock(lines, i)
185
+ i = res.nextI
186
+ atBody = res.inner
187
+ val ind_ = ' '.repeat(self.indentLevel ?? 0)
188
+ self.atRuleSegs.push(ind_ + atSel + ' {\n' + atBody + ind_ + '}\n')
189
+ continue
190
+
191
+ // @media / @supports / @layer — nested recursive
192
+ if line.startsWith('@media') or line.startsWith('@supports') or line.startsWith('@layer'):
193
+ val atSel = ob != -1 ? line.slice(0, ob).trim() : line.trim()
194
+ var atBody = ''
195
+ if ob != -1 and cb != -1 and cb > ob:
196
+ atBody = line.slice(ob + 1, cb)
197
+ else if ob != -1:
198
+ val res = self.collectBlock(lines, i)
199
+ i = res.nextI
200
+ atBody = res.inner
201
+ val innerCss = parseCssBlockContent(atBody, (self.indentLevel ?? 0) + 1)
202
+ val ind_ = ' '.repeat(self.indentLevel ?? 0)
203
+ self.atRuleSegs.push(ind_ + atSel + ' {\n' + innerCss + ind_ + '}\n')
204
+ continue
205
+
206
+ // Inline rule: selector { decls } on one line
207
+ if ob != -1 and cb != -1 and cb > ob:
208
+ val sel = line.slice(0, ob).trim()
209
+ val inner = line.slice(ob + 1, cb).trim()
210
+ val full = resolveSelector(sel, self.curSel())
211
+ self.findOrCreateRule(full)
212
+ if inner: self.addDeclarationLine(inner, full)
213
+ continue
214
+
215
+ // Block opener: selector {
216
+ if ob != -1:
217
+ val sel = line.slice(0, ob).trim()
218
+ val full = resolveSelector(sel, self.curSel())
219
+ self.selStack.push(full)
220
+ self.findOrCreateRule(full)
221
+ val tail = line.slice(ob + 1).trim()
222
+ if tail: self.addDeclarationLine(tail, full)
223
+ continue
224
+
225
+ // Closing brace
226
+ if line == '}' or line == '};':
227
+ self.selStack.pop()
228
+ continue
229
+
230
+ // Declaration inside a rule block
231
+ if self.selStack.length > 0:
232
+ self.addDeclarationLine(line, self.curSel())
233
+
234
+ fn emit():
235
+ var css = ''
236
+ val ind = ' '.repeat(self.indentLevel ?? 0)
237
+ val ind1 = ind + ' '
238
+ for entry in self.rules:
239
+ if entry.decls.length == 0: continue
240
+ css = css + ind + entry.sel + ' {\n'
241
+ for d in entry.decls:
242
+ css = css + ind1 + d + ';\n'
243
+ css = css + ind + '}\n'
244
+ for seg in self.atRuleSegs:
245
+ css = css + seg
246
+ return css
247
+
248
+ export fn parseCssBlockContent(content, indentLevel):
249
+ val parser = new CssBlockParser(content, indentLevel ?? 0, [], [], [])
250
+ parser.parse()
251
+ return parser.emit()
252
+
253
+ // ── Main source preprocessor: replace css { } with CSS string ──────────────
254
+ export class CssPreprocessor:
255
+ src: any
256
+ pos: int
257
+ out: any
258
+
259
+ fn transform():
260
+ while self.pos < self.src.length:
261
+ self.scan()
262
+ return self.out
263
+
264
+ fn scan():
265
+ val c = self.src[self.pos]
266
+
267
+ // Line comment — pass through
268
+ if c == '/' and self.src[self.pos + 1] == '/':
269
+ val end_ = self.src.indexOf("\n", self.pos)
270
+ if end_ == -1:
271
+ self.out = self.out + self.src.slice(self.pos)
272
+ self.pos = self.src.length
273
+ else:
274
+ self.out = self.out + self.src.slice(self.pos, end_ + 1)
275
+ self.pos = end_ + 1
276
+ return
277
+
278
+ // Block comment — pass through
279
+ if c == '/' and self.src[self.pos + 1] == '*':
280
+ val end_ = self.src.indexOf('*/', self.pos + 2)
281
+ if end_ == -1:
282
+ self.out = self.out + self.src.slice(self.pos)
283
+ self.pos = self.src.length
284
+ else:
285
+ self.out = self.out + self.src.slice(self.pos, end_ + 2)
286
+ self.pos = end_ + 2
287
+ return
288
+
289
+ // String literals — pass through unchanged
290
+ if c == '"':
291
+ self.passString('"')
292
+ return
293
+ if c == "'":
294
+ self.passString("'")
295
+ return
296
+ if c == '`':
297
+ self.passTemplate()
298
+ return
299
+
300
+ // Detect standalone css keyword followed by {
301
+ if c == 'c' and self.src.slice(self.pos, self.pos + 3) == 'css':
302
+ val charBefore = self.pos > 0 ? self.src[self.pos - 1] : "\n"
303
+ val charAfter = self.src[self.pos + 3] ?? ''
304
+ if not /[a-zA-Z0-9_]/.test(charBefore) and not /[a-zA-Z0-9_]/.test(charAfter):
305
+ var j = self.pos + 3
306
+ while j < self.src.length and (self.src[j] == ' ' or self.src[j] == "\t" or self.src[j] == "\n" or self.src[j] == "\r"):
307
+ j = j + 1
308
+ if self.src[j] == '{':
309
+ j = j + 1
310
+ var depth = 1
311
+ val blockStart = j
312
+ while j < self.src.length and depth > 0:
313
+ val ch = self.src[j]
314
+ if ch == '"' or ch == "'" or ch == '`':
315
+ val q = ch
316
+ j = j + 1
317
+ while j < self.src.length and self.src[j] != q:
318
+ if self.src[j] == '\\': j = j + 1
319
+ j = j + 1
320
+ j = j + 1
321
+ else if ch == '{':
322
+ depth = depth + 1
323
+ j = j + 1
324
+ else if ch == '}':
325
+ depth = depth - 1
326
+ j = j + 1
327
+ else:
328
+ j = j + 1
329
+ val blockContent = self.src.slice(blockStart, j - 1)
330
+ val css = parseCssBlockContent(blockContent, 0)
331
+ val BT = String.fromCharCode(96)
332
+ val escapedCss = css.replace(/\\/g, '\\\\').replace(new RegExp(BT, 'g'), '\\' + BT)
333
+ self.out = self.out + BT + escapedCss + BT
334
+ self.pos = j
335
+ return
336
+
337
+ self.out = self.out + c
338
+ self.pos = self.pos + 1
339
+
340
+ fn passString(quote):
341
+ self.out = self.out + quote
342
+ self.pos = self.pos + 1
343
+ while self.pos < self.src.length:
344
+ val c = self.src[self.pos]
345
+ if c == '\\':
346
+ self.out = self.out + c + (self.src[self.pos + 1] ?? '')
347
+ self.pos = self.pos + 2
348
+ continue
349
+ if c == quote:
350
+ self.out = self.out + c
351
+ self.pos = self.pos + 1
352
+ return
353
+ self.out = self.out + c
354
+ self.pos = self.pos + 1
355
+
356
+ fn passTemplate():
357
+ self.out = self.out + '`'
358
+ self.pos = self.pos + 1
359
+ while self.pos < self.src.length:
360
+ val c = self.src[self.pos]
361
+ if c == '\\':
362
+ self.out = self.out + c + (self.src[self.pos + 1] ?? '')
363
+ self.pos = self.pos + 2
364
+ continue
365
+ if c == '`':
366
+ self.out = self.out + c
367
+ self.pos = self.pos + 1
368
+ return
369
+ self.out = self.out + c
370
+ self.pos = self.pos + 1
371
+
372
+ export fn transformCss(src):
373
+ return new CssPreprocessor(src, 0, '').transform()